medit/moo/mooedit/plugins/moofilelist.c

2173 lines
58 KiB
C
Raw Normal View History

2008-01-02 13:13:24 -06:00
/*
* moofilelist.c
*
2008-01-07 03:34:52 -06:00
* Copyright (C) 2004-2008 by Yevgen Muntyan <muntyan@tamu.edu>
2008-01-02 13:13:24 -06:00
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* See COPYING file that comes with this distribution.
*/
#include "config.h"
#include "mooeditplugins.h"
#include "mooedit/mooplugin-macro.h"
#include "mooutils/mooi18n.h"
#include "mooutils/mootype-macros.h"
#include "mooutils/mooutils-misc.h"
#include "mooutils/mooutils-treeview.h"
2008-01-02 13:13:24 -06:00
#include "mooutils/mooutils-fs.h"
2008-01-05 02:08:03 -06:00
#include "mooutils/moocompat.h"
2008-01-02 13:13:24 -06:00
#include <gtk/gtk.h>
2008-01-07 03:05:43 -06:00
#undef DEBUG
#if defined(MOO_DEBUG_ENABLED) && 1
#define DEBUG
#endif
#ifdef DEBUG
#define DEBUG_ASSERT(expr) g_assert (expr)
#else
#define DEBUG_ASSERT(expr) g_assert (expr)
#endif
2008-01-02 13:13:24 -06:00
#define FILE_LIST_PLUGIN_ID "MooFileList"
#define WindowPlugin FileListWindowPlugin
#define FILE_LIST(model) ((FileList*)model)
#define GROUP_ITEM(itm) ((Group*)itm)
#define FILE_ITEM(itm) ((File*)itm)
#define ITEM(itm) ((Item*)itm)
#define ITEM_IS_FILE(itm) ((itm) && ((Item*)(itm))->type == ITEM_FILE)
#define ITEM_IS_GROUP(itm) ((itm) && ((Item*)(itm))->type == ITEM_GROUP)
#define CONFIG_FILE "file-list-config.xml"
#define ELM_CONFIG "file-list-config"
#define PROP_VERSION "version"
#define VALUE_VERSION "1.0"
#define ELM_GROUP "group"
#define ELM_FILE "file"
#define PROP_NAME "name"
#define PROP_URI "uri"
typedef struct Item Item;
typedef struct Group Group;
typedef struct File File;
2008-01-07 03:05:43 -06:00
typedef struct FileListWindowPlugin FileListWindowPlugin;
2008-01-02 13:13:24 -06:00
enum {
COLUMN_ITEM,
COLUMN_TOOLTIP
};
typedef enum {
ITEM_FILE,
ITEM_GROUP
} ItemType;
struct Item {
ItemType type;
guint ref_count;
};
struct File {
Item base;
2008-01-07 03:05:43 -06:00
char *uri;
2008-01-02 13:13:24 -06:00
char *display_basename;
char *display_name;
MooEdit *doc;
};
struct Group {
Item base;
char *name;
};
typedef struct {
GtkTreeStore base;
int n_user_items;
GSList *docs;
2008-01-07 03:05:43 -06:00
FileListWindowPlugin *plugin;
2008-01-02 13:13:24 -06:00
} FileList;
typedef struct {
GtkTreeStoreClass base_class;
} FileListClass;
typedef struct {
MooPlugin parent;
} FileListPlugin;
2008-01-07 03:05:43 -06:00
struct FileListWindowPlugin {
2008-01-02 13:13:24 -06:00
MooWinPlugin parent;
FileList *list;
GtkWidget *treeview;
GtkTreeViewColumn *column;
GtkCellRenderer *text_cell;
gboolean first_time_show;
2008-01-07 03:05:43 -06:00
guint update_idle;
2008-01-02 13:13:24 -06:00
char *filename;
2008-01-07 03:05:43 -06:00
};
2008-01-02 13:13:24 -06:00
static GdkAtom atom_tree_model_row;
static GdkAtom atom_uri_list;
2008-01-07 03:05:43 -06:00
static GQuark file_list_row_quark;
static GType item_get_type (void) G_GNUC_CONST;
static Item *item_ref (Item *item);
static void item_unref (Item *item);
static void file_list_load_config (FileList *list,
const char *filename);
static void file_list_save_config (FileList *list,
const char *filename);
static void file_list_update (FileList *list,
GSList *docs);
static void file_list_shutdown (FileList *list);
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
static void window_plugin_queue_update (WindowPlugin *plugin);
2008-01-02 13:13:24 -06:00
static void file_list_drag_source_iface_init (GtkTreeDragSourceIface *iface);
static void file_list_drag_dest_iface_init (GtkTreeDragDestIface *iface);
static gboolean drag_source_row_draggable (GtkTreeDragSource *drag_source,
GtkTreePath *path);
static gboolean drag_source_drag_data_get (GtkTreeDragSource *drag_source,
GtkTreePath *path,
GtkSelectionData *selection_data);
static gboolean drag_source_drag_data_delete (GtkTreeDragSource *drag_source,
GtkTreePath *path);
static gboolean drag_dest_drag_data_received (GtkTreeDragDest *drag_dest,
GtkTreePath *dest,
GtkSelectionData *selection_data);
static gboolean drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest,
GtkTreePath *dest_path,
GtkSelectionData *selection_data);
MOO_DEFINE_TYPE_STATIC_WITH_CODE (FileList, file_list, GTK_TYPE_TREE_STORE,
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
file_list_drag_source_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
file_list_drag_dest_iface_init))
static void
file_list_drag_source_iface_init (GtkTreeDragSourceIface *iface)
{
iface->row_draggable = drag_source_row_draggable;
iface->drag_data_get = drag_source_drag_data_get;
iface->drag_data_delete = drag_source_drag_data_delete;
}
static void
file_list_drag_dest_iface_init (GtkTreeDragDestIface *iface)
{
iface->drag_data_received = drag_dest_drag_data_received;
iface->row_drop_possible = drag_dest_row_drop_possible;
}
static void
file_list_init (FileList *list)
{
GType types[2];
types[COLUMN_ITEM] = item_get_type ();
types[COLUMN_TOOLTIP] = G_TYPE_STRING;
gtk_tree_store_set_column_types (GTK_TREE_STORE (list), 2, types);
list->n_user_items = 0;
list->docs = NULL;
}
static void
file_list_finalize (GObject *object)
{
FileList *list = FILE_LIST (object);
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (!list->docs);
2008-01-02 13:13:24 -06:00
G_OBJECT_CLASS (file_list_parent_class)->finalize (object);
}
static void
file_list_class_init (FileListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2008-01-07 03:05:43 -06:00
file_list_row_quark = g_quark_from_static_string ("moo-file-list-plugin-model-row");
2008-01-02 13:13:24 -06:00
object_class->finalize = file_list_finalize;
atom_tree_model_row = gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW");
atom_uri_list = gdk_atom_intern_static_string ("text/uri-list");
}
static Item *
get_item_at_iter (FileList *list,
GtkTreeIter *iter)
{
Item *item = NULL;
gtk_tree_model_get (GTK_TREE_MODEL (list), iter, COLUMN_ITEM, &item, -1);
if (item)
item_unref (item);
return item;
}
2008-01-02 18:56:30 -06:00
static Item *
get_item_at_path (FileList *list,
GtkTreePath *path)
{
GtkTreeIter iter;
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, path))
return NULL;
else
return get_item_at_iter (list, &iter);
}
2008-01-02 13:13:24 -06:00
static Group *
group_new (const char *name)
{
Group *grp = _moo_new0 (Group);
ITEM (grp)->ref_count = 1;
ITEM (grp)->type = ITEM_GROUP;
grp->name = g_strdup (name);
return grp;
}
static void
group_free (Group *grp)
{
if (grp)
{
g_free (grp->name);
_moo_free (Group, grp);
}
}
static char *
uri_get_basename (const char *uri)
{
const char *last_slash = strrchr (uri, '/');
if (last_slash && last_slash[1])
return g_strdup (last_slash + 1);
else
return g_strdup (uri);
}
static char *
file_get_uri (File *file)
{
2008-01-07 03:05:43 -06:00
if (file->uri)
return g_strdup (file->uri);
2008-01-02 13:13:24 -06:00
else
2008-01-07 03:05:43 -06:00
return moo_edit_get_uri (file->doc);
2008-01-02 13:13:24 -06:00
}
static void
file_update (File *file)
{
2008-01-07 03:05:43 -06:00
if (!file->uri && file->doc)
2008-01-02 13:13:24 -06:00
{
g_free (file->display_name);
g_free (file->display_basename);
file->display_name = g_strdup (moo_edit_get_display_name (file->doc));
file->display_basename = g_strdup (moo_edit_get_display_basename (file->doc));
}
}
static void
file_set_doc (File *file,
MooEdit *doc)
{
2008-01-07 03:05:43 -06:00
if (doc)
g_object_ref (doc);
2008-01-02 13:13:24 -06:00
if (file->doc)
g_object_unref (file->doc);
2008-01-07 03:05:43 -06:00
file->doc = doc;
2008-01-02 13:13:24 -06:00
file_update (file);
}
2008-01-07 03:05:43 -06:00
static File *
file_new (void)
2008-01-02 13:13:24 -06:00
{
File *file = _moo_new0 (File);
ITEM (file)->ref_count = 1;
ITEM (file)->type = ITEM_FILE;
2008-01-07 03:05:43 -06:00
file->uri = NULL;
file->doc = NULL;
file->display_name = NULL;
file->display_basename = NULL;
return file;
}
static Item *
file_new_doc (MooEdit *doc)
{
File *file;
g_return_val_if_fail (MOO_IS_EDIT (doc), NULL);
file = file_new ();
file_set_doc (file, doc);
return ITEM (file);
}
static void
file_set_uri (File *file,
const char *uri)
{
char *tmp;
char *filename;
g_return_if_fail (file != NULL);
g_return_if_fail (uri != NULL);
tmp = file->uri;
2008-01-02 13:13:24 -06:00
file->uri = g_strdup (uri);
2008-01-07 03:05:43 -06:00
g_free (tmp);
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
g_free (file->display_name);
g_free (file->display_basename);
filename = g_filename_from_uri (uri, NULL, NULL);
if (filename)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
file->display_name = g_filename_display_name (filename);
file->display_basename = g_filename_display_basename (filename);
2008-01-02 13:13:24 -06:00
}
2008-01-07 03:05:43 -06:00
else
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
file->display_name = g_strdup (uri);
file->display_basename = uri_get_basename (uri);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
g_free (filename);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
static Item *
file_new_uri (const char *uri)
{
File *file;
g_return_val_if_fail (uri != NULL, NULL);
file = file_new ();
file_set_uri (file, uri);
2008-01-02 13:13:24 -06:00
return ITEM (file);
}
static void
file_free (File *file)
{
if (file)
{
if (file->doc)
g_object_unref (file->doc);
g_free (file->uri);
g_free (file->display_name);
g_free (file->display_basename);
_moo_free (File, file);
}
}
2008-01-07 03:05:43 -06:00
static const char *
item_get_tooltip (Item *item)
{
if (ITEM_IS_FILE (item))
return FILE_ITEM (item)->display_name;
else
return NULL;
}
2008-01-02 13:13:24 -06:00
static Item *
item_ref (Item *item)
{
if (item)
item->ref_count += 1;
return item;
}
static void
item_unref (Item *item)
{
if (item && !--item->ref_count)
{
switch (item->type)
{
case ITEM_FILE:
file_free (FILE_ITEM (item));
break;
case ITEM_GROUP:
group_free (GROUP_ITEM (item));
break;
}
}
}
static GType
item_get_type (void)
{
static GType type;
if (G_UNLIKELY (!type))
type = g_boxed_type_register_static ("MooFileListItem",
(GBoxedCopyFunc) item_ref,
(GBoxedFreeFunc) item_unref);
return type;
}
static gboolean
2008-01-07 03:05:43 -06:00
file_list_iter_is_auto (FileList *list,
GtkTreeIter *iter)
2008-01-02 13:13:24 -06:00
{
gboolean retval;
GtkTreePath *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (list), iter);
retval = gtk_tree_path_get_depth (path) == 1 &&
gtk_tree_path_get_indices (path)[0] >= list->n_user_items;
gtk_tree_path_free (path);
return retval;
}
static void
2008-01-07 03:05:43 -06:00
check_list (FileList *list)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
#ifdef DEBUG
2008-01-02 13:13:24 -06:00
GtkTreeIter iter;
int index = 0;
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (list), &iter, NULL))
{
do
{
Item *item = get_item_at_iter (list, &iter);
if (list->n_user_items)
{
g_assert (index == list->n_user_items || item != NULL);
g_assert (index != list->n_user_items || item == NULL);
}
else
{
g_assert (item != NULL);
}
index += 1;
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list), &iter));
}
g_assert (list->n_user_items == 0 ||
list->n_user_items < gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list), NULL));
#endif
}
static gboolean
iter_find_uri (FileList *list,
const char *uri,
GtkTreeIter *parent,
GtkTreeIter *dest)
{
GtkTreeIter iter;
if (parent)
{
Item *item = get_item_at_iter (list, parent);
if (ITEM_IS_FILE (item) &&
FILE_ITEM (item)->uri &&
strcmp (FILE_ITEM (item)->uri, uri) == 0)
{
*dest = *parent;
return TRUE;
}
}
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (list), &iter, parent))
do
{
if (iter_find_uri (list, uri, &iter, dest))
return TRUE;
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list), &iter));
return FALSE;
}
static gboolean
file_list_find_uri (FileList *list,
const char *uri,
GtkTreeIter *iter)
{
return iter_find_uri (list, uri, NULL, iter);
}
2008-01-07 03:05:43 -06:00
2008-01-02 13:13:24 -06:00
static void
file_list_remove_row (FileList *list,
GtkTreeIter *iter)
{
GtkTreeIter parent;
gboolean last_user_item = FALSE;
if (!gtk_tree_model_iter_parent (GTK_TREE_MODEL (list), &parent, iter) &&
2008-01-07 03:05:43 -06:00
!file_list_iter_is_auto (list, iter))
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (list->n_user_items > 0);
2008-01-02 13:13:24 -06:00
list->n_user_items -= 1;
last_user_item = list->n_user_items == 0;
}
gtk_tree_store_remove (GTK_TREE_STORE (list), iter);
if (last_user_item)
{
GtkTreeIter sep;
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &sep, NULL, 0);
gtk_tree_store_remove (GTK_TREE_STORE (list), &sep);
}
2008-01-07 03:05:43 -06:00
check_list (list);
2008-01-02 13:13:24 -06:00
}
static void
2008-01-07 03:05:43 -06:00
file_list_append_row (FileList *list,
Item *item,
GtkTreeIter *iter)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
gtk_tree_store_append (GTK_TREE_STORE (list), iter, NULL);
gtk_tree_store_set (GTK_TREE_STORE (list), iter,
2008-01-02 13:13:24 -06:00
COLUMN_ITEM, item,
COLUMN_TOOLTIP, item_get_tooltip (item),
-1);
}
static void
file_list_insert_row (FileList *list,
Item *item,
GtkTreeIter *iter,
GtkTreeIter *parent_iter,
int index)
{
gboolean first_user_item = FALSE;
if (index < 0)
{
if (!parent_iter)
index = list->n_user_items;
else
index = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list),
parent_iter);
}
if (!parent_iter)
{
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (index <= list->n_user_items);
2008-01-02 13:13:24 -06:00
list->n_user_items += 1;
first_user_item = list->n_user_items == 1;
}
2008-01-05 02:08:03 -06:00
gtk_tree_store_insert (GTK_TREE_STORE (list), iter, parent_iter, index);
gtk_tree_store_set (GTK_TREE_STORE (list), iter,
COLUMN_ITEM, item,
COLUMN_TOOLTIP, item_get_tooltip (item),
-1);
2008-01-02 13:13:24 -06:00
if (first_user_item)
{
GtkTreeIter sep;
gtk_tree_store_insert (GTK_TREE_STORE (list), &sep, NULL, list->n_user_items);
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), iter, parent_iter, index);
}
2008-01-07 03:05:43 -06:00
check_list (list);
2008-01-02 13:13:24 -06:00
}
2008-01-07 03:05:43 -06:00
2008-01-02 13:13:24 -06:00
static void
2008-01-07 03:05:43 -06:00
doc_filename_changed (FileList *list)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
if (list->plugin)
window_plugin_queue_update (list->plugin);
2008-01-02 13:13:24 -06:00
}
static void
2008-01-07 03:05:43 -06:00
connect_doc (FileList *list,
MooEdit *doc)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (!g_slist_find (list->docs, doc));
list->docs = g_slist_prepend (list->docs, g_object_ref (doc));
g_signal_connect_swapped (doc, "filename-changed",
G_CALLBACK (doc_filename_changed),
list);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
static void
disconnect_doc (FileList *list,
MooEdit *doc)
{
DEBUG_ASSERT (g_slist_find (list->docs, doc));
list->docs = g_slist_remove (list->docs, doc);
g_signal_handlers_disconnect_by_func (doc,
(gpointer) doc_filename_changed,
list);
g_object_unref (doc);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
static void
file_list_add_doc (FileList *list,
MooEdit *doc,
gboolean new)
{
char *uri;
GtkTreeIter iter;
Item *item;
GtkTreeRowReference *row;
GtkTreePath *path;
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (!new || g_object_get_qdata (G_OBJECT (doc), file_list_row_quark) == NULL);
DEBUG_ASSERT (new == !g_slist_find (list->docs, doc));
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
uri = moo_edit_get_uri (doc);
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
if (uri && file_list_find_uri (list, uri, &iter))
{
item = get_item_at_iter (list, &iter);
DEBUG_ASSERT (ITEM_IS_FILE (item) && !FILE_ITEM (item)->doc);
file_set_doc (FILE_ITEM (item), doc);
2008-01-02 13:13:24 -06:00
}
else
{
2008-01-07 03:05:43 -06:00
item = file_new_doc (doc);
file_list_append_row (list, item, &iter);
item_unref (item);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
path = gtk_tree_model_get_path (GTK_TREE_MODEL (list), &iter);
row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list), path);
g_object_set_qdata_full (G_OBJECT (doc), file_list_row_quark, row,
(GDestroyNotify) gtk_tree_row_reference_free);
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
if (new)
connect_doc (list, doc);
}
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
static gboolean
doc_get_list_iter (FileList *list,
MooEdit *doc,
GtkTreeIter *iter)
{
GtkTreeRowReference *row;
GtkTreePath *path;
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
row = g_object_get_qdata (G_OBJECT (doc), file_list_row_quark);
2008-01-02 13:13:24 -06:00
2008-01-07 03:05:43 -06:00
if (!row || !gtk_tree_row_reference_valid (row))
return FALSE;
path = gtk_tree_row_reference_get_path (row);
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), iter, path);
gtk_tree_path_free (path);
return TRUE;
2008-01-02 13:13:24 -06:00
}
static void
2008-01-07 03:05:43 -06:00
file_list_update_doc (FileList *list,
2008-01-02 13:13:24 -06:00
MooEdit *doc)
{
GtkTreeIter iter;
2008-01-07 03:05:43 -06:00
char *new_uri;
2008-01-02 13:13:24 -06:00
Item *item;
2008-01-07 03:05:43 -06:00
if (!g_slist_find (list->docs, doc))
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
file_list_add_doc (list, doc, TRUE);
2008-01-02 13:13:24 -06:00
return;
}
2008-01-07 03:05:43 -06:00
if (!doc_get_list_iter (list, doc, &iter))
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
file_list_add_doc (list, doc, FALSE);
return;
2008-01-02 13:13:24 -06:00
}
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (!file_list_iter_is_auto (list, &iter));
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
item = get_item_at_iter (list, &iter);
DEBUG_ASSERT (ITEM_IS_FILE (item) && FILE_ITEM (item)->doc == doc);
DEBUG_ASSERT (FILE_ITEM (item)->uri != NULL);
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
new_uri = moo_edit_get_uri (doc);
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
if (!new_uri || strcmp (new_uri, FILE_ITEM (item)->uri) != 0)
{
g_object_set_qdata (G_OBJECT (doc), file_list_row_quark, NULL);
file_set_doc (FILE_ITEM (item), NULL);
file_list_add_doc (list, doc, FALSE);
}
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
g_free (new_uri);
2008-01-02 14:59:30 -06:00
}
static void
2008-01-07 03:05:43 -06:00
file_list_remove_doc (FileList *list,
MooEdit *doc)
2008-01-02 14:59:30 -06:00
{
GtkTreeIter iter;
2008-01-07 03:05:43 -06:00
DEBUG_ASSERT (g_slist_find (list->docs, doc) != NULL);
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
if (doc_get_list_iter (list, doc, &iter))
2008-01-02 14:59:30 -06:00
{
2008-01-07 03:05:43 -06:00
Item *item;
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
item = get_item_at_iter (list, &iter);
DEBUG_ASSERT (ITEM_IS_FILE (item) && FILE_ITEM (item)->doc == doc);
DEBUG_ASSERT (file_list_iter_is_auto (list, &iter) == !FILE_ITEM (item)->uri);
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
if (file_list_iter_is_auto (list, &iter))
file_list_remove_row (list, &iter);
else
file_set_doc (FILE_ITEM (item), NULL);
2008-01-02 14:59:30 -06:00
}
2008-01-07 03:05:43 -06:00
g_object_set_qdata (G_OBJECT (doc), file_list_row_quark, NULL);
disconnect_doc (list, doc);
}
static void
remove_auto_items (FileList *list)
{
GtkTreeIter iter;
2008-01-02 14:59:30 -06:00
2008-01-07 03:05:43 -06:00
if (list->n_user_items)
while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
NULL, list->n_user_items + 1))
gtk_tree_store_remove (GTK_TREE_STORE (list), &iter);
else
while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, NULL, 0))
gtk_tree_store_remove (GTK_TREE_STORE (list), &iter);
2008-01-02 14:59:30 -06:00
}
2008-01-02 13:13:24 -06:00
static void
2008-01-07 03:05:43 -06:00
file_list_update (FileList *list,
GSList *docs)
2008-01-02 13:13:24 -06:00
{
GSList *l;
GSList *old_docs;
2008-01-07 03:05:43 -06:00
remove_auto_items (list);
2008-01-02 13:13:24 -06:00
for (l = docs; l != NULL; l = l->next)
file_list_update_doc (list, l->data);
old_docs = g_slist_copy (list->docs);
for (l = docs; l != NULL; l = l->next)
old_docs = g_slist_remove (old_docs, l->data);
for (l = old_docs; l != NULL; l = l->next)
file_list_remove_doc (list, l->data);
2008-01-07 03:05:43 -06:00
check_list (list);
2008-01-02 14:59:30 -06:00
2008-01-02 13:13:24 -06:00
g_slist_free (old_docs);
}
2008-01-02 14:59:30 -06:00
static void
2008-01-07 03:05:43 -06:00
file_list_shutdown (FileList *list)
2008-01-02 14:59:30 -06:00
{
2008-01-07 03:05:43 -06:00
while (list->docs)
{
GtkTreeIter iter;
MooEdit *doc;
doc = list->docs->data;
if (doc_get_list_iter (list, doc, &iter))
{
Item *item = get_item_at_iter (list, &iter);
DEBUG_ASSERT (ITEM_IS_FILE (item));
file_set_doc (FILE_ITEM (item), NULL);
}
g_object_set_qdata (G_OBJECT (doc), file_list_row_quark, NULL);
disconnect_doc (list, doc);
}
2008-01-02 14:59:30 -06:00
}
2008-01-02 13:13:24 -06:00
static void
parse_node (FileList *list,
MooMarkupNode *elm,
GtkTreeIter *parent,
const char *filename)
{
if (strcmp (elm->name, ELM_GROUP) == 0)
{
GtkTreeIter iter;
const char *name;
MooMarkupNode *child;
Group *group;
if (!(name = moo_markup_get_prop (elm, PROP_NAME)) || !name[0])
{
g_critical ("%s: in file %s, element %s: name missing",
G_STRLOC, filename, elm->name);
return;
}
group = group_new (name);
file_list_insert_row (list, ITEM (group), &iter, parent, -1);
item_unref (ITEM (group));
for (child = elm->children; child != NULL; child = child->next)
if (MOO_MARKUP_IS_ELEMENT (child))
parse_node (list, child, &iter, filename);
}
else if (strcmp (elm->name, ELM_FILE) == 0)
{
const char *uri;
Item *item;
GtkTreeIter iter;
if (!(uri = moo_markup_get_prop (elm, PROP_URI)) || !uri[0])
{
g_critical ("%s: in file %s, element %s: uri missing",
G_STRLOC, filename, elm->name);
return;
}
2008-01-07 03:05:43 -06:00
item = file_new_uri (uri);
2008-01-02 13:13:24 -06:00
file_list_insert_row (list, item, &iter, parent, -1);
item_unref (item);
}
else
{
g_critical ("%s: in file %s: unexpected element '%s'",
G_STRLOC, filename, elm->name);
}
}
static void
file_list_load_config (FileList *list,
const char *filename)
{
MooMarkupDoc *doc;
GError *error = NULL;
MooMarkupNode *root, *node;
const char *version;
if (!g_file_test (filename, G_FILE_TEST_EXISTS))
return;
doc = moo_markup_parse_file (filename, &error);
if (!doc)
{
g_critical ("%s: could not open file %s: %s",
G_STRFUNC, filename, error ? error->message : "");
g_error_free (error);
return;
}
if (!(root = moo_markup_get_root_element (doc, ELM_CONFIG)))
{
g_critical ("%s: in file %s: missing element '%s'",
G_STRFUNC, filename, ELM_CONFIG);
goto out;
}
if (!(version = moo_markup_get_prop (root, PROP_VERSION)) ||
strcmp (version, VALUE_VERSION) != 0)
{
g_critical ("%s: in file %s: invalid version '%s'",
G_STRFUNC, filename, VALUE_VERSION);
goto out;
}
for (node = root->children; node != NULL; node = node->next)
{
if (!MOO_MARKUP_IS_ELEMENT (node))
continue;
parse_node (list, node, NULL, filename);
}
out:
moo_markup_doc_unref (doc);
}
static void
format_item (FileList *list,
GtkTreeIter *iter,
GString *buffer,
guint indent)
{
Item *item;
char *indent_s;
item = get_item_at_iter (list, iter);
indent_s = g_strnfill (indent, ' ');
if (ITEM_IS_FILE (item))
{
char *uri_escaped = g_markup_escape_text (FILE_ITEM (item)->uri, -1);
if (uri_escaped)
g_string_append_printf (buffer, "%s<%s %s=\"%s\"/>\n",
indent_s, ELM_FILE, PROP_URI, uri_escaped);
g_free (uri_escaped);
}
else if (ITEM_IS_GROUP (item))
{
char *name_escaped = g_markup_escape_text (GROUP_ITEM (item)->name, -1);
if (name_escaped)
{
GtkTreeIter child;
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (list), &child, iter))
{
g_string_append_printf (buffer, "%s<%s %s=\"%s\">\n",
indent_s, ELM_GROUP, PROP_NAME, name_escaped);
do
{
format_item (list, &child, buffer, indent + 2);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list), &child));
g_string_append_printf (buffer, "%s</%s>\n", indent_s, ELM_GROUP);
}
else
{
g_string_append_printf (buffer, "%s<%s %s=\"%s\"/>\n",
indent_s, ELM_GROUP, PROP_NAME, name_escaped);
}
}
g_free (name_escaped);
}
else
{
g_critical ("%s: oops", G_STRLOC);
}
g_free (indent_s);
}
static void
file_list_save_config (FileList *list,
const char *filename)
{
GtkTreeIter iter;
GString *buffer;
GError *error = NULL;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list), &iter))
{
_moo_unlink (filename);
return;
}
buffer = g_string_new ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
g_string_append (buffer, "<" ELM_CONFIG " " PROP_VERSION "=\"" VALUE_VERSION "\">\n");
do
{
2008-01-07 03:05:43 -06:00
if (file_list_iter_is_auto (list, &iter))
2008-01-02 13:13:24 -06:00
break;
format_item (list, &iter, buffer, 2);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list), &iter));
g_string_append (buffer, "</" ELM_CONFIG ">\n");
if (!g_file_set_contents (filename, buffer->str, buffer->len, &error))
{
g_critical ("%s: could not save file %s: %s",
G_STRLOC, filename, error ? error->message : "");
g_error_free (error);
}
g_string_free (buffer, TRUE);
}
static GtkTreePath *
file_list_add_group (FileList *list,
GtkTreePath *path)
{
GtkTreePath *parent = NULL;
GtkTreeIter parent_iter, new_iter;
GtkTreeIter *piter = NULL;
Group *group;
int index = -1;
if (path)
{
GtkTreeIter iter;
Item *item;
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, path);
item = get_item_at_iter (list, &iter);
if (ITEM_IS_GROUP (item))
{
parent = gtk_tree_path_copy (path);
index = 0;
}
2008-01-07 03:05:43 -06:00
else if (!file_list_iter_is_auto (list, &iter))
2008-01-02 13:13:24 -06:00
{
GtkTreeIter parent_iter;
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (list), &parent_iter, &iter))
{
parent = gtk_tree_path_copy (path);
gtk_tree_path_up (parent);
index = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path)-1] + 1;
}
else
{
index = gtk_tree_path_get_indices (path)[0] + 1;
}
}
}
if (parent)
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &parent_iter, parent);
piter = &parent_iter;
}
if (index < 0)
index = list->n_user_items;
group = group_new ("Group");
file_list_insert_row (list, ITEM (group), &new_iter, piter, index);
item_unref (ITEM (group));
return gtk_tree_model_get_path (GTK_TREE_MODEL (list), &new_iter);
}
static void
2008-01-07 03:05:43 -06:00
file_list_try_remove_item (FileList *list,
GtkTreePath *path)
2008-01-02 13:13:24 -06:00
{
GtkTreeIter iter;
2008-01-07 03:05:43 -06:00
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, path) &&
!file_list_iter_is_auto (list, &iter))
{
file_list_remove_row (list, &iter);
window_plugin_queue_update (list->plugin);
}
2008-01-02 13:13:24 -06:00
}
2008-01-02 18:56:30 -06:00
static void
file_list_remove_items (FileList *list,
GList *paths)
{
GSList *rows = NULL;
while (paths)
{
GtkTreeRowReference *row;
row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list), paths->data);
if (row)
rows = g_slist_prepend (rows, row);
paths = paths->next;
}
rows = g_slist_reverse (rows);
while (rows)
{
GtkTreePath *path = NULL;
if (gtk_tree_row_reference_valid (rows->data))
path = gtk_tree_row_reference_get_path (rows->data);
if (path)
{
2008-01-07 03:05:43 -06:00
file_list_try_remove_item (list, path);
2008-01-02 18:56:30 -06:00
gtk_tree_path_free (path);
}
gtk_tree_row_reference_free (rows->data);
rows = g_slist_delete_link (rows, rows);
}
}
2008-01-02 13:13:24 -06:00
static gboolean
drag_source_row_draggable (G_GNUC_UNUSED GtkTreeDragSource *drag_source,
G_GNUC_UNUSED GtkTreePath *path)
{
return TRUE;
}
static gboolean
drag_source_drag_data_get (GtkTreeDragSource *drag_source,
GtkTreePath *path,
GtkSelectionData *selection_data)
{
if (selection_data->target == atom_tree_model_row)
{
gtk_tree_set_row_drag_data (selection_data, GTK_TREE_MODEL (drag_source), path);
return TRUE;
}
else if (selection_data->target == atom_uri_list)
{
Item *item;
char *uris[2] = {NULL, NULL};
2008-01-02 18:56:30 -06:00
item = get_item_at_path (FILE_LIST (drag_source), path);
2008-01-02 13:13:24 -06:00
if (ITEM_IS_FILE (item))
uris[0] = file_get_uri (FILE_ITEM (item));
if (uris[0])
{
gtk_selection_data_set_uris (selection_data, (char**) uris);
g_free (uris[0]);
return TRUE;
}
}
return FALSE;
}
static gboolean
drag_source_drag_data_delete (G_GNUC_UNUSED GtkTreeDragSource *drag_source,
G_GNUC_UNUSED GtkTreePath *path)
{
return FALSE;
}
static void
copy_row_children (FileList *list,
GtkTreeIter *source,
GtkTreeIter *dest)
{
GtkTreeIter child;
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (list), &child, source))
do
{
GtkTreeIter iter;
Item *item = get_item_at_iter (list, &child);
gtk_tree_store_append (GTK_TREE_STORE (list), &iter, dest);
gtk_tree_store_set (GTK_TREE_STORE (list), &iter,
COLUMN_ITEM, item,
COLUMN_TOOLTIP, item_get_tooltip (item),
-1);
copy_row_children (list, &child, &iter);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list), &child));
}
static void
copy_row (FileList *list,
GtkTreePath *source,
GtkTreePath *parent,
int index)
{
GtkTreeRowReference *source_row;
GtkTreeIter iter, parent_iter;
GtkTreeIter *piter = NULL;
Item *item;
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
item = get_item_at_iter (list, &iter);
g_return_if_fail (item != NULL);
source_row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list), source);
if (parent)
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &parent_iter, parent);
piter = &parent_iter;
}
file_list_insert_row (list, item, &iter, piter, index);
parent_iter = iter;
source = gtk_tree_row_reference_get_path (source_row);
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
copy_row_children (list, &iter, &parent_iter);
gtk_tree_path_free (source);
}
static gboolean
move_row (FileList *list,
GtkTreePath *source,
GtkTreePath *parent,
int index)
{
GtkTreePath *source_parent = NULL;
gboolean same_parent = FALSE;
GtkTreeIter iter;
Item *item;
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
item = get_item_at_iter (list, &iter);
if (ITEM_IS_FILE (item) && FILE_ITEM (item)->doc)
{
char *uri = file_get_uri (FILE_ITEM (item));
if (!uri)
return FALSE;
2008-01-07 03:05:43 -06:00
if (!FILE_ITEM (item)->uri)
file_set_uri (FILE_ITEM (item), uri);
g_object_set_qdata (G_OBJECT (FILE_ITEM (item)->doc),
file_list_row_quark, NULL);
file_set_doc (FILE_ITEM (item), NULL);
2008-01-02 13:13:24 -06:00
g_free (uri);
}
if (gtk_tree_path_get_depth (source) > 1)
{
source_parent = gtk_tree_path_copy (source);
gtk_tree_path_up (source_parent);
}
if (!source_parent && !parent)
same_parent = TRUE;
else if (source_parent && parent && gtk_tree_path_compare (source_parent, parent) == 0)
same_parent = TRUE;
if (same_parent)
{
GtkTreeIter *piter = NULL;
gboolean first_user = FALSE;
if (parent)
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, parent);
piter = &iter;
}
2008-01-07 03:05:43 -06:00
else if (file_list_iter_is_auto (list, &iter))
2008-01-02 13:13:24 -06:00
{
list->n_user_items += 1;
first_user = list->n_user_items == 1;
}
if (index == gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list), piter))
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
gtk_tree_store_move_before (GTK_TREE_STORE (list), &iter, NULL);
}
else
{
GtkTreeIter ch_iter;
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &ch_iter, piter, index);
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
gtk_tree_store_move_before (GTK_TREE_STORE (list), &iter, &ch_iter);
}
if (first_user)
gtk_tree_store_insert (GTK_TREE_STORE (list), &iter,
NULL, list->n_user_items);
}
else
{
GtkTreeRowReference *row;
2008-01-07 03:05:43 -06:00
2008-01-02 13:13:24 -06:00
row = gtk_tree_row_reference_new (GTK_TREE_MODEL (list), source);
2008-01-07 03:05:43 -06:00
2008-01-02 13:13:24 -06:00
copy_row (list, source, parent, index);
2008-01-07 03:05:43 -06:00
2008-01-02 13:13:24 -06:00
source = gtk_tree_row_reference_get_path (row);
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, source);
file_list_remove_row (list, &iter);
gtk_tree_path_free (source);
gtk_tree_row_reference_free (row);
}
if (source_parent)
gtk_tree_path_free (source_parent);
2008-01-07 03:05:43 -06:00
check_list (list);
2008-01-02 13:13:24 -06:00
return TRUE;
}
static gboolean
add_row_from_uri (FileList *list,
const char *uri,
GtkTreePath *parent,
int index)
{
Item *item;
GtkTreeIter iter, dummy;
GtkTreeIter *piter = NULL;
if (parent)
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, parent);
piter = &iter;
}
2008-01-07 03:05:43 -06:00
item = file_new_uri (uri);
2008-01-02 13:13:24 -06:00
file_list_insert_row (list, item, &dummy, piter, index);
item_unref (item);
return TRUE;
}
static gboolean
find_drop_destination (FileList *list,
GtkTreePath *dest,
GtkTreePath **parent_path,
int *index)
{
int n_children;
Group *parent_group = NULL;
GtkTreeIter parent_iter;
*parent_path = NULL;
*index = 0;
if (gtk_tree_path_get_depth (dest) > 1)
{
GtkTreePath *parent;
Item *parent_item;
parent = gtk_tree_path_copy (dest);
gtk_tree_path_up (parent);
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &parent_iter, parent))
{
gtk_tree_path_free (parent);
return FALSE;
}
parent_item = get_item_at_iter (list, &parent_iter);
if (ITEM_IS_GROUP (parent_item))
{
parent_group = GROUP_ITEM (parent_item);
*index = gtk_tree_path_get_indices (dest)[gtk_tree_path_get_depth (dest) - 1];
}
else if (gtk_tree_path_get_depth (parent) > 1)
{
*index = gtk_tree_path_get_indices (parent)[gtk_tree_path_get_depth (parent) - 1];
gtk_tree_path_up (parent);
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &parent_iter, parent))
{
gtk_tree_path_free (parent);
g_return_val_if_reached (FALSE);
}
parent_item = get_item_at_iter (list, &parent_iter);
if (!ITEM_IS_GROUP (parent_item))
{
gtk_tree_path_free (parent);
g_return_val_if_reached (FALSE);
}
parent_group = GROUP_ITEM (parent_item);
}
else
{
parent_group = NULL;
*index = gtk_tree_path_get_indices (parent)[0];
}
gtk_tree_path_free (parent);
}
else
{
*index = gtk_tree_path_get_indices (dest)[0];
}
if (parent_group)
{
n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (list), &parent_iter);
*parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (list), &parent_iter);
}
else
{
n_children = list->n_user_items;
}
*index = CLAMP (*index, 0, n_children);
return TRUE;
}
2008-01-02 18:56:30 -06:00
static gboolean
path_is_descendant (GtkTreePath *path,
GtkTreePath *ancestor)
{
return gtk_tree_path_compare (path, ancestor) == 0 ||
gtk_tree_path_is_descendant (path, ancestor);
}
2008-01-02 13:13:24 -06:00
static gboolean
drop_uris (FileList *list,
GtkTreePath *dest,
char **uris)
{
GtkTreePath *parent_path = NULL;
int index = 0;
if (!find_drop_destination (list, dest, &parent_path, &index))
return FALSE;
for ( ; uris && *uris; ++uris)
{
GtkTreeIter iter;
if (file_list_find_uri (list, *uris, &iter))
{
GtkTreePath *source = gtk_tree_model_get_path (GTK_TREE_MODEL (list), &iter);
2008-01-02 18:56:30 -06:00
if (parent_path && path_is_descendant (parent_path, source))
2008-01-02 13:13:24 -06:00
{
gtk_tree_path_free (source);
continue;
}
if (move_row (list, source, parent_path, index))
index += 1;
gtk_tree_path_free (source);
}
else
{
if (add_row_from_uri (list, *uris, parent_path, index))
index += 1;
}
}
if (parent_path)
gtk_tree_path_free (parent_path);
return TRUE;
}
static gboolean
drop_tree_model_row (FileList *list,
GtkTreePath *dest,
GtkTreePath *source)
{
GtkTreePath *parent_path = NULL;
int index;
gboolean retval;
2008-01-02 18:56:30 -06:00
if (!get_item_at_path (list, source))
2008-01-02 13:13:24 -06:00
return FALSE;
if (!find_drop_destination (list, dest, &parent_path, &index))
return FALSE;
2008-01-02 18:56:30 -06:00
if (parent_path && path_is_descendant (parent_path, source))
2008-01-02 13:13:24 -06:00
{
gtk_tree_path_free (parent_path);
return FALSE;
}
retval = move_row (list, source, parent_path, index);
if (parent_path)
gtk_tree_path_free (parent_path);
return retval;
}
static gboolean
drag_dest_drag_data_received (GtkTreeDragDest *drag_dest,
GtkTreePath *dest,
GtkSelectionData *selection_data)
{
if (selection_data->target == atom_tree_model_row)
{
GtkTreePath *path = NULL;
gboolean retval;
if (!gtk_tree_get_row_drag_data (selection_data, NULL, &path))
return FALSE;
2008-01-07 03:05:43 -06:00
if ((retval = drop_tree_model_row (FILE_LIST (drag_dest), dest, path)))
window_plugin_queue_update (FILE_LIST (drag_dest)->plugin);
2008-01-02 13:13:24 -06:00
gtk_tree_path_free (path);
return retval;
}
else if (selection_data->target == atom_uri_list)
{
char **uris;
gboolean retval;
if (!(uris = gtk_selection_data_get_uris (selection_data)))
return FALSE;
2008-01-07 03:05:43 -06:00
if ((retval = drop_uris (FILE_LIST (drag_dest), dest, uris)))
window_plugin_queue_update (FILE_LIST (drag_dest)->plugin);
2008-01-02 13:13:24 -06:00
g_strfreev (uris);
return retval;
}
return FALSE;
}
static gboolean
drag_dest_row_drop_possible (G_GNUC_UNUSED GtkTreeDragDest *drag_dest,
G_GNUC_UNUSED GtkTreePath *dest_path,
G_GNUC_UNUSED GtkSelectionData *selection_data)
{
return TRUE;
}
2008-01-03 20:27:11 -06:00
gboolean
_moo_str_semicase_compare (const char *string,
const char *key)
{
gboolean has_upper;
const char *p;
g_return_val_if_fail (string != NULL, FALSE);
g_return_val_if_fail (key != NULL, FALSE);
for (p = key, has_upper = FALSE; *p && !has_upper; ++p)
has_upper = g_ascii_isupper (*p);
if (has_upper)
return strncmp (string, key, strlen (key)) == 0;
else
return g_ascii_strncasecmp (string, key, strlen (key)) == 0;
}
2008-01-02 13:13:24 -06:00
static gboolean
row_separator_func (GtkTreeModel *model,
GtkTreeIter *iter)
{
Item *item = get_item_at_iter (FILE_LIST (model), iter);
return item == NULL;
}
2008-01-03 20:27:11 -06:00
static gboolean
tree_view_search_equal_func (GtkTreeModel *model,
G_GNUC_UNUSED int column,
const char *key,
GtkTreeIter *iter)
{
const char *compare_with = NULL;
Item *item;
item = get_item_at_iter (FILE_LIST (model), iter);
if (ITEM_IS_FILE (item))
compare_with = FILE_ITEM (item)->display_basename;
else if (ITEM_IS_GROUP (item))
compare_with = GROUP_ITEM (item)->name;
if (compare_with)
return !_moo_str_semicase_compare (compare_with, key);
else
return TRUE;
}
2008-01-02 13:13:24 -06:00
static void
pixbuf_data_func (G_GNUC_UNUSED GtkTreeViewColumn *column,
GtkCellRenderer *cell,
GtkTreeModel *model,
GtkTreeIter *iter)
{
Item *item = get_item_at_iter (FILE_LIST (model), iter);
if (ITEM_IS_GROUP (item))
g_object_set (cell, "stock-id", GTK_STOCK_DIRECTORY, NULL);
else if (ITEM_IS_FILE (item))
g_object_set (cell, "stock-id", GTK_STOCK_FILE, NULL);
}
static void
text_data_func (G_GNUC_UNUSED GtkTreeViewColumn *column,
GtkCellRenderer *cell,
GtkTreeModel *model,
GtkTreeIter *iter)
{
Item *item = get_item_at_iter (FILE_LIST (model), iter);
if (ITEM_IS_GROUP (item))
g_object_set (cell, "text", GROUP_ITEM (item)->name, NULL);
else if (ITEM_IS_FILE (item))
g_object_set (cell, "text", FILE_ITEM (item)->display_basename, NULL);
}
static void
text_cell_edited (GtkCellRenderer *cell,
const char *path_string,
const char *new_text,
WindowPlugin *plugin)
{
GtkTreeIter iter;
Item *item;
GtkTreePath *path;
g_object_set (cell, "editable", FALSE, NULL);
path = gtk_tree_path_new_from_string (path_string);
g_return_if_fail (path != NULL);
gtk_tree_model_get_iter (GTK_TREE_MODEL (plugin->list), &iter, path);
item = get_item_at_iter (plugin->list, &iter);
g_return_if_fail (ITEM_IS_GROUP (item));
MOO_ASSIGN_STRING (GROUP_ITEM (item)->name, new_text);
gtk_tree_model_row_changed (GTK_TREE_MODEL (plugin->list), path, &iter);
gtk_tree_path_free (path);
}
static void
text_cell_editing_canceled (GtkCellRenderer *cell)
{
g_object_set (cell, "editable", FALSE, NULL);
}
static void
start_edit (WindowPlugin *plugin,
GtkTreePath *path)
{
g_object_set (plugin->text_cell, "editable", TRUE, NULL);
gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (plugin->treeview),
path, plugin->column,
plugin->text_cell,
TRUE);
}
2008-01-02 18:56:30 -06:00
static GList *
get_selected_rows (WindowPlugin *plugin)
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (plugin->treeview));
return gtk_tree_selection_get_selected_rows (selection, NULL);
}
static void
path_list_free (GList *paths)
{
g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
g_list_free (paths);
}
2008-01-02 13:13:24 -06:00
static void
2008-01-02 18:56:30 -06:00
rename_activated (G_GNUC_UNUSED GtkWidget *menuitem,
2008-01-02 13:13:24 -06:00
WindowPlugin *plugin)
{
2008-01-02 18:56:30 -06:00
GList *selected;
2008-01-02 13:13:24 -06:00
Item *item;
2008-01-02 18:56:30 -06:00
selected = get_selected_rows (plugin);
if (!selected || selected->next)
{
path_list_free (selected);
g_return_if_fail (selected && !selected->next);
}
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
item = get_item_at_path (plugin->list, selected->data);
if (!ITEM_IS_GROUP (item))
{
path_list_free (selected);
g_return_if_fail (ITEM_IS_GROUP (item));
}
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
start_edit (plugin, selected->data);
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
path_list_free (selected);
2008-01-02 13:13:24 -06:00
}
static void
2008-01-02 18:56:30 -06:00
add_group_activated (G_GNUC_UNUSED GtkWidget *menuitem,
2008-01-02 13:13:24 -06:00
WindowPlugin *plugin)
{
2008-01-02 18:56:30 -06:00
GtkTreePath *new_path, *path;
GList *selected;
selected = get_selected_rows (plugin);
if (selected)
path = g_list_last (selected)->data;
else
path = NULL;
2008-01-02 13:13:24 -06:00
new_path = file_list_add_group (plugin->list, path);
if (new_path)
{
GtkTreeIter iter, parent;
gtk_tree_model_get_iter (GTK_TREE_MODEL (plugin->list), &iter, new_path);
if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (plugin->list), &parent, &iter))
{
GtkTreePath *parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (plugin->list),
&parent);
if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (plugin->treeview), parent_path))
gtk_tree_view_expand_row (GTK_TREE_VIEW (plugin->treeview), parent_path, FALSE);
gtk_tree_path_free (parent_path);
}
start_edit (plugin, new_path);
gtk_tree_path_free (new_path);
}
2008-01-02 18:56:30 -06:00
path_list_free (selected);
2008-01-02 13:13:24 -06:00
}
static void
2008-01-02 18:56:30 -06:00
remove_activated (G_GNUC_UNUSED GtkWidget *menuitem,
2008-01-02 13:13:24 -06:00
WindowPlugin *plugin)
{
2008-01-02 18:56:30 -06:00
GList *selected;
selected = get_selected_rows (plugin);
file_list_remove_items (plugin->list, selected);
2008-01-07 03:05:43 -06:00
window_plugin_queue_update (plugin);
2008-01-02 18:56:30 -06:00
path_list_free (selected);
}
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
static void
open_file (WindowPlugin *plugin,
GtkTreePath *path)
{
Item *item;
item = get_item_at_path (plugin->list, path);
g_return_if_fail (item != NULL);
if (ITEM_IS_FILE (item))
{
if (FILE_ITEM (item)->doc)
{
moo_editor_set_active_doc (moo_editor_instance (),
FILE_ITEM (item)->doc);
gtk_widget_grab_focus (GTK_WIDGET (FILE_ITEM (item)->doc));
}
else
{
moo_editor_open_uri (moo_editor_instance (),
MOO_WIN_PLUGIN (plugin)->window,
NULL,
FILE_ITEM (item)->uri,
NULL);
}
}
else if (ITEM_IS_GROUP (item))
{
GtkTreeIter iter, child;
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
gtk_tree_model_get_iter (GTK_TREE_MODEL (plugin->list), &iter, path);
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (plugin->list), &child, &iter))
{
do
{
GtkTreePath *child_path;
child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (plugin->list), &child);
open_file (plugin, child_path);
gtk_tree_path_free (child_path);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (plugin->list), &child));
}
}
else
{
g_return_if_reached ();
}
}
static void
open_activated (G_GNUC_UNUSED GtkWidget *menuitem,
WindowPlugin *plugin)
{
GList *selected, *l;
selected = get_selected_rows (plugin);
for (l = selected; l != NULL; l = l->next)
open_file (plugin, l->data);
path_list_free (selected);
}
static gboolean
can_open (G_GNUC_UNUSED FileList *list,
GList *paths)
{
/* XXX */
return paths != NULL;
}
static gboolean
can_remove (FileList *list,
GList *paths)
{
while (paths)
{
GtkTreeIter iter;
GtkTreePath *path = paths->data;
gtk_tree_model_get_iter (GTK_TREE_MODEL (list), &iter, path);
2008-01-07 03:05:43 -06:00
if (!file_list_iter_is_auto (list, &iter))
2008-01-02 18:56:30 -06:00
return TRUE;
paths = paths->next;
}
return FALSE;
2008-01-02 13:13:24 -06:00
}
static void
popup_menu (WindowPlugin *plugin,
2008-01-02 18:56:30 -06:00
GList *selected,
2008-01-02 13:13:24 -06:00
int button,
guint32 time)
{
GtkWidget *menu, *menuitem;
2008-01-02 18:56:30 -06:00
GtkTreePath *single_path;
Item *single_item;
single_path = (selected && !selected->next) ? selected->data : NULL;
single_item = single_path ? get_item_at_path (plugin->list, single_path) : NULL;
2008-01-02 13:13:24 -06:00
menu = gtk_menu_new ();
2008-01-02 18:56:30 -06:00
if (can_open (plugin->list, selected))
{
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_OPEN, NULL);
g_signal_connect (menuitem, "activate", G_CALLBACK (open_activated), plugin);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
2008-01-02 13:13:24 -06:00
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_ADD, NULL);
gtk_label_set_text (GTK_LABEL (GTK_BIN (menuitem)->child), "Add Group");
g_signal_connect (menuitem, "activate", G_CALLBACK (add_group_activated), plugin);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2008-01-02 18:56:30 -06:00
if (selected)
2008-01-02 13:13:24 -06:00
{
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_REMOVE, NULL);
g_signal_connect (menuitem, "activate", G_CALLBACK (remove_activated), plugin);
2008-01-02 18:56:30 -06:00
gtk_widget_set_sensitive (menuitem, can_remove (plugin->list, selected));
2008-01-02 13:13:24 -06:00
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2008-01-02 18:56:30 -06:00
}
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
if (single_item && ITEM_IS_GROUP (single_item))
{
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_EDIT, NULL);
gtk_label_set_text (GTK_LABEL (GTK_BIN (menuitem)->child), "Rename");
g_signal_connect (menuitem, "activate", G_CALLBACK (rename_activated), plugin);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
2008-01-02 13:13:24 -06:00
}
gtk_widget_show_all (menu);
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
}
static gboolean
treeview_button_press (GtkTreeView *treeview,
GdkEventButton *event,
WindowPlugin *plugin)
{
2008-01-02 18:56:30 -06:00
GtkTreeSelection *selection;
2008-01-02 13:13:24 -06:00
GtkTreePath *path = NULL;
2008-01-02 18:56:30 -06:00
GList *selected;
2008-01-02 13:13:24 -06:00
int x, y;
if (event->type != GDK_BUTTON_PRESS || event->button != 3)
return FALSE;
gtk_tree_view_get_path_at_pos (treeview, event->x, event->y,
&path, NULL, &x, &y);
2008-01-02 18:56:30 -06:00
selection = gtk_tree_view_get_selection (treeview);
if (!path)
gtk_tree_selection_unselect_all (selection);
else if (!gtk_tree_selection_path_is_selected (selection, path))
gtk_tree_view_set_cursor (treeview, path, plugin->column, FALSE);
2008-01-02 13:13:24 -06:00
2008-01-02 18:56:30 -06:00
selected = gtk_tree_selection_get_selected_rows (selection, NULL);
popup_menu (plugin, selected, event->button, event->time);
2008-01-02 13:13:24 -06:00
if (path)
gtk_tree_path_free (path);
2008-01-02 18:56:30 -06:00
g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
g_list_free (selected);
2008-01-02 13:13:24 -06:00
return TRUE;
}
static void
treeview_row_activated (WindowPlugin *plugin,
GtkTreePath *path)
2008-01-02 13:13:24 -06:00
{
Item *item;
item = get_item_at_path (plugin->list, path);
2008-01-02 13:13:24 -06:00
g_return_if_fail (item != NULL);
if (ITEM_IS_FILE (item))
open_file (plugin, path);
2008-01-02 13:13:24 -06:00
}
static void
create_treeview (WindowPlugin *plugin)
{
2008-01-02 18:56:30 -06:00
GtkTreeSelection *selection;
2008-01-02 13:13:24 -06:00
GtkCellRenderer *cell;
GtkTargetEntry targets[] = {
{ (char*) "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 },
{ (char*) "text/uri-list", 0, 0 }
};
plugin->treeview = gtk_tree_view_new ();
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (plugin->treeview), FALSE);
gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (plugin->treeview),
(GtkTreeViewRowSeparatorFunc) row_separator_func,
NULL, NULL);
2008-01-03 20:27:11 -06:00
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (plugin->treeview),
(GtkTreeViewSearchEqualFunc) tree_view_search_equal_func,
NULL, NULL);
2008-01-02 13:13:24 -06:00
#if GTK_CHECK_VERSION (2,12,0)
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (plugin->treeview),
COLUMN_TOOLTIP);
#endif
2008-01-02 18:56:30 -06:00
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (plugin->treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2008-01-02 13:13:24 -06:00
g_signal_connect (plugin->treeview, "button-press-event",
G_CALLBACK (treeview_button_press), plugin);
g_signal_connect_swapped (plugin->treeview, "row-activated",
G_CALLBACK (treeview_row_activated), plugin);
2008-01-02 13:13:24 -06:00
gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (plugin->treeview),
targets, G_N_ELEMENTS (targets),
GDK_ACTION_COPY | GDK_ACTION_MOVE |
GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (plugin->treeview),
GDK_BUTTON1_MASK,
targets, G_N_ELEMENTS (targets),
GDK_ACTION_COPY | GDK_ACTION_MOVE |
GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
plugin->column = gtk_tree_view_column_new ();
gtk_tree_view_append_column (GTK_TREE_VIEW (plugin->treeview), plugin->column);
_moo_tree_view_setup_expander (GTK_TREE_VIEW (plugin->treeview),
plugin->column);
2008-01-02 13:13:24 -06:00
cell = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (plugin->column, cell, FALSE);
gtk_tree_view_column_set_cell_data_func (plugin->column, cell,
(GtkTreeCellDataFunc) pixbuf_data_func,
NULL, NULL);
plugin->text_cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (plugin->column, plugin->text_cell, TRUE);
gtk_tree_view_column_set_cell_data_func (plugin->column, plugin->text_cell,
(GtkTreeCellDataFunc) text_data_func,
NULL, NULL);
g_signal_connect (plugin->text_cell, "edited",
G_CALLBACK (text_cell_edited), plugin);
g_signal_connect (plugin->text_cell, "editing-canceled",
G_CALLBACK (text_cell_editing_canceled), plugin);
}
static void
create_model (WindowPlugin *plugin)
{
plugin->list = g_object_new (file_list_get_type (), NULL);
2008-01-07 03:05:43 -06:00
plugin->list->plugin = plugin;
2008-01-02 13:13:24 -06:00
file_list_load_config (plugin->list, plugin->filename);
gtk_tree_view_set_model (GTK_TREE_VIEW (plugin->treeview),
GTK_TREE_MODEL (plugin->list));
}
2008-01-07 03:05:43 -06:00
static int
compare_docs_by_name (MooEdit *doc1,
MooEdit *doc2)
{
const char *name1, *name2;
char *key1, *key2;
int retval;
name1 = moo_edit_get_display_basename (doc1);
name2 = moo_edit_get_display_basename (doc2);
key1 = g_utf8_collate_key_for_filename (name1, -1);
key2 = g_utf8_collate_key_for_filename (name2, -1);
retval = strcmp (key1, key2);
g_free (key2);
g_free (key1);
return retval;
}
2008-01-02 13:13:24 -06:00
static gboolean
do_update (WindowPlugin *plugin)
{
GSList *docs;
plugin->update_idle = 0;
docs = moo_edit_window_list_docs (MOO_WIN_PLUGIN (plugin)->window);
2008-01-07 03:05:43 -06:00
docs = g_slist_sort (docs, (GCompareFunc) compare_docs_by_name);
file_list_update (plugin->list, docs);
2008-01-02 13:13:24 -06:00
g_slist_free (docs);
if (plugin->first_time_show)
{
plugin->first_time_show = FALSE;
gtk_tree_view_expand_all (GTK_TREE_VIEW (plugin->treeview));
_moo_tree_view_select_first (GTK_TREE_VIEW (plugin->treeview));
2008-01-02 13:13:24 -06:00
}
return FALSE;
}
static void
2008-01-07 03:05:43 -06:00
window_plugin_queue_update (WindowPlugin *plugin)
2008-01-02 13:13:24 -06:00
{
2008-01-07 03:05:43 -06:00
g_return_if_fail (plugin != NULL);
2008-01-02 13:13:24 -06:00
if (!plugin->update_idle)
plugin->update_idle = g_idle_add ((GSourceFunc) do_update,
plugin);
}
static gboolean
file_list_window_plugin_create (WindowPlugin *plugin)
{
2008-01-02 20:00:37 -06:00
MooPane *pane;
2008-01-02 13:13:24 -06:00
MooPaneLabel *label;
GtkWidget *scrolled_window;
2008-01-02 20:00:37 -06:00
MooEditWindow *window;
window = MOO_WIN_PLUGIN (plugin)->window;
g_return_val_if_fail (MOO_IS_EDIT_WINDOW (window), FALSE);
2008-01-02 13:13:24 -06:00
plugin->filename = moo_get_named_user_data_file (CONFIG_FILE);
2008-01-02 13:13:24 -06:00
create_treeview (plugin);
create_model (plugin);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolled_window), plugin->treeview);
gtk_widget_show_all (scrolled_window);
label = moo_pane_label_new (GTK_STOCK_DIRECTORY,
NULL, _("File List"),
_("File List"));
2008-01-02 20:00:37 -06:00
moo_edit_window_add_pane (window,
2008-01-02 13:13:24 -06:00
FILE_LIST_PLUGIN_ID,
scrolled_window, label,
MOO_PANE_POS_RIGHT);
moo_pane_label_free (label);
2008-01-02 20:00:37 -06:00
pane = moo_big_paned_find_pane (window->paned,
GTK_WIDGET (scrolled_window), NULL);
moo_pane_set_drag_dest (pane);
2008-01-02 13:13:24 -06:00
plugin->first_time_show = TRUE;
2008-01-02 20:00:37 -06:00
g_signal_connect_swapped (window, "new-doc",
2008-01-07 03:05:43 -06:00
G_CALLBACK (window_plugin_queue_update), plugin);
2008-01-02 20:00:37 -06:00
g_signal_connect_swapped (window, "close-doc",
2008-01-07 03:05:43 -06:00
G_CALLBACK (window_plugin_queue_update), plugin);
window_plugin_queue_update (plugin);
2008-01-02 13:13:24 -06:00
return TRUE;
}
static void
file_list_window_plugin_destroy (WindowPlugin *plugin)
{
file_list_save_config (plugin->list, plugin->filename);
2008-01-07 03:05:43 -06:00
file_list_shutdown (plugin->list);
g_object_unref (plugin->list);
2008-01-02 13:13:24 -06:00
moo_edit_window_remove_pane (MOO_WIN_PLUGIN (plugin)->window,
FILE_LIST_PLUGIN_ID);
g_signal_handlers_disconnect_by_func (MOO_WIN_PLUGIN (plugin)->window,
2008-01-07 03:05:43 -06:00
(gpointer) window_plugin_queue_update,
2008-01-02 13:13:24 -06:00
plugin);
2008-01-07 03:05:43 -06:00
g_free (plugin->filename);
2008-01-02 13:13:24 -06:00
}
static gboolean
file_list_plugin_init (G_GNUC_UNUSED FileListPlugin *plugin)
2008-01-02 13:13:24 -06:00
{
return TRUE;
}
static void
file_list_plugin_deinit (G_GNUC_UNUSED FileListPlugin *plugin)
2008-01-02 13:13:24 -06:00
{
}
MOO_PLUGIN_DEFINE_INFO (file_list,
N_("File List"), N_("List of files"),
"Yevgen Muntyan <muntyan@tamu.edu>",
MOO_VERSION, NULL)
MOO_WIN_PLUGIN_DEFINE (FileList, file_list)
MOO_PLUGIN_DEFINE_FULL (FileList, file_list,
NULL, NULL, NULL, NULL, NULL,
file_list_window_plugin_get_type (), 0)
gboolean
_moo_file_list_plugin_init (void)
{
2008-01-05 04:21:14 -06:00
MooPluginParams params = {TRUE, TRUE};
2008-01-02 13:13:24 -06:00
return moo_plugin_register (FILE_LIST_PLUGIN_ID,
file_list_plugin_get_type (),
&file_list_plugin_info,
&params);
}