/*
* 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) version 3.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Authors:
* Chris Lahey <clahey@ximian.com>
* Miguel de Icaza <miguel@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
/*
* FIXME:
* Sort Dialog: when text is selected, the toggle button switches state.
* Make Clear all work.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-table-config.h"
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "e-table-memory-store.h"
#include "e-table-without.h"
#include "e-unicode.h"
#include "e-util-private.h"
G_DEFINE_TYPE (ETableConfig, e_table_config, G_TYPE_OBJECT)
enum {
CHANGED,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_STATE
};
enum {
COLUMN_ITEM,
COLUMN_VALUE
};
static guint e_table_config_signals[LAST_SIGNAL] = { 0, };
static void
config_finalize (GObject *object)
{
ETableConfig *config = E_TABLE_CONFIG (object);
if (config->state)
g_object_unref (config->state);
config->state = NULL;
if (config->source_state)
g_object_unref (config->source_state);
config->source_state = NULL;
if (config->source_spec)
g_object_unref (config->source_spec);
config->source_spec = NULL;
g_free (config->header);
config->header = NULL;
g_slist_free (config->column_names);
config->column_names = NULL;
g_free (config->domain);
config->domain = NULL;
G_OBJECT_CLASS (e_table_config_parent_class)->finalize (object);
}
static void
e_table_config_changed (ETableConfig *config,
ETableState *state)
{
g_return_if_fail (E_IS_TABLE_CONFIG (config));
g_signal_emit (config, e_table_config_signals[CHANGED], 0, state);
}
static void
config_dialog_changed (ETableConfig *config)
{
/* enable the apply/ok buttons */
gtk_dialog_set_response_sensitive (
GTK_DIALOG (config->dialog_toplevel),
GTK_RESPONSE_APPLY, TRUE);
gtk_dialog_set_response_sensitive (
GTK_DIALOG (config->dialog_toplevel),
GTK_RESPONSE_OK, TRUE);
}
static void
config_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ETableConfig *config = E_TABLE_CONFIG (object);
switch (property_id) {
case PROP_STATE:
g_value_set_object (value, config->state);
break;
default:
break;
}
}
static void
e_table_config_class_init (ETableConfigClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
class->changed = NULL;
object_class->finalize = config_finalize;
object_class->get_property = config_get_property;
e_table_config_signals[CHANGED] = g_signal_new (
"changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ETableConfigClass, changed),
(GSignalAccumulator) NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_object_class_install_property (
object_class,
PROP_STATE,
g_param_spec_object (
"state",
"State",
NULL,
E_TYPE_TABLE_STATE,
G_PARAM_READABLE));
}
static void
configure_combo_box_add (GtkComboBox *combo_box,
const gchar *item,
const gchar *value)
{
GtkTreeRowReference *reference;
GtkTreeModel *model;
GtkTreePath *path;
GHashTable *index;
GtkTreeIter iter;
model = gtk_combo_box_get_model (combo_box);
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
gtk_list_store_set (
GTK_LIST_STORE (model), &iter,
COLUMN_ITEM, item, COLUMN_VALUE, value, -1);
index = g_object_get_data (G_OBJECT (combo_box), "index");
g_return_if_fail (index != NULL);
/* Add an entry to the tree model index. */
path = gtk_tree_model_get_path (model, &iter);
reference = gtk_tree_row_reference_new (model, path);
g_return_if_fail (reference != NULL);
g_hash_table_insert (index, g_strdup (value), reference);
gtk_tree_path_free (path);
}
static gchar *
configure_combo_box_get_active (GtkComboBox *combo_box)
{
GtkTreeIter iter;
gchar *value = NULL;
if (gtk_combo_box_get_active_iter (combo_box, &iter))
gtk_tree_model_get (
gtk_combo_box_get_model (combo_box), &iter,
COLUMN_VALUE, &value, -1);
if (value != NULL && *value == '\0') {
g_free (value);
value = NULL;
}
return value;
}
static void
configure_combo_box_set_active (GtkComboBox *combo_box,
const gchar *value)
{
GtkTreeRowReference *reference;
GHashTable *index;
index = g_object_get_data (G_OBJECT (combo_box), "index");
g_return_if_fail (index != NULL);
reference = g_hash_table_lookup (index, value);
if (reference != NULL) {
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
model = gtk_tree_row_reference_get_model (reference);
path = gtk_tree_row_reference_get_path (reference);
if (path == NULL)
return;
if (gtk_tree_model_get_iter (model, &iter, path))
gtk_combo_box_set_active_iter (combo_box, &iter);
gtk_tree_path_free (path);
}
}
static ETableColumnSpecification *
find_column_spec_by_name (ETableSpecification *spec,
const gchar *s)
{
ETableColumnSpecification *column;
GPtrArray *array;
guint ii;
array = e_table_specification_ref_columns (spec);
for (ii = 0; ii < array->len; ii++) {
ETableColumnSpecification *candidate;
candidate = g_ptr_array_index (array, ii);
if (candidate->disabled)
continue;
if (g_ascii_strcasecmp (candidate->title, s) == 0) {
column = candidate;
break;
}
}
g_ptr_array_unref (array);
return column;
}
static void
update_sort_and_group_config_dialog (ETableConfig *config,
gboolean is_sort)
{
ETableConfigSortWidgets *widgets;
gint count, i;
if (is_sort) {
count = e_table_sort_info_sorting_get_count (
config->temp_state->sort_info);
widgets = &config->sort[0];
} else {
count = e_table_sort_info_grouping_get_count (
config->temp_state->sort_info);
widgets = &config->group[0];
}
for (i = 0; i < 4; i++) {
gboolean sensitive = (i <= count);
const gchar *text = "";
gtk_widget_set_sensitive (widgets[i].frames, sensitive);
/*
* Sorting is set, auto select the text
*/
g_signal_handler_block (
widgets[i].radio_ascending,
widgets[i].toggled_id);
g_signal_handler_block (
widgets[i].combo,
widgets[i].changed_id);
if (i < count) {
GtkWidget *toggle_button;
ETableColumnSpecification *column;
GtkSortType sort_type;
if (is_sort)
column = e_table_sort_info_sorting_get_nth (
config->temp_state->sort_info, i,
&sort_type);
else
column = e_table_sort_info_grouping_get_nth (
config->temp_state->sort_info, i,
&sort_type);
if (column == NULL) {
/*
* This is a bug in the programmer
* stuff, but by the time we arrive
* here, the user has been given a
* warning
*/
continue;
}
text = column->title;
/*
* Update radio buttons
*/
if (sort_type == GTK_SORT_ASCENDING)
toggle_button = widgets[i].radio_ascending;
else
toggle_button = widgets[i].radio_descending;
gtk_toggle_button_set_active (
GTK_TOGGLE_BUTTON (toggle_button), TRUE);
} else {
GtkToggleButton *t;
t = GTK_TOGGLE_BUTTON (
widgets[i].radio_ascending);
if (is_sort)
g_return_if_fail (
widgets[i].radio_ascending !=
config->group[i].radio_ascending);
else
g_return_if_fail (
widgets[i].radio_ascending !=
config->sort[i].radio_ascending);
gtk_toggle_button_set_active (t, 1);
}
/* Set the text */
configure_combo_box_set_active (
GTK_COMBO_BOX (widgets[i].combo), text);
g_signal_handler_unblock (
widgets[i].radio_ascending,
widgets[i].toggled_id);
g_signal_handler_unblock (
widgets[i].combo,
widgets[i].changed_id);
}
}
static void
config_sort_info_update (ETableConfig *config)
{
ETableSortInfo *sort_info;
GString *res;
gint count, i;
sort_info = config->state->sort_info;
count = e_table_sort_info_sorting_get_count (sort_info);
res = g_string_new ("");
for (i = 0; i < count; i++) {
ETableColumnSpecification *column;
GtkSortType sort_type;
column = e_table_sort_info_sorting_get_nth (
sort_info, i, &sort_type);
if (column == NULL) {
g_warning ("Could not find column model in specification");
continue;
}
g_string_append (res, dgettext (config->domain, (column)->title));
g_string_append_c (res, ' ');
g_string_append (
res,
(sort_type == GTK_SORT_ASCENDING) ?
_("(Ascending)") : _("(Descending)"));
if ((i + 1) != count)
g_string_append (res, ", ");
}
if (res->str[0] == 0)
g_string_append (res, _("Not sorted"));
gtk_label_set_text (GTK_LABEL (config->sort_label), res->str);
g_string_free (res, TRUE);
}
static void
config_group_info_update (ETableConfig *config)
{
ETableSortInfo *sort_info;
GString *res;
gint count, i;
sort_info = config->state->sort_info;
if (!e_table_sort_info_get_can_group (sort_info))
return;
count = e_table_sort_info_grouping_get_count (sort_info);
res = g_string_new ("");
for (i = 0; i < count; i++) {
ETableColumnSpecification *column;
GtkSortType sort_type;
column = e_table_sort_info_grouping_get_nth (
sort_info, i, &sort_type);
if (column == NULL) {
g_warning ("Could not find model column in specification");
continue;
}
g_string_append (res, dgettext (config->domain, (column)->title));
g_string_append_c (res, ' ');
g_string_append (
res,
(sort_type == GTK_SORT_ASCENDING) ?
_("(Ascending)") : _("(Descending)"));
if ((i + 1) != count)
g_string_append (res, ", ");
}
if (res->str[0] == 0)
g_string_append (res, _("No grouping"));
gtk_label_set_text (GTK_LABEL (config->group_label), res->str);
g_string_free (res, TRUE);
}
static void
setup_fields (ETableConfig *config)
{
GPtrArray *array;
gint i;
e_table_model_freeze ((ETableModel *) config->available_model);
e_table_model_freeze ((ETableModel *) config->shown_model);
e_table_without_show_all (config->available_model);
e_table_subset_variable_clear (config->shown_model);
array = e_table_specification_ref_columns (config->source_spec);
if (config->temp_state) {
for (i = 0; i < config->temp_state->col_count; i++) {
ETableColumnSpecification *target_column;
gint idx = 0;
guint jj;
target_column = config->temp_state->column_specs[i];
for (jj = 0; jj < array->len; jj++) {
ETableColumnSpecification *column;
column = g_ptr_array_index (array, jj);
if (column == target_column)
break;
if (!column->disabled)
idx++;
}
e_table_subset_variable_add (config->shown_model, idx);
e_table_without_hide (config->available_model, GINT_TO_POINTER (idx));
}
}
g_ptr_array_unref (array);
e_table_model_thaw ((ETableModel *) config->available_model);
e_table_model_thaw ((ETableModel *) config->shown_model);
}
static void
config_fields_info_update (ETableConfig *config)
{
GString *res = g_string_new ("");
gint ii;
for (ii = 0; ii < config->state->col_count; ii++) {
ETableColumnSpecification *column;
const gchar *title;
column = config->state->column_specs[ii];
if (column->disabled)
continue;
title = dgettext (config->domain, column->title);
g_string_append (res, title);
if (ii + 1 < config->state->col_count)
g_string_append (res, ", ");
}
gtk_label_set_text (GTK_LABEL (config->fields_label), res->str);
g_string_free (res, TRUE);
}
static void
do_sort_and_group_config_dialog (ETableConfig *config,
gboolean is_sort)
{
GtkDialog *dialog;
gint response, running = 1;
config->temp_state = e_table_state_duplicate (config->state);
update_sort_and_group_config_dialog (config, is_sort);
gtk_widget_grab_focus (GTK_WIDGET (
is_sort
? config->sort[0].combo
: config->group[0].combo));
if (is_sort)
dialog = GTK_DIALOG (config->dialog_sort);
else
dialog = GTK_DIALOG (config->dialog_group_by);
gtk_window_set_transient_for (
GTK_WINDOW (dialog), GTK_WINDOW (config->dialog_toplevel));
do {
response = gtk_dialog_run (dialog);
switch (response) {
case 0: /* clear fields */
if (is_sort) {
e_table_sort_info_sorting_truncate (
config->temp_state->sort_info, 0);
} else {
e_table_sort_info_grouping_truncate (
config->temp_state->sort_info, 0);
}
update_sort_and_group_config_dialog (config, is_sort);
break;
case GTK_RESPONSE_OK:
g_object_unref (config->state);
config->state = config->temp_state;
config->temp_state = NULL;
running = 0;
config_dialog_changed (config);
break;
case GTK_RESPONSE_DELETE_EVENT:
case GTK_RESPONSE_CANCEL:
g_object_unref (config->temp_state);
config->temp_state = NULL;
running = 0;
break;
}
} while (running);
gtk_widget_hide (GTK_WIDGET (dialog));
if (is_sort)
config_sort_info_update (config);
else
config_group_info_update (config);
}
static void
do_fields_config_dialog (ETableConfig *config)
{
GtkDialog *dialog;
GtkWidget *widget;
gint response, running = 1;
dialog = GTK_DIALOG (config->dialog_show_fields);
gtk_widget_ensure_style (config->dialog_show_fields);
widget = gtk_dialog_get_content_area (dialog);
gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
widget = gtk_dialog_get_action_area (dialog);
gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
config->temp_state = e_table_state_duplicate (config->state);
setup_fields (config);
gtk_window_set_transient_for (
GTK_WINDOW (config->dialog_show_fields),
GTK_WINDOW (config->dialog_toplevel));
do {
response = gtk_dialog_run (GTK_DIALOG (config->dialog_show_fields));
switch (response) {
case GTK_RESPONSE_OK:
g_object_unref (config->state);
config->state = config->temp_state;
config->temp_state = NULL;
running = 0;
config_dialog_changed (config);
break;
case GTK_RESPONSE_DELETE_EVENT:
case GTK_RESPONSE_CANCEL:
g_object_unref (config->temp_state);
config->temp_state = NULL;
running = 0;
break;
}
} while (running);
gtk_widget_hide (GTK_WIDGET (config->dialog_show_fields));
config_fields_info_update (config);
}
static ETableMemoryStoreColumnInfo store_columns[] = {
E_TABLE_MEMORY_STORE_STRING,
E_TABLE_MEMORY_STORE_INTEGER,
E_TABLE_MEMORY_STORE_TERMINATOR
};
static ETableModel *
create_store (ETableConfig *config)
{
ETableModel *store;
GPtrArray *array;
guint ii;
store = e_table_memory_store_new (store_columns);
array = e_table_specification_ref_columns (config->source_spec);
for (ii = 0; ii < array->len; ii++) {
ETableColumnSpecification *column;
gchar *text;
column = g_ptr_array_index (array, ii);
if (column->disabled)
continue;
text = g_strdup (dgettext (config->domain, column->title));
e_table_memory_store_insert_adopt (
E_TABLE_MEMORY_STORE (store), -1, NULL, text, ii);
}
g_ptr_array_unref (array);
return store;
}
static const gchar *spec =
"<ETableSpecification gettext-domain=\"" GETTEXT_PACKAGE "\""
" no-headers=\"true\" cursor-mode=\"line\" draw-grid=\"false\" "
" draw-focus=\"true\" selection-mode=\"browse\">"
"<ETableColumn model_col= \"0\" _title=\"Name\" minimum_width=\"30\""
" resizable=\"true\" cell=\"string\" compare=\"string\"/>"
"<ETableState> <column source=\"0\"/>"
"<grouping/>"
"</ETableState>"
"</ETableSpecification>";
static GtkWidget *
e_table_proxy_etable_shown_new (ETableModel *store)
{
ETableModel *model = NULL;
GtkWidget *widget;
model = e_table_subset_variable_new (store);
widget = e_table_new (model, NULL, spec, NULL);
atk_object_set_name (
gtk_widget_get_accessible (widget),
_("Show Fields"));
return widget;
}
static GtkWidget *
e_table_proxy_etable_available_new (ETableModel *store)
{
ETableModel *model;
GtkWidget *widget;
model = e_table_without_new (
store, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
e_table_without_show_all (E_TABLE_WITHOUT (model));
widget = e_table_new (model, NULL, spec, NULL);
atk_object_set_name (
gtk_widget_get_accessible (widget),
_("Available Fields"));
return widget;
}
static void
config_button_fields (GtkWidget *widget,
ETableConfig *config)
{
do_fields_config_dialog (config);
}
static void
config_button_sort (GtkWidget *widget,
ETableConfig *config)
{
do_sort_and_group_config_dialog (config, TRUE);
}
static void
config_button_group (GtkWidget *widget,
ETableConfig *config)
{
do_sort_and_group_config_dialog (config, FALSE);
}
static void
dialog_destroyed (gpointer data,
GObject *where_object_was)
{
ETableConfig *config = data;
g_object_unref (config);
}
static void
dialog_response (GtkWidget *dialog,
gint response_id,
ETableConfig *config)
{
if (response_id == GTK_RESPONSE_APPLY
|| response_id == GTK_RESPONSE_OK) {
e_table_config_changed (config, config->state);
}
if (response_id == GTK_RESPONSE_CANCEL
|| response_id == GTK_RESPONSE_OK) {
gtk_widget_destroy (dialog);
}
}
/*
* Invoked by the GtkBuilder auto-connect code
*/
static GtkWidget *
e_table_proxy_gtk_combo_text_new (void)
{
GtkCellRenderer *renderer;
GtkListStore *store;
GtkWidget *combo_box;
GHashTable *index;
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (
GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
gtk_cell_layout_add_attribute (
GTK_CELL_LAYOUT (combo_box), renderer, "text", COLUMN_ITEM);
/* Embed a reverse-lookup index into the widget. */
index = g_hash_table_new_full (
g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) gtk_tree_row_reference_free);
g_object_set_data_full (
G_OBJECT (combo_box), "index", index,
(GDestroyNotify) g_hash_table_destroy);
return combo_box;
}
static void
connect_button (ETableConfig *config,
GtkBuilder *builder,
const gchar *widget_name,
GCallback cback)
{
GtkWidget *button = e_builder_get_widget (builder, widget_name);
if (button)
g_signal_connect (button, "clicked", cback, config);
}
static gint
get_source_model_col_index (ETableConfig *config,
gint idx)
{
gint visible_index;
ETableModel *src_model;
src_model = e_table_subset_get_source_model (
E_TABLE_SUBSET (config->available_model));
visible_index = e_table_subset_view_to_model_row (
E_TABLE_SUBSET (config->available_model), idx);
return GPOINTER_TO_INT (e_table_model_value_at (src_model, 1, visible_index));
}
static void
sort_combo_changed (GtkComboBox *combo_box,
ETableConfigSortWidgets *sort)
{
ETableConfig *config = sort->e_table_config;
ETableSortInfo *sort_info = config->temp_state->sort_info;
ETableConfigSortWidgets *base = &config->sort[0];
gint idx = sort - base;
gchar *s;
s = configure_combo_box_get_active (combo_box);
if (s != NULL) {
ETableColumnSpecification *column;
GtkToggleButton *toggle_button;
GtkSortType sort_type;
column = find_column_spec_by_name (config->source_spec, s);
if (column == NULL) {
g_warning ("sort: This should not happen (%s)", s);
g_free (s);
return;
}
toggle_button = GTK_TOGGLE_BUTTON (
config->sort[idx].radio_ascending);
if (gtk_toggle_button_get_active (toggle_button))
sort_type = GTK_SORT_ASCENDING;
else
sort_type = GTK_SORT_DESCENDING;
e_table_sort_info_sorting_set_nth (
sort_info, idx, column, sort_type);
update_sort_and_group_config_dialog (config, TRUE);
} else {
e_table_sort_info_sorting_truncate (sort_info, idx);
update_sort_and_group_config_dialog (config, TRUE);
}
g_free (s);
}
static void
sort_ascending_toggled (GtkToggleButton *toggle_button,
ETableConfigSortWidgets *sort)
{
ETableConfig *config = sort->e_table_config;
ETableSortInfo *si = config->temp_state->sort_info;
ETableConfigSortWidgets *base = &config->sort[0];
ETableColumnSpecification *column;
GtkSortType sort_type;
gint idx = sort - base;
if (gtk_toggle_button_get_active (toggle_button))
sort_type = GTK_SORT_ASCENDING;
else
sort_type = GTK_SORT_DESCENDING;
column = e_table_sort_info_sorting_get_nth (si, idx, NULL);
e_table_sort_info_sorting_set_nth (si, idx, column, sort_type);
}
static void
configure_sort_dialog (ETableConfig *config,
GtkBuilder *builder)
{
GSList *l;
gint i;
const gchar *algs[] = {
"alignment4",
"alignment3",
"alignment2",
"alignment1",
NULL
};
for (i = 0; i < 4; i++) {
gchar buffer[80];
snprintf (buffer, sizeof (buffer), "sort-combo-%d", i + 1);
config->sort[i].combo = e_table_proxy_gtk_combo_text_new ();
gtk_widget_show (GTK_WIDGET (config->sort[i].combo));
gtk_container_add (
GTK_CONTAINER (e_builder_get_widget (
builder, algs[i])), config->sort[i].combo);
configure_combo_box_add (
GTK_COMBO_BOX (config->sort[i].combo), "", "");
snprintf (buffer, sizeof (buffer), "frame-sort-%d", i + 1);
config->sort[i].frames =
e_builder_get_widget (builder, buffer);
snprintf (
buffer, sizeof (buffer),
"radiobutton-ascending-sort-%d", i + 1);
config->sort[i].radio_ascending = e_builder_get_widget (
builder, buffer);
snprintf (
buffer, sizeof (buffer),
"radiobutton-descending-sort-%d", i + 1);
config->sort[i].radio_descending = e_builder_get_widget (
builder, buffer);
config->sort[i].e_table_config = config;
}
for (l = config->column_names; l; l = l->next) {
gchar *label = l->data;
for (i = 0; i < 4; i++) {
configure_combo_box_add (
GTK_COMBO_BOX (config->sort[i].combo),
dgettext (config->domain, label), label);
}
}
/*
* After we have runtime modified things, signal connect
*/
for (i = 0; i < 4; i++) {
config->sort[i].changed_id = g_signal_connect (
config->sort[i].combo,
"changed", G_CALLBACK (sort_combo_changed),
&config->sort[i]);
config->sort[i].toggled_id = g_signal_connect (
config->sort[i].radio_ascending,
"toggled", G_CALLBACK (sort_ascending_toggled),
&config->sort[i]);
}
}
static void
group_combo_changed (GtkComboBox *combo_box,
ETableConfigSortWidgets *group)
{
ETableConfig *config = group->e_table_config;
ETableSortInfo *sort_info = config->temp_state->sort_info;
ETableConfigSortWidgets *base = &config->group[0];
gint idx = group - base;
gchar *s;
s = configure_combo_box_get_active (combo_box);
if (s != NULL) {
ETableColumnSpecification *column;
GtkToggleButton *toggle_button;
GtkSortType sort_type;
column = find_column_spec_by_name (config->source_spec, s);
if (column == NULL) {
g_warning ("grouping: this should not happen, %s", s);
g_free (s);
return;
}
toggle_button = GTK_TOGGLE_BUTTON (
config->group[idx].radio_ascending);
if (gtk_toggle_button_get_active (toggle_button))
sort_type = GTK_SORT_ASCENDING;
else
sort_type = GTK_SORT_DESCENDING;
e_table_sort_info_grouping_set_nth (
sort_info, idx, column, sort_type);
update_sort_and_group_config_dialog (config, FALSE);
} else {
e_table_sort_info_grouping_truncate (sort_info, idx);
update_sort_and_group_config_dialog (config, FALSE);
}
g_free (s);
}
static void
group_ascending_toggled (GtkToggleButton *toggle_button,
ETableConfigSortWidgets *group)
{
ETableConfig *config = group->e_table_config;
ETableSortInfo *si = config->temp_state->sort_info;
ETableConfigSortWidgets *base = &config->group[0];
ETableColumnSpecification *column;
GtkSortType sort_type;
gint idx = group - base;
if (gtk_toggle_button_get_active (toggle_button))
sort_type = GTK_SORT_ASCENDING;
else
sort_type = GTK_SORT_DESCENDING;
column = e_table_sort_info_grouping_get_nth (si, idx, NULL);
e_table_sort_info_grouping_set_nth (si, idx, column, sort_type);
}
static void
configure_group_dialog (ETableConfig *config,
GtkBuilder *builder)
{
GSList *l;
gint i;
const gchar *vboxes[] = {"vbox7", "vbox9", "vbox11", "vbox13", NULL};
for (i = 0; i < 4; i++) {
gchar buffer[80];
snprintf (buffer, sizeof (buffer), "group-combo-%d", i + 1);
config->group[i].combo = e_table_proxy_gtk_combo_text_new ();
gtk_widget_show (GTK_WIDGET (config->group[i].combo));
gtk_box_pack_start (
GTK_BOX (e_builder_get_widget (builder, vboxes[i])),
config->group[i].combo, FALSE, FALSE, 0);
configure_combo_box_add (
GTK_COMBO_BOX (config->group[i].combo), "", "");
snprintf (buffer, sizeof (buffer), "frame-group-%d", i + 1);
config->group[i].frames =
e_builder_get_widget (builder, buffer);
snprintf (
buffer, sizeof (buffer),
"radiobutton-ascending-group-%d", i + 1);
config->group[i].radio_ascending = e_builder_get_widget (
builder, buffer);
snprintf (
buffer, sizeof (buffer),
"radiobutton-descending-group-%d", i + 1);
config->group[i].radio_descending = e_builder_get_widget (
builder, buffer);
snprintf (
buffer, sizeof (buffer),
"checkbutton-group-%d", i + 1);
config->group[i].view_check = e_builder_get_widget (
builder, buffer);
config->group[i].e_table_config = config;
}
for (l = config->column_names; l; l = l->next) {
gchar *label = l->data;
for (i = 0; i < 4; i++) {
configure_combo_box_add (
GTK_COMBO_BOX (config->group[i].combo),
dgettext (config->domain, label), label);
}
}
/*
* After we have runtime modified things, signal connect
*/
for (i = 0; i < 4; i++) {
config->group[i].changed_id = g_signal_connect (
config->group[i].combo,
"changed", G_CALLBACK (group_combo_changed),
&config->group[i]);
config->group[i].toggled_id = g_signal_connect (
config->group[i].radio_ascending,
"toggled", G_CALLBACK (group_ascending_toggled),
&config->group[i]);
}
}
static void
add_column (gint model_row,
gpointer closure)
{
GList **columns = closure;
*columns = g_list_prepend (*columns, GINT_TO_POINTER (model_row));
}
static void
config_button_add (GtkWidget *widget,
ETableConfig *config)
{
GPtrArray *array;
GList *list = NULL;
GList *link;
gint count;
gint i;
e_table_selected_row_foreach (config->available, add_column, &list);
list = g_list_reverse (list);
count = g_list_length (list);
array = e_table_specification_ref_columns (config->source_spec);
config->temp_state->column_specs = g_renew (
ETableColumnSpecification *,
config->temp_state->column_specs,
config->temp_state->col_count + count);
config->temp_state->expansions = g_renew (
gdouble,
config->temp_state->expansions,
config->temp_state->col_count + count);
i = config->temp_state->col_count;
for (link = list; link != NULL; link = g_list_next (link)) {
ETableColumnSpecification *col_spec;
gint index;
index = get_source_model_col_index (
config, GPOINTER_TO_INT (link->data));
col_spec = g_ptr_array_index (array, index);
config->temp_state->column_specs[i] = g_object_ref (col_spec);
config->temp_state->expansions[i] = col_spec->expansion;
i++;
}
config->temp_state->col_count += count;
g_ptr_array_unref (array);
g_list_free (list);
setup_fields (config);
}
static void
config_button_remove (GtkWidget *widget,
ETableConfig *config)
{
GList *list = NULL;
GList *link;
e_table_selected_row_foreach (config->shown, add_column, &list);
for (link = list; link != NULL; link = g_list_next (link)) {
gint row = GPOINTER_TO_INT (link->data);
memmove (
config->temp_state->column_specs + row,
config->temp_state->column_specs + row + 1,
sizeof (gpointer) * (config->temp_state->col_count - row - 1));
memmove (
config->temp_state->expansions + row,
config->temp_state->expansions + row + 1,
sizeof (gdouble) * (config->temp_state->col_count - row - 1));
config->temp_state->col_count--;
}
config->temp_state->column_specs = g_renew (
ETableColumnSpecification *,
config->temp_state->column_specs,
config->temp_state->col_count);
config->temp_state->expansions = g_renew (
gdouble,
config->temp_state->expansions,
config->temp_state->col_count);
g_list_free (list);
setup_fields (config);
}
static void
config_button_up (GtkWidget *widget,
ETableConfig *config)
{
GList *columns = NULL;
GList *column;
ETableColumnSpecification **new_column_specs;
gdouble *new_expansions;
ETableColumnSpecification *next_col;
gdouble next_expansion;
gint i;
e_table_selected_row_foreach (config->shown, add_column, &columns);
/* if no columns left, just return */
if (columns == NULL)
return;
columns = g_list_reverse (columns);
new_column_specs = g_new (
ETableColumnSpecification *, config->temp_state->col_count);
new_expansions = g_new (gdouble, config->temp_state->col_count);
column = columns;
next_col = config->temp_state->column_specs[0];
next_expansion = config->temp_state->expansions[0];
for (i = 1; i < config->temp_state->col_count; i++) {
if (column && (GPOINTER_TO_INT (column->data) == i)) {
new_expansions[i - 1] =
config->temp_state->expansions[i];
new_column_specs[i - 1] =
config->temp_state->column_specs[i];
column = column->next;
} else {
new_column_specs[i - 1] = next_col;
next_col = config->temp_state->column_specs[i];
new_expansions[i - 1] = next_expansion;
next_expansion = config->temp_state->expansions[i];
}
}
new_column_specs[i - 1] = next_col;
new_expansions[i - 1] = next_expansion;
g_free (config->temp_state->column_specs);
g_free (config->temp_state->expansions);
config->temp_state->column_specs = new_column_specs;
config->temp_state->expansions = new_expansions;
g_list_free (columns);
setup_fields (config);
}
static void
config_button_down (GtkWidget *widget,
ETableConfig *config)
{
GList *columns = NULL;
GList *column;
ETableColumnSpecification **new_column_specs;
gdouble *new_expansions;
ETableColumnSpecification *next_col;
gdouble next_expansion;
gint i;
e_table_selected_row_foreach (config->shown, add_column, &columns);
/* if no columns left, just return */
if (columns == NULL)
return;
new_column_specs = g_new (
ETableColumnSpecification *, config->temp_state->col_count);
new_expansions = g_new (gdouble, config->temp_state->col_count);
column = columns;
next_col =
config->temp_state->column_specs[config->temp_state->col_count - 1];
next_expansion =
config->temp_state->expansions[config->temp_state->col_count - 1];
for (i = config->temp_state->col_count - 1; i > 0; i--) {
if (column && (GPOINTER_TO_INT (column->data) == i - 1)) {
new_expansions[i] =
config->temp_state->expansions[i - 1];
new_column_specs[i] =
config->temp_state->column_specs[i - 1];
column = column->next;
} else {
new_column_specs[i] = next_col;
next_col = config->temp_state->column_specs[i - 1];
new_expansions[i] = next_expansion;
next_expansion = config->temp_state->expansions[i - 1];
}
}
new_column_specs[0] = next_col;
new_expansions[0] = next_expansion;
g_free (config->temp_state->column_specs);
g_free (config->temp_state->expansions);
config->temp_state->column_specs = new_column_specs;
config->temp_state->expansions = new_expansions;
g_list_free (columns);
setup_fields (config);
}
static void
configure_fields_dialog (ETableConfig *config,
GtkBuilder *builder)
{
GtkWidget *scrolled;
GtkWidget *etable;
ETableModel *store = create_store (config);
/* "custom-available" widget */
etable = e_table_proxy_etable_available_new (store);
gtk_widget_show (etable);
scrolled = e_builder_get_widget (builder, "available-scrolled");
gtk_container_add (GTK_CONTAINER (scrolled), etable);
config->available = E_TABLE (etable);
g_object_get (
config->available,
"model", &config->available_model,
NULL);
gtk_widget_show_all (etable);
gtk_label_set_mnemonic_widget (
GTK_LABEL (e_builder_get_widget (
builder, "label-available")), etable);
/* "custom-shown" widget */
etable = e_table_proxy_etable_shown_new (store);
gtk_widget_show (etable);
scrolled = e_builder_get_widget (builder, "shown-scrolled");
gtk_container_add (GTK_CONTAINER (scrolled), etable);
config->shown = E_TABLE (etable);
g_object_get (
config->shown,
"model", &config->shown_model,
NULL);
gtk_widget_show_all (etable);
gtk_label_set_mnemonic_widget (
GTK_LABEL (e_builder_get_widget (
builder, "label-displayed")), etable);
connect_button (
config, builder, "button-add",
G_CALLBACK (config_button_add));
connect_button (
config, builder, "button-remove",
G_CALLBACK (config_button_remove));
connect_button (
config, builder, "button-up",
G_CALLBACK (config_button_up));
connect_button (
config, builder, "button-down",
G_CALLBACK (config_button_down));
setup_fields (config);
g_object_unref (store);
}
static void
setup_gui (ETableConfig *config)
{
GtkBuilder *builder;
gboolean can_group;
can_group = e_table_sort_info_get_can_group (config->state->sort_info);
builder = gtk_builder_new ();
e_load_ui_builder_definition (builder, "e-table-config.ui");
config->dialog_toplevel = e_builder_get_widget (
builder, "e-table-config");
if (config->header)
gtk_window_set_title (
GTK_WINDOW (config->dialog_toplevel),
config->header);
config->dialog_show_fields = e_builder_get_widget (
builder, "dialog-show-fields");
config->dialog_group_by = e_builder_get_widget (
builder, "dialog-group-by");
config->dialog_sort = e_builder_get_widget (
builder, "dialog-sort");
config->sort_label = e_builder_get_widget (
builder, "label-sort");
config->group_label = e_builder_get_widget (
builder, "label-group");
config->fields_label = e_builder_get_widget (
builder, "label-fields");
connect_button (
config, builder, "button-sort",
G_CALLBACK (config_button_sort));
connect_button (
config, builder, "button-group",
G_CALLBACK (config_button_group));
connect_button (
config, builder, "button-fields",
G_CALLBACK (config_button_fields));
if (!can_group) {
GtkWidget *w;
w = e_builder_get_widget (builder, "button-group");
if (w)
gtk_widget_hide (w);
w = e_builder_get_widget (builder, "label3");
if (w)
gtk_widget_hide (w);
w = config->group_label;
if (w)
gtk_widget_hide (w);
}
configure_sort_dialog (config, builder);
configure_group_dialog (config, builder);
configure_fields_dialog (config, builder);
g_object_weak_ref (
G_OBJECT (config->dialog_toplevel),
dialog_destroyed, config);
g_signal_connect (
config->dialog_toplevel, "response",
G_CALLBACK (dialog_response), config);
g_object_unref (builder);
}
static void
e_table_config_init (ETableConfig *config)
{
config->domain = NULL;
}
ETableConfig *
e_table_config_construct (ETableConfig *config,
const gchar *header,
ETableSpecification *spec,
ETableState *state,
GtkWindow *parent_window)
{
GPtrArray *array;
guint ii;
g_return_val_if_fail (config != NULL, NULL);
g_return_val_if_fail (header != NULL, NULL);
g_return_val_if_fail (spec != NULL, NULL);
g_return_val_if_fail (state != NULL, NULL);
config->source_spec = spec;
config->source_state = state;
config->header = g_strdup (header);
g_object_ref (config->source_spec);
g_object_ref (config->source_state);
config->state = e_table_state_duplicate (state);
config->domain = g_strdup (spec->domain);
array = e_table_specification_ref_columns (spec);
for (ii = 0; ii < array->len; ii++) {
ETableColumnSpecification *column;
column = g_ptr_array_index (array, ii);
if (column->disabled)
continue;
config->column_names = g_slist_append (
config->column_names, column->title);
}
g_ptr_array_unref (array);
setup_gui (config);
gtk_window_set_transient_for (GTK_WINDOW (config->dialog_toplevel),
parent_window);
config_sort_info_update (config);
config_group_info_update (config);
config_fields_info_update (config);
return E_TABLE_CONFIG (config);
}
/**
* e_table_config_new:
* @header: The title of the dialog for the ETableConfig.
* @spec: The specification for the columns to allow.
* @state: The current state of the configuration.
*
* Creates a new ETable config object.
*
* Returns: The config object.
*/
ETableConfig *
e_table_config_new (const gchar *header,
ETableSpecification *spec,
ETableState *state,
GtkWindow *parent_window)
{
ETableConfig *config;
GtkDialog *dialog;
GtkWidget *widget;
config = g_object_new (E_TYPE_TABLE_CONFIG, NULL);
e_table_config_construct (
config, header, spec, state, parent_window);
dialog = GTK_DIALOG (config->dialog_toplevel);
gtk_widget_ensure_style (config->dialog_toplevel);
widget = gtk_dialog_get_content_area (dialog);
gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
widget = gtk_dialog_get_action_area (dialog);
gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
gtk_dialog_set_response_sensitive (
GTK_DIALOG (config->dialog_toplevel),
GTK_RESPONSE_APPLY, FALSE);
gtk_widget_show (config->dialog_toplevel);
return E_TABLE_CONFIG (config);
}
/**
* e_table_config_raise:
* @config: The ETableConfig object.
*
* Raises the dialog associated with this ETableConfig object.
*/
void
e_table_config_raise (ETableConfig *config)
{
GdkWindow *window;
window = gtk_widget_get_window (GTK_WIDGET (config->dialog_toplevel));
gdk_window_raise (window);
}