aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gnome-month-item.c
blob: 0a9227b325be82462f0a922dae00aaa42b2f2a67 (plain) (tree)




































































































































































































































                                                                                                                                
                                                                                                                    























































































































































































































































































































































                                                                                                       
/* General-purpose monthly calendar canvas item for GNOME
 *
 * Copyright (C) 1998 Red Hat Software, Inc.
 *
 * Author: Federico Mena <federico@nuclecu.unam.mx>
 */

#include <config.h>
#include <math.h>
#include <time.h>
#include <gnome.h>
#include "gnome-month-item.h"


/* These are indices into the items array of a GnomeMonthItem structure */
enum {
    ITEM_HEAD_GROUP = 0,
    ITEM_HEAD_BOX   = 7,
    ITEM_HEAD_LABEL = 14
};

enum {
    ARG_0,
    ARG_YEAR,
    ARG_MONTH,
    ARG_X,
    ARG_Y,
    ARG_WIDTH,
    ARG_HEIGHT,
    ARG_ANCHOR,
    ARG_PADDING,
    ARG_DAY_NAMES,
    ARG_HEADING_HEIGHT,
    ARG_HEADING_ANCHOR,
    ARG_START_ON_MONDAY
};


static void gnome_month_item_class_init (GnomeMonthItemClass *class);
static void gnome_month_item_init       (GnomeMonthItem      *mitem);
static void gnome_month_item_destroy    (GtkObject           *object);
static void gnome_month_item_set_arg    (GtkObject           *object,
                     GtkArg              *arg,
                     guint                arg_id);
static void gnome_month_item_get_arg    (GtkObject           *object,
                     GtkArg              *arg,
                     guint                arg_id);



static GnomeCanvasGroupClass *parent_class;


GtkType
gnome_month_item_get_type (void)
{
    static GtkType month_item_type = 0;

    if (!month_item_type) {
        GtkTypeInfo month_item_info = {
            "GnomeMonthItem",
            sizeof (GnomeMonthItem),
            sizeof (GnomeMonthItemClass),
            (GtkClassInitFunc) gnome_month_item_class_init,
            (GtkObjectInitFunc) gnome_month_item_init,
            NULL, /* reserved_1 */
            NULL, /* reserved_2 */
            (GtkClassInitFunc) NULL
        };

        month_item_type = gtk_type_unique (gnome_canvas_group_get_type (), &month_item_info);
    }

    return month_item_type;
}

