aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
authorDamon Chaplin <damon@helixcode.com>2000-07-26 03:45:47 +0800
committerDamon Chaplin <damon@src.gnome.org>2000-07-26 03:45:47 +0800
commit516b594aa9e8abf1643905e7eb48e21f8684c1aa (patch)
tree5c49805707d514c76c1154681d9b593222e1ea34 /widgets
parent28288cc95b02571c9ede385411392d61abd7d409 (diff)
downloadgsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar.gz
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar.bz2
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar.lz
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar.xz
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.tar.zst
gsoc2013-evolution-516b594aa9e8abf1643905e7eb48e21f8684c1aa.zip
new widget and canvas item to replace GtkCalendar. Not quite finished yet.
2000-07-25 Damon Chaplin <damon@helixcode.com> * e-calendar-item.h: * e-calendar.[hc]: new widget and canvas item to replace GtkCalendar. Not quite finished yet. svn path=/trunk/; revision=4322
Diffstat (limited to 'widgets')
-rw-r--r--widgets/misc/.cvsignore1
-rw-r--r--widgets/misc/ChangeLog6
-rw-r--r--widgets/misc/Makefile.am15
-rw-r--r--widgets/misc/e-calendar-item.c1615
-rw-r--r--widgets/misc/e-calendar-item.h151
-rw-r--r--widgets/misc/e-calendar.c481
-rw-r--r--widgets/misc/e-calendar.h87
-rw-r--r--widgets/misc/test-calendar.c74
8 files changed, 2429 insertions, 1 deletions
diff --git a/widgets/misc/.cvsignore b/widgets/misc/.cvsignore
index 47f6730268..411f0fc219 100644
--- a/widgets/misc/.cvsignore
+++ b/widgets/misc/.cvsignore
@@ -6,3 +6,4 @@ Makefile.in
*.lo
*.la
test-title-bar
+test-calendar
diff --git a/widgets/misc/ChangeLog b/widgets/misc/ChangeLog
index def5d46c96..2d57cab426 100644
--- a/widgets/misc/ChangeLog
+++ b/widgets/misc/ChangeLog
@@ -1,3 +1,9 @@
+2000-07-25 Damon Chaplin <damon@helixcode.com>
+
+ * e-calendar-item.h:
+ * e-calendar.[hc]: new widget and canvas item to replace GtkCalendar.
+ Not quite finished yet.
+
2000-07-21 Ettore Perazzoli <ettore@helixcode.com>
* e-title-bar.c (e_title_bar_set_title): We have a `EClippedLabel',
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index b5d2f1bad2..e2872e7006 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -11,6 +11,10 @@ noinst_LIBRARIES = \
libemiscwidgets.a
libemiscwidgets_a_SOURCES = \
+ e-calendar.c \
+ e-calendar.h \
+ e-calendar-item.c \
+ e-calendar-item.h \
e-clipped-label.c \
e-clipped-label.h \
e-scroll-frame.c \
@@ -19,7 +23,8 @@ libemiscwidgets_a_SOURCES = \
e-title-bar.h
noinst_PROGRAMS = \
- test-title-bar
+ test-title-bar \
+ test-calendar
test_title_bar_SOURCES = \
test-title-bar.c
@@ -27,3 +32,11 @@ test_title_bar_SOURCES = \
test_title_bar_LDADD = \
./libemiscwidgets.a \
$(EXTRA_GNOME_LIBS)
+
+test_calendar_SOURCES = \
+ test-calendar.c
+
+test_calendar_LDADD = \
+ ./libemiscwidgets.a \
+ ../../e-util/libeutil.la \
+ $(EXTRA_GNOME_LIBS)
diff --git a/widgets/misc/e-calendar-item.c b/widgets/misc/e-calendar-item.c
new file mode 100644
index 0000000000..0386eabc84
--- /dev/null
+++ b/widgets/misc/e-calendar-item.c
@@ -0,0 +1,1615 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * ECalendarItem - canvas item displaying a calendar.
+ */
+
+#include <config.h>
+#include <time.h>
+#include <glib.h>
+#include <libgnome/gnome-defs.h>
+#include <libgnome/gnome-i18n.h>
+#include <e-util/e-util.h>
+#include "e-calendar-item.h"
+
+
+/* The minimum padding around the numbers in each cell/day. */
+#define E_CALENDAR_ITEM_MIN_CELL_XPAD 4
+#define E_CALENDAR_ITEM_MIN_CELL_YPAD 0
+
+
+/* These are the padding sizes between various pieces of the calendar. */
+/* FIXME: Use decent names eventually. */
+#define E_CALENDAR_ITEM_XPAD1 4
+#define E_CALENDAR_ITEM_XPAD2 2
+#define E_CALENDAR_ITEM_XPAD3 2
+#define E_CALENDAR_ITEM_XPAD4 4
+
+#define E_CALENDAR_ITEM_YPAD3 1
+#define E_CALENDAR_ITEM_YPAD4 0
+#define E_CALENDAR_ITEM_YPAD5 1
+#define E_CALENDAR_ITEM_YPAD6 2
+
+#define E_CALENDAR_ITEM_XPAD11 16
+#define E_CALENDAR_ITEM_XPAD12 2
+#define E_CALENDAR_ITEM_XPAD13 1
+#define E_CALENDAR_ITEM_XPAD14 1
+#define E_CALENDAR_ITEM_XPAD15 2
+#define E_CALENDAR_ITEM_XPAD16 16
+
+/* The number of rows & columns of days in each month. */
+#define E_CALENDAR_ROWS_PER_MONTH 6
+#define E_CALENDAR_COLS_PER_MONTH 7
+
+
+static void e_calendar_item_class_init (ECalendarItemClass *class);
+static void e_calendar_item_init (ECalendarItem *calitem);
+
+static void e_calendar_item_get_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_calendar_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_calendar_item_realize (GnomeCanvasItem *item);
+static void e_calendar_item_unrealize (GnomeCanvasItem *item);
+static void e_calendar_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+static void e_calendar_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_calendar_item_draw_month (ECalendarItem *calitem,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height,
+ int row,
+ int col);
+static void e_calendar_item_draw_day_numbers (ECalendarItem *calitem,
+ GdkDrawable *drawable,
+ int width,
+ int height,
+ int row,
+ int col,
+ int year,
+ int month,
+ int start_weekday,
+ gint cells_x,
+ gint cells_y);
+static double e_calendar_item_point (GnomeCanvasItem *item,
+ double x,
+ double y,
+ int cx,
+ int cy,
+ GnomeCanvasItem **actual_item);
+static gint e_calendar_item_event (GnomeCanvasItem *item,
+ GdkEvent *event);
+static gboolean e_calendar_item_button_press (ECalendarItem *calitem,
+ GdkEvent *event);
+static gboolean e_calendar_item_button_release (ECalendarItem *calitem,
+ GdkEvent *event);
+static gboolean e_calendar_item_motion (ECalendarItem *calitem,
+ GdkEvent *event);
+
+static gboolean e_calendar_item_convert_position_to_day (ECalendarItem *calitem,
+ gint x,
+ gint y,
+ gint *month,
+ gint *day,
+ gboolean *entire_week);
+static void e_calendar_item_get_month_info (ECalendarItem *calitem,
+ gint row,
+ gint col,
+ gint *first_valid_day,
+ gint *last_valid_day);
+static void e_calendar_item_recalc_sizes(ECalendarItem *calitem);
+
+static gint e_calendar_item_get_week_number (ECalendarItem *calitem,
+ gint day,
+ gint month,
+ gint year);
+
+static void e_calendar_item_get_day_style (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ gboolean today,
+ gboolean current_month,
+ gboolean selected,
+ GdkColor **bg_color,
+ GdkColor **fg_color,
+ GdkColor **box_color,
+ gboolean *bold);
+
+static GnomeCanvasItemClass *parent_class;
+
+/* Our arguments. */
+enum {
+ ARG_0,
+ ARG_YEAR,
+ ARG_MONTH,
+ ARG_X1,
+ ARG_Y1,
+ ARG_X2,
+ ARG_Y2,
+ ARG_FONT,
+ ARG_WEEK_NUMBER_FONT,
+ ARG_ROW_HEIGHT,
+ ARG_COLUMN_WIDTH,
+ ARG_MINIMUM_ROWS,
+ ARG_MINIMUM_COLUMNS,
+ ARG_MAXIMUM_ROWS,
+ ARG_MAXIMUM_COLUMNS,
+ ARG_WEEK_START_DAY,
+ ARG_SHOW_WEEK_NUMBERS
+};
+
+
+E_MAKE_TYPE (e_calendar_item, "ECalendarItem", ECalendarItem,
+ e_calendar_item_class_init, e_calendar_item_init,
+ GNOME_TYPE_CANVAS_ITEM)
+
+
+static void
+e_calendar_item_class_init (ECalendarItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ parent_class = gtk_type_class (gnome_canvas_item_get_type());
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ gtk_object_add_arg_type ("ECalendarItem::year",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_YEAR);
+ gtk_object_add_arg_type ("ECalendarItem::month",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_MONTH);
+ gtk_object_add_arg_type ("ECalendarItem::x1",
+ GTK_TYPE_DOUBLE, GTK_ARG_READWRITE,
+ ARG_X1);
+ gtk_object_add_arg_type ("ECalendarItem::y1",
+ GTK_TYPE_DOUBLE, GTK_ARG_READWRITE,
+ ARG_Y1);
+ gtk_object_add_arg_type ("ECalendarItem::x2",
+ GTK_TYPE_DOUBLE, GTK_ARG_READWRITE,
+ ARG_X2);
+ gtk_object_add_arg_type ("ECalendarItem::y2",
+ GTK_TYPE_DOUBLE, GTK_ARG_READWRITE,
+ ARG_Y2);
+ gtk_object_add_arg_type ("ECalendarItem::font",
+ GTK_TYPE_GDK_FONT, GTK_ARG_READWRITE,
+ ARG_FONT);
+ gtk_object_add_arg_type ("ECalendarItem::week_number_font",
+ GTK_TYPE_GDK_FONT, GTK_ARG_READWRITE,
+ ARG_WEEK_NUMBER_FONT);
+ gtk_object_add_arg_type ("ECalendarItem::row_height",
+ GTK_TYPE_DOUBLE, GTK_ARG_READABLE,
+ ARG_ROW_HEIGHT);
+ gtk_object_add_arg_type ("ECalendarItem::column_width",
+ GTK_TYPE_DOUBLE, GTK_ARG_READABLE,
+ ARG_COLUMN_WIDTH);
+ gtk_object_add_arg_type ("ECalendarItem::mininum_rows",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_MINIMUM_ROWS);
+ gtk_object_add_arg_type ("ECalendarItem::minimum_columns",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_MINIMUM_COLUMNS);
+ gtk_object_add_arg_type ("ECalendarItem::maximum_rows",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_MAXIMUM_ROWS);
+ gtk_object_add_arg_type ("ECalendarItem::maximum_columns",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_MAXIMUM_COLUMNS);
+ gtk_object_add_arg_type ("ECalendarItem::week_start_day",
+ GTK_TYPE_INT, GTK_ARG_READWRITE,
+ ARG_WEEK_START_DAY);
+ gtk_object_add_arg_type ("ECalendarItem::show_week_numbers",
+ GTK_TYPE_BOOL, GTK_ARG_READWRITE,
+ ARG_SHOW_WEEK_NUMBERS);
+
+ object_class->get_arg = e_calendar_item_get_arg;
+ object_class->set_arg = e_calendar_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->realize = e_calendar_item_realize;
+ item_class->unrealize = e_calendar_item_unrealize;
+ item_class->update = e_calendar_item_update;
+ item_class->draw = e_calendar_item_draw;
+ item_class->point = e_calendar_item_point;
+ item_class->event = e_calendar_item_event;
+}
+
+
+static void
+e_calendar_item_init (ECalendarItem *calitem)
+{
+ struct tm *tmp_tm;
+ time_t t;
+
+ /* Set the default time to the current month. */
+ t = time (NULL);
+ tmp_tm = localtime (&t);
+ calitem->year = tmp_tm->tm_year + 1900;
+ calitem->month = tmp_tm->tm_mon;
+
+ calitem->min_cols = 1;
+ calitem->min_rows = 1;
+ calitem->show_week_numbers = FALSE;
+ calitem->week_start_day = 0;
+ calitem->expand = TRUE;
+
+ calitem->x1 = 0.0;
+ calitem->y1 = 0.0;
+ calitem->x2 = 0.0;
+ calitem->y2 = 0.0;
+
+ calitem->selection_start_month_offset = -1;
+
+ /* Translators: These are the first characters of each day of the
+ week, 'M' for 'Monday', 'T' for Tuesday etc. */
+ calitem->days = _("MTWTFSS");
+}
+
+
+static void
+e_calendar_item_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ ECalendarItem *calitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ calitem = E_CALENDAR_ITEM (o);
+
+ switch (arg_id) {
+ case ARG_YEAR:
+ GTK_VALUE_INT (*arg) = calitem->year;
+ break;
+ case ARG_MONTH:
+ GTK_VALUE_INT (*arg) = calitem->month;
+ break;
+ case ARG_X1:
+ GTK_VALUE_DOUBLE (*arg) = calitem->x1;
+ break;
+ case ARG_Y1:
+ GTK_VALUE_DOUBLE (*arg) = calitem->y1;
+ break;
+ case ARG_X2:
+ GTK_VALUE_DOUBLE (*arg) = calitem->x2;
+ break;
+ case ARG_Y2:
+ GTK_VALUE_DOUBLE (*arg) = calitem->y2;
+ break;
+ case ARG_FONT:
+ GTK_VALUE_BOXED (*arg) = calitem->font;
+ break;
+ case ARG_WEEK_NUMBER_FONT:
+ GTK_VALUE_BOXED (*arg) = calitem->week_number_font;
+ break;
+ case ARG_ROW_HEIGHT:
+ e_calendar_item_recalc_sizes (calitem);
+ GTK_VALUE_DOUBLE (*arg) = calitem->min_month_height;
+ break;
+ case ARG_COLUMN_WIDTH:
+ e_calendar_item_recalc_sizes (calitem);
+ GTK_VALUE_DOUBLE (*arg) = calitem->min_month_width;
+ break;
+ case ARG_MINIMUM_ROWS:
+ GTK_VALUE_INT (*arg) = calitem->min_rows;
+ break;
+ case ARG_MINIMUM_COLUMNS:
+ GTK_VALUE_INT (*arg) = calitem->min_cols;
+ break;
+ case ARG_MAXIMUM_ROWS:
+ GTK_VALUE_INT (*arg) = calitem->max_rows;
+ break;
+ case ARG_MAXIMUM_COLUMNS:
+ GTK_VALUE_INT (*arg) = calitem->max_cols;
+ break;
+ case ARG_WEEK_START_DAY:
+ GTK_VALUE_INT (*arg) = calitem->week_start_day;
+ break;
+ case ARG_SHOW_WEEK_NUMBERS:
+ GTK_VALUE_BOOL (*arg) = calitem->show_week_numbers;
+ break;
+ }
+}
+
+
+static void
+e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ ECalendarItem *calitem;
+ GdkFont *font;
+ gboolean need_reshape = FALSE;
+ gdouble dvalue;
+ gint ivalue;
+ gboolean bvalue;
+
+ item = GNOME_CANVAS_ITEM (o);
+ calitem = E_CALENDAR_ITEM (o);
+
+ switch (arg_id){
+ case ARG_YEAR:
+ ivalue = GTK_VALUE_INT (*arg);
+ if (calitem->year != ivalue) {
+ calitem->year = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_MONTH:
+ ivalue = GTK_VALUE_INT (*arg);
+ if (calitem->month != ivalue) {
+ calitem->month = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_X1:
+ dvalue = GTK_VALUE_DOUBLE (*arg);
+ if (calitem->x1 != dvalue) {
+ calitem->x1 = dvalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_Y1:
+ dvalue = GTK_VALUE_DOUBLE (*arg);
+ if (calitem->y1 != dvalue) {
+ calitem->y1 = dvalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_X2:
+ dvalue = GTK_VALUE_DOUBLE (*arg);
+ if (calitem->x2 != dvalue) {
+ calitem->x2 = dvalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_Y2:
+ dvalue = GTK_VALUE_DOUBLE (*arg);
+ if (calitem->y2 != dvalue) {
+ calitem->y2 = dvalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_FONT:
+ font = GTK_VALUE_BOXED (*arg);
+ if (calitem->font != font) {
+ if (calitem->font)
+ gdk_font_unref (calitem->font);
+ calitem->font = font;
+ if (font)
+ gdk_font_ref (font);
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_WEEK_NUMBER_FONT:
+ font = GTK_VALUE_BOXED (*arg);
+ if (calitem->week_number_font != font) {
+ if (calitem->week_number_font)
+ gdk_font_unref (calitem->week_number_font);
+ calitem->week_number_font = font;
+ if (font)
+ gdk_font_ref (font);
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_MINIMUM_ROWS:
+ ivalue = GTK_VALUE_INT (*arg);
+ ivalue = MAX (1, ivalue);
+ if (calitem->min_rows != ivalue) {
+ calitem->min_rows = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_MINIMUM_COLUMNS:
+ ivalue = GTK_VALUE_INT (*arg);
+ ivalue = MAX (1, ivalue);
+ if (calitem->min_cols != ivalue) {
+ calitem->min_cols = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_MAXIMUM_ROWS:
+ ivalue = GTK_VALUE_INT (*arg);
+ if (calitem->max_rows != ivalue) {
+ calitem->max_rows = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_MAXIMUM_COLUMNS:
+ ivalue = GTK_VALUE_INT (*arg);
+ if (calitem->max_cols != ivalue) {
+ calitem->max_cols = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_WEEK_START_DAY:
+ ivalue = GTK_VALUE_INT (*arg);
+ if (calitem->week_start_day != ivalue) {
+ calitem->week_start_day = ivalue;
+ need_reshape = TRUE;
+ }
+ break;
+ case ARG_SHOW_WEEK_NUMBERS:
+ bvalue = GTK_VALUE_BOOL (*arg);
+ if (calitem->show_week_numbers != ivalue) {
+ calitem->show_week_numbers = bvalue;
+ need_reshape = TRUE;
+ }
+ break;
+ default:
+ g_warning ("Invalid arg");
+ }
+
+ /* FIXME: finish. */
+ if (need_reshape) {
+ gnome_canvas_item_request_update (item);
+ }
+}
+
+
+static void
+e_calendar_item_realize (GnomeCanvasItem *item)
+{
+ ECalendarItem *calitem;
+ GdkColormap *colormap;
+ gboolean success[E_CALENDAR_COLOR_LAST];
+ gint nfailed;
+
+ calitem = E_CALENDAR_ITEM (item);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+
+ calitem->colors[E_CALENDAR_COLOR_SELECTION].red = 65535;
+ calitem->colors[E_CALENDAR_COLOR_SELECTION].green = 65535;
+ calitem->colors[E_CALENDAR_COLOR_SELECTION].blue = 65535;
+
+ calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT].red = 56000;
+ calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT].green = 57000;
+ calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT].blue = 57000;
+
+ calitem->colors[E_CALENDAR_COLOR_TODAY].red = 65535;
+ calitem->colors[E_CALENDAR_COLOR_TODAY].green = 0;
+ calitem->colors[E_CALENDAR_COLOR_TODAY].blue = 0;
+
+ nfailed = gdk_colormap_alloc_colors (colormap, calitem->colors,
+ E_CALENDAR_COLOR_LAST, FALSE,
+ TRUE, success);
+ if (nfailed)
+ g_warning ("Failed to allocate all colors");
+}
+
+
+static void
+e_calendar_item_unrealize (GnomeCanvasItem *item)
+{
+ ECalendarItem *calitem;
+ GdkColormap *colormap;
+ gint i;
+
+ calitem = E_CALENDAR_ITEM (item);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+
+ for (i = 0; i < E_CALENDAR_COLOR_LAST; i++)
+ gdk_colors_free (colormap, &calitem->colors[i].pixel, 1, 0);
+}
+
+
+static void
+e_calendar_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags)
+{
+ ECalendarItem *calitem;
+ GtkStyle *style;
+ GdkFont *font;
+ gint char_height, width, height, space, space_per_cal, space_per_cell;
+ gint xthickness, ythickness;
+
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);
+
+ calitem = E_CALENDAR_ITEM (item);
+ style = GTK_WIDGET (item->canvas)->style;
+ xthickness = style->klass->xthickness;
+ ythickness = style->klass->ythickness;
+
+ item->x1 = calitem->x1;
+ item->y1 = calitem->y1;
+ item->x2 = calitem->x2 >= calitem->x1 ? calitem->x2 : calitem->x1;
+ item->y2 = calitem->y2 >= calitem->y1 ? calitem->y2 : calitem->y1;
+
+ /*
+ * Calculate the new layout of the calendar.
+ */
+
+ /* Make sure the minimum row width & cell height and the widths of
+ all the digits and characters are up to date. */
+ e_calendar_item_recalc_sizes (calitem);
+
+ /* Calculate how many rows & cols we can fit in. */
+ width = item->x2 - item->x1;
+ height = item->y2 - item->y1;
+
+ width -= xthickness * 2;
+ height -= ythickness * 2;
+
+ calitem->rows = height / calitem->min_month_height;
+ calitem->rows = MAX (calitem->rows, calitem->min_rows);
+ if (calitem->max_rows > 0)
+ calitem->rows = MIN (calitem->rows, calitem->max_rows);
+ calitem->cols = width / calitem->min_month_width;
+ calitem->cols = MAX (calitem->cols, calitem->min_cols);
+ if (calitem->max_cols > 0)
+ calitem->cols = MIN (calitem->cols, calitem->max_cols);
+
+ /* Split up the empty space according to the configuration.
+ If the calendar is set to expand, we divide the space between the
+ cells and the spaces around the calendar, otherwise we place the
+ calendars in the center of the available area. */
+
+ font = calitem->font;
+ if (!font)
+ font = style->font;
+ char_height = font->ascent + font->descent;
+
+ calitem->month_width = calitem->min_month_width;
+ calitem->month_height = calitem->min_month_height;
+ calitem->cell_width = calitem->max_digit_width * 2
+ + E_CALENDAR_ITEM_MIN_CELL_XPAD;
+ calitem->cell_height = char_height
+ + E_CALENDAR_ITEM_MIN_CELL_YPAD;
+ calitem->month_tpad = 0;
+ calitem->month_bpad = 0;
+ calitem->month_lpad = 0;
+ calitem->month_rpad = 0;
+
+ space = height - calitem->rows * calitem->month_height;
+ if (space > 0) {
+ space_per_cal = space / calitem->rows;
+ calitem->month_height += space_per_cal;
+
+ if (calitem->expand) {
+ space_per_cell = space_per_cal / E_CALENDAR_ROWS_PER_MONTH;
+ calitem->cell_height += space_per_cell;
+ space_per_cal -= space_per_cell * E_CALENDAR_ROWS_PER_MONTH;
+ }
+
+ calitem->month_tpad = space_per_cal / 2;
+ calitem->month_bpad = space_per_cal - calitem->month_tpad;
+ }
+
+ space = width - calitem->cols * calitem->month_width;
+ if (space > 0) {
+ space_per_cal = space / calitem->cols;
+ calitem->month_width += space_per_cal;
+ space -= space_per_cal * calitem->cols;
+
+ if (calitem->expand) {
+ space_per_cell = space_per_cal / E_CALENDAR_COLS_PER_MONTH;
+ calitem->cell_width += space_per_cell;
+ space_per_cal -= space_per_cell * E_CALENDAR_COLS_PER_MONTH;
+ }
+
+ calitem->month_lpad = space_per_cal / 2;
+ calitem->month_rpad = space_per_cal - calitem->month_lpad;
+ }
+
+ space = MAX (0, space);
+ calitem->x_offset = space / 2;
+
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1,
+ item->x2, item->y2);
+}
+
+
+/*
+ * DRAWING ROUTINES - functions to paint the canvas item.
+ */
+
+static void
+e_calendar_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ ECalendarItem *calitem;
+ GtkStyle *style;
+ GdkFont *font;
+ GdkGC *base_gc, *bg_gc;
+ gint char_height, row, col, row_y, bar_height, col_x, ythickness;
+
+#if 0
+ g_print ("In e_calendar_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+ calitem = E_CALENDAR_ITEM (canvas_item);
+ style = GTK_WIDGET (canvas_item->canvas)->style;
+ font = calitem->font;
+ if (!font)
+ font = style->font;
+ char_height = font->ascent + font->descent;
+ ythickness = style->klass->ythickness;
+ base_gc = style->base_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+
+ /* Clear the entire background. */
+ gdk_draw_rectangle (drawable, base_gc, TRUE,
+ calitem->x1 - x, calitem->y1 - y,
+ calitem->x2 - calitem->x1,
+ calitem->y2 - calitem->y1);
+
+ /* Draw the shadow around the entire item.
+ FIXME: must also leave room for the 'Today' & 'None' buttons etc. */
+ gtk_draw_shadow (style, drawable,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ calitem->x1 - x, calitem->y1 - y,
+ calitem->x2 - calitem->x1, calitem->y2 - calitem->y1);
+
+ row_y = canvas_item->y1 + ythickness;
+ bar_height = ythickness * 2 + E_CALENDAR_ITEM_YPAD1 + char_height
+ + E_CALENDAR_ITEM_YPAD2;
+
+ for (row = 0; row < calitem->rows; row++) {
+ /* Draw the background for the title bars and the shadow around
+ it, and the vertical lines between columns. */
+
+ gdk_draw_rectangle (drawable, bg_gc, TRUE,
+ calitem->x1 - x, row_y - y,
+ calitem->x2 - calitem->x1, bar_height);
+
+ gtk_draw_shadow (style, drawable,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ calitem->x1 - x, row_y - y,
+ calitem->x2 - calitem->x1, bar_height);
+
+
+ for (col = 0; col < calitem->cols; col++) {
+ if (col != 0) {
+ col_x = calitem->x1 + calitem->x_offset
+ + calitem->month_width * col;
+ gtk_draw_vline (style, drawable,
+ GTK_STATE_NORMAL,
+ row_y + ythickness + 1 - y,
+ row_y + bar_height
+ - ythickness - 2 - y,
+ col_x - 1 - x);
+ }
+
+
+ e_calendar_item_draw_month (calitem, drawable, x, y,
+ width, height, row, col);
+ }
+
+ row_y += calitem->month_height;
+ }
+}
+
+
+static void
+e_calendar_item_draw_month (ECalendarItem *calitem,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height,
+ int row,
+ int col)
+{
+ GnomeCanvasItem *item;
+ GtkWidget *widget;
+ GtkStyle *style;
+ GdkFont *font;
+ GdkGC *fg_gc, *bg_gc;
+ struct tm tmp_tm;
+ GdkRectangle clip_rect;
+ gint char_height, xthickness, ythickness, start_weekday;
+ gint year, month, month_x, month_y, min_x, max_x, text_x, text_y;
+ gint day, day_index, cells_x, cells_y, min_cell_width, text_width;
+ gchar buffer[64];
+
+#if 0
+ g_print ("In e_calendar_item_draw_month: %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+ item = GNOME_CANVAS_ITEM (calitem);
+ widget = GTK_WIDGET (item->canvas);
+ style = widget->style;
+ font = calitem->font;
+ if (!font)
+ font = style->font;
+ char_height = font->ascent + font->descent;
+ xthickness = style->klass->xthickness;
+ ythickness = style->klass->ythickness;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+
+ /* Calculate the top-left position of the entire month display. */
+ month_x = item->x1 + xthickness + calitem->x_offset
+ + col * calitem->month_width - x;
+ month_y = item->y1 + ythickness + row * calitem->month_height - y;
+
+ /* Just return if the month is outside the given area. */
+ if (month_x >= width || month_x + calitem->month_width <= 0
+ || month_y >= height || month_y + calitem->month_height <= 0)
+ return;
+
+
+ /* Draw the month name & year, with clipping. Note that the top row
+ needs extra space around it for the buttons. */
+ if (row == 0 && col == 0)
+ min_x = E_CALENDAR_ITEM_XPAD11;
+ else
+ min_x = E_CALENDAR_ITEM_XPAD14 + E_CALENDAR_ITEM_XPAD15;
+
+ if (row == 0 && col == calitem->cols - 1)
+ max_x = calitem->month_width - E_CALENDAR_ITEM_XPAD16;
+ else
+ max_x = calitem->month_width - E_CALENDAR_ITEM_XPAD12
+ - E_CALENDAR_ITEM_XPAD13;
+
+ text_y = month_y + style->klass->ythickness
+ + E_CALENDAR_ITEM_YPAD1;
+ clip_rect.x = month_x + min_x;
+ clip_rect.y = text_y;
+ clip_rect.width = max_x - min_x;
+ clip_rect.height = char_height;
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rect);
+
+ memset (&tmp_tm, 0, sizeof (tmp_tm));
+ month = calitem->month + row * calitem->cols + col;
+ year = calitem->year + month / 12;
+ month %= 12;
+ tmp_tm.tm_year = year - 1900;
+ tmp_tm.tm_mon = month;
+ tmp_tm.tm_mday = 1;
+ tmp_tm.tm_isdst = -1;
+ mktime (&tmp_tm);
+ strftime (buffer, 64, "%B %Y", &tmp_tm);
+ start_weekday = (tmp_tm.tm_wday + 6) % 7;
+
+ /* Ideally we place the text centered in the month, but we won't go
+ to the left of the minimum x position. */
+ text_width = gdk_string_width (font, buffer);
+ text_x = (calitem->month_width - text_width) / 2;
+ text_x = MAX (min_x, text_x);
+
+ gdk_draw_string (drawable, font, fg_gc,
+ month_x + text_x, text_y + font->ascent, buffer);
+
+ gdk_gc_set_clip_rectangle (fg_gc, NULL);
+
+
+ /* Draw the day initials across the top of the month. */
+ min_cell_width = calitem->max_digit_width * 2
+ + E_CALENDAR_ITEM_MIN_CELL_XPAD;
+
+ cells_x = month_x + E_CALENDAR_ITEM_XPAD1 + calitem->month_lpad
+ + E_CALENDAR_ITEM_XPAD3;
+ if (calitem->show_week_numbers)
+ cells_x += calitem->max_week_number_digit_width * 2
+ + E_CALENDAR_ITEM_XPAD2 + 1;
+ text_x = cells_x + calitem->cell_width
+ - (calitem->cell_width - min_cell_width) / 2;
+ text_x -= E_CALENDAR_ITEM_MIN_CELL_XPAD / 2;
+ text_y = month_y + ythickness * 2 + E_CALENDAR_ITEM_YPAD1
+ + char_height + E_CALENDAR_ITEM_YPAD2 + E_CALENDAR_ITEM_YPAD3
+ + calitem->month_tpad;
+
+ cells_y = text_y + char_height + E_CALENDAR_ITEM_YPAD4 + 1
+ + E_CALENDAR_ITEM_YPAD5;
+
+ text_y += font->ascent;
+ day_index = calitem->week_start_day;
+ for (day = 0; day < 7; day++) {
+ gdk_draw_text (drawable, font, fg_gc,
+ text_x - calitem->day_widths[day_index], text_y,
+ &calitem->days[day_index], 1);
+ text_x += calitem->cell_width;
+ day_index++;
+ if (day_index == 7)
+ day_index = 0;
+ }
+
+
+ /* Draw the horizontal line beneath the day initials. */
+ gdk_draw_line (drawable, fg_gc,
+ cells_x - E_CALENDAR_ITEM_XPAD3,
+ cells_y - E_CALENDAR_ITEM_YPAD5,
+ cells_x + E_CALENDAR_COLS_PER_MONTH * calitem->cell_width - 1,
+ cells_y - E_CALENDAR_ITEM_YPAD5);
+
+ e_calendar_item_draw_day_numbers (calitem, drawable, width, height,
+ row, col, year, month, start_weekday,
+ cells_x, cells_y);
+
+ /* Draw the vertical line after the week number. */
+ if (calitem->show_week_numbers) {
+ gdk_draw_line (drawable, fg_gc,
+ cells_x - E_CALENDAR_ITEM_XPAD3,
+ cells_y,
+ cells_x - E_CALENDAR_ITEM_XPAD3,
+ cells_y + E_CALENDAR_ROWS_PER_MONTH * calitem->cell_height - 1);
+ }
+}
+
+
+static void
+e_calendar_item_draw_day_numbers (ECalendarItem *calitem,
+ GdkDrawable *drawable,
+ int width,
+ int height,
+ int row,
+ int col,
+ int year,
+ int month,
+ int start_weekday,
+ gint cells_x,
+ gint cells_y)
+{
+ GnomeCanvasItem *item;
+ GtkWidget *widget;
+ GtkStyle *style;
+ GdkFont *font, *wkfont;
+ GdkGC *fg_gc;
+ GdkColor *bg_color, *fg_color, *box_color;
+ struct tm *today_tm;
+ time_t t;
+ gint char_height, min_cell_width, min_cell_height;
+ gint day_num, drow, dcol, day_x, day_y;
+ gint text_x, text_y;
+ gint num_chars, digit;
+ gint week_num, mon, days_from_week_start;
+ gint years[3], months[3], days_in_month[3];
+ gboolean bold, today, draw_day, finished = FALSE, selected;
+ gint today_year, today_month, today_mday, month_offset, day_offset;
+ gchar buffer[2];
+
+ item = GNOME_CANVAS_ITEM (calitem);
+ widget = GTK_WIDGET (item->canvas);
+ style = widget->style;
+ font = calitem->font;
+ if (!font)
+ font = style->font;
+ wkfont = calitem->week_number_font;
+ if (!wkfont)
+ wkfont = font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ char_height = font->ascent + font->descent;
+
+ min_cell_width = calitem->max_digit_width * 2
+ + E_CALENDAR_ITEM_MIN_CELL_XPAD;
+ min_cell_height = char_height + E_CALENDAR_ITEM_MIN_CELL_YPAD;
+
+ /* Calculate the number of days in the previous, current, and next
+ months. Note that g_date uses 1 to 12 for months. */
+ years[0] = years[1] = years[2] = year;
+ months[0] = month - 1;
+ months[1] = month;
+ months[2] = month + 1;
+ if (months[0] == -1) {
+ months[0] = 11;
+ years[0]--;
+ }
+ if (months[2] == 12) {
+ months[2] = 0;
+ years[2]++;
+ }
+
+ days_in_month[0] = g_date_days_in_month (months[0] + 1, years[0]);
+ days_in_month[1] = g_date_days_in_month (months[1] + 1, years[1]);
+ days_in_month[2] = g_date_days_in_month (months[2] + 1, years[2]);
+
+ /* Mon 0 is the previous month, which we may show the end of. Mon 1 is
+ the current month, and mon 2 is the next month. */
+ mon = 0;
+
+ day_num = days_in_month[0];
+ days_from_week_start = (start_weekday + 7 - calitem->week_start_day)
+ % 7;
+ /* For the top-left month we show the end of the previous month, and
+ if the new month starts on the first day of the week we show a
+ complete week from the previous month. */
+ if (days_from_week_start == 0) {
+ if (row == 0 && col == 0) {
+ day_num -= 6;
+ } else {
+ mon++;
+ day_num = 1;
+ }
+ } else {
+ day_num -= days_from_week_start - 1;
+ }
+
+ /* Get today's date, so we can highlight it. */
+ t = time (NULL);
+ today_tm = localtime (&t);
+ today_year = today_tm->tm_year + 1900;
+ today_month = today_tm->tm_mon;
+ today_mday = today_tm->tm_mday;
+
+ /* We usually skip the last days of the previous month (mon = 0),
+ except for the top-left month displayed. */
+ draw_day = (mon == 1 || (row == 0 && col == 0));
+
+ month_offset = row * calitem->cols + col;
+ day_offset = 0;
+
+ for (drow = 0; drow < 6; drow++) {
+ /* Draw the week number. */
+ if (calitem->show_week_numbers) {
+ week_num = e_calendar_item_get_week_number (calitem,
+ day_num,
+ months[mon],
+ years[mon]);
+
+ text_x = cells_x - E_CALENDAR_ITEM_XPAD3 - 1
+ - E_CALENDAR_ITEM_XPAD2;
+ text_y = cells_y + drow * calitem->cell_height +
+ + (calitem->cell_height - min_cell_height + 1) / 2;
+
+ num_chars = 0;
+ if (week_num >= 10) {
+ digit = week_num / 10;
+ text_x -= calitem->week_number_digit_widths[digit];
+ buffer[num_chars++] = digit + '0';
+ }
+
+ digit = week_num % 10;
+ text_x -= calitem->week_number_digit_widths[digit];
+ buffer[num_chars++] = digit + '0';
+
+ gdk_draw_text (drawable, wkfont, fg_gc,
+ text_x, text_y + font->ascent,
+ buffer, num_chars);
+ }
+
+ for (dcol = 0; dcol < 7; dcol++) {
+ if (draw_day) {
+ day_x = cells_x + dcol * calitem->cell_width;
+ day_y = cells_y + drow * calitem->cell_height;
+
+ today = years[mon] == today_year
+ && months[mon] == today_month
+ && day_num == today_mday;
+
+ selected = calitem->selection_start_month_offset != -1
+ && (calitem->selection_start_month_offset < month_offset
+ || (calitem->selection_start_month_offset == month_offset
+ && calitem->selection_start_day_offset <= day_offset))
+ && (calitem->selection_end_month_offset > month_offset
+ || (calitem->selection_end_month_offset == month_offset
+ && calitem->selection_end_day_offset >= day_offset));
+
+ /* Get the colors & style to use for the day.*/
+ e_calendar_item_get_day_style (calitem,
+ years[mon],
+ months[mon],
+ day_num,
+ today,
+ mon == 1,
+ selected,
+ &bg_color,
+ &fg_color,
+ &box_color,
+ &bold);
+
+ /* Draw the background, if set. */
+ if (bg_color) {
+ gdk_gc_set_foreground (fg_gc, bg_color);
+ gdk_draw_rectangle (drawable, fg_gc,
+ TRUE,
+ day_x, day_y,
+ calitem->cell_width,
+ calitem->cell_height);
+ }
+
+ /* Draw the box, if set. */
+ if (box_color) {
+ gdk_gc_set_foreground (fg_gc, box_color);
+ gdk_draw_rectangle (drawable, fg_gc,
+ FALSE,
+ day_x, day_y,
+ calitem->cell_width - 1,
+ calitem->cell_height - 1);
+ }
+
+ /* Draw the 1- or 2-digit day number. */
+ day_x += calitem->cell_width - (calitem->cell_width - min_cell_width) / 2;
+ day_x -= E_CALENDAR_ITEM_MIN_CELL_XPAD / 2;
+ day_y += (calitem->cell_height - min_cell_height + 1) / 2;
+ day_y += E_CALENDAR_ITEM_MIN_CELL_YPAD / 2;
+
+ num_chars = 0;
+ if (day_num >= 10) {
+ digit = day_num / 10;
+ day_x -= calitem->digit_widths[digit];
+ buffer[num_chars++] = digit + '0';
+ }
+
+ digit = day_num % 10;
+ day_x -= calitem->digit_widths[digit];
+ buffer[num_chars++] = digit + '0';
+
+ if (fg_color) {
+ gdk_gc_set_foreground (fg_gc,
+ fg_color);
+ } else {
+ gdk_gc_set_foreground (fg_gc,
+ &style->fg[GTK_STATE_NORMAL]);
+ }
+
+ gdk_draw_text (drawable, font, fg_gc,
+ day_x,
+ day_y + font->ascent,
+ buffer, num_chars);
+ /* We use a stupid technique for bold. Just
+ draw it again 1 pixel to the left. */
+ if (bold)
+ gdk_draw_text (drawable, font, fg_gc,
+ day_x - 1,
+ day_y + font->ascent,
+ buffer, num_chars);
+ }
+
+ /* See if we've reached the end of a month. */
+ if (day_num == days_in_month[mon]) {
+ mon++;
+ /* We only draw the start of the next month
+ for the bottom-right month displayed. */
+ if (mon == 2 && (row != calitem->rows - 1
+ || col != calitem->cols - 1)) {
+ /* Set a flag so we exit the loop. */
+ finished = TRUE;
+ break;
+ }
+ day_num = 1;
+ draw_day = TRUE;
+ } else {
+ day_num++;
+ }
+
+ day_offset++;
+ }
+
+ /* Exit the loop if the flag is set. */
+ if (finished)
+ break;
+ }
+
+ /* Reset the foreground color. */
+ gdk_gc_set_foreground (fg_gc, &style->fg[GTK_STATE_NORMAL]);
+}
+
+
+static gint
+e_calendar_item_get_week_number (ECalendarItem *calitem,
+ gint day,
+ gint month,
+ gint year)
+{
+ GDate tmp_date;
+ gint weekday, yearday, offset, week_num;
+
+ /* FIXME: check what happens at year boundaries. */
+
+ g_date_clear (&tmp_date, 1);
+ g_date_set_dmy (&tmp_date, day, month + 1, year);
+
+ /* This results in a value of 0 (Monday) - 6 (Sunday). */
+ weekday = g_date_weekday (&tmp_date) - 1;
+
+ /* Calculate the offset from the start of the week. */
+ offset = (calitem->week_start_day + 7 - weekday) % 7;
+
+ /* Calculate the day of the year, from 0 to 365. */
+ yearday = g_date_day_of_year (&tmp_date) - 1;
+
+ /* If the week starts on or after 29th December, it is week 1 of the
+ next year, since there are 4 days in the next year. */
+ g_date_subtract_days (&tmp_date, offset);
+ if (g_date_month (&tmp_date) == 12 && g_date_day (&tmp_date) >= 29)
+ return 1;
+
+ /* Calculate the week number, from 0. */
+ week_num = (yearday - offset) / 7;
+
+ /* If the first week starts on or after Jan 5th, then we need to add
+ 1 since the previous week will really be the first week. */
+ if ((yearday - offset) % 7 >= 4)
+ week_num++;
+
+ /* Add 1 so week numbers are from 1 to 53. */
+ return week_num + 1;
+}
+
+
+
+/* This is supposed to return the nearest item the the point and the distance.
+ Since we are the only item we just return ourself and 0 for the distance.
+ This is needed so that we get button/motion events. */
+static double
+e_calendar_item_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.0;
+}
+
+
+static gint
+e_calendar_item_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ ECalendarItem *calitem;
+
+ calitem = E_CALENDAR_ITEM (item);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ return e_calendar_item_button_press (calitem, event);
+ case GDK_BUTTON_RELEASE:
+ return e_calendar_item_button_release (calitem, event);
+ case GDK_MOTION_NOTIFY:
+ return e_calendar_item_motion (calitem, event);
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+
+/* This checks if any fonts have changed, and if so it recalculates the
+ layout of the item. */
+static void
+e_calendar_item_recalc_sizes (ECalendarItem *calitem)
+{
+ GnomeCanvasItem *canvas_item;
+ GtkStyle *style;
+ GdkFont *font, *wkfont;
+ gchar *digits = "0123456789";
+ gint day, digit, max_digit_width, max_week_number_digit_width;
+ gint char_height, width, min_cell_width, min_cell_height;
+
+ canvas_item = GNOME_CANVAS_ITEM (calitem);
+ style = GTK_WIDGET (canvas_item->canvas)->style;
+
+ font = calitem->font;
+ if (!font)
+ font = style->font;
+ wkfont = calitem->week_number_font;
+ if (!wkfont)
+ wkfont = font;
+ char_height = font->ascent + font->descent;
+
+ /* If both fonts are the same, just return. */
+ if (font == calitem->old_font
+ && wkfont == calitem->old_week_number_font)
+ return;
+
+ calitem->old_font = font;
+ calitem->old_week_number_font = wkfont;
+
+ for (day = 0; day < 7; day++)
+ calitem->day_widths[day] = gdk_char_width (font,
+ calitem->days[day]);
+
+ max_digit_width = 0;
+ max_week_number_digit_width = 0;
+ for (digit = 0; digit < 10; digit++) {
+ width = gdk_char_width (font, digits[digit]);
+ calitem->digit_widths[digit] = width;
+ max_digit_width = MAX (max_digit_width, width);
+
+ if (wkfont) {
+ width = gdk_char_width (wkfont, digits[digit]);
+ calitem->week_number_digit_widths[digit] = width;
+ max_week_number_digit_width = MAX (max_week_number_digit_width, width);
+ } else {
+ calitem->week_number_digit_widths[digit] = width;
+ max_week_number_digit_width = max_digit_width;
+ }
+ }
+ calitem->max_digit_width = max_digit_width;
+ calitem->max_week_number_digit_width = max_week_number_digit_width;
+
+
+ min_cell_width = max_digit_width * 2 + E_CALENDAR_ITEM_MIN_CELL_XPAD;
+ min_cell_height = char_height + E_CALENDAR_ITEM_MIN_CELL_YPAD;
+
+ calitem->min_month_width = E_CALENDAR_ITEM_XPAD1
+ + E_CALENDAR_ITEM_XPAD3 + min_cell_width * 7
+ + E_CALENDAR_ITEM_XPAD4;
+ if (calitem->show_week_numbers)
+ calitem->min_month_width += max_week_number_digit_width * 2
+ + E_CALENDAR_ITEM_XPAD2 + 1;
+
+ calitem->min_month_height = style->klass->ythickness * 2
+ + E_CALENDAR_ITEM_YPAD1 + char_height
+ + E_CALENDAR_ITEM_YPAD2 + 1 + E_CALENDAR_ITEM_YPAD3
+ + char_height + E_CALENDAR_ITEM_YPAD4 + 1
+ + E_CALENDAR_ITEM_YPAD5 + min_cell_height * 6
+ + E_CALENDAR_ITEM_YPAD6;
+}
+
+
+static void
+e_calendar_item_get_day_style (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ gboolean today,
+ gboolean current_month,
+ gboolean selected,
+ GdkColor **bg_color,
+ GdkColor **fg_color,
+ GdkColor **box_color,
+ gboolean *bold)
+{
+ *bg_color = NULL;
+ *fg_color = NULL;
+ *box_color = NULL;
+ *bold = FALSE;
+
+ if (today)
+ *box_color = &calitem->colors[E_CALENDAR_COLOR_TODAY];
+
+ if (!current_month)
+ *fg_color = &calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT];
+
+ if (selected) {
+ *fg_color = &calitem->colors[E_CALENDAR_COLOR_SELECTION];
+ *bg_color = &calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT];
+ }
+}
+
+
+
+static gboolean
+e_calendar_item_button_press (ECalendarItem *calitem,
+ GdkEvent *event)
+{
+ gint month, day;
+ gboolean all_week;
+
+ g_print ("In e_calendar_item_button_press\n");
+
+ if (e_calendar_item_convert_position_to_day (calitem,
+ event->button.x,
+ event->button.y,
+ &month, &day, &all_week)) {
+ g_print (" Pressed month: %i day: %i\n", month, day);
+
+ calitem->selection_start_month_offset = month;
+ calitem->selection_start_day_offset = day;
+ calitem->selection_end_month_offset = month;
+ calitem->selection_end_day_offset = day;
+ calitem->selecting = TRUE;
+ calitem->selection_dragging_end = TRUE;
+
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_calendar_item_button_release (ECalendarItem *calitem,
+ GdkEvent *event)
+{
+ gint month, day;
+ gboolean all_week;
+
+ g_print ("In e_calendar_item_button_release\n");
+
+ calitem->selecting = FALSE;
+
+ if (e_calendar_item_convert_position_to_day (calitem,
+ event->button.x,
+ event->button.y,
+ &month, &day, &all_week))
+ g_print (" Released month: %i day: %i\n", month, day);
+
+ return FALSE;
+}
+
+
+static gboolean
+e_calendar_item_motion (ECalendarItem *calitem,
+ GdkEvent *event)
+{
+ gint month, day;
+ gboolean all_week;
+
+ if (!calitem->selecting)
+ return FALSE;
+
+ if (e_calendar_item_convert_position_to_day (calitem,
+ event->button.x,
+ event->button.y,
+ &month, &day, &all_week)) {
+ if (calitem->selection_dragging_end) {
+ if (calitem->selection_end_month_offset == month
+ && calitem->selection_end_day_offset == day)
+ return FALSE;
+ calitem->selection_end_month_offset = month;
+ calitem->selection_end_day_offset = day;
+ } else {
+ if (calitem->selection_start_month_offset == month
+ && calitem->selection_start_day_offset == day)
+ return FALSE;
+ calitem->selection_start_month_offset = month;
+ calitem->selection_start_day_offset = day;
+ }
+
+ if (calitem->selection_start_month_offset > calitem->selection_end_month_offset
+ || (calitem->selection_start_month_offset == calitem->selection_end_month_offset
+ && calitem->selection_start_day_offset > calitem->selection_end_day_offset)) {
+ month = calitem->selection_start_month_offset;
+ day = calitem->selection_start_day_offset;
+ calitem->selection_start_month_offset = calitem->selection_end_month_offset;
+ calitem->selection_end_month_offset = month;
+ calitem->selection_start_day_offset = calitem->selection_end_day_offset;
+ calitem->selection_end_day_offset = day;
+
+ calitem->selection_dragging_end = !calitem->selection_dragging_end;
+ }
+
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem));
+ }
+
+ return FALSE;
+}
+
+
+
+
+/* Converts a position within the item to a month & day.
+ The month returned is 0 for the top-left month displayed.
+ If the position is over a week number the first day of the week is returned
+ and entire_week is set to TRUE. */
+static gboolean
+e_calendar_item_convert_position_to_day (ECalendarItem *calitem,
+ gint event_x,
+ gint event_y,
+ gint *month,
+ gint *day,
+ gboolean *entire_week)
+{
+ GnomeCanvasItem *item;
+ GtkWidget *widget;
+ GtkStyle *style;
+ gint xthickness, ythickness, char_height;
+ gint x, y, row, col, cells_x, cells_y, day_row, day_col;
+ gint first_valid_day, last_valid_day;
+
+ item = GNOME_CANVAS_ITEM (calitem);
+ widget = GTK_WIDGET (item->canvas);
+ style = widget->style;
+ char_height = style->font->ascent + style->font->descent;
+ xthickness = style->klass->xthickness;
+ ythickness = style->klass->ythickness;
+
+ x = event_x - xthickness - calitem->x_offset;
+ y = event_y - ythickness;
+
+ if (x < 0 || y < 0)
+ return FALSE;
+
+ row = y / calitem->month_height;
+ col = x / calitem->month_width;
+
+ if (row < 0 || row >= calitem->rows
+ || col < 0 || col >= calitem->cols)
+ return FALSE;
+
+ x = x % calitem->month_width;
+ y = y % calitem->month_height;
+
+ cells_x = E_CALENDAR_ITEM_XPAD1 + calitem->month_lpad
+ + E_CALENDAR_ITEM_XPAD3;
+ if (calitem->show_week_numbers)
+ cells_x += calitem->max_week_number_digit_width * 2
+ + E_CALENDAR_ITEM_XPAD2 + 1;
+ cells_y = ythickness * 2 + E_CALENDAR_ITEM_YPAD1
+ + char_height + E_CALENDAR_ITEM_YPAD2 + E_CALENDAR_ITEM_YPAD3
+ + calitem->month_tpad
+ + char_height + E_CALENDAR_ITEM_YPAD4 + 1
+ + E_CALENDAR_ITEM_YPAD5;
+
+ x -= cells_x;
+ y -= cells_y;
+
+ if (x < 0 || y < 0)
+ return FALSE;
+
+ day_row = y / calitem->cell_height;
+ day_col = x / calitem->cell_width;
+
+ if (day_row < 0 || day_row >= E_CALENDAR_ROWS_PER_MONTH
+ || day_col < 0 || day_col >= E_CALENDAR_COLS_PER_MONTH)
+ return FALSE;
+
+ *month = row * calitem->cols + col;
+ *day = day_row * E_CALENDAR_COLS_PER_MONTH + day_col;
+ *entire_week = FALSE;
+
+ e_calendar_item_get_month_info (calitem, row, col,
+ &first_valid_day, &last_valid_day);
+ if (*day < first_valid_day || *day > last_valid_day)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+e_calendar_item_get_month_info (ECalendarItem *calitem,
+ gint row,
+ gint col,
+ gint *first_valid_day,
+ gint *last_valid_day)
+{
+ gint year, month, days_in_month, start_weekday, first_day_of_month;
+ struct tm tmp_tm = { 0 };
+
+ month = calitem->month + row * calitem->cols + col;
+ year = calitem->year + month / 12;
+ month = month % 12;
+ days_in_month = g_date_days_in_month (month + 1, year);
+
+ tmp_tm.tm_year = year - 1900;
+ tmp_tm.tm_mon = month;
+ tmp_tm.tm_mday = 1;
+ tmp_tm.tm_isdst = -1;
+ mktime (&tmp_tm);
+
+ /* Convert to 0 (Monday) to 6 (Sunday). */
+ start_weekday = (tmp_tm.tm_wday + 6) % 7;
+
+ first_day_of_month = (start_weekday + 7 - calitem->week_start_day) % 7;
+
+ if (row == 0 && col == 0)
+ *first_valid_day = 0;
+ else
+ *first_valid_day = first_day_of_month;
+
+ if (row == calitem->rows - 1 && col == calitem->cols - 1)
+ *last_valid_day = E_CALENDAR_ROWS_PER_MONTH * E_CALENDAR_COLS_PER_MONTH - 1;
+ else
+ *last_valid_day = first_day_of_month + days_in_month - 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+
+static gint
+e_calendar_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ ECalendar *cal;
+ gint day;
+
+ cal = E_CALENDAR (widget);
+
+ g_print ("In e_calendar_button_press\n");
+
+ day = e_calendar_convert_position_to_day (cal, event->x, event->y);
+
+ cal->selection_start_day = day;
+ cal->selection_end_day = day;
+ cal->selection_dragging_end = TRUE;
+
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+
+
+static gint
+e_calendar_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ g_print ("In e_calendar_button_release\n");
+
+ return FALSE;
+}
+
+
+static gint
+e_calendar_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ ECalendar *cal;
+ gint x, y, day;
+
+ cal = E_CALENDAR (widget);
+
+ g_print ("In e_calendar_motion\n");
+
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || event->window != widget->window)
+ gtk_widget_get_pointer (widget, &x, &y);
+
+ day = e_calendar_convert_position_to_day (cal, event->x, event->y);
+
+ if (cal->selection_dragging_end)
+ cal->selection_end_day = day;
+ else
+ cal->selection_start_day = day;
+
+ if (cal->selection_start_day > cal->selection_end_day) {
+ day = cal->selection_start_day;
+ cal->selection_start_day = cal->selection_end_day;
+ cal->selection_end_day = day;
+ cal->selection_dragging_end = !cal->selection_dragging_end;
+ }
+
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+#endif
diff --git a/widgets/misc/e-calendar-item.h b/widgets/misc/e-calendar-item.h
new file mode 100644
index 0000000000..a59ca6d407
--- /dev/null
+++ b/widgets/misc/e-calendar-item.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_CALENDAR_ITEM_H_
+#define _E_CALENDAR_ITEM_H_
+
+#include <libgnomeui/gnome-canvas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * ECalendarItem - canvas item displaying a calendar.
+ */
+
+#define E_CALENDAR_ITEM_YPAD1 1
+#define E_CALENDAR_ITEM_YPAD2 1
+
+/* These index our colors array. */
+typedef enum
+{
+ E_CALENDAR_COLOR_SELECTION,
+ E_CALENDAR_COLOR_HIGHLIGHT,
+ E_CALENDAR_COLOR_TODAY,
+
+ E_CALENDAR_COLOR_LAST
+} ECalendarColors;
+
+
+#define E_CALENDAR_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_calendar_item_get_type (), ECalendarItem))
+#define E_CALENDAR_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_calendar_item_get_type ()))
+#define E_IS_CALENDAR_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_calendar_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The year & month of the first calendar being displayed. */
+ gint year;
+ gint month; /* 0 to 11 */
+
+ /* Bounds of item. */
+ gdouble x1, y1, x2, y2;
+
+ /* The minimum & maximum number of rows & columns of months. */
+ gint min_rows;
+ gint min_cols;
+ gint max_rows;
+ gint max_cols;
+
+ /* The number of rows & columns of months. */
+ gint rows;
+ gint cols;
+
+ /* Whether we show week nubers. */
+ gboolean show_week_numbers;
+
+ /* The first day of the week, 0 (Monday) to 6 (Sunday). */
+ gint week_start_day;
+
+ /* Whether the cells expand to fill extra space. */
+ gboolean expand;
+
+ /* The minimum size of each month, based on the fonts used. */
+ gint min_month_width;
+ gint min_month_height;
+
+ /* The actual size of each month, after dividing extra space. */
+ gint month_width;
+ gint month_height;
+
+ /* The offset to the left edge of the first calendar. */
+ gint x_offset;
+
+ /* The padding around each calendar month. */
+ gint month_lpad, month_rpad;
+ gint month_tpad, month_bpad;
+
+ /* The size of each cell. */
+ gint cell_width;
+ gint cell_height;
+
+
+ /* The current selection. The month offsets are from 0, which is the
+ top-left calendar month view. The day offsets are from 0, which is
+ the top-left cell in the month view (which may be empty). */
+ gint selection_start_month_offset;
+ gint selection_start_day_offset;
+ gint selection_end_month_offset;
+ gint selection_end_day_offset;
+ gboolean selecting;
+ gboolean selection_dragging_end;
+
+ /* The first character of each day of the week, e.g. 'MTWTFSS'. */
+ gchar *days;
+
+ /* Widths of the day characters. */
+ gint day_widths[7];
+
+ /* Widths of the digits, '0' .. '9'. */
+ gint digit_widths[10];
+ gint max_digit_width;
+ gint week_number_digit_widths[10];
+ gint max_week_number_digit_width;
+
+ /* Fonts for drawing text. If font isn't set it uses the font from the
+ canvas widget. If week_number_font isn't set it uses font. */
+ GdkFont *font, *old_font;
+ GdkFont *week_number_font, *old_week_number_font;
+
+ /* Colors for drawing. */
+ GdkColor colors[E_CALENDAR_COLOR_LAST];
+} ECalendarItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} ECalendarItemClass;
+
+
+GtkType e_calendar_item_get_type (void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_CALENDAR_ITEM_H_ */
diff --git a/widgets/misc/e-calendar.c b/widgets/misc/e-calendar.c
new file mode 100644
index 0000000000..638d50de56
--- /dev/null
+++ b/widgets/misc/e-calendar.c
@@ -0,0 +1,481 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * ECalendar - displays a table of monthly calendars, allowing highlighting
+ * and selection of one or more days. Like GtkCalendar with more features.
+ * Most of the functionality is in the ECalendarItem canvas item, though
+ * we also add GnomeCanvasWidget buttons to go to the previous/next month and
+ * to got to the current day.
+ */
+
+#include <config.h>
+#include <e-util/e-util.h>
+#include "e-calendar.h"
+
+#define E_CALENDAR_SMALL_FONT \
+ "-adobe-utopia-regular-r-normal-*-*-100-*-*-p-*-iso8859-*"
+#define E_CALENDAR_SMALL_FONT_FALLBACK \
+ "-adobe-helvetica-medium-r-normal-*-*-80-*-*-p-*-iso8859-*"
+
+#define E_CALENDAR_BUTTON_X_PAD 2
+#define E_CALENDAR_BUTTON_Y_PAD 0
+
+static void e_calendar_class_init (ECalendarClass *class);
+static void e_calendar_init (ECalendar *cal);
+static void e_calendar_destroy (GtkObject *object);
+static void e_calendar_realize (GtkWidget *widget);
+static void e_calendar_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void e_calendar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void e_calendar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint e_calendar_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void e_calendar_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gint e_calendar_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint e_calendar_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint e_calendar_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_calendar_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_calendar_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+
+static void e_calendar_paint (ECalendar *cal,
+ GdkRectangle *area);
+
+static void e_calendar_on_prev_clicked (ECalendar *cal);
+static void e_calendar_on_next_clicked (ECalendar *cal);
+
+static GnomeCanvasClass *parent_class;
+static GtkLayoutClass *grandparent_class;
+
+E_MAKE_TYPE (e_calendar, "ECalendar", ECalendar,
+ e_calendar_class_init, e_calendar_init, E_CANVAS_TYPE)
+
+
+static void
+e_calendar_class_init (ECalendarClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ parent_class = gtk_type_class (E_CANVAS_TYPE);
+ grandparent_class = gtk_type_class (GTK_TYPE_LAYOUT);
+
+ object_class->destroy = e_calendar_destroy;
+
+ widget_class->realize = e_calendar_realize;
+ widget_class->style_set = e_calendar_style_set;
+ widget_class->size_request = e_calendar_size_request;
+ widget_class->size_allocate = e_calendar_size_allocate;
+ widget_class->expose_event = e_calendar_expose;
+ widget_class->draw = e_calendar_draw;
+#if 0
+ widget_class->button_press_event = e_calendar_button_press;
+ widget_class->button_release_event = e_calendar_button_release;
+#endif
+ widget_class->focus_in_event = e_calendar_focus_in;
+ widget_class->focus_out_event = e_calendar_focus_out;
+ widget_class->key_press_event = e_calendar_key_press;
+}
+
+
+static void
+e_calendar_init (ECalendar *cal)
+{
+ GnomeCanvasGroup *canvas_group;
+ GdkFont *small_font;
+ GtkWidget *button, *arrow;
+
+ /* Create the small font. */
+ small_font = gdk_font_load (E_CALENDAR_SMALL_FONT);
+ if (!small_font)
+ small_font = gdk_font_load (E_CALENDAR_SMALL_FONT_FALLBACK);
+ if (!small_font)
+ g_warning ("Couldn't load font");
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (cal)->root);
+
+ cal->calitem = E_CALENDAR_ITEM (gnome_canvas_item_new (canvas_group,
+ e_calendar_item_get_type (),
+ "week_number_font", small_font,
+ "week_start_day", 6,
+ NULL));
+
+#if 0
+ "show_week_numbers", TRUE,
+ "minimum_columns", 5,
+ "maximum_columns", 5,
+#endif
+
+ if (small_font)
+ gdk_font_unref (small_font);
+
+
+ button = gtk_button_new ();
+ /* FIXME: The buttons doesn't display properly if we do this. */
+ /*gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);*/
+ gtk_widget_show (button);
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (e_calendar_on_prev_clicked),
+ GTK_OBJECT (cal));
+ arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT);
+ gtk_widget_show (arrow);
+ gtk_container_add (GTK_CONTAINER (button), arrow);
+ cal->prev_item = gnome_canvas_item_new (canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+
+ button = gtk_button_new ();
+ /*gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);*/
+ gtk_widget_show (button);
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (e_calendar_on_next_clicked),
+ GTK_OBJECT (cal));
+ arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
+ gtk_widget_show (arrow);
+ gtk_container_add (GTK_CONTAINER (button), arrow);
+ cal->next_item = gnome_canvas_item_new (canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", button,
+ NULL);
+
+ cal->min_rows = 1;
+ cal->min_cols = 1;
+ cal->max_rows = -1;
+ cal->max_cols = -1;
+}
+
+
+/**
+ * e_calendar_new:
+ * @Returns: a new #ECalendar.
+ *
+ * Creates a new #ECalendar.
+ **/
+GtkWidget *
+e_calendar_new (void)
+{
+ GtkWidget *cal;
+
+ cal = gtk_type_new (e_calendar_get_type ());
+
+ return cal;
+}
+
+
+static void
+e_calendar_destroy (GtkObject *object)
+{
+ ECalendar *cal;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (E_IS_CALENDAR (object));
+
+ cal = E_CALENDAR (object);
+
+
+
+}
+
+
+static void
+e_calendar_realize (GtkWidget *widget)
+{
+ (*GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+}
+
+
+static void
+e_calendar_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+}
+
+
+static void
+e_calendar_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ ECalendar *cal;
+ GtkStyle *style;
+ gdouble col_width, row_height;
+ gint width, height;
+
+ cal = E_CALENDAR (widget);
+ style = GTK_WIDGET (cal)->style;
+
+ gtk_object_get (GTK_OBJECT (cal->calitem),
+ "row_height", &row_height,
+ "column_width", &col_width,
+ NULL);
+
+ height = row_height * cal->min_rows;
+ width = col_width * cal->min_cols;
+
+ /* FIXME: Add on space for line & button if shown. */
+
+ requisition->width = width + style->klass->xthickness * 2;
+ requisition->height = height + style->klass->ythickness * 2;
+}
+
+
+static void
+e_calendar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ ECalendar *cal;
+ GdkFont *font;
+ gdouble old_x2, old_y2, new_x2, new_y2;
+ gdouble xthickness, ythickness, button_size;
+
+ cal = E_CALENDAR (widget);
+ font = widget->style->font;
+ xthickness = widget->style->klass->xthickness;
+ ythickness = widget->style->klass->ythickness;
+
+ (*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+ /* Set the scroll region to its allocated size, if changed. */
+ gnome_canvas_get_scroll_region (GNOME_CANVAS (cal),
+ NULL, NULL, &old_x2, &old_y2);
+ new_x2 = widget->allocation.width - 1;
+ new_y2 = widget->allocation.height - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (cal),
+ 0, 0, new_x2, new_y2);
+
+ /* FIXME: Take off space for line & buttons if shown. */
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", new_x2,
+ "y2", new_y2,
+ NULL);
+
+ button_size = font->ascent + font->descent + E_CALENDAR_ITEM_YPAD1
+ + E_CALENDAR_ITEM_YPAD2 - E_CALENDAR_BUTTON_Y_PAD * 2;
+
+ gnome_canvas_item_set (cal->prev_item,
+ "x", xthickness * 2 + E_CALENDAR_BUTTON_X_PAD,
+ "y", ythickness * 2 + E_CALENDAR_BUTTON_Y_PAD,
+ "width", button_size,
+ "height", button_size,
+ NULL);
+
+ gnome_canvas_item_set (cal->next_item,
+ "x", new_x2 + 1 - xthickness * 2
+ - E_CALENDAR_BUTTON_X_PAD - button_size,
+ "y", ythickness * 2 + E_CALENDAR_BUTTON_Y_PAD,
+ "width", button_size,
+ "height", button_size,
+ NULL);
+}
+
+
+static gint
+e_calendar_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ ECalendar *cal;
+
+ cal = E_CALENDAR (widget);
+
+ (*GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
+
+ e_calendar_paint (cal, &event->area);
+
+ return FALSE;
+}
+
+
+static void
+e_calendar_draw (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ ECalendar *cal;
+
+ cal = E_CALENDAR (widget);
+
+ (*GTK_WIDGET_CLASS (parent_class)->draw) (widget, area);
+
+ (*GTK_WIDGET_CLASS (grandparent_class)->draw) (widget, area);
+
+ e_calendar_paint (cal, area);
+}
+
+
+static void
+e_calendar_paint (ECalendar *cal,
+ GdkRectangle *area)
+{
+#if 0
+ g_print ("In e_calendar_paint: %i,%i %ix%i\n", area->x, area->y,
+ area->width, area->height);
+#endif
+}
+
+
+
+static gint
+e_calendar_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ ECalendar *cal;
+
+ cal = E_CALENDAR (widget);
+
+ g_print ("In e_calendar_button_press\n");
+
+ return FALSE;
+}
+
+
+static gint
+e_calendar_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ g_print ("In e_calendar_button_release\n");
+
+ return FALSE;
+}
+
+
+static gint
+e_calendar_focus_in (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ return FALSE;
+}
+
+
+static gint
+e_calendar_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ return FALSE;
+}
+
+
+static gint
+e_calendar_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ return FALSE;
+}
+
+
+void
+e_calendar_set_minimum_size (ECalendar *cal,
+ gint rows,
+ gint cols)
+{
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ cal->min_rows = rows;
+ cal->min_cols = cols;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
+ "minimum_rows", rows,
+ "minimum_columns", cols,
+ NULL);
+
+ gtk_widget_queue_resize (GTK_WIDGET (cal));
+}
+
+
+void
+e_calendar_set_maximum_size (ECalendar *cal,
+ gint rows,
+ gint cols)
+{
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ cal->max_rows = rows;
+ cal->max_cols = cols;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
+ "maximum_rows", rows,
+ "maximum_columns", cols,
+ NULL);
+
+ gtk_widget_queue_resize (GTK_WIDGET (cal));
+}
+
+
+static void
+e_calendar_on_prev_clicked (ECalendar *cal)
+{
+ gint year, month;
+
+ gtk_object_get (GTK_OBJECT (cal->calitem),
+ "year", &year,
+ "month", &month,
+ NULL);
+
+ month--;
+ if (month == -1) {
+ year--;
+ month = 11;
+ }
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
+ "year", year,
+ "month", month,
+ NULL);
+}
+
+
+static void
+e_calendar_on_next_clicked (ECalendar *cal)
+{
+ gint year, month;
+
+ gtk_object_get (GTK_OBJECT (cal->calitem),
+ "year", &year,
+ "month", &month,
+ NULL);
+
+ month++;
+ if (month == 12) {
+ year++;
+ month = 0;
+ }
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
+ "year", year,
+ "month", month,
+ NULL);
+}
+
diff --git a/widgets/misc/e-calendar.h b/widgets/misc/e-calendar.h
new file mode 100644
index 0000000000..b0e9c1f985
--- /dev/null
+++ b/widgets/misc/e-calendar.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_CALENDAR_H_
+#define _E_CALENDAR_H_
+
+#include <gtk/gtkwidget.h>
+#include "e-util/e-canvas.h"
+#include "e-calendar-item.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * ECalendar - displays a table of monthly calendars, allowing highlighting
+ * and selection of one or more days. Like GtkCalendar with more features.
+ * Most of the functionality is in the ECalendarItem canvas item, though
+ * we also add GnomeCanvasWidget buttons to go to the previous/next month and
+ * to got to the current day.
+ */
+
+#define E_CALENDAR(obj) GTK_CHECK_CAST (obj, e_calendar_get_type (), ECalendar)
+#define E_CALENDAR_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_calendar_get_type (), ECalendarClass)
+#define E_IS_CALENDAR(obj) GTK_CHECK_TYPE (obj, e_calendar_get_type ())
+
+
+typedef struct _ECalendar ECalendar;
+typedef struct _ECalendarClass ECalendarClass;
+
+struct _ECalendar
+{
+ ECanvas canvas;
+
+ ECalendarItem *calitem;
+
+ GnomeCanvasItem *prev_item, *next_item;
+
+ gint min_rows;
+ gint min_cols;
+
+ gint max_rows;
+ gint max_cols;
+};
+
+struct _ECalendarClass
+{
+ ECanvasClass parent_class;
+};
+
+
+GtkType e_calendar_get_type (void);
+GtkWidget* e_calendar_new (void);
+
+void e_calendar_set_minimum_size (ECalendar *cal,
+ gint rows,
+ gint cols);
+void e_calendar_set_maximum_size (ECalendar *cal,
+ gint rows,
+ gint cols);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_CALENDAR_H_ */
diff --git a/widgets/misc/test-calendar.c b/widgets/misc/test-calendar.c
new file mode 100644
index 0000000000..6463a21ce3
--- /dev/null
+++ b/widgets/misc/test-calendar.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * test-calendar - tests the ECalendar widget.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gnome.h>
+
+#include "e-calendar.h"
+
+static void
+delete_event_cb (GtkWidget *widget,
+ GdkEventAny *event,
+ gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *app;
+ GtkWidget *cal;
+ GtkWidget *vbox;
+
+ gnome_init ("test-calendar", "0.0", argc, argv);
+
+ app = gnome_app_new ("Test", "Test");
+ gtk_window_set_default_size (GTK_WINDOW (app), 400, 400);
+ gtk_window_set_policy (GTK_WINDOW (app), FALSE, TRUE, FALSE);
+
+ gtk_signal_connect (GTK_OBJECT (app), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event_cb), NULL);
+
+ cal = e_calendar_new ();
+ gtk_widget_show (cal);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), cal, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ gnome_app_set_contents (GNOME_APP (app), vbox);
+ gtk_widget_show (app);
+
+ gtk_main ();
+
+ return 0;
+}