Calls e_source_selector_update_row() for each ESource being shown by the ESourceSelector, according to the "extension-name" property.
2166 lines
57 KiB
C
2166 lines
57 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
|
/* e-source-selector.c
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
* Author: Ettore Perazzoli <ettore@ximian.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "e-cell-renderer-color.h"
|
|
#include "e-source-selector.h"
|
|
|
|
#define E_SOURCE_SELECTOR_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE \
|
|
((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate))
|
|
|
|
typedef struct _AsyncContext AsyncContext;
|
|
|
|
struct _ESourceSelectorPrivate {
|
|
ESourceRegistry *registry;
|
|
GHashTable *source_index;
|
|
gchar *extension_name;
|
|
|
|
GtkTreeRowReference *saved_primary_selection;
|
|
|
|
/* ESource -> GSource */
|
|
GHashTable *pending_writes;
|
|
GMainContext *main_context;
|
|
|
|
gboolean toggled_last;
|
|
gboolean select_new;
|
|
gboolean show_colors;
|
|
gboolean show_toggles;
|
|
};
|
|
|
|
struct _AsyncContext {
|
|
ESourceSelector *selector;
|
|
ESource *source;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_EXTENSION_NAME,
|
|
PROP_PRIMARY_SELECTION,
|
|
PROP_REGISTRY,
|
|
PROP_SHOW_COLORS,
|
|
PROP_SHOW_TOGGLES
|
|
};
|
|
|
|
enum {
|
|
SELECTION_CHANGED,
|
|
PRIMARY_SELECTION_CHANGED,
|
|
POPUP_EVENT,
|
|
DATA_DROPPED,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
enum {
|
|
COLUMN_NAME,
|
|
COLUMN_COLOR,
|
|
COLUMN_ACTIVE,
|
|
COLUMN_SHOW_COLOR,
|
|
COLUMN_SHOW_TOGGLE,
|
|
COLUMN_WEIGHT,
|
|
COLUMN_SOURCE,
|
|
NUM_COLUMNS
|
|
};
|
|
|
|
static guint signals[NUM_SIGNALS];
|
|
|
|
G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW)
|
|
|
|
/* ESafeToggleRenderer does not emit 'toggled' signal
|
|
* on 'activate' when mouse is not over the toggle. */
|
|
|
|
typedef GtkCellRendererToggle ECellRendererSafeToggle;
|
|
typedef GtkCellRendererToggleClass ECellRendererSafeToggleClass;
|
|
|
|
/* Forward Declarations */
|
|
GType e_cell_renderer_safe_toggle_get_type (void);
|
|
|
|
G_DEFINE_TYPE (
|
|
ECellRendererSafeToggle,
|
|
e_cell_renderer_safe_toggle,
|
|
GTK_TYPE_CELL_RENDERER_TOGGLE)
|
|
|
|
static gboolean
|
|
safe_toggle_activate (GtkCellRenderer *cell,
|
|
GdkEvent *event,
|
|
GtkWidget *widget,
|
|
const gchar *path,
|
|
const GdkRectangle *background_area,
|
|
const GdkRectangle *cell_area,
|
|
GtkCellRendererState flags)
|
|
{
|
|
gboolean point_in_cell_area = TRUE;
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && cell_area != NULL) {
|
|
cairo_region_t *region;
|
|
|
|
region = cairo_region_create_rectangle (cell_area);
|
|
point_in_cell_area = cairo_region_contains_point (
|
|
region, event->button.x, event->button.y);
|
|
cairo_region_destroy (region);
|
|
}
|
|
|
|
if (!point_in_cell_area)
|
|
return FALSE;
|
|
|
|
return GTK_CELL_RENDERER_CLASS (
|
|
e_cell_renderer_safe_toggle_parent_class)->activate (
|
|
cell, event, widget, path, background_area, cell_area, flags);
|
|
}
|
|
|
|
static void
|
|
e_cell_renderer_safe_toggle_class_init (ECellRendererSafeToggleClass *class)
|
|
{
|
|
GtkCellRendererClass *cell_renderer_class;
|
|
|
|
cell_renderer_class = GTK_CELL_RENDERER_CLASS (class);
|
|
cell_renderer_class->activate = safe_toggle_activate;
|
|
}
|
|
|
|
static void
|
|
e_cell_renderer_safe_toggle_init (ECellRendererSafeToggle *obj)
|
|
{
|
|
}
|
|
|
|
static GtkCellRenderer *
|
|
e_cell_renderer_safe_toggle_new (void)
|
|
{
|
|
return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL);
|
|
}
|
|
|
|
static void
|
|
clear_saved_primary_selection (ESourceSelector *selector)
|
|
{
|
|
gtk_tree_row_reference_free (selector->priv->saved_primary_selection);
|
|
selector->priv->saved_primary_selection = NULL;
|
|
}
|
|
|
|
static void
|
|
async_context_free (AsyncContext *async_context)
|
|
{
|
|
if (async_context->selector != NULL)
|
|
g_object_unref (async_context->selector);
|
|
|
|
if (async_context->source != NULL)
|
|
g_object_unref (async_context->source);
|
|
|
|
g_slice_free (AsyncContext, async_context);
|
|
}
|
|
|
|
static void
|
|
pending_writes_destroy_source (GSource *source)
|
|
{
|
|
g_source_destroy (source);
|
|
g_source_unref (source);
|
|
}
|
|
|
|
static void
|
|
source_selector_write_done_cb (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
ESource *source;
|
|
ESourceSelector *selector;
|
|
GError *error = NULL;
|
|
|
|
source = E_SOURCE (source_object);
|
|
selector = E_SOURCE_SELECTOR (user_data);
|
|
|
|
e_source_write_finish (source, result, &error);
|
|
|
|
/* FIXME Display the error in the selector somehow? */
|
|
if (error != NULL) {
|
|
g_warning ("%s: %s", G_STRFUNC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_object_unref (selector);
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_write_idle_cb (gpointer user_data)
|
|
{
|
|
AsyncContext *async_context = user_data;
|
|
GHashTable *pending_writes;
|
|
|
|
/* XXX This operation is not cancellable. */
|
|
e_source_write (
|
|
async_context->source, NULL,
|
|
source_selector_write_done_cb,
|
|
g_object_ref (async_context->selector));
|
|
|
|
pending_writes = async_context->selector->priv->pending_writes;
|
|
g_hash_table_remove (pending_writes, async_context->source);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
source_selector_cancel_write (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
GHashTable *pending_writes;
|
|
|
|
/* Cancel any pending writes for this ESource so as not
|
|
* to overwrite whatever change we're being notified of. */
|
|
pending_writes = selector->priv->pending_writes;
|
|
g_hash_table_remove (pending_writes, source);
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_traverse (GNode *node,
|
|
ESourceSelector *selector)
|
|
{
|
|
ESource *source;
|
|
GHashTable *source_index;
|
|
GtkTreeRowReference *reference = NULL;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
/* Skip the root node. */
|
|
if (G_NODE_IS_ROOT (node))
|
|
return FALSE;
|
|
|
|
source_index = selector->priv->source_index;
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
|
|
|
|
if (node->parent != NULL && node->parent->data != NULL)
|
|
reference = g_hash_table_lookup (
|
|
source_index, node->parent->data);
|
|
|
|
if (gtk_tree_row_reference_valid (reference)) {
|
|
GtkTreeIter parent;
|
|
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
gtk_tree_model_get_iter (model, &parent, path);
|
|
gtk_tree_path_free (path);
|
|
|
|
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
|
|
} else
|
|
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
|
|
|
|
source = E_SOURCE (node->data);
|
|
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
reference = gtk_tree_row_reference_new (model, path);
|
|
g_hash_table_insert (source_index, g_object_ref (source), reference);
|
|
gtk_tree_path_free (path);
|
|
|
|
e_source_selector_update_row (selector, source);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
source_selector_save_expanded (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
GQueue *queue)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
ESource *source;
|
|
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
g_queue_push_tail (queue, source);
|
|
}
|
|
|
|
static void
|
|
source_selector_build_model (ESourceSelector *selector)
|
|
{
|
|
ESourceRegistry *registry;
|
|
GQueue queue = G_QUEUE_INIT;
|
|
GHashTable *source_index;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
ESource *selected;
|
|
const gchar *extension_name;
|
|
GNode *root;
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
|
|
registry = e_source_selector_get_registry (selector);
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
/* Make sure we have what we need to build the model, since
|
|
* this can get called early in the initialization phase. */
|
|
if (registry == NULL || extension_name == NULL)
|
|
return;
|
|
|
|
source_index = selector->priv->source_index;
|
|
selected = e_source_selector_ref_primary_selection (selector);
|
|
|
|
/* Save expanded sources to restore later. */
|
|
gtk_tree_view_map_expanded_rows (
|
|
tree_view, (GtkTreeViewMappingFunc)
|
|
source_selector_save_expanded, &queue);
|
|
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
gtk_tree_store_clear (GTK_TREE_STORE (model));
|
|
|
|
g_hash_table_remove_all (source_index);
|
|
|
|
root = e_source_registry_build_display_tree (registry, extension_name);
|
|
|
|
g_node_traverse (
|
|
root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
|
|
(GNodeTraverseFunc) source_selector_traverse,
|
|
selector);
|
|
|
|
e_source_registry_free_display_tree (root);
|
|
|
|
/* Restore previously expanded sources. */
|
|
while (!g_queue_is_empty (&queue)) {
|
|
GtkTreeRowReference *reference;
|
|
ESource *source;
|
|
|
|
source = g_queue_pop_head (&queue);
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
|
|
if (gtk_tree_row_reference_valid (reference)) {
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
gtk_tree_view_expand_to_path (tree_view, path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
g_object_unref (source);
|
|
}
|
|
|
|
/* Restore the primary selection. */
|
|
if (selected != NULL) {
|
|
e_source_selector_set_primary_selection (selector, selected);
|
|
g_object_unref (selected);
|
|
}
|
|
|
|
/* Make sure we have a primary selection. If not, pick one. */
|
|
selected = e_source_selector_ref_primary_selection (selector);
|
|
if (selected == NULL) {
|
|
selected = e_source_registry_ref_default_for_extension_name (
|
|
registry, extension_name);
|
|
e_source_selector_set_primary_selection (selector, selected);
|
|
}
|
|
g_object_unref (selected);
|
|
}
|
|
|
|
static void
|
|
source_selector_expand_to_source (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
GHashTable *source_index;
|
|
GtkTreeRowReference *reference;
|
|
GtkTreePath *path;
|
|
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
|
|
/* If the ESource is not in our tree model then return silently. */
|
|
if (reference == NULL)
|
|
return;
|
|
|
|
/* If we do have a row reference, it should be valid. */
|
|
g_return_if_fail (gtk_tree_row_reference_valid (reference));
|
|
|
|
/* Expand the tree view to the path containing the ESource */
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
gtk_tree_view_expand_to_path (GTK_TREE_VIEW (selector), path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
source_selector_source_added_cb (ESourceRegistry *registry,
|
|
ESource *source,
|
|
ESourceSelector *selector)
|
|
{
|
|
source_selector_build_model (selector);
|
|
|
|
source_selector_expand_to_source (selector, source);
|
|
}
|
|
|
|
static void
|
|
source_selector_source_changed_cb (ESourceRegistry *registry,
|
|
ESource *source,
|
|
ESourceSelector *selector)
|
|
{
|
|
source_selector_cancel_write (selector, source);
|
|
|
|
e_source_selector_update_row (selector, source);
|
|
}
|
|
|
|
static void
|
|
source_selector_source_removed_cb (ESourceRegistry *registry,
|
|
ESource *source,
|
|
ESourceSelector *selector)
|
|
{
|
|
source_selector_build_model (selector);
|
|
}
|
|
|
|
static void
|
|
source_selector_source_enabled_cb (ESourceRegistry *registry,
|
|
ESource *source,
|
|
ESourceSelector *selector)
|
|
{
|
|
source_selector_build_model (selector);
|
|
|
|
source_selector_expand_to_source (selector, source);
|
|
}
|
|
|
|
static void
|
|
source_selector_source_disabled_cb (ESourceRegistry *registry,
|
|
ESource *source,
|
|
ESourceSelector *selector)
|
|
{
|
|
source_selector_build_model (selector);
|
|
}
|
|
|
|
static gboolean
|
|
same_source_name_exists (ESourceSelector *selector,
|
|
const gchar *display_name)
|
|
{
|
|
GHashTable *source_index;
|
|
GHashTableIter iter;
|
|
gpointer key;
|
|
|
|
source_index = selector->priv->source_index;
|
|
g_hash_table_iter_init (&iter, source_index);
|
|
|
|
while (g_hash_table_iter_next (&iter, &key, NULL)) {
|
|
ESource *source = E_SOURCE (key);
|
|
const gchar *source_name;
|
|
|
|
source_name = e_source_get_display_name (source);
|
|
if (g_strcmp0 (display_name, source_name) == 0)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
selection_func (GtkTreeSelection *selection,
|
|
GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
gboolean path_currently_selected,
|
|
ESourceSelector *selector)
|
|
{
|
|
ESource *source;
|
|
GtkTreeIter iter;
|
|
const gchar *extension_name;
|
|
|
|
if (selector->priv->toggled_last) {
|
|
selector->priv->toggled_last = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (path_currently_selected)
|
|
return TRUE;
|
|
|
|
if (!gtk_tree_model_get_iter (model, &iter, path))
|
|
return FALSE;
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
if (!e_source_has_extension (source, extension_name)) {
|
|
g_object_unref (source);
|
|
return FALSE;
|
|
}
|
|
|
|
clear_saved_primary_selection (selector);
|
|
|
|
g_object_unref (source);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
text_cell_edited_cb (ESourceSelector *selector,
|
|
const gchar *path_string,
|
|
const gchar *new_name)
|
|
{
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
ESource *source;
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
path = gtk_tree_path_new_from_string (path_string);
|
|
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
gtk_tree_path_free (path);
|
|
|
|
if (new_name == NULL || *new_name == '\0')
|
|
return;
|
|
|
|
if (same_source_name_exists (selector, new_name))
|
|
return;
|
|
|
|
e_source_set_display_name (source, new_name);
|
|
|
|
e_source_selector_queue_write (selector, source);
|
|
}
|
|
|
|
static void
|
|
cell_toggled_callback (GtkCellRendererToggle *renderer,
|
|
const gchar *path_string,
|
|
ESourceSelector *selector)
|
|
{
|
|
ESource *source;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
|
|
path = gtk_tree_path_new_from_string (path_string);
|
|
|
|
if (!gtk_tree_model_get_iter (model, &iter, path)) {
|
|
gtk_tree_path_free (path);
|
|
return;
|
|
}
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
if (e_source_selector_source_is_selected (selector, source))
|
|
e_source_selector_unselect_source (selector, source);
|
|
else
|
|
e_source_selector_select_source (selector, source);
|
|
|
|
selector->priv->toggled_last = TRUE;
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
g_object_unref (source);
|
|
}
|
|
|
|
static void
|
|
selection_changed_callback (GtkTreeSelection *selection,
|
|
ESourceSelector *selector)
|
|
{
|
|
g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
|
|
g_object_notify (G_OBJECT (selector), "primary-selection");
|
|
}
|
|
|
|
static void
|
|
source_selector_set_extension_name (ESourceSelector *selector,
|
|
const gchar *extension_name)
|
|
{
|
|
g_return_if_fail (extension_name != NULL);
|
|
g_return_if_fail (selector->priv->extension_name == NULL);
|
|
|
|
selector->priv->extension_name = g_strdup (extension_name);
|
|
}
|
|
|
|
static void
|
|
source_selector_set_registry (ESourceSelector *selector,
|
|
ESourceRegistry *registry)
|
|
{
|
|
g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
|
|
g_return_if_fail (selector->priv->registry == NULL);
|
|
|
|
selector->priv->registry = g_object_ref (registry);
|
|
}
|
|
|
|
static void
|
|
source_selector_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_EXTENSION_NAME:
|
|
source_selector_set_extension_name (
|
|
E_SOURCE_SELECTOR (object),
|
|
g_value_get_string (value));
|
|
return;
|
|
|
|
case PROP_PRIMARY_SELECTION:
|
|
e_source_selector_set_primary_selection (
|
|
E_SOURCE_SELECTOR (object),
|
|
g_value_get_object (value));
|
|
return;
|
|
|
|
case PROP_REGISTRY:
|
|
source_selector_set_registry (
|
|
E_SOURCE_SELECTOR (object),
|
|
g_value_get_object (value));
|
|
return;
|
|
|
|
case PROP_SHOW_COLORS:
|
|
e_source_selector_set_show_colors (
|
|
E_SOURCE_SELECTOR (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
|
|
case PROP_SHOW_TOGGLES:
|
|
e_source_selector_set_show_toggles (
|
|
E_SOURCE_SELECTOR (object),
|
|
g_value_get_boolean (value));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
source_selector_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_EXTENSION_NAME:
|
|
g_value_set_string (
|
|
value,
|
|
e_source_selector_get_extension_name (
|
|
E_SOURCE_SELECTOR (object)));
|
|
return;
|
|
|
|
case PROP_PRIMARY_SELECTION:
|
|
g_value_take_object (
|
|
value,
|
|
e_source_selector_ref_primary_selection (
|
|
E_SOURCE_SELECTOR (object)));
|
|
return;
|
|
|
|
case PROP_REGISTRY:
|
|
g_value_set_object (
|
|
value,
|
|
e_source_selector_get_registry (
|
|
E_SOURCE_SELECTOR (object)));
|
|
return;
|
|
|
|
case PROP_SHOW_COLORS:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_source_selector_get_show_colors (
|
|
E_SOURCE_SELECTOR (object)));
|
|
return;
|
|
|
|
case PROP_SHOW_TOGGLES:
|
|
g_value_set_boolean (
|
|
value,
|
|
e_source_selector_get_show_toggles (
|
|
E_SOURCE_SELECTOR (object)));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
source_selector_dispose (GObject *object)
|
|
{
|
|
ESourceSelectorPrivate *priv;
|
|
|
|
priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
|
|
|
|
if (priv->registry != NULL) {
|
|
g_signal_handlers_disconnect_matched (
|
|
priv->registry,
|
|
G_SIGNAL_MATCH_DATA,
|
|
0, 0, NULL, NULL, object);
|
|
g_object_unref (priv->registry);
|
|
priv->registry = NULL;
|
|
}
|
|
|
|
g_hash_table_remove_all (priv->source_index);
|
|
g_hash_table_remove_all (priv->pending_writes);
|
|
|
|
clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
|
|
|
|
/* Chain up to parent's dispose() method. */
|
|
G_OBJECT_CLASS (e_source_selector_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
source_selector_finalize (GObject *object)
|
|
{
|
|
ESourceSelectorPrivate *priv;
|
|
|
|
priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
|
|
|
|
g_hash_table_destroy (priv->source_index);
|
|
g_hash_table_destroy (priv->pending_writes);
|
|
|
|
g_free (priv->extension_name);
|
|
|
|
if (priv->main_context != NULL)
|
|
g_main_context_unref (priv->main_context);
|
|
|
|
/* Chain up to parent's finalize() method. */
|
|
G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
source_selector_constructed (GObject *object)
|
|
{
|
|
ESourceRegistry *registry;
|
|
ESourceSelector *selector;
|
|
|
|
selector = E_SOURCE_SELECTOR (object);
|
|
registry = e_source_selector_get_registry (selector);
|
|
|
|
g_signal_connect (
|
|
registry, "source-added",
|
|
G_CALLBACK (source_selector_source_added_cb), selector);
|
|
|
|
g_signal_connect (
|
|
registry, "source-changed",
|
|
G_CALLBACK (source_selector_source_changed_cb), selector);
|
|
|
|
g_signal_connect (
|
|
registry, "source-removed",
|
|
G_CALLBACK (source_selector_source_removed_cb), selector);
|
|
|
|
g_signal_connect (
|
|
registry, "source-enabled",
|
|
G_CALLBACK (source_selector_source_enabled_cb), selector);
|
|
|
|
g_signal_connect (
|
|
registry, "source-disabled",
|
|
G_CALLBACK (source_selector_source_disabled_cb), selector);
|
|
|
|
source_selector_build_model (selector);
|
|
|
|
gtk_tree_view_expand_all (GTK_TREE_VIEW (selector));
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
ESourceSelector *selector;
|
|
GtkWidgetClass *widget_class;
|
|
GtkTreePath *path;
|
|
ESource *source = NULL;
|
|
ESource *primary;
|
|
gboolean right_click = FALSE;
|
|
gboolean triple_click = FALSE;
|
|
gboolean row_exists;
|
|
gboolean res = FALSE;
|
|
|
|
selector = E_SOURCE_SELECTOR (widget);
|
|
|
|
selector->priv->toggled_last = FALSE;
|
|
|
|
/* Triple-clicking a source selects it exclusively. */
|
|
|
|
if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
|
|
right_click = TRUE;
|
|
else if (event->button == 1 && event->type == GDK_3BUTTON_PRESS)
|
|
triple_click = TRUE;
|
|
else
|
|
goto chainup;
|
|
|
|
row_exists = gtk_tree_view_get_path_at_pos (
|
|
GTK_TREE_VIEW (widget), event->x, event->y,
|
|
&path, NULL, NULL, NULL);
|
|
|
|
/* Get the source/group */
|
|
if (row_exists) {
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
|
|
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
}
|
|
|
|
if (source == NULL)
|
|
goto chainup;
|
|
|
|
primary = e_source_selector_ref_primary_selection (selector);
|
|
if (source != primary)
|
|
e_source_selector_set_primary_selection (selector, source);
|
|
if (primary != NULL)
|
|
g_object_unref (primary);
|
|
|
|
if (right_click)
|
|
g_signal_emit (
|
|
widget, signals[POPUP_EVENT], 0, source, event, &res);
|
|
|
|
if (triple_click) {
|
|
e_source_selector_select_exclusive (selector, source);
|
|
res = TRUE;
|
|
}
|
|
|
|
g_object_unref (source);
|
|
|
|
return res;
|
|
|
|
chainup:
|
|
|
|
/* Chain up to parent's button_press_event() method. */
|
|
widget_class = GTK_WIDGET_CLASS (e_source_selector_parent_class);
|
|
return widget_class->button_press_event (widget, event);
|
|
}
|
|
|
|
static void
|
|
source_selector_drag_leave (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
guint time_)
|
|
{
|
|
GtkTreeView *tree_view;
|
|
GtkTreeViewDropPosition pos;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
pos = GTK_TREE_VIEW_DROP_BEFORE;
|
|
|
|
gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos);
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_drag_motion (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint time_)
|
|
{
|
|
ESource *source = NULL;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path = NULL;
|
|
GtkTreeIter iter;
|
|
GtkTreeViewDropPosition pos;
|
|
GdkDragAction action = 0;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
|
|
if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
|
|
goto exit;
|
|
|
|
if (!gtk_tree_model_get_iter (model, &iter, path))
|
|
goto exit;
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
if (!e_source_get_writable (source))
|
|
goto exit;
|
|
|
|
pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
|
|
gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
|
|
|
|
if (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)
|
|
action = GDK_ACTION_MOVE;
|
|
else
|
|
action = gdk_drag_context_get_suggested_action (context);
|
|
|
|
exit:
|
|
if (path != NULL)
|
|
gtk_tree_path_free (path);
|
|
|
|
if (source != NULL)
|
|
g_object_unref (source);
|
|
|
|
gdk_drag_status (context, action, time_);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_drag_drop (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint time_)
|
|
{
|
|
ESource *source;
|
|
ESourceSelector *selector;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
const gchar *extension_name;
|
|
gboolean drop_zone;
|
|
gboolean valid;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
|
|
if (!gtk_tree_view_get_path_at_pos (
|
|
tree_view, x, y, &path, NULL, NULL, NULL))
|
|
return FALSE;
|
|
|
|
valid = gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_path_free (path);
|
|
g_return_val_if_fail (valid, FALSE);
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
selector = E_SOURCE_SELECTOR (widget);
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
drop_zone = e_source_has_extension (source, extension_name);
|
|
|
|
g_object_unref (source);
|
|
|
|
return drop_zone;
|
|
}
|
|
|
|
static void
|
|
source_selector_drag_data_received (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint time_)
|
|
{
|
|
ESource *source = NULL;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path = NULL;
|
|
GtkTreeIter iter;
|
|
GdkDragAction action;
|
|
gboolean delete;
|
|
gboolean success = FALSE;
|
|
|
|
tree_view = GTK_TREE_VIEW (widget);
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
|
|
action = gdk_drag_context_get_selected_action (context);
|
|
delete = (action == GDK_ACTION_MOVE);
|
|
|
|
if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
|
|
goto exit;
|
|
|
|
if (!gtk_tree_model_get_iter (model, &iter, path))
|
|
goto exit;
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
if (!e_source_get_writable (source))
|
|
goto exit;
|
|
|
|
g_signal_emit (
|
|
widget, signals[DATA_DROPPED], 0, selection_data,
|
|
source, gdk_drag_context_get_selected_action (context),
|
|
info, &success);
|
|
|
|
exit:
|
|
if (path != NULL)
|
|
gtk_tree_path_free (path);
|
|
|
|
if (source != NULL)
|
|
g_object_unref (source);
|
|
|
|
gtk_drag_finish (context, success, delete, time_);
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_popup_menu (GtkWidget *widget)
|
|
{
|
|
ESourceSelector *selector;
|
|
ESource *source;
|
|
gboolean res = FALSE;
|
|
|
|
selector = E_SOURCE_SELECTOR (widget);
|
|
source = e_source_selector_ref_primary_selection (selector);
|
|
g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res);
|
|
|
|
if (source != NULL)
|
|
g_object_unref (source);
|
|
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_test_collapse_row (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
ESourceSelectorPrivate *priv;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter child_iter;
|
|
|
|
priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
|
|
|
|
/* Clear this because something else has been clicked on now */
|
|
priv->toggled_last = FALSE;
|
|
|
|
if (priv->saved_primary_selection)
|
|
return FALSE;
|
|
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (!gtk_tree_selection_get_selected (selection, &model, &child_iter))
|
|
return FALSE;
|
|
|
|
if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
|
|
GtkTreeRowReference *reference;
|
|
GtkTreePath *child_path;
|
|
|
|
child_path = gtk_tree_model_get_path (model, &child_iter);
|
|
reference = gtk_tree_row_reference_new (model, child_path);
|
|
priv->saved_primary_selection = reference;
|
|
gtk_tree_path_free (child_path);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
source_selector_row_expanded (GtkTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
ESourceSelectorPrivate *priv;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *child_path;
|
|
GtkTreeIter child_iter;
|
|
|
|
priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
|
|
|
|
if (!priv->saved_primary_selection)
|
|
return;
|
|
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
|
|
child_path = gtk_tree_row_reference_get_path (
|
|
priv->saved_primary_selection);
|
|
gtk_tree_model_get_iter (model, &child_iter, child_path);
|
|
|
|
if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
|
|
GtkTreeSelection *selection;
|
|
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
gtk_tree_selection_select_iter (selection, &child_iter);
|
|
|
|
clear_saved_primary_selection (E_SOURCE_SELECTOR (tree_view));
|
|
}
|
|
|
|
gtk_tree_path_free (child_path);
|
|
}
|
|
|
|
static gboolean
|
|
source_selector_get_source_selected (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
ESourceSelectable *extension;
|
|
const gchar *extension_name;
|
|
gboolean selected = TRUE;
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
if (!e_source_has_extension (source, extension_name))
|
|
return FALSE;
|
|
|
|
extension = e_source_get_extension (source, extension_name);
|
|
|
|
if (E_IS_SOURCE_SELECTABLE (extension))
|
|
selected = e_source_selectable_get_selected (extension);
|
|
|
|
return selected;
|
|
}
|
|
|
|
static void
|
|
source_selector_set_source_selected (ESourceSelector *selector,
|
|
ESource *source,
|
|
gboolean selected)
|
|
{
|
|
ESourceSelectable *extension;
|
|
const gchar *extension_name;
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
if (!e_source_has_extension (source, extension_name))
|
|
return;
|
|
|
|
extension = e_source_get_extension (source, extension_name);
|
|
|
|
if (!E_IS_SOURCE_SELECTABLE (extension))
|
|
return;
|
|
|
|
if (selected != e_source_selectable_get_selected (extension)) {
|
|
e_source_selectable_set_selected (extension, selected);
|
|
e_source_selector_queue_write (selector, source);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
ess_bool_accumulator (GSignalInvocationHint *ihint,
|
|
GValue *out,
|
|
const GValue *in,
|
|
gpointer data)
|
|
{
|
|
gboolean v_boolean;
|
|
|
|
v_boolean = g_value_get_boolean (in);
|
|
g_value_set_boolean (out, v_boolean);
|
|
|
|
return !v_boolean;
|
|
}
|
|
|
|
static void
|
|
e_source_selector_class_init (ESourceSelectorClass *class)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
GtkTreeViewClass *tree_view_class;
|
|
|
|
g_type_class_add_private (class, sizeof (ESourceSelectorPrivate));
|
|
|
|
object_class = G_OBJECT_CLASS (class);
|
|
object_class->set_property = source_selector_set_property;
|
|
object_class->get_property = source_selector_get_property;
|
|
object_class->dispose = source_selector_dispose;
|
|
object_class->finalize = source_selector_finalize;
|
|
object_class->constructed = source_selector_constructed;
|
|
|
|
widget_class = GTK_WIDGET_CLASS (class);
|
|
widget_class->button_press_event = source_selector_button_press_event;
|
|
widget_class->drag_leave = source_selector_drag_leave;
|
|
widget_class->drag_motion = source_selector_drag_motion;
|
|
widget_class->drag_drop = source_selector_drag_drop;
|
|
widget_class->drag_data_received = source_selector_drag_data_received;
|
|
widget_class->popup_menu = source_selector_popup_menu;
|
|
|
|
tree_view_class = GTK_TREE_VIEW_CLASS (class);
|
|
tree_view_class->test_collapse_row = source_selector_test_collapse_row;
|
|
tree_view_class->row_expanded = source_selector_row_expanded;
|
|
|
|
class->get_source_selected = source_selector_get_source_selected;
|
|
class->set_source_selected = source_selector_set_source_selected;
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_EXTENSION_NAME,
|
|
g_param_spec_string (
|
|
"extension-name",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_PRIMARY_SELECTION,
|
|
g_param_spec_object (
|
|
"primary-selection",
|
|
NULL,
|
|
NULL,
|
|
E_TYPE_SOURCE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_REGISTRY,
|
|
g_param_spec_object (
|
|
"registry",
|
|
NULL,
|
|
NULL,
|
|
E_TYPE_SOURCE_REGISTRY,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_SHOW_COLORS,
|
|
g_param_spec_boolean (
|
|
"show-colors",
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_SHOW_TOGGLES,
|
|
g_param_spec_boolean (
|
|
"show-toggles",
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
signals[SELECTION_CHANGED] = g_signal_new (
|
|
"selection-changed",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/* XXX Consider this signal deprecated. Connect
|
|
* to "notify::primary-selection" instead. */
|
|
signals[PRIMARY_SELECTION_CHANGED] = g_signal_new (
|
|
"primary-selection-changed",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[POPUP_EVENT] = g_signal_new (
|
|
"popup-event",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ESourceSelectorClass, popup_event),
|
|
ess_bool_accumulator, NULL, NULL,
|
|
G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT,
|
|
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
signals[DATA_DROPPED] = g_signal_new (
|
|
"data-dropped",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ESourceSelectorClass, data_dropped),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_BOOLEAN, 4,
|
|
GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
|
|
E_TYPE_SOURCE,
|
|
GDK_TYPE_DRAG_ACTION,
|
|
G_TYPE_UINT);
|
|
}
|
|
|
|
static void
|
|
e_source_selector_init (ESourceSelector *selector)
|
|
{
|
|
GHashTable *pending_writes;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeSelection *selection;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeStore *tree_store;
|
|
GtkTreeView *tree_view;
|
|
|
|
pending_writes = g_hash_table_new_full (
|
|
(GHashFunc) g_direct_hash,
|
|
(GEqualFunc) g_direct_equal,
|
|
(GDestroyNotify) g_object_unref,
|
|
(GDestroyNotify) pending_writes_destroy_source);
|
|
|
|
selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector);
|
|
|
|
selector->priv->pending_writes = pending_writes;
|
|
|
|
selector->priv->main_context = g_main_context_get_thread_default ();
|
|
if (selector->priv->main_context != NULL)
|
|
g_main_context_ref (selector->priv->main_context);
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
|
|
gtk_tree_view_set_search_column (tree_view, COLUMN_SOURCE);
|
|
gtk_tree_view_set_enable_search (tree_view, TRUE);
|
|
|
|
selector->priv->toggled_last = FALSE;
|
|
selector->priv->select_new = FALSE;
|
|
selector->priv->show_colors = TRUE;
|
|
selector->priv->show_toggles = TRUE;
|
|
|
|
selector->priv->source_index = g_hash_table_new_full (
|
|
(GHashFunc) e_source_hash,
|
|
(GEqualFunc) e_source_equal,
|
|
(GDestroyNotify) g_object_unref,
|
|
(GDestroyNotify) gtk_tree_row_reference_free);
|
|
|
|
tree_store = gtk_tree_store_new (
|
|
NUM_COLUMNS,
|
|
G_TYPE_STRING, /* COLUMN_NAME */
|
|
GDK_TYPE_COLOR, /* COLUMN_COLOR */
|
|
G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */
|
|
G_TYPE_BOOLEAN, /* COLUMN_SHOW_COLOR */
|
|
G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */
|
|
G_TYPE_INT, /* COLUMN_WEIGHT */
|
|
E_TYPE_SOURCE); /* COLUMN_SOURCE */
|
|
|
|
gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
|
|
|
|
column = gtk_tree_view_column_new ();
|
|
gtk_tree_view_column_set_expand (column, TRUE);
|
|
gtk_tree_view_append_column (tree_view, column);
|
|
|
|
renderer = e_cell_renderer_color_new ();
|
|
g_object_set (
|
|
G_OBJECT (renderer), "mode",
|
|
GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_add_attribute (
|
|
column, renderer, "color", COLUMN_COLOR);
|
|
gtk_tree_view_column_add_attribute (
|
|
column, renderer, "visible", COLUMN_SHOW_COLOR);
|
|
|
|
renderer = e_cell_renderer_safe_toggle_new ();
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_add_attribute (
|
|
column, renderer, "active", COLUMN_ACTIVE);
|
|
gtk_tree_view_column_add_attribute (
|
|
column, renderer, "visible", COLUMN_SHOW_TOGGLE);
|
|
g_signal_connect (
|
|
renderer, "toggled",
|
|
G_CALLBACK (cell_toggled_callback), selector);
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
g_object_set (
|
|
G_OBJECT (renderer),
|
|
"ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
|
g_signal_connect_swapped (
|
|
renderer, "edited",
|
|
G_CALLBACK (text_cell_edited_cb), selector);
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
gtk_tree_view_column_set_attributes (
|
|
column, renderer,
|
|
"text", COLUMN_NAME,
|
|
"weight", COLUMN_WEIGHT,
|
|
NULL);
|
|
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
gtk_tree_selection_set_select_function (
|
|
selection, (GtkTreeSelectionFunc)
|
|
selection_func, selector, NULL);
|
|
g_signal_connect_object (
|
|
selection, "changed",
|
|
G_CALLBACK (selection_changed_callback),
|
|
G_OBJECT (selector), 0);
|
|
|
|
gtk_tree_view_set_headers_visible (tree_view, FALSE);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_new:
|
|
* @registry: an #ESourceRegistry
|
|
* @extension_name: the name of an #ESource extension
|
|
*
|
|
* Displays a list of sources from @registry having an extension named
|
|
* @extension_name. The sources are grouped by backend or groupware
|
|
* account, which are described by the parent source.
|
|
*
|
|
* Returns: a new #ESourceSelector
|
|
**/
|
|
GtkWidget *
|
|
e_source_selector_new (ESourceRegistry *registry,
|
|
const gchar *extension_name)
|
|
{
|
|
g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
|
|
g_return_val_if_fail (extension_name != NULL, NULL);
|
|
|
|
return g_object_new (
|
|
E_TYPE_SOURCE_SELECTOR, "registry", registry,
|
|
"extension-name", extension_name, NULL);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_get_registry:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Returns the #ESourceRegistry that @selector is getting sources from.
|
|
*
|
|
* Returns: an #ESourceRegistry
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
ESourceRegistry *
|
|
e_source_selector_get_registry (ESourceSelector *selector)
|
|
{
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
|
|
return selector->priv->registry;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_get_extension_name:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Returns the extension name used to filter which sources are displayed.
|
|
*
|
|
* Returns: the #ESource extension name
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
const gchar *
|
|
e_source_selector_get_extension_name (ESourceSelector *selector)
|
|
{
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
|
|
return selector->priv->extension_name;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_get_show_colors:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Returns whether colors are shown next to data sources.
|
|
*
|
|
* Returns: %TRUE if colors are being shown
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
gboolean
|
|
e_source_selector_get_show_colors (ESourceSelector *selector)
|
|
{
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
|
|
|
|
return selector->priv->show_colors;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_set_show_colors:
|
|
* @selector: an #ESourceSelector
|
|
* @show_colors: whether to show colors
|
|
*
|
|
* Sets whether to show colors next to data sources.
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
void
|
|
e_source_selector_set_show_colors (ESourceSelector *selector,
|
|
gboolean show_colors)
|
|
{
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
|
|
if ((show_colors ? 1 : 0) == (selector->priv->show_colors ? 1 : 0))
|
|
return;
|
|
|
|
selector->priv->show_colors = show_colors;
|
|
|
|
g_object_notify (G_OBJECT (selector), "show-colors");
|
|
|
|
source_selector_build_model (selector);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_get_show_toggles:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Returns whether toggles are shown next to data sources.
|
|
*
|
|
* Returns: %TRUE if toggles are being shown
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
gboolean
|
|
e_source_selector_get_show_toggles (ESourceSelector *selector)
|
|
{
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
|
|
|
|
return selector->priv->show_toggles;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_set_show_toggles:
|
|
* @selector: an #ESourceSelector
|
|
* @show_toggles: whether to show toggles
|
|
*
|
|
* Sets whether to show toggles next to data sources.
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
void
|
|
e_source_selector_set_show_toggles (ESourceSelector *selector,
|
|
gboolean show_toggles)
|
|
{
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
|
|
if ((show_toggles ? 1 : 0) == (selector->priv->show_toggles ? 1 : 0))
|
|
return;
|
|
|
|
selector->priv->show_toggles = show_toggles;
|
|
|
|
g_object_notify (G_OBJECT (selector), "show-toggles");
|
|
|
|
source_selector_build_model (selector);
|
|
}
|
|
|
|
/* Helper for e_source_selector_get_selection() */
|
|
static gboolean
|
|
source_selector_check_selected (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
ESource *source;
|
|
|
|
struct {
|
|
ESourceSelector *selector;
|
|
GSList *list;
|
|
} *closure = user_data;
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
if (e_source_selector_source_is_selected (closure->selector, source))
|
|
closure->list = g_slist_prepend (closure->list, source);
|
|
else
|
|
g_object_unref (source);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_get_selection:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Get the list of selected sources, i.e. those that were enabled through the
|
|
* corresponding checkboxes in the tree.
|
|
*
|
|
* Returns: A list of the ESources currently selected. The sources will
|
|
* be in the same order as they appear on the screen, and the list should be
|
|
* freed using e_source_selector_free_selection().
|
|
**/
|
|
GSList *
|
|
e_source_selector_get_selection (ESourceSelector *selector)
|
|
{
|
|
struct {
|
|
ESourceSelector *selector;
|
|
GSList *list;
|
|
} closure;
|
|
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
|
|
closure.selector = selector;
|
|
closure.list = NULL;
|
|
|
|
gtk_tree_model_foreach (
|
|
gtk_tree_view_get_model (GTK_TREE_VIEW (selector)),
|
|
(GtkTreeModelForeachFunc) source_selector_check_selected,
|
|
&closure);
|
|
|
|
return g_slist_reverse (closure.list);
|
|
}
|
|
|
|
/**
|
|
* e_source_list_free_selection:
|
|
* @list: A selection list returned by e_source_selector_get_selection().
|
|
*
|
|
* Free the selection list.
|
|
**/
|
|
void
|
|
e_source_selector_free_selection (GSList *list)
|
|
{
|
|
g_slist_foreach (list, (GFunc) g_object_unref, NULL);
|
|
g_slist_free (list);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_set_select_new:
|
|
* @selector: An #ESourceSelector widget
|
|
* @state: A gboolean
|
|
*
|
|
* Set whether or not to select new sources added to @selector.
|
|
**/
|
|
void
|
|
e_source_selector_set_select_new (ESourceSelector *selector,
|
|
gboolean state)
|
|
{
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
|
|
selector->priv->select_new = state;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_select_source:
|
|
* @selector: An #ESourceSelector widget
|
|
* @source: An #ESource.
|
|
*
|
|
* Select @source in @selector.
|
|
**/
|
|
void
|
|
e_source_selector_select_source (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
ESourceSelectorClass *class;
|
|
GtkTreeRowReference *reference;
|
|
GHashTable *source_index;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
/* Make sure the ESource is in our tree model. */
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
g_return_if_fail (gtk_tree_row_reference_valid (reference));
|
|
|
|
class = E_SOURCE_SELECTOR_GET_CLASS (selector);
|
|
g_return_if_fail (class->set_source_selected != NULL);
|
|
|
|
class->set_source_selected (selector, source, TRUE);
|
|
|
|
g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_unselect_source:
|
|
* @selector: An #ESourceSelector widget
|
|
* @source: An #ESource.
|
|
*
|
|
* Unselect @source in @selector.
|
|
**/
|
|
void
|
|
e_source_selector_unselect_source (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
ESourceSelectorClass *class;
|
|
GtkTreeRowReference *reference;
|
|
GHashTable *source_index;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
/* Make sure the ESource is in our tree model. */
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
|
|
/* can be NULL when the source was just removed */
|
|
if (!reference)
|
|
return;
|
|
|
|
g_return_if_fail (gtk_tree_row_reference_valid (reference));
|
|
|
|
class = E_SOURCE_SELECTOR_GET_CLASS (selector);
|
|
g_return_if_fail (class->set_source_selected != NULL);
|
|
|
|
class->set_source_selected (selector, source, FALSE);
|
|
|
|
g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_select_exclusive:
|
|
* @selector: An #ESourceSelector widget
|
|
* @source: An #ESource.
|
|
*
|
|
* Select @source in @selector and unselect all others.
|
|
*
|
|
* Since: 2.30
|
|
**/
|
|
void
|
|
e_source_selector_select_exclusive (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
ESourceSelectorClass *class;
|
|
GHashTable *source_index;
|
|
GHashTableIter iter;
|
|
gpointer key;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
class = E_SOURCE_SELECTOR_GET_CLASS (selector);
|
|
g_return_if_fail (class->set_source_selected != NULL);
|
|
|
|
source_index = selector->priv->source_index;
|
|
g_hash_table_iter_init (&iter, source_index);
|
|
|
|
while (g_hash_table_iter_next (&iter, &key, NULL)) {
|
|
gboolean selected = e_source_equal (key, source);
|
|
class->set_source_selected (selector, key, selected);
|
|
}
|
|
|
|
g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_source_is_selected:
|
|
* @selector: An #ESourceSelector widget
|
|
* @source: An #ESource.
|
|
*
|
|
* Check whether @source is selected in @selector.
|
|
*
|
|
* Returns: %TRUE if @source is currently selected, %FALSE otherwise.
|
|
**/
|
|
gboolean
|
|
e_source_selector_source_is_selected (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
ESourceSelectorClass *class;
|
|
GtkTreeRowReference *reference;
|
|
GHashTable *source_index;
|
|
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
|
|
g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
|
|
|
|
/* Make sure the ESource is in our tree model. */
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
|
|
|
|
class = E_SOURCE_SELECTOR_GET_CLASS (selector);
|
|
g_return_val_if_fail (class->get_source_selected != NULL, FALSE);
|
|
|
|
return class->get_source_selected (selector, source);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_edit_primary_selection:
|
|
* @selector: An #ESourceSelector widget
|
|
*
|
|
* Allows the user to rename the primary selected source by opening an
|
|
* entry box directly in @selector.
|
|
*
|
|
* Since: 2.26
|
|
**/
|
|
void
|
|
e_source_selector_edit_primary_selection (ESourceSelector *selector)
|
|
{
|
|
GtkTreeRowReference *reference;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path = NULL;
|
|
GtkTreeIter iter;
|
|
GList *list;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
column = gtk_tree_view_get_column (tree_view, 0);
|
|
reference = selector->priv->saved_primary_selection;
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
if (reference != NULL)
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
else if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
|
|
if (path == NULL)
|
|
return;
|
|
|
|
/* XXX Because we stuff three renderers in a single column,
|
|
* we have to manually hunt for the text renderer. */
|
|
renderer = NULL;
|
|
list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
|
|
while (list != NULL) {
|
|
renderer = list->data;
|
|
if (GTK_IS_CELL_RENDERER_TEXT (renderer))
|
|
break;
|
|
list = g_list_delete_link (list, list);
|
|
}
|
|
g_list_free (list);
|
|
|
|
/* Make the text cell renderer editable, but only temporarily.
|
|
* We don't want editing to be activated by simply clicking on
|
|
* the source name. Too easy for accidental edits to occur. */
|
|
g_object_set (renderer, "editable", TRUE, NULL);
|
|
gtk_tree_view_expand_to_path (tree_view, path);
|
|
gtk_tree_view_set_cursor_on_cell (
|
|
tree_view, path, column, renderer, TRUE);
|
|
g_object_set (renderer, "editable", FALSE, NULL);
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_ref_primary_selection:
|
|
* @selector: An #ESourceSelector widget
|
|
*
|
|
* Get the primary selected source. The primary selection is the one that is
|
|
* highlighted through the normal #GtkTreeView selection mechanism (as opposed
|
|
* to the "normal" selection, which is the set of source whose checkboxes are
|
|
* checked).
|
|
*
|
|
* The returned #ESource is referenced for thread-safety and must be
|
|
* unreferenced with g_object_unref() when finished with it.
|
|
*
|
|
* Returns: The selected source.
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
ESource *
|
|
e_source_selector_ref_primary_selection (ESourceSelector *selector)
|
|
{
|
|
ESource *source;
|
|
GtkTreeRowReference *reference;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeView *tree_view;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
const gchar *extension_name;
|
|
gboolean have_iter = FALSE;
|
|
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
model = gtk_tree_view_get_model (tree_view);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
reference = selector->priv->saved_primary_selection;
|
|
|
|
if (gtk_tree_row_reference_valid (reference)) {
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
have_iter = gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
if (!have_iter)
|
|
have_iter = gtk_tree_selection_get_selected (
|
|
selection, NULL, &iter);
|
|
|
|
if (!have_iter)
|
|
return NULL;
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
if (!e_source_has_extension (source, extension_name)) {
|
|
g_object_unref (source);
|
|
return NULL;
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_set_primary_selection:
|
|
* @selector: an #ESourceSelector widget
|
|
* @source: an #ESource to select
|
|
*
|
|
* Highlights @source in @selector. The highlighted #ESource is called
|
|
* the primary selection.
|
|
*
|
|
* Do not confuse this function with e_source_selector_select_source(),
|
|
* which activates the check box next to an #ESource's display name in
|
|
* @selector. This function does not alter the check box.
|
|
**/
|
|
void
|
|
e_source_selector_set_primary_selection (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
GHashTable *source_index;
|
|
GtkTreeRowReference *reference;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeView *tree_view;
|
|
GtkTreePath *child_path;
|
|
GtkTreePath *parent_path;
|
|
const gchar *extension_name;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
tree_view = GTK_TREE_VIEW (selector);
|
|
selection = gtk_tree_view_get_selection (tree_view);
|
|
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
|
|
/* XXX Maybe we should return a success/fail boolean? */
|
|
if (!gtk_tree_row_reference_valid (reference))
|
|
return;
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
/* Return silently if attempting to select a parent node
|
|
* lacking the expected extension (e.g. On This Computer). */
|
|
if (!e_source_has_extension (source, extension_name))
|
|
return;
|
|
|
|
/* We block the signal because this all needs to be atomic */
|
|
g_signal_handlers_block_matched (
|
|
selection, G_SIGNAL_MATCH_FUNC,
|
|
0, 0, NULL, selection_changed_callback, NULL);
|
|
gtk_tree_selection_unselect_all (selection);
|
|
g_signal_handlers_unblock_matched (
|
|
selection, G_SIGNAL_MATCH_FUNC,
|
|
0, 0, NULL, selection_changed_callback, NULL);
|
|
|
|
clear_saved_primary_selection (selector);
|
|
|
|
child_path = gtk_tree_row_reference_get_path (reference);
|
|
|
|
parent_path = gtk_tree_path_copy (child_path);
|
|
gtk_tree_path_up (parent_path);
|
|
|
|
if (gtk_tree_view_row_expanded (tree_view, parent_path)) {
|
|
gtk_tree_selection_select_path (selection, child_path);
|
|
} else {
|
|
selector->priv->saved_primary_selection =
|
|
gtk_tree_row_reference_copy (reference);
|
|
g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
|
|
g_object_notify (G_OBJECT (selector), "primary-selection");
|
|
}
|
|
|
|
gtk_tree_path_free (child_path);
|
|
gtk_tree_path_free (parent_path);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_ref_source_by_iter:
|
|
* @selector: an #ESourceSelector
|
|
* @iter: a #GtkTreeIter
|
|
*
|
|
* Returns the #ESource object at @iter.
|
|
*
|
|
* The returned #ESource is referenced for thread-safety and must be
|
|
* unreferenced with g_object_unref() when finished with it.
|
|
*
|
|
* Returns: the #ESource object at @iter, or %NULL
|
|
*
|
|
* Since: 3.8
|
|
**/
|
|
ESource *
|
|
e_source_selector_ref_source_by_iter (ESourceSelector *selector,
|
|
GtkTreeIter *iter)
|
|
{
|
|
ESource *source = NULL;
|
|
GtkTreeModel *model;
|
|
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
g_return_val_if_fail (iter != NULL, NULL);
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_ref_source_by_path:
|
|
* @selector: an #ESourceSelector
|
|
* @path: a #GtkTreePath
|
|
*
|
|
* Returns the #ESource object at @path, or %NULL if @path is invalid.
|
|
*
|
|
* The returned #ESource is referenced for thread-safety and must be
|
|
* unreferenced with g_object_unref() when finished with it.
|
|
*
|
|
* Returns: the #ESource object at @path, or %NULL
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
ESource *
|
|
e_source_selector_ref_source_by_path (ESourceSelector *selector,
|
|
GtkTreePath *path)
|
|
{
|
|
ESource *source = NULL;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
|
|
|
|
if (gtk_tree_model_get_iter (model, &iter, path))
|
|
gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
|
|
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_queue_write:
|
|
* @selector: an #ESourceSelector
|
|
* @source: an #ESource with changes to be written
|
|
*
|
|
* Queues a main loop idle callback to write changes to @source back to
|
|
* the D-Bus registry service.
|
|
*
|
|
* Since: 3.6
|
|
**/
|
|
void
|
|
e_source_selector_queue_write (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
GSource *idle_source;
|
|
GHashTable *pending_writes;
|
|
GMainContext *main_context;
|
|
AsyncContext *async_context;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
main_context = selector->priv->main_context;
|
|
pending_writes = selector->priv->pending_writes;
|
|
|
|
idle_source = g_hash_table_lookup (pending_writes, source);
|
|
if (idle_source != NULL && !g_source_is_destroyed (idle_source))
|
|
return;
|
|
|
|
async_context = g_slice_new0 (AsyncContext);
|
|
async_context->selector = g_object_ref (selector);
|
|
async_context->source = g_object_ref (source);
|
|
|
|
/* Set a higher priority so this idle source runs before our
|
|
* source_selector_cancel_write() signal handler, which will
|
|
* cancel this idle source. Cancellation is the right thing
|
|
* to do when receiving changes from OTHER registry clients,
|
|
* but we don't want to cancel our own changes.
|
|
*
|
|
* XXX This might be an argument for using etags.
|
|
*/
|
|
idle_source = g_idle_source_new ();
|
|
g_hash_table_insert (
|
|
pending_writes,
|
|
g_object_ref (source),
|
|
g_source_ref (idle_source));
|
|
g_source_set_callback (
|
|
idle_source,
|
|
source_selector_write_idle_cb,
|
|
async_context,
|
|
(GDestroyNotify) async_context_free);
|
|
g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
|
|
g_source_attach (idle_source, main_context);
|
|
g_source_unref (idle_source);
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_update_row:
|
|
* @selector: an #ESourceSelector
|
|
* @source: an #ESource
|
|
*
|
|
* Updates the corresponding #GtkTreeModel row for @source.
|
|
*
|
|
* This function is public so it can be called from subclasses like
|
|
* #EClientSelector.
|
|
*
|
|
* Since: 3.8
|
|
**/
|
|
void
|
|
e_source_selector_update_row (ESourceSelector *selector,
|
|
ESource *source)
|
|
{
|
|
GHashTable *source_index;
|
|
ESourceExtension *extension = NULL;
|
|
GtkTreeRowReference *reference;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
const gchar *extension_name;
|
|
const gchar *display_name;
|
|
gboolean selected;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
g_return_if_fail (E_IS_SOURCE (source));
|
|
|
|
source_index = selector->priv->source_index;
|
|
reference = g_hash_table_lookup (source_index, source);
|
|
|
|
/* This function runs when ANY ESource in the registry changes.
|
|
* If the ESource is not in our tree model then return silently. */
|
|
if (reference == NULL)
|
|
return;
|
|
|
|
/* If we do have a row reference, it should be valid. */
|
|
g_return_if_fail (gtk_tree_row_reference_valid (reference));
|
|
|
|
model = gtk_tree_row_reference_get_model (reference);
|
|
path = gtk_tree_row_reference_get_path (reference);
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_path_free (path);
|
|
|
|
display_name = e_source_get_display_name (source);
|
|
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
selected = e_source_selector_source_is_selected (selector, source);
|
|
|
|
if (e_source_has_extension (source, extension_name))
|
|
extension = e_source_get_extension (source, extension_name);
|
|
|
|
if (extension != NULL) {
|
|
GdkColor color;
|
|
const gchar *color_spec = NULL;
|
|
gboolean show_color = FALSE;
|
|
gboolean show_toggle;
|
|
|
|
show_color =
|
|
E_IS_SOURCE_SELECTABLE (extension) &&
|
|
e_source_selector_get_show_colors (selector);
|
|
|
|
if (show_color)
|
|
color_spec = e_source_selectable_get_color (
|
|
E_SOURCE_SELECTABLE (extension));
|
|
|
|
if (color_spec != NULL && *color_spec != '\0')
|
|
show_color = gdk_color_parse (color_spec, &color);
|
|
|
|
show_toggle = e_source_selector_get_show_toggles (selector);
|
|
|
|
gtk_tree_store_set (
|
|
GTK_TREE_STORE (model), &iter,
|
|
COLUMN_NAME, display_name,
|
|
COLUMN_COLOR, show_color ? &color : NULL,
|
|
COLUMN_ACTIVE, selected,
|
|
COLUMN_SHOW_COLOR, show_color,
|
|
COLUMN_SHOW_TOGGLE, show_toggle,
|
|
COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
|
|
COLUMN_SOURCE, source,
|
|
-1);
|
|
} else {
|
|
gtk_tree_store_set (
|
|
GTK_TREE_STORE (model), &iter,
|
|
COLUMN_NAME, display_name,
|
|
COLUMN_COLOR, NULL,
|
|
COLUMN_ACTIVE, FALSE,
|
|
COLUMN_SHOW_COLOR, FALSE,
|
|
COLUMN_SHOW_TOGGLE, FALSE,
|
|
COLUMN_WEIGHT, PANGO_WEIGHT_BOLD,
|
|
COLUMN_SOURCE, source,
|
|
-1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* e_source_selector_update_all_rows:
|
|
* @selector: an #ESourceSelector
|
|
*
|
|
* Calls e_source_selector_update_row() for each #ESource being shown by
|
|
* @selector, according to the #ESourceSelector:extension_name property.
|
|
*
|
|
* Since: 3.10
|
|
**/
|
|
void
|
|
e_source_selector_update_all_rows (ESourceSelector *selector)
|
|
{
|
|
ESourceRegistry *registry;
|
|
GList *list, *link;
|
|
const gchar *extension_name;
|
|
|
|
g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
|
|
|
|
registry = e_source_selector_get_registry (selector);
|
|
extension_name = e_source_selector_get_extension_name (selector);
|
|
|
|
list = e_source_registry_list_sources (registry, extension_name);
|
|
|
|
for (link = list; link != NULL; link = g_list_next (link)) {
|
|
ESource *source = E_SOURCE (link->data);
|
|
e_source_selector_update_row (selector, source);
|
|
}
|
|
|
|
g_list_free_full (list, (GDestroyNotify) g_object_unref);
|
|
}
|
|
|