diff options
author | Damon Chaplin <damon@helixcode.com> | 2000-08-31 04:46:55 +0800 |
---|---|---|
committer | Damon Chaplin <damon@src.gnome.org> | 2000-08-31 04:46:55 +0800 |
commit | c03e143c6cf5cbcf5b3408a81071d064bf0cb4df (patch) | |
tree | 73e913e4efc34d8a294a69b1f6a4ee4f8d6148de /widgets/misc/e-calendar-item.c | |
parent | e9fd9ec06698877a1082a3ccea622b7446439c55 (diff) | |
download | gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar.gz gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar.bz2 gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar.lz gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar.xz gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.tar.zst gsoc2013-evolution-c03e143c6cf5cbcf5b3408a81071d064bf0cb4df.zip |
Updated.
2000-08-30 Damon Chaplin <damon@helixcode.com>
* e-calendar-item.[hc]:
* e-calendar.[hc]: Updated.
svn path=/trunk/; revision=5120
Diffstat (limited to 'widgets/misc/e-calendar-item.c')
-rw-r--r-- | widgets/misc/e-calendar-item.c | 1778 |
1 files changed, 1462 insertions, 316 deletions
diff --git a/widgets/misc/e-calendar-item.c b/widgets/misc/e-calendar-item.c index e51754d786..08a0fa7e71 100644 --- a/widgets/misc/e-calendar-item.c +++ b/widgets/misc/e-calendar-item.c @@ -29,44 +29,61 @@ #include <config.h> #include <time.h> #include <glib.h> +#include <gtk/gtkmain.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenuitem.h> +#include <gtk/gtksignal.h> #include <libgnome/gnome-defs.h> #include <libgnome/gnome-i18n.h> #include <e-util/e-util.h> #include "e-calendar-item.h" +/* + * These are the padding sizes between various pieces of the calendar. + */ + /* 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 +/* Vertical padding. */ +#define E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS 1 +#define E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS 0 +#define E_CALENDAR_ITEM_YPAD_ABOVE_CELLS 1 +#define E_CALENDAR_ITEM_YPAD_BELOW_CELLS 2 -/* 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 +/* Horizontal padding in the heading bars. */ +#define E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON 16 +#define E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME 3 +#define E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME 3 +#define E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON 16 -#define E_CALENDAR_ITEM_YPAD3 1 -#define E_CALENDAR_ITEM_YPAD4 0 -#define E_CALENDAR_ITEM_YPAD5 1 -#define E_CALENDAR_ITEM_YPAD6 2 +/* Horizontal padding in the month displays. */ +#define E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS 4 +#define E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS 2 +#define E_CALENDAR_ITEM_XPAD_BEFORE_CELLS 1 +#define E_CALENDAR_ITEM_XPAD_AFTER_CELLS 4 -#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 space on each end of the horizontal line. */ +#define E_CALENDAR_ITEM_LINE_PAD 4 /* 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 const int e_calendar_item_days_in_month[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define DAYS_IN_MONTH(year, month) \ + e_calendar_item_days_in_month[month] + (((month) == 1 \ + && ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))) ? 1 : 0) + static void e_calendar_item_class_init (ECalendarItemClass *class); static void e_calendar_item_init (ECalendarItem *calitem); - +static void e_calendar_item_destroy (GtkObject *o); static void e_calendar_item_get_arg (GtkObject *o, GtkArg *arg, guint arg_id); @@ -122,14 +139,16 @@ static gboolean e_calendar_item_motion (ECalendarItem *calitem, static gboolean e_calendar_item_convert_position_to_day (ECalendarItem *calitem, gint x, gint y, - gint *month, + gboolean round_empty_positions, + gint *month_offset, 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); + gint *first_day_offset, + gint *days_in_month, + gint *days_in_prev_month); static void e_calendar_item_recalc_sizes(ECalendarItem *calitem); static gint e_calendar_item_get_week_number (ECalendarItem *calitem, @@ -141,15 +160,64 @@ static void e_calendar_item_get_day_style (ECalendarItem *calitem, gint year, gint month, gint day, + gint day_style, gboolean today, - gboolean current_month, + gboolean prev_or_next_month, gboolean selected, + gboolean has_focus, + gboolean drop_target, GdkColor **bg_color, GdkColor **fg_color, GdkColor **box_color, gboolean *bold); - -static GnomeCanvasItemClass *parent_class; +static void e_calendar_item_check_selection_end (ECalendarItem *calitem, + gint start_month, + gint start_day, + gint *end_month, + gint *end_day); +static void e_calendar_item_check_selection_start(ECalendarItem *calitem, + gint *start_month, + gint *start_day, + gint end_month, + gint end_day); +static void e_calendar_item_normalize_date (ECalendarItem *calitem, + gint *year, + gint *month); +static void e_calendar_item_add_days_to_selection(ECalendarItem *calitem, + gint days); +static void e_calendar_item_round_up_selection (ECalendarItem *calitem, + gint *month_offset, + gint *day); +static void e_calendar_item_round_down_selection (ECalendarItem *calitem, + gint *month_offset, + gint *day); +static gint e_calendar_item_get_inclusive_days (ECalendarItem *calitem, + gint start_month_offset, + gint start_day, + gint end_month_offset, + gint end_day); +static void e_calendar_item_ensure_valid_day (ECalendarItem *calitem, + gint *month_offset, + gint *day); +static gboolean e_calendar_item_ensure_days_visible (ECalendarItem *calitem, + gint start_year, + gint start_month, + gint start_day, + gint end_year, + gint end_month, + gint end_day); +static void e_calendar_item_show_popup_menu (ECalendarItem *calitem, + GdkEventButton *event, + gint month_offset); +static void e_calendar_item_on_menu_item_activate(GtkWidget *menuitem, + ECalendarItem *calitem); +static void e_calendar_item_position_menu (GtkMenu *menu, + gint *x, + gint *y, + gpointer user_data); +static void e_calendar_item_date_range_changed (ECalendarItem *calitem); +static void e_calendar_item_queue_signal_emission (ECalendarItem *calitem); +static gboolean e_calendar_item_signal_emission_idle_cb (gpointer data); /* Our arguments. */ enum { @@ -160,6 +228,7 @@ enum { ARG_Y1, ARG_X2, ARG_Y2, + ARG_BUTTONS_SPACE, ARG_FONT, ARG_WEEK_NUMBER_FONT, ARG_ROW_HEIGHT, @@ -169,10 +238,23 @@ enum { ARG_MAXIMUM_ROWS, ARG_MAXIMUM_COLUMNS, ARG_WEEK_START_DAY, - ARG_SHOW_WEEK_NUMBERS + ARG_SHOW_WEEK_NUMBERS, + ARG_MAXIMUM_DAYS_SELECTED, + ARG_DAYS_TO_START_WEEK_SELECTION, + ARG_ROUND_SELECTION_WHEN_MOVING +}; + +enum { + DATE_RANGE_CHANGED, + SELECTION_CHANGED, + LAST_SIGNAL }; +static GnomeCanvasItemClass *parent_class; +static guint e_calendar_item_signals[LAST_SIGNAL] = { 0 }; + + E_MAKE_TYPE (e_calendar_item, "ECalendarItem", ECalendarItem, e_calendar_item_class_init, e_calendar_item_init, GNOME_TYPE_CANVAS_ITEM) @@ -207,6 +289,9 @@ e_calendar_item_class_init (ECalendarItemClass *class) gtk_object_add_arg_type ("ECalendarItem::y2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y2); + gtk_object_add_arg_type ("ECalendarItem::buttons_space", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, + ARG_BUTTONS_SPACE); gtk_object_add_arg_type ("ECalendarItem::font", GTK_TYPE_GDK_FONT, GTK_ARG_READWRITE, ARG_FONT); @@ -214,12 +299,12 @@ e_calendar_item_class_init (ECalendarItemClass *class) 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, + GTK_TYPE_INT, GTK_ARG_READABLE, ARG_ROW_HEIGHT); gtk_object_add_arg_type ("ECalendarItem::column_width", - GTK_TYPE_DOUBLE, GTK_ARG_READABLE, + GTK_TYPE_INT, GTK_ARG_READABLE, ARG_COLUMN_WIDTH); - gtk_object_add_arg_type ("ECalendarItem::mininum_rows", + gtk_object_add_arg_type ("ECalendarItem::minimum_rows", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MINIMUM_ROWS); gtk_object_add_arg_type ("ECalendarItem::minimum_columns", @@ -237,7 +322,36 @@ e_calendar_item_class_init (ECalendarItemClass *class) gtk_object_add_arg_type ("ECalendarItem::show_week_numbers", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_SHOW_WEEK_NUMBERS); - + gtk_object_add_arg_type ("ECalendarItem::maximum_days_selected", + GTK_TYPE_INT, GTK_ARG_READWRITE, + ARG_MAXIMUM_DAYS_SELECTED); + gtk_object_add_arg_type ("ECalendarItem::days_to_start_week_selection", + GTK_TYPE_INT, GTK_ARG_READWRITE, + ARG_DAYS_TO_START_WEEK_SELECTION); + gtk_object_add_arg_type ("ECalendarItem::round_selection_when_moving", + GTK_TYPE_BOOL, GTK_ARG_READWRITE, + ARG_ROUND_SELECTION_WHEN_MOVING); + + e_calendar_item_signals[DATE_RANGE_CHANGED] = + gtk_signal_new ("date_range_changed", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (ECalendarItemClass, date_range_changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + e_calendar_item_signals[SELECTION_CHANGED] = + gtk_signal_new ("selection_changed", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (ECalendarItemClass, selection_changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + gtk_object_class_add_signals (object_class, e_calendar_item_signals, + LAST_SIGNAL); + + + object_class->destroy = e_calendar_item_destroy; object_class->get_arg = e_calendar_item_get_arg; object_class->set_arg = e_calendar_item_set_arg; @@ -248,6 +362,9 @@ e_calendar_item_class_init (ECalendarItemClass *class) item_class->draw = e_calendar_item_draw; item_class->point = e_calendar_item_point; item_class->event = e_calendar_item_event; + + class->date_range_changed = NULL; + class->selection_changed = NULL; } @@ -263,22 +380,66 @@ e_calendar_item_init (ECalendarItem *calitem) calitem->year = tmp_tm->tm_year + 1900; calitem->month = tmp_tm->tm_mon; + calitem->styles = NULL; + calitem->min_cols = 1; calitem->min_rows = 1; + calitem->max_cols = -1; + calitem->max_rows = -1; + + calitem->rows = 0; + calitem->cols = 0; + calitem->show_week_numbers = FALSE; calitem->week_start_day = 0; calitem->expand = TRUE; + calitem->max_days_selected = 42; + calitem->days_to_start_week_selection = 9; + calitem->round_selection_when_moving = FALSE; calitem->x1 = 0.0; calitem->y1 = 0.0; calitem->x2 = 0.0; calitem->y2 = 0.0; - calitem->selection_start_month_offset = -1; + calitem->buttons_space = 0.0; + + calitem->selection_start_month_offset = -2; + + calitem->selection_changed = FALSE; + calitem->date_range_changed = FALSE; + + calitem->style_callback = NULL; + calitem->style_callback_destroy = NULL; /* Translators: These are the first characters of each day of the week, 'M' for 'Monday', 'T' for Tuesday etc. */ calitem->days = _("MTWTFSS"); + + calitem->signal_emission_idle_id = 0; +} + + +static void +e_calendar_item_destroy (GtkObject *o) +{ + ECalendarItem *calitem; + + calitem = E_CALENDAR_ITEM (o); + + e_calendar_item_set_style_callback (calitem, NULL, NULL, NULL); + + g_free (calitem->styles); + + if (calitem->signal_emission_idle_id != 0) { + g_source_remove (calitem->signal_emission_idle_id); + calitem->signal_emission_idle_id = 0; + } + + if (calitem->old_font) + gdk_font_unref (calitem->old_font); + if (calitem->old_week_number_font) + gdk_font_unref (calitem->old_week_number_font); } @@ -310,6 +471,9 @@ e_calendar_item_get_arg (GtkObject *o, GtkArg *arg, guint arg_id) case ARG_Y2: GTK_VALUE_DOUBLE (*arg) = calitem->y2; break; + case ARG_BUTTONS_SPACE: + GTK_VALUE_DOUBLE (*arg) = calitem->buttons_space; + break; case ARG_FONT: GTK_VALUE_BOXED (*arg) = calitem->font; break; @@ -318,11 +482,11 @@ e_calendar_item_get_arg (GtkObject *o, GtkArg *arg, guint arg_id) break; case ARG_ROW_HEIGHT: e_calendar_item_recalc_sizes (calitem); - GTK_VALUE_DOUBLE (*arg) = calitem->min_month_height; + GTK_VALUE_INT (*arg) = calitem->min_month_height; break; case ARG_COLUMN_WIDTH: e_calendar_item_recalc_sizes (calitem); - GTK_VALUE_DOUBLE (*arg) = calitem->min_month_width; + GTK_VALUE_INT (*arg) = calitem->min_month_width; break; case ARG_MINIMUM_ROWS: GTK_VALUE_INT (*arg) = calitem->min_rows; @@ -342,6 +506,15 @@ e_calendar_item_get_arg (GtkObject *o, GtkArg *arg, guint arg_id) case ARG_SHOW_WEEK_NUMBERS: GTK_VALUE_BOOL (*arg) = calitem->show_week_numbers; break; + case ARG_MAXIMUM_DAYS_SELECTED: + GTK_VALUE_INT (*arg) = calitem->max_days_selected; + break; + case ARG_DAYS_TO_START_WEEK_SELECTION: + GTK_VALUE_INT (*arg) = calitem->days_to_start_week_selection; + break; + case ARG_ROUND_SELECTION_WHEN_MOVING: + GTK_VALUE_BOOL (*arg) = calitem->round_selection_when_moving; + break; } } @@ -352,7 +525,7 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) GnomeCanvasItem *item; ECalendarItem *calitem; GdkFont *font; - gboolean need_reshape = FALSE; + gboolean need_update = FALSE; gdouble dvalue; gint ivalue; gboolean bvalue; @@ -363,44 +536,47 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) switch (arg_id){ case ARG_YEAR: ivalue = GTK_VALUE_INT (*arg); - if (calitem->year != ivalue) { - calitem->year = ivalue; - need_reshape = TRUE; - } + e_calendar_item_set_first_month (calitem, ivalue, + calitem->month); break; case ARG_MONTH: ivalue = GTK_VALUE_INT (*arg); - if (calitem->month != ivalue) { - calitem->month = ivalue; - need_reshape = TRUE; - } + e_calendar_item_set_first_month (calitem, calitem->year, + ivalue); break; case ARG_X1: dvalue = GTK_VALUE_DOUBLE (*arg); if (calitem->x1 != dvalue) { calitem->x1 = dvalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_Y1: dvalue = GTK_VALUE_DOUBLE (*arg); if (calitem->y1 != dvalue) { calitem->y1 = dvalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_X2: dvalue = GTK_VALUE_DOUBLE (*arg); if (calitem->x2 != dvalue) { calitem->x2 = dvalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_Y2: dvalue = GTK_VALUE_DOUBLE (*arg); if (calitem->y2 != dvalue) { calitem->y2 = dvalue; - need_reshape = TRUE; + need_update = TRUE; + } + break; + case ARG_BUTTONS_SPACE: + dvalue = GTK_VALUE_DOUBLE (*arg); + if (calitem->buttons_space != dvalue) { + calitem->buttons_space = dvalue; + need_update = TRUE; } break; case ARG_FONT: @@ -411,7 +587,7 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) calitem->font = font; if (font) gdk_font_ref (font); - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_WEEK_NUMBER_FONT: @@ -422,7 +598,7 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) calitem->week_number_font = font; if (font) gdk_font_ref (font); - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_MINIMUM_ROWS: @@ -430,7 +606,7 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) ivalue = MAX (1, ivalue); if (calitem->min_rows != ivalue) { calitem->min_rows = ivalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_MINIMUM_COLUMNS: @@ -438,43 +614,55 @@ e_calendar_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) ivalue = MAX (1, ivalue); if (calitem->min_cols != ivalue) { calitem->min_cols = ivalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_MAXIMUM_ROWS: ivalue = GTK_VALUE_INT (*arg); if (calitem->max_rows != ivalue) { calitem->max_rows = ivalue; - need_reshape = TRUE; + need_update = TRUE; } break; case ARG_MAXIMUM_COLUMNS: ivalue = GTK_VALUE_INT (*arg); if (calitem->max_cols != ivalue) { calitem->max_cols = ivalue; - need_reshape = TRUE; + need_update = 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; + need_update = TRUE; } break; case ARG_SHOW_WEEK_NUMBERS: bvalue = GTK_VALUE_BOOL (*arg); if (calitem->show_week_numbers != bvalue) { calitem->show_week_numbers = bvalue; - need_reshape = TRUE; + need_update = TRUE; } break; + case ARG_MAXIMUM_DAYS_SELECTED: + ivalue = GTK_VALUE_INT (*arg); + ivalue = MAX (1, ivalue); + calitem->max_days_selected = ivalue; + break; + case ARG_DAYS_TO_START_WEEK_SELECTION: + ivalue = GTK_VALUE_INT (*arg); + calitem->days_to_start_week_selection = ivalue; + break; + case ARG_ROUND_SELECTION_WHEN_MOVING: + bvalue = GTK_VALUE_BOOL (*arg); + calitem->round_selection_when_moving = bvalue; + break; default: g_warning ("Invalid arg"); } - /* FIXME: finish. */ - if (need_reshape) { + if (need_update) { gnome_canvas_item_request_update (item); } } @@ -485,27 +673,31 @@ e_calendar_item_realize (GnomeCanvasItem *item) { ECalendarItem *calitem; GdkColormap *colormap; - gboolean success[E_CALENDAR_COLOR_LAST]; + gboolean success[E_CALENDAR_ITEM_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_ITEM_COLOR_TODAY_BOX].red = 65535; + calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX].green = 0; + calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX].blue = 0; + + calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG].red = 65535; + calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG].green = 65535; + calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG].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_ITEM_COLOR_SELECTION_BG].red = 47000; + calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG].green = 47000; + calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG].blue = 48000; - calitem->colors[E_CALENDAR_COLOR_TODAY].red = 65535; - calitem->colors[E_CALENDAR_COLOR_TODAY].green = 0; - calitem->colors[E_CALENDAR_COLOR_TODAY].blue = 0; + calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG].red = 47000; + calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG].green = 47000; + calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG].blue = 48000; nfailed = gdk_colormap_alloc_colors (colormap, calitem->colors, - E_CALENDAR_COLOR_LAST, FALSE, + E_CALENDAR_ITEM_COLOR_LAST, FALSE, TRUE, success); if (nfailed) g_warning ("Failed to allocate all colors"); @@ -523,7 +715,7 @@ e_calendar_item_unrealize (GnomeCanvasItem *item) colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); - for (i = 0; i < E_CALENDAR_COLOR_LAST; i++) + for (i = 0; i < E_CALENDAR_ITEM_COLOR_LAST; i++) gdk_colors_free (colormap, &calitem->colors[i].pixel, 1, 0); } @@ -538,7 +730,7 @@ e_calendar_item_update (GnomeCanvasItem *item, GtkStyle *style; GdkFont *font; gint char_height, width, height, space, space_per_cal, space_per_cell; - gint xthickness, ythickness; + gint rows, cols, xthickness, ythickness; if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags); @@ -563,19 +755,25 @@ e_calendar_item_update (GnomeCanvasItem *item, /* Calculate how many rows & cols we can fit in. */ width = item->x2 - item->x1; - height = item->y2 - item->y1; + height = item->y2 - item->y1 - calitem->buttons_space; width -= xthickness * 2; height -= ythickness * 2; - calitem->rows = height / calitem->min_month_height; - calitem->rows = MAX (calitem->rows, calitem->min_rows); + rows = height / calitem->min_month_height; + rows = MAX (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); + rows = MIN (rows, calitem->max_rows); + cols = width / calitem->min_month_width; + cols = MAX (cols, calitem->min_cols); if (calitem->max_cols > 0) - calitem->cols = MIN (calitem->cols, calitem->max_cols); + cols = MIN (cols, calitem->max_cols); + + if (rows != calitem->rows || cols != calitem->cols) + e_calendar_item_date_range_changed (calitem); + + calitem->rows = rows; + calitem->cols = cols; /* Split up the empty space according to the configuration. If the calendar is set to expand, we divide the space between the @@ -653,7 +851,9 @@ e_calendar_item_draw (GnomeCanvasItem *canvas_item, GtkStyle *style; GdkFont *font; GdkGC *base_gc, *bg_gc; - gint char_height, row, col, row_y, bar_height, col_x, ythickness; + gint char_height, row, col, row_y, bar_height, col_x; + gint xthickness, ythickness; + gint line_y, line_x1, line_x2; #if 0 g_print ("In e_calendar_item_draw %i,%i %ix%i\n", @@ -665,6 +865,7 @@ e_calendar_item_draw (GnomeCanvasItem *canvas_item, if (!font) font = style->font; char_height = font->ascent + font->descent; + xthickness = style->klass->xthickness; ythickness = style->klass->ythickness; base_gc = style->base_gc[GTK_STATE_NORMAL]; bg_gc = style->bg_gc[GTK_STATE_NORMAL]; @@ -672,32 +873,37 @@ e_calendar_item_draw (GnomeCanvasItem *canvas_item, /* 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); + calitem->x2 - calitem->x1 + 1, + calitem->y2 - calitem->y1 + 1); - /* Draw the shadow around the entire item. - FIXME: must also leave room for the 'Today' & 'None' buttons etc. */ + /* Draw the shadow around the entire item. */ gtk_draw_shadow (style, drawable, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, + GTK_STATE_NORMAL, GTK_SHADOW_IN, calitem->x1 - x, calitem->y1 - y, - calitem->x2 - calitem->x1, calitem->y2 - calitem->y1); + calitem->x2 - calitem->x1 + 1, + calitem->y2 - calitem->y1 + 1); row_y = canvas_item->y1 + ythickness; - bar_height = ythickness * 2 + E_CALENDAR_ITEM_YPAD1 + char_height - + E_CALENDAR_ITEM_YPAD2; + bar_height = ythickness * 2 + + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + char_height + + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME; 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); + calitem->x1 + xthickness - x, row_y - y, + calitem->x2 - calitem->x1 + 1 + - xthickness * 2, + 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); + calitem->x1 + xthickness - x, row_y - y, + calitem->x2 - calitem->x1 + 1 + - xthickness * 2, + bar_height); for (col = 0; col < calitem->cols; col++) { @@ -719,6 +925,18 @@ e_calendar_item_draw (GnomeCanvasItem *canvas_item, row_y += calitem->month_height; } + + /* Draw the horizontal line, if the Today or None buttons is shown. */ + if (calitem->buttons_space) { + line_y = calitem->y2 + 1 - ythickness - calitem->buttons_space + - y; + line_x1 = calitem->x1 + xthickness + + E_CALENDAR_ITEM_LINE_PAD - x; + line_x2 = calitem->x2 + 1 - xthickness + - E_CALENDAR_ITEM_LINE_PAD - x; + gdk_draw_line (drawable, bg_gc, + line_x1, line_y, line_x2, line_y); + } } @@ -736,17 +954,20 @@ e_calendar_item_draw_month (ECalendarItem *calitem, GtkWidget *widget; GtkStyle *style; GdkFont *font; - GdkGC *fg_gc, *bg_gc; + GdkGC *fg_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 year, month; + gint month_x, month_y, month_w, month_h; + gint min_x, max_x, text_x, text_y; gint day, day_index, cells_x, cells_y, min_cell_width, text_width; + gint clip_width, clip_height; gchar buffer[64]; #if 0 - g_print ("In e_calendar_item_draw_month: %i,%i %ix%i\n", - x, y, width, height); + g_print ("In e_calendar_item_draw_month: %i,%i %ix%i row:%i col:%i\n", + x, y, width, height, row, col); #endif item = GNOME_CANVAS_ITEM (calitem); widget = GTK_WIDGET (item->canvas); @@ -758,82 +979,104 @@ e_calendar_item_draw_month (ECalendarItem *calitem, 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_w = item->x2 - item->x1 - xthickness * 2; + month_w = MIN (month_w, calitem->month_width); month_y = item->y1 + ythickness + row * calitem->month_height - y; + month_h = item->y2 - item->y1 - calitem->buttons_space + - ythickness * 2; + month_h = MIN (month_h, calitem->month_height); /* 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; + month = calitem->month + row * calitem->cols + col; + year = calitem->year + month / 12; + month %= 12; /* 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; + min_x = E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON; else - min_x = E_CALENDAR_ITEM_XPAD14 + E_CALENDAR_ITEM_XPAD15; + min_x = E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME; + max_x = month_w; if (row == 0 && col == calitem->cols - 1) - max_x = calitem->month_width - E_CALENDAR_ITEM_XPAD16; + max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON; else - max_x = calitem->month_width - E_CALENDAR_ITEM_XPAD12 - - E_CALENDAR_ITEM_XPAD13; + max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME; text_y = month_y + style->klass->ythickness - + E_CALENDAR_ITEM_YPAD1; + + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME; 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); + clip_rect.x = MAX (0, clip_rect.x); + clip_rect.y = MAX (0, text_y); + + if (month_x + max_x - clip_rect.x > 0) { + clip_rect.width = month_x + max_x - clip_rect.x; + clip_rect.height = text_y + char_height - clip_rect.y; + gdk_gc_set_clip_rectangle (fg_gc, &clip_rect); + + memset (&tmp_tm, 0, sizeof (tmp_tm)); + 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); + } - 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; + /* Set the clip rectangle for the main month display. */ + clip_rect.x = MAX (0, month_x); + clip_rect.y = MAX (0, month_y); + clip_width = month_x + month_w - clip_rect.x; + clip_height = month_y + month_h - clip_rect.y; - /* 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); + if (clip_width <= 0 || clip_height <= 0) + return; - gdk_draw_string (drawable, font, fg_gc, - month_x + text_x, text_y + font->ascent, buffer); + clip_rect.width = clip_width; + clip_rect.height = clip_height; - gdk_gc_set_clip_rectangle (fg_gc, NULL); + gdk_gc_set_clip_rectangle (fg_gc, &clip_rect); /* 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; + cells_x = month_x + E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS + calitem->month_lpad + + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS; if (calitem->show_week_numbers) cells_x += calitem->max_week_number_digit_width * 2 - + E_CALENDAR_ITEM_XPAD2 + 1; + + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 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; + text_y = month_y + ythickness * 2 + + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME + + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS + calitem->month_tpad; - cells_y = text_y + char_height + E_CALENDAR_ITEM_YPAD4 + 1 - + E_CALENDAR_ITEM_YPAD5; + cells_y = text_y + char_height + + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1 + + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS; text_y += font->ascent; day_index = calitem->week_start_day; @@ -850,10 +1093,10 @@ e_calendar_item_draw_month (ECalendarItem *calitem, /* 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_ITEM_XPAD_BEFORE_CELLS, + cells_y - E_CALENDAR_ITEM_YPAD_ABOVE_CELLS - 1, cells_x + E_CALENDAR_COLS_PER_MONTH * calitem->cell_width - 1, - cells_y - E_CALENDAR_ITEM_YPAD5); + cells_y - E_CALENDAR_ITEM_YPAD_ABOVE_CELLS - 1); e_calendar_item_draw_day_numbers (calitem, drawable, width, height, row, col, year, month, start_weekday, @@ -862,11 +1105,13 @@ e_calendar_item_draw_month (ECalendarItem *calitem, /* 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_x - E_CALENDAR_ITEM_XPAD_BEFORE_CELLS - 1, + cells_y - E_CALENDAR_ITEM_YPAD_ABOVE_CELLS - 1, + cells_x - E_CALENDAR_ITEM_XPAD_BEFORE_CELLS - 1, cells_y + E_CALENDAR_ROWS_PER_MONTH * calitem->cell_height - 1); } + + gdk_gc_set_clip_rectangle (fg_gc, NULL); } @@ -897,9 +1142,11 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, 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; + gboolean today, selected, has_focus = FALSE, drop_target = FALSE; + gboolean bold, draw_day, finished = FALSE; + gint today_year, today_month, today_mday, month_offset; gchar buffer[2]; + gint day_style = 0; item = GNOME_CANVAS_ITEM (calitem); widget = GTK_WIDGET (item->canvas); @@ -918,7 +1165,7 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, 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. */ + months. */ years[0] = years[1] = years[2] = year; months[0] = month - 1; months[1] = month; @@ -932,14 +1179,15 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, 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]); + days_in_month[0] = DAYS_IN_MONTH (years[0], months[0]); + days_in_month[1] = DAYS_IN_MONTH (years[1], months[1]); + days_in_month[2] = DAYS_IN_MONTH (years[2], months[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; + month_offset = row * calitem->cols + col - 1; day_num = days_in_month[0]; days_from_week_start = (start_weekday + 7 - calitem->week_start_day) % 7; @@ -951,6 +1199,7 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, day_num -= 6; } else { mon++; + month_offset++; day_num = 1; } } else { @@ -968,9 +1217,6 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, 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) { @@ -979,8 +1225,8 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, months[mon], years[mon]); - text_x = cells_x - E_CALENDAR_ITEM_XPAD3 - 1 - - E_CALENDAR_ITEM_XPAD2; + text_x = cells_x - E_CALENDAR_ITEM_XPAD_BEFORE_CELLS - 1 + - E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS; text_y = cells_y + drow * calitem->cell_height + + (calitem->cell_height - min_cell_height + 1) / 2; @@ -995,6 +1241,8 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, text_x -= calitem->week_number_digit_widths[digit]; buffer[num_chars++] = digit + '0'; + gdk_gc_set_foreground (fg_gc, + &style->fg[GTK_STATE_NORMAL]); gdk_draw_text (drawable, wkfont, fg_gc, text_x, text_y + font->ascent, buffer, num_chars); @@ -1009,26 +1257,51 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, && months[mon] == today_month && day_num == today_mday; - selected = calitem->selection_start_month_offset != -1 + selected = calitem->selection_start_month_offset != -2 && (calitem->selection_start_month_offset < month_offset || (calitem->selection_start_month_offset == month_offset - && calitem->selection_start_day_offset <= day_offset)) + && calitem->selection_start_day <= day_num)) && (calitem->selection_end_month_offset > month_offset || (calitem->selection_end_month_offset == month_offset - && calitem->selection_end_day_offset >= day_offset)); + && calitem->selection_end_day >= day_num)); + + if (calitem->styles) + day_style = calitem->styles[(month_offset + 1) * 32 + day_num]; /* 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); + if (calitem->style_callback) + (*calitem->style_callback) + (calitem, + years[mon], + months[mon], + day_num, + day_style, + today, + mon != 1, + selected, + has_focus, + drop_target, + &bg_color, + &fg_color, + &box_color, + &bold, + calitem->style_callback_data); + else + e_calendar_item_get_day_style + (calitem, + years[mon], + months[mon], + day_num, + day_style, + today, + mon != 1, + selected, + has_focus, + drop_target, + &bg_color, + &fg_color, + &box_color, + &bold); /* Draw the background, if set. */ if (bg_color) { @@ -1090,6 +1363,7 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, /* See if we've reached the end of a month. */ if (day_num == days_in_month[mon]) { + month_offset++; mon++; /* We only draw the start of the next month for the bottom-right month displayed. */ @@ -1104,8 +1378,6 @@ e_calendar_item_draw_day_numbers (ECalendarItem *calitem, } else { day_num++; } - - day_offset++; } /* Exit the loop if the flag is set. */ @@ -1198,7 +1470,7 @@ e_calendar_item_event (GnomeCanvasItem *item, GdkEvent *event) /* This checks if any fonts have changed, and if so it recalculates the - layout of the item. */ + text sizes and the minimum month size. */ static void e_calendar_item_recalc_sizes (ECalendarItem *calitem) { @@ -1220,13 +1492,23 @@ e_calendar_item_recalc_sizes (ECalendarItem *calitem) wkfont = font; char_height = font->ascent + font->descent; + g_return_if_fail (font != NULL); + g_return_if_fail (wkfont != NULL); + /* If both fonts are the same, just return. */ if (font == calitem->old_font && wkfont == calitem->old_week_number_font) return; + if (calitem->old_font) + gdk_font_unref (calitem->old_font); calitem->old_font = font; + gdk_font_ref (font); + + if (calitem->old_week_number_font) + gdk_font_unref (calitem->old_week_number_font); calitem->old_week_number_font = wkfont; + gdk_font_ref (wkfont); for (day = 0; day < 7; day++) calitem->day_widths[day] = gdk_char_width (font, @@ -1255,19 +1537,20 @@ e_calendar_item_recalc_sizes (ECalendarItem *calitem) 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; + calitem->min_month_width = E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS + + E_CALENDAR_ITEM_XPAD_BEFORE_CELLS + min_cell_width * 7 + + E_CALENDAR_ITEM_XPAD_AFTER_CELLS; if (calitem->show_week_numbers) calitem->min_month_width += max_week_number_digit_width * 2 - + E_CALENDAR_ITEM_XPAD2 + 1; + + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 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; + + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + char_height + + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME + 1 + + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS + + char_height + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1 + + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS + min_cell_height * 6 + + E_CALENDAR_ITEM_YPAD_BELOW_CELLS; } @@ -1276,9 +1559,12 @@ e_calendar_item_get_day_style (ECalendarItem *calitem, gint year, gint month, gint day, + gint day_style, gboolean today, - gboolean current_month, + gboolean prev_or_next_month, gboolean selected, + gboolean has_focus, + gboolean drop_target, GdkColor **bg_color, GdkColor **fg_color, GdkColor **box_color, @@ -1289,15 +1575,18 @@ e_calendar_item_get_day_style (ECalendarItem *calitem, *box_color = NULL; *bold = FALSE; + if (day_style == 1) + *bold = TRUE; + if (today) - *box_color = &calitem->colors[E_CALENDAR_COLOR_TODAY]; + *box_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX]; - if (!current_month) - *fg_color = &calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT]; + if (prev_or_next_month) + *fg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG]; if (selected) { - *fg_color = &calitem->colors[E_CALENDAR_COLOR_SELECTION]; - *bg_color = &calitem->colors[E_CALENDAR_COLOR_HIGHLIGHT]; + *fg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG]; + *bg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG]; } } @@ -1307,30 +1596,69 @@ static gboolean e_calendar_item_button_press (ECalendarItem *calitem, GdkEvent *event) { - gint month, day; - gboolean all_week; + gint month_offset, day; + gboolean all_week, round_up_end = FALSE, round_down_start = FALSE; 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); + if (event->button.button == 4) + e_calendar_item_set_first_month (calitem, calitem->year, + calitem->month - 1); + else if (event->button.button == 5) + e_calendar_item_set_first_month (calitem, calitem->year, + calitem->month + 1); + + if (!e_calendar_item_convert_position_to_day (calitem, + event->button.x, + event->button.y, + TRUE, + &month_offset, &day, + &all_week)) + return FALSE; - 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; + if (event->button.button == 3 && day == -1) { + e_calendar_item_show_popup_menu (calitem, + (GdkEventButton*) event, + month_offset); + return TRUE; + } - gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); + if (event->button.button != 1 || day == -1) + return FALSE; - return TRUE; + g_print (" month offset: %i day: %i\n", month_offset, day); + + calitem->selection_start_month_offset = month_offset; + calitem->selection_start_day = day; + calitem->selection_end_month_offset = month_offset; + calitem->selection_end_day = day; + + calitem->selection_real_start_month_offset = month_offset; + calitem->selection_real_start_day = day; + + calitem->selection_from_full_week = FALSE; + calitem->selecting = TRUE; + calitem->selection_dragging_end = TRUE; + + if (all_week) { + calitem->selection_from_full_week = TRUE; + round_up_end = TRUE; } - return FALSE; + if (calitem->days_to_start_week_selection == 1) { + round_down_start = TRUE; + round_up_end = TRUE; + } + + if (round_up_end) + e_calendar_item_round_up_selection (calitem, &calitem->selection_end_month_offset, &calitem->selection_end_day); + + if (round_down_start) + e_calendar_item_round_down_selection (calitem, &calitem->selection_start_month_offset, &calitem->selection_start_day); + + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); + + return TRUE; } @@ -1338,18 +1666,26 @@ 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"); + if (!calitem->selecting) + return FALSE; + 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); + /* If the user selects the grayed dates before the first month or + after the last month, we move backwards or forwards one month. + The set_month() call should take care of updating the selection. */ + if (calitem->selection_end_month_offset == -1) + e_calendar_item_set_first_month (calitem, calitem->year, + calitem->month - 1); + else if (calitem->selection_start_month_offset == calitem->rows * calitem->cols) + e_calendar_item_set_first_month (calitem, calitem->year, + calitem->month + 1); + + calitem->selection_changed = TRUE; + e_calendar_item_queue_signal_emission (calitem); + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); return FALSE; } @@ -1359,61 +1695,200 @@ static gboolean e_calendar_item_motion (ECalendarItem *calitem, GdkEvent *event) { - gint month, day; - gboolean all_week; + gint start_month, start_day, end_month, end_day, month_offset, day; + gint tmp_month, tmp_day, days_in_selection; + gboolean all_week, round_up_end = FALSE, round_down_start = FALSE; 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 (!e_calendar_item_convert_position_to_day (calitem, + event->button.x, + event->button.y, + TRUE, + &month_offset, &day, + &all_week)) + return FALSE; + + if (day == -1) + return FALSE; + + if (calitem->selection_dragging_end) { + start_month = calitem->selection_real_start_month_offset; + start_day = calitem->selection_real_start_day; + end_month = month_offset; + end_day = day; + } else { + start_month = month_offset; + start_day = day; + end_month = calitem->selection_real_start_month_offset; + end_day = calitem->selection_real_start_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; + if (start_month > end_month || (start_month == end_month + && start_day > end_day)) { + tmp_month = start_month; + tmp_day = start_day; + start_month = end_month; + start_day = end_day; + end_month = tmp_month; + end_day = tmp_day; + + calitem->selection_dragging_end = !calitem->selection_dragging_end; + } + + if (calitem->days_to_start_week_selection > 0) { + days_in_selection = e_calendar_item_get_inclusive_days (calitem, start_month, start_day, end_month, end_day); + if (days_in_selection >= calitem->days_to_start_week_selection) { + round_down_start = TRUE; + round_up_end = TRUE; } + } - gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); + /* If we are over a week number and we are dragging the end of the + selection, we round up to the end of this week. */ + if (all_week && calitem->selection_dragging_end) + round_up_end = TRUE; + + /* If the selection was started from a week number and we are dragging + the start of the selection, we need to round up the end to include + all of the original week selected. */ + if (calitem->selection_from_full_week + && !calitem->selection_dragging_end) + round_up_end = TRUE; + + if (round_up_end) + e_calendar_item_round_up_selection (calitem, &end_month, + &end_day); + if (round_down_start) + e_calendar_item_round_down_selection (calitem, &start_month, + &start_day); + + + /* Check we don't go over the maximum number of days to select. */ + if (calitem->selection_dragging_end) { + e_calendar_item_check_selection_end (calitem, + start_month, + start_day, + &end_month, + &end_day); + } else { + e_calendar_item_check_selection_start (calitem, + &start_month, + &start_day, + end_month, + end_day); } - return FALSE; + if (start_month == calitem->selection_start_month_offset + && start_day == calitem->selection_start_day + && end_month == calitem->selection_end_month_offset + && end_day == calitem->selection_end_day) + return FALSE; + + calitem->selection_start_month_offset = start_month; + calitem->selection_start_day = start_day; + calitem->selection_end_month_offset = end_month; + calitem->selection_end_day = end_day; + + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); + + return TRUE; +} + + +static void +e_calendar_item_check_selection_end (ECalendarItem *calitem, + gint start_month, + gint start_day, + gint *end_month, + gint *end_day) +{ + gint year, month, max_month, max_day, days_in_month; + + if (calitem->max_days_selected <= 0) + return; + + year = calitem->year; + month = calitem->month + start_month; + e_calendar_item_normalize_date (calitem, &year, &month); + + max_month = start_month; + max_day = start_day + calitem->max_days_selected - 1; + + for (;;) { + days_in_month = DAYS_IN_MONTH (year, month); + if (max_day <= days_in_month) + break; + max_month++; + month++; + if (month == 12) { + year++; + month = 0; + } + max_day -= days_in_month; + } + + if (*end_month > max_month) { + *end_month = max_month; + *end_day = max_day; + } else if (*end_month == max_month && *end_day > max_day) { + *end_day = max_day; + } } +static void +e_calendar_item_check_selection_start (ECalendarItem *calitem, + gint *start_month, + gint *start_day, + gint end_month, + gint end_day) +{ + gint year, month, min_month, min_day, days_in_month; + + if (calitem->max_days_selected <= 0) + return; + + year = calitem->year; + month = calitem->month + end_month; + e_calendar_item_normalize_date (calitem, &year, &month); + + min_month = end_month; + min_day = end_day - calitem->max_days_selected + 1; + + while (min_day <= 0) { + min_month--; + month--; + if (month == -1) { + year--; + month = 11; + } + days_in_month = DAYS_IN_MONTH (year, month); + min_day += days_in_month; + } + + if (*start_month < min_month) { + *start_month = min_month; + *start_day = min_day; + } else if (*start_month == min_month && *start_day < min_day) { + *start_day = min_day; + } +} /* 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 the month heading -1 is returned for the day. If the position is over a week number the first day of the week is returned - and entire_week is set to TRUE. */ + and entire_week is set to TRUE. + It returns FALSE if the position is completely outside all months. */ static gboolean e_calendar_item_convert_position_to_day (ECalendarItem *calitem, gint event_x, gint event_y, - gint *month, + gboolean round_empty_positions, + gint *month_offset, gint *day, gboolean *entire_week) { @@ -1422,7 +1897,8 @@ e_calendar_item_convert_position_to_day (ECalendarItem *calitem, 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; + gint first_day_offset, days_in_month, days_in_prev_month; + gint week_num_x1, week_num_x2; item = GNOME_CANVAS_ITEM (calitem); widget = GTK_WIDGET (item->canvas); @@ -1431,6 +1907,8 @@ e_calendar_item_convert_position_to_day (ECalendarItem *calitem, xthickness = style->klass->xthickness; ythickness = style->klass->ythickness; + *entire_week = FALSE; + x = event_x - xthickness - calitem->x_offset; y = event_y - ythickness; @@ -1440,45 +1918,86 @@ e_calendar_item_convert_position_to_day (ECalendarItem *calitem, row = y / calitem->month_height; col = x / calitem->month_width; - if (row < 0 || row >= calitem->rows - || col < 0 || col >= calitem->cols) + if (row >= calitem->rows || col >= calitem->cols) return FALSE; + *month_offset = row * calitem->cols + col; + 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 (y < ythickness * 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME) { + *day = -1; + return TRUE; + } - if (x < 0 || y < 0) + cells_y = ythickness * 2 + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + + char_height + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME + + E_CALENDAR_ITEM_YPAD_ABOVE_DAY_LETTERS + calitem->month_tpad + + char_height + E_CALENDAR_ITEM_YPAD_BELOW_DAY_LETTERS + 1 + + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS; + y -= cells_y; + if (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) + if (day_row >= E_CALENDAR_ROWS_PER_MONTH) return FALSE; - *month = row * calitem->cols + col; + week_num_x1 = E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS + calitem->month_lpad; + + if (calitem->show_week_numbers) { + week_num_x2 = week_num_x1 + + calitem->max_week_number_digit_width * 2; + if (x >= week_num_x1 && x < week_num_x2) + *entire_week = TRUE; + cells_x = week_num_x2 + E_CALENDAR_ITEM_XPAD_AFTER_WEEK_NUMBERS + 1; + } else { + cells_x = week_num_x1; + } + + if (*entire_week) { + day_col = 0; + } else { + cells_x += E_CALENDAR_ITEM_XPAD_BEFORE_CELLS; + x -= cells_x; + if (x < 0) + return FALSE; + day_col = x / calitem->cell_width; + if (day_col >= E_CALENDAR_COLS_PER_MONTH) + return FALSE; + } + *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; + e_calendar_item_get_month_info (calitem, row, col, &first_day_offset, + &days_in_month, &days_in_prev_month); + if (*day < first_day_offset) { + if (*entire_week || (row == 0 && col == 0)) { + (*month_offset)--; + *day = days_in_prev_month + 1 - first_day_offset + + *day; + return TRUE; + } else if (round_empty_positions) { + *day = first_day_offset; + } else { + return FALSE; + } + } + + *day -= first_day_offset - 1; + + if (*day > days_in_month) { + if (row == calitem->rows - 1 && col == calitem->cols - 1) { + (*month_offset)++; + *day -= days_in_month; + return TRUE; + } else if (round_empty_positions) { + *day = days_in_month; + } else { + return FALSE; + } + } return TRUE; } @@ -1488,16 +2007,22 @@ static void e_calendar_item_get_month_info (ECalendarItem *calitem, gint row, gint col, - gint *first_valid_day, - gint *last_valid_day) + gint *first_day_offset, + gint *days_in_month, + gint *days_in_prev_month) { - gint year, month, days_in_month, start_weekday, first_day_of_month; + gint year, 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); + + *days_in_month = DAYS_IN_MONTH (year, month); + if (month == 0) + *days_in_prev_month = DAYS_IN_MONTH (year - 1, 11); + else + *days_in_prev_month = DAYS_IN_MONTH (year, month - 1); tmp_tm.tm_year = year - 1900; tmp_tm.tm_mon = month; @@ -1510,106 +2035,727 @@ e_calendar_item_get_month_info (ECalendarItem *calitem, first_day_of_month = (start_weekday + 7 - calitem->week_start_day) % 7; - if (row == 0 && col == 0) - *first_valid_day = 0; + if (row == 0 && col == 0 && first_day_of_month == 0) + *first_day_offset = 7; else - *first_valid_day = first_day_of_month; + *first_day_offset = 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; + +void +e_calendar_item_get_first_month(ECalendarItem *calitem, + gint *year, + gint *month) +{ + *year = calitem->year; + *month = calitem->month; } +/* This also handles values of month < 0 or > 11 by updating the year. */ +void +e_calendar_item_set_first_month(ECalendarItem *calitem, + gint year, + gint month) +{ + gint new_year, new_month, months_diff, num_months; + gint old_days_in_selection, new_days_in_selection; + new_year = year; + new_month = month; + e_calendar_item_normalize_date (calitem, &new_year, &new_month); + if (calitem->year == new_year && calitem->month == new_month) + return; + /* Update the selection. */ + num_months = calitem->rows * calitem->cols; + months_diff = (new_year - calitem->year) * 12 + + new_month - calitem->month; + if (calitem->selection_start_month_offset != -2) { + if (calitem->selection_start_month_offset - months_diff >= 0 + && calitem->selection_end_month_offset - months_diff < num_months) { + calitem->selection_start_month_offset -= months_diff; + calitem->selection_end_month_offset -= months_diff; + calitem->selection_real_start_month_offset -= months_diff; + calitem->year = new_year; + calitem->month = new_month; + } else { + old_days_in_selection = e_calendar_item_get_inclusive_days (calitem, calitem->selection_start_month_offset, calitem->selection_start_day, calitem->selection_end_month_offset, calitem->selection_end_day); + /* Make sure the selection will be displayed. */ + if (calitem->selection_start_month_offset < 0 + || calitem->selection_start_month_offset >= num_months) { + calitem->selection_end_month_offset -= calitem->selection_start_month_offset; + calitem->selection_start_month_offset = 0; + } + /* We want to ensure that the same number of days are + selected after we have moved the selection. */ + calitem->year = new_year; + calitem->month = new_month; + e_calendar_item_ensure_valid_day (calitem, &calitem->selection_start_month_offset, &calitem->selection_start_day); + e_calendar_item_ensure_valid_day (calitem, &calitem->selection_end_month_offset, &calitem->selection_end_day); + if (calitem->round_selection_when_moving) { + e_calendar_item_round_down_selection (calitem, &calitem->selection_start_month_offset, &calitem->selection_start_day); + } + new_days_in_selection = e_calendar_item_get_inclusive_days (calitem, calitem->selection_start_month_offset, calitem->selection_start_day, calitem->selection_end_month_offset, calitem->selection_end_day); + if (old_days_in_selection != new_days_in_selection) + e_calendar_item_add_days_to_selection (calitem, old_days_in_selection - new_days_in_selection); + /* Flag that we need to emit the "selection_changed" + signal. We don't want to emit it here since setting + the "year" and "month" args would result in 2 + signals emitted. */ + calitem->selection_changed = TRUE; + } + } else { + calitem->year = new_year; + calitem->month = new_month; + } + e_calendar_item_date_range_changed (calitem); + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); +} +/* This will make sure that the given year & month are valid, i.e. if month + is < 0 or > 11 the year and month will be updated accordingly. */ +static void +e_calendar_item_normalize_date (ECalendarItem *calitem, + gint *year, + gint *month) +{ + if (*month >= 0) { + *year += *month / 12; + *month = *month % 12; + } else { + *year += *month / 12 - 1; + *month = *month % 12; + if (*month != 0) + *month += 12; + } +} +/* Adds or subtracts days from the selection. It is used when we switch months + and the selection extends past the end of a month but we want to keep the + number of days selected the same. days should not be more than 30. */ +static void +e_calendar_item_add_days_to_selection (ECalendarItem *calitem, + gint days) +{ + gint year, month, days_in_month; + year = calitem->year; + month = calitem->month + calitem->selection_end_month_offset; + e_calendar_item_normalize_date (calitem, &year, &month); + g_print ("In e_calendar_item_add_days_to_selection days: %i month:%i\n", days, month); -#if 0 + calitem->selection_end_day += days; + if (calitem->selection_end_day <= 0) { + month--; + e_calendar_item_normalize_date (calitem, &year, &month); + calitem->selection_end_month_offset--; + calitem->selection_end_day += DAYS_IN_MONTH (year, month); + } else { + days_in_month = DAYS_IN_MONTH (year, month); + if (calitem->selection_end_day > days_in_month) { + calitem->selection_end_month_offset++; + calitem->selection_end_day -= days_in_month; + } + } +} -static gint -e_calendar_button_press (GtkWidget *widget, - GdkEventButton *event) + +/* Returns the range of dates actually shown. Months are 0 to 11. */ +void +e_calendar_item_get_date_range (ECalendarItem *calitem, + gint *start_year, + gint *start_month, + gint *start_day, + gint *end_year, + gint *end_month, + gint *end_day) { - ECalendar *cal; - gint day; + gint first_day_offset, days_in_month, days_in_prev_month; + + /* Calculate the first day shown. This will be one of the greyed-out + days before the first full month begins. */ + e_calendar_item_get_month_info (calitem, 0, 0, &first_day_offset, + &days_in_month, &days_in_prev_month); + *start_year = calitem->year; + *start_month = calitem->month - 1; + if (*start_month == -1) { + (*start_year)--; + *start_month = 11; + } + *start_day = days_in_prev_month + 1 - first_day_offset; + + + /* Calculate the last day shown. This will be one of the greyed-out + days after the last full month ends. */ + e_calendar_item_get_month_info (calitem, calitem->rows - 1, + calitem->cols - 1, &first_day_offset, + &days_in_month, &days_in_prev_month); + *end_month = calitem->month + calitem->rows * calitem->cols; + *end_year = calitem->year + *end_month / 12; + *end_month %= 12; + *end_day = E_CALENDAR_ROWS_PER_MONTH * E_CALENDAR_COLS_PER_MONTH + - first_day_offset - days_in_month; +} - cal = E_CALENDAR (widget); - g_print ("In e_calendar_button_press\n"); +/* Simple way to mark days so they appear bold. + A more flexible interface may be added later. */ +void +e_calendar_item_clear_marks (ECalendarItem *calitem) +{ + GnomeCanvasItem *item; + + item = GNOME_CANVAS_ITEM (calitem); - day = e_calendar_convert_position_to_day (cal, event->x, event->y); + g_free (calitem->styles); + calitem->styles = NULL; - cal->selection_start_day = day; - cal->selection_end_day = day; - cal->selection_dragging_end = TRUE; + gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, + item->x2, item->y2); +} - gtk_widget_queue_draw (widget); - return FALSE; +void +e_calendar_item_mark_day (ECalendarItem *calitem, + gint year, + gint month, + gint day, + guint8 day_style) +{ + gint month_offset; + + month_offset = (year - calitem->year) * 12 + month - calitem->month; + if (month_offset < -1 || month_offset > calitem->rows * calitem->cols) + return; + + if (!calitem->styles) + calitem->styles = g_new0 (guint8, (calitem->rows * calitem->cols + 2) * 32); + + calitem->styles[(month_offset + 1) * 32 + day] = day_style; } -static gint -e_calendar_button_release (GtkWidget *widget, - GdkEventButton *event) +void +e_calendar_item_mark_days (ECalendarItem *calitem, + gint start_year, + gint start_month, + gint start_day, + gint end_year, + gint end_month, + gint end_day, + guint8 day_style) { - g_print ("In e_calendar_button_release\n"); + gint month_offset, end_month_offset, day; - return FALSE; + month_offset = (start_year - calitem->year) * 12 + start_month + - calitem->month; + day = start_day; + if (month_offset > calitem->rows * calitem->cols) + return; + if (month_offset < -1) { + month_offset = -1; + day = 1; + } + + end_month_offset = (end_year - calitem->year) * 12 + end_month + - calitem->month; + if (end_month_offset < -1) + return; + if (end_month_offset > calitem->rows * calitem->cols) { + end_month_offset = calitem->rows * calitem->cols; + end_day = 31; + } + + if (month_offset > end_month_offset) + return; + + if (!calitem->styles) + calitem->styles = g_new0 (guint8, (calitem->rows * calitem->cols + 2) * 32); + + for (;;) { + if (month_offset == end_month_offset && day > end_day) + break; + + calitem->styles[(month_offset + 1) * 32 + day] = day_style; + + day++; + if (day == 32) { + month_offset++; + day = 1; + if (month_offset > end_month_offset) + break; + } + } +} + + +/* Rounds up the given day to the end of the week. */ +static void +e_calendar_item_round_up_selection (ECalendarItem *calitem, + gint *month_offset, + gint *day) +{ + gint year, month, weekday, days, days_in_month; + struct tm tmp_tm = { 0 }; + + year = calitem->year; + month = calitem->month + *month_offset; + e_calendar_item_normalize_date (calitem, &year, &month); + + tmp_tm.tm_year = year - 1900; + tmp_tm.tm_mon = month; + tmp_tm.tm_mday = *day; + tmp_tm.tm_isdst = -1; + mktime (&tmp_tm); + + /* Convert to 0 (Monday) to 6 (Sunday). */ + weekday = (tmp_tm.tm_wday + 6) % 7; + + /* Calculate how many days to the end of the row. */ + days = (calitem->week_start_day + 6 - weekday) % 7; + + *day += days; + days_in_month = DAYS_IN_MONTH (year, month); + if (*day > days_in_month) { + (*month_offset)++; + *day -= days_in_month; + } +} + + +/* Rounds down the given day to the start of the week. */ +static void +e_calendar_item_round_down_selection (ECalendarItem *calitem, + gint *month_offset, + gint *day) +{ + gint year, month, weekday, days, days_in_month; + struct tm tmp_tm = { 0 }; + + g_print ("In e_calendar_item_round_down_selection month:%i day:%i\n", + *month_offset, *day); + + year = calitem->year; + month = calitem->month + *month_offset; + e_calendar_item_normalize_date (calitem, &year, &month); + + tmp_tm.tm_year = year - 1900; + tmp_tm.tm_mon = month; + tmp_tm.tm_mday = *day; + tmp_tm.tm_isdst = -1; + mktime (&tmp_tm); + + /* Convert to 0 (Monday) to 6 (Sunday). */ + weekday = (tmp_tm.tm_wday + 6) % 7; + + /* Calculate how many days to the start of the row. */ + days = (weekday + 7 - calitem->week_start_day) % 7; + + *day -= days; + if (*day <= 0) { + month--; + if (month == -1) { + year--; + month = 11; + } + days_in_month = DAYS_IN_MONTH (year, month); + (*month_offset)--; + *day += days_in_month; + } + + g_print ("Out e_calendar_item_round_down_selection month:%i day:%i\n", + *month_offset, *day); } static gint -e_calendar_motion (GtkWidget *widget, - GdkEventMotion *event) +e_calendar_item_get_inclusive_days (ECalendarItem *calitem, + gint start_month_offset, + gint start_day, + gint end_month_offset, + gint end_day) { - ECalendar *cal; - gint x, y, day; + gint start_year, start_month, end_year, end_month, days = 0; + + start_year = calitem->year; + start_month = calitem->month + start_month_offset; + e_calendar_item_normalize_date (calitem, &start_year, &start_month); + + end_year = calitem->year; + end_month = calitem->month + end_month_offset; + e_calendar_item_normalize_date (calitem, &end_year, &end_month); + + while (start_year < end_year || start_month < end_month) { + days += DAYS_IN_MONTH (start_year, start_month); + start_month++; + if (start_month == 12) { + start_year++; + start_month = 0; + } + } - cal = E_CALENDAR (widget); + days += end_day - start_day + 1; - g_print ("In e_calendar_motion\n"); + return days; +} - x = event->x; - y = event->y; - if (event->is_hint || event->window != widget->window) - gtk_widget_get_pointer (widget, &x, &y); +/* If the day is off the end of the month it is set to the last day of the + month. */ +static void +e_calendar_item_ensure_valid_day (ECalendarItem *calitem, + gint *month_offset, + gint *day) +{ + gint year, month, days_in_month; - day = e_calendar_convert_position_to_day (cal, event->x, event->y); + year = calitem->year; + month = calitem->month + *month_offset; + e_calendar_item_normalize_date (calitem, &year, &month); - if (cal->selection_dragging_end) - cal->selection_end_day = day; - else - cal->selection_start_day = day; + days_in_month = DAYS_IN_MONTH (year, month); + if (*day > days_in_month) + *day = days_in_month; +} + + +gboolean +e_calendar_item_get_selection (ECalendarItem *calitem, + GDate *start_date, + GDate *end_date) +{ + gint start_year, start_month, start_day; + gint end_year, end_month, end_day; + + g_date_clear (start_date, 1); + g_date_clear (end_date, 1); + + if (calitem->selection_start_month_offset == -2) + return FALSE; + + start_year = calitem->year; + start_month = calitem->month + calitem->selection_start_month_offset; + e_calendar_item_normalize_date (calitem, &start_year, &start_month); + start_day = calitem->selection_start_day; + + end_year = calitem->year; + end_month = calitem->month + calitem->selection_end_month_offset; + e_calendar_item_normalize_date (calitem, &end_year, &end_month); + end_day = calitem->selection_end_day; + + g_date_set_dmy (start_date, start_day, start_month + 1, start_year); + g_date_set_dmy (end_date, end_day, end_month + 1, end_year); + + return TRUE; +} + + +void +e_calendar_item_set_selection (ECalendarItem *calitem, + GDate *start_date, + GDate *end_date) +{ + gint start_year, start_month, start_day; + gint end_year, end_month, end_day; + gint new_start_month_offset, new_start_day; + gint new_end_month_offset, new_end_day; + gboolean need_update; + + g_return_if_fail (E_IS_CALENDAR_ITEM (calitem)); + g_return_if_fail (g_date_compare (start_date, end_date) <= 0); + + start_year = g_date_year (start_date); + start_month = g_date_month (start_date) - 1; + start_day = g_date_day (start_date); + end_year = g_date_year (end_date); + end_month = g_date_month (end_date) - 1; + end_day = g_date_day (end_date); + + g_print ("In e_calendar_item_set_selection start: %i/%i/%i end: %i/%i/%i\n", start_day, start_month, start_year, end_day, end_month, end_year); + + need_update = e_calendar_item_ensure_days_visible (calitem, + start_year, + start_month, + start_day, + end_year, + end_month, + end_day); + + new_start_month_offset = (start_year - calitem->year) * 12 + + start_month - calitem->month; + new_start_day = start_day; + + /* This may go outside the visible months, but we don't care. */ + new_end_month_offset = (end_year - calitem->year) * 12 + + end_month - calitem->month; + new_end_day = end_day; + + + if (calitem->selection_start_month_offset != new_start_month_offset + || calitem->selection_start_day != new_start_day + || calitem->selection_end_month_offset != new_end_month_offset + || calitem->selection_end_day != new_end_day) { + need_update = TRUE; + calitem->selection_changed = TRUE; + e_calendar_item_queue_signal_emission (calitem); + calitem->selection_start_month_offset = new_start_month_offset; + calitem->selection_start_day = new_start_day; + calitem->selection_end_month_offset = new_end_month_offset; + calitem->selection_end_day = new_end_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); + if (need_update) + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (calitem)); +} + + +/* This tries to ensure that the given time range is visible. If the range + given is longer than we can show, only the start of it will be visible. + Note that this will not update the selection. That should be done somewhere + else. It returns TRUE if the visible range has been changed. */ +static gboolean +e_calendar_item_ensure_days_visible (ECalendarItem *calitem, + gint start_year, + gint start_month, + gint start_day, + gint end_year, + gint end_month, + gint end_day) +{ + gint current_end_year, current_end_month; + gint months_shown, months; + gint first_day_offset, days_in_month, days_in_prev_month; + gboolean need_update = FALSE; + + months_shown = calitem->rows * calitem->cols; + months = (end_year - start_year) * 12 + end_month - start_month; + + /* Calculate the range of months currently displayed. */ + current_end_year = calitem->year; + current_end_month = calitem->month + months_shown - 1; + e_calendar_item_normalize_date (calitem, ¤t_end_year, + ¤t_end_month); + + /* Try to ensure that the end month is shown. */ + if (end_year > current_end_year + || (end_year == current_end_year + && end_month > current_end_month)) { + need_update = TRUE; + calitem->year = end_year; + calitem->month = end_month - months_shown + 1; + e_calendar_item_normalize_date (calitem, &calitem->year, + &calitem->month); + } + + /* Now try to ensure that the start month is shown. We do this after + the end month so that the start month will always be shown. */ + if (start_year < calitem->year + || (start_year == calitem->year + && start_month < calitem->month)) { + need_update = TRUE; + + /* First we see if the start of the selection will fit in the + leftover days of the month before the first one shown. */ + calitem->year = start_year; + calitem->month = start_month + 1; + e_calendar_item_normalize_date (calitem, &calitem->year, + &calitem->month); + + e_calendar_item_get_month_info (calitem, 0, 0, + &first_day_offset, + &days_in_month, + &days_in_prev_month); + + if (start_day <= days_in_prev_month - first_day_offset) { + calitem->year = start_year; + calitem->month = start_month; + } + } + + if (need_update) + e_calendar_item_date_range_changed (calitem); + + return need_update; +} + + +static void +e_calendar_item_show_popup_menu (ECalendarItem *calitem, + GdkEventButton *event, + gint month_offset) +{ + GtkWidget *menu, *submenu, *menuitem; + gint year, month; + gchar buffer[64]; + struct tm tmp_tm; + + menu = gtk_menu_new (); + + for (year = calitem->year - 2; year <= calitem->year + 2; year++) { + g_snprintf (buffer, 64, "%i", year); + menuitem = gtk_menu_item_new_with_label (buffer); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); + + gtk_object_set_data (GTK_OBJECT (submenu), "year", + GINT_TO_POINTER (year)); + gtk_object_set_data (GTK_OBJECT (submenu), "month_offset", + GINT_TO_POINTER (month_offset)); + + for (month = 0; month < 12; month++) { + memset (&tmp_tm, 0, sizeof (tmp_tm)); + 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); + + menuitem = gtk_menu_item_new_with_label (buffer); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (submenu), menuitem); + + gtk_object_set_data (GTK_OBJECT (menuitem), "month", + GINT_TO_POINTER (month)); + + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (e_calendar_item_on_menu_item_activate), calitem); + } + } + + /* Run the menu modal so we can destroy it after. */ + gtk_signal_connect (GTK_OBJECT (menu), "deactivate", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + e_calendar_item_position_menu, calitem, + event->button, event->time); + gtk_grab_add (menu); + gtk_main (); + gtk_grab_remove (menu); + gtk_widget_destroy (menu); +} + +static void +e_calendar_item_on_menu_item_activate (GtkWidget *menuitem, + ECalendarItem *calitem) +{ + gint year, month_offset, month; + + year = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menuitem->parent), "year")); + month_offset = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menuitem->parent), "month_offset")); + month = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menuitem), "month")); + + month -= month_offset; + e_calendar_item_normalize_date (calitem, &year, &month); + e_calendar_item_set_first_month (calitem, year, month); +} + + +static void +e_calendar_item_position_menu (GtkMenu *menu, + gint *x, + gint *y, + gpointer user_data) +{ + GtkRequisition requisition; + gint screen_width, screen_height; + + gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition); + + *x -= 2; + *y -= requisition.height / 2; + + screen_width = gdk_screen_width (); + screen_height = gdk_screen_height (); + + *x = CLAMP (*x, 0, screen_width - requisition.width); + *y = CLAMP (*y, 0, screen_height - requisition.height); +} + + +/* Sets the function to call to get the colors to use for a particular day. */ +void +e_calendar_item_set_style_callback (ECalendarItem *calitem, + ECalendarItemStyleCallback cb, + gpointer data, + GtkDestroyNotify destroy) +{ + g_return_if_fail (E_IS_CALENDAR_ITEM (calitem)); + + if (calitem->style_callback_data) + (*calitem->style_callback_destroy) (calitem->style_callback_data); + + calitem->style_callback = cb; + calitem->style_callback_data = data; + calitem->style_callback_destroy = destroy; +} + + +static void +e_calendar_item_date_range_changed (ECalendarItem *calitem) +{ + g_free (calitem->styles); + calitem->styles = NULL; + calitem->date_range_changed = TRUE; + e_calendar_item_queue_signal_emission (calitem); +} + + +static void +e_calendar_item_queue_signal_emission (ECalendarItem *calitem) +{ + if (calitem->signal_emission_idle_id == 0) { + calitem->signal_emission_idle_id = g_idle_add_full (G_PRIORITY_HIGH, e_calendar_item_signal_emission_idle_cb, calitem, NULL); + } +} + + +static gboolean +e_calendar_item_signal_emission_idle_cb (gpointer data) +{ + ECalendarItem *calitem; + + g_return_val_if_fail (E_IS_CALENDAR_ITEM (data), FALSE); + + GDK_THREADS_ENTER (); + + calitem = E_CALENDAR_ITEM (data); + + calitem->signal_emission_idle_id = 0; + + if (calitem->date_range_changed) { + gtk_signal_emit (GTK_OBJECT (calitem), + e_calendar_item_signals[DATE_RANGE_CHANGED]); + calitem->date_range_changed = FALSE; + } + + if (calitem->selection_changed) { + gtk_signal_emit (GTK_OBJECT (calitem), + e_calendar_item_signals[SELECTION_CHANGED]); + calitem->selection_changed = FALSE; + } + + GDK_THREADS_LEAVE (); return FALSE; } -#endif + |