aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gnome-month-item.c
blob: 0f5aea462c3878e78d87c35411f6c8ba579996f0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                          





                                               

  
























                                                                                                








                   

                         


                           
                       



























































                                                                                                                

                                                                                                                       


                                                                                                                                
                                                                                                                        
















































































                                                                                                                           



                                                                           









                                                                         















































                                                                                      






                                                                                           
                             




























                                                                                                      
                                                                                                                    



                                                     























































                                                                                                                    
















                                                                                            
                                                                         










                                                                                          


                        
                             
                          

           
                                                      

                                
                            

































                                                                     

                                  

                                               
                                              



































































































































































                                                                                                       






                                                                     
































                                                                                                   




                                                          













































                                                                       





                                                              









                                                             



                                                          








                                                               
/* 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,    /* 7 groups */
    ITEM_HEAD_BOX   = 7,    /* 7 boxes */
    ITEM_HEAD_LABEL = 14,   /* 7 labels */
    ITEM_DAY_GROUP  = 21,   /* 42 groups */
    ITEM_DAY_BOX    = 63,   /* 42 boxes */
    ITEM_DAY_LABEL  = 105   /* 42 labels */
};


/* Number of days in a month, for normal and leap years */
static int days_in_month[2][12] = {
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
    { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

/* The weird month of September 1752, where 3 Sep through 13 Sep were eliminated due to the
 * Gregorian reformation.
 */
static int sept_1752[42] = {
     0,  0,  1,  2, 14, 15, 16,
    17, 18, 19, 20, 21, 22, 23,
    24, 25, 26, 27, 28, 29, 30,
     0,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0
};

#define REFORMATION_DAY 639787      /* First day of the reformation, counted from 1 Jan 1 */
#define MISSING_DAYS 11         /* They corrected out 11 days */
#define THURSDAY 4          /* First day of reformation */
#define SATURDAY 6


enum {
    ARG_0,
    ARG_YEAR,
    ARG_MONTH,
    ARG_X,
    ARG_Y,
    ARG_WIDTH,
    ARG_HEIGHT,
    ARG_ANCHOR,
    ARG_HEAD_PADDING,
    ARG_DAY_PADDING,
    ARG_DAY_NAMES,
    ARG_HEADING_HEIGHT,
    ARG_HEADING_ANCHOR,
    ARG_DAY_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::head_padding", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_HEAD_PADDING);
    gtk_object_add_arg_type ("GnomeMonthItem::day_padding", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_DAY_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::day_anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_DAY_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->head_padding,
                  mitem->head_padding,
                  width - mitem->head_padding,
                  mitem->head_height - mitem->head_padding,
                  &x, &y);

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

/* Resets the position of the days in the calendar */
static void
reshape_days (GnomeMonthItem *mitem)
{
    double width, height;
    double x, y;
    int row, col;
    int i;

    width = mitem->width / 7;
    height = (mitem->height - mitem->head_height) / 6;

    i = 0;

    for (row = 0; row < 6; row++)
        for (col = 0; col < 7; col++) {
            /* Group */
            gnome_canvas_item_set (mitem->items[ITEM_DAY_GROUP + i],
                           "x", width * col,
                           "y", mitem->head_height + height * row,
                           NULL);

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

            /* Label */
            get_label_anchor (mitem->day_anchor,
                      mitem->day_padding,
                      mitem->day_padding,
                      width - mitem->day_padding,
                      height - mitem->day_padding,
                      &x, &y);

            gnome_canvas_item_set (mitem->items[ITEM_DAY_LABEL + i],
                           "x", x,
                           "y", y,
                           "anchor", mitem->day_anchor,
                           NULL);

            i++;
        }
}

/* 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);
    reshape_days (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--10-*-72-72-p-*-iso8859-1",
                           NULL);
    }
}

/* Set the day numbers in the monthly calendar */
static void
set_days (GnomeMonthItem *mitem)
{
    int i;
    char buf[100];

    /* FIXME: actually calculate the numbers */

    for (i = 0; i < 42; i++) {
        sprintf (buf, "%d", i);
        gnome_canvas_item_set (mitem->items[ITEM_DAY_LABEL + i],
                       "text", buf,
                       NULL);
    }
}

/* Creates the items for the days */
static void
create_days (GnomeMonthItem *mitem)
{
    int i;
    char buf[100];
    GdkColor *c;

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

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

        /* Box */
        c = &GTK_WIDGET (GNOME_CANVAS_ITEM (mitem)->canvas)->style->bg[GTK_STATE_NORMAL];
        sprintf (buf, "#%04x%04x%04x", c->red, c->green, c->blue);
        mitem->items[ITEM_DAY_BOX + i] =
            gnome_canvas_item_new (GNOME_CANVAS_GROUP (mitem->items[ITEM_DAY_GROUP + i]),
                           gnome_canvas_rect_get_type (),
                           "outline_color", "black",
                           "fill_color", buf,
                           NULL);

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

    set_days (mitem);
}

/* 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 + 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
     * 42 day groups
     * 42 day boxes
     * 42 day labels
     * ------------------
     * 147 items total
     */

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

    create_headings (mitem);
    create_days (mitem);

    /* 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->head_padding = 0.0;
    mitem->day_padding = 2.0;
    mitem->head_height = 14.0;
    mitem->head_anchor = GTK_ANCHOR_CENTER;
    mitem->day_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_HEAD_PADDING:
        mitem->head_padding = fabs (GTK_VALUE_DOUBLE (*arg));
        reshape (mitem);
        break;

    case ARG_DAY_PADDING:
        mitem->day_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_DAY_ANCHOR:
        mitem->day_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_HEAD_PADDING:
        GTK_VALUE_DOUBLE (*arg) = mitem->head_padding;
        break;

    case ARG_DAY_PADDING:
        GTK_VALUE_DOUBLE (*arg) = mitem->day_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_DAY_ANCHOR:
        GTK_VALUE_ENUM (*arg) = mitem->day_anchor;
        break;

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

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