static void
gnome_month_item_class_init (GnomeMonthItemClass *class)
{
    GtkObjectClass *object_class;
    GnomeCanvasItemClass *item_class;

    object_class = (GtkObjectClass *) class;
    item_class = (GnomeCanvasItemClass *) class;

    parent_class = gtk_type_class (gnome_canvas_group_get_type ());

    gtk_object_add_arg_type ("GnomeMonthItem::year", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_YEAR);
    gtk_object_add_arg_type ("GnomeMonthItem::month", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_MONTH);
    gtk_object_add_arg_type ("GnomeMonthItem::x", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X);
    gtk_object_add_arg_type ("GnomeMonthItem::y", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y);
    gtk_object_add_arg_type ("GnomeMonthItem::width", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_WIDTH);
    gtk_object_add_arg_type ("GnomeMonthItem::height", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_HEIGHT);
    gtk_object_add_arg_type ("GnomeMonthItem::anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_ANCHOR);
    gtk_object_add_arg_type ("GnomeMonthItem::padding", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_PADDING);
    gtk_object_add_arg_type ("GnomeMonthItem::day_names", GTK_TYPE_POINTER, GTK_ARG_READABLE, ARG_DAY_NAMES);
    gtk_object_add_arg_type ("GnomeMonthItem::heading_height", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_HEADING_HEIGHT);
    gtk_object_add_arg_type ("GnomeMonthItem::heading_anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_HEADING_ANCHOR);
    gtk_object_add_arg_type ("GnomeMonthItem::start_on_monday", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_START_ON_MONDAY);

    object_class->destroy = gnome_month_item_destroy;
    object_class->set_arg = gnome_month_item_set_arg;
    object_class->get_arg = gnome_month_item_get_arg;
}

/* Takes an anchor specification and the corners of a rectangle, and returns an anchored point with
 * respect to that rectangle.
 */
static void
get_label_anchor (GtkAnchorType anchor, double x1, double y1, double x2, double y2, double *x, double *y)
{
    switch (anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_W:
    case GTK_ANCHOR_SW:
        *x = x1;
        break;

    case GTK_ANCHOR_N:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_S:
        *x = (x1 + x2) / 2.0;
        break;

    case GTK_ANCHOR_NE:
    case GTK_ANCHOR_E:
    case GTK_ANCHOR_SE:
        *x = x2;
        break;
    }

    switch (anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_N:
    case GTK_ANCHOR_NE:
        *y = y1;
        break;

    case GTK_ANCHOR_W:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_E:
        *y = (y1 + y2) / 2.0;
        break;

    case GTK_ANCHOR_SW:
    case GTK_ANCHOR_S:
    case GTK_ANCHOR_SE:
        *y = y2;
        break;
    }
}

/* Resets the position of the day name headings in the calendar */
static void
reshape_headings (GnomeMonthItem *mitem)
{
    double width;
    int i;
    double x, y;

    width = mitem->width / 7;

    for (i = 0; i < 7; i++) {
        /* Group */
        gnome_canvas_item_set (mitem->items[ITEM_HEAD_GROUP + i],
                       "x", width * i,
                       "y", 0.0,
                       NULL);

        /* Box */
        gnome_canvas_item_set (mitem->items[ITEM_HEAD_BOX + i],
                       "x1", 0.0,
                       "y1", 0.0,
                       "x2", width,
                       "y2", mitem->head_height,
                       NULL);

        /* Label */
        get_label_anchor (mitem->head_anchor,
                  mitem->padding,
                  mitem->padding,
                  width - mitem->padding,
                  mitem->head_height - mitem->padding,
                  &x, &y);

        gnome_canvas_item_set (mitem->items[ITEM_HEAD_LABEL + i],
                       "x", x,
                       "y", y,
                       "anchor", mitem->head_anchor,
                       NULL);
    }
}

/* Changes the positions and resizes the items in the calendar to match the new size of the
 * calendar.
 */
static void
reshape (GnomeMonthItem *mitem)
{
    reshape_headings (mitem);
}

/* Creates the items for the day name headings */
static void
create_headings (GnomeMonthItem *mitem)
{
    int i;

    /* Just create the items; they will be positioned and configured by a call to reshape() */

    for (i = 0; i < 7; i++) {
        /* Group */
        mitem->items[ITEM_HEAD_GROUP + i] =
            gnome_canvas_item_new (GNOME_CANVAS_GROUP (mitem),
                           gnome_canvas_group_get_type (),
                           NULL);

        /* Box */
        mitem->items[ITEM_HEAD_BOX + i] =
            gnome_canvas_item_new (GNOME_CANVAS_GROUP (mitem->items[ITEM_HEAD_GROUP + i]),
                           gnome_canvas_rect_get_type (),
                           "fill_color", "black",
                           NULL);

        /* Label */
        mitem->items[ITEM_HEAD_LABEL + i] =
            gnome_canvas_item_new (GNOME_CANVAS_GROUP (mitem->items[ITEM_HEAD_GROUP + i]),
                           gnome_canvas_text_get_type (),
                           "fill_color", "white",
                           "font", "-adobe-helvetica-medium-r-normal--12-*-72-72-p-*-iso8859-1",
                           NULL);
    }
}

/* Returns a normalized day index (as in sunday to saturday) based on a visible day index */
static int
get_day_index (GnomeMonthItem *mitem, int draw_index)
{
    if (mitem->start_on_monday)
        return (draw_index + 1) % 7;
    else
        return draw_index;
}

/* Resets the text of the day name headings */
static void
set_day_names (GnomeMonthItem *mitem)
{
    int i;

    for (i = 0; i < 7; i++)
        gnome_canvas_item_set (mitem->items[ITEM_HEAD_LABEL + get_day_index (mitem, i)],
                       "text", mitem->day_names[get_day_index (mitem, i)],
                       NULL);
}

/* Creates all the canvas items that make up the calendar */
static void
create_items (GnomeMonthItem *mitem)
{
    /*  7 heading groups
     *  7 heading boxes
     *  7 heading labels
     * ------------------
     * 21 items total
     */

    mitem->items = g_new (GnomeCanvasItem *, 21);

    create_headings (mitem);
    /* FIXME */

    /* Initialize by default to three-letter day names */

    mitem->day_names[0] = _("Sun");
    mitem->day_names[1] = _("Mon");
    mitem->day_names[2] = _("Tue");
    mitem->day_names[3] = _("Wed");
    mitem->day_names[4] = _("Thu");
    mitem->day_names[5] = _("Fri");
    mitem->day_names[6] = _("Sat");
    
    set_day_names (mitem);
    reshape (mitem);
}

static void
gnome_month_item_init (GnomeMonthItem *mitem)
{
    time_t t;
    struct tm *tm;

    /* Initialize to the current month by default */

    t = time (NULL);
    tm = localtime (&t);

    mitem->year = tm->tm_year + 1900;
    mitem->month = tm->tm_mon;

    mitem->x = 0.0;
    mitem->y = 0.0;
    mitem->width = 150.0; /* not unreasonable defaults, I hope */
    mitem->height = 100.0;
    mitem->anchor = GTK_ANCHOR_NW;
    mitem->padding = 0.0;
    mitem->head_height = 14.0;
    mitem->head_anchor = GTK_ANCHOR_CENTER;
}

GnomeCanvasItem *
gnome_month_item_new (GnomeCanvasGroup *parent)
{
    GnomeMonthItem *mitem;

    g_return_val_if_fail (parent != NULL, NULL);
    g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);

    mitem = GNOME_MONTH_ITEM (gnome_canvas_item_new (parent,
                             gnome_month_item_get_type (),
                             NULL));

    gnome_month_item_construct (mitem);

    return GNOME_CANVAS_ITEM (mitem);
}

void
gnome_month_item_construct (GnomeMonthItem *mitem)
{
    g_return_if_fail (mitem != NULL);
    g_return_if_fail (GNOME_IS_MONTH_ITEM (mitem));

    create_items (mitem);
}

static void
free_day_names (GnomeMonthItem *mitem)
{
    int i;

    if (mitem->day_names[0])
        for (i = 0; i < 7; i++)
            g_free (mitem->day_names[i]);
}

static void
gnome_month_item_destroy (GtkObject *object)
{
    GnomeMonthItem *mitem;

    g_return_if_fail (object != NULL);
    g_return_if_fail (GNOME_IS_MONTH_ITEM (object));

    mitem = GNOME_MONTH_ITEM (object);

    free_day_names (mitem);

    if (GTK_OBJECT_CLASS (parent_class)->destroy)
        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

/* Recalculates the position of the toplevel calendar group based on the logical position and anchor */
static void
reanchor (GnomeMonthItem *mitem)
{
    double x, y;

    x = mitem->x;
    y = mitem->y;

    switch (mitem->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_W:
    case GTK_ANCHOR_SW:
        break;

    case GTK_ANCHOR_N:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_S:
        x -= mitem->width / 2;
        break;

    case GTK_ANCHOR_NE:
    case GTK_ANCHOR_E:
    case GTK_ANCHOR_SE:
        x -= mitem->width;
        break;
    }

    switch (mitem->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_N:
    case GTK_ANCHOR_NE:
        break;

    case GTK_ANCHOR_W:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_E:
        y -= mitem->height / 2;
        break;

    case GTK_ANCHOR_SW:
    case GTK_ANCHOR_S:
    case GTK_ANCHOR_SE:
        y -= mitem->height;
        break;
    }

    /* Explicitly use the canvas group class prefix since the month item class has x and y
     * arguments as well.
     */

    gnome_canvas_item_set (GNOME_CANVAS_ITEM (mitem),
                   "GnomeCanvasGroup::x", x,
                   "GnomeCanvasGroup::y", y,
                   NULL);
}

static void
recalc_month (GnomeMonthItem *mitem)
{
    /* FIXME */
}

static void
gnome_month_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
    GnomeMonthItem *mitem;
    char **day_names;
    int i;

    mitem = GNOME_MONTH_ITEM (object);

    switch (arg_id) {
    case ARG_YEAR:
        mitem->year = GTK_VALUE_UINT (*arg);
        recalc_month (mitem);
        break;

    case ARG_MONTH:
        mitem->month = GTK_VALUE_UINT (*arg);
        recalc_month (mitem);
        break;

    case ARG_X:
        mitem->x = GTK_VALUE_DOUBLE (*arg);
        reanchor (mitem);
        break;

    case ARG_Y:
        mitem->y = GTK_VALUE_DOUBLE (*arg);
        reanchor (mitem);
        break;

    case ARG_WIDTH:
        mitem->width = fabs (GTK_VALUE_DOUBLE (*arg));
        reanchor (mitem);
        reshape (mitem);
        break;

    case ARG_HEIGHT:
        mitem->height = fabs (GTK_VALUE_DOUBLE (*arg));
        reanchor (mitem);
        reshape (mitem);
        break;

    case ARG_ANCHOR:
        mitem->anchor = GTK_VALUE_ENUM (*arg);
        reanchor (mitem);
        break;

    case ARG_PADDING:
        mitem->padding = fabs (GTK_VALUE_DOUBLE (*arg));
        reshape (mitem);
        break;

    case ARG_DAY_NAMES:
        day_names = GTK_VALUE_POINTER (*arg);

        /* First, check that none of the names is null */

        for (i = 0; i < 7; i++)
            if (!day_names[i]) {
                g_warning ("Day number %d was NULL; day names cannot be NULL!", i);
                return;
            }

        /* Set the new names */

        free_day_names (mitem);
        for (i = 0; i < 7; i++)
            mitem->day_names[i] = g_strdup (day_names[i]);

        set_day_names (mitem);
        break;

    case ARG_HEADING_HEIGHT:
        mitem->head_height = fabs (GTK_VALUE_DOUBLE (*arg));
        reshape (mitem);
        break;

    case ARG_HEADING_ANCHOR:
        mitem->head_anchor = GTK_VALUE_ENUM (*arg);
        reshape (mitem);
        break;

    case ARG_START_ON_MONDAY:
        mitem->start_on_monday = GTK_VALUE_BOOL (*arg);
        set_day_names (mitem);
        break;

    default:
        break;
    }
}

static void
gnome_month_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
    GnomeMonthItem *mitem;

    mitem = GNOME_MONTH_ITEM (object);

    switch (arg_id) {
    case ARG_YEAR:
        GTK_VALUE_UINT (*arg) = mitem->year;
        break;

    case ARG_MONTH:
        GTK_VALUE_UINT (*arg) = mitem->month;
        break;

    case ARG_X:
        GTK_VALUE_DOUBLE (*arg) = mitem->x;
        break;

    case ARG_Y:
        GTK_VALUE_DOUBLE (*arg) = mitem->y;
        break;

    case ARG_WIDTH:
        GTK_VALUE_DOUBLE (*arg) = mitem->width;
        break;

    case ARG_HEIGHT:
        GTK_VALUE_DOUBLE (*arg) = mitem->height;
        break;

    case ARG_ANCHOR:
        GTK_VALUE_ENUM (*arg) = mitem->anchor;
        break;

    case ARG_PADDING:
        GTK_VALUE_DOUBLE (*arg) = mitem->padding;
        break;

    case ARG_HEADING_HEIGHT:
        GTK_VALUE_DOUBLE (*arg) = mitem->head_height;
        break;

    case ARG_HEADING_ANCHOR:
        GTK_VALUE_ENUM (*arg) = mitem->head_anchor;
        break;

    case ARG_START_ON_MONDAY:
        GTK_VALUE_BOOL (*arg) = mitem->start_on_monday;
        break;

    default:
        arg->type = GTK_TYPE_INVALID;
        break;
    }
}