aboutsummaryrefslogblamecommitdiffstats
path: root/e-util/e-table-column-selector.c
blob: beab1570d2fff11774479e28c163ceff40cb3a2e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                            


                                                                           
  



                                                                             
  

                                                                           

































































































































































































































                                                                             
                                                                                    



























































































































































































































                                                                               
/*
 * e-table-column-selector.c
 *
 * 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.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * SECTION: e-table-column-selector
 * @include: e-util/e-util.h
 * @short_description: Select columns for an #ETable or #ETree
 *
 * #ETableColumnSelector is a widget for choosing and ordering the
 * available columns of an #ETable or #ETree.
 **/

#include "e-table-column-selector.h"

#include <config.h>
#include <glib/gi18n-lib.h>

#include "e-util/e-table-specification.h"

#define E_TABLE_COLUMN_SELECTOR_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), E_TYPE_TABLE_COLUMN_SELECTOR, ETableColumnSelectorPrivate))

struct _ETableColumnSelectorPrivate {
    ETableState *state;
};

enum {
    PROP_0,
    PROP_STATE
};

enum {
    COLUMN_ACTIVE,
    COLUMN_TITLE,
    COLUMN_SPECIFICATION,
    COLUMN_EXPANSION,
    NUM_COLUMNS
};

G_DEFINE_TYPE (
    ETableColumnSelector,
    e_table_column_selector,
    E_TYPE_TREE_VIEW_FRAME)

static void
table_column_selector_toggled_cb (GtkCellRendererToggle *renderer,
                                  const gchar *path_string,
                                  GtkTreeView *tree_view)
{
    GtkTreeModel *tree_model;
    GtkTreeIter iter;
    gboolean active;

    tree_model = gtk_tree_view_get_model (tree_view);
    gtk_tree_model_get_iter_from_string (tree_model, &iter, path_string);

    gtk_tree_model_get (tree_model, &iter, COLUMN_ACTIVE, &active, -1);

    gtk_list_store_set (
        GTK_LIST_STORE (tree_model),
        &iter, COLUMN_ACTIVE, !active, -1);
}

static GtkTreeModel *
table_column_selector_build_model (ETableColumnSelector *selector)
{
    GtkListStore *list_store;
    GtkTreeIter iter;
    ETableState *state;
    ETableSpecification *specification;
    GPtrArray *columns;
    GHashTable *columns_added;
    guint ii;

    state = e_table_column_selector_get_state (selector);
    specification = e_table_state_ref_specification (state);
    columns = e_table_specification_ref_columns (specification);

    /* Set of ETableColumnSpecifications to help keep track
     * of which ones are already added to the list store. */
    columns_added = g_hash_table_new (NULL, NULL);

    list_store = gtk_list_store_new (
        NUM_COLUMNS,
        G_TYPE_BOOLEAN,
        G_TYPE_STRING,
        E_TYPE_TABLE_COLUMN_SPECIFICATION,
        G_TYPE_DOUBLE);

    /* Add selected columns from ETableState first. */

    for (ii = 0; ii < state->col_count; ii++) {
        ETableColumnSpecification *column_spec;
        gdouble expansion;

        column_spec = state->column_specs[ii];
        expansion = state->expansions[ii];

        gtk_list_store_append (list_store, &iter);

        gtk_list_store_set (
            list_store, &iter,
            COLUMN_ACTIVE, TRUE,
            COLUMN_TITLE, column_spec->title,
            COLUMN_SPECIFICATION, column_spec,
            COLUMN_EXPANSION, expansion,
            -1);

        g_hash_table_add (columns_added, column_spec);
    }

    /* Add the rest of the columns from ETableSpecification. */

    for (ii = 0; ii < columns->len; ii++) {
        ETableColumnSpecification *column_spec;

        column_spec = g_ptr_array_index (columns, ii);

        if (g_hash_table_contains (columns_added, column_spec))
            continue;

        /* XXX We have this unfortunate "disabled" flag because
         *     past developers made the mistake of having table
         *     config files reference columns by number instead
         *     of name so removing a column would break all the
         *     table config files out in the wild. */
        if (column_spec->disabled)
            continue;

        gtk_list_store_append (list_store, &iter);

        gtk_list_store_set (
            list_store, &iter,
            COLUMN_ACTIVE, FALSE,
            COLUMN_TITLE, column_spec->title,
            COLUMN_SPECIFICATION, column_spec,
            COLUMN_EXPANSION, 1.0,
            -1);

        g_hash_table_add (columns_added, column_spec);
    }

    g_hash_table_destroy (columns_added);

    g_object_unref (specification);
    g_ptr_array_unref (columns);

    return GTK_TREE_MODEL (list_store);
}

