aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--widgets/misc/ChangeLog5
-rw-r--r--widgets/misc/e-calendar-item.c1778
-rw-r--r--widgets/misc/e-calendar-item.h179
-rw-r--r--widgets/misc/e-calendar.c490
-rw-r--r--widgets/misc/e-calendar.h29
-rw-r--r--widgets/misc/test-calendar.c146
6 files changed, 2151 insertions, 476 deletions
diff --git a/widgets/misc/ChangeLog b/widgets/misc/ChangeLog
index 279e7fc9bc..d47f4b6798 100644
--- a/widgets/misc/ChangeLog
+++ b/widgets/misc/ChangeLog
@@ -1,3 +1,8 @@
+2000-08-30 Damon Chaplin <damon@helixcode.com>
+
+ * e-calendar-item.[hc]:
+ * e-calendar.[hc]: Updated.
+
2000-08-10 Christopher James Lahey <clahey@helixcode.com>
* e-calendar-item.c, e-calendar.c: Fixed some warnings.
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, &current_end_year,
+ &current_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
+
diff --git a/widgets/misc/e-calendar-item.h b/widgets/misc/e-calendar-item.h
index a59ca6d407..3091e47bb4 100644
--- a/widgets/misc/e-calendar-item.h
+++ b/widgets/misc/e-calendar-item.h
@@ -34,18 +34,42 @@ extern "C" {
* ECalendarItem - canvas item displaying a calendar.
*/
-#define E_CALENDAR_ITEM_YPAD1 1
-#define E_CALENDAR_ITEM_YPAD2 1
+#define E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME 1
+#define E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME 1
+
+/* Used to mark days as bold in e_calendar_item_mark_day(). */
+#define E_CALENDAR_ITEM_MARK_BOLD 1
+
/* These index our colors array. */
typedef enum
{
- E_CALENDAR_COLOR_SELECTION,
- E_CALENDAR_COLOR_HIGHLIGHT,
- E_CALENDAR_COLOR_TODAY,
+ E_CALENDAR_ITEM_COLOR_TODAY_BOX,
+ E_CALENDAR_ITEM_COLOR_SELECTION_FG,
+ E_CALENDAR_ITEM_COLOR_SELECTION_BG,
+ E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG,
- E_CALENDAR_COLOR_LAST
-} ECalendarColors;
+ E_CALENDAR_ITEM_COLOR_LAST
+} ECalendarItemColors;
+
+typedef struct _ECalendarItem ECalendarItem;
+typedef struct _ECalendarItemClass ECalendarItemClass;
+
+typedef void (*ECalendarItemStyleCallback) (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ gint day_style,
+ gboolean today,
+ gboolean prev_or_next_month,
+ gboolean selected,
+ gboolean has_focus,
+ gboolean drop_target,
+ GdkColor **bg_color,
+ GdkColor **fg_color,
+ GdkColor **box_color,
+ gboolean *bold,
+ gpointer data);
#define E_CALENDAR_ITEM(obj) (GTK_CHECK_CAST((obj), \
@@ -55,23 +79,34 @@ typedef enum
#define E_IS_CALENDAR_ITEM(o) (GTK_CHECK_TYPE((o), \
e_calendar_item_get_type ()))
-typedef struct {
+struct _ECalendarItem
+{
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;
+ /* Points to an array of styles, one char for each day. We use 32
+ chars for each month, with n + 2 months, where n is the number of
+ complete months shown (since we show some days before the first
+ month and after the last month grayes out).
+ A value of 0 is the default, and 1 is bold. */
+ guint8 *styles;
+
+ /*
+ * Options.
+ */
- /* The minimum & maximum number of rows & columns of months. */
+ /* The minimum & maximum number of rows & columns of months.
+ If the maximum values are -1 then there is no maximum.
+ The minimum valies default to 1. The maximum values to -1. */
gint min_rows;
gint min_cols;
gint max_rows;
gint max_cols;
- /* The number of rows & columns of months. */
+ /* The actual number of rows & columns of months. */
gint rows;
gint cols;
@@ -84,6 +119,27 @@ typedef struct {
/* Whether the cells expand to fill extra space. */
gboolean expand;
+ /* The maximum number of days that can be selected. Defaults to 1. */
+ gint max_days_selected;
+
+ /* The number of days selected before we switch to selecting whole
+ weeks, or -1 if we never switch. Defaults to -1. */
+ gint days_to_start_week_selection;
+
+ /* Whether the selection is rounded down to the nearest week when we
+ move back/forward one month. Used for the week view. */
+ gboolean round_selection_when_moving;
+
+ /* The space to leave at the bottom for the Today & None buttons. */
+ gdouble buttons_space;
+
+ /*
+ * Internal stuff.
+ */
+
+ /* Bounds of item. */
+ gdouble x1, y1, x2, y2;
+
/* The minimum size of each month, based on the fonts used. */
gint min_month_width;
gint min_month_height;
@@ -103,16 +159,18 @@ typedef struct {
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;
+ top-left calendar month view. Note that -1 is used for the last days
+ from the previous month. The days are real month days. */
gboolean selecting;
gboolean selection_dragging_end;
+ gboolean selection_from_full_week;
+ gint selection_start_month_offset;
+ gint selection_start_day;
+ gint selection_end_month_offset;
+ gint selection_end_day;
+ gint selection_real_start_month_offset;
+ gint selection_real_start_day;
/* The first character of each day of the week, e.g. 'MTWTFSS'. */
gchar *days;
@@ -131,18 +189,87 @@ typedef struct {
GdkFont *font, *old_font;
GdkFont *week_number_font, *old_week_number_font;
- /* Colors for drawing. */
- GdkColor colors[E_CALENDAR_COLOR_LAST];
-} ECalendarItem;
+ ECalendarItemStyleCallback style_callback;
+ gpointer style_callback_data;
+ GtkDestroyNotify style_callback_destroy;
-typedef struct {
- GnomeCanvasItemClass parent_class;
+ /* Colors for drawing. */
+ GdkColor colors[E_CALENDAR_ITEM_COLOR_LAST];
-} ECalendarItemClass;
+ /* Our idle handler for emitting signals. */
+ gint signal_emission_idle_id;
+ /* A flag to indicate that the selection or date range has changed.
+ When set the idle function will emit the signal and reset it to
+ FALSE. This is so we don't emit it several times when args are set
+ etc. */
+ gboolean selection_changed;
+ gboolean date_range_changed;
+};
-GtkType e_calendar_item_get_type (void);
+struct _ECalendarItemClass
+{
+ GnomeCanvasItemClass parent_class;
+ void (* date_range_changed) (ECalendarItem *calitem);
+ void (* selection_changed) (ECalendarItem *calitem);
+};
+
+
+GtkType e_calendar_item_get_type (void);
+
+void e_calendar_item_get_first_month (ECalendarItem *calitem,
+ gint *year,
+ gint *month);
+void e_calendar_item_set_first_month (ECalendarItem *calitem,
+ gint year,
+ gint month);
+
+/* Returns the range of dates actually shown. Months are 0 to 11.
+ This also includes the last days of the previous month and the first days
+ of the following month, which are normally shown in gray. */
+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);
+
+gboolean e_calendar_item_get_selection (ECalendarItem *calitem,
+ GDate *start_date,
+ GDate *end_date);
+void e_calendar_item_set_selection (ECalendarItem *calitem,
+ GDate *start_date,
+ GDate *end_date);
+
+/* Marks a particular day. Passing E_CALENDAR_ITEM_MARK_BOLD as the day style
+ will result in the day being shown as bold by default. The style callback
+ could support more day_styles, or the style callback could determine the
+ colors itself, without needing to mark days. */
+void e_calendar_item_clear_marks (ECalendarItem *calitem);
+void e_calendar_item_mark_day (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ guint8 day_style);
+
+/* Mark a range of days. Any days outside the currently shown range are
+ ignored. */
+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);
+
+/* 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);
#ifdef __cplusplus
}
diff --git a/widgets/misc/e-calendar.c b/widgets/misc/e-calendar.c
index ec85e3471f..33a6874562 100644
--- a/widgets/misc/e-calendar.c
+++ b/widgets/misc/e-calendar.c
@@ -39,8 +39,24 @@
#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
+/* The space between the arrow buttons and the edge of the widget. */
+#define E_CALENDAR_ARROW_BUTTON_X_PAD 2
+#define E_CALENDAR_ARROW_BUTTON_Y_PAD 0
+
+/* Vertical padding. The padding above the button includes the space for the
+ horizontal line. */
+#define E_CALENDAR_YPAD_ABOVE_LOWER_BUTTONS 4
+#define E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS 3
+
+/* Horizontal padding inside & between buttons. */
+#define E_CALENDAR_IXPAD_BUTTONS 4
+#define E_CALENDAR_XPAD_BUTTONS 8
+
+/* The time between steps when the prev/next buttons is pressed, in 1/1000ths
+ of a second, and the number of timeouts we skip before we start
+ automatically moving back/forward. */
+#define E_CALENDAR_AUTO_MOVE_TIMEOUT 150
+#define E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY 2
static void e_calendar_class_init (ECalendarClass *class);
static void e_calendar_init (ECalendar *cal);
@@ -52,28 +68,32 @@ 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);
-#if 0
-static gint e_calendar_button_press (GtkWidget *widget,
- GdkEventButton *event);
-static gint e_calendar_button_release (GtkWidget *widget,
- GdkEventButton *event);
-#endif
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 gint e_calendar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static void e_calendar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time);
+
+static void e_calendar_on_prev_pressed (ECalendar *cal);
+static void e_calendar_on_prev_released (ECalendar *cal);
+static void e_calendar_on_next_pressed (ECalendar *cal);
+static void e_calendar_on_next_released (ECalendar *cal);
+
+static void e_calendar_start_auto_move (ECalendar *cal,
+ gboolean moving_forward);
+static gboolean e_calendar_auto_move_handler (gpointer data);
+static void e_calendar_stop_auto_move (ECalendar *cal);
static GnomeCanvasClass *parent_class;
static GtkLayoutClass *grandparent_class;
@@ -100,15 +120,12 @@ e_calendar_class_init (ECalendarClass *class)
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;
+ widget_class->drag_motion = e_calendar_drag_motion;
+ widget_class->drag_leave = e_calendar_drag_leave;
}
@@ -131,25 +148,39 @@ e_calendar_init (ECalendar *cal)
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);
+ /* Create the 'Today' and 'None' buttons but don't show them. */
+ cal->today_button = gtk_button_new_with_label (_("Today"));
+ cal->today_button_item = gnome_canvas_item_new (canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", cal->today_button,
+ NULL);
+ gnome_canvas_item_hide (cal->today_button_item);
+
+
+ cal->none_button = gtk_button_new_with_label (_("None"));
+ cal->none_button_item = gnome_canvas_item_new (canvas_group,
+ gnome_canvas_widget_get_type (),
+ "widget", cal->none_button,
+ NULL);
+ gnome_canvas_item_hide (cal->none_button_item);
+
+
+ /* Create the arrow buttons to move to the previous/next month. */
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_signal_connect_object (GTK_OBJECT (button), "pressed",
+ GTK_SIGNAL_FUNC (e_calendar_on_prev_pressed),
+ GTK_OBJECT (cal));
+ gtk_signal_connect_object (GTK_OBJECT (button), "released",
+ GTK_SIGNAL_FUNC (e_calendar_on_prev_released),
GTK_OBJECT (cal));
arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
@@ -162,8 +193,11 @@ e_calendar_init (ECalendar *cal)
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_signal_connect_object (GTK_OBJECT (button), "pressed",
+ GTK_SIGNAL_FUNC (e_calendar_on_next_pressed),
+ GTK_OBJECT (cal));
+ gtk_signal_connect_object (GTK_OBJECT (button), "released",
+ GTK_SIGNAL_FUNC (e_calendar_on_next_released),
GTK_OBJECT (cal));
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
gtk_widget_show (arrow);
@@ -177,6 +211,8 @@ e_calendar_init (ECalendar *cal)
cal->min_cols = 1;
cal->max_rows = -1;
cal->max_cols = -1;
+
+ cal->timeout_id = 0;
}
@@ -207,8 +243,10 @@ e_calendar_destroy (GtkObject *object)
cal = E_CALENDAR (object);
-
-
+ if (cal->timeout_id != 0) {
+ gtk_timeout_remove (cal->timeout_id);
+ cal->timeout_id = 0;
+ }
}
@@ -223,6 +261,10 @@ static void
e_calendar_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget,
+ previous_style);
}
@@ -232,8 +274,13 @@ e_calendar_size_request (GtkWidget *widget,
{
ECalendar *cal;
GtkStyle *style;
- gdouble col_width, row_height;
- gint width, height;
+ gint col_width, row_height, width, height;
+ gboolean today_button_shown, none_button_shown;
+ gint buttons_shown = 0, button_height, button_width, buttons_width;
+ GtkRequisition today_button_requisition = { 0 };
+ GtkRequisition none_button_requisition = { 0 };
+ gint today_button_width = 0, today_button_height = 0;
+ gint none_button_width = 0, none_button_height = 0;
cal = E_CALENDAR (widget);
style = GTK_WIDGET (cal)->style;
@@ -246,7 +293,39 @@ e_calendar_size_request (GtkWidget *widget,
height = row_height * cal->min_rows;
width = col_width * cal->min_cols;
- /* FIXME: Add on space for line & button if shown. */
+ /* Add on space for line & button if shown. */
+ today_button_shown = GTK_WIDGET_VISIBLE (cal->today_button);
+ none_button_shown = GTK_WIDGET_VISIBLE (cal->none_button);
+
+ if (today_button_shown || none_button_shown) {
+ /* Note that we use the buttons' requisition fields directly
+ since we want the sizes without any usize modification. */
+ if (today_button_shown) {
+ gtk_widget_size_request (cal->today_button,
+ &today_button_requisition);
+ today_button_width = cal->today_button->requisition.width;
+ today_button_height = cal->today_button->requisition.height;
+ buttons_shown++;
+ }
+ if (none_button_shown) {
+ gtk_widget_size_request (cal->none_button,
+ &none_button_requisition);
+ none_button_width = cal->none_button->requisition.width;
+ none_button_height = cal->none_button->requisition.height;
+ buttons_shown++;
+ }
+
+ button_height = MAX (today_button_height, none_button_height);
+ height += E_CALENDAR_YPAD_ABOVE_LOWER_BUTTONS + button_height
+ + E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS;
+
+ button_width = MAX (today_button_width, none_button_width);
+ button_width += E_CALENDAR_IXPAD_BUTTONS;
+ buttons_width = buttons_shown * button_width
+ + (buttons_shown - 1) * E_CALENDAR_XPAD_BUTTONS;
+
+ width = MAX (width, buttons_width);
+ }
requisition->width = width + style->klass->xthickness * 2;
requisition->height = height + style->klass->ythickness * 2;
@@ -260,7 +339,16 @@ e_calendar_size_allocate (GtkWidget *widget,
ECalendar *cal;
GdkFont *font;
gdouble old_x2, old_y2, new_x2, new_y2;
- gdouble xthickness, ythickness, button_size;
+ gdouble buttons_x1, buttons_y1;
+ gdouble button_width, button_height;
+ gdouble buttons_width = 0, buttons_height = 0;
+ gdouble xthickness, ythickness, arrow_button_size;
+ gboolean today_button_shown, none_button_shown;
+ gint buttons_shown = 0;
+ GtkRequisition today_button_requisition = { 0 };
+ GtkRequisition none_button_requisition = { 0 };
+ gint today_button_width = 0, today_button_height = 0;
+ gint none_button_width = 0, none_button_height = 0;
cal = E_CALENDAR (widget);
font = widget->style->font;
@@ -278,50 +366,97 @@ e_calendar_size_allocate (GtkWidget *widget,
gnome_canvas_set_scroll_region (GNOME_CANVAS (cal),
0, 0, new_x2, new_y2);
- /* FIXME: Take off space for line & buttons if shown. */
+ /* Set the positions of the Today & None buttons if shown. */
+ today_button_shown = GTK_WIDGET_VISIBLE (cal->today_button);
+ none_button_shown = GTK_WIDGET_VISIBLE (cal->none_button);
+
+ if (today_button_shown || none_button_shown) {
+ if (today_button_shown) {
+ gtk_widget_size_request (cal->today_button,
+ &today_button_requisition);
+ today_button_width = cal->today_button->requisition.width;
+ today_button_height = cal->today_button->requisition.height;
+ buttons_shown++;
+ }
+ if (none_button_shown) {
+ gtk_widget_size_request (cal->none_button,
+ &none_button_requisition);
+ none_button_width = cal->none_button->requisition.width;
+ none_button_height = cal->none_button->requisition.height;
+ buttons_shown++;
+ }
+
+ button_height = MAX (today_button_height, none_button_height);
+ buttons_height = E_CALENDAR_YPAD_ABOVE_LOWER_BUTTONS
+ + button_height + E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS;
+
+ button_width = MAX (today_button_width, none_button_width);
+ button_width += E_CALENDAR_IXPAD_BUTTONS;
+ buttons_width = buttons_shown * button_width
+ + (buttons_shown - 1) * E_CALENDAR_XPAD_BUTTONS;
+
+ buttons_x1 = (new_x2 + 1 - buttons_width) / 2;
+ buttons_x1 = MAX (0, buttons_x1);
+
+ buttons_y1 = new_y2 + 1 - E_CALENDAR_YPAD_BELOW_LOWER_BUTTONS
+ - button_height - ythickness;
+ buttons_y1 = MAX (0, buttons_y1);
+
+ gnome_canvas_item_set (cal->today_button_item,
+ "x", buttons_x1,
+ "y", buttons_y1,
+ "width", (gdouble) button_width,
+ "height", (gdouble) button_height,
+ NULL);
+
+ if (today_button_shown)
+ buttons_x1 += button_width + E_CALENDAR_XPAD_BUTTONS;
+
+ gnome_canvas_item_set (cal->none_button_item,
+ "x", buttons_x1,
+ "y", buttons_y1,
+ "width", (gdouble) button_width,
+ "height", (gdouble) button_height,
+ NULL);
+ }
+
+ /* 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,
+ "buttons_space", buttons_height,
NULL);
- button_size = font->ascent + font->descent + E_CALENDAR_ITEM_YPAD1
- + E_CALENDAR_ITEM_YPAD2 - E_CALENDAR_BUTTON_Y_PAD * 2;
+
+ /* Position the arrow buttons. */
+ arrow_button_size = font->ascent + font->descent
+ + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME
+ + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME
+ - E_CALENDAR_ARROW_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,
+ "x", xthickness * 2
+ + E_CALENDAR_ARROW_BUTTON_X_PAD,
+ "y", ythickness * 2
+ + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_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,
+ - E_CALENDAR_ARROW_BUTTON_X_PAD
+ - arrow_button_size,
+ "y", ythickness * 2
+ + E_CALENDAR_ARROW_BUTTON_Y_PAD,
+ "width", arrow_button_size,
+ "height", arrow_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)
@@ -332,51 +467,13 @@ e_calendar_draw (GtkWidget *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)
-{
+ /* GnomeCanvas bug workaround to draw the GnomeCanvasWidgets. */
#if 0
- g_print ("In e_calendar_paint: %i,%i %ix%i\n", area->x, area->y,
- area->width, area->height);
+ (*GTK_WIDGET_CLASS (grandparent_class)->draw) (widget, area);
#endif
}
-/* FIXME These aren't used? */
-
-#if 0
-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;
-}
-#endif
-
-
static gint
e_calendar_focus_in (GtkWidget *widget,
GdkEventFocus *event)
@@ -439,48 +536,175 @@ e_calendar_set_maximum_size (ECalendar *cal,
}
+/* Returns the border size on each side of the month displays. */
+void
+e_calendar_get_border_size (ECalendar *cal,
+ gint *top,
+ gint *bottom,
+ gint *left,
+ gint *right)
+{
+ GtkStyle *style;
+
+ g_return_if_fail (E_IS_CALENDAR (cal));
+
+ style = GTK_WIDGET (cal)->style;
+
+ if (style) {
+ *top = style->klass->ythickness;
+ *bottom = style->klass->ythickness
+ + cal->calitem->buttons_space;
+ *left = style->klass->xthickness;
+ *right = style->klass->xthickness;
+ } else {
+ *top = *bottom = *left = *right = 0;
+ }
+}
+
+
static void
-e_calendar_on_prev_clicked (ECalendar *cal)
+e_calendar_on_prev_pressed (ECalendar *cal)
{
- gint year, month;
+ e_calendar_start_auto_move (cal, FALSE);
+}
- gtk_object_get (GTK_OBJECT (cal->calitem),
- "year", &year,
- "month", &month,
- NULL);
- month--;
- if (month == -1) {
- year--;
- month = 11;
+static void
+e_calendar_on_next_pressed (ECalendar *cal)
+{
+ e_calendar_start_auto_move (cal, TRUE);
+}
+
+
+static void
+e_calendar_start_auto_move (ECalendar *cal,
+ gboolean moving_forward)
+{
+ ECalendarItem *calitem;
+ gint offset;
+
+ if (cal->timeout_id == 0) {
+ cal->timeout_id = g_timeout_add (E_CALENDAR_AUTO_MOVE_TIMEOUT,
+ e_calendar_auto_move_handler,
+ cal);
}
+ cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY;
+ cal->moving_forward = moving_forward;
- gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
- "year", year,
- "month", month,
- NULL);
+ calitem = cal->calitem;
+ offset = cal->moving_forward ? 1 : -1;
+ e_calendar_item_set_first_month (calitem, calitem->year,
+ calitem->month + offset);
+}
+
+
+static gboolean
+e_calendar_auto_move_handler (gpointer data)
+{
+ ECalendar *cal;
+ ECalendarItem *calitem;
+ gint offset;
+
+ g_return_val_if_fail (E_IS_CALENDAR (data), FALSE);
+
+ cal = E_CALENDAR (data);
+ calitem = cal->calitem;
+
+ GDK_THREADS_ENTER ();
+
+ if (cal->timeout_delay > 0) {
+ cal->timeout_delay--;
+ } else {
+ offset = cal->moving_forward ? 1 : -1;
+ e_calendar_item_set_first_month (calitem, calitem->year,
+ calitem->month + offset);
+ }
+
+ GDK_THREADS_LEAVE ();
+ return TRUE;
}
static void
-e_calendar_on_next_clicked (ECalendar *cal)
+e_calendar_on_prev_released (ECalendar *cal)
{
- gint year, month;
+ e_calendar_stop_auto_move (cal);
+}
- gtk_object_get (GTK_OBJECT (cal->calitem),
- "year", &year,
- "month", &month,
- NULL);
- month++;
- if (month == 12) {
- year++;
- month = 0;
+static void
+e_calendar_on_next_released (ECalendar *cal)
+{
+ e_calendar_stop_auto_move (cal);
+}
+
+
+static void
+e_calendar_stop_auto_move (ECalendar *cal)
+{
+ if (cal->timeout_id != 0) {
+ gtk_timeout_remove (cal->timeout_id);
+ cal->timeout_id = 0;
}
+}
- gnome_canvas_item_set (GNOME_CANVAS_ITEM (cal->calitem),
- "year", year,
- "month", month,
- NULL);
+
+/* Set which, if any, of the buttons to show beneath the month displays.
+ You must connect to the button's clicked signal and perform the
+ required action yourself. */
+void
+e_calendar_set_buttons (ECalendar *cal,
+ gboolean show_today_button,
+ gboolean show_none_button)
+{
+ if (show_today_button) {
+ gtk_widget_show (cal->today_button);
+ gnome_canvas_item_show (cal->today_button_item);
+ } else {
+ gtk_widget_hide (cal->today_button);
+ gnome_canvas_item_hide (cal->today_button_item);
+ }
+
+ if (show_none_button) {
+ gtk_widget_show (cal->none_button);
+ gnome_canvas_item_show (cal->none_button_item);
+ } else {
+ gtk_widget_hide (cal->none_button);
+ gnome_canvas_item_hide (cal->none_button_item);
+ }
+}
+
+
+static gint
+e_calendar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ ECalendar *cal;
+
+ g_return_val_if_fail (E_IS_CALENDAR (widget), FALSE);
+
+ cal = E_CALENDAR (widget);
+
+ g_print ("In e_calendar_drag_motion\n");
+
+ return FALSE;
+}
+
+
+static void
+e_calendar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ ECalendar *cal;
+
+ g_return_if_fail (E_IS_CALENDAR (widget));
+
+ cal = E_CALENDAR (widget);
+
+ g_print ("In e_calendar_drag_leave\n");
}
diff --git a/widgets/misc/e-calendar.h b/widgets/misc/e-calendar.h
index b0e9c1f985..3c849cc0a1 100644
--- a/widgets/misc/e-calendar.h
+++ b/widgets/misc/e-calendar.h
@@ -54,13 +54,27 @@ struct _ECalendar
ECalendarItem *calitem;
- GnomeCanvasItem *prev_item, *next_item;
+ GtkWidget *today_button;
+ GtkWidget *none_button;
+
+ GnomeCanvasItem *today_button_item;
+ GnomeCanvasItem *none_button_item;
+
+ GnomeCanvasItem *prev_item;
+ GnomeCanvasItem *next_item;
gint min_rows;
gint min_cols;
gint max_rows;
gint max_cols;
+
+ /* These are all used when the prev/next buttons are held down.
+ moving_forward is TRUE if we are moving forward in time, i.e. the
+ next button is pressed. */
+ gint timeout_id;
+ gint timeout_delay;
+ gboolean moving_forward;
};
struct _ECalendarClass
@@ -79,6 +93,19 @@ void e_calendar_set_maximum_size (ECalendar *cal,
gint rows,
gint cols);
+/* Returns the border size on each side of the month displays. */
+void e_calendar_get_border_size (ECalendar *cal,
+ gint *top,
+ gint *bottom,
+ gint *left,
+ gint *right);
+
+/* Set which, if any, of the buttons to show beneath the month displays.
+ You must connect to the button's clicked signal and perform the
+ required action yourself. */
+void e_calendar_set_buttons (ECalendar *cal,
+ gboolean show_today_button,
+ gboolean show_none_button);
#ifdef __cplusplus
}
diff --git a/widgets/misc/test-calendar.c b/widgets/misc/test-calendar.c
index 6463a21ce3..28656e216e 100644
--- a/widgets/misc/test-calendar.c
+++ b/widgets/misc/test-calendar.c
@@ -34,6 +34,34 @@
#include "e-calendar.h"
+/* Drag and Drop stuff. */
+enum {
+ TARGET_SHORTCUT
+};
+static GtkTargetEntry target_table[] = {
+ { "E-SHORTCUT", 0, TARGET_SHORTCUT }
+};
+static guint n_targets = sizeof(target_table) / sizeof(target_table[0]);
+
+
+static void on_date_range_changed (ECalendarItem *calitem);
+static void on_selection_changed (ECalendarItem *calitem);
+static void get_day_style (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ gint day_style,
+ gboolean today,
+ gboolean prev_or_next_month,
+ gboolean selected,
+ gboolean has_focus,
+ gboolean drop_target,
+ GdkColor **bg_color,
+ GdkColor **fg_color,
+ GdkColor **box_color,
+ gboolean *bold,
+ gpointer data);
+
static void
delete_event_cb (GtkWidget *widget,
GdkEventAny *event,
@@ -48,18 +76,39 @@ main (int argc, char **argv)
GtkWidget *app;
GtkWidget *cal;
GtkWidget *vbox;
+ ECalendarItem *calitem;
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_container_set_border_width (GTK_CONTAINER (app), 8);
gtk_signal_connect (GTK_OBJECT (app), "delete_event",
GTK_SIGNAL_FUNC (delete_event_cb), NULL);
cal = e_calendar_new ();
+#if 1
+ e_calendar_set_buttons (E_CALENDAR (cal), TRUE, TRUE);
+#endif
+ e_calendar_set_minimum_size (E_CALENDAR (cal), 1, 1);
+ calitem = E_CALENDAR (cal)->calitem;
gtk_widget_show (cal);
+ e_calendar_item_set_style_callback (calitem, get_day_style,
+ NULL, NULL);
+
+ gtk_signal_connect (GTK_OBJECT (calitem), "date_range_changed",
+ GTK_SIGNAL_FUNC (on_date_range_changed), NULL);
+ gtk_signal_connect (GTK_OBJECT (calitem), "selection_changed",
+ GTK_SIGNAL_FUNC (on_selection_changed), NULL);
+
+
+ gtk_drag_dest_set (cal,
+ GTK_DEST_DEFAULT_ALL,
+ target_table, n_targets,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
vbox = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), cal, TRUE, TRUE, 0);
@@ -72,3 +121,100 @@ main (int argc, char **argv)
return 0;
}
+
+
+static void
+on_date_range_changed (ECalendarItem *calitem)
+{
+ gint start_year, start_month, start_day;
+ gint end_year, end_month, end_day;
+
+ e_calendar_item_get_date_range (calitem,
+ &start_year, &start_month, &start_day,
+ &end_year, &end_month, &end_day);
+
+ g_print ("Date range changed (D/M/Y): %i/%i/%i - %i/%i/%i\n",
+ start_day, start_month + 1, start_year,
+ end_day, end_month + 1, end_year);
+
+ /* These days should appear bold. Remember month is 0 to 11. */
+ e_calendar_item_mark_day (calitem, 2000, 7, 26, /* 26th Aug 2000. */
+ E_CALENDAR_ITEM_MARK_BOLD);
+ e_calendar_item_mark_day (calitem, 2000, 8, 13, /* 13th Sep 2000. */
+ E_CALENDAR_ITEM_MARK_BOLD);
+}
+
+
+static void
+on_selection_changed (ECalendarItem *calitem)
+{
+ GDate start_date, end_date;
+
+ e_calendar_item_get_selection (calitem, &start_date, &end_date);
+
+ g_print ("Selection changed (D/M/Y): %i/%i/%i - %i/%i/%i\n",
+ g_date_day (&start_date),
+ g_date_month (&start_date),
+ g_date_year (&start_date),
+ g_date_day (&end_date),
+ g_date_month (&end_date),
+ g_date_year (&end_date));
+}
+
+
+static void
+get_day_style (ECalendarItem *calitem,
+ gint year,
+ gint month,
+ gint day,
+ gint day_style,
+ gboolean today,
+ gboolean prev_or_next_month,
+ gboolean selected,
+ gboolean has_focus,
+ gboolean drop_target,
+ GdkColor **bg_color,
+ GdkColor **fg_color,
+ GdkColor **box_color,
+ gboolean *bold,
+ gpointer data)
+{
+ *bg_color = NULL;
+ *fg_color = NULL;
+ *box_color = NULL;
+ *bold = FALSE;
+
+#if 1
+
+ if (day_style == 1)
+ *bold = TRUE;
+
+ if (today)
+ *box_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX];
+
+ 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_ITEM_COLOR_SELECTION_FG];
+ *bg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_BG];
+ }
+
+#else
+
+ if (day_style == 1)
+ *bold = TRUE;
+
+ if (today)
+ *box_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG];
+
+ if (prev_or_next_month)
+ *fg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_TODAY_BOX];
+
+ if (selected) {
+ *fg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_SELECTION_FG];
+ *bg_color = &calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG];
+ }
+
+#endif
+}