/* * 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 * */ #include "e-table-specification.h" #include #include #include #include #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; }