From 516b594aa9e8abf1643905e7eb48e21f8684c1aa Mon Sep 17 00:00:00 2001 From: Damon Chaplin Date: Tue, 25 Jul 2000 19:45:47 +0000 Subject: new widget and canvas item to replace GtkCalendar. Not quite finished yet. 2000-07-25 Damon Chaplin * e-calendar-item.h: * e-calendar.[hc]: new widget and canvas item to replace GtkCalendar. Not quite finished yet. svn path=/trunk/; revision=4322 --- widgets/misc/.cvsignore | 1 + widgets/misc/ChangeLog | 6 + widgets/misc/Makefile.am | 15 +- widgets/misc/e-calendar-item.c | 1615 ++++++++++++++++++++++++++++++++++++++++ widgets/misc/e-calendar-item.h | 151 ++++ widgets/misc/e-calendar.c | 481 ++++++++++++ widgets/misc/e-calendar.h | 87 +++ widgets/misc/test-calendar.c | 74 ++ 8 files changed, 2429 insertions(+), 1 deletion(-) create mode 100644 widgets/misc/e-calendar-item.c create mode 100644 widgets/misc/e-calendar-item.h create mode 100644 widgets/misc/e-calendar.c create mode 100644 widgets/misc/e-calendar.h create mode 100644 widgets/misc/test-calendar.c (limited to 'widgets') 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 + + * e-calendar-item.h: + * e-calendar.[hc]: new widget and canvas item to replace GtkCalendar. + Not quite finished yet. + 2000-07-21 Ettore Perazzoli * 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 + * + * 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 +#include +#include +#include +#include +#include +#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 + * + * 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 + +#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 + * + * 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 +#include +#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 + * + * 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 +#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 + * + * 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 +#endif + +#include + +#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; +} -- cgit v1.2.3