static void
table_column_selector_set_state (ETableColumnSelector *selector,
                                 ETableState *state)
{
    g_return_if_fail (E_IS_TABLE_STATE (state));
    g_return_if_fail (selector->priv->state == NULL);

    selector->priv->state = g_object_ref (state);
}

static void
table_column_selector_set_property (GObject *object,
                                    guint property_id,
                                    const GValue *value,
                                    GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_STATE:
            table_column_selector_set_state (
                E_TABLE_COLUMN_SELECTOR (object),
                g_value_get_object (value));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
table_column_selector_get_property (GObject *object,
                                    guint property_id,
                                    GValue *value,
                                    GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_STATE:
            g_value_set_object (
                value,
                e_table_column_selector_get_state (
                E_TABLE_COLUMN_SELECTOR (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
table_column_selector_dispose (GObject *object)
{
    ETableColumnSelectorPrivate *priv;

    priv = E_TABLE_COLUMN_SELECTOR_GET_PRIVATE (object);

    g_clear_object (&priv->state);

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (e_table_column_selector_parent_class)->
        dispose (object);
}

static void
table_column_selector_constructed (GObject *object)
{
    ETableColumnSelector *selector;
    ETreeViewFrame *tree_view_frame;
    GtkAction *action;
    GtkTreeView *tree_view;
    GtkTreeModel *tree_model;
    GtkTreeSelection *selection;
    GtkTreeViewColumn *column;
    GtkCellRenderer *renderer;
    const gchar *tooltip;

    selector = E_TABLE_COLUMN_SELECTOR (object);

    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (e_table_column_selector_parent_class)->constructed (object);

    tree_view_frame = E_TREE_VIEW_FRAME (object);
    tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);

    gtk_tree_view_set_reorderable (tree_view, TRUE);
    gtk_tree_view_set_headers_visible (tree_view, FALSE);

    selection = gtk_tree_view_get_selection (tree_view);
    gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);

    /* Configure the toolbar actions. */

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD);
    gtk_action_set_visible (action, FALSE);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
    gtk_action_set_visible (action, FALSE);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP);
    tooltip = _("Move selected column names to top");
    gtk_action_set_tooltip (action, tooltip);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP);
    tooltip = _("Move selected column names up one row");
    gtk_action_set_tooltip (action, tooltip);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN);
    tooltip = _("Move selected column names down one row");
    gtk_action_set_tooltip (action, tooltip);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM);
    tooltip = _("Move selected column names to bottom");
    gtk_action_set_tooltip (action, tooltip);

    action = e_tree_view_frame_lookup_toolbar_action (
        tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL);
    tooltip = _("Select all column names");
    gtk_action_set_tooltip (action, tooltip);

    /* Configure the tree view columns. */

    column = gtk_tree_view_column_new ();
    renderer = gtk_cell_renderer_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_append_column (tree_view, column);

    g_signal_connect (
        renderer, "toggled",
        G_CALLBACK (table_column_selector_toggled_cb),
        tree_view);

    column = gtk_tree_view_column_new ();
    renderer = gtk_cell_renderer_text_new ();
    gtk_tree_view_column_pack_start (column, renderer, FALSE);
    gtk_tree_view_column_add_attribute (
        column, renderer, "text", COLUMN_TITLE);
    gtk_tree_view_append_column (tree_view, column);

    /* Create and populate the tree model. */

    tree_model = table_column_selector_build_model (selector);
    gtk_tree_view_set_model (tree_view, tree_model);
    g_object_unref (tree_model);
}

