/*
* 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);
}