aboutsummaryrefslogblamecommitdiffstats
path: root/e-util/e-table-specification.c
blob: bacb5ece75e3b26a86afd363758f136e64d5a487 (plain) (tree)
1
2
3
4
5
6
7
8
  

                          



                                                                
  



                                                                    
  
                                                                   
                                                                             
  
   
 

                                  
                   
                   
 
                        
 
                                          
 




                                                                        
                           
                        

  









                                                                            

                              



                                                     

           









































































































































































































































































































































































                                                                              












































                                                                       
                                             
 
                                           
 

                                                       
                                               
 

                                                               


                                                                              
 








                                                       
 
                                                         
                                               
 
                                                     
                                                                               

 










































                                                                             
           
                                                                  
 
                                   
 

                                                                              
                                              

                                                                      

                                                              

















                                                               


           
                                                               
 

                                                                  


                                                 
 












                                                                       

 

                             

                                                  
  

                                                                         
  
                                             
   
                     

                                                 
 


















                                                                              

 
   












                                                                           
                                                                              
 
                                                              


   







































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