* Evolution-Storage.idl (StorageListener): add notifyHasSubfolders, to announce that a folder has currently- unknown subfolders. (Storage): add asyncOpenFolder, to request that previously- announced subfolders be filled in. * evolution-storage.c (impl_Storage_async_open_folder): emit OPEN_FOLDER. (evolution_storage_has_subfolders): Implement by calling notifyHasSubfolders on all of its listeners. * evolution-storage-listener.c (impl_GNOME_Evolution_StorageListener_notifyHasSubfolders): emit HAS_SUBFOLDERS. * e-corba-storage.c (impl_StorageListener_notifyHasSubfolders): Implement by calling e_storage_has_subfolders. (async_open_folder): Implement by calling asyncOpenFolder on the CORBA storage. * e-storage.c (EStoragePrivate, init, destroy): Keep a list of pseudofolders representing un-filled-in subtrees. (impl_async_open_folder): No-op default implementation (e_storage_async_open_folder): New function to request that un-filled-in subtrees be filled in. (e_storage_new_folder): If the new folder's parent has an "un-filled-in children" pseudofolder, remove it. (e_storage_has_subfolders): New function to note that a folder has unknown children. If the folder previously was marked as having real children, remove them, and emit CLOSE_FOLDER to reset it back to an a "unknown subfolders" state. * e-storage-set.c (make_full_path): Make this deal with path being "/", since that case gets used from storage_close_folder_cb sometimes. (storage_close_folder_cb): Proxy EStorage's CLOSE_FOLDER signal. (storage_set_view_folder_opened): Handle EStorageSetView's FOLDER_OPENED signal by calling e_storage_async_open_folder. * e-storage-set-view.c (etree_fill_in_children): If the given node is its parent's first child, emit FOLDER_OPENED for the parent. (close_folder_cb): Handler for EStorageSet's CLOSE_FOLDER signal. Ask the model to close that node. (e_storage_set_view_construct): Set the default expanded state for the tree to FALSE rather than TRUE, to prevent unwanted expansion of delayed nodes. (This only affects the very first time the tree is displayed anyway: after that its state is loaded off disk.) * e-shell.c (e_shell_construct): Register the "noselect" type with the folder type registry, so icon lookups on placeholder folders will work. svn path=/trunk/; revision=16169
2206 lines
59 KiB
C
2206 lines
59 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
||
/* e-storage-set-view.c
|
||
*
|
||
* Copyright (C) 2000, 2001, 2002 Ximian, Inc.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of version 2 of the GNU General Public
|
||
* License as published by the Free Software Foundation.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public
|
||
* License along with this program; if not, write to the
|
||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
* Boston, MA 02111-1307, USA.
|
||
*
|
||
* Author: Ettore Perazzoli
|
||
* Etree-ification: Chris Toshok
|
||
*/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
#include <config.h>
|
||
#endif
|
||
|
||
#include "e-storage-set-view.h"
|
||
|
||
#include <glib.h>
|
||
#include <gnome.h>
|
||
#include <libgnome/gnome-defs.h>
|
||
#include <libgnome/gnome-util.h>
|
||
#include <gal/util/e-util.h>
|
||
#include <gal/widgets/e-gui-utils.h>
|
||
#include <gal/e-table/e-tree-memory-callbacks.h>
|
||
#include <gal/e-table/e-cell-tree.h>
|
||
#include <gal/e-table/e-cell-text.h>
|
||
#include <gal/unicode/gunicode.h>
|
||
|
||
#include <libgnome/gnome-util.h>
|
||
|
||
#include "e-util/e-gtk-utils.h"
|
||
|
||
#include "e-shell-constants.h"
|
||
|
||
|
||
/*#define DEBUG_XML*/
|
||
|
||
#define ROOT_NODE_NAME "/RootNode"
|
||
|
||
|
||
/* This is used on the source side to define the two basic types that we always
|
||
export. */
|
||
enum _DndTargetTypeIdx {
|
||
EVOLUTION_PATH_TARGET_TYPE_IDX = 0,
|
||
E_SHORTCUT_TARGET_TYPE_IDX = 1
|
||
};
|
||
typedef enum _DndTargetTypeIdx DndTargetTypeIdx;
|
||
|
||
#define EVOLUTION_PATH_TARGET_TYPE "_EVOLUTION_PRIVATE_PATH"
|
||
#define E_SHORTCUT_TARGET_TYPE "E-SHORTCUT"
|
||
|
||
|
||
#define PARENT_TYPE E_TREE_TYPE
|
||
static ETreeClass *parent_class = NULL;
|
||
|
||
struct _EStorageSetViewPrivate {
|
||
EStorageSet *storage_set;
|
||
|
||
BonoboUIContainer *container;
|
||
|
||
ETreeModel *etree_model;
|
||
ETreePath root_node;
|
||
|
||
GHashTable *path_to_etree_node;
|
||
|
||
GHashTable *type_name_to_pixbuf;
|
||
|
||
/* Path of the row selected by the latest "cursor_activated" signal. */
|
||
char *selected_row_path;
|
||
|
||
/* Path of the row selected by a right click. */
|
||
char *right_click_row_path;
|
||
|
||
unsigned int show_folders : 1;
|
||
unsigned int allow_dnd : 1;
|
||
|
||
/* The `Evolution::ShellComponentDnd::SourceFolder' interface for the
|
||
folder we are dragging from, or CORBA_OBJECT_NIL if no dragging is
|
||
happening. */
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder drag_corba_source_interface;
|
||
|
||
/* Source context information. NULL if no dragging is in progress. */
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_Context *drag_corba_source_context;
|
||
|
||
/* The data. */
|
||
GNOME_Evolution_ShellComponentDnd_Data *drag_corba_data;
|
||
};
|
||
|
||
|
||
enum {
|
||
FOLDER_SELECTED,
|
||
FOLDER_OPENED,
|
||
DND_ACTION,
|
||
FOLDER_CONTEXT_MENU_POPPING_UP,
|
||
FOLDER_CONTEXT_MENU_POPPED_DOWN,
|
||
LAST_SIGNAL
|
||
};
|
||
|
||
static unsigned int signals[LAST_SIGNAL] = { 0 };
|
||
|
||
|
||
/* DND stuff. */
|
||
|
||
enum _DndTargetType {
|
||
DND_TARGET_TYPE_URI_LIST,
|
||
DND_TARGET_TYPE_E_SHORTCUT
|
||
};
|
||
typedef enum _DndTargetType DndTargetType;
|
||
|
||
#define URI_LIST_TYPE "text/uri-list"
|
||
#define E_SHORTCUT_TYPE "E-SHORTCUT"
|
||
|
||
|
||
/* Sorting callbacks. */
|
||
|
||
static int
|
||
storage_sort_callback (ETreeMemory *etmm,
|
||
ETreePath node1,
|
||
ETreePath node2,
|
||
void *closure)
|
||
{
|
||
char *folder_path1;
|
||
char *folder_path2;
|
||
gboolean path1_local;
|
||
gboolean path2_local;
|
||
|
||
folder_path1 = e_tree_memory_node_get_data(etmm, node1);
|
||
folder_path2 = e_tree_memory_node_get_data(etmm, node2);
|
||
|
||
/* FIXME bad hack to put the "my evolution" and "local" storages on
|
||
top. */
|
||
|
||
if (strcmp (folder_path1, G_DIR_SEPARATOR_S E_SUMMARY_STORAGE_NAME) == 0)
|
||
return -1;
|
||
if (strcmp (folder_path2, G_DIR_SEPARATOR_S E_SUMMARY_STORAGE_NAME) == 0)
|
||
return +1;
|
||
|
||
path1_local = ! strcmp (folder_path1, G_DIR_SEPARATOR_S E_LOCAL_STORAGE_NAME);
|
||
path2_local = ! strcmp (folder_path2, G_DIR_SEPARATOR_S E_LOCAL_STORAGE_NAME);
|
||
|
||
if (path1_local && path2_local)
|
||
return 0;
|
||
if (path1_local)
|
||
return -1;
|
||
if (path2_local)
|
||
return 1;
|
||
|
||
return g_utf8_collate (e_tree_model_value_at (E_TREE_MODEL (etmm), node1, 0),
|
||
e_tree_model_value_at (E_TREE_MODEL (etmm), node2, 0));
|
||
}
|
||
|
||
static int
|
||
folder_sort_callback (ETreeMemory *etmm, ETreePath path1, ETreePath path2, gpointer closure)
|
||
{
|
||
return g_utf8_collate (e_tree_model_value_at (E_TREE_MODEL (etmm), path1, 0),
|
||
e_tree_model_value_at (E_TREE_MODEL (etmm), path2, 0));
|
||
}
|
||
|
||
|
||
|
||
/* Helper functions. */
|
||
|
||
static gboolean
|
||
add_node_to_hash (EStorageSetView *storage_set_view,
|
||
const char *path,
|
||
ETreePath node)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
char *hash_path;
|
||
|
||
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
if (g_hash_table_lookup (priv->path_to_etree_node, path) != NULL) {
|
||
g_warning ("EStorageSetView: Node already existing while adding -- %s", path);
|
||
return FALSE;
|
||
}
|
||
|
||
hash_path = g_strdup (path);
|
||
|
||
g_hash_table_insert (priv->path_to_etree_node, hash_path, node);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static ETreePath
|
||
lookup_node_in_hash (EStorageSetView *storage_set_view,
|
||
const char *path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath node;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
node = g_hash_table_lookup (priv->path_to_etree_node, path);
|
||
if (node == NULL)
|
||
g_warning ("EStorageSetView: Node not found while updating -- %s", path);
|
||
|
||
return node;
|
||
}
|
||
|
||
static ETreePath
|
||
remove_node_from_hash (EStorageSetView *storage_set_view,
|
||
const char *path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath node;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
node = g_hash_table_lookup (priv->path_to_etree_node, path);
|
||
if (node == NULL) {
|
||
g_warning ("EStorageSetView: Node not found while removing -- %s", path);
|
||
return NULL;
|
||
}
|
||
|
||
g_hash_table_remove (priv->path_to_etree_node, path);
|
||
|
||
return node;
|
||
}
|
||
|
||
static GdkPixbuf *
|
||
get_pixbuf_for_folder (EStorageSetView *storage_set_view,
|
||
EFolder *folder)
|
||
{
|
||
GdkPixbuf *scaled_pixbuf;
|
||
const char *type_name;
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
type_name = e_folder_get_type_string (folder);
|
||
|
||
scaled_pixbuf = g_hash_table_lookup (priv->type_name_to_pixbuf, type_name);
|
||
|
||
if (scaled_pixbuf == NULL) {
|
||
EFolderTypeRegistry *folder_type_registry;
|
||
EStorageSet *storage_set;
|
||
GdkPixbuf *icon_pixbuf;
|
||
int icon_pixbuf_width, icon_pixbuf_height;
|
||
|
||
storage_set = priv->storage_set;
|
||
folder_type_registry = e_storage_set_get_folder_type_registry (storage_set);
|
||
|
||
icon_pixbuf = e_folder_type_registry_get_icon_for_type (folder_type_registry,
|
||
type_name, TRUE);
|
||
|
||
if (icon_pixbuf == NULL)
|
||
return NULL;
|
||
|
||
icon_pixbuf_width = gdk_pixbuf_get_width (icon_pixbuf);
|
||
icon_pixbuf_height = gdk_pixbuf_get_height (icon_pixbuf);
|
||
|
||
if (icon_pixbuf_width == E_SHELL_MINI_ICON_SIZE && icon_pixbuf_height == E_SHELL_MINI_ICON_SIZE) {
|
||
scaled_pixbuf = gdk_pixbuf_ref (icon_pixbuf);
|
||
} else {
|
||
scaled_pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (icon_pixbuf),
|
||
gdk_pixbuf_get_has_alpha (icon_pixbuf),
|
||
gdk_pixbuf_get_bits_per_sample (icon_pixbuf),
|
||
E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE);
|
||
|
||
gdk_pixbuf_scale (icon_pixbuf, scaled_pixbuf,
|
||
0, 0, E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE,
|
||
0.0, 0.0,
|
||
(double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_width (icon_pixbuf),
|
||
(double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_height (icon_pixbuf),
|
||
GDK_INTERP_HYPER);
|
||
}
|
||
|
||
g_hash_table_insert (priv->type_name_to_pixbuf, g_strdup (type_name), scaled_pixbuf);
|
||
}
|
||
|
||
return scaled_pixbuf;
|
||
}
|
||
|
||
static EFolder *
|
||
get_folder_at_node (EStorageSetView *storage_set_view,
|
||
ETreePath path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
const char *folder_path;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
if (path == NULL)
|
||
return NULL;
|
||
|
||
folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path);
|
||
g_assert (folder_path != NULL);
|
||
|
||
return e_storage_set_get_folder (priv->storage_set, folder_path);
|
||
}
|
||
|
||
static EvolutionShellComponentClient *
|
||
get_component_at_node (EStorageSetView *storage_set_view,
|
||
ETreePath path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
EvolutionShellComponentClient *component_client;
|
||
EFolderTypeRegistry *folder_type_registry;
|
||
EFolder *folder;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
folder = get_folder_at_node (storage_set_view, path);
|
||
if (folder == NULL)
|
||
return NULL;
|
||
|
||
folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
|
||
g_assert (folder_type_registry != NULL);
|
||
|
||
component_client = e_folder_type_registry_get_handler_for_type (folder_type_registry,
|
||
e_folder_get_type_string (folder));
|
||
|
||
return component_client;
|
||
}
|
||
|
||
static GNOME_Evolution_ShellComponentDnd_ActionSet
|
||
convert_gdk_drag_action_set_to_corba (GdkDragAction action)
|
||
{
|
||
GNOME_Evolution_ShellComponentDnd_Action retval;
|
||
|
||
retval = GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
|
||
|
||
if (action & GDK_ACTION_COPY)
|
||
retval |= GNOME_Evolution_ShellComponentDnd_ACTION_COPY;
|
||
if (action & GDK_ACTION_MOVE)
|
||
retval |= GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
|
||
if (action & GDK_ACTION_LINK)
|
||
retval |= GNOME_Evolution_ShellComponentDnd_ACTION_LINK;
|
||
if (action & GDK_ACTION_ASK)
|
||
retval |= GNOME_Evolution_ShellComponentDnd_ACTION_ASK;
|
||
|
||
return retval;
|
||
}
|
||
|
||
static GNOME_Evolution_ShellComponentDnd_ActionSet
|
||
convert_gdk_drag_action_to_corba (GdkDragAction action)
|
||
{
|
||
if (action == GDK_ACTION_COPY)
|
||
return GNOME_Evolution_ShellComponentDnd_ACTION_COPY;
|
||
else if (action == GDK_ACTION_MOVE)
|
||
return GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
|
||
else if (action == GDK_ACTION_LINK)
|
||
return GNOME_Evolution_ShellComponentDnd_ACTION_LINK;
|
||
else if (action == GDK_ACTION_ASK)
|
||
return GNOME_Evolution_ShellComponentDnd_ACTION_ASK;
|
||
else {
|
||
g_warning ("Unknown GdkDragAction %d", action);
|
||
return GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
|
||
}
|
||
}
|
||
|
||
static GdkDragAction
|
||
convert_corba_drag_action_to_gdk (GNOME_Evolution_ShellComponentDnd_ActionSet action)
|
||
{
|
||
if (action == GNOME_Evolution_ShellComponentDnd_ACTION_COPY)
|
||
return GDK_ACTION_COPY;
|
||
else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE)
|
||
return GDK_ACTION_MOVE;
|
||
else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_LINK)
|
||
return GDK_ACTION_LINK;
|
||
else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_ASK)
|
||
return GDK_ACTION_ASK;
|
||
else {
|
||
g_warning ("unknown GNOME_Evolution_ShellComponentDnd_ActionSet %d", action);
|
||
return GDK_ACTION_DEFAULT;
|
||
}
|
||
}
|
||
|
||
/* This will look for the targets in @drag_context, choose one that matches
|
||
with the allowed types at @path, and return its name. The EVOLUTION_PATH
|
||
type always matches. */
|
||
static const char *
|
||
find_matching_target_for_drag_context (EStorageSetView *storage_set_view,
|
||
ETreePath path,
|
||
GdkDragContext *drag_context)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
EFolderTypeRegistry *folder_type_registry;
|
||
EFolder *folder;
|
||
GList *accepted_types;
|
||
GList *p, *q;
|
||
|
||
priv = storage_set_view->priv;
|
||
folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
|
||
|
||
folder = get_folder_at_node (storage_set_view, path);
|
||
if (folder == NULL)
|
||
return EVOLUTION_PATH_TARGET_TYPE;
|
||
|
||
accepted_types = e_folder_type_registry_get_accepted_dnd_types_for_type (folder_type_registry,
|
||
e_folder_get_type_string (folder));
|
||
|
||
/* FIXME? We might make this more efficient. Currently it takes `n *
|
||
m' string compares, where `n' is the number of targets in the
|
||
@drag_context, and `m' is the number of supported types in
|
||
@folder. */
|
||
|
||
for (p = drag_context->targets; p != NULL; p = p->next) {
|
||
char *possible_type;
|
||
|
||
possible_type = gdk_atom_name ((GdkAtom) p->data);
|
||
if (strcmp (possible_type, EVOLUTION_PATH_TARGET_TYPE) == 0) {
|
||
g_free (possible_type);
|
||
return EVOLUTION_PATH_TARGET_TYPE;
|
||
}
|
||
|
||
for (q = accepted_types; q != NULL; q = q->next) {
|
||
const char *accepted_type;
|
||
|
||
accepted_type = (const char *) q->data;
|
||
if (strcmp (possible_type, accepted_type) == 0) {
|
||
g_free (possible_type);
|
||
return accepted_type;
|
||
}
|
||
}
|
||
|
||
g_free (possible_type);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
/* Custom marshalling function. */
|
||
|
||
typedef void (* GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING) (GtkObject *object,
|
||
GdkDragContext *action,
|
||
const char *,
|
||
const char *,
|
||
const char *);
|
||
|
||
static void
|
||
marshal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING (GtkObject *object,
|
||
GtkSignalFunc func,
|
||
void *func_data,
|
||
GtkArg *args)
|
||
{
|
||
GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING rfunc;
|
||
|
||
rfunc = (GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING) func;
|
||
(* rfunc) (object,
|
||
GTK_VALUE_POINTER (args[0]),
|
||
GTK_VALUE_STRING (args[1]),
|
||
GTK_VALUE_STRING (args[2]),
|
||
GTK_VALUE_STRING (args[3]));
|
||
}
|
||
|
||
|
||
/* DnD selection setup stuff. */
|
||
|
||
/* This will create an array of GtkTargetEntries from the specified list of DND
|
||
types. The type name will *not* be allocated in the list, as this is
|
||
supposed to be used only temporarily to set up the cell as a drag source. */
|
||
static GtkTargetEntry *
|
||
create_target_entries_from_dnd_type_list (GList *dnd_types,
|
||
int *num_entries_return)
|
||
{
|
||
GtkTargetEntry *entries;
|
||
GList *p;
|
||
int num_entries;
|
||
int i;
|
||
|
||
if (dnd_types == NULL)
|
||
num_entries = 0;
|
||
else
|
||
num_entries = g_list_length (dnd_types);
|
||
|
||
/* We always add two entries, one for an Evolution URI type, and one
|
||
for e-shortcuts. This will let us do drag & drop within Evolution
|
||
at least. */
|
||
num_entries += 2;
|
||
|
||
entries = g_new (GtkTargetEntry, num_entries);
|
||
|
||
i = 0;
|
||
|
||
/* The Evolution URI will always come first. */
|
||
entries[i].target = EVOLUTION_PATH_TARGET_TYPE;
|
||
entries[i].flags = 0;
|
||
entries[i].info = i;
|
||
g_assert (i == EVOLUTION_PATH_TARGET_TYPE_IDX);
|
||
i ++;
|
||
|
||
/* ...Then the shortcut type. */
|
||
entries[i].target = E_SHORTCUT_TARGET_TYPE;
|
||
entries[i].flags = 0;
|
||
entries[i].info = i;
|
||
g_assert (i == E_SHORTCUT_TARGET_TYPE_IDX);
|
||
i ++;
|
||
|
||
for (p = dnd_types; p != NULL; p = p->next, i++) {
|
||
const char *dnd_type;
|
||
|
||
g_assert (i < num_entries);
|
||
|
||
dnd_type = (const char *) p->data;
|
||
|
||
entries[i].target = (char *) dnd_type;
|
||
entries[i].flags = 0;
|
||
entries[i].info = i;
|
||
}
|
||
|
||
*num_entries_return = num_entries;
|
||
return entries;
|
||
}
|
||
|
||
static void
|
||
free_target_entries (GtkTargetEntry *entries)
|
||
{
|
||
g_assert (entries != NULL);
|
||
|
||
/* The target names are not strdup()ed so a simple free will do. */
|
||
g_free (entries);
|
||
}
|
||
|
||
static GtkTargetList *
|
||
create_target_list_for_node (EStorageSetView *storage_set_view,
|
||
ETreePath node)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
GtkTargetList *target_list;
|
||
EFolderTypeRegistry *folder_type_registry;
|
||
GList *exported_dnd_types;
|
||
GtkTargetEntry *target_entries;
|
||
EFolder *folder;
|
||
const char *folder_type;
|
||
int num_target_entries;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
|
||
|
||
folder = get_folder_at_node (storage_set_view, node);
|
||
folder_type = e_folder_get_type_string (folder);
|
||
|
||
exported_dnd_types = e_folder_type_registry_get_exported_dnd_types_for_type (folder_type_registry,
|
||
folder_type);
|
||
|
||
target_entries = create_target_entries_from_dnd_type_list (exported_dnd_types,
|
||
&num_target_entries);
|
||
g_assert (target_entries != NULL);
|
||
|
||
target_list = gtk_target_list_new (target_entries, num_target_entries);
|
||
|
||
free_target_entries (target_entries);
|
||
|
||
return target_list;
|
||
}
|
||
|
||
static void
|
||
set_e_shortcut_selection (EStorageSetView *storage_set_view,
|
||
GtkSelectionData *selection_data)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath node;
|
||
EFolder *folder;
|
||
int shortcut_len;
|
||
char *shortcut;
|
||
const char *name;
|
||
const char *folder_path;
|
||
|
||
g_assert (storage_set_view != NULL);
|
||
g_assert (selection_data != NULL);
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
node = lookup_node_in_hash (storage_set_view, priv->selected_row_path);
|
||
|
||
folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), node);
|
||
g_assert (folder_path != NULL);
|
||
|
||
folder = e_storage_set_get_folder (priv->storage_set, folder_path);
|
||
if (folder != NULL)
|
||
name = e_folder_get_name (folder);
|
||
else
|
||
name = NULL;
|
||
|
||
/* FIXME: Get `evolution:' from somewhere instead of hardcoding it here. */
|
||
|
||
if (name != NULL)
|
||
shortcut_len = strlen (name);
|
||
else
|
||
shortcut_len = 0;
|
||
|
||
shortcut_len ++; /* Separating zero. */
|
||
|
||
shortcut_len += strlen ("evolution:");
|
||
shortcut_len += strlen (priv->selected_row_path);
|
||
shortcut_len ++; /* Trailing zero. */
|
||
|
||
shortcut = g_malloc (shortcut_len);
|
||
|
||
if (name == NULL)
|
||
sprintf (shortcut, "%cevolution:%s", '\0', priv->selected_row_path);
|
||
else
|
||
sprintf (shortcut, "%s%cevolution:%s", name, '\0', priv->selected_row_path);
|
||
|
||
gtk_selection_data_set (selection_data, selection_data->target,
|
||
8, (guchar *) shortcut, shortcut_len);
|
||
|
||
g_free (shortcut);
|
||
}
|
||
|
||
static void
|
||
set_evolution_path_selection (EStorageSetView *storage_set_view,
|
||
GtkSelectionData *selection_data)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
g_assert (storage_set_view != NULL);
|
||
g_assert (selection_data != NULL);
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
gtk_selection_data_set (selection_data, selection_data->target,
|
||
8, (guchar *) priv->selected_row_path, strlen (priv->selected_row_path) + 1);
|
||
}
|
||
|
||
|
||
/* Callbacks for folder operations. */
|
||
|
||
static void
|
||
folder_xfer_callback (EStorageSet *storage_set,
|
||
EStorageResult result,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
|
||
if (result != E_STORAGE_OK)
|
||
e_notice (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (storage_set_view))),
|
||
GNOME_MESSAGE_BOX_ERROR,
|
||
_("Cannot transfer folder:\n%s"),
|
||
e_storage_result_to_string (result));
|
||
}
|
||
|
||
|
||
/* Folder context menu. */
|
||
|
||
static void
|
||
popup_folder_menu (EStorageSetView *storage_set_view,
|
||
GdkEventButton *event)
|
||
{
|
||
EvolutionShellComponentClient *handler;
|
||
EStorageSetViewPrivate *priv;
|
||
EFolderTypeRegistry *folder_type_registry;
|
||
EFolder *folder;
|
||
GtkWidget *menu;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
folder = e_storage_set_get_folder (priv->storage_set, priv->right_click_row_path);
|
||
if (folder == NULL) {
|
||
/* Uh!? */
|
||
return;
|
||
}
|
||
|
||
folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
|
||
g_assert (folder_type_registry != NULL);
|
||
|
||
handler = e_folder_type_registry_get_handler_for_type (folder_type_registry,
|
||
e_folder_get_type_string (folder));
|
||
if (handler == NULL)
|
||
return;
|
||
|
||
menu = gtk_menu_new ();
|
||
bonobo_window_add_popup (bonobo_ui_container_get_win (priv->container),
|
||
GTK_MENU (menu), "/popups/FolderPopup");
|
||
|
||
evolution_shell_component_client_populate_folder_context_menu (handler,
|
||
priv->container,
|
||
e_folder_get_physical_uri (folder),
|
||
e_folder_get_type_string (folder));
|
||
|
||
gtk_widget_show (GTK_WIDGET (menu));
|
||
|
||
gnome_popup_menu_do_popup_modal (GTK_WIDGET (menu), NULL, NULL, event, NULL);
|
||
|
||
gtk_widget_destroy (GTK_WIDGET (menu));
|
||
|
||
e_tree_right_click_up (E_TREE (storage_set_view));
|
||
}
|
||
|
||
|
||
/* GtkObject methods. */
|
||
|
||
static void
|
||
pixbuf_free_func (gpointer key, gpointer value, gpointer user_data)
|
||
{
|
||
g_free (key);
|
||
gdk_pixbuf_unref ((GdkPixbuf*)value);
|
||
}
|
||
|
||
static void
|
||
impl_destroy (GtkObject *object)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (object);
|
||
priv = storage_set_view->priv;
|
||
|
||
/* need to destroy our tree */
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), priv->root_node);
|
||
gtk_object_unref (GTK_OBJECT (priv->etree_model));
|
||
|
||
/* the data in the hash table was all freed by freeing the tree */
|
||
g_hash_table_destroy (priv->path_to_etree_node);
|
||
|
||
/* now free up all the type_names and pixbufs stored in the
|
||
hash table and destroy the hash table itself */
|
||
g_hash_table_foreach (priv->type_name_to_pixbuf, pixbuf_free_func, NULL);
|
||
g_hash_table_destroy (priv->type_name_to_pixbuf);
|
||
|
||
gtk_object_unref (GTK_OBJECT (priv->storage_set));
|
||
|
||
if (priv->drag_corba_source_interface != CORBA_OBJECT_NIL) {
|
||
CORBA_Environment ev;
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
g_assert (priv->drag_corba_source_context != NULL);
|
||
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_endDrag (priv->drag_corba_source_interface,
|
||
priv->drag_corba_source_context,
|
||
&ev);
|
||
|
||
Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
|
||
CORBA_Object_release (priv->drag_corba_source_interface, &ev);
|
||
|
||
CORBA_exception_free (&ev);
|
||
}
|
||
|
||
if (priv->drag_corba_source_context != NULL)
|
||
CORBA_free (priv->drag_corba_source_context);
|
||
|
||
if (priv->drag_corba_data != NULL)
|
||
CORBA_free (priv->drag_corba_data);
|
||
|
||
g_free (priv->selected_row_path);
|
||
g_free (priv->right_click_row_path);
|
||
|
||
g_free (priv);
|
||
|
||
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
||
}
|
||
|
||
|
||
/* ETree methods. */
|
||
|
||
/* -- Source-side DnD. */
|
||
|
||
static gint
|
||
impl_tree_start_drag (ETree *tree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkEvent *event)
|
||
{
|
||
GdkDragContext *context;
|
||
GtkTargetList *target_list;
|
||
GdkDragAction actions;
|
||
EStorageSetView *storage_set_view;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (tree);
|
||
|
||
if (! storage_set_view->priv->allow_dnd)
|
||
return FALSE;
|
||
|
||
target_list = create_target_list_for_node (storage_set_view, path);
|
||
if (target_list == NULL)
|
||
return FALSE;
|
||
|
||
actions = GDK_ACTION_MOVE | GDK_ACTION_COPY;
|
||
|
||
context = e_tree_drag_begin (tree, row, col,
|
||
target_list,
|
||
actions,
|
||
1, event);
|
||
|
||
gtk_drag_set_icon_default (context);
|
||
|
||
gtk_target_list_unref (target_list);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_begin (ETree *etree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
EFolder *folder;
|
||
EvolutionShellComponentClient *component_client;
|
||
GNOME_Evolution_ShellComponent corba_component;
|
||
GNOME_Evolution_ShellComponentDnd_ActionSet possible_actions;
|
||
GNOME_Evolution_ShellComponentDnd_Action suggested_action;
|
||
CORBA_Environment ev;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (etree);
|
||
priv = storage_set_view->priv;
|
||
|
||
g_free (priv->selected_row_path);
|
||
priv->selected_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path));
|
||
|
||
g_assert (priv->drag_corba_source_interface == CORBA_OBJECT_NIL);
|
||
|
||
folder = get_folder_at_node (storage_set_view, path);
|
||
component_client = get_component_at_node (storage_set_view, path);
|
||
|
||
if (component_client == NULL)
|
||
return;
|
||
|
||
/* Query the `ShellComponentDnd::SourceFolder' interface on the
|
||
component. */
|
||
/* FIXME we could use the new
|
||
`evolution_shell_component_client_get_dnd_source_interface()'
|
||
call. */
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
corba_component = bonobo_object_corba_objref (BONOBO_OBJECT (component_client));
|
||
priv->drag_corba_source_interface = Bonobo_Unknown_queryInterface (corba_component,
|
||
"IDL:GNOME/Evolution/ShellComponentDnd/SourceFolder:1.0",
|
||
&ev);
|
||
if (ev._major != CORBA_NO_EXCEPTION
|
||
|| priv->drag_corba_source_interface == CORBA_OBJECT_NIL) {
|
||
priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
|
||
|
||
CORBA_exception_free (&ev);
|
||
return;
|
||
}
|
||
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_beginDrag (priv->drag_corba_source_interface,
|
||
e_folder_get_physical_uri (folder),
|
||
e_folder_get_type_string (folder),
|
||
&possible_actions,
|
||
&suggested_action,
|
||
&ev);
|
||
|
||
if (ev._major != CORBA_NO_EXCEPTION) {
|
||
Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
|
||
CORBA_Object_release (priv->drag_corba_source_interface, &ev);
|
||
|
||
priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
|
||
|
||
CORBA_exception_free (&ev);
|
||
return;
|
||
}
|
||
|
||
CORBA_exception_free (&ev);
|
||
|
||
if (priv->drag_corba_source_context != NULL)
|
||
CORBA_free (priv->drag_corba_source_context);
|
||
|
||
priv->drag_corba_source_context = GNOME_Evolution_ShellComponentDnd_SourceFolder_Context__alloc ();
|
||
priv->drag_corba_source_context->physicalUri = CORBA_string_dup (e_folder_get_physical_uri (folder));
|
||
priv->drag_corba_source_context->folderType = CORBA_string_dup (e_folder_get_type_string (folder));
|
||
priv->drag_corba_source_context->possibleActions = possible_actions;
|
||
priv->drag_corba_source_context->suggestedAction = suggested_action;
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_end (ETree *tree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
CORBA_Environment ev;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (tree);
|
||
priv = storage_set_view->priv;
|
||
|
||
if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
|
||
return;
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_endDrag (priv->drag_corba_source_interface,
|
||
priv->drag_corba_source_context,
|
||
&ev);
|
||
|
||
CORBA_free (priv->drag_corba_source_context);
|
||
priv->drag_corba_source_context = NULL;
|
||
|
||
Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
|
||
CORBA_Object_release (priv->drag_corba_source_interface, &ev);
|
||
|
||
CORBA_exception_free (&ev);
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_data_get (ETree *etree,
|
||
int drag_row,
|
||
ETreePath drag_path,
|
||
int drag_col,
|
||
GdkDragContext *context,
|
||
GtkSelectionData *selection_data,
|
||
unsigned int info,
|
||
guint32 time)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
CORBA_Environment ev;
|
||
char *target_type;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (etree);
|
||
priv = storage_set_view->priv;
|
||
|
||
switch (info) {
|
||
case E_SHORTCUT_TARGET_TYPE_IDX:
|
||
set_e_shortcut_selection (storage_set_view, selection_data);
|
||
return;
|
||
case EVOLUTION_PATH_TARGET_TYPE_IDX:
|
||
set_evolution_path_selection (storage_set_view, selection_data);
|
||
return;
|
||
}
|
||
|
||
g_assert (info > 0);
|
||
|
||
if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
|
||
return;
|
||
|
||
target_type = gdk_atom_name ((GdkAtom) context->targets->data);
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_getData (priv->drag_corba_source_interface,
|
||
priv->drag_corba_source_context,
|
||
convert_gdk_drag_action_set_to_corba (context->action),
|
||
target_type,
|
||
& priv->drag_corba_data,
|
||
&ev);
|
||
|
||
if (ev._major != CORBA_NO_EXCEPTION)
|
||
gtk_selection_data_set (selection_data, selection_data->target, 8, "", -1);
|
||
else
|
||
gtk_selection_data_set (selection_data,
|
||
priv->drag_corba_data->target,
|
||
priv->drag_corba_data->format,
|
||
priv->drag_corba_data->bytes._buffer,
|
||
priv->drag_corba_data->bytes._length);
|
||
|
||
g_free (target_type);
|
||
|
||
CORBA_exception_free (&ev);
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_data_delete (ETree *tree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
CORBA_Environment ev;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (tree);
|
||
priv = storage_set_view->priv;
|
||
|
||
if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
|
||
return;
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
GNOME_Evolution_ShellComponentDnd_SourceFolder_deleteData (priv->drag_corba_source_interface,
|
||
priv->drag_corba_source_context,
|
||
&ev);
|
||
|
||
CORBA_exception_free (&ev);
|
||
}
|
||
|
||
/* -- Destination-side DnD. */
|
||
|
||
static gboolean
|
||
handle_evolution_path_drag_motion (EStorageSetView *storage_set_view,
|
||
ETreePath path,
|
||
int row,
|
||
GdkDragContext *context,
|
||
unsigned int time)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
GdkModifierType modifiers;
|
||
GdkDragAction action;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
gdk_window_get_pointer (NULL, NULL, NULL, &modifiers);
|
||
|
||
if ((modifiers & GDK_CONTROL_MASK) != 0) {
|
||
action = GDK_ACTION_COPY;
|
||
} else {
|
||
GtkWidget *source_widget;
|
||
|
||
action = GDK_ACTION_MOVE;
|
||
|
||
source_widget = gtk_drag_get_source_widget (context);
|
||
if (source_widget != NULL
|
||
&& E_IS_STORAGE_SET_VIEW (storage_set_view)) {
|
||
const char *source_path;
|
||
|
||
source_path = e_storage_set_view_get_current_folder (storage_set_view);
|
||
if (source_path != NULL) {
|
||
EFolder *folder;
|
||
int source_path_len;
|
||
const char *destination_path_base;
|
||
char *destination_path;
|
||
|
||
folder = e_storage_set_get_folder (priv->storage_set, source_path);
|
||
if (folder != NULL && e_folder_get_is_stock (folder))
|
||
return FALSE;
|
||
|
||
source_path_len = strlen (path);
|
||
destination_path_base = e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model), path);
|
||
if (strcmp (destination_path_base, source_path) == 0)
|
||
return FALSE;
|
||
|
||
destination_path = g_strconcat (destination_path_base, "/", g_basename (source_path), NULL);
|
||
|
||
if (strncmp (destination_path, source_path, source_path_len) == 0) {
|
||
g_free (destination_path);
|
||
return FALSE;
|
||
}
|
||
|
||
g_free (destination_path);
|
||
}
|
||
}
|
||
}
|
||
|
||
e_tree_drag_highlight (E_TREE (storage_set_view), row, -1);
|
||
|
||
gdk_drag_status (context, action, time);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
impl_tree_drag_motion (ETree *tree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context,
|
||
int x,
|
||
int y,
|
||
unsigned int time)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EFolder *folder;
|
||
EStorageSetViewPrivate *priv;
|
||
EvolutionShellComponentClient *component_client;
|
||
GNOME_Evolution_ShellComponentDnd_DestinationFolder destination_folder_interface;
|
||
GNOME_Evolution_ShellComponentDnd_Action suggested_action;
|
||
GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context corba_context;
|
||
CORBA_boolean can_handle;
|
||
CORBA_Environment ev;
|
||
const char *dnd_type;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (tree);
|
||
priv = storage_set_view->priv;
|
||
|
||
if (! priv->allow_dnd)
|
||
return FALSE;
|
||
|
||
path = e_tree_node_at_row (E_TREE (storage_set_view), row);
|
||
|
||
dnd_type = find_matching_target_for_drag_context (storage_set_view, path, context);
|
||
if (dnd_type == NULL)
|
||
return FALSE;
|
||
|
||
if (strcmp (dnd_type, EVOLUTION_PATH_TARGET_TYPE) == 0)
|
||
return handle_evolution_path_drag_motion (storage_set_view, path, row, context, time);
|
||
|
||
component_client = get_component_at_node (storage_set_view, path);
|
||
if (component_client == NULL)
|
||
return FALSE;
|
||
|
||
destination_folder_interface = evolution_shell_component_client_get_dnd_destination_interface (component_client);
|
||
if (destination_folder_interface == NULL)
|
||
return FALSE;
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
corba_context.dndType = (char *) dnd_type; /* (Safe cast, as we don't actually free the corba_context.) */
|
||
corba_context.possibleActions = convert_gdk_drag_action_set_to_corba (context->actions);
|
||
corba_context.suggestedAction = convert_gdk_drag_action_to_corba (context->suggested_action);
|
||
|
||
folder = get_folder_at_node (storage_set_view, path);
|
||
|
||
can_handle = GNOME_Evolution_ShellComponentDnd_DestinationFolder_handleMotion (destination_folder_interface,
|
||
e_folder_get_physical_uri (folder),
|
||
e_folder_get_type_string (folder),
|
||
&corba_context,
|
||
&suggested_action,
|
||
&ev);
|
||
if (ev._major != CORBA_NO_EXCEPTION || ! can_handle) {
|
||
CORBA_exception_free (&ev);
|
||
return FALSE;
|
||
}
|
||
|
||
CORBA_exception_free (&ev);
|
||
|
||
e_tree_drag_highlight (tree, row, -1);
|
||
|
||
gdk_drag_status (context, convert_corba_drag_action_to_gdk (suggested_action), time);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_leave (ETree *etree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context,
|
||
unsigned int time)
|
||
{
|
||
e_tree_drag_unhighlight (etree);
|
||
}
|
||
|
||
static gboolean
|
||
impl_tree_drag_drop (ETree *etree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context,
|
||
int x,
|
||
int y,
|
||
unsigned int time)
|
||
{
|
||
e_tree_drag_unhighlight (etree);
|
||
if (context->targets != NULL) {
|
||
gtk_drag_get_data (GTK_WIDGET (etree), context,
|
||
GPOINTER_TO_INT (context->targets->data),
|
||
time);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
impl_tree_drag_data_received (ETree *etree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkDragContext *context,
|
||
int x,
|
||
int y,
|
||
GtkSelectionData *selection_data,
|
||
unsigned int info,
|
||
unsigned int time)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
gboolean handled = FALSE;
|
||
char *target_type;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (etree);
|
||
priv = storage_set_view->priv;
|
||
|
||
if (selection_data->data == NULL && selection_data->length == -1)
|
||
return;
|
||
|
||
target_type = gdk_atom_name (selection_data->target);
|
||
|
||
if (strcmp (target_type, EVOLUTION_PATH_TARGET_TYPE) == 0) {
|
||
const char *source_path;
|
||
const char *destination_folder_path;
|
||
char *destination_path;
|
||
|
||
g_free (target_type);
|
||
source_path = (const char *) selection_data->data;
|
||
/* (Basic sanity checks.) */
|
||
if (source_path == NULL || source_path[0] != G_DIR_SEPARATOR || source_path[1] == '\0')
|
||
return;
|
||
|
||
destination_folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model), path);
|
||
if (destination_folder_path == NULL)
|
||
return;
|
||
|
||
destination_path = g_concat_dir_and_file (destination_folder_path,
|
||
g_basename (source_path));
|
||
|
||
|
||
switch (context->action) {
|
||
case GDK_ACTION_MOVE:
|
||
e_storage_set_async_xfer_folder (priv->storage_set, source_path, destination_path, TRUE,
|
||
folder_xfer_callback, storage_set_view);
|
||
handled = TRUE;
|
||
break;
|
||
case GDK_ACTION_COPY:
|
||
e_storage_set_async_xfer_folder (priv->storage_set, source_path, destination_path, FALSE,
|
||
folder_xfer_callback, storage_set_view);
|
||
handled = TRUE;
|
||
break;
|
||
default:
|
||
handled = FALSE;
|
||
g_warning ("EStorageSetView: Unknown action %d", context->action);
|
||
}
|
||
|
||
g_free (destination_path);
|
||
} else {
|
||
GNOME_Evolution_ShellComponentDnd_DestinationFolder destination_folder_interface;
|
||
GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context corba_context;
|
||
GNOME_Evolution_ShellComponentDnd_Data corba_data;
|
||
EvolutionShellComponentClient *component_client;
|
||
const char *target_path;
|
||
|
||
target_path = e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path);
|
||
|
||
component_client = get_component_at_node (storage_set_view, path);
|
||
if (component_client != NULL) {
|
||
destination_folder_interface = evolution_shell_component_client_get_dnd_destination_interface (component_client);
|
||
if (destination_folder_interface != NULL) {
|
||
EFolder *folder;
|
||
CORBA_Environment ev;
|
||
|
||
CORBA_exception_init (&ev);
|
||
|
||
|
||
folder = get_folder_at_node (storage_set_view, path);
|
||
|
||
corba_context.dndType = (char *) target_type;
|
||
corba_context.possibleActions = convert_gdk_drag_action_set_to_corba (context->actions);
|
||
corba_context.suggestedAction = convert_gdk_drag_action_to_corba (context->suggested_action);
|
||
|
||
corba_data.format = selection_data->format;
|
||
corba_data.target = selection_data->target;
|
||
|
||
corba_data.bytes._release = FALSE;
|
||
|
||
if (selection_data->data == NULL) {
|
||
/* If data is NULL the length is -1 and this would mess things
|
||
up so we handle it separately. */
|
||
corba_data.bytes._length = 0;
|
||
corba_data.bytes._buffer = NULL;
|
||
} else {
|
||
corba_data.bytes._length = selection_data->length;
|
||
corba_data.bytes._buffer = selection_data->data;
|
||
}
|
||
|
||
/* pass off the data to the component's DestinationFolderInterface */
|
||
handled = GNOME_Evolution_ShellComponentDnd_DestinationFolder_handleDrop (destination_folder_interface,
|
||
e_folder_get_physical_uri (folder),
|
||
e_folder_get_type_string (folder),
|
||
&corba_context,
|
||
convert_gdk_drag_action_to_corba (context->action),
|
||
&corba_data,
|
||
&ev);
|
||
|
||
}
|
||
}
|
||
g_free (target_type);
|
||
}
|
||
|
||
gtk_drag_finish (context, handled, FALSE, time);
|
||
}
|
||
|
||
static gboolean
|
||
impl_right_click (ETree *etree,
|
||
int row,
|
||
ETreePath path,
|
||
int col,
|
||
GdkEvent *event)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (etree);
|
||
priv = storage_set_view->priv;
|
||
|
||
/* This should never happen, but you never know with ETree. */
|
||
if (priv->right_click_row_path != NULL)
|
||
g_free (priv->right_click_row_path);
|
||
priv->right_click_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path));
|
||
|
||
if (priv->container) {
|
||
gtk_signal_emit (GTK_OBJECT (storage_set_view),
|
||
signals[FOLDER_CONTEXT_MENU_POPPING_UP],
|
||
priv->right_click_row_path);
|
||
|
||
popup_folder_menu (storage_set_view, (GdkEventButton *) event);
|
||
|
||
gtk_signal_emit (GTK_OBJECT (storage_set_view),
|
||
signals[FOLDER_CONTEXT_MENU_POPPED_DOWN]);
|
||
}
|
||
|
||
g_free (priv->right_click_row_path);
|
||
priv->right_click_row_path = NULL;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
impl_cursor_activated (ETree *tree,
|
||
int row,
|
||
ETreePath path)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (tree);
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
g_free (priv->selected_row_path);
|
||
if (path) {
|
||
priv->selected_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model), path));
|
||
|
||
gtk_signal_emit (GTK_OBJECT (storage_set_view), signals[FOLDER_SELECTED],
|
||
priv->selected_row_path);
|
||
}
|
||
else
|
||
priv->selected_row_path = NULL;
|
||
}
|
||
|
||
|
||
/* ETreeModel Methods */
|
||
|
||
static gboolean
|
||
path_is_storage (ETreeModel *etree,
|
||
ETreePath tree_path)
|
||
{
|
||
return e_tree_model_node_depth (etree, tree_path) == 1;
|
||
}
|
||
|
||
static GdkPixbuf*
|
||
etree_icon_at (ETreeModel *etree,
|
||
ETreePath tree_path,
|
||
void *model_data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSet *storage_set;
|
||
EFolder *folder;
|
||
char *path;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (model_data);
|
||
storage_set = storage_set_view->priv->storage_set;
|
||
|
||
path = (char*) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), tree_path);
|
||
|
||
folder = e_storage_set_get_folder (storage_set, path);
|
||
if (folder == NULL)
|
||
return NULL;
|
||
|
||
/* No icon for a storage with children (or with no real root folder) */
|
||
if (path_is_storage (etree, tree_path)) {
|
||
EStorage *storage;
|
||
GList *subfolder_paths;
|
||
|
||
if (! strcmp (e_folder_get_type_string (folder), "noselect"))
|
||
return NULL;
|
||
|
||
storage = e_storage_set_get_storage (storage_set, path + 1);
|
||
subfolder_paths = e_storage_get_subfolder_paths (storage, "/");
|
||
if (subfolder_paths != NULL) {
|
||
e_free_string_list (subfolder_paths);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
return get_pixbuf_for_folder (storage_set_view, folder);
|
||
}
|
||
|
||
/* This function returns the number of columns in our ETreeModel. */
|
||
static int
|
||
etree_column_count (ETreeModel *etc,
|
||
void *data)
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
static gboolean
|
||
etree_has_save_id (ETreeModel *etm,
|
||
void *data)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
static gchar *
|
||
etree_get_save_id (ETreeModel *etm,
|
||
ETreePath node,
|
||
void *model_data)
|
||
{
|
||
return g_strdup(e_tree_memory_node_get_data (E_TREE_MEMORY(etm), node));
|
||
}
|
||
|
||
static gboolean
|
||
etree_has_get_node_by_id (ETreeModel *etm,
|
||
void *data)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
static ETreePath
|
||
etree_get_node_by_id (ETreeModel *etm,
|
||
const char *save_id,
|
||
void *model_data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
storage_set_view = E_STORAGE_SET_VIEW (model_data);
|
||
|
||
return g_hash_table_lookup (storage_set_view->priv->path_to_etree_node, save_id);
|
||
}
|
||
|
||
static void *
|
||
etree_value_at (ETreeModel *etree,
|
||
ETreePath tree_path,
|
||
int col,
|
||
void *model_data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSet *storage_set;
|
||
EFolder *folder;
|
||
char *path;
|
||
const char *folder_name;
|
||
int unread_count;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (model_data);
|
||
storage_set = storage_set_view->priv->storage_set;
|
||
|
||
/* Storages are always highlighted. */
|
||
if (path_is_storage (etree, tree_path) && col == 1)
|
||
return (void *) TRUE;
|
||
|
||
path = (char *) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), tree_path);
|
||
|
||
folder = e_storage_set_get_folder (storage_set, path);
|
||
if (folder == NULL)
|
||
return (void *) "?";
|
||
|
||
if (col == 1)
|
||
return (void *) e_folder_get_highlighted (folder);
|
||
|
||
folder_name = e_folder_get_name (folder);
|
||
unread_count = e_folder_get_unread_count (folder);
|
||
|
||
if (unread_count > 0) {
|
||
char *name_with_unread;
|
||
|
||
name_with_unread = g_strdup_printf ("%s (%d)", folder_name,
|
||
unread_count);
|
||
gtk_object_set_data_full (GTK_OBJECT (folder),
|
||
"name_with_unread",
|
||
name_with_unread, g_free);
|
||
|
||
return (void *) name_with_unread;
|
||
} else
|
||
return (void *) folder_name;
|
||
}
|
||
|
||
static void
|
||
etree_fill_in_children (ETreeModel *etree,
|
||
ETreePath tree_path,
|
||
void *model_data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSet *storage_set;
|
||
ETreePath *parent;
|
||
char *path;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (model_data);
|
||
storage_set = storage_set_view->priv->storage_set;
|
||
|
||
parent = e_tree_model_node_get_parent (etree, tree_path);
|
||
path = (char *) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), parent);
|
||
if (tree_path == e_tree_model_node_get_first_child (etree, parent)) {
|
||
gtk_signal_emit (GTK_OBJECT (storage_set_view),
|
||
signals[FOLDER_OPENED], path);
|
||
}
|
||
}
|
||
|
||
static void
|
||
etree_set_value_at (ETreeModel *etree,
|
||
ETreePath path,
|
||
int col,
|
||
const void *val,
|
||
void *model_data)
|
||
{
|
||
/* nada */
|
||
}
|
||
|
||
static gboolean
|
||
etree_is_editable (ETreeModel *etree,
|
||
ETreePath path,
|
||
int col,
|
||
void *model_data)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
/* This function duplicates the value passed to it. */
|
||
static void *
|
||
etree_duplicate_value (ETreeModel *etc,
|
||
int col,
|
||
const void *value,
|
||
void *data)
|
||
{
|
||
if (col == 0)
|
||
return (void *)g_strdup (value);
|
||
else
|
||
return (void *)value;
|
||
}
|
||
|
||
/* This function frees the value passed to it. */
|
||
static void
|
||
etree_free_value (ETreeModel *etc,
|
||
int col,
|
||
void *value,
|
||
void *data)
|
||
{
|
||
if (col == 0)
|
||
g_free (value);
|
||
}
|
||
|
||
/* This function creates an empty value. */
|
||
static void *
|
||
etree_initialize_value (ETreeModel *etc,
|
||
int col,
|
||
void *data)
|
||
{
|
||
if (col == 0)
|
||
return g_strdup ("");
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
/* This function reports if a value is empty. */
|
||
static gboolean
|
||
etree_value_is_empty (ETreeModel *etc,
|
||
int col,
|
||
const void *value,
|
||
void *data)
|
||
{
|
||
if (col == 0)
|
||
return !(value && *(char *)value);
|
||
else
|
||
return !value;
|
||
}
|
||
|
||
/* This function reports if a value is empty. */
|
||
static char *
|
||
etree_value_to_string (ETreeModel *etc,
|
||
int col,
|
||
const void *value,
|
||
void *data)
|
||
{
|
||
if (col == 0)
|
||
return g_strdup(value);
|
||
else
|
||
return g_strdup(value ? "Yes" : "No");
|
||
}
|
||
|
||
static void
|
||
etree_node_destroy_func (void *data,
|
||
void *user_data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
char *path;
|
||
|
||
path = (char *) data;
|
||
storage_set_view = E_STORAGE_SET_VIEW (user_data);
|
||
|
||
if (strcmp (path, ROOT_NODE_NAME))
|
||
remove_node_from_hash (storage_set_view, path);
|
||
g_free (path);
|
||
}
|
||
|
||
|
||
/* StorageSet signal handling. */
|
||
|
||
static void
|
||
new_storage_cb (EStorageSet *storage_set,
|
||
EStorage *storage,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath node;
|
||
char *path;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
|
||
path = g_strconcat (G_DIR_SEPARATOR_S, e_storage_get_name (storage), NULL);
|
||
|
||
node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), priv->root_node, -1, path);
|
||
e_tree_memory_sort_node (E_TREE_MEMORY(priv->etree_model), priv->root_node, storage_sort_callback, storage_set_view);
|
||
|
||
if (! add_node_to_hash (storage_set_view, path, node)) {
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), node);
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void
|
||
removed_storage_cb (EStorageSet *storage_set,
|
||
EStorage *storage,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath node;
|
||
char *path;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
path = g_strconcat (G_DIR_SEPARATOR_S, e_storage_get_name (storage), NULL);
|
||
node = lookup_node_in_hash (storage_set_view, path);
|
||
g_free (path);
|
||
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(etree), node);
|
||
}
|
||
|
||
static void
|
||
new_folder_cb (EStorageSet *storage_set,
|
||
const char *path,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath parent_node;
|
||
ETreePath new_node;
|
||
const char *last_separator;
|
||
char *parent_path;
|
||
char *copy_of_path;
|
||
|
||
g_return_if_fail (g_path_is_absolute (path));
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
last_separator = strrchr (path, G_DIR_SEPARATOR);
|
||
|
||
parent_path = g_strndup (path, last_separator - path);
|
||
parent_node = g_hash_table_lookup (priv->path_to_etree_node, parent_path);
|
||
if (parent_node == NULL) {
|
||
g_warning ("EStorageSetView: EStorageSet reported new subfolder for non-existing folder -- %s",
|
||
parent_path);
|
||
g_free (parent_path);
|
||
return;
|
||
}
|
||
|
||
g_free (parent_path);
|
||
|
||
copy_of_path = g_strdup (path);
|
||
new_node = e_tree_memory_node_insert (E_TREE_MEMORY(etree), parent_node, -1, copy_of_path);
|
||
e_tree_memory_sort_node (E_TREE_MEMORY(etree), parent_node, folder_sort_callback, storage_set_view);
|
||
|
||
if (! add_node_to_hash (storage_set_view, path, new_node)) {
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(etree), new_node);
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void
|
||
updated_folder_cb (EStorageSet *storage_set,
|
||
const char *path,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath node;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
node = lookup_node_in_hash (storage_set_view, path);
|
||
e_tree_model_pre_change (etree);
|
||
e_tree_model_node_data_changed (etree, node);
|
||
}
|
||
|
||
static void
|
||
removed_folder_cb (EStorageSet *storage_set,
|
||
const char *path,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath node;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
node = lookup_node_in_hash (storage_set_view, path);
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(etree), node);
|
||
}
|
||
|
||
static void
|
||
close_folder_cb (EStorageSet *storage_set,
|
||
const char *path,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath node;
|
||
|
||
storage_set_view = E_STORAGE_SET_VIEW (data);
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
node = lookup_node_in_hash (storage_set_view, path);
|
||
e_tree_model_node_request_collapse (priv->etree_model, node);
|
||
}
|
||
|
||
|
||
static void
|
||
class_init (EStorageSetViewClass *klass)
|
||
{
|
||
GtkObjectClass *object_class;
|
||
ETreeClass *etree_class;
|
||
|
||
parent_class = gtk_type_class (e_tree_get_type ());
|
||
|
||
object_class = GTK_OBJECT_CLASS (klass);
|
||
object_class->destroy = impl_destroy;
|
||
|
||
etree_class = E_TREE_CLASS (klass);
|
||
etree_class->right_click = impl_right_click;
|
||
etree_class->cursor_activated = impl_cursor_activated;
|
||
etree_class->start_drag = impl_tree_start_drag;
|
||
etree_class->tree_drag_begin = impl_tree_drag_begin;
|
||
etree_class->tree_drag_end = impl_tree_drag_end;
|
||
etree_class->tree_drag_data_get = impl_tree_drag_data_get;
|
||
etree_class->tree_drag_data_delete = impl_tree_drag_data_delete;
|
||
etree_class->tree_drag_motion = impl_tree_drag_motion;
|
||
etree_class->tree_drag_drop = impl_tree_drag_drop;
|
||
etree_class->tree_drag_leave = impl_tree_drag_leave;
|
||
etree_class->tree_drag_data_received = impl_tree_drag_data_received;
|
||
|
||
signals[FOLDER_SELECTED]
|
||
= gtk_signal_new ("folder_selected",
|
||
GTK_RUN_FIRST,
|
||
object_class->type,
|
||
GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_selected),
|
||
gtk_marshal_NONE__STRING,
|
||
GTK_TYPE_NONE, 1,
|
||
GTK_TYPE_STRING);
|
||
|
||
signals[FOLDER_OPENED]
|
||
= gtk_signal_new ("folder_opened",
|
||
GTK_RUN_FIRST,
|
||
object_class->type,
|
||
GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_opened),
|
||
gtk_marshal_NONE__STRING,
|
||
GTK_TYPE_NONE, 1,
|
||
GTK_TYPE_STRING);
|
||
|
||
signals[DND_ACTION]
|
||
= gtk_signal_new ("dnd_action",
|
||
GTK_RUN_FIRST,
|
||
object_class->type,
|
||
GTK_SIGNAL_OFFSET (EStorageSetViewClass, dnd_action),
|
||
marshal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING,
|
||
GTK_TYPE_NONE, 4,
|
||
GTK_TYPE_GDK_DRAG_CONTEXT,
|
||
GTK_TYPE_STRING,
|
||
GTK_TYPE_STRING,
|
||
GTK_TYPE_STRING);
|
||
|
||
signals[FOLDER_CONTEXT_MENU_POPPING_UP]
|
||
= gtk_signal_new ("folder_context_menu_popping_up",
|
||
GTK_RUN_FIRST,
|
||
object_class->type,
|
||
GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_context_menu_popping_up),
|
||
gtk_marshal_NONE__STRING,
|
||
GTK_TYPE_NONE, 1,
|
||
GTK_TYPE_STRING);
|
||
|
||
signals[FOLDER_CONTEXT_MENU_POPPED_DOWN]
|
||
= gtk_signal_new ("folder_context_menu_popped_down",
|
||
GTK_RUN_FIRST,
|
||
object_class->type,
|
||
GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_context_menu_popped_down),
|
||
gtk_marshal_NONE__NONE,
|
||
GTK_TYPE_NONE, 0);
|
||
|
||
gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
|
||
}
|
||
|
||
static void
|
||
init (EStorageSetView *storage_set_view)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
priv = g_new (EStorageSetViewPrivate, 1);
|
||
|
||
priv->storage_set = NULL;
|
||
priv->path_to_etree_node = g_hash_table_new (g_str_hash, g_str_equal);
|
||
priv->type_name_to_pixbuf = g_hash_table_new (g_str_hash, g_str_equal);
|
||
|
||
priv->selected_row_path = NULL;
|
||
priv->right_click_row_path = NULL;
|
||
|
||
priv->show_folders = TRUE;
|
||
priv->allow_dnd = TRUE;
|
||
|
||
priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
|
||
|
||
priv->drag_corba_source_context = NULL;
|
||
priv->drag_corba_data = NULL;
|
||
|
||
storage_set_view->priv = priv;
|
||
}
|
||
|
||
|
||
/* Handling of the "changed" signal in EFolders displayed in the EStorageSetView. */
|
||
|
||
struct _FolderChangedCallbackData {
|
||
EStorageSetView *storage_set_view;
|
||
char *path;
|
||
};
|
||
typedef struct _FolderChangedCallbackData FolderChangedCallbackData;
|
||
|
||
static void
|
||
folder_changed_callback_data_destroy_notify (void *data)
|
||
{
|
||
FolderChangedCallbackData *callback_data;
|
||
|
||
callback_data = (FolderChangedCallbackData *) data;
|
||
|
||
g_free (callback_data->path);
|
||
g_free (callback_data);
|
||
}
|
||
|
||
static void
|
||
folder_changed_cb (EFolder *folder,
|
||
void *data)
|
||
{
|
||
EStorageSetView *storage_set_view;
|
||
EStorageSetViewPrivate *priv;
|
||
FolderChangedCallbackData *callback_data;
|
||
ETreePath node;
|
||
|
||
callback_data = (FolderChangedCallbackData *) data;
|
||
|
||
storage_set_view = callback_data->storage_set_view;
|
||
priv = callback_data->storage_set_view->priv;
|
||
|
||
node = g_hash_table_lookup (priv->path_to_etree_node, callback_data->path);
|
||
if (node == NULL) {
|
||
g_warning ("EStorageSetView -- EFolder::changed emitted for a folder whose path I don't know.");
|
||
return;
|
||
}
|
||
|
||
e_tree_model_pre_change (priv->etree_model);
|
||
e_tree_model_node_data_changed (priv->etree_model, node);
|
||
}
|
||
|
||
|
||
static void
|
||
insert_folders (EStorageSetView *storage_set_view,
|
||
ETreePath parent,
|
||
EStorage *storage,
|
||
const char *path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreeModel *etree;
|
||
ETreePath node;
|
||
GList *folder_path_list;
|
||
GList *p;
|
||
const char *storage_name;
|
||
|
||
priv = storage_set_view->priv;
|
||
etree = priv->etree_model;
|
||
|
||
storage_name = e_storage_get_name (storage);
|
||
|
||
folder_path_list = e_storage_get_subfolder_paths (storage, path);
|
||
if (folder_path_list == NULL)
|
||
return;
|
||
|
||
for (p = folder_path_list; p != NULL; p = p->next) {
|
||
FolderChangedCallbackData *folder_changed_callback_data;
|
||
EFolder *folder;
|
||
const char *folder_name;
|
||
const char *folder_path;
|
||
char *full_path;
|
||
|
||
folder_path = (const char *) p->data;
|
||
folder = e_storage_get_folder (storage, folder_path);
|
||
folder_name = e_folder_get_name (folder);
|
||
|
||
full_path = g_strconcat ("/", storage_name, folder_path, NULL);
|
||
node = e_tree_memory_node_insert (E_TREE_MEMORY(etree), parent, -1, (void *) full_path);
|
||
e_tree_memory_sort_node(E_TREE_MEMORY(etree), parent, folder_sort_callback, storage_set_view);
|
||
add_node_to_hash (storage_set_view, full_path, node);
|
||
|
||
insert_folders (storage_set_view, node, storage, folder_path);
|
||
|
||
folder_changed_callback_data = g_new (FolderChangedCallbackData, 1);
|
||
folder_changed_callback_data->storage_set_view = storage_set_view;
|
||
folder_changed_callback_data->path = g_strdup (full_path);
|
||
|
||
e_gtk_signal_connect_full_while_alive (GTK_OBJECT (folder), "changed",
|
||
GTK_SIGNAL_FUNC (folder_changed_cb),
|
||
NULL,
|
||
folder_changed_callback_data,
|
||
folder_changed_callback_data_destroy_notify,
|
||
FALSE, FALSE,
|
||
GTK_OBJECT (storage_set_view));
|
||
}
|
||
|
||
e_free_string_list (folder_path_list);
|
||
}
|
||
|
||
static void
|
||
insert_storages (EStorageSetView *storage_set_view)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
EStorageSet *storage_set;
|
||
GList *storage_list;
|
||
GList *p;
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
storage_set = priv->storage_set;
|
||
|
||
storage_list = e_storage_set_get_storage_list (storage_set);
|
||
|
||
for (p = storage_list; p != NULL; p = p->next) {
|
||
EStorage *storage = E_STORAGE (p->data);
|
||
const char *name;
|
||
char *path;
|
||
ETreePath parent;
|
||
|
||
name = e_storage_get_name (storage);
|
||
path = g_strconcat ("/", name, NULL);
|
||
|
||
parent = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), priv->root_node, -1, path);
|
||
e_tree_memory_sort_node (E_TREE_MEMORY(priv->etree_model),
|
||
priv->root_node,
|
||
storage_sort_callback, storage_set_view);
|
||
|
||
g_hash_table_insert (priv->path_to_etree_node, path, parent);
|
||
|
||
if (priv->show_folders)
|
||
insert_folders (storage_set_view, parent, storage, "/");
|
||
}
|
||
|
||
e_free_object_list (storage_list);
|
||
}
|
||
|
||
void
|
||
e_storage_set_view_construct (EStorageSetView *storage_set_view,
|
||
EStorageSet *storage_set,
|
||
BonoboUIContainer *container)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETableExtras *extras;
|
||
ECell *cell;
|
||
|
||
g_return_if_fail (storage_set_view != NULL);
|
||
g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
|
||
g_return_if_fail (storage_set != NULL);
|
||
g_return_if_fail (E_IS_STORAGE_SET (storage_set));
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
priv->container = container;
|
||
|
||
priv->etree_model = e_tree_memory_callbacks_new (etree_icon_at,
|
||
|
||
etree_column_count,
|
||
|
||
etree_has_save_id,
|
||
etree_get_save_id,
|
||
etree_has_get_node_by_id,
|
||
etree_get_node_by_id,
|
||
|
||
etree_value_at,
|
||
etree_set_value_at,
|
||
etree_is_editable,
|
||
|
||
etree_duplicate_value,
|
||
etree_free_value,
|
||
etree_initialize_value,
|
||
etree_value_is_empty,
|
||
etree_value_to_string,
|
||
|
||
storage_set_view);
|
||
|
||
e_tree_memory_set_node_destroy_func (E_TREE_MEMORY (priv->etree_model),
|
||
etree_node_destroy_func, storage_set_view);
|
||
e_tree_memory_set_expanded_default (E_TREE_MEMORY (priv->etree_model), FALSE);
|
||
|
||
priv->root_node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), NULL, -1,
|
||
g_strdup (ROOT_NODE_NAME));
|
||
add_node_to_hash (storage_set_view, ROOT_NODE_NAME, priv->root_node);
|
||
|
||
extras = e_table_extras_new ();
|
||
cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
|
||
gtk_object_set (GTK_OBJECT (cell), "bold_column", 1, NULL);
|
||
e_table_extras_add_cell (extras, "render_tree",
|
||
e_cell_tree_new (NULL, NULL, TRUE, cell));
|
||
|
||
e_tree_construct_from_spec_file (E_TREE (storage_set_view), priv->etree_model, extras,
|
||
EVOLUTION_ETSPECDIR "/e-storage-set-view.etspec", NULL);
|
||
|
||
e_tree_root_node_set_visible (E_TREE(storage_set_view), FALSE);
|
||
|
||
gtk_object_unref (GTK_OBJECT (extras));
|
||
|
||
gtk_object_ref (GTK_OBJECT (storage_set));
|
||
priv->storage_set = storage_set;
|
||
|
||
e_tree_drag_dest_set (E_TREE (storage_set_view), 0, NULL, 0, GDK_ACTION_MOVE | GDK_ACTION_COPY);
|
||
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "new_storage",
|
||
GTK_SIGNAL_FUNC (new_storage_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "removed_storage",
|
||
GTK_SIGNAL_FUNC (removed_storage_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "new_folder",
|
||
GTK_SIGNAL_FUNC (new_folder_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "updated_folder",
|
||
GTK_SIGNAL_FUNC (updated_folder_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "removed_folder",
|
||
GTK_SIGNAL_FUNC (removed_folder_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "close_folder",
|
||
GTK_SIGNAL_FUNC (close_folder_cb), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
|
||
gtk_signal_connect_while_alive (GTK_OBJECT (priv->etree_model), "fill_in_children",
|
||
GTK_SIGNAL_FUNC (etree_fill_in_children), storage_set_view,
|
||
GTK_OBJECT (storage_set_view));
|
||
|
||
insert_storages (storage_set_view);
|
||
}
|
||
|
||
/* DON'T USE THIS. Use e_storage_set_new_view() instead. */
|
||
GtkWidget *
|
||
e_storage_set_view_new (EStorageSet *storage_set,
|
||
BonoboUIContainer *container)
|
||
{
|
||
GtkWidget *new;
|
||
|
||
g_return_val_if_fail (storage_set != NULL, NULL);
|
||
g_return_val_if_fail (E_IS_STORAGE_SET (storage_set), NULL);
|
||
|
||
new = gtk_type_new (e_storage_set_view_get_type ());
|
||
e_storage_set_view_construct (E_STORAGE_SET_VIEW (new), storage_set, container);
|
||
|
||
return new;
|
||
}
|
||
|
||
|
||
void
|
||
e_storage_set_view_set_current_folder (EStorageSetView *storage_set_view,
|
||
const char *path)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath node;
|
||
|
||
g_return_if_fail (storage_set_view != NULL);
|
||
g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
|
||
g_return_if_fail (path != NULL && g_path_is_absolute (path));
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
node = g_hash_table_lookup (priv->path_to_etree_node, path);
|
||
if (node == NULL)
|
||
return;
|
||
|
||
e_tree_show_node (E_TREE (storage_set_view), node);
|
||
e_tree_set_cursor (E_TREE (storage_set_view), node);
|
||
|
||
g_free (priv->selected_row_path);
|
||
priv->selected_row_path = g_strdup (path);
|
||
|
||
gtk_signal_emit (GTK_OBJECT (storage_set_view), signals[FOLDER_SELECTED], path);
|
||
}
|
||
|
||
const char *
|
||
e_storage_set_view_get_current_folder (EStorageSetView *storage_set_view)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
ETreePath etree_node;
|
||
const char *path;
|
||
|
||
g_return_val_if_fail (storage_set_view != NULL, NULL);
|
||
g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), NULL);
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
if (!priv->show_folders)
|
||
return NULL; /* Mmh! */
|
||
|
||
etree_node = e_tree_get_cursor (E_TREE (storage_set_view));
|
||
|
||
if (etree_node == NULL)
|
||
return NULL; /* Mmh? */
|
||
|
||
path = (char*)e_tree_memory_node_get_data(E_TREE_MEMORY(priv->etree_model), etree_node);
|
||
|
||
return path;
|
||
}
|
||
|
||
void
|
||
e_storage_set_view_set_show_folders (EStorageSetView *storage_set_view,
|
||
gboolean show)
|
||
{
|
||
EStorageSetViewPrivate *priv;
|
||
|
||
g_return_if_fail (storage_set_view != NULL);
|
||
g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
|
||
|
||
priv = storage_set_view->priv;
|
||
|
||
if (show == priv->show_folders)
|
||
return;
|
||
|
||
/* tear down existing tree and hash table mappings */
|
||
e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), priv->root_node);
|
||
|
||
/* now re-add the root node */
|
||
priv->root_node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), NULL, -1,
|
||
g_strdup (ROOT_NODE_NAME));
|
||
add_node_to_hash (storage_set_view, ROOT_NODE_NAME, priv->root_node);
|
||
|
||
/* then reinsert the storages after setting the "show_folders"
|
||
flag. insert_storages will call insert_folders if
|
||
show_folders is TRUE */
|
||
|
||
priv->show_folders = show;
|
||
insert_storages (storage_set_view);
|
||
}
|
||
|
||
gboolean
|
||
e_storage_set_view_get_show_folders (EStorageSetView *storage_set_view)
|
||
{
|
||
return storage_set_view->priv->show_folders;
|
||
}
|
||
|
||
|
||
void
|
||
e_storage_set_view_set_allow_dnd (EStorageSetView *storage_set_view,
|
||
gboolean allow_dnd)
|
||
{
|
||
g_return_if_fail (storage_set_view != NULL);
|
||
g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
|
||
|
||
storage_set_view->priv->allow_dnd = !! allow_dnd;
|
||
}
|
||
|
||
gboolean
|
||
e_storage_set_view_get_allow_dnd (EStorageSetView *storage_set_view)
|
||
{
|
||
g_return_val_if_fail (storage_set_view != NULL, FALSE);
|
||
g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), FALSE);
|
||
|
||
return storage_set_view->priv->allow_dnd;
|
||
}
|
||
|
||
const char *
|
||
e_storage_set_view_get_right_click_path (EStorageSetView *storage_set_view)
|
||
{
|
||
g_return_val_if_fail (storage_set_view != NULL, NULL);
|
||
g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), NULL);
|
||
|
||
return storage_set_view->priv->right_click_row_path;
|
||
}
|
||
|
||
|
||
E_MAKE_TYPE (e_storage_set_view, "EStorageSetView", EStorageSetView, class_init, init, PARENT_TYPE)
|