aboutsummaryrefslogblamecommitdiffstats
path: root/calendar/gncal-full-day.c
blob: 4b4820417a78ace2c1d48661b3c2b2963b2ecf67 (plain) (tree)































































































































































































































































































































                                                                                                                     
/* Full day widget for gncal
 *
 * Copyright (C) 1998 The Free Software Foundation
 *
 * Author: Federico Mena <federico@nuclecu.unam.mx>
 */

#include <gtk/gtkdrawingarea.h>
#include "gncal-full-day.h"


#define TEXT_BORDER 2
#define MIN_WIDTH 200


static void gncal_full_day_class_init    (GncalFullDayClass *class);
static void gncal_full_day_init          (GncalFullDay      *fullday);
static void gncal_full_day_realize       (GtkWidget         *widget);
static void gncal_full_day_size_request  (GtkWidget         *widget,
                      GtkRequisition    *requisition);
static void gncal_full_day_size_allocate (GtkWidget         *widget,
                      GtkAllocation     *allocation);
static gint gncal_full_day_expose        (GtkWidget         *widget,
                      GdkEventExpose    *event);


static GtkContainerClass *parent_class;


guint
gncal_full_day_get_type (void)
{
    static guint full_day_type = 0;

    if (!full_day_type) {
        GtkTypeInfo full_day_info = {
            "GncalFullDay",
            sizeof (GncalFullDay),
            sizeof (GncalFullDayClass),
            (GtkClassInitFunc) gncal_full_day_class_init,
            (GtkObjectInitFunc) gncal_full_day_init,
            (GtkArgSetFunc) NULL,
            (GtkArgGetFunc) NULL
        };

        full_day_type = gtk_type_unique (gtk_container_get_type (), &full_day_info);
    }

    return full_day_type;
}

static void
gncal_full_day_class_init (GncalFullDayClass *class)
{
    GtkWidgetClass    *widget_class;
    GtkContainerClass *container_class;

    widget_class = (GtkWidgetClass *) class;
    container_class = (GtkContainerClass *) class;

    parent_class = gtk_type_class (gtk_container_get_type ());

    widget_class->realize = gncal_full_day_realize;
    widget_class->size_request = gncal_full_day_size_request;
    widget_class->size_allocate = gncal_full_day_size_allocate;
    widget_class->expose_event = gncal_full_day_expose;
}

static void
gncal_full_day_init (GncalFullDay *fullday)
{
    GTK_WIDGET_UNSET_FLAGS (fullday, GTK_NO_WINDOW);

    fullday->calendar = NULL;

    fullday->lower = 0;
    fullday->upper = 0;
    fullday->interval = 30; /* 30 minutes by default */
}

GtkWidget *
gncal_full_day_new (GnomeCalendar *calendar, time_t lower, time_t upper)
{
    GncalFullDay *fullday;

    g_return_val_if_fail (calendar != NULL, NULL);

    fullday = gtk_type_new (gncal_full_day_get_type ());

    fullday->calendar = calendar;

    gncal_full_day_set_bounds (fullday, lower, upper);

    return GTK_WIDGET (fullday);
}

static void
gncal_full_day_realize (GtkWidget *widget)
{
    GdkWindowAttr attributes;
    gint attributes_mask;

    g_return_if_fail (widget != NULL);
    g_return_if_fail (GNCAL_IS_FULL_DAY (widget));

    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

    attributes.window_type = GDK_WINDOW_CHILD;
    attributes.x = widget->allocation.x;
    attributes.y = widget->allocation.y;
    attributes.width = widget->allocation.width;
    attributes.height = widget->allocation.height;
    attributes.wclass = GDK_INPUT_OUTPUT;
    attributes.visual = gtk_widget_get_visual (widget);
    attributes.colormap = gtk_widget_get_colormap (widget);
    attributes.event_mask = (gtk_widget_get_events (widget)
                 | GDK_EXPOSURE_MASK);

    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
    gdk_window_set_user_data (widget->window, widget);

    widget->style = gtk_style_attach (widget->style, widget->window);
    gdk_window_set_background (widget->window, &widget->style->bg[GTK_STATE_PRELIGHT]);
}

static int
get_tm_bounds (GncalFullDay *fullday, struct tm *lower, struct tm *upper)
{
    struct tm tm_lower, tm_upper;
    int lmin, umin;

    /* Lower */

    tm_lower = *localtime (&fullday->lower);

    if ((tm_lower.tm_min % fullday->interval) != 0) {
        tm_lower.tm_min -= tm_lower.tm_min % fullday->interval; /* round down */
        mktime (&tm_lower);
    }

    /* Upper */

    tm_upper = *localtime (&fullday->upper);

    if ((tm_upper.tm_min % fullday->interval) != 0) {
        tm_upper.tm_min += fullday->interval - (tm_upper.tm_min % fullday->interval); /* round up */
        mktime (&tm_upper);
    }

    if (lower)
        *lower = tm_lower;

    if (upper)
        *upper = tm_upper;

    lmin = 60 * tm_lower.tm_hour + tm_lower.tm_min;
    umin = 60 * tm_upper.tm_hour + tm_upper.tm_min;

    return (umin - lmin) / fullday->interval; /* number of rows in view */
}

