From d09d8de870b6697c8a8b262e7e077b871a69b315 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 10 Dec 2012 08:09:59 -0500 Subject: Consolidate base utility libraries into libeutil. Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start. --- e-util/e-table-config.c | 1481 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1481 insertions(+) create mode 100644 e-util/e-table-config.c (limited to 'e-util/e-table-config.c') diff --git a/e-util/e-table-config.c b/e-util/e-table-config.c new file mode 100644 index 0000000000..98f89ffd10 --- /dev/null +++ b/e-util/e-table-config.c @@ -0,0 +1,1481 @@ +/* + * 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 + * + * + * Authors: + * Chris Lahey + * Miguel de Icaza + * + * 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 +#endif + +#include "e-table-config.h" + +#include +#include + +#include +#include + +#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_in_spec (ETableSpecification *spec, + gint model_col) +{ + ETableColumnSpecification **column; + + for (column = spec->columns; *column; column++) { + if ((*column)->disabled) + continue; + if ((*column)->model_col != model_col) + continue; + + return *column; + } + + return NULL; +} + +static gint +find_model_column_by_name (ETableSpecification *spec, + const gchar *s) +{ + ETableColumnSpecification **column; + + for (column = spec->columns; *column; column++) { + + if ((*column)->disabled) + continue; + if (g_ascii_strcasecmp ((*column)->title, s) == 0) + return (*column)->model_col; + } + return -1; +} + +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) { + GtkToggleButton *a, *d; + ETableSortColumn col = + is_sort + ? e_table_sort_info_sorting_get_nth ( + config->temp_state->sort_info, + i) + : e_table_sort_info_grouping_get_nth ( + config->temp_state->sort_info, + i); + + ETableColumnSpecification *column = + find_column_in_spec (config->source_spec, col.column); + + if (!column) { + /* + * 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 + */ + a = GTK_TOGGLE_BUTTON ( + widgets[i].radio_ascending); + d = GTK_TOGGLE_BUTTON ( + widgets[i].radio_descending); + + gtk_toggle_button_set_active (col.ascending ? a : d, 1); + } 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 *info = config->state->sort_info; + GString *res; + gint count, i; + + count = e_table_sort_info_sorting_get_count (info); + res = g_string_new (""); + + for (i = 0; i < count; i++) { + ETableSortColumn col = e_table_sort_info_sorting_get_nth (info, i); + ETableColumnSpecification *column; + + column = find_column_in_spec (config->source_spec, col.column); + if (!column) { + 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, + col.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 *info = config->state->sort_info; + GString *res; + gint count, i; + + if (!e_table_sort_info_get_can_group (info)) + return; + + count = e_table_sort_info_grouping_get_count (info); + res = g_string_new (""); + + for (i = 0; i < count; i++) { + ETableSortColumn col = e_table_sort_info_grouping_get_nth (info, i); + ETableColumnSpecification *column; + + column = find_column_in_spec (config->source_spec, col.column); + if (!column) { + 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, + col.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) +{ + 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); + + if (config->temp_state) { + for (i = 0; i < config->temp_state->col_count; i++) { + gint j, idx; + for (j = 0, idx = 0; j < config->temp_state->columns[i]; j++) + if (!config->source_spec->columns[j]->disabled) + idx++; + + e_table_subset_variable_add (config->shown_model, idx); + e_table_without_hide (config->available_model, GINT_TO_POINTER (idx)); + } + } + e_table_model_thaw ((ETableModel *) config->available_model); + e_table_model_thaw ((ETableModel *) config->shown_model); +} + +static void +config_fields_info_update (ETableConfig *config) +{ + ETableColumnSpecification **column; + GString *res = g_string_new (""); + gint i, j; + + for (i = 0; i < config->state->col_count; i++) { + for (j = 0, column = config->source_spec->columns; *column; column++, j++) { + + if ((*column)->disabled) + continue; + + if (config->state->columns[i] != j) + continue; + + g_string_append (res, dgettext (config->domain, (*column)->title)); + if (i + 1 < config->state->col_count) + g_string_append (res, ", "); + + break; + } + } + + 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) +{ + gint i; + ETableModel *store; + + store = e_table_memory_store_new (store_columns); + for (i = 0; config->source_spec->columns[i]; i++) { + + gchar *text; + + if (config->source_spec->columns[i]->disabled) + continue; + + text = g_strdup (dgettext ( + config->domain, + config->source_spec->columns[i]->title)); + e_table_memory_store_insert_adopt ( + E_TABLE_MEMORY_STORE (store), -1, NULL, text, i); + } + + return store; +} + +static const gchar *spec = +"" +"" +" " +"" +"" +""; + +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 (config->available_model)->source; + + 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]; + GtkToggleButton *toggle_button; + gint idx = sort - base; + gchar *s; + + s = configure_combo_box_get_active (combo_box); + + if (s != NULL) { + ETableSortColumn c; + gint col; + + col = find_model_column_by_name (config->source_spec, s); + if (col == -1) { + g_warning ("sort: This should not happen (%s)", s); + g_free (s); + return; + } + + toggle_button = GTK_TOGGLE_BUTTON ( + config->sort[idx].radio_ascending); + c.ascending = gtk_toggle_button_get_active (toggle_button); + c.column = col; + e_table_sort_info_sorting_set_nth (sort_info, idx, c); + + 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 *t, + ETableConfigSortWidgets *sort) +{ + ETableConfig *config = sort->e_table_config; + ETableSortInfo *si = config->temp_state->sort_info; + ETableConfigSortWidgets *base = &config->sort[0]; + gint idx = sort - base; + ETableSortColumn c; + + c = e_table_sort_info_sorting_get_nth (si, idx); + c.ascending = gtk_toggle_button_get_active (t); + e_table_sort_info_sorting_set_nth (si, idx, c); +} + +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) { + GtkToggleButton *toggle_button; + ETableSortColumn c; + gint col; + + col = find_model_column_by_name (config->source_spec, s); + if (col == -1) { + g_warning ("grouping: this should not happen, %s", s); + g_free (s); + return; + } + + toggle_button = GTK_TOGGLE_BUTTON ( + config->group[idx].radio_ascending); + c.ascending = gtk_toggle_button_get_active (toggle_button); + c.column = col; + e_table_sort_info_grouping_set_nth (sort_info, idx, c); + + 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 *t, + ETableConfigSortWidgets *group) +{ + ETableConfig *config = group->e_table_config; + ETableSortInfo *si = config->temp_state->sort_info; + ETableConfigSortWidgets *base = &config->group[0]; + gint idx = group - base; + ETableSortColumn c; + + c = e_table_sort_info_grouping_get_nth (si, idx); + c.ascending = gtk_toggle_button_get_active (t); + e_table_sort_info_grouping_set_nth (si, idx, c); +} + +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) +{ + GList *columns = NULL; + GList *column; + gint count; + gint i; + + e_table_selected_row_foreach (config->available, add_column, &columns); + columns = g_list_reverse (columns); + + count = g_list_length (columns); + + config->temp_state->columns = g_renew ( + int, config->temp_state->columns, + 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 (column = columns; column; column = column->next) { + config->temp_state->columns[i] = + get_source_model_col_index ( + config, GPOINTER_TO_INT (column->data)); + config->temp_state->expansions[i] = + config->source_spec->columns + [config->temp_state->columns[i]]->expansion; + i++; + } + config->temp_state->col_count += count; + + g_list_free (columns); + + setup_fields (config); +} + +static void +config_button_remove (GtkWidget *widget, + ETableConfig *config) +{ + GList *columns = NULL; + GList *column; + + e_table_selected_row_foreach (config->shown, add_column, &columns); + + for (column = columns; column; column = column->next) { + gint row = GPOINTER_TO_INT (column->data); + + memmove ( + config->temp_state->columns + row, + config->temp_state->columns + row + 1, + sizeof (gint) * (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->columns = g_renew ( + int, config->temp_state->columns, + config->temp_state->col_count); + config->temp_state->expansions = g_renew ( + gdouble, config->temp_state->expansions, + config->temp_state->col_count); + + g_list_free (columns); + + setup_fields (config); +} + +static void +config_button_up (GtkWidget *widget, + ETableConfig *config) +{ + GList *columns = NULL; + GList *column; + gint *new_shown; + gdouble *new_expansions; + gint 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_shown = g_new (int, config->temp_state->col_count); + new_expansions = g_new (double, config->temp_state->col_count); + + column = columns; + + next_col = config->temp_state->columns[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_shown[i - 1] = config->temp_state->columns[i]; + column = column->next; + } else { + new_shown[i - 1] = next_col; + next_col = config->temp_state->columns[i]; + + new_expansions[i - 1] = next_expansion; + next_expansion = config->temp_state->expansions[i]; + } + } + + new_shown[i - 1] = next_col; + new_expansions[i - 1] = next_expansion; + + g_free (config->temp_state->columns); + g_free (config->temp_state->expansions); + + config->temp_state->columns = new_shown; + 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; + gint *new_shown; + gdouble *new_expansions; + gint 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_shown = g_new (gint, config->temp_state->col_count); + new_expansions = g_new (gdouble, config->temp_state->col_count); + + column = columns; + + next_col = + config->temp_state->columns[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_shown[i] = config->temp_state->columns[i - 1]; + column = column->next; + } else { + new_shown[i] = next_col; + next_col = config->temp_state->columns[i - 1]; + + new_expansions[i] = next_expansion; + next_expansion = config->temp_state->expansions[i - 1]; + } + } + + new_shown[0] = next_col; + new_expansions[0] = next_expansion; + + g_free (config->temp_state->columns); + g_free (config->temp_state->expansions); + + config->temp_state->columns = new_shown; + 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) +{ + ETableColumnSpecification **column; + + 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); + + for (column = config->source_spec->columns; *column; column++) { + gchar *label = (*column)->title; + + if ((*column)->disabled) + continue; + + config->column_names = g_slist_append ( + config->column_names, label); + } + + 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); +} + -- cgit v1.2.3