static void
e_table_column_selector_class_init (ETableColumnSelectorClass *class)
{
    GObjectClass *object_class;

    g_type_class_add_private (class, sizeof (ETableColumnSelectorPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = table_column_selector_set_property;
    object_class->get_property = table_column_selector_get_property;
    object_class->dispose = table_column_selector_dispose;
    object_class->constructed = table_column_selector_constructed;

    g_object_class_install_property (
        object_class,
        PROP_STATE,
        g_param_spec_object (
            "state",
            "Table State",
            "Column state of the source table",
            E_TYPE_TABLE_STATE,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT_ONLY |
            G_PARAM_STATIC_STRINGS));
}

static void
e_table_column_selector_init (ETableColumnSelector *selector)
{
    selector->priv = E_TABLE_COLUMN_SELECTOR_GET_PRIVATE (selector);
}

/**
 * e_table_column_selector_new:
 * @state: an #ETableState
 *
 * Creates a new #ETableColumnSelector, obtaining the initial column
 * selection content from @state.
 *
 * Note that @state remains unmodified until e_table_column_selector_apply()
 * is called.
 *
 * Returns: an #ETableColumnSelector
 **/
GtkWidget *
e_table_column_selector_new (ETableState *state)
{
    g_return_val_if_fail (E_IS_TABLE_STATE (state), NULL);

    return g_object_new (
        E_TYPE_TABLE_COLUMN_SELECTOR,
        "state", state, NULL);
}

/**
 * e_table_column_selector_get_state:
 * @selector: an #ETableColumnSelector
 *
 * Returns the #ETableState passed to e_table_column_selector_new().
 *
 * Returns: an #ETableState
 **/
ETableState *
e_table_column_selector_get_state (ETableColumnSelector *selector)
{
    g_return_val_if_fail (E_IS_TABLE_COLUMN_SELECTOR (selector), NULL);

    return selector->priv->state;
}

/**
 * e_table_column_selector_apply:
 * @selector: an #ETableColumnSelector
 *
 * Applies the user's column preferences to the @selector's
 * #ETableColumnSelector:state instance.
 **/
void
e_table_column_selector_apply (ETableColumnSelector *selector)
{
    ETableState *state;
    ETreeViewFrame *tree_view_frame;
    GtkTreeView *tree_view;
    GtkTreeModel *tree_model;
    GArray *active_iters;
    GtkTreeIter iter;
    gboolean iter_valid;
    guint ii;

    g_return_if_fail (E_IS_TABLE_COLUMN_SELECTOR (selector));

    tree_view_frame = E_TREE_VIEW_FRAME (selector);
    tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
    tree_model = gtk_tree_view_get_model (tree_view);

    /* Collect all the "active" rows into an array of iterators. */

    active_iters = g_array_new (FALSE, TRUE, sizeof (GtkTreeIter));

    iter_valid = gtk_tree_model_get_iter_first (tree_model, &iter);

    while (iter_valid) {
        gboolean active;

        gtk_tree_model_get (
            tree_model, &iter, COLUMN_ACTIVE, &active, -1);

        if (active)
            g_array_append_val (active_iters, iter);

        iter_valid = gtk_tree_model_iter_next (tree_model, &iter);
    }

    /* Reconstruct the ETableState from the array of iterators. */

    state = e_table_column_selector_get_state (selector);

    for (ii = 0; ii < state->col_count; ii++)
        g_object_unref (state->column_specs[ii]);
    g_free (state->column_specs);
    g_free (state->expansions);

    state->col_count = active_iters->len;
    state->column_specs = g_new0 (
        ETableColumnSpecification *, active_iters->len);
    state->expansions = g_new0 (gdouble, active_iters->len);

    for (ii = 0; ii < active_iters->len; ii++) {
        ETableColumnSpecification *column_spec;
        gdouble expansion;

        iter = g_array_index (active_iters, GtkTreeIter, ii);

        gtk_tree_model_get (
            tree_model, &iter,
            COLUMN_SPECIFICATION, &column_spec,
            COLUMN_EXPANSION, &expansion,
            -1);

        state->column_specs[ii] = g_object_ref (column_spec);
        state->expansions[ii] = expansion;

        g_object_unref (column_spec);
    }

    g_array_free (active_iters, TRUE);
}