aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/ChangeLog4
-rw-r--r--calendar/gui/e-day-view-main-item.c631
-rw-r--r--calendar/gui/e-day-view-main-item.h65
-rw-r--r--calendar/gui/e-day-view-time-item.c312
-rw-r--r--calendar/gui/e-day-view-time-item.h71
-rw-r--r--calendar/gui/e-day-view-top-item.c553
-rw-r--r--calendar/gui/e-day-view-top-item.h65
-rw-r--r--calendar/gui/e-day-view.c4585
-rw-r--r--calendar/gui/e-day-view.h463
9 files changed, 6749 insertions, 0 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index 01c67ce777..9f73875d9b 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,3 +1,7 @@
+2000-03-13 Damon Chaplin <damon@helixcode.com>
+
+ * gui/e-day-view*.[hc]: new files for the Day/Work-Week views.
+
2000-03-12 Federico Mena Quintero <federico@helixcode.com>
* gui/main.c (gnome_calendar_locate): Removed function now that it
diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c
new file mode 100644
index 0000000000..5d002b5945
--- /dev/null
+++ b/calendar/gui/e-day-view-main-item.c
@@ -0,0 +1,631 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * EDayViewMainItem - canvas item which displays most of the appointment
+ * data in the main Day/Work Week display.
+ */
+
+#include "e-day-view-main-item.h"
+
+static void e_day_view_main_item_class_init (EDayViewMainItemClass *class);
+static void e_day_view_main_item_init (EDayViewMainItem *dvtitem);
+
+static void e_day_view_main_item_set_arg (GtkObject *o, GtkArg *arg,
+ guint arg_id);
+static void e_day_view_main_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path, int flags);
+static void e_day_view_main_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height);
+static double e_day_view_main_item_point (GnomeCanvasItem *item,
+ double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static gint e_day_view_main_item_event (GnomeCanvasItem *item,
+ GdkEvent *event);
+
+static void e_day_view_main_item_draw_long_events_in_vbars (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_day_view_main_item_draw_events_in_vbars (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height,
+ gint day);
+static void e_day_view_main_item_draw_day_events (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height,
+ gint day);
+static void e_day_view_main_item_draw_day_event (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height,
+ gint day, gint event_num);
+
+static GnomeCanvasItemClass *parent_class;
+
+/* The arguments we take */
+enum {
+ ARG_0,
+ ARG_DAY_VIEW
+};
+
+
+GtkType
+e_day_view_main_item_get_type (void)
+{
+ static GtkType e_day_view_main_item_type = 0;
+
+ if (!e_day_view_main_item_type) {
+ GtkTypeInfo e_day_view_main_item_info = {
+ "EDayViewMainItem",
+ sizeof (EDayViewMainItem),
+ sizeof (EDayViewMainItemClass),
+ (GtkClassInitFunc) e_day_view_main_item_class_init,
+ (GtkObjectInitFunc) e_day_view_main_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_day_view_main_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_day_view_main_item_info);
+ }
+
+ return e_day_view_main_item_type;
+}
+
+
+static void
+e_day_view_main_item_class_init (EDayViewMainItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ parent_class = gtk_type_class (gnome_canvas_item_get_type());
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ gtk_object_add_arg_type ("EDayViewMainItem::day_view",
+ GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
+ ARG_DAY_VIEW);
+
+ object_class->set_arg = e_day_view_main_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_day_view_main_item_update;
+ item_class->draw = e_day_view_main_item_draw;
+ item_class->point = e_day_view_main_item_point;
+ item_class->event = e_day_view_main_item_event;
+}
+
+
+static void
+e_day_view_main_item_init (EDayViewMainItem *dvtitem)
+{
+ dvtitem->day_view = NULL;
+}
+
+
+static void
+e_day_view_main_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EDayViewMainItem *dvmitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ dvmitem = E_DAY_VIEW_MAIN_ITEM (o);
+
+ switch (arg_id){
+ case ARG_DAY_VIEW:
+ dvmitem->day_view = GTK_VALUE_POINTER (*arg);
+ break;
+ }
+}
+
+
+static void
+e_day_view_main_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags)
+{
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);
+
+ /* The item covers the entire canvas area. */
+ item->x1 = 0;
+ item->y1 = 0;
+ item->x2 = INT_MAX;
+ item->y2 = INT_MAX;
+}
+
+
+/*
+ * DRAWING ROUTINES - functions to paint the canvas item.
+ */
+
+static void
+e_day_view_main_item_draw (GnomeCanvasItem *canvas_item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ EDayViewMainItem *dvmitem;
+ EDayView *day_view;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc, *light_gc, *dark_gc, *gc;
+ GdkFont *font;
+ gint row, row_y, grid_x1, grid_x2;
+ gint day, grid_y1, grid_y2;
+ gint work_day_start_row, work_day_end_row;
+ gint work_day_start_y, work_day_end_y;
+ gint work_day_x, work_day_w;
+ gint start_row, end_row, rect_x, rect_y, rect_width, rect_height;
+
+#if 0
+ g_print ("In e_day_view_main_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+ dvmitem = E_DAY_VIEW_MAIN_ITEM (canvas_item);
+ day_view = dvmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ style = GTK_WIDGET (day_view)->style;
+ font = style->font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+ light_gc = style->light_gc[GTK_STATE_NORMAL];
+ dark_gc = style->dark_gc[GTK_STATE_NORMAL];
+
+ /* Paint the background colors. */
+ gc = day_view->main_gc;
+ work_day_start_row = e_day_view_convert_time_to_row (day_view, day_view->work_day_start_hour, day_view->work_day_start_minute);
+ work_day_start_y = work_day_start_row * day_view->row_height - y;
+ work_day_end_row = e_day_view_convert_time_to_row (day_view, day_view->work_day_end_hour, day_view->work_day_end_minute);
+ work_day_end_y = work_day_end_row * day_view->row_height - y;
+
+ work_day_x = day_view->day_offsets[0] - x;
+ work_day_w = width - work_day_x;
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]);
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ work_day_x, 0 - y,
+ work_day_w, work_day_start_y - (0 - y));
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING]);
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ work_day_x, work_day_start_y,
+ work_day_w, work_day_end_y - work_day_start_y);
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING]);
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ work_day_x, work_day_end_y,
+ work_day_w, height - work_day_end_y);
+
+ /* Paint the selection background. */
+ if (day_view->selection_start_col != -1
+ && !day_view->selection_in_top_canvas) {
+ for (day = day_view->selection_start_col;
+ day <= day_view->selection_end_col;
+ day++) {
+ if (day == day_view->selection_start_col
+ && day_view->selection_start_row != -1)
+ start_row = day_view->selection_start_row;
+ else
+ start_row = 0;
+ if (day == day_view->selection_end_col
+ && day_view->selection_end_row != -1)
+ end_row = day_view->selection_end_row;
+ else
+ end_row = day_view->rows - 1;
+
+ rect_x = day_view->day_offsets[day] - x;
+ rect_width = day_view->day_widths[day];
+ rect_y = start_row * day_view->row_height - y;
+ rect_height = (end_row - start_row + 1) * day_view->row_height;
+
+ gc = style->bg_gc[GTK_STATE_SELECTED];
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ rect_x, rect_y,
+ rect_width, rect_height);
+ }
+ }
+
+ /* Drawing the horizontal grid lines. */
+ grid_x1 = day_view->day_offsets[0] - x;
+ grid_x2 = day_view->day_offsets[day_view->days_shown] - x;
+
+ for (row = 0, row_y = 0 - y;
+ row < day_view->rows && row_y < height;
+ row++, row_y += day_view->row_height) {
+ if (row_y >= 0 && row_y < height)
+ gdk_draw_line (drawable, dark_gc,
+ grid_x1, row_y, grid_x2, row_y);
+ }
+
+ /* Draw the vertical bars down the left of each column. */
+ grid_y1 = 0;
+ grid_y2 = height;
+ for (day = 0; day < day_view->days_shown; day++) {
+ grid_x1 = day_view->day_offsets[day] - x;
+
+ /* Skip if it isn't visible. */
+ if (grid_x1 >= width || grid_x1 + E_DAY_VIEW_BAR_WIDTH <= 0)
+ continue;
+
+ gdk_draw_line (drawable, fg_gc,
+ grid_x1, grid_y1,
+ grid_x1, grid_y2);
+ gdk_draw_line (drawable, fg_gc,
+ grid_x1 + E_DAY_VIEW_BAR_WIDTH - 1, grid_y1,
+ grid_x1 + E_DAY_VIEW_BAR_WIDTH - 1, grid_y2);
+ gdk_draw_rectangle (drawable, style->white_gc, TRUE,
+ grid_x1 + 1, grid_y1,
+ E_DAY_VIEW_BAR_WIDTH - 2, grid_y2 - grid_y1);
+
+ /* Fill in the bars when the user is busy. */
+ e_day_view_main_item_draw_events_in_vbars (dvmitem, drawable,
+ x, y,
+ width, height,
+ day);
+ }
+
+ /* Fill in the vertical bars corresponding to the busy times from the
+ long events. */
+ e_day_view_main_item_draw_long_events_in_vbars (dvmitem, drawable,
+ x, y, width, height);
+
+ /* Draw the event borders and backgrounds, and the vertical bars
+ down the left edges. */
+ for (day = 0; day < day_view->days_shown; day++) {
+ e_day_view_main_item_draw_day_events (dvmitem, drawable,
+ x, y, width, height,
+ day);
+ }
+}
+
+
+static void
+e_day_view_main_item_draw_events_in_vbars (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height,
+ gint day)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ GdkGC *gc;
+ gint grid_x, event_num, bar_y, bar_h;
+
+ day_view = dvmitem->day_view;
+
+ gc = day_view->main_gc;
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]);
+
+ grid_x = day_view->day_offsets[day] + 1 - x;
+
+ /* Draw the busy times corresponding to the events in the day. */
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* We can skip the events in the first column since they will
+ draw over this anyway. */
+ if (event->num_columns > 0 && event->start_row_or_col == 0)
+ continue;
+
+ bar_y = event->start_minute * day_view->row_height / day_view->mins_per_row;
+ bar_h = event->end_minute * day_view->row_height / day_view->mins_per_row - bar_y;
+ bar_y -= y;
+
+ /* Skip it if it isn't visible. */
+ if (bar_y >= height || bar_y + bar_h <= 0)
+ continue;
+
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ grid_x, bar_y,
+ E_DAY_VIEW_BAR_WIDTH - 2, bar_h);
+ }
+}
+
+
+static void
+e_day_view_main_item_draw_long_events_in_vbars (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ gint event_num, start_day, end_day, day, bar_y1, bar_y2, grid_x;
+ GdkGC *gc;
+
+ day_view = dvmitem->day_view;
+
+ gc = day_view->main_gc;
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]);
+
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ if (!e_day_view_find_long_event_days (day_view, event,
+ &start_day, &end_day))
+ continue;
+
+ for (day = start_day; day <= end_day; day++) {
+ grid_x = day_view->day_offsets[day] + 1 - x;
+
+ /* Skip if it isn't visible. */
+ if (grid_x >= width
+ || grid_x + E_DAY_VIEW_BAR_WIDTH <= 0)
+ continue;
+
+ if (event->start <= day_view->day_starts[day]) {
+ bar_y1 = 0;
+ } else {
+ bar_y1 = event->start_minute * day_view->row_height / day_view->mins_per_row - y;
+ }
+
+ if (event->end >= day_view->day_starts[day + 1]) {
+ bar_y2 = height;
+ } else {
+ bar_y2 = event->end_minute * day_view->row_height / day_view->mins_per_row - y;
+ }
+
+ if (bar_y1 < height && bar_y2 > 0 && bar_y2 > bar_y1) {
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ grid_x, bar_y1,
+ E_DAY_VIEW_BAR_WIDTH - 2,
+ bar_y2 - bar_y1);
+ }
+ }
+
+
+ }
+}
+
+
+static void
+e_day_view_main_item_draw_day_events (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y, int width, int height,
+ gint day)
+{
+ EDayView *day_view;
+ gint event_num;
+
+ day_view = dvmitem->day_view;
+
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ e_day_view_main_item_draw_day_event (dvmitem, drawable,
+ x, y, width, height,
+ day, event_num);
+ }
+}
+
+
+static void
+e_day_view_main_item_draw_day_event (EDayViewMainItem *dvmitem,
+ GdkDrawable *drawable,
+ int x, int y, int width, int height,
+ gint day, gint event_num)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ gint item_x, item_y, item_w, item_h, bar_y1, bar_y2;
+ GtkStyle *style;
+ GdkGC *gc;
+ iCalObject *ico;
+ gint num_icons, icon_x, icon_y, icon_x_inc, icon_y_inc;
+ gboolean draw_reminder_icon, draw_recurrence_icon;
+
+ day_view = dvmitem->day_view;
+
+ /* If the event is currently being dragged, don't draw it. It will
+ be drawn in the special drag items. */
+ if (day_view->drag_event_day == day
+ && day_view->drag_event_num == event_num)
+ return;
+
+ style = GTK_WIDGET (day_view)->style;
+
+ gc = day_view->main_gc;
+ gdk_gc_set_foreground (gc, &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR]);
+
+ /* Get the position of the event. If it is not shown skip it.*/
+ if (!e_day_view_get_event_position (day_view, day, event_num,
+ &item_x, &item_y,
+ &item_w, &item_h))
+ return;
+
+ item_x -= x;
+ item_y -= y;
+
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* Fill in the white background. Note that for events in the first
+ column of the day, we might not want topaint over the vertical bar,
+ since that is used for multiple events. But then you can't see
+ where the event in the first column finishes. */
+#if 0
+ if (event->start_row_or_col == 0)
+ gdk_draw_rectangle (drawable, style->white_gc, TRUE,
+ item_x + E_DAY_VIEW_BAR_WIDTH, item_y + 1,
+ item_w - E_DAY_VIEW_BAR_WIDTH - 1,
+ item_h - 2);
+ else
+#endif
+ gdk_draw_rectangle (drawable, style->white_gc, TRUE,
+ item_x + 1, item_y + 1,
+ item_w - 2, item_h - 2);
+
+ /* Draw the right edge of the vertical bar. */
+ gdk_draw_line (drawable, style->black_gc,
+ item_x + E_DAY_VIEW_BAR_WIDTH - 1,
+ item_y + 1,
+ item_x + E_DAY_VIEW_BAR_WIDTH - 1,
+ item_y + item_h - 2);
+
+ /* Draw the vertical colored bar showing when the appointment
+ begins & ends. */
+ bar_y1 = event->start_minute * day_view->row_height / day_view->mins_per_row - y;
+ bar_y2 = event->end_minute * day_view->row_height / day_view->mins_per_row - y;
+
+ /* When an item is being resized, we fill the bar up to the new row. */
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
+ && day_view->resize_event_day == day
+ && day_view->resize_event_num == event_num) {
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_TOP_EDGE)
+ bar_y1 = item_y + 1;
+ else if (day_view->resize_drag_pos == E_DAY_VIEW_POS_BOTTOM_EDGE)
+ bar_y2 = item_y + item_h - 1;
+ }
+
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ item_x + 1, bar_y1,
+ E_DAY_VIEW_BAR_WIDTH - 2, bar_y2 - bar_y1);
+
+ /* Draw the box around the entire event. Do this after drawing
+ the colored bar so we don't have to worry about being 1
+ pixel out. */
+ gdk_draw_rectangle (drawable, style->black_gc, FALSE,
+ item_x, item_y, item_w - 1, item_h - 1);
+
+#if 0
+ /* Draw the horizontal bars above and beneath the event if it
+ is currently being edited. */
+ if (day_view->editing_event_day == day
+ && day_view->editing_event_num == event_num) {
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ item_x,
+ item_y - E_DAY_VIEW_BAR_HEIGHT,
+ item_w,
+ E_DAY_VIEW_BAR_HEIGHT);
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ item_x, item_y + item_h,
+ item_w, E_DAY_VIEW_BAR_HEIGHT);
+ }
+#endif
+
+ /* Draw the reminder & recurrence icons, if needed. */
+ num_icons = 0;
+ draw_reminder_icon = FALSE;
+ draw_recurrence_icon = FALSE;
+ icon_x = item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_ICON_X_PAD;
+ icon_y = item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT
+ + E_DAY_VIEW_ICON_Y_PAD;
+ ico = event->ico;
+
+ if (ico->dalarm.enabled || ico->malarm.enabled
+ || ico->palarm.enabled || ico->aalarm.enabled) {
+ draw_reminder_icon = TRUE;
+ num_icons++;
+ }
+
+ if (ico->recur) {
+ draw_recurrence_icon = TRUE;
+ num_icons++;
+ }
+
+ if (num_icons != 0) {
+ if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD)
+ * num_icons) {
+ icon_x_inc = 0;
+ icon_y_inc = E_DAY_VIEW_ICON_HEIGHT
+ + E_DAY_VIEW_ICON_Y_PAD;
+ } else {
+ icon_x_inc = E_DAY_VIEW_ICON_WIDTH
+ + E_DAY_VIEW_ICON_X_PAD;
+ icon_y_inc = 0;
+ }
+
+ if (draw_reminder_icon) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, day_view->reminder_mask);
+ gdk_draw_pixmap (drawable, gc,
+ day_view->reminder_icon,
+ 0, 0, icon_x, icon_y,
+ E_DAY_VIEW_ICON_WIDTH,
+ E_DAY_VIEW_ICON_HEIGHT);
+ icon_x += icon_x_inc;
+ icon_y += icon_y_inc;
+ }
+
+ if (draw_recurrence_icon) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, day_view->recurrence_mask);
+ gdk_draw_pixmap (drawable, gc,
+ day_view->recurrence_icon,
+ 0, 0, icon_x, icon_y,
+ E_DAY_VIEW_ICON_WIDTH,
+ E_DAY_VIEW_ICON_HEIGHT);
+ }
+ gdk_gc_set_clip_mask (gc, NULL);
+ }
+}
+
+
+/* This is supposed to return the nearest item the the point and the distance.
+ Since we are the only item we just return ourself and 0 for the distance.
+ This is needed so that we get button/motion events. */
+static double
+e_day_view_main_item_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.0;
+}
+
+
+static gint
+e_day_view_main_item_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ EDayViewMainItem *dvtitem;
+
+ dvtitem = E_DAY_VIEW_MAIN_ITEM (item);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+
+ case GDK_BUTTON_RELEASE:
+
+ case GDK_MOTION_NOTIFY:
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
diff --git a/calendar/gui/e-day-view-main-item.h b/calendar/gui/e-day-view-main-item.h
new file mode 100644
index 0000000000..d8305e594d
--- /dev/null
+++ b/calendar/gui/e-day-view-main-item.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_DAY_VIEW_MAIN_ITEM_H_
+#define _E_DAY_VIEW_MAIN_ITEM_H_
+
+#include "e-day-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EDayViewMainItem - canvas item which displays most of the appointment
+ * data in the main Day/Work Week display.
+ */
+
+#define E_DAY_VIEW_MAIN_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_day_view_main_item_get_type (), EDayViewMainItem))
+#define E_DAY_VIEW_MAIN_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_day_view_main_item_get_type ()))
+#define E_IS_DAY_VIEW_MAIN_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_day_view_main_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The parent EDayView widget. */
+ EDayView *day_view;
+} EDayViewMainItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EDayViewMainItemClass;
+
+
+GtkType e_day_view_main_item_get_type (void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_DAY_VIEW_MAIN_ITEM_H_ */
diff --git a/calendar/gui/e-day-view-time-item.c b/calendar/gui/e-day-view-time-item.c
new file mode 100644
index 0000000000..aa86cc2ca8
--- /dev/null
+++ b/calendar/gui/e-day-view-time-item.c
@@ -0,0 +1,312 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * EDayViewTimeItem - canvas item which displays the times down the left of
+ * the EDayView.
+ */
+
+#include "e-day-view-time-item.h"
+
+
+/* The spacing between items in the time column. GRID_X_PAD is the space down
+ either side of the column, i.e. outside the main horizontal grid lines.
+ HOUR_L_PAD & HOUR_R_PAD are the spaces on the left & right side of the
+ big hour number (this is inside the horizontal grid lines).
+ MIN_X_PAD is the spacing either side of the minute number. The smaller
+ horizontal grid lines match with this.
+ 60_MIN_X_PAD is the space either side of the HH:MM display used when
+ we are displaying 60 mins per row (inside the main grid lines). */
+#define E_DVTMI_TIME_GRID_X_PAD 4
+#define E_DVTMI_HOUR_L_PAD 4
+#define E_DVTMI_HOUR_R_PAD 2
+#define E_DVTMI_MIN_X_PAD 2
+#define E_DVTMI_60_MIN_X_PAD 4
+
+
+static void e_day_view_time_item_class_init (EDayViewTimeItemClass *class);
+static void e_day_view_time_item_init (EDayViewTimeItem *dvtmitem);
+static void e_day_view_time_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+
+static void e_day_view_time_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path, int flags);
+static void e_day_view_time_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height);
+static double e_day_view_time_item_point (GnomeCanvasItem *item,
+ double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+
+
+
+static GnomeCanvasItemClass *parent_class;
+
+
+/* The arguments we take */
+enum {
+ ARG_0,
+ ARG_DAY_VIEW
+};
+
+
+GtkType
+e_day_view_time_item_get_type (void)
+{
+ static GtkType e_day_view_time_item_type = 0;
+
+ if (!e_day_view_time_item_type) {
+ GtkTypeInfo e_day_view_time_item_info = {
+ "EDayViewTimeItem",
+ sizeof (EDayViewTimeItem),
+ sizeof (EDayViewTimeItemClass),
+ (GtkClassInitFunc) e_day_view_time_item_class_init,
+ (GtkObjectInitFunc) e_day_view_time_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_day_view_time_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_day_view_time_item_info);
+ }
+
+ return e_day_view_time_item_type;
+}
+
+
+static void
+e_day_view_time_item_class_init (EDayViewTimeItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ parent_class = gtk_type_class (gnome_canvas_item_get_type());
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ gtk_object_add_arg_type ("EDayViewTimeItem::day_view",
+ GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
+ ARG_DAY_VIEW);
+
+ object_class->set_arg = e_day_view_time_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_day_view_time_item_update;
+ item_class->draw = e_day_view_time_item_draw;
+ item_class->point = e_day_view_time_item_point;
+}
+
+
+static void
+e_day_view_time_item_init (EDayViewTimeItem *dvtmitm)
+{
+
+}
+
+
+static void
+e_day_view_time_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EDayViewTimeItem *dvtmitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ dvtmitem = E_DAY_VIEW_TIME_ITEM (o);
+
+ switch (arg_id){
+ case ARG_DAY_VIEW:
+ dvtmitem->day_view = GTK_VALUE_POINTER (*arg);
+ break;
+ }
+}
+
+
+static void
+e_day_view_time_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags)
+{
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);
+
+ /* The item covers the entire canvas area. */
+ item->x1 = 0;
+ item->y1 = 0;
+ item->x2 = INT_MAX;
+ item->y2 = INT_MAX;
+}
+
+
+/* Returns the minimum width needed for the column, by adding up all the
+ maximum widths of the strings. The string widths are all calculated in
+ the style_set handlers of EDayView and EDayViewTimeCanvas. */
+gint
+e_day_view_time_item_get_column_width (EDayViewTimeItem *dvtmitem)
+{
+ EDayView *day_view;
+
+ day_view = dvtmitem->day_view;
+ g_return_val_if_fail (day_view != NULL, 0);
+
+ /* Calculate the width of each time column. */
+ if (day_view->mins_per_row == 60) {
+ dvtmitem->column_width = day_view->max_small_hour_width
+ + day_view->colon_width
+ + day_view->max_minute_width
+ + E_DVTMI_60_MIN_X_PAD * 2
+ + E_DVTMI_TIME_GRID_X_PAD * 2;
+ } else {
+ dvtmitem->column_width = day_view->max_large_hour_width
+ + day_view->max_minute_width
+ + E_DVTMI_MIN_X_PAD * 2
+ + E_DVTMI_HOUR_L_PAD
+ + E_DVTMI_HOUR_R_PAD
+ + E_DVTMI_TIME_GRID_X_PAD * 2;
+ }
+
+ return dvtmitem->column_width;
+}
+
+
+/*
+ * DRAWING ROUTINES - functions to paint the canvas item.
+ */
+
+static void
+e_day_view_time_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EDayView *day_view;
+ EDayViewTimeItem *dvtmitem;
+ gint time_hour_x1, time_hour_x2, time_min_x1;
+ gint hour, minute, hour_y, min_y, hour_r, min_r, start_y;
+ gint row, row_y, min_width, hour_width;
+ GtkStyle *style;
+ GdkFont *small_font, *large_font;
+ GdkGC *fg_gc, *dark_gc;
+ gchar buffer[16];
+
+ dvtmitem = E_DAY_VIEW_TIME_ITEM (canvas_item);
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ style = GTK_WIDGET (day_view)->style;
+ small_font = style->font;
+ large_font = day_view->large_font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ dark_gc = style->dark_gc[GTK_STATE_NORMAL];
+
+ /* Step through each row, drawing the horizontal grid lines for each
+ day column and the times. */
+ time_hour_x1 = E_DVTMI_TIME_GRID_X_PAD - x;
+ time_hour_x2 = dvtmitem->column_width - E_DVTMI_TIME_GRID_X_PAD - x;
+ if (day_view->mins_per_row == 60) {
+ min_r = time_hour_x2 - E_DVTMI_60_MIN_X_PAD;
+ } else {
+ time_min_x1 = time_hour_x2 - E_DVTMI_MIN_X_PAD * 2
+ - day_view->max_minute_width;
+ hour_r = time_min_x1 - E_DVTMI_HOUR_R_PAD;
+ min_r = time_hour_x2 - E_DVTMI_MIN_X_PAD;
+ }
+
+ hour = day_view->first_hour_shown;
+ hour_y = large_font->ascent + 2; /* FIXME */
+ minute = day_view->first_minute_shown;
+ min_y = small_font->ascent + 2; /* FIXME */
+ start_y = 0 - MAX (day_view->row_height, hour_y + large_font->descent);
+ for (row = 0, row_y = 0 - y;
+ row < day_view->rows && row_y < height;
+ row++, row_y += day_view->row_height) {
+ if (row_y > start_y) {
+ /* Draw the times down the left if needed. */
+ if (min_r <= 0)
+ continue;
+
+ if (day_view->mins_per_row == 60) {
+ gdk_draw_line (drawable, dark_gc,
+ time_hour_x1, row_y,
+ time_hour_x2, row_y);
+ sprintf (buffer, "%02i:%02i", hour, minute);
+ min_width = day_view->small_hour_widths[hour] + day_view->minute_widths[minute / 5] + day_view->colon_width;
+ gdk_draw_string (drawable, small_font, fg_gc,
+ min_r - min_width,
+ row_y + min_y, buffer);
+ } else {
+ if (minute == 0) {
+ gdk_draw_line (drawable, dark_gc,
+ time_hour_x1, row_y,
+ time_hour_x2, row_y);
+ sprintf (buffer, "%02i", hour);
+ hour_width = day_view->large_hour_widths[hour];
+ gdk_draw_string (drawable, large_font,
+ fg_gc,
+ hour_r - hour_width,
+ row_y + hour_y,
+ buffer);
+ } else {
+ gdk_draw_line (drawable, dark_gc,
+ time_min_x1, row_y,
+ time_hour_x2, row_y);
+ }
+
+ if (day_view->mins_per_row != 30
+ || minute != 30) {
+ sprintf (buffer, "%02i", minute);
+ min_width = day_view->minute_widths[minute / 5];
+ gdk_draw_string (drawable, small_font,
+ fg_gc,
+ min_r - min_width,
+ row_y + min_y,
+ buffer);
+ }
+ }
+ }
+
+ minute += day_view->mins_per_row;
+ if (minute >= 60) {
+ hour++;
+ minute -= 60;
+ }
+ }
+}
+
+
+static double
+e_day_view_time_item_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.0;
+}
diff --git a/calendar/gui/e-day-view-time-item.h b/calendar/gui/e-day-view-time-item.h
new file mode 100644
index 0000000000..9fec96d7dc
--- /dev/null
+++ b/calendar/gui/e-day-view-time-item.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_DAY_VIEW_TIME_ITEM_H_
+#define _E_DAY_VIEW_TIME_ITEM_H_
+
+#include "e-day-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EDayViewTimeItem - canvas item which displays the times down the left of
+ * the EDayView.
+ */
+
+#define E_DAY_VIEW_TIME_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_day_view_time_item_get_type (), EDayViewTimeItem))
+#define E_DAY_VIEW_TIME_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_day_view_time_item_get_type ()))
+#define E_IS_DAY_VIEW_TIME_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_day_view_time_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The parent EDayView widget. */
+ EDayView *day_view;
+
+ /* The width of the time column. */
+ gint column_width;
+} EDayViewTimeItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EDayViewTimeItemClass;
+
+
+GtkType e_day_view_time_item_get_type (void);
+
+
+gint e_day_view_time_item_get_column_width (EDayViewTimeItem *dvtmitem);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_DAY_VIEW_TIME_ITEM_H_ */
diff --git a/calendar/gui/e-day-view-top-item.c b/calendar/gui/e-day-view-top-item.c
new file mode 100644
index 0000000000..fe19c6b491
--- /dev/null
+++ b/calendar/gui/e-day-view-top-item.c
@@ -0,0 +1,553 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * EDayViewTopItem - displays the top part of the Day/Work Week calendar view.
+ */
+
+#include "e-day-view-top-item.h"
+
+static void e_day_view_top_item_class_init (EDayViewTopItemClass *class);
+static void e_day_view_top_item_init (EDayViewTopItem *dvtitem);
+
+static void e_day_view_top_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_day_view_top_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+static void e_day_view_top_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_day_view_top_item_draw_long_event (EDayViewTopItem *dvtitem,
+ gint event_num,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_day_view_top_item_draw_triangle (EDayViewTopItem *dvtitem,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint w,
+ gint h);
+static double e_day_view_top_item_point (GnomeCanvasItem *item,
+ double x,
+ double y,
+ int cx,
+ int cy,
+ GnomeCanvasItem **actual_item);
+static gint e_day_view_top_item_event (GnomeCanvasItem *item,
+ GdkEvent *event);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+/* The arguments we take */
+enum {
+ ARG_0,
+ ARG_DAY_VIEW
+};
+
+
+GtkType
+e_day_view_top_item_get_type (void)
+{
+ static GtkType e_day_view_top_item_type = 0;
+
+ if (!e_day_view_top_item_type) {
+ GtkTypeInfo e_day_view_top_item_info = {
+ "EDayViewTopItem",
+ sizeof (EDayViewTopItem),
+ sizeof (EDayViewTopItemClass),
+ (GtkClassInitFunc) e_day_view_top_item_class_init,
+ (GtkObjectInitFunc) e_day_view_top_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_day_view_top_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_day_view_top_item_info);
+ }
+
+ return e_day_view_top_item_type;
+}
+
+
+static void
+e_day_view_top_item_class_init (EDayViewTopItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ parent_class = gtk_type_class (gnome_canvas_item_get_type());
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ gtk_object_add_arg_type ("EDayViewTopItem::day_view",
+ GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
+ ARG_DAY_VIEW);
+
+ object_class->set_arg = e_day_view_top_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_day_view_top_item_update;
+ item_class->draw = e_day_view_top_item_draw;
+ item_class->point = e_day_view_top_item_point;
+ item_class->event = e_day_view_top_item_event;
+}
+
+
+static void
+e_day_view_top_item_init (EDayViewTopItem *dvtitem)
+{
+ dvtitem->day_view = NULL;
+}
+
+
+static void
+e_day_view_top_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EDayViewTopItem *dvtitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ dvtitem = E_DAY_VIEW_TOP_ITEM (o);
+
+ switch (arg_id){
+ case ARG_DAY_VIEW:
+ dvtitem->day_view = GTK_VALUE_POINTER (*arg);
+ break;
+ }
+}
+
+
+static void
+e_day_view_top_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags)
+{
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);
+
+ /* The item covers the entire canvas area. */
+ item->x1 = 0;
+ item->y1 = 0;
+ item->x2 = INT_MAX;
+ item->y2 = INT_MAX;
+}
+
+
+/*
+ * DRAWING ROUTINES - functions to paint the canvas item.
+ */
+
+static void
+e_day_view_top_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EDayViewTopItem *dvtitem;
+ EDayView *day_view;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc, *light_gc, *dark_gc;
+ gchar buffer[128];
+ GdkRectangle clip_rect;
+ GdkFont *font;
+ gint canvas_width, canvas_height, left_edge, day, date_width, date_x;
+ gint item_height, event_num;
+ struct tm *day_start;
+
+#if 0
+ g_print ("In e_day_view_top_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+ dvtitem = E_DAY_VIEW_TOP_ITEM (canvas_item);
+ day_view = dvtitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ style = GTK_WIDGET (day_view)->style;
+ font = style->font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+ light_gc = style->light_gc[GTK_STATE_NORMAL];
+ dark_gc = style->dark_gc[GTK_STATE_NORMAL];
+ canvas_width = GTK_WIDGET (canvas_item->canvas)->allocation.width;
+ canvas_height = GTK_WIDGET (canvas_item->canvas)->allocation.height;
+ left_edge = 0;
+ item_height = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
+
+ /* Clear the entire background. */
+ gdk_draw_rectangle (drawable, dark_gc, TRUE,
+ left_edge - x, 0,
+ canvas_width - left_edge, height);
+
+ /* Draw the shadow around the dates. */
+ gdk_draw_line (drawable, light_gc,
+ left_edge + 1 - x, 1 - y,
+ canvas_width - 2 - x, 1 - y);
+ gdk_draw_line (drawable, light_gc,
+ left_edge + 1 - x, 2 - y,
+ left_edge + 1 - x, item_height - 1 - y);
+
+ /* Draw the background for the dates. */
+ gdk_draw_rectangle (drawable, bg_gc, TRUE,
+ left_edge + 2 - x, 2 - y,
+ canvas_width - left_edge - 3,
+ item_height - 3);
+
+ /* Draw the selection background. */
+ if (day_view->selection_start_col != -1) {
+ gint start_col, end_col, rect_x, rect_y, rect_w, rect_h;
+
+ start_col = day_view->selection_start_col;
+ end_col = day_view->selection_end_col;
+
+ if (end_col > start_col
+ || day_view->selection_start_row == -1
+ || day_view->selection_end_row == -1) {
+ rect_x = day_view->day_offsets[start_col];
+ rect_y = item_height;
+ rect_w = day_view->day_offsets[end_col + 1] - rect_x;
+ rect_h = canvas_height - 1 - rect_y;
+
+ gdk_draw_rectangle (drawable, style->white_gc, TRUE,
+ rect_x - x, rect_y - y,
+ rect_w, rect_h);
+ }
+ }
+
+ /* Draw the date. Set a clipping rectangle so we don't draw over the
+ next day. */
+ for (day = 0; day < day_view->days_shown; day++) {
+ day_start = localtime (&day_view->day_starts[day]);
+
+ if (day_view->date_format == E_DAY_VIEW_DATE_FULL)
+ strftime (buffer, 128, "%d %B", day_start);
+ else if (day_view->date_format == E_DAY_VIEW_DATE_ABBREVIATED)
+ strftime (buffer, 128, "%d %b", day_start);
+ else
+ strftime (buffer, 128, "%d", day_start);
+
+ clip_rect.x = day_view->day_offsets[day] - x;
+ clip_rect.y = 2 - y;
+ clip_rect.width = day_view->day_widths[day];
+ clip_rect.height = item_height - 2;
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rect);
+
+ date_width = gdk_string_width (font, buffer);
+ date_x = day_view->day_offsets[day] + (day_view->day_widths[day] - date_width) / 2;
+ gdk_draw_string (drawable, font, fg_gc,
+ date_x - x, 3 + font->ascent - y, buffer);
+
+ gdk_gc_set_clip_rectangle (fg_gc, NULL);
+
+ /* Draw the lines down the left and right of the date cols. */
+ if (day != 0) {
+ gdk_draw_line (drawable, light_gc,
+ day_view->day_offsets[day] - x,
+ 4 - y,
+ day_view->day_offsets[day] - x,
+ item_height - 4 - y);
+
+ gdk_draw_line (drawable, dark_gc,
+ day_view->day_offsets[day] - 1 - x,
+ 4 - y,
+ day_view->day_offsets[day] - 1 - x,
+ item_height - 4 - y);
+ }
+
+ /* Draw the lines between each column. */
+ if (day != 0) {
+ gdk_draw_line (drawable, style->black_gc,
+ day_view->day_offsets[day] - x,
+ item_height - y,
+ day_view->day_offsets[day] - x,
+ canvas_height - y);
+ }
+ }
+
+ /* Draw the long events. */
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ e_day_view_top_item_draw_long_event (dvtitem, event_num,
+ drawable,
+ x, y, width, height);
+ }
+}
+
+
+/* This draws one event in the top canvas. */
+static void
+e_day_view_top_item_draw_long_event (EDayViewTopItem *dvtitem,
+ gint event_num,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ GtkStyle *style;
+ GdkGC *gc, *fg_gc, *bg_gc;
+ GdkFont *font;
+ gint start_day, end_day;
+ gint item_x, item_y, item_w, item_h;
+ gint text_x, icon_x, icon_y, icon_x_inc;
+ iCalObject *ico;
+ gchar buffer[16];
+ gint hour, minute, offset, time_width;
+ gboolean draw_start_triangle, draw_end_triangle;
+
+ day_view = dvtitem->day_view;
+
+ /* If the event is currently being dragged, don't draw it. It will
+ be drawn in the special drag items. */
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->drag_event_num == event_num)
+ return;
+
+ if (!e_day_view_get_long_event_position (day_view, event_num,
+ &start_day, &end_day,
+ &item_x, &item_y,
+ &item_w, &item_h))
+ return;
+
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ style = GTK_WIDGET (day_view)->style;
+ font = style->font;
+ gc = day_view->main_gc;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+ ico = event->ico;
+
+ /* Draw the lines across the top & bottom of the entire event. */
+ gdk_draw_line (drawable, fg_gc,
+ item_x - x, item_y - y,
+ item_x + item_w - 1 - x, item_y - y);
+ gdk_draw_line (drawable, fg_gc,
+ item_x - x, item_y + item_h - 1 - y,
+ item_x + item_w - 1 - x, item_y + item_h - 1 - y);
+
+ /* Fill it in. */
+ gdk_draw_rectangle (drawable, bg_gc, TRUE,
+ item_x - x, item_y + 1 - y,
+ item_w, item_h - 2);
+
+ /* When resizing we don't draw the triangles.*/
+ draw_start_triangle = TRUE;
+ draw_end_triangle = TRUE;
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
+ && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->resize_event_num == event_num) {
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_LEFT_EDGE)
+ draw_start_triangle = FALSE;
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_RIGHT_EDGE)
+ draw_end_triangle = FALSE;
+ }
+
+ /* If the event starts before the first day shown, draw a triangle,
+ else just draw a vertical line down the left. */
+ if (draw_start_triangle
+ && event->start < day_view->day_starts[start_day]) {
+ e_day_view_top_item_draw_triangle (dvtitem, drawable,
+ item_x - x, item_y - y,
+ -E_DAY_VIEW_BAR_WIDTH,
+ item_h);
+ } else {
+ gdk_draw_line (drawable, fg_gc,
+ item_x - x, item_y - y,
+ item_x - x, item_y + item_h - 1 - y);
+ }
+
+ /* Similar for the event end. */
+ if (draw_end_triangle
+ && event->end > day_view->day_starts[end_day + 1]) {
+ e_day_view_top_item_draw_triangle (dvtitem, drawable,
+ item_x + item_w - 1 - x,
+ item_y - y,
+ E_DAY_VIEW_BAR_WIDTH,
+ item_h);
+ } else {
+ gdk_draw_line (drawable, fg_gc,
+ item_x + item_w - 1 - x,
+ item_y - y,
+ item_x + item_w - 1 - x,
+ item_y + item_h - 1 - y);
+ }
+
+ /* If we are editing the event we don't show the icons or the start
+ & end times. */
+ if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->editing_event_num == event_num)
+ return;
+
+ /* Determine the position of the label, so we know where to place the
+ icons. Note that since the top canvas never scrolls we don't need
+ to take the scroll offset into account. It will always be 0. */
+ text_x = event->canvas_item->x1;
+
+ /* Draw the icons. */
+ icon_x_inc = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD;
+ icon_x = text_x - icon_x_inc - x;
+ icon_y = item_y + 1 + E_DAY_VIEW_ICON_Y_PAD - y;
+
+ if (ico->recur) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, day_view->recurrence_mask);
+ gdk_draw_pixmap (drawable, gc,
+ day_view->recurrence_icon,
+ 0, 0, icon_x, icon_y,
+ E_DAY_VIEW_ICON_WIDTH,
+ E_DAY_VIEW_ICON_HEIGHT);
+ icon_x -= icon_x_inc;
+ }
+
+ if (ico->dalarm.enabled || ico->malarm.enabled
+ || ico->palarm.enabled || ico->aalarm.enabled) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, day_view->reminder_mask);
+ gdk_draw_pixmap (drawable, gc,
+ day_view->reminder_icon,
+ 0, 0, icon_x, icon_y,
+ E_DAY_VIEW_ICON_WIDTH,
+ E_DAY_VIEW_ICON_HEIGHT);
+ icon_x -= icon_x_inc;
+ }
+ gdk_gc_set_clip_mask (gc, NULL);
+
+ /* Draw the start & end times, if necessary.
+ Note that GtkLabel adds 1 to the ascent so we must do that to be
+ level with it. */
+ if (event->start > day_view->day_starts[start_day]) {
+ offset = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown + event->start_minute;
+ hour = offset / 60;
+ minute = offset % 60;
+ sprintf (buffer, "%02i:%02i", hour, minute);
+ gdk_draw_string (drawable, font, fg_gc,
+ item_x + + E_DAY_VIEW_LONG_EVENT_X_PAD - x,
+ item_y + E_DAY_VIEW_LONG_EVENT_Y_PAD + font->ascent + 1 - y,
+ buffer);
+ }
+
+ if (event->end < day_view->day_starts[end_day + 1]) {
+ offset = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown + event->end_minute;
+ hour = offset / 60;
+ minute = offset % 60;
+ sprintf (buffer, "%02i:%02i", hour, minute);
+ time_width = day_view->small_hour_widths[hour]
+ + day_view->max_minute_width + day_view->colon_width;
+ gdk_draw_string (drawable, font, fg_gc,
+ item_x + item_w - E_DAY_VIEW_LONG_EVENT_X_PAD - time_width - E_DAY_VIEW_LONG_EVENT_TIME_X_PAD - x,
+ item_y + E_DAY_VIEW_LONG_EVENT_Y_PAD + font->ascent + 1 - y,
+ buffer);
+ }
+}
+
+
+/* This draws a little triangle to indicate that an event extends past
+ the days visible on screen. */
+static void
+e_day_view_top_item_draw_triangle (EDayViewTopItem *dvtitem,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint w,
+ gint h)
+{
+ EDayView *day_view;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc;
+ GdkPoint points[3];
+
+ day_view = dvtitem->day_view;
+
+ style = GTK_WIDGET (day_view)->style;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_NORMAL];
+
+ points[0].x = x;
+ points[0].y = y;
+ points[1].x = x + w;
+ points[1].y = y + (h / 2) - 1;
+ points[2].x = x;
+ points[2].y = y + h - 1;
+
+ gdk_draw_polygon (drawable, bg_gc, TRUE, points, 3);
+ gdk_draw_line (drawable, fg_gc, x, y, x + w, y + (h / 2) - 1);
+ gdk_draw_line (drawable, fg_gc, x, y + h - 1, x + w, y + h - (h / 2));
+}
+
+
+/* This is supposed to return the nearest item the the point and the distance.
+ Since we are the only item we just return ourself and 0 for the distance.
+ This is needed so that we get button/motion events. */
+static double
+e_day_view_top_item_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.0;
+}
+
+
+static gint
+e_day_view_top_item_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ EDayViewTopItem *dvtitem;
+
+ dvtitem = E_DAY_VIEW_TOP_ITEM (item);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+
+ case GDK_BUTTON_RELEASE:
+
+ case GDK_MOTION_NOTIFY:
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
diff --git a/calendar/gui/e-day-view-top-item.h b/calendar/gui/e-day-view-top-item.h
new file mode 100644
index 0000000000..435ef12b58
--- /dev/null
+++ b/calendar/gui/e-day-view-top-item.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_DAY_VIEW_TOP_ITEM_H_
+#define _E_DAY_VIEW_TOP_ITEM_H_
+
+#include "e-day-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EDayViewTopItem - displays the top part of the Day/Work Week calendar view.
+ */
+
+#define E_DAY_VIEW_TOP_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_day_view_top_item_get_type (), EDayViewTopItem))
+#define E_DAY_VIEW_TOP_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_day_view_top_item_get_type ()))
+#define E_IS_DAY_VIEW_TOP_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_day_view_top_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The parent EDayView widget. */
+ EDayView *day_view;
+} EDayViewTopItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EDayViewTopItemClass;
+
+
+GtkType e_day_view_top_item_get_type (void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_DAY_VIEW_TOP_ITEM_H_ */
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
new file mode 100644
index 0000000000..8b8e272faf
--- /dev/null
+++ b/calendar/gui/e-day-view.c
@@ -0,0 +1,4585 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * EDayView - displays the Day & Work-Week views of the calendar.
+ */
+
+#include <math.h>
+#include <time.h>
+#include <gnome.h>
+#include <gdk/gdkx.h>
+
+#include "e-day-view.h"
+#include "e-day-view-time-item.h"
+#include "e-day-view-top-item.h"
+#include "e-day-view-main-item.h"
+#include "main.h"
+#include "timeutil.h"
+#include "popup-menu.h"
+#include "eventedit.h"
+#include "../e-util/e-canvas.h"
+#include "../widgets/e-text/e-text.h"
+
+/* Images */
+#include "bell.xpm"
+#include "recur.xpm"
+
+/* The minimum amount of space wanted on each side of the date string. */
+#define E_DAY_VIEW_DATE_X_PAD 4
+
+#define E_DAY_VIEW_LARGE_FONT \
+ "-adobe-utopia-regular-r-normal-*-*-240-*-*-p-*-iso8859-*"
+#define E_DAY_VIEW_LARGE_FONT_FALLBACK \
+ "-adobe-helvetica-bold-r-normal-*-*-240-*-*-p-*-iso8859-*"
+
+/* The offset from the top/bottom of the canvas before auto-scrolling starts.*/
+#define E_DAY_VIEW_AUTO_SCROLL_OFFSET 16
+
+/* The time between each auto-scroll, in milliseconds. */
+#define E_DAY_VIEW_AUTO_SCROLL_TIMEOUT 50
+
+/* The number of timeouts we skip before we start scrolling. */
+#define E_DAY_VIEW_AUTO_SCROLL_DELAY 5
+
+/* The number of pixels the mouse has to be moved with the button down before
+ we start a drag. */
+#define E_DAY_VIEW_DRAG_START_OFFSET 4
+
+/* Drag and Drop stuff. */
+enum {
+ TARGET_CALENDAR_EVENT
+};
+static GtkTargetEntry target_table[] = {
+ { "application/x-e-calendar-event", 0, TARGET_CALENDAR_EVENT }
+};
+static guint n_targets = sizeof(target_table) / sizeof(target_table[0]);
+
+static void e_day_view_class_init (EDayViewClass *class);
+static void e_day_view_init (EDayView *day_view);
+static void e_day_view_destroy (GtkObject *object);
+static void e_day_view_realize (GtkWidget *widget);
+static void e_day_view_unrealize (GtkWidget *widget);
+static void e_day_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void e_day_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint e_day_view_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_day_view_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_day_view_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+
+static void e_day_view_on_canvas_realized (GtkWidget *widget,
+ EDayView *day_view);
+
+static gboolean e_day_view_on_top_canvas_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view);
+static gboolean e_day_view_on_top_canvas_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view);
+static gboolean e_day_view_on_top_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ EDayView *day_view);
+
+static gboolean e_day_view_on_main_canvas_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view);
+static gboolean e_day_view_on_main_canvas_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view);
+static gboolean e_day_view_on_main_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ EDayView *day_view);
+static gboolean e_day_view_convert_event_coords (EDayView *day_view,
+ GdkEvent *event,
+ GdkWindow *window,
+ gint *x_return,
+ gint *y_return);
+static void e_day_view_update_selection (EDayView *day_view,
+ gint row,
+ gint col);
+static void e_day_view_update_long_event_resize (EDayView *day_view,
+ gint day);
+static void e_day_view_update_resize (EDayView *day_view,
+ gint row);
+static void e_day_view_finish_long_event_resize (EDayView *day_view);
+static void e_day_view_finish_resize (EDayView *day_view);
+static void e_day_view_abort_resize (EDayView *day_view,
+ guint32 time);
+
+
+static gboolean e_day_view_on_long_event_button_press (EDayView *day_view,
+ gint event_num,
+ GdkEventButton *event,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y);
+static gboolean e_day_view_on_event_button_press (EDayView *day_view,
+ gint day,
+ gint event_num,
+ GdkEventButton *event,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y);
+static void e_day_view_on_long_event_click (EDayView *day_view,
+ gint event_num,
+ GdkEventButton *bevent,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y);
+static void e_day_view_on_event_click (EDayView *day_view,
+ gint day,
+ gint event_num,
+ GdkEventButton *event,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y);
+static void e_day_view_on_event_double_click (EDayView *day_view,
+ gint day,
+ gint event_num);
+static void e_day_view_on_event_right_click (EDayView *day_view,
+ GdkEventButton *bevent,
+ gint day,
+ gint event_num);
+
+static void e_day_view_recalc_num_rows (EDayView *day_view);
+
+static EDayViewPosition e_day_view_convert_position_in_top_canvas (EDayView *day_view,
+ gint x,
+ gint y,
+ gint *day_return,
+ gint *event_num_return);
+static EDayViewPosition e_day_view_convert_position_in_main_canvas (EDayView *day_view,
+ gint x,
+ gint y,
+ gint *day_return,
+ gint *row_return,
+ gint *event_num_return);
+static gboolean e_day_view_find_event_from_item (EDayView *day_view,
+ GnomeCanvasItem *item,
+ gint *day_return,
+ gint *event_num_return);
+static gboolean e_day_view_find_event_from_ico (EDayView *day_view,
+ iCalObject *ico,
+ gint *day_return,
+ gint *event_num_return);
+
+static void e_day_view_reload_events (EDayView *day_view);
+static void e_day_view_free_events (EDayView *day_view);
+static void e_day_view_free_event_array (EDayView *day_view,
+ GArray *array);
+static int e_day_view_add_event (iCalObject *ico,
+ time_t start,
+ time_t end,
+ gpointer data);
+static void e_day_view_update_event_label (EDayView *day_view,
+ gint day,
+ gint event_num);
+static void e_day_view_update_long_event_label (EDayView *day_view,
+ gint event_num);
+
+static void e_day_view_layout_long_events (EDayView *day_view);
+static void e_day_view_layout_long_event (EDayView *day_view,
+ EDayViewEvent *event,
+ guint8 *grid);
+static void e_day_view_reshape_long_events (EDayView *day_view);
+static void e_day_view_reshape_long_event (EDayView *day_view,
+ gint event_num);
+static void e_day_view_layout_day_events (EDayView *day_view,
+ gint day);
+static void e_day_view_layout_day_event (EDayView *day_view,
+ gint day,
+ EDayViewEvent *event,
+ guint8 *grid,
+ guint16 *group_starts);
+static void e_day_view_expand_day_event (EDayView *day_view,
+ gint day,
+ EDayViewEvent *event,
+ guint8 *grid);
+static void e_day_view_recalc_cols_per_row (EDayView *day_view,
+ gint day,
+ guint16 *group_starts);
+static void e_day_view_reshape_day_events (EDayView *day_view,
+ gint day);
+static void e_day_view_reshape_day_event (EDayView *day_view,
+ gint day,
+ gint event_num);
+static void e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view);
+static void e_day_view_reshape_resize_long_event_rect_item (EDayView *day_view);
+static void e_day_view_reshape_resize_rect_item (EDayView *day_view);
+
+static void e_day_view_ensure_events_sorted (EDayView *day_view);
+static gint e_day_view_event_sort_func (const void *arg1,
+ const void *arg2);
+
+static void e_day_view_start_editing_event (EDayView *day_view,
+ gint day,
+ gint event_num,
+ gchar *initial_text);
+static void e_day_view_stop_editing_event (EDayView *day_view);
+static gboolean e_day_view_on_text_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EDayView *day_view);
+static void e_day_view_on_editing_started (EDayView *day_view,
+ GnomeCanvasItem *item);
+static void e_day_view_on_editing_stopped (EDayView *day_view,
+ GnomeCanvasItem *item);
+
+static void e_day_view_get_selection_range (EDayView *day_view,
+ time_t *start,
+ time_t *end);
+static time_t e_day_view_convert_grid_position_to_time (EDayView *day_view,
+ gint col,
+ gint row);
+
+static void e_day_view_check_auto_scroll (EDayView *day_view,
+ gint event_y);
+static void e_day_view_start_auto_scroll (EDayView *day_view,
+ gboolean scroll_up);
+static void e_day_view_stop_auto_scroll (EDayView *day_view);
+static gboolean e_day_view_auto_scroll_handler (gpointer data);
+
+static void e_day_view_on_new_appointment (GtkWidget *widget,
+ gpointer data);
+static void e_day_view_on_edit_appointment (GtkWidget *widget,
+ gpointer data);
+static void e_day_view_on_delete_occurance (GtkWidget *widget,
+ gpointer data);
+static void e_day_view_on_delete_appointment (GtkWidget *widget,
+ gpointer data);
+static void e_day_view_on_unrecur_appointment (GtkWidget *widget,
+ gpointer data);
+static EDayViewEvent* e_day_view_get_popup_menu_event (EDayView *day_view);
+
+static gint e_day_view_on_top_canvas_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_update_top_canvas_drag (EDayView *day_view,
+ gint day);
+static void e_day_view_reshape_top_canvas_drag_item (EDayView *day_view);
+static gint e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_reshape_main_canvas_drag_item (EDayView *day_view);
+static void e_day_view_update_main_canvas_drag (EDayView *day_view,
+ gint row,
+ gint day);
+static void e_day_view_on_top_canvas_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_on_main_canvas_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_on_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ EDayView *day_view);
+static void e_day_view_on_drag_end (GtkWidget *widget,
+ GdkDragContext *context,
+ EDayView *day_view);
+static void e_day_view_on_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EDayView *day_view);
+static void e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EDayView *day_view);
+
+
+static GtkTableClass *parent_class;
+
+
+GtkType
+e_day_view_get_type (void)
+{
+ static GtkType e_day_view_type = 0;
+
+ if (!e_day_view_type){
+ GtkTypeInfo e_day_view_info = {
+ "EDayView",
+ sizeof (EDayView),
+ sizeof (EDayViewClass),
+ (GtkClassInitFunc) e_day_view_class_init,
+ (GtkObjectInitFunc) e_day_view_init,
+ NULL, /* reserved 1 */
+ NULL, /* reserved 2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ parent_class = gtk_type_class (GTK_TYPE_TABLE);
+ e_day_view_type = gtk_type_unique (GTK_TYPE_TABLE,
+ &e_day_view_info);
+ }
+
+ return e_day_view_type;
+}
+
+
+static void
+e_day_view_class_init (EDayViewClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ /* Method override */
+ object_class->destroy = e_day_view_destroy;
+
+ widget_class->realize = e_day_view_realize;
+ widget_class->unrealize = e_day_view_unrealize;
+ widget_class->style_set = e_day_view_style_set;
+ widget_class->size_allocate = e_day_view_size_allocate;
+ widget_class->focus_in_event = e_day_view_focus_in;
+ widget_class->focus_out_event = e_day_view_focus_out;
+ widget_class->key_press_event = e_day_view_key_press;
+}
+
+
+static void
+e_day_view_init (EDayView *day_view)
+{
+ GdkColormap *colormap;
+ gboolean success[E_DAY_VIEW_COLOR_LAST];
+ gint day, nfailed;
+ GnomeCanvasGroup *canvas_group;
+
+ GTK_WIDGET_SET_FLAGS (day_view, GTK_CAN_FOCUS);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (day_view));
+
+ day_view->calendar = NULL;
+
+ day_view->long_events = g_array_new (FALSE, FALSE,
+ sizeof (EDayViewEvent));
+ day_view->long_events_sorted = TRUE;
+ day_view->long_events_need_layout = FALSE;
+ day_view->long_events_need_reshape = FALSE;
+
+ for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) {
+ day_view->events[day] = g_array_new (FALSE, FALSE,
+ sizeof (EDayViewEvent));
+ day_view->events_sorted[day] = TRUE;
+ day_view->need_layout[day] = FALSE;
+ day_view->need_reshape[day] = FALSE;
+ }
+
+ /* FIXME: Initialize lower, upper, day_starts. */
+ day_view->days_shown = 1;
+
+ day_view->mins_per_row = 30;
+ day_view->date_format = E_DAY_VIEW_DATE_FULL;
+ day_view->rows_in_top_display = 0;
+ day_view->first_hour_shown = 0;
+ day_view->first_minute_shown = 0;
+ day_view->last_hour_shown = 24;
+ day_view->last_minute_shown = 0;
+ day_view->main_gc = NULL;
+ e_day_view_recalc_num_rows (day_view);
+
+ day_view->work_day_start_hour = 9;
+ day_view->work_day_start_minute = 0;
+ day_view->work_day_end_hour = 17;
+ day_view->work_day_end_minute = 0;
+ day_view->scroll_to_work_day = TRUE;
+
+ day_view->editing_event_day = -1;
+ day_view->editing_event_num = -1;
+
+ day_view->resize_bars_event_day = -1;
+ day_view->resize_bars_event_num = -1;
+
+ day_view->selection_start_row = -1;
+ day_view->selection_start_col = -1;
+ day_view->selection_end_row = -1;
+ day_view->selection_end_col = -1;
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
+ day_view->selection_in_top_canvas = FALSE;
+
+ day_view->resize_drag_pos = E_DAY_VIEW_POS_NONE;
+
+ day_view->pressed_event_day = -1;
+
+ day_view->drag_event_day = -1;
+ day_view->drag_last_day = -1;
+
+ day_view->auto_scroll_timeout_id = 0;
+
+ /* Create the large font. */
+ day_view->large_font = gdk_font_load (E_DAY_VIEW_LARGE_FONT);
+ if (!day_view->large_font)
+ day_view->large_font = gdk_font_load (E_DAY_VIEW_LARGE_FONT_FALLBACK);
+ if (!day_view->large_font)
+ g_warning ("Couldn't load font");
+
+
+ /* Allocate the colors. */
+#if 1
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].red = 255 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].green = 255 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].blue = 131 * 257;
+
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].red = 211 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].green = 208 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].blue = 6 * 257;
+#else
+
+ /* FG: MistyRose1, LightPink3 | RosyBrown | MistyRose3. */
+
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].red = 255 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].green = 228 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_WORKING].blue = 225 * 257;
+
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].red = 238 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].green = 162 * 257;
+ day_view->colors[E_DAY_VIEW_COLOR_BG_NOT_WORKING].blue = 173 * 257;
+#endif
+
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR].red = 0;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR].green = 0;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR].blue = 65535;
+
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].red = 65535;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].green = 65535;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND].blue = 65535;
+
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER].red = 0;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER].green = 0;
+ day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER].blue = 0;
+
+ nfailed = gdk_colormap_alloc_colors (colormap, day_view->colors,
+ E_DAY_VIEW_COLOR_LAST, FALSE,
+ TRUE, success);
+ if (nfailed)
+ g_warning ("Failed to allocate all colors");
+
+
+
+ /*
+ * Top Canvas
+ */
+ day_view->top_canvas = e_canvas_new ();
+ gtk_table_attach (GTK_TABLE (day_view), day_view->top_canvas,
+ 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (day_view->top_canvas);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->top_canvas), "button_press_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_button_press),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->top_canvas), "button_release_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_button_release),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->top_canvas), "motion_notify_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_motion),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->top_canvas),
+ "drag_motion",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_drag_motion),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->top_canvas),
+ "drag_leave",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_drag_leave),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->top_canvas),
+ "drag_begin",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_begin),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->top_canvas),
+ "drag_end",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_end),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->top_canvas),
+ "drag_data_get",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_data_get),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->top_canvas),
+ "drag_data_received",
+ GTK_SIGNAL_FUNC (e_day_view_on_top_canvas_drag_data_received),
+ day_view);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root);
+
+ day_view->top_canvas_item =
+ gnome_canvas_item_new (canvas_group,
+ e_day_view_top_item_get_type (),
+ "EDayViewTopItem::day_view", day_view,
+ NULL);
+
+ day_view->resize_long_event_rect_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type(),
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->resize_long_event_rect_item);
+
+ day_view->drag_long_event_rect_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type (),
+ "width_pixels", 1,
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
+
+ day_view->drag_long_event_item =
+ gnome_canvas_item_new (canvas_group,
+ e_text_get_type (),
+ "anchor", GTK_ANCHOR_NW,
+ "line_wrap", TRUE,
+ "clip", TRUE,
+ "max_lines", 1,
+ "editable", TRUE,
+ NULL);
+ gnome_canvas_item_hide (day_view->drag_long_event_item);
+
+ /*
+ * Main Canvas
+ */
+ day_view->main_canvas = e_canvas_new ();
+ gtk_table_attach (GTK_TABLE (day_view), day_view->main_canvas,
+ 1, 2, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (day_view->main_canvas);
+ gtk_signal_connect (GTK_OBJECT (day_view->main_canvas), "realize",
+ GTK_SIGNAL_FUNC (e_day_view_on_canvas_realized),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->main_canvas),
+ "button_press_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_button_press),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->main_canvas),
+ "button_release_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_button_release),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->main_canvas),
+ "motion_notify_event",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_motion),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->main_canvas),
+ "drag_motion",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_drag_motion),
+ day_view);
+ gtk_signal_connect_after (GTK_OBJECT (day_view->main_canvas),
+ "drag_leave",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_drag_leave),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->main_canvas),
+ "drag_begin",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_begin),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->main_canvas),
+ "drag_end",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_end),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->main_canvas),
+ "drag_data_get",
+ GTK_SIGNAL_FUNC (e_day_view_on_drag_data_get),
+ day_view);
+ gtk_signal_connect (GTK_OBJECT (day_view->main_canvas),
+ "drag_data_received",
+ GTK_SIGNAL_FUNC (e_day_view_on_main_canvas_drag_data_received),
+ day_view);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root);
+
+ day_view->main_canvas_item =
+ gnome_canvas_item_new (canvas_group,
+ e_day_view_main_item_get_type (),
+ "EDayViewMainItem::day_view", day_view,
+ NULL);
+
+ day_view->resize_rect_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type(),
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->resize_rect_item);
+
+ day_view->resize_bar_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type(),
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->resize_bar_item);
+
+ day_view->main_canvas_top_resize_bar_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type (),
+ "width_pixels", 1,
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->main_canvas_top_resize_bar_item);
+
+ day_view->main_canvas_bottom_resize_bar_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type (),
+ "width_pixels", 1,
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->main_canvas_bottom_resize_bar_item);
+
+
+ day_view->drag_rect_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type (),
+ "width_pixels", 1,
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BACKGROUND],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->drag_rect_item);
+
+ day_view->drag_bar_item =
+ gnome_canvas_item_new (canvas_group,
+ gnome_canvas_rect_get_type (),
+ "width_pixels", 1,
+ "fill_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_VBAR],
+ "outline_color_gdk", &day_view->colors[E_DAY_VIEW_COLOR_EVENT_BORDER],
+ NULL);
+ gnome_canvas_item_hide (day_view->drag_bar_item);
+
+ day_view->drag_item =
+ gnome_canvas_item_new (canvas_group,
+ e_text_get_type (),
+ "anchor", GTK_ANCHOR_NW,
+ "line_wrap", TRUE,
+ "clip", TRUE,
+ "editable", TRUE,
+ NULL);
+ gnome_canvas_item_hide (day_view->drag_item);
+
+
+ /*
+ * Times Canvas
+ */
+ day_view->time_canvas = e_canvas_new ();
+ gtk_layout_set_vadjustment (GTK_LAYOUT (day_view->time_canvas),
+ GTK_LAYOUT (day_view->main_canvas)->vadjustment);
+ gtk_table_attach (GTK_TABLE (day_view), day_view->time_canvas,
+ 0, 1, 1, 2,
+ GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (day_view->time_canvas);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->time_canvas)->root);
+
+ day_view->time_canvas_item =
+ gnome_canvas_item_new (canvas_group,
+ e_day_view_time_item_get_type (),
+ "EDayViewTimeItem::day_view", day_view,
+ NULL);
+
+
+ /*
+ * Scrollbar.
+ */
+ day_view->vscrollbar = gtk_vscrollbar_new (GTK_LAYOUT (day_view->main_canvas)->vadjustment);
+ gtk_table_attach (GTK_TABLE (day_view), day_view->vscrollbar,
+ 2, 3, 1, 2, 0, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (day_view->vscrollbar);
+
+
+ /* Create the pixmaps. */
+ day_view->reminder_icon = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &day_view->reminder_mask, NULL, bell_xpm);
+ day_view->recurrence_icon = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &day_view->recurrence_mask, NULL, recur_xpm);
+
+
+ /* Create the cursors. */
+ day_view->normal_cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
+ day_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
+ day_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+ day_view->resize_height_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
+ day_view->last_cursor_set_in_top_canvas = NULL;
+ day_view->last_cursor_set_in_main_canvas = NULL;
+
+ /* Set up the drop sites. */
+ gtk_drag_dest_set (GTK_WIDGET (day_view->top_canvas),
+ GTK_DEST_DEFAULT_ALL,
+ target_table, n_targets,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ gtk_drag_dest_set (GTK_WIDGET (day_view->main_canvas),
+ GTK_DEST_DEFAULT_ALL,
+ target_table, n_targets,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+}
+
+
+/* Turn off the background of the canvas windows. This reduces flicker
+ considerably when scrolling. (Why isn't it in GnomeCanvas?). */
+static void
+e_day_view_on_canvas_realized (GtkWidget *widget,
+ EDayView *day_view)
+{
+ gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window,
+ NULL, FALSE);
+}
+
+
+/**
+ * e_day_view_new:
+ * @Returns: a new #EDayView.
+ *
+ * Creates a new #EDayView.
+ **/
+GtkWidget *
+e_day_view_new (void)
+{
+ GtkWidget *day_view;
+
+ day_view = GTK_WIDGET (gtk_type_new (e_day_view_get_type ()));
+
+ return day_view;
+}
+
+
+static void
+e_day_view_destroy (GtkObject *object)
+{
+ EDayView *day_view;
+ gint day;
+
+ day_view = E_DAY_VIEW (object);
+
+ e_day_view_stop_auto_scroll (day_view);
+
+ gdk_cursor_destroy (day_view->normal_cursor);
+ gdk_cursor_destroy (day_view->move_cursor);
+ gdk_cursor_destroy (day_view->resize_width_cursor);
+ gdk_cursor_destroy (day_view->resize_height_cursor);
+
+ e_day_view_free_events (day_view);
+ g_array_free (day_view->long_events, TRUE);
+ for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
+ g_array_free (day_view->events[day], TRUE);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+
+static void
+e_day_view_realize (GtkWidget *widget)
+{
+ EDayView *day_view;
+
+ if (GTK_WIDGET_CLASS (parent_class)->realize)
+ (*GTK_WIDGET_CLASS (parent_class)->realize)(widget);
+
+ day_view = E_DAY_VIEW (widget);
+ day_view->main_gc = gdk_gc_new (widget->window);
+}
+
+
+static void
+e_day_view_unrealize (GtkWidget *widget)
+{
+ EDayView *day_view;
+
+ day_view = E_DAY_VIEW (widget);
+
+ gdk_gc_unref (day_view->main_gc);
+ day_view->main_gc = NULL;
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget);
+}
+
+
+static void
+e_day_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ EDayView *day_view;
+ GdkFont *font;
+ gint top_rows, top_canvas_height;
+ gint month, max_month_width, max_abbr_month_width, number_width;
+ gint hour, max_large_hour_width;
+ gint minute, max_minute_width, i;
+ GDate date;
+ gchar buffer[128];
+ gint times_width;
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ (*GTK_WIDGET_CLASS (parent_class)->style_set)(widget, previous_style);
+
+ day_view = E_DAY_VIEW (widget);
+ font = widget->style->font;
+
+ /* Recalculate the height of each row based on the font size. */
+ day_view->row_height = font->ascent + font->descent + E_DAY_VIEW_EVENT_BORDER_HEIGHT * 2 + E_DAY_VIEW_EVENT_Y_PAD * 2 + 2 /* FIXME */;
+ day_view->row_height = MAX (day_view->row_height, E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2);
+ GTK_LAYOUT (day_view->main_canvas)->vadjustment->step_increment = day_view->row_height;
+
+ day_view->top_row_height = font->ascent + font->descent + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT * 2 + E_DAY_VIEW_LONG_EVENT_Y_PAD * 2 + E_DAY_VIEW_TOP_CANVAS_Y_GAP;
+ day_view->top_row_height = MAX (day_view->top_row_height, E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD + 2 + E_DAY_VIEW_TOP_CANVAS_Y_GAP);
+
+ /* Set the height of the top canvas based on the row height and the
+ number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/
+ top_rows = MAX (1, day_view->rows_in_top_display);
+ top_canvas_height = (top_rows + 2) * day_view->top_row_height;
+ gtk_widget_set_usize (day_view->top_canvas, -1, top_canvas_height);
+
+ /* Find the biggest full month name. */
+ g_date_clear (&date, 1);
+ g_date_set_dmy (&date, 20, 1, 2000);
+ max_month_width = 0;
+ for (month = 1; month <= 12; month++) {
+ g_date_set_month (&date, month);
+
+ g_date_strftime (buffer, 128, "%B", &date);
+ max_month_width = MAX (max_month_width,
+ gdk_string_width (font, buffer));
+
+ g_date_strftime (buffer, 128, "%b", &date);
+ max_abbr_month_width = MAX (max_abbr_month_width,
+ gdk_string_width (font, buffer));
+ }
+ number_width = gdk_string_width (font, "31 ");
+ day_view->long_format_width = number_width + max_month_width
+ + E_DAY_VIEW_DATE_X_PAD;
+ day_view->abbreviated_format_width = number_width
+ + max_abbr_month_width + E_DAY_VIEW_DATE_X_PAD;
+
+ /* Calculate the widths of all the time strings necessary. */
+ for (hour = 0; hour < 24; hour++) {
+ sprintf (buffer, "%02i", hour);
+ day_view->small_hour_widths[hour] = gdk_string_width (font, buffer);
+ day_view->large_hour_widths[hour] = gdk_string_width (day_view->large_font, buffer);
+ day_view->max_small_hour_width = MAX (day_view->max_small_hour_width, day_view->small_hour_widths[hour]);
+ max_large_hour_width = MAX (max_large_hour_width, day_view->large_hour_widths[hour]);
+ }
+ day_view->max_large_hour_width = max_large_hour_width;
+
+ for (minute = 0, i = 0; minute < 60; minute += 5, i++) {
+ sprintf (buffer, "%02i", minute);
+ day_view->minute_widths[i] = gdk_string_width (font, buffer);
+ max_minute_width = MAX (max_minute_width, day_view->minute_widths[i]);
+ }
+ day_view->max_minute_width = max_minute_width;
+ day_view->colon_width = gdk_string_width (font, ":");
+
+ /* Calculate the width of the time column. */
+ times_width = e_day_view_time_item_get_column_width (E_DAY_VIEW_TIME_ITEM (day_view->time_canvas_item));
+ gtk_widget_set_usize (day_view->time_canvas, times_width, -1);
+}
+
+
+/* This recalculates the sizes of each column. */
+static void
+e_day_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ EDayView *day_view;
+ gfloat width, offset;
+ gint col, day;
+ gdouble old_width, old_height, new_width, new_height;
+ gint scroll_y;
+
+ g_print ("In e_day_view_size_allocate\n");
+
+ day_view = E_DAY_VIEW (widget);
+
+ (*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+ /* Calculate the column sizes, using floating point so that pixels
+ get divided evenly. Note that we use one more element than the
+ number of columns, to make it easy to get the column widths. */
+ width = day_view->main_canvas->allocation.width;
+ width /= day_view->days_shown;
+ offset = 0;
+ for (col = 0; col <= day_view->days_shown; col++) {
+ day_view->day_offsets[col] = floor (offset + 0.5);
+ offset += width;
+ }
+
+ /* Calculate the days widths based on the offsets. */
+ for (col = 0; col < day_view->days_shown; col++) {
+ day_view->day_widths[col] = day_view->day_offsets[col + 1] - day_view->day_offsets[col];
+ }
+
+ /* Determine which date format to use, based on the column widths. */
+ if (day_view->day_widths[0] > day_view->long_format_width)
+ day_view->date_format = E_DAY_VIEW_DATE_FULL;
+ else if (day_view->day_widths[0] > day_view->abbreviated_format_width)
+ day_view->date_format = E_DAY_VIEW_DATE_ABBREVIATED;
+ else
+ day_view->date_format = E_DAY_VIEW_DATE_SHORT;
+
+ /* Set the scroll region of the top canvas to its allocated size. */
+ gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->top_canvas),
+ NULL, NULL, &old_width, &old_height);
+ new_width = day_view->top_canvas->allocation.width;
+ new_height = day_view->top_canvas->allocation.height;
+ if (old_width != new_width || old_height != new_height)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->top_canvas),
+ 0, 0, new_width, new_height);
+
+ /* Set the scroll region of the time canvas to its allocated width,
+ but with the height the same as the main canvas. */
+ gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->time_canvas),
+ NULL, NULL, &old_width, &old_height);
+ new_width = day_view->time_canvas->allocation.width;
+ new_height = MAX (day_view->rows * day_view->row_height, day_view->main_canvas->allocation.height);
+ if (old_width != new_width || old_height != new_height)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->time_canvas),
+ 0, 0, new_width, new_height);
+
+ /* Set the scroll region of the main canvas to its allocated width,
+ but with the height depending on the number of rows needed. */
+ gnome_canvas_get_scroll_region (GNOME_CANVAS (day_view->main_canvas),
+ NULL, NULL, &old_width, &old_height);
+ new_width = day_view->main_canvas->allocation.width;
+ if (old_width != new_width || old_height != new_height)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->main_canvas),
+ 0, 0, new_width, new_height);
+
+ /* Scroll to the start of the working day, if this is the initial
+ allocation. */
+ if (day_view->scroll_to_work_day) {
+ scroll_y = e_day_view_convert_time_to_position (day_view, day_view->work_day_start_hour, day_view->work_day_start_minute);
+ gnome_canvas_scroll_to (GNOME_CANVAS (day_view->main_canvas),
+ 0, scroll_y);
+ day_view->scroll_to_work_day = FALSE;
+ }
+
+ /* Flag that we need to reshape the events. Note that changes in height
+ don't matter, since the rows are always the same height. */
+ if (old_width != new_width) {
+ g_print ("Need reshape\n");
+
+ day_view->long_events_need_reshape = TRUE;
+ for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
+ day_view->need_reshape[day] = TRUE;
+
+ e_day_view_check_layout (day_view);
+ }
+}
+
+
+static gint
+e_day_view_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ g_print ("In e_day_view_focus_in\n");
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+ gtk_widget_draw_focus (widget);
+
+ return FALSE;
+}
+
+
+static gint
+e_day_view_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ EDayView *day_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ g_print ("In e_day_view_focus_out\n");
+
+ day_view = E_DAY_VIEW (widget);
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ /* Get rid of selection. */
+ day_view->selection_start_col = -1;
+
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+
+ return FALSE;
+}
+
+
+void
+e_day_view_set_calendar (EDayView *day_view,
+ GnomeCalendar *calendar)
+{
+ day_view->calendar = calendar;
+
+ /* FIXME: free current events? */
+}
+
+
+void
+e_day_view_update_event (EDayView *day_view,
+ iCalObject *ico,
+ int flags)
+{
+ gint day, event_num;
+
+ g_return_if_fail (E_IS_DAY_VIEW (day_view));
+
+ g_print ("In e_day_view_update_event\n");
+
+ /* We only care about events. */
+ if (ico && ico->type != ICAL_EVENT)
+ return;
+
+ /* If one non-recurring event was added, we can just add it. */
+ if (flags == CHANGE_NEW && !ico->recur) {
+ if (ico->dtstart < day_view->upper
+ && ico->dtend > day_view->lower) {
+ e_day_view_add_event (ico, ico->dtstart, ico->dtend,
+ day_view);
+ e_day_view_check_layout (day_view);
+
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+ return;
+
+ /* If only the summary changed, we can update that easily. */
+ } else if (!(flags & ~CHANGE_SUMMARY)) {
+ if (e_day_view_find_event_from_ico (day_view, ico,
+ &day, &event_num)) {
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ e_day_view_update_long_event_label (day_view,
+ event_num);
+ /* For long events we also have to reshape it
+ as the text is centered. */
+ e_day_view_reshape_long_event (day_view,
+ event_num);
+ } else {
+ e_day_view_update_event_label (day_view, day,
+ event_num);
+ }
+
+ return;
+ }
+ }
+
+ e_day_view_reload_events (day_view);
+}
+
+
+/* This updates the text shown for an event. If the event start or end do not
+ lie on a row boundary, the time is displayed before the summary. */
+static void
+e_day_view_update_event_label (EDayView *day_view,
+ gint day,
+ gint event_num)
+{
+ EDayViewEvent *event;
+ gchar *text;
+ gboolean free_text;
+ gint offset, start_minute, end_minute;
+
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ if (event->start_minute % day_view->mins_per_row != 0
+ || event->end_minute % day_view->mins_per_row != 0) {
+ offset = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown;
+ start_minute = offset + event->start_minute;
+ end_minute = offset + event->end_minute;
+ text = g_strdup_printf ("%02i:%02i-%02i:%02i %s",
+ start_minute / 60,
+ start_minute % 60,
+ end_minute / 60,
+ end_minute % 60,
+ event->ico->summary);
+ free_text = TRUE;
+ } else {
+ text = event->ico->summary;
+ free_text = FALSE;
+ }
+
+ gnome_canvas_item_set (event->canvas_item,
+ "text", event->ico->summary ? event->ico->summary : "",
+ NULL);
+
+ if (free_text)
+ g_free (text);
+}
+
+
+static void
+e_day_view_update_long_event_label (EDayView *day_view,
+ gint event_num)
+{
+ EDayViewEvent *event;
+
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ gnome_canvas_item_set (event->canvas_item,
+ "text", event->ico->summary ? event->ico->summary : "",
+ NULL);
+}
+
+
+/* Finds the day and index of the event with the given canvas item.
+ If is is a long event, -1 is returned as the day.
+ Returns TRUE if the event was found. */
+static gboolean
+e_day_view_find_event_from_item (EDayView *day_view,
+ GnomeCanvasItem *item,
+ gint *day_return,
+ gint *event_num_return)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+
+ for (day = 0; day < day_view->days_shown; day++) {
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ event = &g_array_index (day_view->events[day],
+ EDayViewEvent, event_num);
+ if (event->canvas_item == item) {
+ *day_return = day;
+ *event_num_return = event_num;
+ return TRUE;
+ }
+ }
+ }
+
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events,
+ EDayViewEvent, event_num);
+ if (event->canvas_item == item) {
+ *day_return = E_DAY_VIEW_LONG_EVENT;
+ *event_num_return = event_num;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* Finds the day and index of the event containing the iCalObject.
+ If is is a long event, E_DAY_VIEW_LONG_EVENT is returned as the day.
+ Returns TRUE if the event was found. */
+static gboolean
+e_day_view_find_event_from_ico (EDayView *day_view,
+ iCalObject *ico,
+ gint *day_return,
+ gint *event_num_return)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+
+ for (day = 0; day < day_view->days_shown; day++) {
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ event = &g_array_index (day_view->events[day],
+ EDayViewEvent, event_num);
+ if (event->ico == ico) {
+ *day_return = day;
+ *event_num_return = event_num;
+ return TRUE;
+ }
+ }
+ }
+
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events,
+ EDayViewEvent, event_num);
+ if (event->ico == ico) {
+ *day_return = E_DAY_VIEW_LONG_EVENT;
+ *event_num_return = event_num;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* Note that the times must be the start and end of days. */
+void
+e_day_view_set_interval (EDayView *day_view,
+ time_t lower,
+ time_t upper)
+{
+ time_t tmp_lower, day_starts[E_DAY_VIEW_MAX_DAYS + 1];
+ gint day;
+
+ g_return_if_fail (E_IS_DAY_VIEW (day_view));
+
+ g_print ("In e_day_view_set_interval\n");
+
+ if (lower == day_view->lower && upper == day_view->upper)
+ return;
+
+ /* Check that the first time is the start of a day. */
+ tmp_lower = time_day_begin (lower);
+ g_return_if_fail (lower == tmp_lower);
+
+ /* Calculate the start of each day shown, and check that upper is
+ valid. */
+ day_starts[0] = lower;
+ for (day = 1; day <= E_DAY_VIEW_MAX_DAYS; day++) {
+ day_starts[day] = time_add_day (day_starts[day - 1], 1);
+ /* Check if we have reached the upper time. */
+ if (day_starts[day] == upper) {
+ day_view->days_shown = day;
+ break;
+ }
+
+ /* Check that we haven't gone past the upper time. */
+ g_return_if_fail (day_starts[day] < upper);
+ }
+
+ /* Now that we know that lower & upper are valid, update the fields
+ in the EDayView. */
+ day_view->lower = lower;
+ day_view->upper = upper;
+
+ for (day = 0; day <= day_view->days_shown; day++) {
+ day_view->day_starts[day] = day_starts[day];
+ }
+
+ e_day_view_reload_events (day_view);
+}
+
+
+gint
+e_day_view_get_days_shown (EDayView *day_view)
+{
+ g_return_val_if_fail (E_IS_DAY_VIEW (day_view), -1);
+
+ return day_view->days_shown;
+}
+
+
+void
+e_day_view_set_days_shown (EDayView *day_view,
+ gint days_shown)
+{
+ g_return_if_fail (E_IS_DAY_VIEW (day_view));
+ g_return_if_fail (days_shown >= 1);
+ g_return_if_fail (days_shown <= E_DAY_VIEW_MAX_DAYS);
+
+ if (day_view->days_shown != days_shown) {
+ day_view->days_shown = days_shown;
+
+ /* FIXME: Update everything. */
+ }
+}
+
+
+gint
+e_day_view_get_mins_per_row (EDayView *day_view)
+{
+ g_return_val_if_fail (E_IS_DAY_VIEW (day_view), -1);
+
+ return day_view->mins_per_row;
+}
+
+
+void
+e_day_view_set_mins_per_row (EDayView *day_view,
+ gint mins_per_row)
+{
+ g_return_if_fail (E_IS_DAY_VIEW (day_view));
+
+ if (mins_per_row != 5 && mins_per_row != 10 && mins_per_row != 15
+ && mins_per_row != 30 && mins_per_row != 60) {
+ g_warning ("Invalid minutes per row setting");
+ return;
+ }
+
+ if (day_view->mins_per_row != mins_per_row) {
+ day_view->mins_per_row = mins_per_row;
+
+ /* FIXME: Update positions & display. */
+ }
+}
+
+
+/* This recalculates the number of rows to display, based on the time range
+ shown and the minutes per row. */
+static void
+e_day_view_recalc_num_rows (EDayView *day_view)
+{
+ gint hours, minutes, total_minutes;
+
+ hours = day_view->last_hour_shown - day_view->first_hour_shown;
+ /* This could be negative but it works out OK. */
+ minutes = day_view->last_minute_shown - day_view->first_minute_shown;
+ total_minutes = hours * 60 + minutes;
+ day_view->rows = total_minutes / day_view->mins_per_row;
+}
+
+
+/* Converts an hour and minute to a row in the canvas. */
+gint
+e_day_view_convert_time_to_row (EDayView *day_view,
+ gint hour,
+ gint minute)
+{
+ gint total_minutes, start_minute, offset;
+
+ total_minutes = hour * 60 + minute;
+ start_minute = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown;
+ offset = total_minutes - start_minute;
+
+ return offset / day_view->mins_per_row;
+}
+
+
+/* Converts an hour and minute to a y coordinate in the canvas. */
+gint
+e_day_view_convert_time_to_position (EDayView *day_view,
+ gint hour,
+ gint minute)
+{
+ gint total_minutes, start_minute, offset;
+
+ total_minutes = hour * 60 + minute;
+ start_minute = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown;
+ offset = total_minutes - start_minute;
+
+ return offset * day_view->row_height / day_view->mins_per_row;
+}
+
+
+static gboolean
+e_day_view_on_top_canvas_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view)
+{
+ gint event_x, event_y, scroll_x, scroll_y, day, event_num;
+ EDayViewPosition pos;
+
+ g_print ("In e_day_view_on_top_canvas_button_press\n");
+
+ /* Convert the coords to the main canvas window, or return if the
+ window is not found. */
+ if (!e_day_view_convert_event_coords (day_view, (GdkEvent*) event,
+ GTK_LAYOUT (widget)->bin_window,
+ &event_x, &event_y))
+ return FALSE;
+
+ /* The top canvas doesn't scroll, but just in case. */
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ event_x += scroll_x;
+ event_y += scroll_y;
+
+ pos = e_day_view_convert_position_in_top_canvas (day_view,
+ event_x, event_y,
+ &day, &event_num);
+
+ if (pos == E_DAY_VIEW_POS_OUTSIDE)
+ return FALSE;
+
+ if (pos != E_DAY_VIEW_POS_NONE)
+ return e_day_view_on_long_event_button_press (day_view,
+ event_num,
+ event, pos,
+ event_x,
+ event_y);
+
+ e_day_view_stop_editing_event (day_view);
+
+ if (event->button == 1) {
+ if (!GTK_WIDGET_HAS_FOCUS (day_view))
+ gtk_widget_grab_focus (GTK_WIDGET (day_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (widget)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, event->time) == 0) {
+ day_view->selection_start_row = -1;
+ day_view->selection_start_col = day;
+ day_view->selection_end_row = -1;
+ day_view->selection_end_col = day;
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
+ day_view->selection_in_top_canvas = TRUE;
+
+ /* FIXME: Optimise? */
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+ } else if (event->button == 3) {
+ e_day_view_on_event_right_click (day_view, event, -1, -1);
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+e_day_view_convert_event_coords (EDayView *day_view,
+ GdkEvent *event,
+ GdkWindow *window,
+ gint *x_return,
+ gint *y_return)
+{
+ gint event_x, event_y, win_x, win_y;
+ GdkWindow *event_window;;
+
+ /* Get the event window, x & y from the appropriate event struct. */
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ event_x = event->button.x;
+ event_y = event->button.y;
+ event_window = event->button.window;
+ break;
+ case GDK_MOTION_NOTIFY:
+ event_x = event->motion.x;
+ event_y = event->motion.y;
+ event_window = event->motion.window;
+ break;
+ default:
+ /* Shouldn't get here. */
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ while (event_window && event_window != window
+ && event_window != GDK_ROOT_PARENT()) {
+ gdk_window_get_position (event_window, &win_x, &win_y);
+ event_x += win_x;
+ event_y += win_y;
+ event_window = gdk_window_get_parent (event_window);
+ }
+
+ *x_return = event_x;
+ *y_return = event_y;
+
+ if (event_window != window)
+ g_print ("Couldn't find event window\n");
+
+ return (event_window == window) ? TRUE : FALSE;
+}
+
+
+static gboolean
+e_day_view_on_main_canvas_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view)
+{
+ gint event_x, event_y, scroll_x, scroll_y, row, day, event_num;
+ EDayViewPosition pos;
+
+ g_print ("In e_day_view_on_main_canvas_button_press\n");
+
+ /* Convert the coords to the main canvas window, or return if the
+ window is not found. */
+ if (!e_day_view_convert_event_coords (day_view, (GdkEvent*) event,
+ GTK_LAYOUT (widget)->bin_window,
+ &event_x, &event_y))
+ return FALSE;
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ event_x += scroll_x;
+ event_y += scroll_y;
+
+ /* Find out where the mouse is. */
+ pos = e_day_view_convert_position_in_main_canvas (day_view,
+ event_x, event_y,
+ &day, &row,
+ &event_num);
+
+ if (pos == E_DAY_VIEW_POS_OUTSIDE)
+ return FALSE;
+
+ if (pos != E_DAY_VIEW_POS_NONE)
+ return e_day_view_on_event_button_press (day_view, day,
+ event_num, event, pos,
+ event_x, event_y);
+
+ e_day_view_stop_editing_event (day_view);
+
+ /* Start the selection drag. */
+ if (event->button == 1) {
+ if (!GTK_WIDGET_HAS_FOCUS (day_view))
+ gtk_widget_grab_focus (GTK_WIDGET (day_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (widget)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, event->time) == 0) {
+ day_view->selection_start_row = row;
+ day_view->selection_start_col = day;
+ day_view->selection_end_row = row;
+ day_view->selection_end_col = day;
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
+ day_view->selection_in_top_canvas = FALSE;
+
+ /* FIXME: Optimise? */
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+ } else if (event->button == 3) {
+ e_day_view_on_event_right_click (day_view, event, -1, -1);
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+e_day_view_on_long_event_button_press (EDayView *day_view,
+ gint event_num,
+ GdkEventButton *event,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y)
+{
+ if (event->button == 1) {
+ if (event->type == GDK_BUTTON_PRESS) {
+ e_day_view_on_long_event_click (day_view, event_num,
+ event, pos,
+ event_x, event_y);
+ return TRUE;
+ } else if (event->type == GDK_2BUTTON_PRESS) {
+ e_day_view_on_event_double_click (day_view, -1,
+ event_num);
+ return TRUE;
+ }
+ } else if (event->button == 3) {
+ e_day_view_on_event_right_click (day_view, event,
+ E_DAY_VIEW_LONG_EVENT,
+ event_num);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static gboolean
+e_day_view_on_event_button_press (EDayView *day_view,
+ gint day,
+ gint event_num,
+ GdkEventButton *event,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y)
+{
+ if (event->button == 1) {
+ if (event->type == GDK_BUTTON_PRESS) {
+ e_day_view_on_event_click (day_view, day, event_num,
+ event, pos,
+ event_x, event_y);
+ return TRUE;
+ } else if (event->type == GDK_2BUTTON_PRESS) {
+ e_day_view_on_event_double_click (day_view, day,
+ event_num);
+ return TRUE;
+ }
+ } else if (event->button == 3) {
+ e_day_view_on_event_right_click (day_view, event,
+ day, event_num);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static void
+e_day_view_on_long_event_click (EDayView *day_view,
+ gint event_num,
+ GdkEventButton *bevent,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y)
+{
+ EDayViewEvent *event;
+ gint start_day, end_day, day;
+ gint item_x, item_y, item_w, item_h;
+
+ g_print ("In e_day_view_on_long_event_click\n");
+
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ /* Ignore clicks on the EText while editing. */
+ if (pos == E_DAY_VIEW_POS_EVENT
+ && E_TEXT (event->canvas_item)->editing)
+ return;
+
+ if (pos == E_DAY_VIEW_POS_LEFT_EDGE
+ || pos == E_DAY_VIEW_POS_RIGHT_EDGE) {
+ if (!e_day_view_find_long_event_days (day_view, event,
+ &start_day, &end_day))
+ return;
+
+ /* Grab the keyboard focus, so the event being edited is saved
+ and we can use the Escape key to abort the resize. */
+ if (!GTK_WIDGET_HAS_FOCUS (day_view))
+ gtk_widget_grab_focus (GTK_WIDGET (day_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (day_view->top_canvas)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, bevent->time) == 0) {
+
+ day_view->resize_event_day = E_DAY_VIEW_LONG_EVENT;
+ day_view->resize_event_num = event_num;
+ day_view->resize_drag_pos = pos;
+ day_view->resize_start_row = start_day;
+ day_view->resize_end_row = end_day;
+
+ /* Create the edit rect if necessary. */
+ e_day_view_reshape_resize_long_event_rect_item (day_view);
+
+ /* Make sure the text item is on top. */
+ gnome_canvas_item_raise_to_top (day_view->resize_long_event_rect_item);
+
+ /* Raise the event's item, above the rect as well. */
+ gnome_canvas_item_raise_to_top (event->canvas_item);
+ }
+ } else if (e_day_view_get_long_event_position (day_view, event_num,
+ &start_day, &end_day,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ /* Remember the item clicked and the mouse position,
+ so we can start a drag if the mouse moves. */
+ day_view->pressed_event_day = E_DAY_VIEW_LONG_EVENT;
+ day_view->pressed_event_num = event_num;
+
+ day_view->drag_event_x = event_x;
+ day_view->drag_event_y = event_y;
+
+ e_day_view_convert_position_in_top_canvas (day_view,
+ event_x, event_y,
+ &day, NULL);
+ day_view->drag_event_offset = day - start_day;
+ g_print ("Y offset: %i\n", day_view->drag_event_offset);
+ }
+}
+
+
+static void
+e_day_view_on_event_click (EDayView *day_view,
+ gint day,
+ gint event_num,
+ GdkEventButton *bevent,
+ EDayViewPosition pos,
+ gint event_x,
+ gint event_y)
+{
+ EDayViewEvent *event;
+ gint tmp_day, row, start_row;
+
+ g_print ("In e_day_view_on_event_click\n");
+
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* Ignore clicks on the EText while editing. */
+ if (pos == E_DAY_VIEW_POS_EVENT
+ && E_TEXT (event->canvas_item)->editing)
+ return;
+
+ if (pos == E_DAY_VIEW_POS_TOP_EDGE
+ || pos == E_DAY_VIEW_POS_BOTTOM_EDGE) {
+ /* Grab the keyboard focus, so the event being edited is saved
+ and we can use the Escape key to abort the resize. */
+ if (!GTK_WIDGET_HAS_FOCUS (day_view))
+ gtk_widget_grab_focus (GTK_WIDGET (day_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (day_view->main_canvas)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, bevent->time) == 0) {
+
+ day_view->resize_event_day = day;
+ day_view->resize_event_num = event_num;
+ day_view->resize_drag_pos = pos;
+ day_view->resize_start_row = event->start_minute / day_view->mins_per_row;
+ day_view->resize_end_row = (event->end_minute - 1) / day_view->mins_per_row;
+
+ day_view->resize_bars_event_day = day;
+ day_view->resize_bars_event_num = event_num;
+
+ /* Create the edit rect if necessary. */
+ e_day_view_reshape_resize_rect_item (day_view);
+
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+
+ /* Make sure the text item is on top. */
+ gnome_canvas_item_raise_to_top (day_view->resize_rect_item);
+ gnome_canvas_item_raise_to_top (day_view->resize_bar_item);
+
+ /* Raise the event's item, above the rect as well. */
+ gnome_canvas_item_raise_to_top (event->canvas_item);
+ }
+
+ } else {
+ /* Remember the item clicked and the mouse position,
+ so we can start a drag if the mouse moves. */
+ day_view->pressed_event_day = day;
+ day_view->pressed_event_num = event_num;
+
+ day_view->drag_event_x = event_x;
+ day_view->drag_event_y = event_y;
+
+ e_day_view_convert_position_in_main_canvas (day_view,
+ event_x, event_y,
+ &tmp_day, &row,
+ NULL);
+ start_row = event->start_minute / day_view->mins_per_row;
+ day_view->drag_event_offset = row - start_row;
+ g_print ("Y offset: %i Row: %i Start: %i\n",
+ day_view->drag_event_offset, row, start_row);
+ }
+}
+
+
+static void
+e_day_view_reshape_resize_long_event_rect_item (EDayView *day_view)
+{
+ gint day, event_num, start_day, end_day;
+ gint item_x, item_y, item_w, item_h;
+ gdouble x1, y1, x2, y2;
+
+ day = day_view->resize_event_day;
+ event_num = day_view->resize_event_num;
+
+ /* If we're not resizing an event, or the event is not shown,
+ hide the resize bars. */
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_NONE
+ || !e_day_view_get_long_event_position (day_view, event_num,
+ &start_day, &end_day,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ gnome_canvas_item_hide (day_view->resize_long_event_rect_item);
+ return;
+ }
+
+ x1 = item_x;
+ y1 = item_y;
+ x2 = item_x + item_w - 1;
+ y2 = item_y + item_h - 1;
+
+ gnome_canvas_item_set (day_view->resize_long_event_rect_item,
+ "x1", x1,
+ "y1", y1,
+ "x2", x2,
+ "y2", y2,
+ NULL);
+ gnome_canvas_item_show (day_view->resize_long_event_rect_item);
+}
+
+
+static void
+e_day_view_reshape_resize_rect_item (EDayView *day_view)
+{
+ gint day, event_num;
+ gint item_x, item_y, item_w, item_h;
+ gdouble x1, y1, x2, y2;
+
+ day = day_view->resize_event_day;
+ event_num = day_view->resize_event_num;
+
+ /* If we're not resizing an event, or the event is not shown,
+ hide the resize bars. */
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_NONE
+ || !e_day_view_get_event_position (day_view, day, event_num,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ gnome_canvas_item_hide (day_view->resize_rect_item);
+ return;
+ }
+
+ x1 = item_x;
+ y1 = item_y;
+ x2 = item_x + item_w - 1;
+ y2 = item_y + item_h - 1;
+
+ gnome_canvas_item_set (day_view->resize_rect_item,
+ "x1", x1 + E_DAY_VIEW_BAR_WIDTH - 1,
+ "y1", y1,
+ "x2", x2,
+ "y2", y2,
+ NULL);
+ gnome_canvas_item_show (day_view->resize_rect_item);
+
+ gnome_canvas_item_set (day_view->resize_bar_item,
+ "x1", x1,
+ "y1", y1,
+ "x2", x1 + E_DAY_VIEW_BAR_WIDTH - 1,
+ "y2", y2,
+ NULL);
+ gnome_canvas_item_show (day_view->resize_bar_item);
+}
+
+
+static void
+e_day_view_on_event_double_click (EDayView *day_view,
+ gint day,
+ gint event_num)
+{
+ g_print ("In e_day_view_on_event_double_click\n");
+
+}
+
+
+static void
+e_day_view_on_event_right_click (EDayView *day_view,
+ GdkEventButton *bevent,
+ gint day,
+ gint event_num)
+{
+ EDayViewEvent *event;
+ int have_selection, not_being_edited, items, i;
+ struct menu_item *context_menu;
+
+ static struct menu_item main_items[] = {
+ { N_("New appointment..."), (GtkSignalFunc) e_day_view_on_new_appointment, NULL, TRUE }
+ };
+
+ static struct menu_item child_items[] = {
+ { N_("Edit this appointment..."), (GtkSignalFunc) e_day_view_on_edit_appointment, NULL, TRUE },
+ { N_("Delete this appointment"), (GtkSignalFunc) e_day_view_on_delete_appointment, NULL, TRUE },
+ { NULL, NULL, NULL, TRUE },
+ { N_("New appointment..."), (GtkSignalFunc) e_day_view_on_new_appointment, NULL, TRUE }
+ };
+
+ static struct menu_item recur_child_items[] = {
+ { N_("Make this appointment movable"), (GtkSignalFunc) e_day_view_on_unrecur_appointment, NULL, TRUE },
+ { N_("Edit this appointment..."), (GtkSignalFunc) e_day_view_on_edit_appointment, NULL, TRUE },
+ { N_("Delete this occurance"), (GtkSignalFunc) e_day_view_on_delete_occurance, NULL, TRUE },
+ { N_("Delete all occurances"), (GtkSignalFunc) e_day_view_on_delete_appointment, NULL, TRUE },
+ { NULL, NULL, NULL, TRUE },
+ { N_("New appointment..."), (GtkSignalFunc) e_day_view_on_new_appointment, NULL, TRUE }
+ };
+
+ g_print ("In e_day_view_on_event_right_click\n");
+
+ have_selection = (day_view->selection_start_col != -1);
+
+ if (event_num == -1) {
+ items = 1;
+ context_menu = &main_items[0];
+ context_menu[0].sensitive = have_selection;
+ } else {
+ if (day == E_DAY_VIEW_LONG_EVENT)
+ event = &g_array_index (day_view->long_events,
+ EDayViewEvent, event_num);
+ else
+ event = &g_array_index (day_view->events[day],
+ EDayViewEvent, event_num);
+
+ /* Check if the event is being edited in the event editor. */
+ not_being_edited = (event->ico->user_data == NULL);
+
+ if (event->ico->recur) {
+ items = 6;
+ context_menu = &recur_child_items[0];
+ context_menu[3].sensitive = not_being_edited;
+ context_menu[5].sensitive = have_selection;
+ } else {
+ items = 4;
+ context_menu = &child_items[0];
+ context_menu[3].sensitive = have_selection;
+ }
+ /* These settings are common for each context sensitive menu */
+ context_menu[0].sensitive = not_being_edited;
+ context_menu[1].sensitive = not_being_edited;
+ context_menu[2].sensitive = not_being_edited;
+ }
+
+ for (i = 0; i < items; i++)
+ context_menu[i].data = day_view;
+
+ day_view->popup_event_day = day;
+ day_view->popup_event_num = event_num;
+ popup_menu (context_menu, items, bevent);
+}
+
+
+static void
+e_day_view_on_new_appointment (GtkWidget *widget, gpointer data)
+{
+ EDayView *day_view;
+ GtkWidget *event_editor;
+ iCalObject *ico;
+
+ day_view = E_DAY_VIEW (data);
+
+ ico = ical_new ("", user_name, "");
+ ico->new = 1;
+
+ e_day_view_get_selection_range (day_view, &ico->dtstart, &ico->dtend);
+ event_editor = event_editor_new (day_view->calendar, ico);
+ gtk_widget_show (event_editor);
+}
+
+
+static void
+e_day_view_on_edit_appointment (GtkWidget *widget, gpointer data)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ GtkWidget *event_editor;
+
+ day_view = E_DAY_VIEW (data);
+
+ event = e_day_view_get_popup_menu_event (day_view);
+ if (event == NULL)
+ return;
+
+ event_editor = event_editor_new (day_view->calendar, event->ico);
+ gtk_widget_show (event_editor);
+}
+
+
+static void
+e_day_view_on_delete_occurance (GtkWidget *widget, gpointer data)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+
+ day_view = E_DAY_VIEW (data);
+
+ event = e_day_view_get_popup_menu_event (day_view);
+ if (event == NULL)
+ return;
+
+ ical_object_add_exdate (event->ico, event->start);
+ gnome_calendar_object_changed (day_view->calendar, event->ico,
+ CHANGE_DATES);
+ save_default_calendar (day_view->calendar);
+}
+
+
+static void
+e_day_view_on_delete_appointment (GtkWidget *widget, gpointer data)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+
+ day_view = E_DAY_VIEW (data);
+
+ event = e_day_view_get_popup_menu_event (day_view);
+ if (event == NULL)
+ return;
+
+ gnome_calendar_remove_object (day_view->calendar, event->ico);
+ save_default_calendar (day_view->calendar);
+}
+
+
+static void
+e_day_view_on_unrecur_appointment (GtkWidget *widget, gpointer data)
+{
+ EDayView *day_view;
+ EDayViewEvent *event;
+ iCalObject *ico;
+
+ day_view = E_DAY_VIEW (data);
+
+ event = e_day_view_get_popup_menu_event (day_view);
+ if (event == NULL)
+ return;
+
+ /* New object */
+ ico = ical_object_duplicate (event->ico);
+ g_free (ico->recur);
+ ico->recur = 0;
+ ico->dtstart = event->start;
+ ico->dtend = event->end;
+
+ /* Duplicate, and eliminate the recurrency fields */
+ ical_object_add_exdate (event->ico, event->start);
+ gnome_calendar_object_changed (day_view->calendar, event->ico,
+ CHANGE_ALL);
+ gnome_calendar_add_object (day_view->calendar, ico);
+ save_default_calendar (day_view->calendar);
+}
+
+
+static EDayViewEvent*
+e_day_view_get_popup_menu_event (EDayView *day_view)
+{
+ if (day_view->popup_event_num == -1)
+ return NULL;
+
+ if (day_view->popup_event_day == E_DAY_VIEW_LONG_EVENT)
+ return &g_array_index (day_view->long_events,
+ EDayViewEvent,
+ day_view->popup_event_num);
+ else
+ return &g_array_index (day_view->events[day_view->popup_event_day],
+ EDayViewEvent,
+ day_view->popup_event_num);
+}
+
+
+static gboolean
+e_day_view_on_top_canvas_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view)
+{
+
+ g_print ("In e_day_view_on_top_canvas_button_release\n");
+
+ if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
+ gdk_pointer_ungrab (event->time);
+ } else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ e_day_view_finish_long_event_resize (day_view);
+ gdk_pointer_ungrab (event->time);
+ } else if (day_view->pressed_event_day != -1) {
+ e_day_view_start_editing_event (day_view,
+ day_view->pressed_event_day,
+ day_view->pressed_event_num,
+ NULL);
+ }
+
+ day_view->pressed_event_day = -1;
+
+ return FALSE;
+}
+
+
+static gboolean
+e_day_view_on_main_canvas_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EDayView *day_view)
+{
+
+ g_print ("In e_day_view_on_main_canvas_button_release\n");
+
+ if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_NONE;
+ gdk_pointer_ungrab (event->time);
+ e_day_view_stop_auto_scroll (day_view);
+ } else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ e_day_view_finish_resize (day_view);
+ gdk_pointer_ungrab (event->time);
+ e_day_view_stop_auto_scroll (day_view);
+ } else if (day_view->pressed_event_day != -1) {
+ e_day_view_start_editing_event (day_view,
+ day_view->pressed_event_day,
+ day_view->pressed_event_num,
+ NULL);
+ }
+
+ day_view->pressed_event_day = -1;
+
+ return FALSE;
+}
+
+
+static gboolean
+e_day_view_on_top_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *mevent,
+ EDayView *day_view)
+{
+ EDayViewPosition pos;
+ gint event_x, event_y, scroll_x, scroll_y, canvas_x, canvas_y;
+ gint day, event_num;
+ GdkCursor *cursor;
+
+#if 0
+ g_print ("In e_day_view_on_top_canvas_motion\n");
+#endif
+
+ /* Convert the coords to the main canvas window, or return if the
+ window is not found. */
+ if (!e_day_view_convert_event_coords (day_view, (GdkEvent*) mevent,
+ GTK_LAYOUT (widget)->bin_window,
+ &event_x, &event_y))
+ return FALSE;
+
+ /* The top canvas doesn't scroll, but just in case. */
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ canvas_x = event_x + scroll_x;
+ canvas_y = event_y + scroll_y;
+
+ pos = e_day_view_convert_position_in_top_canvas (day_view,
+ canvas_x, canvas_y,
+ &day, &event_num);
+
+ if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
+ e_day_view_update_selection (day_view, -1, day);
+ return TRUE;
+ } else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ e_day_view_update_long_event_resize (day_view, day);
+ return TRUE;
+ }
+ } else if (day_view->pressed_event_day == E_DAY_VIEW_LONG_EVENT) {
+ GtkTargetList *target_list;
+
+ g_print ("Checking whether to start drag - Pressed %i,%i Canvas: %i,%i\n", day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y);
+
+ if (abs (canvas_x - day_view->drag_event_x) > E_DAY_VIEW_DRAG_START_OFFSET
+ || abs (canvas_y - day_view->drag_event_y) > E_DAY_VIEW_DRAG_START_OFFSET) {
+ day_view->drag_event_day = day_view->pressed_event_day;
+ day_view->drag_event_num = day_view->pressed_event_num;
+ day_view->pressed_event_day = -1;
+
+ target_list = gtk_target_list_new (target_table,
+ n_targets);
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE,
+ 1, (GdkEvent*)mevent);
+ gtk_target_list_unref (target_list);
+ }
+ } else {
+ cursor = day_view->normal_cursor;
+ switch (pos) {
+ case E_DAY_VIEW_POS_LEFT_EDGE:
+ case E_DAY_VIEW_POS_RIGHT_EDGE:
+ cursor = day_view->resize_width_cursor;
+ break;
+ default:
+ break;
+ }
+ /* Only set the cursor if it is different to last one set. */
+ if (day_view->last_cursor_set_in_top_canvas != cursor) {
+ day_view->last_cursor_set_in_top_canvas = cursor;
+ gdk_window_set_cursor (widget->window, cursor);
+ }
+
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_day_view_on_main_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *mevent,
+ EDayView *day_view)
+{
+ EDayViewPosition pos;
+ gint event_x, event_y, scroll_x, scroll_y, canvas_x, canvas_y;
+ gint row, day, event_num;
+ GdkCursor *cursor;
+
+#if 0
+ g_print ("In e_day_view_on_main_canvas_motion\n");
+#endif
+
+ /* Convert the coords to the main canvas window, or return if the
+ window is not found. */
+ if (!e_day_view_convert_event_coords (day_view, (GdkEvent*) mevent,
+ GTK_LAYOUT (widget)->bin_window,
+ &event_x, &event_y))
+ return FALSE;
+
+ day_view->last_mouse_x = event_x;
+ day_view->last_mouse_y = event_y;
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ canvas_x = event_x + scroll_x;
+ canvas_y = event_y + scroll_y;
+
+ pos = e_day_view_convert_position_in_main_canvas (day_view,
+ canvas_x, canvas_y,
+ &day, &row,
+ &event_num);
+
+ if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ e_day_view_update_selection (day_view, row, day);
+ e_day_view_check_auto_scroll (day_view, event_y);
+ return TRUE;
+ }
+ } else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ e_day_view_update_resize (day_view, row);
+ e_day_view_check_auto_scroll (day_view, event_y);
+ return TRUE;
+ }
+ } else if (day_view->pressed_event_day != -1) {
+ GtkTargetList *target_list;
+
+ g_print ("Checking whether to start drag - Pressed %i,%i Canvas: %i,%i\n", day_view->drag_event_x, day_view->drag_event_y, canvas_x, canvas_y);
+
+ if (abs (canvas_x - day_view->drag_event_x) > E_DAY_VIEW_DRAG_START_OFFSET
+ || abs (canvas_y - day_view->drag_event_y) > E_DAY_VIEW_DRAG_START_OFFSET) {
+ day_view->drag_event_day = day_view->pressed_event_day;
+ day_view->drag_event_num = day_view->pressed_event_num;
+ day_view->pressed_event_day = -1;
+
+ target_list = gtk_target_list_new (target_table,
+ n_targets);
+ gtk_drag_begin (widget, target_list,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE,
+ 1, (GdkEvent*)mevent);
+ gtk_target_list_unref (target_list);
+ }
+ } else {
+ cursor = day_view->normal_cursor;
+ switch (pos) {
+ case E_DAY_VIEW_POS_LEFT_EDGE:
+ cursor = day_view->move_cursor;
+ break;
+ case E_DAY_VIEW_POS_TOP_EDGE:
+ case E_DAY_VIEW_POS_BOTTOM_EDGE:
+ cursor = day_view->resize_height_cursor;
+ break;
+ default:
+ break;
+ }
+ /* Only set the cursor if it is different to last one set. */
+ if (day_view->last_cursor_set_in_main_canvas != cursor) {
+ day_view->last_cursor_set_in_main_canvas = cursor;
+ gdk_window_set_cursor (widget->window, cursor);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_day_view_update_selection (EDayView *day_view,
+ gint row,
+ gint col)
+{
+ gint tmp_row, tmp_col;
+ gboolean need_redraw = FALSE;
+
+#if 0
+ g_print ("Updating selection %i,%i\n", col, row);
+#endif
+
+ day_view->selection_in_top_canvas = (row == -1) ? TRUE : FALSE;
+
+ if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START) {
+ if (row != day_view->selection_start_row
+ || col != day_view->selection_start_col) {
+ need_redraw = TRUE;
+ day_view->selection_start_row = row;
+ day_view->selection_start_col = col;
+ }
+ } else {
+ if (row != day_view->selection_end_row
+ || col != day_view->selection_end_col) {
+ need_redraw = TRUE;
+ day_view->selection_end_row = row;
+ day_view->selection_end_col = col;
+ }
+ }
+
+ /* Switch the drag position if necessary. */
+ if (day_view->selection_start_col > day_view->selection_end_col
+ || (day_view->selection_start_col == day_view->selection_end_col
+ && day_view->selection_start_row > day_view->selection_end_row)) {
+ tmp_row = day_view->selection_start_row;
+ tmp_col = day_view->selection_start_col;
+ day_view->selection_start_col = day_view->selection_end_col;
+ day_view->selection_start_row = day_view->selection_end_row;
+ day_view->selection_end_col = tmp_col;
+ day_view->selection_end_row = tmp_row;
+ if (day_view->selection_drag_pos == E_DAY_VIEW_DRAG_START)
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_END;
+ else
+ day_view->selection_drag_pos = E_DAY_VIEW_DRAG_START;
+ }
+
+ /* FIXME: Optimise? */
+ if (need_redraw) {
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+}
+
+
+static void
+e_day_view_update_long_event_resize (EDayView *day_view,
+ gint day)
+{
+ EDayViewEvent *event;
+ gint event_num;
+ gboolean need_reshape = FALSE;
+
+#if 1
+ g_print ("Updating resize Day:%i\n", day);
+#endif
+
+ event_num = day_view->resize_event_num;
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_LEFT_EDGE) {
+ day = MIN (day, day_view->resize_end_row);
+ if (day != day_view->resize_start_row) {
+ need_reshape = TRUE;
+ day_view->resize_start_row = day;
+
+ }
+ } else {
+ day = MAX (day, day_view->resize_start_row);
+ if (day != day_view->resize_end_row) {
+ need_reshape = TRUE;
+ day_view->resize_end_row = day;
+ }
+ }
+
+ /* FIXME: Optimise? */
+ if (need_reshape) {
+ e_day_view_reshape_long_event (day_view, event_num);
+ e_day_view_reshape_resize_long_event_rect_item (day_view);
+ gtk_widget_queue_draw (day_view->top_canvas);
+ }
+}
+
+
+static void
+e_day_view_update_resize (EDayView *day_view,
+ gint row)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+ gboolean need_reshape = FALSE;
+
+#if 0
+ g_print ("Updating resize Row:%i\n", row);
+#endif
+
+ day = day_view->resize_event_day;
+ event_num = day_view->resize_event_num;
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_TOP_EDGE) {
+ row = MIN (row, day_view->resize_end_row);
+ if (row != day_view->resize_start_row) {
+ need_reshape = TRUE;
+ day_view->resize_start_row = row;
+
+ }
+ } else {
+ row = MAX (row, day_view->resize_start_row);
+ if (row != day_view->resize_end_row) {
+ need_reshape = TRUE;
+ day_view->resize_end_row = row;
+ }
+ }
+
+ /* FIXME: Optimise? */
+ if (need_reshape) {
+ e_day_view_reshape_day_event (day_view, day, event_num);
+ e_day_view_reshape_resize_rect_item (day_view);
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+}
+
+
+/* This converts the resize start or end row back to a time and updates the
+ event. */
+static void
+e_day_view_finish_long_event_resize (EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint event_num;
+
+
+ g_print ("In e_day_view_finish_long_event_resize\n");
+
+ event_num = day_view->resize_event_num;
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_LEFT_EDGE) {
+ event->ico->dtstart = day_view->day_starts[day_view->resize_start_row];
+ } else {
+ event->ico->dtend = day_view->day_starts[day_view->resize_end_row + 1];
+ }
+
+ gnome_canvas_item_hide (day_view->resize_long_event_rect_item);
+
+ day_view->resize_drag_pos = E_DAY_VIEW_POS_NONE;
+
+ /* Notify calendar of change */
+ gnome_calendar_object_changed (day_view->calendar, event->ico,
+ CHANGE_DATES);
+ save_default_calendar (day_view->calendar);
+}
+
+
+/* This converts the resize start or end row back to a time and updates the
+ event. */
+static void
+e_day_view_finish_resize (EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+
+
+ g_print ("In e_day_view_finish_resize\n");
+
+ day = day_view->resize_event_day;
+ event_num = day_view->resize_event_num;
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_TOP_EDGE) {
+ event->ico->dtstart = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_start_row);
+ } else {
+ event->ico->dtend = e_day_view_convert_grid_position_to_time (day_view, day, day_view->resize_end_row + 1);
+ }
+
+ gnome_canvas_item_hide (day_view->resize_rect_item);
+ gnome_canvas_item_hide (day_view->resize_bar_item);
+
+ /* Hide the horizontal bars. */
+ g_print ("Hiding resize bars\n");
+ day_view->resize_bars_event_day = -1;
+ day_view->resize_bars_event_num = -1;
+ gnome_canvas_item_hide (day_view->main_canvas_top_resize_bar_item);
+ gnome_canvas_item_hide (day_view->main_canvas_bottom_resize_bar_item);
+
+ day_view->resize_drag_pos = E_DAY_VIEW_POS_NONE;
+
+ /* Notify calendar of change */
+ gnome_calendar_object_changed (day_view->calendar, event->ico,
+ CHANGE_DATES);
+ save_default_calendar (day_view->calendar);
+}
+
+
+static void
+e_day_view_abort_resize (EDayView *day_view,
+ guint32 time)
+{
+ gint day, event_num;
+
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_NONE)
+ return;
+
+ g_print ("In e_day_view_abort_resize\n");
+
+ day_view->resize_drag_pos = E_DAY_VIEW_POS_NONE;
+ gdk_pointer_ungrab (time);
+
+ day = day_view->resize_event_day;
+ event_num = day_view->resize_event_num;
+
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ e_day_view_reshape_long_event (day_view, event_num);
+ gtk_widget_queue_draw (day_view->top_canvas);
+
+ day_view->last_cursor_set_in_top_canvas = day_view->normal_cursor;
+ gdk_window_set_cursor (day_view->top_canvas->window,
+ day_view->normal_cursor);
+ gnome_canvas_item_hide (day_view->resize_long_event_rect_item);
+ } else {
+ e_day_view_reshape_day_event (day_view, day, event_num);
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+ gtk_widget_queue_draw (day_view->main_canvas);
+
+ day_view->last_cursor_set_in_main_canvas = day_view->normal_cursor;
+ gdk_window_set_cursor (day_view->main_canvas->window,
+ day_view->normal_cursor);
+ gnome_canvas_item_hide (day_view->resize_rect_item);
+ gnome_canvas_item_hide (day_view->resize_bar_item);
+ }
+}
+
+
+static void
+e_day_view_reload_events (EDayView *day_view)
+{
+ e_day_view_free_events (day_view);
+
+ /* Reset all our indices. */
+ day_view->editing_event_day = -1;
+ day_view->popup_event_day = -1;
+ day_view->resize_bars_event_day = -1;
+ day_view->resize_event_day = -1;
+ day_view->pressed_event_day = -1;
+ day_view->drag_event_day = -1;
+
+ if (day_view->calendar) {
+ calendar_iterate (day_view->calendar->cal,
+ day_view->lower,
+ day_view->upper,
+ e_day_view_add_event,
+ day_view);
+ }
+
+ e_day_view_check_layout (day_view);
+
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+}
+
+
+static void
+e_day_view_free_events (EDayView *day_view)
+{
+ gint day;
+
+ e_day_view_free_event_array (day_view, day_view->long_events);
+
+ for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
+ e_day_view_free_event_array (day_view, day_view->events[day]);
+}
+
+
+static void
+e_day_view_free_event_array (EDayView *day_view,
+ GArray *array)
+{
+ EDayViewEvent *event;
+ gint event_num;
+
+ for (event_num = 0; event_num < array->len; event_num++) {
+ event = &g_array_index (array, EDayViewEvent, event_num);
+ if (event->canvas_item)
+ gtk_object_destroy (GTK_OBJECT (event->canvas_item));
+ }
+
+ g_array_set_size (array, 0);
+}
+
+
+/* This adds one event to the view, adding it to the appropriate array. */
+static int
+e_day_view_add_event (iCalObject *ico,
+ time_t start,
+ time_t end,
+ gpointer data)
+
+{
+ EDayView *day_view;
+ EDayViewEvent event;
+ gint day;
+ struct tm start_tm, end_tm;
+
+ day_view = E_DAY_VIEW (data);
+
+ /* Check that the event times are valid. */
+ g_return_val_if_fail (start <= end, TRUE);
+ g_return_val_if_fail (start < day_view->upper, TRUE);
+ g_return_val_if_fail (end > day_view->lower, TRUE);
+
+ start_tm = *(localtime (&start));
+ end_tm = *(localtime (&end));
+
+ event.ico = ico;
+ event.start = start;
+ event.end = end;
+ event.canvas_item = NULL;
+
+ /* Calculate the start & end minute, relative to the
+ top of the display. FIXME. */
+ event.start_minute = start_tm.tm_hour * 60 + start_tm.tm_min;
+ event.end_minute = end_tm.tm_hour * 60 + end_tm.tm_min;
+
+ event.start_row_or_col = -1;
+ event.num_columns = -1;
+
+ /* Find out which array to add the event to. */
+ for (day = 0; day < day_view->days_shown; day++) {
+ if (start >= day_view->day_starts[day]
+ && end <= day_view->day_starts[day + 1]) {
+
+ /* Special case for when the appointment ends at
+ midnight, i.e. the start of the next day. */
+ if (end == day_view->day_starts[day + 1]) {
+
+ /* If the event last the entire day, then we
+ skip it here so it gets added to the top
+ canvas. */
+ if (start == day_view->day_starts[day])
+ break;
+
+ event.end_minute = 24 * 60;
+ }
+
+ g_array_append_val (day_view->events[day], event);
+ day_view->events_sorted[day] = FALSE;
+ day_view->need_layout[day] = TRUE;
+ return TRUE;
+ }
+ }
+
+ /* The event wasn't within one day so it must be a long event,
+ i.e. shown in the top canvas. */
+ g_array_append_val (day_view->long_events, event);
+ day_view->long_events_sorted = FALSE;
+ day_view->long_events_need_layout = TRUE;
+ return TRUE;
+}
+
+
+/* This lays out the short (less than 1 day) events in the columns.
+ Any long events are simply skipped. */
+void
+e_day_view_check_layout (EDayView *day_view)
+{
+ gint day;
+
+ /* Don't bother if we aren't visible. */
+ if (!GTK_WIDGET_VISIBLE (day_view))
+ return;
+
+ /* Make sure the events are sorted (by start and size). */
+ e_day_view_ensure_events_sorted (day_view);
+
+ for (day = 0; day < day_view->days_shown; day++) {
+ if (day_view->need_layout[day])
+ e_day_view_layout_day_events (day_view, day);
+
+ if (day_view->need_layout[day]
+ || day_view->need_reshape[day]) {
+ e_day_view_reshape_day_events (day_view, day);
+
+ if (day_view->resize_bars_event_day == day)
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+ }
+
+ day_view->need_layout[day] = FALSE;
+ day_view->need_reshape[day] = FALSE;
+ }
+
+ if (day_view->long_events_need_layout)
+ e_day_view_layout_long_events (day_view);
+
+ if (day_view->long_events_need_layout
+ || day_view->long_events_need_reshape)
+ e_day_view_reshape_long_events (day_view);
+
+ day_view->long_events_need_layout = FALSE;
+ day_view->long_events_need_reshape = FALSE;
+}
+
+
+static void
+e_day_view_layout_long_events (EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint event_num, old_rows_in_top_display, top_canvas_height, top_rows;
+ guint8 *grid;
+
+ /* This is a temporary 2-d grid which is used to place events.
+ Each element is 0 if the position is empty, or 1 if occupied.
+ We allocate the maximum size possible here, assuming that each
+ event will need its own row. */
+ grid = g_new0 (guint8,
+ day_view->long_events->len * E_DAY_VIEW_MAX_DAYS);
+
+ /* Reset the number of rows in the top display to 0. It will be
+ updated as events are layed out below. */
+ old_rows_in_top_display = day_view->rows_in_top_display;
+ day_view->rows_in_top_display = 0;
+
+ /* Iterate over the events, finding which days they cover, and putting
+ them in the first free row available. */
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events,
+ EDayViewEvent, event_num);
+ e_day_view_layout_long_event (day_view, event, grid);
+ }
+
+ /* Free the grid. */
+ g_free (grid);
+
+ /* Set the height of the top canvas based on the row height and the
+ number of rows needed (min 1 + 1 for the dates + 1 space for DnD).*/
+ if (day_view->rows_in_top_display != old_rows_in_top_display) {
+ top_rows = MAX (1, day_view->rows_in_top_display);
+ top_canvas_height = (top_rows + 2) * day_view->top_row_height;
+ gtk_widget_set_usize (day_view->top_canvas, -1,
+ top_canvas_height);
+ }
+}
+
+
+static void
+e_day_view_layout_long_event (EDayView *day_view,
+ EDayViewEvent *event,
+ guint8 *grid)
+{
+ gint start_day, end_day, free_row, day, row;
+
+ event->num_columns = 0;
+
+ if (!e_day_view_find_long_event_days (day_view, event,
+ &start_day, &end_day))
+ return;
+
+ /* Try each row until we find a free one. */
+ row = 0;
+ do {
+ free_row = row;
+ for (day = start_day; day <= end_day; day++) {
+ if (grid[row * E_DAY_VIEW_MAX_DAYS + day]) {
+ free_row = -1;
+ break;
+ }
+ }
+ row++;
+ } while (free_row == -1);
+
+ event->start_row_or_col = free_row;
+ event->num_columns = 1;
+
+ /* Mark the cells as full. */
+ for (day = start_day; day <= end_day; day++) {
+ grid[free_row * E_DAY_VIEW_MAX_DAYS + day] = 1;
+ }
+
+ /* Update the number of rows in the top canvas if necessary. */
+ day_view->rows_in_top_display = MAX (day_view->rows_in_top_display,
+ free_row + 1);
+}
+
+
+static void
+e_day_view_reshape_long_events (EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint event_num;
+
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ if (event->num_columns == 0) {
+ if (event->canvas_item) {
+ gtk_object_destroy (GTK_OBJECT (event->canvas_item));
+ event->canvas_item = NULL;
+ }
+ } else {
+ e_day_view_reshape_long_event (day_view, event_num);
+ }
+ }
+}
+
+
+static void
+e_day_view_reshape_long_event (EDayView *day_view,
+ gint event_num)
+{
+ EDayViewEvent *event;
+ gint start_day, end_day, item_x, item_y, item_w, item_h;
+ gint text_x, text_w, num_icons, icons_width, width, time_width;
+ iCalObject *ico;
+ gint min_text_x, max_text_w;
+ gdouble text_width;
+ gboolean show_icons = TRUE, use_max_width = FALSE;
+
+ if (!e_day_view_get_long_event_position (day_view, event_num,
+ &start_day, &end_day,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ if (event->canvas_item) {
+ gtk_object_destroy (GTK_OBJECT (event->canvas_item));
+ event->canvas_item = NULL;
+ }
+ return;
+ }
+
+ /* Take off the border and padding. */
+ item_x += E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD;
+ item_w -= (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2;
+ item_y += E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD;
+ item_h -= (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2;
+
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+ /* We don't show the icons while resizing, since we'd have to
+ draw them on top of the resize rect. Nor when editing. */
+ num_icons = 0;
+ ico = event->ico;
+
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
+ && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->resize_event_num == event_num)
+ show_icons = FALSE;
+
+ if (day_view->editing_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->editing_event_num == event_num) {
+ g_print ("Reshaping long event which is being edited.\n");
+ show_icons = FALSE;
+ use_max_width = TRUE;
+ }
+
+ if (show_icons) {
+ if (ico->dalarm.enabled || ico->malarm.enabled
+ || ico->palarm.enabled || ico->aalarm.enabled)
+ num_icons++;
+ if (ico->recur)
+ num_icons++;
+ }
+
+ /* FIXME: Handle item_w & item_h <= 0. */
+
+ if (!event->canvas_item) {
+ event->canvas_item =
+ gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->top_canvas)->root),
+ e_text_get_type (),
+ "font_gdk", GTK_WIDGET (day_view)->style->font,
+ "anchor", GTK_ANCHOR_NW,
+ "clip", TRUE,
+ "max_lines", 1,
+ "editable", TRUE,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (event->canvas_item), "event",
+ GTK_SIGNAL_FUNC (e_day_view_on_text_item_event),
+ day_view);
+ e_day_view_update_long_event_label (day_view, event_num);
+ }
+
+ /* Calculate its position. We first calculate the ideal position which
+ is centered with the icons. We then make sure we haven't gone off
+ the left edge of the available space. Finally we make sure we don't
+ go off the right edge. */
+ icons_width = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD)
+ * num_icons;
+ time_width = day_view->max_small_hour_width + day_view->colon_width
+ + day_view->max_minute_width;
+
+ if (use_max_width) {
+ text_x = item_x;
+ text_w = item_w;
+ } else {
+ /* Get the requested size of the label. */
+ gtk_object_get (GTK_OBJECT (event->canvas_item),
+ "text_width", &text_width,
+ NULL);
+
+ width = text_width + icons_width;
+ text_x = item_x + (item_w - width) / 2;
+
+ min_text_x = item_x;
+ if (event->start > day_view->day_starts[start_day])
+ min_text_x += time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
+
+ text_x = MAX (text_x, min_text_x);
+
+ max_text_w = item_x + item_w - text_x;
+ if (event->end < day_view->day_starts[end_day + 1])
+ max_text_w -= time_width + E_DAY_VIEW_LONG_EVENT_TIME_X_PAD;
+
+ text_w = MIN (width, max_text_w);
+ }
+
+ /* Now take out the space for the icons. */
+ text_x += icons_width;
+ text_w -= icons_width;
+
+ gnome_canvas_item_set (event->canvas_item,
+ "x", (gdouble) text_x,
+ "y", (gdouble) item_y,
+ "clip_width", (gdouble) text_w,
+ "clip_height", (gdouble) item_h,
+ NULL);
+}
+
+
+/* Find the start and end days for the event. */
+gboolean
+e_day_view_find_long_event_days (EDayView *day_view,
+ EDayViewEvent *event,
+ gint *start_day_return,
+ gint *end_day_return)
+{
+ gint day, start_day, end_day;
+
+ start_day = -1;
+ end_day = -1;
+
+ for (day = 0; day < day_view->days_shown; day++) {
+ if (start_day == -1
+ && event->start < day_view->day_starts[day + 1])
+ start_day = day;
+ if (event->end > day_view->day_starts[day])
+ end_day = day;
+ }
+
+ /* Sanity check. */
+ if (start_day < 0 || start_day >= day_view->days_shown
+ || end_day < 0 || end_day >= day_view->days_shown
+ || end_day < start_day) {
+ g_warning ("Invalid date range for event");
+ return FALSE;
+ }
+
+ *start_day_return = start_day;
+ *end_day_return = end_day;
+
+ return TRUE;
+}
+
+
+static void
+e_day_view_layout_day_events (EDayView *day_view,
+ gint day)
+{
+ EDayViewEvent *event;
+ gint row, event_num;
+ guint8 *grid;
+
+ /* This is a temporary array which keeps track of rows which are
+ connected. When an appointment spans multiple rows then the number
+ of columns in each of these rows must be the same (i.e. the maximum
+ of all of them). Each element in the array corresponds to one row
+ and contains the index of the first row in the group of connected
+ rows. */
+ guint16 group_starts[12 * 24];
+
+ /* Reset the cols_per_row array, and initialize the connected rows. */
+ for (row = 0; row < day_view->rows; row++) {
+ day_view->cols_per_row[day][row] = 0;
+ group_starts[row] = row;
+ }
+
+ /* This is a temporary 2-d grid which is used to place events.
+ Each element is 0 if the position is empty, or 1 if occupied. */
+ grid = g_new0 (guint8, day_view->rows * E_DAY_VIEW_MAX_COLUMNS);
+
+
+ /* Iterate over the events, finding which rows they cover, and putting
+ them in the first free column available. Increment the number of
+ events in each of the rows it covers, and make sure they are all
+ in one group. */
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ e_day_view_layout_day_event (day_view, day, event,
+ grid, group_starts);
+ }
+
+ /* Recalculate the number of columns needed in each row. */
+ e_day_view_recalc_cols_per_row (day_view, day, group_starts);
+
+ /* Iterate over the events again, trying to expand events horizontally
+ if there is enough space. */
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+ e_day_view_expand_day_event (day_view, day, event, grid);
+ }
+
+ /* Free the grid. */
+ g_free (grid);
+}
+
+
+/* Finds the first free position to place the event in.
+ Increments the number of events in each of the rows it covers, and makes
+ sure they are all in one group. */
+static void
+e_day_view_layout_day_event (EDayView *day_view,
+ gint day,
+ EDayViewEvent *event,
+ guint8 *grid,
+ guint16 *group_starts)
+{
+ gint start_row, end_row, free_col, col, row, group_start;
+
+ start_row = event->start_minute / day_view->mins_per_row;
+ end_row = (event->end_minute - 1) / day_view->mins_per_row;
+
+ /* Try each column until we find a free one. */
+ for (col = 0; col < E_DAY_VIEW_MAX_COLUMNS; col++) {
+ free_col = col;
+ for (row = start_row; row <= end_row; row++) {
+ if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) {
+ free_col = -1;
+ break;
+ }
+ }
+
+ if (free_col != -1)
+ break;
+ }
+
+ /* If we can't find space for the event, mark it as not displayed. */
+ if (free_col == -1) {
+ event->num_columns = 0;
+ return;
+ }
+
+ /* The event is assigned 1 col initially, but may be expanded later. */
+ event->start_row_or_col = free_col;
+ event->num_columns = 1;
+
+ /* Determine the start index of the group. */
+ group_start = group_starts[start_row];
+
+ /* Increment number of events in each of the rows the event covers.
+ We use the cols_per_row array for this. It will be sorted out after
+ all the events have been layed out. Also make sure all the rows that
+ the event covers are in one group. */
+ for (row = start_row; row <= end_row; row++) {
+ grid[row * E_DAY_VIEW_MAX_COLUMNS + free_col] = 1;
+ day_view->cols_per_row[day][row]++;
+ group_starts[row] = group_start;
+ }
+
+ /* If any following rows should be in the same group, add them. */
+ for (row = end_row + 1; row < day_view->rows; row++) {
+ if (group_starts[row] > end_row)
+ break;
+ group_starts[row] = group_start;
+ }
+}
+
+
+/* For each group of rows, find the max number of events in all the
+ rows, and set the number of cols in each of the rows to that. */
+static void
+e_day_view_recalc_cols_per_row (EDayView *day_view,
+ gint day,
+ guint16 *group_starts)
+{
+ gint start_row = 0, row, next_start_row, max_events;
+
+ while (start_row < day_view->rows) {
+
+ max_events = 0;
+ for (row = start_row; row < day_view->rows && group_starts[row] == start_row; row++)
+ max_events = MAX (max_events, day_view->cols_per_row[day][row]);
+
+ next_start_row = row;
+
+ for (row = start_row; row < next_start_row; row++)
+ day_view->cols_per_row[day][row] = max_events;
+
+ start_row = next_start_row;
+ }
+}
+
+
+/* Expands the event horizontally to fill any free space. */
+static void
+e_day_view_expand_day_event (EDayView *day_view,
+ gint day,
+ EDayViewEvent *event,
+ guint8 *grid)
+{
+ gint start_row, end_row, col, row;
+ gboolean clashed;
+
+ start_row = event->start_minute / day_view->mins_per_row;
+ end_row = (event->end_minute - 1) / day_view->mins_per_row;
+
+ /* Try each column until we find a free one. */
+ clashed = FALSE;
+ for (col = event->start_row_or_col + 1; col < day_view->cols_per_row[day][start_row]; col++) {
+ for (row = start_row; row <= end_row; row++) {
+ if (grid[row * E_DAY_VIEW_MAX_COLUMNS + col]) {
+ clashed = TRUE;
+ break;
+ }
+ }
+
+ if (clashed)
+ break;
+
+ event->num_columns++;
+ }
+}
+
+
+/* This creates or updates the sizes of the canvas items for one day of the
+ main canvas. */
+static void
+e_day_view_reshape_day_events (EDayView *day_view,
+ gint day)
+{
+ gint event_num;
+
+ g_print ("In e_day_view_reshape_day_events\n");
+
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ e_day_view_reshape_day_event (day_view, day, event_num);
+ }
+}
+
+
+static void
+e_day_view_reshape_day_event (EDayView *day_view,
+ gint day,
+ gint event_num)
+{
+ EDayViewEvent *event;
+ gint item_x, item_y, item_w, item_h;
+ gint num_icons, icons_offset;
+ iCalObject *ico;
+
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+ ico = event->ico;
+
+ if (!e_day_view_get_event_position (day_view, day, event_num,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ if (event->canvas_item) {
+ gtk_object_destroy (GTK_OBJECT (event->canvas_item));
+ event->canvas_item = NULL;
+ }
+ } else {
+ /* Skip the border and padding. */
+ item_x += E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD;
+ item_w -= E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD * 2;
+ item_y += E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD;
+ item_h -= (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2;
+
+ /* We don't show the icons while resizing, since we'd have to
+ draw them on top of the resize rect. */
+ num_icons = 0;
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_NONE
+ || day_view->resize_event_day != day
+ || day_view->resize_event_num != event_num) {
+ if (ico->dalarm.enabled || ico->malarm.enabled
+ || ico->palarm.enabled || ico->aalarm.enabled)
+ num_icons++;
+ if (ico->recur)
+ num_icons++;
+ }
+
+ if (num_icons > 0) {
+ if (item_h >= (E_DAY_VIEW_ICON_HEIGHT + E_DAY_VIEW_ICON_Y_PAD) * num_icons)
+ icons_offset = E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD * 2;
+ else
+ icons_offset = (E_DAY_VIEW_ICON_WIDTH + E_DAY_VIEW_ICON_X_PAD) * num_icons + E_DAY_VIEW_ICON_X_PAD;
+ item_x += icons_offset;
+ item_w -= icons_offset;
+ }
+
+ /* FIXME: Handle item_w & item_h <= 0. */
+
+ if (!event->canvas_item) {
+ event->canvas_item =
+ gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (day_view->main_canvas)->root),
+ e_text_get_type (),
+ "font_gdk", GTK_WIDGET (day_view)->style->font,
+ "anchor", GTK_ANCHOR_NW,
+ "line_wrap", TRUE,
+ "editable", TRUE,
+ "clip", TRUE,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (event->canvas_item),
+ "event",
+ GTK_SIGNAL_FUNC (e_day_view_on_text_item_event),
+ day_view);
+ e_day_view_update_event_label (day_view, day,
+ event_num);
+ }
+
+ gnome_canvas_item_set (event->canvas_item,
+ "x", (gdouble) item_x,
+ "y", (gdouble) item_y,
+ "clip_width", (gdouble) item_w,
+ "clip_height", (gdouble) item_h,
+ NULL);
+ }
+}
+
+
+/* This creates or resizes the horizontal bars used to resize events in the
+ main canvas. */
+static void
+e_day_view_reshape_main_canvas_resize_bars (EDayView *day_view)
+{
+ gint day, event_num;
+ gint item_x, item_y, item_w, item_h;
+ gdouble x, y, w, h;
+
+ day = day_view->resize_bars_event_day;
+ event_num = day_view->resize_bars_event_num;
+
+ /* If we're not editing an event, or the event is not shown,
+ hide the resize bars. */
+ if (day != -1 && day == day_view->drag_event_day
+ && event_num == day_view->drag_event_num) {
+ gtk_object_get (GTK_OBJECT (day_view->drag_rect_item),
+ "x1", &x,
+ "y1", &y,
+ "x2", &w,
+ "y2", &h,
+ NULL);
+ w -= x;
+ x++;
+ h -= y;
+ } else if (day != -1
+ && e_day_view_get_event_position (day_view, day, event_num,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ x = item_x + E_DAY_VIEW_BAR_WIDTH;
+ y = item_y;
+ w = item_w - E_DAY_VIEW_BAR_WIDTH;
+ h = item_h;
+ } else {
+ gnome_canvas_item_hide (day_view->main_canvas_top_resize_bar_item);
+ gnome_canvas_item_hide (day_view->main_canvas_bottom_resize_bar_item);
+ return;
+ }
+
+ gnome_canvas_item_set (day_view->main_canvas_top_resize_bar_item,
+ "x1", x - E_DAY_VIEW_BAR_WIDTH,
+ "y1", y - E_DAY_VIEW_BAR_HEIGHT,
+ "x2", x + w - 1,
+ "y2", y - 1,
+ NULL);
+ gnome_canvas_item_show (day_view->main_canvas_top_resize_bar_item);
+
+ gnome_canvas_item_set (day_view->main_canvas_bottom_resize_bar_item,
+ "x1", x - E_DAY_VIEW_BAR_WIDTH,
+ "y1", y + h,
+ "x2", x + w - 1,
+ "y2", y + h + E_DAY_VIEW_BAR_HEIGHT - 1,
+ NULL);
+ gnome_canvas_item_show (day_view->main_canvas_bottom_resize_bar_item);
+}
+
+
+static void
+e_day_view_ensure_events_sorted (EDayView *day_view)
+{
+ gint day;
+
+ /* Sort the long events. */
+ if (!day_view->long_events_sorted) {
+ qsort (day_view->long_events->data,
+ day_view->long_events->len,
+ sizeof (EDayViewEvent),
+ e_day_view_event_sort_func);
+ day_view->long_events_sorted = TRUE;
+ }
+
+ /* Sort the events for each day. */
+ for (day = 0; day < day_view->days_shown; day++) {
+ if (!day_view->events_sorted[day]) {
+ qsort (day_view->events[day]->data,
+ day_view->events[day]->len,
+ sizeof (EDayViewEvent),
+ e_day_view_event_sort_func);
+ day_view->events_sorted[day] = TRUE;
+ }
+ }
+}
+
+
+static gint
+e_day_view_event_sort_func (const void *arg1,
+ const void *arg2)
+{
+ EDayViewEvent *event1, *event2;
+
+ event1 = (EDayViewEvent*) arg1;
+ event2 = (EDayViewEvent*) arg2;
+
+ if (event1->start < event2->start)
+ return -1;
+ if (event1->start > event2->start)
+ return 1;
+
+ if (event1->end > event2->end)
+ return -1;
+ if (event1->end < event2->end)
+ return 1;
+
+ return 0;
+}
+
+
+static gint
+e_day_view_key_press (GtkWidget *widget, GdkEventKey *event)
+{
+ EDayView *day_view;
+ iCalObject *ico;
+ gint day, event_num;
+ gchar *initial_text;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ day_view = E_DAY_VIEW (widget);
+
+ g_print ("In e_day_view_key_press\n");
+
+ /* The Escape key aborts a resize operation. */
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ if (event->keyval == GDK_Escape) {
+ e_day_view_abort_resize (day_view, event->time);
+ }
+ return FALSE;
+ }
+
+
+ if (day_view->selection_start_col == -1)
+ return FALSE;
+
+ /* We only want to start an edit with a return key or a simple
+ character. */
+ if (event->keyval == GDK_Return) {
+ initial_text = NULL;
+ } else if ((event->keyval < 0x20)
+ || (event->keyval > 0xFF)
+ || (event->length == 0)
+ || (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))) {
+ return FALSE;
+ } else {
+ initial_text = event->string;
+ }
+
+ /* Add a new event covering the selected range.
+ Note that user_name is a global variable. */
+ ico = ical_new ("", user_name, "");
+ ico->new = 1;
+
+ e_day_view_get_selection_range (day_view, &ico->dtstart, &ico->dtend);
+
+ gnome_calendar_add_object (day_view->calendar, ico);
+ save_default_calendar (day_view->calendar);
+
+ /* gnome_calendar_add_object() should have resulted in a call to
+ e_day_view_update_event(), so the new event should now be layed out.
+ So we try to find it so we can start editing it. */
+ if (e_day_view_find_event_from_ico (day_view, ico, &day, &event_num)) {
+ /* Start editing the new event. */
+ e_day_view_start_editing_event (day_view, day, event_num,
+ initial_text);
+ }
+
+ return TRUE;
+}
+
+
+static void
+e_day_view_start_editing_event (EDayView *day_view,
+ gint day,
+ gint event_num,
+ gchar *initial_text)
+{
+ EDayViewEvent *event;
+
+ /* If we are already editing the event, just return. */
+ if (day == day_view->editing_event_day
+ && event_num == day_view->editing_event_num)
+ return;
+
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+ } else {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+ }
+
+ /* If the event is not shown, don't try to edit it. */
+ if (!event->canvas_item)
+ return;
+
+ if (initial_text) {
+ gnome_canvas_item_set (event->canvas_item,
+ "text", initial_text,
+ NULL);
+ }
+
+ e_canvas_item_grab_focus (event->canvas_item);
+}
+
+
+/* This stops the current edit. If accept is TRUE the event summary is update,
+ else the edit is cancelled. */
+static void
+e_day_view_stop_editing_event (EDayView *day_view)
+{
+ GtkWidget *toplevel;
+
+ /* Check we are editing an event. */
+ if (day_view->editing_event_day == -1)
+ return;
+
+ /* Set focus to the toplevel so the item loses focus. */
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (day_view));
+ if (toplevel && GTK_IS_WINDOW (toplevel))
+ gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
+}
+
+
+static gboolean
+e_day_view_on_text_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EDayView *day_view)
+{
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ /* Only let the EText handle the event while editing. */
+ if (!E_TEXT (item)->editing)
+ gtk_signal_emit_stop_by_name (GTK_OBJECT (item),
+ "event");
+ break;
+ case GDK_FOCUS_CHANGE:
+ if (event->focus_change.in)
+ e_day_view_on_editing_started (day_view, item);
+ else
+ e_day_view_on_editing_stopped (day_view, item);
+
+ return FALSE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_day_view_on_editing_started (EDayView *day_view,
+ GnomeCanvasItem *item)
+{
+ gint day, event_num;
+
+ if (!e_day_view_find_event_from_item (day_view, item,
+ &day, &event_num))
+ return;
+
+ g_print ("In e_day_view_on_editing_started Day:%i Event:%i\n",
+ day, event_num);
+
+ day_view->editing_event_day = day;
+ day_view->editing_event_num = event_num;
+
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ e_day_view_reshape_long_event (day_view, event_num);
+ } else {
+ day_view->resize_bars_event_day = day;
+ day_view->resize_bars_event_num = event_num;
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+ }
+}
+
+
+static void
+e_day_view_on_editing_stopped (EDayView *day_view,
+ GnomeCanvasItem *item)
+{
+ gint day, event_num;
+ gboolean editing_long_event = FALSE;
+ EDayViewEvent *event;
+ gchar *text = NULL;
+
+ /* Note: the item we are passed here isn't reliable, so we just stop
+ the edit of whatever item was being edited. We also receive this
+ event twice for some reason. */
+ day = day_view->editing_event_day;
+ event_num = day_view->editing_event_num;
+
+ /* If no item is being edited, just return. */
+ if (day == -1)
+ return;
+
+ g_print ("In e_day_view_on_editing_stopped Day:%i Event:%i\n",
+ day, event_num);
+
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ editing_long_event = TRUE;
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+ } else {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* Hide the horizontal bars. */
+ gnome_canvas_item_hide (day_view->main_canvas_top_resize_bar_item);
+ gnome_canvas_item_hide (day_view->main_canvas_bottom_resize_bar_item);
+ }
+
+ /* Reset the edit fields. */
+ day_view->editing_event_day = -1;
+ day_view->editing_event_num = -1;
+
+ day_view->resize_bars_event_day = -1;
+ day_view->resize_bars_event_num = -1;
+
+ gtk_object_get (GTK_OBJECT (event->canvas_item),
+ "text", &text,
+ NULL);
+
+ /* Only update the summary if necessary. */
+ if (text && event->ico->summary
+ && !strcmp (text, event->ico->summary)) {
+ g_free (text);
+
+ if (day == E_DAY_VIEW_LONG_EVENT)
+ e_day_view_reshape_long_event (day_view, event_num);
+ return;
+ }
+
+ if (event->ico->summary)
+ g_free (event->ico->summary);
+
+ event->ico->summary = text;
+
+ /* Notify calendar of change. This will result in a call to update,
+ which will reset the event label as appropriate. */
+ gnome_calendar_object_changed (day_view->calendar, event->ico,
+ CHANGE_SUMMARY);
+ save_default_calendar (day_view->calendar);
+}
+
+
+/* Converts the selected range into a start and end time. */
+static void
+e_day_view_get_selection_range (EDayView *day_view,
+ time_t *start,
+ time_t *end)
+{
+ /* Check if the selection is only in the top canvas, in which case
+ we can simply use the day_starts array. */
+ if (day_view->selection_in_top_canvas) {
+ *start = day_view->day_starts[day_view->selection_start_col];
+ *end = day_view->day_starts[day_view->selection_end_col + 1];
+ } else {
+ /* Convert the start col + row into a time. */
+ *start = e_day_view_convert_grid_position_to_time (day_view, day_view->selection_start_col, day_view->selection_start_row);
+ *end = e_day_view_convert_grid_position_to_time (day_view, day_view->selection_end_col, day_view->selection_end_row + 1);
+ }
+}
+
+
+/* FIXME: It is possible that we may produce an invalid time due to daylight
+ saving times (i.e. when clocks go forward there is a range of time which
+ is not valid). I don't know the best way to handle daylight saving time. */
+static time_t
+e_day_view_convert_grid_position_to_time (EDayView *day_view,
+ gint col,
+ gint row)
+{
+ struct tm *tmp_tm;
+ time_t val;
+ gint minutes;
+
+ /* Calulate the number of minutes since the start of the day. */
+ minutes = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown
+ + row * day_view->mins_per_row;
+
+ /* A special case for midnight, where we can use the start of the
+ next day. */
+ if (minutes == 60 * 24)
+ return day_view->day_starts[col + 1];
+
+ /* We convert the start of the day to a struct tm, then set the
+ hour and minute, then convert it back to a time_t. */
+ tmp_tm = localtime (&day_view->day_starts[col]);
+
+ tmp_tm->tm_hour = minutes / 60;
+ tmp_tm->tm_min = minutes % 60;
+ tmp_tm->tm_isdst = -1;
+
+ val = mktime (tmp_tm);
+ return val;
+}
+
+
+/* This starts or stops auto-scrolling when dragging a selection or resizing
+ an event. */
+static void
+e_day_view_check_auto_scroll (EDayView *day_view,
+ gint event_y)
+{
+ g_print ("Event Y:%i\n", event_y);
+ if (event_y < E_DAY_VIEW_AUTO_SCROLL_OFFSET)
+ e_day_view_start_auto_scroll (day_view, TRUE);
+ else if (event_y >= day_view->main_canvas->allocation.height
+ - E_DAY_VIEW_AUTO_SCROLL_OFFSET)
+ e_day_view_start_auto_scroll (day_view, FALSE);
+ else
+ e_day_view_stop_auto_scroll (day_view);
+}
+
+
+static void
+e_day_view_start_auto_scroll (EDayView *day_view,
+ gboolean scroll_up)
+{
+ if (day_view->auto_scroll_timeout_id == 0) {
+ day_view->auto_scroll_timeout_id = g_timeout_add (E_DAY_VIEW_AUTO_SCROLL_TIMEOUT, e_day_view_auto_scroll_handler, day_view);
+ day_view->auto_scroll_delay = E_DAY_VIEW_AUTO_SCROLL_DELAY;
+ }
+ day_view->auto_scroll_up = scroll_up;
+}
+
+
+static void
+e_day_view_stop_auto_scroll (EDayView *day_view)
+{
+ if (day_view->auto_scroll_timeout_id != 0) {
+ gtk_timeout_remove (day_view->auto_scroll_timeout_id);
+ day_view->auto_scroll_timeout_id = 0;
+ }
+}
+
+
+static gboolean
+e_day_view_auto_scroll_handler (gpointer data)
+{
+ EDayView *day_view;
+ EDayViewPosition pos;
+ gint scroll_x, scroll_y, new_scroll_y, canvas_x, canvas_y, row, day;
+ GtkAdjustment *adj;
+
+ g_return_val_if_fail (E_IS_DAY_VIEW (data), FALSE);
+
+ day_view = E_DAY_VIEW (data);
+
+ GDK_THREADS_ENTER ();
+
+ if (day_view->auto_scroll_delay > 0) {
+ day_view->auto_scroll_delay--;
+ GDK_THREADS_LEAVE ();
+ return TRUE;
+ }
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (day_view->main_canvas),
+ &scroll_x, &scroll_y);
+
+ adj = GTK_LAYOUT (day_view->main_canvas)->vadjustment;
+
+ if (day_view->auto_scroll_up)
+ new_scroll_y = MAX (scroll_y - adj->step_increment, 0);
+ else
+ new_scroll_y = MIN (scroll_y + adj->step_increment,
+ adj->upper - adj->page_size);
+
+ if (new_scroll_y != scroll_y) {
+ /* NOTE: This reduces flicker, but only works if we don't use
+ canvas items which have X windows. */
+ gtk_layout_freeze (GTK_LAYOUT (day_view->main_canvas));
+
+ gnome_canvas_scroll_to (GNOME_CANVAS (day_view->main_canvas),
+ scroll_x, new_scroll_y);
+
+ gtk_layout_thaw (GTK_LAYOUT (day_view->main_canvas));
+ }
+
+ canvas_x = day_view->last_mouse_x + scroll_x;
+ canvas_y = day_view->last_mouse_y + new_scroll_y;
+
+ /* Update the selection/resize/drag if necessary. */
+ pos = e_day_view_convert_position_in_main_canvas (day_view,
+ canvas_x, canvas_y,
+ &day, &row, NULL);
+
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ if (day_view->selection_drag_pos != E_DAY_VIEW_DRAG_NONE) {
+ e_day_view_update_selection (day_view, row, day);
+ } else if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE) {
+ e_day_view_update_resize (day_view, row);
+ } else if (day_view->drag_item->object.flags
+ & GNOME_CANVAS_ITEM_VISIBLE) {
+ e_day_view_update_main_canvas_drag (day_view, row,
+ day);
+ }
+ }
+
+ GDK_THREADS_LEAVE ();
+ return TRUE;
+}
+
+
+gboolean
+e_day_view_get_event_position (EDayView *day_view,
+ gint day,
+ gint event_num,
+ gint *item_x,
+ gint *item_y,
+ gint *item_w,
+ gint *item_h)
+{
+ EDayViewEvent *event;
+ gint start_row, end_row, cols_in_row, start_col, num_columns;
+
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* If the event is flagged as not displayed, return FALSE. */
+ if (event->num_columns == 0)
+ return FALSE;
+
+ start_row = event->start_minute / day_view->mins_per_row;
+ end_row = (event->end_minute - 1) / day_view->mins_per_row;
+ cols_in_row = day_view->cols_per_row[day][start_row];
+ start_col = event->start_row_or_col;
+ num_columns = event->num_columns;
+
+ /* If the event is being resize, use the resize position. */
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
+ && day_view->resize_event_day == day
+ && day_view->resize_event_num == event_num) {
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_TOP_EDGE)
+ start_row = day_view->resize_start_row;
+ else if (day_view->resize_drag_pos == E_DAY_VIEW_POS_BOTTOM_EDGE)
+ end_row = day_view->resize_end_row;
+ }
+
+ *item_x = day_view->day_offsets[day] + day_view->day_widths[day] * start_col / cols_in_row;
+ *item_w = day_view->day_widths[day] * num_columns / cols_in_row - E_DAY_VIEW_GAP_WIDTH;
+ *item_y = start_row * day_view->row_height;
+ *item_h = (end_row - start_row + 1) * day_view->row_height;
+
+ return TRUE;
+}
+
+
+gboolean
+e_day_view_get_long_event_position (EDayView *day_view,
+ gint event_num,
+ gint *start_day,
+ gint *end_day,
+ gint *item_x,
+ gint *item_y,
+ gint *item_w,
+ gint *item_h)
+{
+ EDayViewEvent *event;
+
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ /* If the event is flagged as not displayed, return FALSE. */
+ if (event->num_columns == 0)
+ return FALSE;
+
+ if (!e_day_view_find_long_event_days (day_view, event,
+ start_day, end_day))
+ return FALSE;
+
+ /* If the event is being resize, use the resize position. */
+ if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
+ && day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
+ && day_view->resize_event_num == event_num) {
+ if (day_view->resize_drag_pos == E_DAY_VIEW_POS_LEFT_EDGE)
+ *start_day = day_view->resize_start_row;
+ else if (day_view->resize_drag_pos == E_DAY_VIEW_POS_RIGHT_EDGE)
+ *end_day = day_view->resize_end_row;
+ }
+
+ *item_x = day_view->day_offsets[*start_day] + E_DAY_VIEW_BAR_WIDTH;
+ *item_w = day_view->day_offsets[*end_day + 1] - *item_x
+ - E_DAY_VIEW_GAP_WIDTH;
+ *item_y = (event->start_row_or_col + 1) * day_view->top_row_height;
+ *item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
+ return TRUE;
+}
+
+
+/* Converts a position within the entire top canvas to a day & event and
+ a place within the event if appropriate. */
+static EDayViewPosition
+e_day_view_convert_position_in_top_canvas (EDayView *day_view,
+ gint x,
+ gint y,
+ gint *day_return,
+ gint *event_num_return)
+{
+ EDayViewEvent *event;
+ gint day, row, col;
+ gint event_num, start_day, end_day, item_x, item_y, item_w, item_h;
+
+ if (x < 0 || y < 0)
+ return E_DAY_VIEW_POS_OUTSIDE;
+
+ row = y / day_view->top_row_height - 1;
+
+ day = -1;
+ for (col = 1; col <= day_view->days_shown; col++) {
+ if (x < day_view->day_offsets[col]) {
+ day = col - 1;
+ break;
+ }
+ }
+ if (day == -1)
+ return E_DAY_VIEW_POS_OUTSIDE;
+
+ *day_return = day;
+
+ /* If only the grid position is wanted, return. */
+ if (event_num_return == NULL)
+ return E_DAY_VIEW_POS_NONE;
+
+ for (event_num = 0; event_num < day_view->long_events->len;
+ event_num++) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+
+ if (event->start_row_or_col != row)
+ continue;
+
+ if (!e_day_view_get_long_event_position (day_view, event_num,
+ &start_day, &end_day,
+ &item_x, &item_y,
+ &item_w, &item_h))
+ continue;
+
+ if (x < item_x)
+ continue;
+
+ if (x >= item_x + item_w)
+ continue;
+
+ *event_num_return = event_num;
+
+ if (x < item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
+ + E_DAY_VIEW_LONG_EVENT_X_PAD)
+ return E_DAY_VIEW_POS_LEFT_EDGE;
+
+ if (x >= item_x + item_w - E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH
+ - E_DAY_VIEW_LONG_EVENT_X_PAD)
+ return E_DAY_VIEW_POS_RIGHT_EDGE;
+
+ return E_DAY_VIEW_POS_EVENT;
+ }
+
+ return E_DAY_VIEW_POS_NONE;
+}
+
+
+/* Converts a position within the entire main canvas to a day, row, event and
+ a place within the event if appropriate. */
+static EDayViewPosition
+e_day_view_convert_position_in_main_canvas (EDayView *day_view,
+ gint x,
+ gint y,
+ gint *day_return,
+ gint *row_return,
+ gint *event_num_return)
+{
+ gint day, row, col, event_num;
+ gint item_x, item_y, item_w, item_h;
+
+ /* Check the position is inside the canvas, and determine the day
+ and row. */
+ if (x < 0 || y < 0)
+ return E_DAY_VIEW_POS_OUTSIDE;
+
+ row = y / day_view->row_height;
+ if (row >= day_view->rows)
+ return E_DAY_VIEW_POS_OUTSIDE;
+
+ day = -1;
+ for (col = 1; col <= day_view->days_shown; col++) {
+ if (x < day_view->day_offsets[col]) {
+ day = col - 1;
+ break;
+ }
+ }
+ if (day == -1)
+ return E_DAY_VIEW_POS_OUTSIDE;
+
+ *day_return = day;
+ *row_return = row;
+
+ /* If only the grid position is wanted, return. */
+ if (event_num_return == NULL)
+ return E_DAY_VIEW_POS_NONE;
+
+ /* Check the selected item first, since the horizontal resizing bars
+ may be above other events. */
+ if (day_view->resize_bars_event_day == day) {
+ if (e_day_view_get_event_position (day_view, day,
+ day_view->resize_bars_event_num,
+ &item_x, &item_y,
+ &item_w, &item_h)) {
+ if (x >= item_x && x < item_x + item_w) {
+ *event_num_return = day_view->resize_bars_event_num;
+ if (y >= item_y - E_DAY_VIEW_BAR_HEIGHT
+ && y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT)
+ return E_DAY_VIEW_POS_TOP_EDGE;
+ if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
+ && y < item_y + item_h + E_DAY_VIEW_BAR_HEIGHT)
+ return E_DAY_VIEW_POS_BOTTOM_EDGE;
+ }
+ }
+ }
+
+ /* Try to find the event at the found position. */
+ *event_num_return = -1;
+ for (event_num = 0; event_num < day_view->events[day]->len;
+ event_num++) {
+ if (!e_day_view_get_event_position (day_view, day, event_num,
+ &item_x, &item_y,
+ &item_w, &item_h))
+ continue;
+
+ if (x < item_x || x >= item_x + item_w
+ || y < item_y || y >= item_y + item_h)
+ continue;
+
+ *event_num_return = event_num;
+
+ if (x < item_x + E_DAY_VIEW_BAR_WIDTH)
+ return E_DAY_VIEW_POS_LEFT_EDGE;
+
+ if (y < item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT
+ + E_DAY_VIEW_EVENT_Y_PAD)
+ return E_DAY_VIEW_POS_TOP_EDGE;
+
+ if (y >= item_y + item_h - E_DAY_VIEW_EVENT_BORDER_HEIGHT
+ - E_DAY_VIEW_EVENT_Y_PAD)
+ return E_DAY_VIEW_POS_BOTTOM_EDGE;
+
+ return E_DAY_VIEW_POS_EVENT;
+ }
+
+ return E_DAY_VIEW_POS_NONE;
+}
+
+
+static gint
+e_day_view_on_top_canvas_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EDayView *day_view)
+{
+ gint scroll_x, scroll_y;
+
+ g_print ("In e_day_view_on_top_canvas_drag_motion\n");
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ day_view->drag_event_x = x + scroll_x;
+ day_view->drag_event_y = y + scroll_y;
+
+ e_day_view_reshape_top_canvas_drag_item (day_view);
+
+ return TRUE;
+}
+
+
+static void
+e_day_view_reshape_top_canvas_drag_item (EDayView *day_view)
+{
+ EDayViewPosition pos;
+ gint x, y, day;
+
+ /* Calculate the day & start row of the event being dragged, using
+ the current mouse position. */
+ x = day_view->drag_event_x;
+ y = day_view->drag_event_y;
+ pos = e_day_view_convert_position_in_top_canvas (day_view, x, y,
+ &day, NULL);
+ /* This shouldn't really happen in a drag. */
+ if (pos == E_DAY_VIEW_POS_OUTSIDE)
+ return;
+
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT)
+ day -= day_view->drag_event_offset;
+ day = MAX (day, 0);
+
+ e_day_view_update_top_canvas_drag (day_view, day);
+}
+
+
+static void
+e_day_view_update_top_canvas_drag (EDayView *day_view,
+ gint day)
+{
+ EDayViewEvent *event = NULL;
+ gint row, num_days, start_day, end_day;
+ gdouble item_x, item_y, item_w, item_h;
+ GdkFont *font;
+ gchar *text;
+
+
+ /* Calculate the event's position. If the event is in the same
+ position we started in, we use the same columns. */
+ row = day_view->rows_in_top_display + 1;
+ num_days = 1;
+
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ day_view->drag_event_num);
+ row = event->start_row_or_col + 1;
+
+ if (!e_day_view_find_long_event_days (day_view, event,
+ &start_day, &end_day))
+ return;
+
+ num_days = end_day - start_day + 1;
+
+ /* Make sure we don't go off the screen. */
+ day = MIN (day, day_view->days_shown - num_days);
+
+ } else if (day_view->drag_event_day != -1) {
+ event = &g_array_index (day_view->events[day_view->drag_event_day],
+ EDayViewEvent,
+ day_view->drag_event_num);
+ }
+
+ /* If the position hasn't changed, just return. */
+ if (day_view->drag_last_day == day
+ && (day_view->drag_long_event_item->object.flags
+ & GNOME_CANVAS_ITEM_VISIBLE))
+ return;
+
+ day_view->drag_last_day = day;
+
+
+ item_x = day_view->day_offsets[day] + E_DAY_VIEW_BAR_WIDTH;
+ item_w = day_view->day_offsets[day + num_days] - item_x
+ - E_DAY_VIEW_GAP_WIDTH;
+ item_y = row * day_view->top_row_height;
+ item_h = day_view->top_row_height - E_DAY_VIEW_TOP_CANVAS_Y_GAP;
+
+
+ g_print ("Moving to %g,%g %gx%g\n", item_x, item_y, item_w, item_h);
+
+ /* Set the positions of the event & associated items. */
+ gnome_canvas_item_set (day_view->drag_long_event_rect_item,
+ "x1", item_x,
+ "y1", item_y,
+ "x2", item_x + item_w - 1,
+ "y2", item_y + item_h - 1,
+ NULL);
+
+ font = GTK_WIDGET (day_view)->style->font;
+ gnome_canvas_item_set (day_view->drag_long_event_item,
+ "font_gdk", font,
+ "x", item_x + E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD,
+ "y", item_y + E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD,
+ "clip_width", item_w - (E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH + E_DAY_VIEW_LONG_EVENT_X_PAD) * 2,
+ "clip_height", item_h - (E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT + E_DAY_VIEW_LONG_EVENT_Y_PAD) * 2,
+ NULL);
+
+ if (!(day_view->drag_long_event_rect_item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ gnome_canvas_item_raise_to_top (day_view->drag_long_event_rect_item);
+ gnome_canvas_item_show (day_view->drag_long_event_rect_item);
+ }
+
+ /* Set the text, if necessary. We don't want to set the text every
+ time it moves, so we check if it is currently invisible and only
+ set the text then. */
+ if (!(day_view->drag_long_event_item->object.flags
+ & GNOME_CANVAS_ITEM_VISIBLE)) {
+ if (event)
+ text = event->ico->summary;
+ else
+ text = NULL;
+
+ gnome_canvas_item_set (day_view->drag_long_event_item,
+ "text", text ? text : "",
+ NULL);
+ gnome_canvas_item_raise_to_top (day_view->drag_long_event_item);
+ gnome_canvas_item_show (day_view->drag_long_event_item);
+ }
+}
+
+
+static gint
+e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ EDayView *day_view)
+{
+ gint scroll_x, scroll_y;
+
+ g_print ("In e_day_view_on_main_canvas_drag_motion\n");
+
+ day_view->last_mouse_x = x;
+ day_view->last_mouse_y = y;
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ day_view->drag_event_x = x + scroll_x;
+ day_view->drag_event_y = y + scroll_y;
+
+ e_day_view_reshape_main_canvas_drag_item (day_view);
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
+
+ e_day_view_check_auto_scroll (day_view, y);
+
+ return TRUE;
+}
+
+
+static void
+e_day_view_reshape_main_canvas_drag_item (EDayView *day_view)
+{
+ EDayViewPosition pos;
+ gint x, y, day, row;
+
+ /* Calculate the day & start row of the event being dragged, using
+ the current mouse position. */
+ x = day_view->drag_event_x;
+ y = day_view->drag_event_y;
+ pos = e_day_view_convert_position_in_main_canvas (day_view, x, y,
+ &day, &row, NULL);
+ /* This shouldn't really happen in a drag. */
+ if (pos == E_DAY_VIEW_POS_OUTSIDE)
+ return;
+
+ if (day_view->drag_event_day != -1
+ && day_view->drag_event_day != E_DAY_VIEW_LONG_EVENT)
+ row -= day_view->drag_event_offset;
+ row = MAX (row, 0);
+
+ e_day_view_update_main_canvas_drag (day_view, row, day);
+}
+
+
+static void
+e_day_view_update_main_canvas_drag (EDayView *day_view,
+ gint row,
+ gint day)
+{
+ EDayViewEvent *event = NULL;
+ gint cols_in_row, start_col, num_columns, num_rows, start_row, end_row;
+ gdouble item_x, item_y, item_w, item_h;
+ GdkFont *font;
+ gchar *text;
+
+ /* If the position hasn't changed, just return. */
+ if (day_view->drag_last_day == day
+ && day_view->drag_last_row == row
+ && (day_view->drag_item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+ return;
+
+ day_view->drag_last_day = day;
+ day_view->drag_last_row = row;
+
+ /* Calculate the event's position. If the event is in the same
+ position we started in, we use the same columns. */
+ cols_in_row = 1;
+ start_col = 0;
+ num_columns = 1;
+ num_rows = 1;
+
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ day_view->drag_event_num);
+ } else if (day_view->drag_event_day != -1) {
+ event = &g_array_index (day_view->events[day_view->drag_event_day],
+ EDayViewEvent,
+ day_view->drag_event_num);
+ start_row = event->start_minute / day_view->mins_per_row;
+ end_row = (event->end_minute - 1) / day_view->mins_per_row;
+ num_rows = end_row - start_row + 1;
+ }
+
+ if (day_view->drag_event_day == day && start_row == row) {
+ cols_in_row = day_view->cols_per_row[day][row];
+ start_col = event->start_row_or_col;
+ num_columns = event->num_columns;
+ }
+
+ item_x = day_view->day_offsets[day]
+ + day_view->day_widths[day] * start_col / cols_in_row;
+ item_w = day_view->day_widths[day] * num_columns / cols_in_row
+ - E_DAY_VIEW_GAP_WIDTH;
+ item_y = row * day_view->row_height;
+ item_h = num_rows * day_view->row_height;
+
+ g_print ("Moving to %g,%g %gx%g\n", item_x, item_y, item_w, item_h);
+
+ /* Set the positions of the event & associated items. */
+ gnome_canvas_item_set (day_view->drag_rect_item,
+ "x1", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
+ "y1", item_y,
+ "x2", item_x + item_w - 1,
+ "y2", item_y + item_h - 1,
+ NULL);
+
+ gnome_canvas_item_set (day_view->drag_bar_item,
+ "x1", item_x,
+ "y1", item_y,
+ "x2", item_x + E_DAY_VIEW_BAR_WIDTH - 1,
+ "y2", item_y + item_h - 1,
+ NULL);
+
+ font = GTK_WIDGET (day_view)->style->font;
+ gnome_canvas_item_set (day_view->drag_item,
+ "font_gdk", font,
+ "x", item_x + E_DAY_VIEW_BAR_WIDTH + E_DAY_VIEW_EVENT_X_PAD,
+ "y", item_y + E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD,
+ "clip_width", item_w - E_DAY_VIEW_BAR_WIDTH - E_DAY_VIEW_EVENT_X_PAD * 2,
+ "clip_height", item_h - (E_DAY_VIEW_EVENT_BORDER_HEIGHT + E_DAY_VIEW_EVENT_Y_PAD) * 2,
+ NULL);
+
+ if (!(day_view->drag_bar_item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ gnome_canvas_item_raise_to_top (day_view->drag_bar_item);
+ gnome_canvas_item_show (day_view->drag_bar_item);
+ }
+
+ if (!(day_view->drag_rect_item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ gnome_canvas_item_raise_to_top (day_view->drag_rect_item);
+ gnome_canvas_item_show (day_view->drag_rect_item);
+ }
+
+ /* Set the text, if necessary. We don't want to set the text every
+ time it moves, so we check if it is currently invisible and only
+ set the text then. */
+ if (!(day_view->drag_item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ if (event)
+ text = event->ico->summary;
+ else
+ text = NULL;
+
+ gnome_canvas_item_set (day_view->drag_item,
+ "text", text ? text : "",
+ NULL);
+ gnome_canvas_item_raise_to_top (day_view->drag_item);
+ gnome_canvas_item_show (day_view->drag_item);
+ }
+}
+
+
+static void
+e_day_view_on_top_canvas_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EDayView *day_view)
+{
+ g_print ("In e_day_view_on_top_canvas_drag_leave\n");
+
+ day_view->drag_last_day = -1;
+
+ gnome_canvas_item_hide (day_view->drag_long_event_rect_item);
+ gnome_canvas_item_hide (day_view->drag_long_event_item);
+}
+
+
+static void
+e_day_view_on_main_canvas_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ EDayView *day_view)
+{
+ g_print ("In e_day_view_on_main_canvas_drag_leave\n");
+
+ day_view->drag_last_day = -1;
+
+ e_day_view_stop_auto_scroll (day_view);
+
+ gnome_canvas_item_hide (day_view->drag_rect_item);
+ gnome_canvas_item_hide (day_view->drag_bar_item);
+ gnome_canvas_item_hide (day_view->drag_item);
+
+ /* Hide the resize bars if they are being used in the drag. */
+ if (day_view->drag_event_day == day_view->resize_bars_event_day
+ && day_view->drag_event_num == day_view->resize_bars_event_num) {
+ gnome_canvas_item_hide (day_view->main_canvas_top_resize_bar_item);
+ gnome_canvas_item_hide (day_view->main_canvas_bottom_resize_bar_item);
+ }
+}
+
+
+static void
+e_day_view_on_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+
+ g_print ("In e_day_view_on_main_canvas_drag_begin\n");
+
+ day = day_view->drag_event_day;
+ event_num = day_view->drag_event_num;
+
+ /* These should both be set. */
+ g_return_if_fail (day != -1);
+ g_return_if_fail (event_num != -1);
+
+ if (day == E_DAY_VIEW_LONG_EVENT)
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+ else
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+
+ /* Hide the text item, since it will be shown in the special drag
+ items. */
+ gnome_canvas_item_hide (event->canvas_item);
+}
+
+
+static void
+e_day_view_on_drag_end (GtkWidget *widget,
+ GdkDragContext *context,
+ EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+
+ g_print ("In e_day_view_on_main_canvas_drag_end\n");
+
+ day = day_view->drag_event_day;
+ event_num = day_view->drag_event_num;
+
+ /* If the calendar has already been updated in drag_data_received()
+ we just return. */
+ if (day == -1 || event_num == -1)
+ return;
+
+ if (day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ event_num);
+ gtk_widget_queue_draw (day_view->top_canvas);
+ } else {
+ event = &g_array_index (day_view->events[day], EDayViewEvent,
+ event_num);
+ gtk_widget_queue_draw (day_view->main_canvas);
+ }
+
+ /* Show the text item again. */
+ gnome_canvas_item_show (event->canvas_item);
+
+ day_view->drag_event_day = -1;
+ day_view->drag_event_num = -1;
+}
+
+
+static void
+e_day_view_on_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ EDayView *day_view)
+{
+ EDayViewEvent *event;
+ gint day, event_num;
+ gchar *event_uid;
+
+ g_print ("In e_day_view_on_drag_data_get\n");
+
+ day = day_view->drag_event_day;
+ event_num = day_view->drag_event_num;
+
+ /* These should both be set. */
+ g_return_if_fail (day != -1);
+ g_return_if_fail (event_num != -1);
+
+ if (day == E_DAY_VIEW_LONG_EVENT)
+ event = &g_array_index (day_view->long_events,
+ EDayViewEvent, event_num);
+ else
+ event = &g_array_index (day_view->events[day],
+ EDayViewEvent, event_num);
+
+ event_uid = event->ico->uid;
+ g_return_if_fail (event_uid != NULL);
+
+ if (info == TARGET_CALENDAR_EVENT) {
+ gtk_selection_data_set (selection_data, selection_data->target,
+ 8, event_uid, strlen (event_uid));
+ }
+}
+
+
+static void
+e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EDayView *day_view)
+{
+ EDayViewEvent *event;
+ EDayViewPosition pos;
+ gint day, row, scroll_x, scroll_y, start_day, end_day, num_days;
+ gchar *event_uid;
+
+ g_print ("In e_day_view_on_top_canvas_drag_data_received\n");
+
+ if ((data->length >= 0) && (data->format == 8)) {
+ pos = e_day_view_convert_position_in_top_canvas (day_view,
+ x, y, &day,
+ NULL);
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ num_days = 1;
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ day_view->drag_event_num);
+ day -= day_view->drag_event_offset;
+ day = MAX (day, 0);
+
+ e_day_view_find_long_event_days (day_view,
+ event,
+ &start_day,
+ &end_day);
+ num_days = end_day - start_day + 1;
+ /* Make sure we don't go off the screen. */
+ day = MIN (day, day_view->days_shown - num_days);
+ } else if (day_view->drag_event_day != -1) {
+ event = &g_array_index (day_view->events[day_view->drag_event_day],
+ EDayViewEvent,
+ day_view->drag_event_num);
+ }
+
+ event_uid = data->data;
+
+ g_print ("Dropped Day:%i UID:%s\n", day, event_uid);
+
+ if (!event_uid || !event->ico->uid
+ || strcmp (event_uid, event->ico->uid))
+ g_warning ("Unexpected event UID");
+
+ event->ico->dtstart = day_view->day_starts[day];
+ event->ico->dtend = day_view->day_starts[day + num_days];
+
+ gtk_drag_finish (context, TRUE, TRUE, time);
+
+ /* Reset this since it will be invalid. */
+ day_view->drag_event_day = -1;
+
+ /* Notify calendar of change */
+ gnome_calendar_object_changed (day_view->calendar,
+ event->ico,
+ CHANGE_DATES);
+ save_default_calendar (day_view->calendar);
+
+ return;
+ }
+ }
+
+ gtk_drag_finish (context, FALSE, FALSE, time);
+}
+
+
+static void
+e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ EDayView *day_view)
+{
+ EDayViewEvent *event;
+ EDayViewPosition pos;
+ gint day, start_row, end_row, num_rows;
+ gchar *event_uid;
+
+ g_print ("In e_day_view_on_main_canvas_drag_data_received\n");
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (widget),
+ &scroll_x, &scroll_y);
+ x += scroll_x;
+ y += scroll_y;
+
+ if ((data->length >= 0) && (data->format == 8)) {
+ pos = e_day_view_convert_position_in_main_canvas (day_view,
+ x, y, &day,
+ &row, NULL);
+ if (pos != E_DAY_VIEW_POS_OUTSIDE) {
+ num_rows = 1;
+ if (day_view->drag_event_day == E_DAY_VIEW_LONG_EVENT) {
+ event = &g_array_index (day_view->long_events, EDayViewEvent,
+ day_view->drag_event_num);
+ } else if (day_view->drag_event_day != -1) {
+ event = &g_array_index (day_view->events[day_view->drag_event_day],
+ EDayViewEvent,
+ day_view->drag_event_num);
+ row -= day_view->drag_event_offset;
+
+ /* Calculate time offset from start row. */
+ start_row = event->start_minute / day_view->mins_per_row;
+ end_row = (event->end_minute - 1) / day_view->mins_per_row;
+ num_rows = end_row - start_row + 1;
+ }
+
+ event_uid = data->data;
+
+ g_print ("Dropped Day:%i Row:%i UID:%s\n", day, row,
+ event_uid);
+
+ if (!event_uid || !event->ico->uid
+ || strcmp (event_uid, event->ico->uid))
+ g_warning ("Unexpected event UID");
+
+ event->ico->dtstart = e_day_view_convert_grid_position_to_time (day_view, day, row);
+ event->ico->dtend = e_day_view_convert_grid_position_to_time (day_view, day, row + num_rows);
+
+ gtk_drag_finish (context, TRUE, TRUE, time);
+
+ /* Reset this since it will be invalid. */
+ day_view->drag_event_day = -1;
+
+ /* Notify calendar of change */
+ gnome_calendar_object_changed (day_view->calendar,
+ event->ico,
+ CHANGE_DATES);
+ save_default_calendar (day_view->calendar);
+
+ return;
+ }
+ }
+
+ gtk_drag_finish (context, FALSE, FALSE, time);
+}
+
diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h
new file mode 100644
index 0000000000..b829e76384
--- /dev/null
+++ b/calendar/gui/e-day-view.h
@@ -0,0 +1,463 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@helixcode.com>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef _E_DAY_VIEW_H_
+#define _E_DAY_VIEW_H_
+
+#include <time.h>
+#include <gtk/gtktable.h>
+#include <libgnomeui/gnome-canvas.h>
+
+#include "gnome-cal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EDayView - displays the Day & Work-Week views of the calendar.
+ */
+
+/* The maximum number of days shown. We use 7 since we only show 1 week max. */
+#define E_DAY_VIEW_MAX_DAYS 7
+
+/* This is used as a special code to signify a long event instead of the day
+ of a normal event. */
+#define E_DAY_VIEW_LONG_EVENT E_DAY_VIEW_MAX_DAYS
+
+/* The maximum number of columns of appointments within a day. */
+#define E_DAY_VIEW_MAX_COLUMNS 6
+
+/* The width of the gap between appointments. This should be at least
+ E_DAY_VIEW_BAR_WIDTH. */
+#define E_DAY_VIEW_GAP_WIDTH 6
+
+/* The width of the bars down the left of each column and appointment.
+ This includes the borders on each side of it. */
+#define E_DAY_VIEW_BAR_WIDTH 6
+
+/* The height of the horizontal bar above & beneath the selected event.
+ This includes the borders on the top and bottom. */
+#define E_DAY_VIEW_BAR_HEIGHT 6
+
+/* The size of the reminder & recurrence icons, and padding around them. */
+#define E_DAY_VIEW_ICON_WIDTH 16
+#define E_DAY_VIEW_ICON_HEIGHT 16
+#define E_DAY_VIEW_ICON_X_PAD 0
+#define E_DAY_VIEW_ICON_Y_PAD 0
+
+/* The size of the border around the event. */
+#define E_DAY_VIEW_EVENT_BORDER_WIDTH 1
+#define E_DAY_VIEW_EVENT_BORDER_HEIGHT 1
+
+/* The padding on each side of the event text. */
+#define E_DAY_VIEW_EVENT_X_PAD 2
+#define E_DAY_VIEW_EVENT_Y_PAD 1
+
+/* The padding on each side of the event text for events in the top canvas. */
+#define E_DAY_VIEW_LONG_EVENT_X_PAD 2
+#define E_DAY_VIEW_LONG_EVENT_Y_PAD 2
+
+/* The size of the border around the long events in the top canvas. */
+#define E_DAY_VIEW_LONG_EVENT_BORDER_WIDTH 1
+#define E_DAY_VIEW_LONG_EVENT_BORDER_HEIGHT 1
+
+/* The space between the time and the icon/text in the top canvas. */
+#define E_DAY_VIEW_LONG_EVENT_TIME_X_PAD 2
+
+/* The gap between rows in the top canvas. */
+#define E_DAY_VIEW_TOP_CANVAS_Y_GAP 2
+
+
+/* These are used to specify the type of an appointment. They match those
+ used in EMeetingTimeSelector. */
+typedef enum
+{
+ E_DAY_VIEW_BUSY_TENTATIVE = 0,
+ E_DAY_VIEW_BUSY_OUT_OF_OFFICE = 1,
+ E_DAY_VIEW_BUSY_BUSY = 2,
+
+ E_DAY_VIEW_BUSY_LAST = 3
+} EDayViewBusyType;
+
+/* This is used to specify the format used when displaying the dates.
+ The full format is like 'September 12'. The abbreviated format is like
+ 'Sep 12'. The short format is like '12'. The actual format used is
+ determined in style_set(), once we know the font being used. */
+typedef enum
+{
+ E_DAY_VIEW_DATE_FULL,
+ E_DAY_VIEW_DATE_ABBREVIATED,
+ E_DAY_VIEW_DATE_SHORT
+} EDayViewDateFormat;
+
+/* These index our colors array. */
+typedef enum
+{
+ E_DAY_VIEW_COLOR_BG_WORKING,
+ E_DAY_VIEW_COLOR_BG_NOT_WORKING,
+ E_DAY_VIEW_COLOR_EVENT_VBAR,
+
+ E_DAY_VIEW_COLOR_EVENT_BACKGROUND,
+ E_DAY_VIEW_COLOR_EVENT_BORDER,
+
+ E_DAY_VIEW_COLOR_LAST
+} EDayViewColors;
+
+/* These specify which part of the selection we are dragging, if any. */
+typedef enum
+{
+ E_DAY_VIEW_DRAG_NONE,
+ E_DAY_VIEW_DRAG_START,
+ E_DAY_VIEW_DRAG_END
+} EDayViewDragPosition;
+
+/* Specifies the position of the mouse. */
+typedef enum
+{
+ E_DAY_VIEW_POS_OUTSIDE,
+ E_DAY_VIEW_POS_NONE,
+ E_DAY_VIEW_POS_EVENT,
+ E_DAY_VIEW_POS_LEFT_EDGE,
+ E_DAY_VIEW_POS_RIGHT_EDGE,
+ E_DAY_VIEW_POS_TOP_EDGE,
+ E_DAY_VIEW_POS_BOTTOM_EDGE
+} EDayViewPosition;
+
+typedef struct _EDayViewEvent EDayViewEvent;
+struct _EDayViewEvent {
+ iCalObject *ico;
+ time_t start;
+ time_t end;
+ guint8 start_row_or_col;/* The start column for normal events, or the
+ start row for long events. */
+ guint8 num_columns; /* 0 indicates not displayed. For long events
+ this is just 1 if the event is shown. */
+ guint16 start_minute; /* Offsets from the start of the display. */
+ guint16 end_minute;
+ GnomeCanvasItem *canvas_item;
+};
+
+
+#define E_DAY_VIEW(obj) GTK_CHECK_CAST (obj, e_day_view_get_type (), EDayView)
+#define E_DAY_VIEW_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_day_view_get_type (), EDayViewClass)
+#define E_IS_DAY_VIEW(obj) GTK_CHECK_TYPE (obj, e_day_view_get_type ())
+
+
+typedef struct _EDayView EDayView;
+typedef struct _EDayViewClass EDayViewClass;
+
+struct _EDayView
+{
+ GtkTable table;
+
+ /* The top canvas where the dates and long appointments are shown. */
+ GtkWidget *top_canvas;
+ GnomeCanvasItem *top_canvas_item;
+
+ /* The main canvas where the rest of the appointments are shown. */
+ GtkWidget *main_canvas;
+ GnomeCanvasItem *main_canvas_item;
+
+ /* The canvas displaying the times of the day. */
+ GtkWidget *time_canvas;
+ GnomeCanvasItem *time_canvas_item;
+
+ GtkWidget *vscrollbar;
+
+ /* The calendar we are associated with. */
+ GnomeCalendar *calendar;
+
+ /* The start and end of the day shown. */
+ time_t lower;
+ time_t upper;
+
+ /* The number of days we are showing. Usually 1 or 5. Maybe 6 or 7. */
+ gint days_shown;
+
+ /* The start of each day & an extra element to hold the last time. */
+ time_t day_starts[E_DAY_VIEW_MAX_DAYS + 1];
+
+
+ /* An array of EDayViewEvent elements for the top view and each day. */
+ GArray *long_events;
+ GArray *events[E_DAY_VIEW_MAX_DAYS];
+
+ /* These are set to FALSE whenever an event in the corresponding array
+ is changed. Any function that needs the events sorted calls
+ e_day_view_ensure_events_sorted(). */
+ gboolean long_events_sorted;
+ gboolean events_sorted[E_DAY_VIEW_MAX_DAYS];
+
+ /* This is TRUE if we need to relayout the events before drawing. */
+ gboolean long_events_need_layout;
+ gboolean need_layout[E_DAY_VIEW_MAX_DAYS];
+
+ /* This is TRUE if we need to reshape the canvas items, but a full
+ layout is not necessary. */
+ gboolean long_events_need_reshape;
+ gboolean need_reshape[E_DAY_VIEW_MAX_DAYS];
+
+ /* The number of minutes per row. 5, 10, 15, 30 or 60. */
+ gint mins_per_row;
+
+ /* The number of rows needed, depending on the times shown and the
+ minutes per row. */
+ gint rows;
+
+ /* The height of each row. */
+ gint row_height;
+
+ /* The number of rows in the top display. */
+ gint rows_in_top_display;
+
+ /* The height of each row in the top canvas. */
+ gint top_row_height;
+
+ /* The first and last times shown in the display. The last time isn't
+ included in the range. Default is 0:00-24:00 */
+ gint first_hour_shown;
+ gint first_minute_shown;
+ gint last_hour_shown;
+ gint last_minute_shown;
+
+ /* The start and end of the work day, rounded to the nearest row. */
+ gint work_day_start_hour;
+ gint work_day_start_minute;
+ gint work_day_end_hour;
+ gint work_day_end_minute;
+
+ /* This is set to TRUE when the widget is created, so it scrolls to
+ the start of the working day when first shown. */
+ gboolean scroll_to_work_day;
+
+ /* This is the width & offset of each of the day columns in the
+ display. */
+ gint day_widths[E_DAY_VIEW_MAX_DAYS];
+ gint day_offsets[E_DAY_VIEW_MAX_DAYS + 1];
+
+ /* An array holding the number of columns in each row, in each day. */
+ guint8 cols_per_row[E_DAY_VIEW_MAX_DAYS][12 * 24];
+
+ /* Sizes of the various time strings. */
+ gint large_hour_widths[24];
+ gint small_hour_widths[24];
+ gint minute_widths[12]; /* intervals of 5 minutes. */
+ gint max_small_hour_width;
+ gint max_large_hour_width;
+ gint max_minute_width;
+ gint colon_width;
+
+ /* This specifies how we are displaying the dates at the top. */
+ EDayViewDateFormat date_format;
+
+ /* These are the maximum widths of the different types of dates. */
+ gint long_format_width;
+ gint abbreviated_format_width;
+
+ /* The large font use to display the hours. I don't think we need a
+ fontset since we only display numbers. */
+ GdkFont *large_font;
+
+ /* The GC used for painting in different colors. */
+ GdkGC *main_gc;
+
+ /* The icons. */
+ GdkPixmap *reminder_icon;
+ GdkBitmap *reminder_mask;
+ GdkPixmap *recurrence_icon;
+ GdkBitmap *recurrence_mask;
+
+ /* Colors for drawing. */
+ GdkColor colors[E_DAY_VIEW_COLOR_LAST];
+
+ /* The normal & resizing cursors. */
+ GdkCursor *normal_cursor;
+ GdkCursor *move_cursor;
+ GdkCursor *resize_width_cursor;
+ GdkCursor *resize_height_cursor;
+
+ /* This remembers the last cursor set on the window. */
+ GdkCursor *last_cursor_set_in_top_canvas;
+ GdkCursor *last_cursor_set_in_main_canvas;
+
+ /*
+ * Editing, Selection & Dragging data
+ */
+
+ /* The horizontal bars to resize events in the main canvas. */
+ GnomeCanvasItem *main_canvas_top_resize_bar_item;
+ GnomeCanvasItem *main_canvas_bottom_resize_bar_item;
+
+ /* The event currently being edited. The day is -1 if no event is
+ being edited, or E_DAY_VIEW_LONG_EVENT if a long event is edited. */
+ gint editing_event_day;
+ gint editing_event_num;
+
+ /* This is a GnomeCanvasRect which is placed around an item while it
+ is being resized, so we can raise it above all other EText items. */
+ GnomeCanvasItem *resize_long_event_rect_item;
+ GnomeCanvasItem *resize_rect_item;
+ GnomeCanvasItem *resize_bar_item;
+
+ /* The event for which a popup menu is being displayed, as above. */
+ gint popup_event_day;
+ gint popup_event_num;
+
+ /* The currently selected region. If selection_start_col is -1 there is
+ no current selection. If start_row or end_row is -1 then the
+ selection is in the top canvas. */
+ gint selection_start_col;
+ gint selection_end_col;
+ gint selection_start_row;
+ gint selection_end_row;
+
+ /* This is TRUE if the user is selecting a region on the calendar. */
+ EDayViewDragPosition selection_drag_pos;
+
+ /* This is TRUE if the selection is in the top canvas only (i.e. if the
+ last motion event was in the top canvas). */
+ gboolean selection_in_top_canvas;
+
+ /* The last mouse position, relative to the main canvas window.
+ Used when auto-scrolling to update the selection. */
+ gint last_mouse_x;
+ gint last_mouse_y;
+
+ /* Auto-scroll info for when selecting an area or dragging an item. */
+ gint auto_scroll_timeout_id;
+ gint auto_scroll_delay;
+ gboolean auto_scroll_up;
+
+ /* These are used for the resize bars. */
+ gint resize_bars_event_day;
+ gint resize_bars_event_num;
+
+ /* These are used when resizing events. */
+ gint resize_event_day;
+ gint resize_event_num;
+ EDayViewPosition resize_drag_pos;
+ gint resize_start_row;
+ gint resize_end_row;
+
+ /* This is the event the mouse button was pressed on. If the button
+ is released we start editing it, but if the mouse is dragged we set
+ this to -1. */
+ gint pressed_event_day;
+ gint pressed_event_num;
+
+ /* These are used when dragging events. If drag_event_day is not -1 we
+ know that we are dragging one of the EDayView events around. */
+ gint drag_event_day;
+ gint drag_event_num;
+
+ /* The last mouse position when dragging, in the entire canvas. */
+ gint drag_event_x;
+ gint drag_event_y;
+
+ /* The offset of the mouse from the top of the event, in rows.
+ In the top canvas this is the offset from the left, in days. */
+ gint drag_event_offset;
+
+ /* The last day & row dragged to, so we know when we need to update
+ the dragged event's position. */
+ gint drag_last_day;
+ gint drag_last_row;
+
+ /* This is a GnomeCanvasRect which is placed around an item while it
+ is being resized, so we can raise it above all other EText items. */
+ GnomeCanvasItem *drag_long_event_rect_item;
+ GnomeCanvasItem *drag_long_event_item;
+ GnomeCanvasItem *drag_rect_item;
+ GnomeCanvasItem *drag_bar_item;
+ GnomeCanvasItem *drag_item;
+};
+
+struct _EDayViewClass
+{
+ GtkTableClass parent_class;
+};
+
+
+GtkType e_day_view_get_type (void);
+GtkWidget* e_day_view_new (void);
+
+void e_day_view_set_calendar (EDayView *day_view,
+ GnomeCalendar *calendar);
+
+void e_day_view_set_interval (EDayView *day_view,
+ time_t lower,
+ time_t upper);
+
+void e_day_view_update_event (EDayView *fullday,
+ iCalObject *ico,
+ int flags);
+
+
+
+
+gint e_day_view_get_days_shown (EDayView *day_view);
+void e_day_view_set_days_shown (EDayView *day_view,
+ gint days_shown);
+
+gint e_day_view_get_mins_per_row (EDayView *day_view);
+void e_day_view_set_mins_per_row (EDayView *day_view,
+ gint mins_per_row);
+
+
+
+/*
+ * Internal functions called by the associated canvas items.
+ */
+void e_day_view_check_layout (EDayView *day_view);
+gint e_day_view_convert_time_to_row (EDayView *day_view,
+ gint hour,
+ gint minute);
+gint e_day_view_convert_time_to_position (EDayView *day_view,
+ gint hour,
+ gint minute);
+gboolean e_day_view_get_event_position (EDayView *day_view,
+ gint day,
+ gint event_num,
+ gint *item_x,
+ gint *item_y,
+ gint *item_w,
+ gint *item_h);
+gboolean e_day_view_get_long_event_position (EDayView *day_view,
+ gint event_num,
+ gint *start_day,
+ gint *end_day,
+ gint *item_x,
+ gint *item_y,
+ gint *item_w,
+ gint *item_h);
+gboolean e_day_view_find_long_event_days (EDayView *day_view,
+ EDayViewEvent *event,
+ gint *start_day,
+ gint *end_day);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_DAY_VIEW_H_ */