static int
calc_labels_width (GncalFullDay *fullday)
{
    struct tm cur, upper;
    time_t tim, time_upper;
    int width, max_w;
    char buf[256];

    get_tm_bounds (fullday, &cur, &upper);

    max_w = 0;

    tim = mktime (&cur);
    time_upper = mktime (&upper);

    while (tim < time_upper) {
        strftime (buf, 256, "%R%p", &cur);

        width = gdk_string_width (GTK_WIDGET (fullday)->style->font, buf);

        if (width > max_w)
            max_w = width;

        cur.tm_min += fullday->interval;
        tim = mktime (&cur);
    }

    return max_w;
}

static void
gncal_full_day_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
    GncalFullDay *fullday;
    int labels_width;
    int rows;

    g_return_if_fail (widget != NULL);
    g_return_if_fail (GNCAL_IS_FULL_DAY (widget));
    g_return_if_fail (requisition != NULL);

    fullday = GNCAL_FULL_DAY (widget);

    /* Border and min width */

    labels_width = calc_labels_width (fullday);

    requisition->width = 2 * widget->style->klass->xthickness + 4 * TEXT_BORDER + labels_width + MIN_WIDTH;
    requisition->height = 2 * widget->style->klass->ythickness;

    /* Rows */

    rows = get_tm_bounds (fullday, NULL, NULL);

    requisition->height += (rows * (2 * TEXT_BORDER + widget->style->font->ascent + widget->style->font->descent)
                + (rows - 1)); /* division lines */
}

static void
gncal_full_day_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
    g_return_if_fail (widget != NULL);
    g_return_if_fail (GNCAL_IS_FULL_DAY (widget));
    g_return_if_fail (allocation != NULL);

    widget->allocation = *allocation;

    if (GTK_WIDGET_REALIZED (widget))
        gdk_window_move_resize (widget->window,
                    allocation->x, allocation->y,
                    allocation->width, allocation->height);

    /* FIXME: adjust children */
}

static void
paint_back (GncalFullDay *fullday, GdkRectangle *area)
{
    GtkWidget *widget;
    GdkRectangle rect;
    int x1, y1, width, height;
    int labels_width, division_x;

    widget = GTK_WIDGET (fullday);

    x1 = widget->style->klass->xthickness;
    y1 = widget->style->klass->ythickness;
    width = widget->allocation.width - 2 * x1;
    height = widget->allocation.height - 2 * y1;

    /* Clear and paint frame shadow */

    gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);

    gtk_draw_shadow (widget->style, widget->window,
             GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_IN,
             0, 0,
             widget->allocation.width,
             widget->allocation.height);

    /* Vertical division */

    labels_width = calc_labels_width (fullday);

    division_x = x1 + 2 * TEXT_BORDER + labels_width;

    gtk_draw_vline (widget->style, widget->window,
            GTK_STATE_NORMAL,
            y1,
            y1 + height - 1,
            division_x);
}

static gint
gncal_full_day_expose (GtkWidget *widget, GdkEventExpose *event)
{
    GncalFullDay *fullday;

    g_return_val_if_fail (widget != NULL, FALSE);
    g_return_val_if_fail (GNCAL_IS_FULL_DAY (widget), FALSE);
    g_return_val_if_fail (event != NULL, FALSE);

    if (!GTK_WIDGET_DRAWABLE (widget))
        return FALSE;

    fullday = GNCAL_FULL_DAY (widget);

    if (event->window == widget->window)
        paint_back (fullday, &event->area);

    /* FIXME: paint handles in windows if event->window == blah blah */

    return FALSE;
}

void
gncal_full_day_update (GncalFullDay *fullday)
{
    g_return_if_fail (fullday != NULL);
    g_return_if_fail (GNCAL_IS_FULL_DAY (fullday));

    /* FIXME */
}

void
gncal_full_day_set_bounds (GncalFullDay *fullday, time_t lower, time_t upper)
{
    g_return_if_fail (fullday != NULL);
    g_return_if_fail (GNCAL_IS_FULL_DAY (fullday));

    if ((lower != fullday->lower) || (upper != fullday->upper)) {
        fullday->lower = lower;
        fullday->upper = upper;

        gncal_full_day_update (fullday);
    }
}