diff options
Diffstat (limited to 'e-util/e-table-specification.c')
-rw-r--r-- | e-util/e-table-specification.c | 686 |
1 files changed, 686 insertions, 0 deletions
diff --git a/e-util/e-table-specification.c b/e-util/e-table-specification.c new file mode 100644 index 0000000000..bacb5ece75 --- /dev/null +++ b/e-util/e-table-specification.c @@ -0,0 +1,686 @@ +/* + * e-table-specification.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; 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/> + * + */ + +#include "e-table-specification.h" + +#include <stdlib.h> +#include <string.h> + +#include <glib/gstdio.h> + +#include <libedataserver/libedataserver.h> + +#define E_TABLE_SPECIFICATION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TABLE_SPECIFICATION, ETableSpecificationPrivate)) + +struct _ETableSpecificationPrivate { + GPtrArray *columns; + gchar *filename; +}; + +enum { + PROP_0, + PROP_FILENAME +}; + +/* Forward Declarations */ +static void e_table_specification_initable_init + (GInitableIface *interface); + +G_DEFINE_TYPE_WITH_CODE ( + ETableSpecification, + e_table_specification, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE ( + G_TYPE_INITABLE, + e_table_specification_initable_init)) + +static void +table_specification_start_specification (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ETableSpecification *specification, + GError **error) +{ + const gchar *cursor_mode = NULL; + const gchar *selection_mode = NULL; + gboolean fallback_draw_grid = FALSE; + gboolean missing; + + g_free (specification->click_to_add_message); + specification->click_to_add_message = NULL; + + g_free (specification->domain); + specification->domain = NULL; + + /* Use G_MARKUP_COLLECT_TRISTATE to identify + * missing attributes that default to TRUE. */ + g_markup_collect_attributes ( + element_name, + attribute_names, + attribute_values, + error, + + G_MARKUP_COLLECT_TRISTATE, + "alternating-row-colors", + &specification->alternating_row_colors, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "no-headers", + &specification->no_headers, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "click-to-add", + &specification->click_to_add, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "click-to-add-end", + &specification->click_to_add_end, + + G_MARKUP_COLLECT_TRISTATE, + "horizontal-draw-grid", + &specification->horizontal_draw_grid, + + G_MARKUP_COLLECT_TRISTATE, + "vertical-draw-grid", + &specification->vertical_draw_grid, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "draw-grid", + &fallback_draw_grid, + + G_MARKUP_COLLECT_TRISTATE, + "draw-focus", + &specification->draw_focus, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "horizontal-scrolling", + &specification->horizontal_scrolling, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "horizontal-resize", + &specification->horizontal_resize, + + G_MARKUP_COLLECT_TRISTATE, + "allow-grouping", + &specification->allow_grouping, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "selection-mode", + &selection_mode, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "cursor-mode", + &cursor_mode, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "_click-to-add-message", + &specification->click_to_add_message, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "gettext-domain", + &specification->domain, + + G_MARKUP_COLLECT_INVALID); + + /* Additional tweaks. */ + + missing = + (specification->alternating_row_colors != TRUE) && + (specification->alternating_row_colors != FALSE); + if (missing) + specification->alternating_row_colors = TRUE; + + if (!specification->click_to_add) + specification->click_to_add_end = FALSE; + + missing = + (specification->horizontal_draw_grid != TRUE) && + (specification->horizontal_draw_grid != FALSE); + if (missing) + specification->horizontal_draw_grid = fallback_draw_grid; + + missing = + (specification->vertical_draw_grid != TRUE) && + (specification->vertical_draw_grid != FALSE); + if (missing) + specification->vertical_draw_grid = fallback_draw_grid; + + missing = + (specification->draw_focus != TRUE) && + (specification->draw_focus != FALSE); + if (missing) + specification->draw_focus = TRUE; + + missing = + (specification->allow_grouping != TRUE) && + (specification->allow_grouping != FALSE); + if (missing) + specification->allow_grouping = TRUE; + + if (selection_mode == NULL) /* attribute missing */ + specification->selection_mode = GTK_SELECTION_MULTIPLE; + else if (g_ascii_strcasecmp (selection_mode, "single") == 0) + specification->selection_mode = GTK_SELECTION_SINGLE; + else if (g_ascii_strcasecmp (selection_mode, "browse") == 0) + specification->selection_mode = GTK_SELECTION_BROWSE; + else if (g_ascii_strcasecmp (selection_mode, "extended") == 0) + specification->selection_mode = GTK_SELECTION_MULTIPLE; + else /* unrecognized attribute value */ + specification->selection_mode = GTK_SELECTION_MULTIPLE; + + if (cursor_mode == NULL) /* attribute missing */ + specification->cursor_mode = E_CURSOR_SIMPLE; + else if (g_ascii_strcasecmp (cursor_mode, "line") == 0) + specification->cursor_mode = E_CURSOR_LINE; + else if (g_ascii_strcasecmp (cursor_mode, "spreadsheet") == 0) + specification->cursor_mode = E_CURSOR_SPREADSHEET; + else /* unrecognized attribute value */ + specification->cursor_mode = E_CURSOR_SIMPLE; + + if (specification->domain != NULL && *specification->domain == '\0') { + g_free (specification->domain); + specification->domain = NULL; + } +} + +static void +table_specification_start_column (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + GPtrArray *columns, + GError **error) +{ + ETableColumnSpecification *column_spec; + const gchar *model_col_str = NULL; + const gchar *compare_col_str = NULL; + const gchar *expansion_str = NULL; + const gchar *minimum_width_str = NULL; + const gchar *priority_str = NULL; + gint64 int_value; + gboolean missing; + + column_spec = e_table_column_specification_new (); + + /* Use G_MARKUP_COLLECT_TRISTATE to identify + * missing attributes that default to TRUE. */ + g_markup_collect_attributes ( + element_name, + attribute_names, + attribute_values, + error, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "model_col", + &model_col_str, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "compare_col", + &compare_col_str, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "_title", + &column_spec->title, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "pixbuf", + &column_spec->pixbuf, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "expansion", + &expansion_str, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "minimum_width", + &minimum_width_str, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "resizable", + &column_spec->resizable, + + G_MARKUP_COLLECT_BOOLEAN | + G_MARKUP_COLLECT_OPTIONAL, + "disabled", + &column_spec->disabled, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "cell", + &column_spec->cell, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "compare", + &column_spec->compare, + + G_MARKUP_COLLECT_STRDUP | + G_MARKUP_COLLECT_OPTIONAL, + "search", + &column_spec->search, + + G_MARKUP_COLLECT_TRISTATE, + "sortable", + &column_spec->sortable, + + G_MARKUP_COLLECT_STRING | + G_MARKUP_COLLECT_OPTIONAL, + "priority", + &priority_str, + + G_MARKUP_COLLECT_INVALID); + + /* Additional tweaks. */ + + if (model_col_str != NULL) { + int_value = g_ascii_strtoll (model_col_str, NULL, 10); + column_spec->model_col = (gint) int_value; + column_spec->compare_col = (gint) int_value; + } + + if (compare_col_str != NULL) { + int_value = g_ascii_strtoll (compare_col_str, NULL, 10); + column_spec->compare_col = (gint) int_value; + } + + if (column_spec->title == NULL) + column_spec->title = g_strdup (""); + + if (expansion_str != NULL) + column_spec->expansion = g_ascii_strtod (expansion_str, NULL); + + if (minimum_width_str != NULL) { + int_value = g_ascii_strtoll (minimum_width_str, NULL, 10); + column_spec->minimum_width = (gint) int_value; + } + + if (priority_str != NULL) { + int_value = g_ascii_strtoll (priority_str, NULL, 10); + column_spec->priority = (gint) int_value; + } + + missing = + (column_spec->sortable != TRUE) && + (column_spec->sortable != FALSE); + if (missing) + column_spec->sortable = TRUE; + + g_ptr_array_add (columns, g_object_ref (column_spec)); + + g_object_unref (column_spec); +} + +static void +table_specification_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ETableSpecification *specification; + GPtrArray *columns; + + specification = E_TABLE_SPECIFICATION (user_data); + columns = e_table_specification_ref_columns (specification); + + if (g_str_equal (element_name, "ETableSpecification")) + table_specification_start_specification ( + context, + element_name, + attribute_names, + attribute_values, + specification, + error); + + if (g_str_equal (element_name, "ETableColumn")) + table_specification_start_column ( + context, + element_name, + attribute_names, + attribute_values, + columns, + error); + + if (g_str_equal (element_name, "ETableState")) + e_table_state_parse_context_push (context, specification); + + g_ptr_array_unref (columns); +} + +static void +table_specification_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ETableSpecification *specification; + + specification = E_TABLE_SPECIFICATION (user_data); + + if (g_str_equal (element_name, "ETableState")) { + ETableState *state; + + state = e_table_state_parse_context_pop (context); + g_return_if_fail (E_IS_TABLE_STATE (state)); + + g_clear_object (&specification->state); + specification->state = g_object_ref (state); + + g_object_unref (state); + } +} + +static const GMarkupParser table_specification_parser = { + table_specification_start_element, + table_specification_end_element, + NULL, + NULL, + NULL +}; + +static void +table_specification_set_filename (ETableSpecification *specification, + const gchar *filename) +{ + g_return_if_fail (filename != NULL); + g_return_if_fail (specification->priv->filename == NULL); + + specification->priv->filename = g_strdup (filename); +} + +static void +table_specification_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILENAME: + table_specification_set_filename ( + E_TABLE_SPECIFICATION (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +table_specification_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILENAME: + g_value_set_string ( + value, + e_table_specification_get_filename ( + E_TABLE_SPECIFICATION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +table_specification_dispose (GObject *object) +{ + ETableSpecification *specification; + + specification = E_TABLE_SPECIFICATION (object); + + g_clear_object (&specification->state); + + g_ptr_array_set_size (specification->priv->columns, 0); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_table_specification_parent_class)->dispose (object); +} + +static void +table_specification_finalize (GObject *object) +{ + ETableSpecification *specification; + + specification = E_TABLE_SPECIFICATION (object); + + g_free (specification->click_to_add_message); + g_free (specification->domain); + + g_ptr_array_unref (specification->priv->columns); + g_free (specification->priv->filename); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_table_specification_parent_class)->finalize (object); +} + +static gboolean +table_specification_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + ETableSpecification *specification; + GMarkupParseContext *context; + const gchar *filename; + gchar *contents = NULL; + gboolean success = FALSE; + + specification = E_TABLE_SPECIFICATION (initable); + filename = e_table_specification_get_filename (specification); + g_return_val_if_fail (filename != NULL, FALSE); + + if (!g_file_get_contents (filename, &contents, NULL, error)) { + g_warn_if_fail (contents == NULL); + return FALSE; + } + + context = g_markup_parse_context_new ( + &table_specification_parser, + 0, /* no flags */ + g_object_ref (specification), + (GDestroyNotify) g_object_unref); + + if (g_markup_parse_context_parse (context, contents, -1, error)) + success = g_markup_parse_context_end_parse (context, error); + + g_markup_parse_context_free (context); + + if (specification->state == NULL) + specification->state = e_table_state_vanilla (specification); + + e_table_sort_info_set_can_group ( + specification->state->sort_info, + specification->allow_grouping); + + g_free (contents); + + return success; +} + +static void +e_table_specification_class_init (ETableSpecificationClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ETableSpecificationPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = table_specification_set_property; + object_class->get_property = table_specification_get_property; + object_class->dispose = table_specification_dispose; + object_class->finalize = table_specification_finalize; + + g_object_class_install_property ( + object_class, + PROP_FILENAME, + g_param_spec_string ( + "filename", + "Filename", + "Name of the table specification file", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_table_specification_initable_init (GInitableIface *interface) +{ + interface->init = table_specification_initable_init; +} + +static void +e_table_specification_init (ETableSpecification *specification) +{ + specification->priv = + E_TABLE_SPECIFICATION_GET_PRIVATE (specification); + specification->priv->columns = + g_ptr_array_new_with_free_func ( + (GDestroyNotify) g_object_unref); + + specification->alternating_row_colors = TRUE; + specification->no_headers = FALSE; + specification->click_to_add = FALSE; + specification->click_to_add_end = FALSE; + specification->horizontal_draw_grid = FALSE; + specification->vertical_draw_grid = FALSE; + specification->draw_focus = TRUE; + specification->horizontal_scrolling = FALSE; + specification->horizontal_resize = FALSE; + specification->allow_grouping = TRUE; + + specification->cursor_mode = E_CURSOR_SIMPLE; + specification->selection_mode = GTK_SELECTION_MULTIPLE; +} + +/** + * e_table_specification_new: + * @filename: a table specification file + * @error: return location for a #GError, or %NULL + * + * Creates a new #ETableSpecification from @filename. If a file or parse + * error occurs, the function sets @error and returns %NULL. + * + * Returns: an #ETableSpecification, or %NULL + */ +ETableSpecification * +e_table_specification_new (const gchar *filename, + GError **error) +{ + return g_initable_new ( + E_TYPE_TABLE_SPECIFICATION, NULL, error, + "filename", filename, NULL); +} + +/** + * e_table_specification_get_filename: + * @specification: an #ETableSpecification + * + * Returns the filename from which @specification was loaded. + * + * Returns: the table specification filename + **/ +const gchar * +e_table_specification_get_filename (ETableSpecification *specification) +{ + g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL); + + return specification->priv->filename; +} + +/** + * e_table_specification_ref_columns: + * @specification: an #ETableSpecification + * + * Returns a #GPtrArray containing #ETableColumnSpecification instances for + * all columns defined by @specification. The array contents are owned by + * the @specification and should not be modified. Unreference the array + * with g_ptr_array_unref() when finished with it. + * + * Returns: a #GPtrArray of #ETableColumnSpecification instances + **/ +GPtrArray * +e_table_specification_ref_columns (ETableSpecification *specification) +{ + g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL); + + return g_ptr_array_ref (specification->priv->columns); +} + +/** + * e_table_specification_get_column_index: + * @specification: an #ETableSpecification + * @column_spec: an #ETableColumnSpecification + * + * Returns the zero-based index of @column_spec within @specification, + * or a negative value if @column_spec is not defined by @specification. + * + * Returns: the column index of @column_spec, or a negative value + **/ +gint +e_table_specification_get_column_index (ETableSpecification *specification, + ETableColumnSpecification *column_spec) +{ + GPtrArray *columns; + gint column_index = -1; + guint ii; + + g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), -1); + g_return_val_if_fail (E_IS_TABLE_COLUMN_SPECIFICATION (column_spec), -1); + + columns = e_table_specification_ref_columns (specification); + + for (ii = 0; ii < columns->len; ii++) { + gboolean column_specs_equal; + + column_specs_equal = + e_table_column_specification_equal ( + column_spec, columns->pdata[ii]); + + if (column_specs_equal) { + column_index = (gint) ii; + break; + } + } + + g_ptr_array_unref (columns); + + return column_index; +} + |