/*
* 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;
}