aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-table-specification.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-table-specification.c')
-rw-r--r--e-util/e-table-specification.c686
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;
+}
+