aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/gui/e-week-view.c
diff options
context:
space:
mode:
authorDamon Chaplin <damon@helixcode.com>2000-04-24 22:01:46 +0800
committerDamon Chaplin <damon@src.gnome.org>2000-04-24 22:01:46 +0800
commit94a486a7b4207e617da30b172aa9873e0fbb0215 (patch)
tree032fd6642291a41b61a17692bf5a69e3dd41d316 /calendar/gui/e-week-view.c
parent0c3a86937674a6214f68746ee5a273e195228fde (diff)
downloadgsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar.gz
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar.bz2
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar.lz
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar.xz
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.tar.zst
gsoc2013-evolution-94a486a7b4207e617da30b172aa9873e0fbb0215.zip
added new source files and pixmaps, and removed old source files, which
2000-04-24 Damon Chaplin <damon@helixcode.com> * gui/Makefile.am: added new source files and pixmaps, and removed old source files, which can be deleted. * gui/e-week-view-titles-item.[hc]: * gui/e-week-view-main-item.[hc]: * gui/e-week-view-event-item.[hc]: * gui/e-week-view.[hc]: new files implementing the week/month views. * gui/yearview.xpm: * gui/monthview.xpm: * gui/weekview.xpm: * gui/workweekview.xpm: * gui/dayview.xpm: new pixmaps for the toolbar buttons. These aren't intended to be the final pixmaps. * gui/calendar-commands.c: added radio buttons to the toolbar to switch between the calendar views, and moved the am_pm_flag here so we can get rid of view-utils.c. * gui/gnome-cal.[hc]: made it a subclass of GtkVBox, rearranged the widgets into 2 notebooks, and added the selection_start_time and selection_end_time fields. * gui/goto.c: updated to use new selection time range. * gui/quick-view.c: added '#include <gtk/gtkwindow.h>' so it compiles. * gui/e-day-view.[hc]: changed the interface to support the new selection time range, got rid of a few debugging messages and changed a few bits. svn path=/trunk/; revision=2583
Diffstat (limited to 'calendar/gui/e-week-view.c')
-rw-r--r--calendar/gui/e-week-view.c2265
1 files changed, 2265 insertions, 0 deletions
diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c
new file mode 100644
index 0000000000..28ca5ed396
--- /dev/null
+++ b/calendar/gui/e-week-view.c
@@ -0,0 +1,2265 @@
+/* -*- 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
+ */
+
+/*
+ * EWeekView - displays the Week & Month views of the calendar.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gnome.h>
+#include "e-week-view.h"
+#include "e-week-view-event-item.h"
+#include "e-week-view-main-item.h"
+#include "e-week-view-titles-item.h"
+#include "main.h"
+#include <cal-util/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"
+
+#define E_WEEK_VIEW_SMALL_FONT \
+ "-adobe-utopia-regular-r-normal-*-*-100-*-*-p-*-iso8859-*"
+#define E_WEEK_VIEW_SMALL_FONT_FALLBACK \
+ "-adobe-helvetica-medium-r-normal-*-*-80-*-*-p-*-iso8859-*"
+
+/* We use a 7-bit field to store row numbers in EWeekViewEventSpan, so the
+ maximum number or rows we can allow is 127. It is very unlikely to be
+ reached anyway. */
+#define E_WEEK_VIEW_MAX_ROWS_PER_CELL 127
+
+static void e_week_view_class_init (EWeekViewClass *class);
+static void e_week_view_init (EWeekView *week_view);
+static void e_week_view_destroy (GtkObject *object);
+static void e_week_view_realize (GtkWidget *widget);
+static void e_week_view_unrealize (GtkWidget *widget);
+static void e_week_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void e_week_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void e_week_view_recalc_cell_sizes (EWeekView *week_view);
+static gint e_week_view_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_week_view_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_week_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+static void e_week_view_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static void e_week_view_draw_shadow (EWeekView *week_view);
+
+static gboolean e_week_view_on_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EWeekView *week_view);
+static gboolean e_week_view_on_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EWeekView *week_view);
+static gboolean e_week_view_on_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ EWeekView *week_view);
+static gint e_week_view_convert_position_to_day (EWeekView *week_view,
+ gint x,
+ gint y);
+static void e_week_view_update_selection (EWeekView *week_view,
+ gint day);
+
+static void e_week_view_reload_events (EWeekView *week_view);
+static void e_week_view_free_events (EWeekView *week_view);
+static int e_week_view_add_event (iCalObject *ico,
+ time_t start,
+ time_t end,
+ gpointer data);
+static void e_week_view_check_layout (EWeekView *week_view);
+static void e_week_view_layout_events (EWeekView *week_view);
+static void e_week_view_layout_event (EWeekView *week_view,
+ EWeekViewEvent *event,
+ guint8 *grid,
+ GArray *spans);
+static void e_week_view_ensure_events_sorted (EWeekView *week_view);
+static gint e_week_view_event_sort_func (const void *arg1,
+ const void *arg2);
+static void e_week_view_reshape_events (EWeekView *week_view);
+static void e_week_view_reshape_event_span (EWeekView *week_view,
+ gint event_num,
+ gint span_num);
+static gint e_week_view_find_day (EWeekView *week_view,
+ time_t time_to_find,
+ gboolean include_midnight_in_prev_day);
+static gint e_week_view_find_span_end (EWeekView *week_view,
+ gint day);
+static void e_week_view_recalc_day_starts (EWeekView *week_view,
+ time_t lower);
+static void e_week_view_on_adjustment_changed (GtkAdjustment *adjustment,
+ EWeekView *week_view);
+static void e_week_view_on_editing_started (EWeekView *week_view,
+ GnomeCanvasItem *item);
+static void e_week_view_on_editing_stopped (EWeekView *week_view,
+ GnomeCanvasItem *item);
+static gboolean e_week_view_find_event_from_item (EWeekView *week_view,
+ GnomeCanvasItem *item,
+ gint *event_num,
+ gint *span_num);
+static gboolean e_week_view_find_event_from_ico (EWeekView *week_view,
+ iCalObject *ico,
+ gint *event_num_return);
+static gboolean e_week_view_on_text_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EWeekView *week_view);
+static gint e_week_view_key_press (GtkWidget *widget, GdkEventKey *event);
+static void e_week_view_show_popup_menu (EWeekView *week_view,
+ GdkEventButton *event,
+ gint day);
+static void e_week_view_on_new_appointment (GtkWidget *widget,
+ gpointer data);
+
+static GtkTableClass *parent_class;
+
+
+GtkType
+e_week_view_get_type (void)
+{
+ static GtkType e_week_view_type = 0;
+
+ if (!e_week_view_type){
+ GtkTypeInfo e_week_view_info = {
+ "EWeekView",
+ sizeof (EWeekView),
+ sizeof (EWeekViewClass),
+ (GtkClassInitFunc) e_week_view_class_init,
+ (GtkObjectInitFunc) e_week_view_init,
+ NULL, /* reserved 1 */
+ NULL, /* reserved 2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ parent_class = gtk_type_class (GTK_TYPE_TABLE);
+ e_week_view_type = gtk_type_unique (GTK_TYPE_TABLE,
+ &e_week_view_info);
+ }
+
+ return e_week_view_type;
+}
+
+
+static void
+e_week_view_class_init (EWeekViewClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ /* Method override */
+ object_class->destroy = e_week_view_destroy;
+
+ widget_class->realize = e_week_view_realize;
+ widget_class->unrealize = e_week_view_unrealize;
+ widget_class->style_set = e_week_view_style_set;
+ widget_class->size_allocate = e_week_view_size_allocate;
+ widget_class->focus_in_event = e_week_view_focus_in;
+ widget_class->focus_out_event = e_week_view_focus_out;
+ widget_class->key_press_event = e_week_view_key_press;
+ widget_class->expose_event = e_week_view_expose_event;
+ widget_class->draw = e_week_view_draw;
+}
+
+
+static void
+e_week_view_init (EWeekView *week_view)
+{
+ GdkColormap *colormap;
+ gboolean success[E_WEEK_VIEW_COLOR_LAST];
+ gint nfailed;
+ GnomeCanvasGroup *canvas_group;
+ GtkObject *adjustment;
+
+ GTK_WIDGET_SET_FLAGS (week_view, GTK_CAN_FOCUS);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (week_view));
+
+ week_view->calendar = NULL;
+
+ week_view->events = g_array_new (FALSE, FALSE,
+ sizeof (EWeekViewEvent));
+ week_view->events_sorted = TRUE;
+ week_view->events_need_layout = FALSE;
+ week_view->events_need_reshape = FALSE;
+
+ week_view->spans = NULL;
+
+ week_view->display_month = FALSE;
+ week_view->rows = 6;
+ week_view->columns = 2;
+ week_view->compress_weekend = TRUE;
+
+ g_date_clear (&week_view->base_date, 1);
+ g_date_clear (&week_view->first_day_shown, 1);
+
+ week_view->row_height = 10;
+ week_view->rows_per_cell = 1;
+
+ week_view->selection_start_day = -1;
+ week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
+
+ week_view->pressed_event_num = -1;
+ week_view->editing_event_num = -1;
+
+ /* Create the small font. */
+ week_view->use_small_font = TRUE;
+ week_view->small_font = gdk_font_load (E_WEEK_VIEW_SMALL_FONT);
+ if (!week_view->small_font)
+ week_view->small_font = gdk_font_load (E_WEEK_VIEW_SMALL_FONT_FALLBACK);
+ if (!week_view->small_font)
+ g_warning ("Couldn't load font");
+
+ /* Allocate the colors. */
+ week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS].red = 0xeded;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS].green = 0xeded;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS].blue = 0xeded;
+
+ week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS].red = 65535;
+ week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS].green = 65535;
+ week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS].blue = 65535;
+
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].red = 0xd6d6;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].green = 0xd6d6;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND].blue = 0xd6d6;
+
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER].red = 0;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER].green = 0;
+ week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER].blue = 0;
+
+ nfailed = gdk_colormap_alloc_colors (colormap, week_view->colors,
+ E_WEEK_VIEW_COLOR_LAST, FALSE,
+ TRUE, success);
+ if (nfailed)
+ g_warning ("Failed to allocate all colors");
+
+
+ /*
+ * Titles Canvas. Note that we don't show it is only shown in the
+ * Month view.
+ */
+ week_view->titles_canvas = e_canvas_new ();
+ gtk_table_attach (GTK_TABLE (week_view), week_view->titles_canvas,
+ 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->titles_canvas)->root);
+
+ week_view->titles_canvas_item =
+ gnome_canvas_item_new (canvas_group,
+ e_week_view_titles_item_get_type (),
+ "EWeekViewTitlesItem::week_view", week_view,
+ NULL);
+
+ /*
+ * Main Canvas
+ */
+ week_view->main_canvas = e_canvas_new ();
+ gtk_table_attach (GTK_TABLE (week_view), week_view->main_canvas,
+ 1, 2, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
+ gtk_widget_show (week_view->main_canvas);
+
+ canvas_group = GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root);
+
+ week_view->main_canvas_item =
+ gnome_canvas_item_new (canvas_group,
+ e_week_view_main_item_get_type (),
+ "EWeekViewMainItem::week_view", week_view,
+ NULL);
+
+ gtk_signal_connect_after (GTK_OBJECT (week_view->main_canvas),
+ "button_press_event",
+ GTK_SIGNAL_FUNC (e_week_view_on_button_press),
+ week_view);
+ gtk_signal_connect_after (GTK_OBJECT (week_view->main_canvas),
+ "button_release_event",
+ GTK_SIGNAL_FUNC (e_week_view_on_button_release),
+ week_view);
+ gtk_signal_connect_after (GTK_OBJECT (week_view->main_canvas),
+ "motion_notify_event",
+ GTK_SIGNAL_FUNC (e_week_view_on_motion),
+ week_view);
+
+ /*
+ * Scrollbar.
+ */
+ adjustment = gtk_adjustment_new (0, -52, 52, 1, 1, 1);
+ gtk_signal_connect (adjustment, "value_changed",
+ GTK_SIGNAL_FUNC (e_week_view_on_adjustment_changed),
+ week_view);
+
+ week_view->vscrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (adjustment));
+ gtk_table_attach (GTK_TABLE (week_view), week_view->vscrollbar,
+ 2, 3, 1, 2, 0, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (week_view->vscrollbar);
+
+
+ /* Create the pixmaps. */
+ week_view->reminder_icon = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &week_view->reminder_mask, NULL, bell_xpm);
+ week_view->recurrence_icon = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &week_view->recurrence_mask, NULL, recur_xpm);
+
+
+ /* Create the cursors. */
+ week_view->normal_cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
+ week_view->move_cursor = gdk_cursor_new (GDK_FLEUR);
+ week_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+ week_view->last_cursor_set = NULL;
+}
+
+
+/**
+ * e_week_view_new:
+ * @Returns: a new #EWeekView.
+ *
+ * Creates a new #EWeekView.
+ **/
+GtkWidget *
+e_week_view_new (void)
+{
+ GtkWidget *week_view;
+
+ week_view = GTK_WIDGET (gtk_type_new (e_week_view_get_type ()));
+
+ return week_view;
+}
+
+
+static void
+e_week_view_destroy (GtkObject *object)
+{
+ EWeekView *week_view;
+
+ week_view = E_WEEK_VIEW (object);
+
+ /* FIXME: free the colors. In EDayView as well. */
+ /* FIXME: free the events and the spans. In EDayView as well? */
+
+ if (week_view->small_font)
+ gdk_font_unref (week_view->small_font);
+
+ gdk_cursor_destroy (week_view->normal_cursor);
+ gdk_cursor_destroy (week_view->move_cursor);
+ gdk_cursor_destroy (week_view->resize_width_cursor);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+
+static void
+e_week_view_realize (GtkWidget *widget)
+{
+ EWeekView *week_view;
+
+ if (GTK_WIDGET_CLASS (parent_class)->realize)
+ (*GTK_WIDGET_CLASS (parent_class)->realize)(widget);
+
+ week_view = E_WEEK_VIEW (widget);
+ week_view->main_gc = gdk_gc_new (widget->window);
+}
+
+
+static void
+e_week_view_unrealize (GtkWidget *widget)
+{
+ EWeekView *week_view;
+
+ week_view = E_WEEK_VIEW (widget);
+
+ gdk_gc_unref (week_view->main_gc);
+ week_view->main_gc = NULL;
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget);
+}
+
+
+static void
+e_week_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ EWeekView *week_view;
+ GdkFont *font;
+ gint day, day_width, max_day_width, max_abbr_day_width;
+ gint month, month_width, max_month_width, max_abbr_month_width;
+ GDate date;
+ gchar buffer[128];
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ (*GTK_WIDGET_CLASS (parent_class)->style_set)(widget, previous_style);
+
+ week_view = E_WEEK_VIEW (widget);
+ font = widget->style->font;
+
+ /* Recalculate the height of each row based on the font size. */
+ week_view->row_height = font->ascent + font->descent + E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2 + E_WEEK_VIEW_EVENT_TEXT_Y_PAD * 2;
+ week_view->row_height = MAX (week_view->row_height, E_WEEK_VIEW_ICON_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD + E_WEEK_VIEW_EVENT_BORDER_HEIGHT * 2);
+
+ /* Set the height of the top canvas. */
+ gtk_widget_set_usize (week_view->titles_canvas, -1,
+ font->ascent + font->descent + 5);
+
+ /* Save the sizes of various strings in the font, so we can quickly
+ decide which date formats to use. */
+ g_date_clear (&date, 1);
+ g_date_set_dmy (&date, 27, 3, 2000); /* Must be a Monday. */
+
+ max_day_width = 0;
+ max_abbr_day_width = 0;
+ for (day = 0; day < 7; day++) {
+ g_date_strftime (buffer, 128, "%A", &date);
+ day_width = gdk_string_width (font, buffer);
+ week_view->day_widths[day] = day_width;
+ max_day_width = MAX (max_day_width, day_width);
+
+ g_date_strftime (buffer, 128, "%a", &date);
+ day_width = gdk_string_width (font, buffer);
+ week_view->abbr_day_widths[day] = day_width;
+ max_abbr_day_width = MAX (max_abbr_day_width, day_width);
+
+ g_date_add_days (&date, 1);
+ }
+
+ max_month_width = 0;
+ max_abbr_month_width = 0;
+ for (month = 0; month < 12; month++) {
+ g_date_set_month (&date, month + 1);
+
+ g_date_strftime (buffer, 128, "%B", &date);
+ month_width = gdk_string_width (font, buffer);
+ week_view->month_widths[month] = month_width;
+ max_month_width = MAX (max_month_width, month_width);
+
+ g_date_strftime (buffer, 128, "%b", &date);
+ month_width = gdk_string_width (font, buffer);
+ week_view->abbr_month_widths[month] = month_width;
+ max_abbr_month_width = MAX (max_abbr_month_width, month_width);
+ }
+
+ week_view->space_width = gdk_string_width (font, " ");
+ week_view->colon_width = gdk_string_width (font, ":");
+ week_view->slash_width = gdk_string_width (font, "/");
+ week_view->digit_width = gdk_string_width (font, "5");
+ if (week_view->small_font)
+ week_view->small_digit_width = gdk_string_width (week_view->small_font, "5");
+ week_view->max_day_width = max_day_width;
+ week_view->max_abbr_day_width = max_abbr_day_width;
+ week_view->max_month_width = max_month_width;
+ week_view->max_abbr_month_width = max_abbr_month_width;
+}
+
+
+/* This recalculates the sizes of each column. */
+static void
+e_week_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ EWeekView *week_view;
+ gint width, height, time_width;
+ gdouble old_x2, old_y2, new_x2, new_y2;
+ GdkFont *font;
+
+ week_view = E_WEEK_VIEW (widget);
+ font = widget->style->font;
+
+ (*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+ e_week_view_recalc_cell_sizes (week_view);
+
+ /* Calculate the number of rows of events in each cell, for the large
+ cells and the compressed weekend cells. */
+ if (week_view->display_month) {
+ week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
+ + font->ascent + font->descent
+ + E_WEEK_VIEW_DATE_B_PAD;
+ } else {
+ week_view->events_y_offset = E_WEEK_VIEW_DATE_T_PAD
+ + font->ascent + font->descent
+ + E_WEEK_VIEW_DATE_LINE_T_PAD + 1
+ + E_WEEK_VIEW_DATE_LINE_B_PAD;
+ }
+
+ height = week_view->row_heights[0];
+ week_view->rows_per_cell = (height * 2 - week_view->events_y_offset)
+ / (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
+ week_view->rows_per_cell = MIN (week_view->rows_per_cell,
+ E_WEEK_VIEW_MAX_ROWS_PER_CELL);
+
+ week_view->rows_per_compressed_cell =
+ (height - week_view->events_y_offset)
+ / (week_view->row_height + E_WEEK_VIEW_EVENT_Y_SPACING);
+ week_view->rows_per_compressed_cell = MIN (week_view->rows_per_compressed_cell,
+ E_WEEK_VIEW_MAX_ROWS_PER_CELL);
+
+ /* Determine which time format to use, based on the width of the cells.
+ We only allow the time to take up about half of the width. */
+ width = week_view->col_widths[0];
+
+ week_view->time_format = E_WEEK_VIEW_TIME_NONE;
+ if (week_view->use_small_font && week_view->small_font) {
+ time_width = week_view->digit_width * 2
+ + week_view->small_digit_width * 2;
+ if (width / 2 > time_width * 2 + week_view->space_width)
+ week_view->time_format = E_WEEK_VIEW_TIME_BOTH_SMALL_MIN;
+ else if (width / 2 > time_width)
+ week_view->time_format = E_WEEK_VIEW_TIME_START_SMALL_MIN;
+ } else {
+ time_width = week_view->digit_width * 4
+ + week_view->colon_width;
+ if (width / 2 > time_width * 2 + week_view->space_width)
+ week_view->time_format = E_WEEK_VIEW_TIME_BOTH;
+ else if (width / 2 > time_width)
+ week_view->time_format = E_WEEK_VIEW_TIME_START;
+ }
+
+ /* Set the scroll region of the top canvas to its allocated size. */
+ gnome_canvas_get_scroll_region (GNOME_CANVAS (week_view->titles_canvas),
+ NULL, NULL, &old_x2, &old_y2);
+ new_x2 = week_view->titles_canvas->allocation.width - 1;
+ new_y2 = week_view->titles_canvas->allocation.height - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (week_view->titles_canvas),
+ 0, 0, new_x2, new_y2);
+
+
+ /* 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 (week_view->main_canvas),
+ NULL, NULL, &old_x2, &old_y2);
+ new_x2 = week_view->main_canvas->allocation.width - 1;
+ new_y2 = week_view->main_canvas->allocation.height - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (week_view->main_canvas),
+ 0, 0, new_x2, new_y2);
+
+ /* Flag that we need to reshape the events. */
+ if (old_x2 != new_x2 || old_y2 != new_y2) {
+ week_view->events_need_reshape = TRUE;
+ e_week_view_check_layout (week_view);
+ }
+}
+
+
+static void
+e_week_view_recalc_cell_sizes (EWeekView *week_view)
+{
+ gfloat width, height, offset;
+ gint row, col;
+
+ if (week_view->display_month) {
+ week_view->rows = 10;
+ week_view->columns = 6;
+ } else {
+ week_view->rows = 6;
+ week_view->columns = 2;
+ }
+
+ /* 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.
+ We also add one to the width so that the right border of the last
+ column is off the edge of the displayed area. */
+ width = week_view->main_canvas->allocation.width + 1;
+ width /= week_view->columns;
+ offset = 0;
+ for (col = 0; col <= week_view->columns; col++) {
+ week_view->col_offsets[col] = floor (offset + 0.5);
+ offset += width;
+ }
+
+ /* Calculate the cell widths based on the offsets. */
+ for (col = 0; col < week_view->columns; col++) {
+ week_view->col_widths[col] = week_view->col_offsets[col + 1]
+ - week_view->col_offsets[col];
+ }
+
+ /* Now do the same for the row heights. */
+ height = week_view->main_canvas->allocation.height + 1;
+ height /= week_view->rows;
+ offset = 0;
+ for (row = 0; row <= week_view->rows; row++) {
+ week_view->row_offsets[row] = floor (offset + 0.5);
+ offset += height;
+ }
+
+ /* Calculate the cell heights based on the offsets. */
+ for (row = 0; row < week_view->rows; row++) {
+ week_view->row_heights[row] = week_view->row_offsets[row + 1]
+ - week_view->row_offsets[row];
+ }
+}
+
+
+static gint
+e_week_view_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+ gtk_widget_draw_focus (widget);
+
+ return FALSE;
+}
+
+
+static gint
+e_week_view_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ EWeekView *week_view;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ week_view = E_WEEK_VIEW (widget);
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ /* Get rid of selection. */
+ week_view->selection_start_day = -1;
+
+ gtk_widget_queue_draw (week_view->main_canvas);
+
+ return FALSE;
+}
+
+
+/* This draws a shadow around the top display and main display. */
+static gint
+e_week_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ EWeekView *week_view;
+
+ week_view = E_WEEK_VIEW (widget);
+
+ e_week_view_draw_shadow (week_view);
+
+ if (GTK_WIDGET_CLASS (parent_class)->expose_event)
+ (*GTK_WIDGET_CLASS (parent_class)->expose_event)(widget, event);
+
+ return FALSE;
+}
+
+
+static void
+e_week_view_draw (GtkWidget *widget,
+ GdkRectangle *area)
+{
+ EWeekView *week_view;
+
+ week_view = E_WEEK_VIEW (widget);
+
+ e_week_view_draw_shadow (week_view);
+
+ if (GTK_WIDGET_CLASS (parent_class)->draw)
+ (*GTK_WIDGET_CLASS (parent_class)->draw)(widget, area);
+}
+
+
+static void
+e_week_view_draw_shadow (EWeekView *week_view)
+{
+ gint x1, y1, x2, y2;
+ GtkStyle *style;
+ GdkGC *light_gc, *dark_gc;
+ GdkWindow *window;
+
+ /* Draw the shadow around the graphical displays. */
+ x1 = week_view->main_canvas->allocation.x - 1;
+ y1 = week_view->main_canvas->allocation.y - 1;
+ x2 = x1 + week_view->main_canvas->allocation.width + 2;
+ y2 = y1 + week_view->main_canvas->allocation.height + 2;
+
+ style = GTK_WIDGET (week_view)->style;
+ dark_gc = style->dark_gc[GTK_STATE_NORMAL];
+ light_gc = style->light_gc[GTK_STATE_NORMAL];
+
+ window = GTK_WIDGET (week_view)->window;
+ gdk_draw_line (window, dark_gc, x1, y1, x1, y2);
+ gdk_draw_line (window, dark_gc, x1, y1, x2, y1);
+ gdk_draw_line (window, light_gc, x2, y1, x2, y2);
+ gdk_draw_line (window, light_gc, x1, y2, x2, y2);
+}
+
+
+void
+e_week_view_set_calendar (EWeekView *week_view,
+ GnomeCalendar *calendar)
+{
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ week_view->calendar = calendar;
+
+ /* FIXME: free current events? */
+}
+
+
+/* This sets the selected time range. The EWeekView will show the corresponding
+ month and the days between start_time and end_time will be selected.
+ To select a single day, use the same value for start_time & end_time. */
+void
+e_week_view_set_selected_time_range (EWeekView *week_view,
+ time_t start_time,
+ time_t end_time)
+{
+ GDate date, base_date, end_date;
+ gint day_offset, num_days;
+ gboolean update_adjustment_value = FALSE;
+
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ g_date_clear (&date, 1);
+ g_date_set_time (&date, start_time);
+
+ if (week_view->display_month) {
+ /* Find the number of days since the start of the month. */
+ day_offset = g_date_day (&date) - 1;
+
+ /* Find the 1st Monday at or before the start of the month. */
+ base_date = date;
+ g_date_set_day (&base_date, 1);
+ day_offset += g_date_weekday (&base_date) - 1;
+ } else {
+ /* Find the 1st Monday at or before the given day. */
+ day_offset = g_date_weekday (&date) - 1;
+ }
+
+ /* Calculate the base date, i.e. the first day shown when the
+ scrollbar adjustment value is 0. */
+ base_date = date;
+ g_date_subtract_days (&base_date, day_offset);
+
+ /* See if we need to update the base date. */
+ if (!g_date_valid (&week_view->base_date)
+ || g_date_compare (&week_view->base_date, &base_date)) {
+ week_view->base_date = base_date;
+ update_adjustment_value = TRUE;
+ }
+
+ /* See if we need to update the first day shown. */
+ if (!g_date_valid (&week_view->first_day_shown)
+ || g_date_compare (&week_view->first_day_shown, &base_date)) {
+ week_view->first_day_shown = base_date;
+ start_time = time_add_day (start_time, -day_offset);
+ start_time = time_day_begin (start_time);
+ e_week_view_recalc_day_starts (week_view, start_time);
+ e_week_view_reload_events (week_view);
+ }
+
+ /* Set the selection to the given days. */
+ week_view->selection_start_day = g_date_julian (&date)
+ - g_date_julian (&base_date);
+ if (end_time == start_time
+ || end_time <= time_add_day (start_time, 1))
+ week_view->selection_end_day = week_view->selection_start_day;
+ else {
+ g_date_clear (&end_date, 1);
+ g_date_set_time (&end_date, end_time - 60);
+ week_view->selection_end_day = g_date_julian (&end_date)
+ - g_date_julian (&base_date);
+ }
+
+ /* Make sure the selection is valid. */
+ num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ num_days--;
+ week_view->selection_start_day = CLAMP (week_view->selection_start_day,
+ 0, num_days);
+ week_view->selection_end_day = CLAMP (week_view->selection_end_day,
+ week_view->selection_start_day,
+ num_days);
+
+ /* Reset the adjustment value to 0 if the base address has changed.
+ Note that we do this after updating first_day_shown so that our
+ signal handler will not try to reload the events. */
+ if (update_adjustment_value)
+ gtk_adjustment_set_value (GTK_RANGE (week_view->vscrollbar)->adjustment, 0);
+
+
+ gtk_widget_queue_draw (week_view->main_canvas);
+}
+
+
+/* Recalculates the time_t corresponding to the start of each day. */
+static void
+e_week_view_recalc_day_starts (EWeekView *week_view,
+ time_t lower)
+{
+ gint num_days, day;
+ time_t tmp_time;
+
+ num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+
+ tmp_time = lower;
+ week_view->day_starts[0] = tmp_time;
+ for (day = 1; day <= num_days; day++) {
+ /* FIXME: There is a bug in time_add_day(). */
+#if 0
+ g_print ("Day:%i - %s\n", day, ctime (&tmp_time));
+#endif
+ tmp_time = time_add_day (tmp_time, 1);
+ week_view->day_starts[day] = tmp_time;
+ }
+}
+
+
+gboolean
+e_week_view_get_display_month (EWeekView *week_view)
+{
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
+
+ return week_view->display_month;
+}
+
+
+void
+e_week_view_set_display_month (EWeekView *week_view,
+ gboolean display_month)
+{
+ GtkAdjustment *adjustment;
+ gint page_increment, page_size;
+
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ if (week_view->display_month == display_month)
+ return;
+
+ week_view->display_month = display_month;
+
+ if (display_month) {
+ gtk_widget_show (week_view->titles_canvas);
+ page_increment = 4;
+ page_size = 5;
+ } else {
+ gtk_widget_hide (week_view->titles_canvas);
+ page_increment = page_size = 1;
+ }
+
+ adjustment = GTK_RANGE (week_view->vscrollbar)->adjustment;
+ adjustment->page_increment = page_increment;
+ adjustment->page_size = page_size;
+ gtk_adjustment_changed (adjustment);
+
+ /* FIXME: Need to change start date and adjustment value? */
+
+ e_week_view_recalc_day_starts (week_view, week_view->day_starts[0]);
+ e_week_view_recalc_cell_sizes (week_view);
+ e_week_view_reload_events (week_view);
+}
+
+
+gboolean
+e_week_view_get_compress_weekend (EWeekView *week_view)
+{
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
+
+ return week_view->compress_weekend;
+}
+
+
+void
+e_week_view_set_compress_weekend (EWeekView *week_view,
+ gboolean compress)
+{
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ if (week_view->compress_weekend == compress)
+ return;
+
+ week_view->compress_weekend = compress;
+
+ /* The option only affects the month view. */
+ if (!week_view->display_month)
+ return;
+
+ /* FIXME: Need to update layout. */
+}
+
+
+void
+e_week_view_update_event (EWeekView *week_view,
+ iCalObject *ico,
+ int flags)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ gint event_num, num_days, span_num;
+ gboolean one_day_event;
+
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ /* We only care about events. */
+ if (ico && ico->type != ICAL_EVENT)
+ return;
+
+ /* If we don't have a valid date set yet, just return. */
+ if (!g_date_valid (&week_view->first_day_shown))
+ return;
+
+ /* If one non-recurring event was added, we can just add it. */
+ if (flags == CHANGE_NEW && !ico->recur) {
+ num_days = week_view->display_month
+ ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ if (ico->dtstart < week_view->day_starts[num_days]
+ && ico->dtend > week_view->day_starts[0]) {
+ e_week_view_add_event (ico, ico->dtstart, ico->dtend,
+ week_view);
+ e_week_view_check_layout (week_view);
+ gtk_widget_queue_draw (week_view->main_canvas);
+ }
+ return;
+
+ /* If only the summary changed, we can update that easily.
+ Though we have to update all spans of the event. */
+ } else if (!(flags & ~CHANGE_SUMMARY)) {
+ if (e_week_view_find_event_from_ico (week_view, ico,
+ &event_num)) {
+ event = &g_array_index (week_view->events,
+ EWeekViewEvent, event_num);
+ one_day_event = e_week_view_is_one_day_event (week_view, event_num);
+ for (span_num = 0; span_num < event->num_spans;
+ span_num++) {
+ span = &g_array_index (week_view->spans,
+ EWeekViewEventSpan,
+ event->spans_index
+ + span_num);
+
+ if (span->text_item) {
+ gnome_canvas_item_set (span->text_item,
+ "text", ico->summary ? ico->summary : "",
+ NULL);
+
+ if (!one_day_event)
+ e_week_view_reshape_event_span (week_view, event_num, span_num);
+ }
+ }
+
+ return;
+ }
+ }
+
+ e_week_view_reload_events (week_view);
+}
+
+
+void
+e_week_view_get_day_position (EWeekView *week_view,
+ gint day,
+ gint *day_x,
+ gint *day_y,
+ gint *day_w,
+ gint *day_h)
+{
+ gint week, day_of_week, row;
+
+ *day_x = *day_y = *day_w = *day_h = 0;
+ g_return_if_fail (day >= 0);
+
+ if (week_view->display_month) {
+ g_return_if_fail (day < E_WEEK_VIEW_MAX_WEEKS * 7);
+
+ week = day / 7;
+ day_of_week = day % 7;
+ if (week_view->compress_weekend && day_of_week >= 5) {
+ /* In the compressed view Saturday is above Sunday and
+ both have just one row as opposed to 2 for all the
+ other days. */
+ if (day_of_week == 5) {
+ *day_y = week_view->row_offsets[week * 2];
+ *day_h = week_view->row_heights[week * 2];
+ } else {
+ *day_y = week_view->row_offsets[week * 2 + 1];
+ *day_h = week_view->row_heights[week * 2 + 1];
+ }
+ /* Both Saturday and Sunday are in the 6th column. */
+ *day_x = week_view->col_offsets[5];
+ *day_w = week_view->col_widths[5];
+ } else {
+ *day_y = week_view->row_offsets[week * 2];
+ *day_h = week_view->row_heights[week * 2]
+ + week_view->row_heights[week * 2 + 1];
+ *day_x = week_view->col_offsets[day_of_week];
+ *day_w = week_view->col_widths[day_of_week];
+ }
+ } else {
+ g_return_if_fail (day < 7);
+
+ /* The week view has Mon, Tue & Wed down the left column and
+ Thu, Fri & Sat/Sun down the right. */
+ if (day < 3) {
+ *day_x = week_view->col_offsets[0];
+ *day_w = week_view->col_widths[0];
+ } else {
+ *day_x = week_view->col_offsets[1];
+ *day_w = week_view->col_widths[1];
+ }
+
+ if (day < 5) {
+ row = (day % 3) * 2;
+ *day_y = week_view->row_offsets[row];
+ *day_h = week_view->row_heights[row]
+ + week_view->row_heights[row + 1];
+ } else {
+ /* Saturday & Sunday. */
+ *day_y = week_view->row_offsets[day - 1];
+ *day_h = week_view->row_heights[day - 1];
+ }
+ }
+}
+
+
+/* Returns the bounding box for a span of an event. Usually this can easily
+ be determined by the start & end days and row of the span, which are set in
+ e_week_view_layout_event(). Though we need a special case for the weekends
+ when they are compressed, since the span may not fit. */
+gboolean
+e_week_view_get_span_position (EWeekView *week_view,
+ gint event_num,
+ gint span_num,
+ gint *span_x,
+ gint *span_y,
+ gint *span_w)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ gint end_day_of_week, num_days;
+ gint start_x, start_y, start_w, start_h;
+ gint end_x, end_y, end_w, end_h;
+
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
+ g_return_val_if_fail (event_num < week_view->events->len, FALSE);
+
+ event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
+
+ g_return_val_if_fail (span_num < event->num_spans, FALSE);
+
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + span_num);
+
+ if (span->row >= week_view->rows_per_cell)
+ return FALSE;
+
+ end_day_of_week = (span->start_day + span->num_days - 1) % 7;
+ num_days = span->num_days;
+ /* Check if the row will not be visible in compressed cells. */
+ if (span->row >= week_view->rows_per_compressed_cell) {
+ if (week_view->display_month) {
+ if (week_view->compress_weekend) {
+ /* If it ends on a Saturday and is 1 day long
+ we skip it, else we shorten it. If it ends
+ on a Sunday it must be 1 day long and we
+ skip it. */
+ if (end_day_of_week == 5) { /* Sat */
+ if (num_days == 1) {
+ return FALSE;
+ } else {
+ num_days--;
+ }
+ } else if (end_day_of_week == 6) { /* Sun */
+ return FALSE;
+ }
+ }
+ } else {
+ /* All spans are 1 day long in the week view, so we
+ just skip it. */
+ if (end_day_of_week > 4)
+ return FALSE;
+ }
+ }
+
+ e_week_view_get_day_position (week_view, span->start_day,
+ &start_x, &start_y, &start_w, &start_h);
+ *span_y = start_y + week_view->events_y_offset
+ + span->row * (week_view->row_height
+ + E_WEEK_VIEW_EVENT_Y_SPACING);
+ if (num_days == 1) {
+ *span_x = start_x;
+ *span_w = start_w;
+ } else {
+ e_week_view_get_day_position (week_view,
+ span->start_day + num_days - 1,
+ &end_x, &end_y, &end_w, &end_h);
+ *span_x = start_x;
+ *span_w = end_x - start_x + end_w;
+ }
+
+ return TRUE;
+}
+
+
+
+static gboolean
+e_week_view_on_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ EWeekView *week_view)
+{
+ gint x, y, day;
+
+ /* If an event is pressed, just return. */
+ if (week_view->pressed_event_num != -1)
+ return FALSE;
+
+ /* Convert the mouse position to a week & day. */
+ x = event->x;
+ y = event->y;
+ day = e_week_view_convert_position_to_day (week_view, x, y);
+ if (day == -1)
+ return FALSE;
+
+ /* Start the selection drag. */
+ if (event->button == 1) {
+ if (!GTK_WIDGET_HAS_FOCUS (week_view))
+ gtk_widget_grab_focus (GTK_WIDGET (week_view));
+
+ if (gdk_pointer_grab (GTK_LAYOUT (widget)->bin_window, FALSE,
+ GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK,
+ FALSE, NULL, event->time) == 0) {
+ week_view->selection_start_day = day;
+ week_view->selection_end_day = day;
+ week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
+
+ /* FIXME: Optimise? */
+ gtk_widget_queue_draw (week_view->main_canvas);
+ }
+ } else if (event->button == 3) {
+ e_week_view_show_popup_menu (week_view, event, day);
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_week_view_on_button_release (GtkWidget *widget,
+ GdkEventButton *event,
+ EWeekView *week_view)
+{
+ time_t start, end;
+
+ if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
+ week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_NONE;
+ gdk_pointer_ungrab (event->time);
+ start = week_view->day_starts[week_view->selection_start_day];
+ end = week_view->day_starts[week_view->selection_end_day + 1];
+ gnome_calendar_set_selected_time_range (week_view->calendar,
+ start, end);
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_week_view_on_motion (GtkWidget *widget,
+ GdkEventMotion *mevent,
+ EWeekView *week_view)
+{
+ gint x, y, day;
+
+#if 0
+ g_print ("In e_week_view_on_motion\n");
+#endif
+
+ /* Convert the mouse position to a week & day. */
+ x = mevent->x;
+ y = mevent->y;
+ day = e_week_view_convert_position_to_day (week_view, x, y);
+ if (day == -1)
+ return FALSE;
+
+ if (week_view->selection_drag_pos != E_WEEK_VIEW_DRAG_NONE) {
+ e_week_view_update_selection (week_view, day);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Converts a position in the canvas window to a day offset from the first
+ day displayed. Returns -1 if the position is outside the grid. */
+static gint
+e_week_view_convert_position_to_day (EWeekView *week_view,
+ gint x,
+ gint y)
+{
+ gint col, row, grid_x = -1, grid_y = -1, week, day;
+
+ /* First we convert it to a grid position. */
+ for (col = 0; col <= week_view->columns; col++) {
+ if (x < week_view->col_offsets[col]) {
+ grid_x = col - 1;
+ break;
+ }
+ }
+
+ for (row = 0; row <= week_view->rows; row++) {
+ if (y < week_view->row_offsets[row]) {
+ grid_y = row - 1;
+ break;
+ }
+ }
+
+ /* If the mouse is outside the grid return FALSE. */
+ if (grid_x == -1 || grid_y == -1)
+ return -1;
+
+ /* Now convert the grid position to a week and day. */
+ if (week_view->display_month) {
+ week = grid_y / 2;
+ if (week_view->compress_weekend && grid_x == 5
+ && grid_y % 2 == 1)
+ day = 6;
+ else
+ day = grid_x;
+ } else {
+ week = 0;
+ if (grid_x == 0)
+ day = grid_y / 2;
+ else if (grid_y == 5)
+ day = 6;
+ else
+ day = grid_y / 2 + 3;
+ }
+
+ return week * 7 + day;
+}
+
+
+static void
+e_week_view_update_selection (EWeekView *week_view,
+ gint day)
+{
+ gint tmp_day;
+ gboolean need_redraw = FALSE;
+
+#if 0
+ g_print ("Updating selection %i,%i\n", week, day);
+#endif
+
+ if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START) {
+ if (day != week_view->selection_start_day) {
+ need_redraw = TRUE;
+ week_view->selection_start_day = day;
+ }
+ } else {
+ if (day != week_view->selection_end_day) {
+ need_redraw = TRUE;
+ week_view->selection_end_day = day;
+ }
+ }
+
+ /* Switch the drag position if necessary. */
+ if (week_view->selection_start_day > week_view->selection_end_day) {
+ tmp_day = week_view->selection_start_day;
+ week_view->selection_start_day = week_view->selection_end_day;
+ week_view->selection_end_day = tmp_day;
+ if (week_view->selection_drag_pos == E_WEEK_VIEW_DRAG_START)
+ week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_END;
+ else
+ week_view->selection_drag_pos = E_WEEK_VIEW_DRAG_START;
+ }
+
+ /* FIXME: Optimise? */
+ if (need_redraw) {
+ gtk_widget_queue_draw (week_view->main_canvas);
+ }
+}
+
+
+static void
+e_week_view_reload_events (EWeekView *week_view)
+{
+ gint num_days;
+
+ e_week_view_free_events (week_view);
+
+ if (week_view->calendar
+ && g_date_valid (&week_view->first_day_shown)) {
+ num_days = week_view->display_month
+ ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ calendar_iterate (week_view->calendar,
+ week_view->day_starts[0],
+ week_view->day_starts[num_days],
+ e_week_view_add_event,
+ week_view);
+ }
+
+ e_week_view_check_layout (week_view);
+
+ gtk_widget_queue_draw (week_view->main_canvas);
+}
+
+
+static void
+e_week_view_free_events (EWeekView *week_view)
+{
+ EWeekViewEventSpan *span;
+ gint span_num;
+
+ /* There is nothing to free in the event structs at present. */
+#if 0
+ for (event_num = 0; event_num < week_view->events->len; event_num++) {
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ event_num);
+
+ if (event->canvas_item)
+ gtk_object_destroy (GTK_OBJECT (event->canvas_item));
+ }
+#endif
+ g_array_set_size (week_view->events, 0);
+
+ /* Destroy all the old canvas items. */
+ if (week_view->spans) {
+ for (span_num = 0; span_num < week_view->spans->len;
+ span_num++) {
+ span = &g_array_index (week_view->spans,
+ EWeekViewEventSpan, span_num);
+ if (span->background_item)
+ gtk_object_destroy (GTK_OBJECT (span->background_item));
+ if (span->text_item)
+ gtk_object_destroy (GTK_OBJECT (span->text_item));
+ }
+ g_array_free (week_view->spans, TRUE);
+ week_view->spans = NULL;
+ }
+}
+
+
+/* This adds one event to the view, adding it to the appropriate array. */
+static int
+e_week_view_add_event (iCalObject *ico,
+ time_t start,
+ time_t end,
+ gpointer data)
+
+{
+ EWeekView *week_view;
+ EWeekViewEvent event;
+ gint num_days;
+ struct tm start_tm, end_tm;
+
+ week_view = E_WEEK_VIEW (data);
+
+ /* Check that the event times are valid. */
+ num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ g_return_val_if_fail (start <= end, TRUE);
+ g_return_val_if_fail (start < week_view->day_starts[num_days], TRUE);
+ g_return_val_if_fail (end > week_view->day_starts[0], TRUE);
+
+ start_tm = *(localtime (&start));
+ end_tm = *(localtime (&end));
+
+ event.ico = ico;
+ event.start = start;
+ event.end = end;
+ event.spans_index = 0;
+ event.num_spans = 0;
+
+ event.start_minute = start_tm.tm_hour * 60 + start_tm.tm_min;
+ event.end_minute = end_tm.tm_hour * 60 + end_tm.tm_min;
+ if (event.end_minute == 0 && start != end)
+ event.end_minute = 24 * 60;
+
+ g_array_append_val (week_view->events, event);
+ week_view->events_sorted = FALSE;
+ week_view->events_need_layout = TRUE;
+ return TRUE;
+}
+
+
+/* This lays out the events, or reshapes them, as necessary. */
+static void
+e_week_view_check_layout (EWeekView *week_view)
+{
+ /* Don't bother if we aren't visible. */
+ if (!GTK_WIDGET_VISIBLE (week_view))
+ return;
+
+ /* Make sure the events are sorted (by start and size). */
+ e_week_view_ensure_events_sorted (week_view);
+
+ if (week_view->events_need_layout)
+ e_week_view_layout_events (week_view);
+
+ if (week_view->events_need_layout || week_view->events_need_reshape)
+ e_week_view_reshape_events (week_view);
+
+ week_view->events_need_layout = FALSE;
+ week_view->events_need_reshape = FALSE;
+}
+
+
+static void
+e_week_view_layout_events (EWeekView *week_view)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ gint event_num, span_num;
+ guint8 *grid;
+ GArray *spans, *old_spans;
+
+ /* 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, E_WEEK_VIEW_MAX_ROWS_PER_CELL * 7
+ * E_WEEK_VIEW_MAX_WEEKS);
+
+ /* We create a new array of spans, which will replace the old one. */
+ spans = g_array_new (FALSE, FALSE, sizeof (EWeekViewEventSpan));
+
+ /* Iterate over the events, finding which weeks they cover, and putting
+ them in the first free row available. */
+ for (event_num = 0; event_num < week_view->events->len; event_num++) {
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ event_num);
+ e_week_view_layout_event (week_view, event, grid, spans);
+ }
+
+ /* Free the grid. */
+ g_free (grid);
+
+ /* Replace the spans array. */
+ old_spans = week_view->spans;
+ week_view->spans = spans;
+
+ /* Destroy the old spans array, destroying any unused canvas items. */
+ if (old_spans) {
+ for (span_num = 0; span_num < old_spans->len; span_num++) {
+ span = &g_array_index (old_spans, EWeekViewEventSpan,
+ span_num);
+ if (span->background_item)
+ gtk_object_destroy (GTK_OBJECT (span->background_item));
+ if (span->text_item)
+ gtk_object_destroy (GTK_OBJECT (span->text_item));
+ }
+ g_array_free (old_spans, TRUE);
+ }
+}
+
+
+static void
+e_week_view_layout_event (EWeekView *week_view,
+ EWeekViewEvent *event,
+ guint8 *grid,
+ GArray *spans)
+{
+ gint start_day, end_day, span_start_day, span_end_day, rows_per_cell;
+ gint free_row, row, day, span_num, spans_index, num_spans, max_day;
+ EWeekViewEventSpan span, *old_span;
+
+ start_day = e_week_view_find_day (week_view, event->start, FALSE);
+ end_day = e_week_view_find_day (week_view, event->end, TRUE);
+ max_day = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 - 1
+ : 7 - 1;
+ start_day = CLAMP (start_day, 0, max_day);
+ end_day = CLAMP (end_day, 0, max_day);
+
+#if 0
+ g_print ("In e_week_view_layout_event Start:%i End: %i\n",
+ start_day, end_day);
+#endif
+
+ /* Iterate through each of the spans of the event, where each span
+ is a sequence of 1 or more days displayed next to each other. */
+ span_start_day = start_day;
+ rows_per_cell = E_WEEK_VIEW_MAX_ROWS_PER_CELL;
+ span_num = 0;
+ spans_index = spans->len;
+ num_spans = 0;
+ while (span_start_day <= end_day) {
+ span_end_day = e_week_view_find_span_end (week_view,
+ span_start_day);
+ span_end_day = MIN (span_end_day, end_day);
+#if 0
+ g_print (" Span start:%i end:%i\n", span_start_day,
+ span_end_day);
+#endif
+ /* Try each row until we find a free one or we fall off the
+ bottom of the available rows. */
+ row = 0;
+ free_row = -1;
+ while (free_row == -1 && row < rows_per_cell) {
+ free_row = row;
+ for (day = span_start_day; day <= span_end_day;
+ day++) {
+ if (grid[day * rows_per_cell + row]) {
+ free_row = -1;
+ break;
+ }
+ }
+ row++;
+ };
+
+ if (free_row != -1) {
+ /* Mark the cells as full. */
+ for (day = span_start_day; day <= span_end_day;
+ day++) {
+ grid[day * rows_per_cell + free_row] = 1;
+ }
+#if 0
+ g_print (" Span start:%i end:%i row:%i\n",
+ span_start_day, span_end_day, free_row);
+#endif
+ /* Add the span to the array, and try to reuse any
+ canvas items from the old spans. */
+ span.start_day = span_start_day;
+ span.num_days = span_end_day - span_start_day + 1;
+ span.row = free_row;
+ span.background_item = NULL;
+ span.text_item = NULL;
+ if (event->num_spans > span_num) {
+ old_span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + span_num);
+ span.background_item = old_span->background_item;
+ span.text_item = old_span->text_item;
+ old_span->background_item = NULL;
+ old_span->text_item = NULL;
+ }
+
+ g_array_append_val (spans, span);
+ num_spans++;
+ }
+
+ span_start_day = span_end_day + 1;
+ span_num++;
+ }
+
+ /* Set the event's spans. */
+ event->spans_index = spans_index;
+ event->num_spans = num_spans;
+}
+
+
+static void
+e_week_view_ensure_events_sorted (EWeekView *week_view)
+{
+ if (!week_view->events_sorted) {
+ qsort (week_view->events->data,
+ week_view->events->len,
+ sizeof (EWeekViewEvent),
+ e_week_view_event_sort_func);
+ week_view->events_sorted = TRUE;
+ }
+}
+
+
+static gint
+e_week_view_event_sort_func (const void *arg1,
+ const void *arg2)
+{
+ EWeekViewEvent *event1, *event2;
+
+ event1 = (EWeekViewEvent*) arg1;
+ event2 = (EWeekViewEvent*) 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 void
+e_week_view_reshape_events (EWeekView *week_view)
+{
+ EWeekViewEvent *event;
+ gint event_num, span_num;
+
+ for (event_num = 0; event_num < week_view->events->len; event_num++) {
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ event_num);
+ for (span_num = 0; span_num < event->num_spans; span_num++) {
+ e_week_view_reshape_event_span (week_view, event_num,
+ span_num);
+ }
+ }
+}
+
+
+static void
+e_week_view_reshape_event_span (EWeekView *week_view,
+ gint event_num,
+ gint span_num)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ GdkFont *font;
+ gint span_x, span_y, span_w, num_icons, icons_width, time_width;
+ gint min_text_x, max_text_w, width;
+ gboolean show_icons = TRUE, use_max_width = FALSE;
+ gboolean one_day_event;
+ iCalObject *ico;
+ gdouble text_x, text_y, text_w, text_h;
+ gchar *text, *end_of_line;
+ gint line_len, text_width;
+
+ event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + span_num);
+ ico = event->ico;
+ font = GTK_WIDGET (week_view)->style->font;
+
+ one_day_event = e_week_view_is_one_day_event (week_view, event_num);
+
+ /* If the span will not be visible destroy the canvas items and
+ return. */
+ if (!e_week_view_get_span_position (week_view, event_num, span_num,
+ &span_x, &span_y, &span_w)) {
+ if (span->background_item)
+ gtk_object_destroy (GTK_OBJECT (span->background_item));
+ if (span->text_item)
+ gtk_object_destroy (GTK_OBJECT (span->text_item));
+ span->background_item = NULL;
+ span->text_item = NULL;
+ return;
+ }
+
+ if (!one_day_event && week_view->editing_event_num == event_num
+ && week_view->editing_span_num == span_num) {
+ show_icons = FALSE;
+ use_max_width = TRUE;
+ }
+
+ num_icons = 0;
+ if (show_icons) {
+ if (ico->dalarm.enabled || ico->malarm.enabled
+ || ico->palarm.enabled || ico->aalarm.enabled)
+ num_icons++;
+ if (ico->recur)
+ num_icons++;
+ }
+
+ /* Create the background canvas item if necessary. */
+ if (!span->background_item) {
+ span->background_item =
+ gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
+ e_week_view_event_item_get_type (),
+ NULL);
+ }
+
+ gnome_canvas_item_set (span->background_item,
+ "event_num", event_num,
+ "span_num", span_num,
+ NULL);
+
+ /* Create the text item if necessary. */
+ if (!span->text_item) {
+ span->text_item =
+ gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (week_view->main_canvas)->root),
+ e_text_get_type (),
+ "font_gdk", GTK_WIDGET (week_view)->style->font,
+ "anchor", GTK_ANCHOR_NW,
+ "clip", TRUE,
+ "max_lines", 1,
+ "editable", TRUE,
+ "text", ico->summary ? ico->summary : "",
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (span->text_item), "event",
+ GTK_SIGNAL_FUNC (e_week_view_on_text_item_event),
+ week_view);
+ }
+
+ /* Calculate the position of the text item.
+ For events < 1 day it starts after the times & icons and ends at the
+ right edge of the span.
+ For events > 1 day we need to determine whether times are shown at
+ the start and end of the span, then try to center the text item with
+ the icons in the middle, but making sure we don't go over the times.
+ */
+
+
+ /* Calculate the space necessary to display a time, e.g. "13:00". */
+ if (week_view->use_small_font && week_view->small_font)
+ time_width = week_view->digit_width * 2
+ + week_view->small_digit_width * 2;
+ else
+ time_width = week_view->digit_width * 4
+ + week_view->colon_width;
+
+ /* Calculate the space needed for the icons. */
+ icons_width = (E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD)
+ * num_icons;
+
+ /* The y position and height are the same for both event types. */
+ text_y = span_y + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
+ + E_WEEK_VIEW_EVENT_TEXT_Y_PAD;
+ text_h = font->ascent + font->descent;
+
+ if (one_day_event) {
+ text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD + icons_width;
+
+ switch (week_view->time_format) {
+ case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
+ case E_WEEK_VIEW_TIME_BOTH:
+ text_x += time_width * 2 + week_view->space_width
+ + E_WEEK_VIEW_EVENT_TIME_R_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_START_SMALL_MIN:
+ case E_WEEK_VIEW_TIME_START:
+ text_x += time_width + E_WEEK_VIEW_EVENT_TIME_R_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_NONE:
+ break;
+ }
+ text_w = span_x + span_w - E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ - E_WEEK_VIEW_EVENT_R_PAD - text_x;
+
+ } else {
+ if (use_max_width) {
+ text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
+ + E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
+ - E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ - E_WEEK_VIEW_EVENT_TEXT_X_PAD - text_x;
+ } else {
+ /* Get the requested size of the label. */
+ gtk_object_get (GTK_OBJECT (span->text_item),
+ "text", &text,
+ NULL);
+ text_width = 0;
+ if (text) {
+ end_of_line = strchr (text, '\n');
+ if (end_of_line)
+ line_len = end_of_line - text;
+ else
+ line_len = strlen (text);
+ text_width = gdk_text_width (font, text, line_len);
+ g_free (text);
+ }
+
+ /* Add on the width of the icons and find the default
+ position. */
+ width = text_width + icons_width;
+ text_x = span_x + (span_w - width) / 2;
+
+ /* Now calculate the left-most valid position, and make
+ sure we don't go to the left of that. */
+ min_text_x = span_x + E_WEEK_VIEW_EVENT_L_PAD
+ + E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ if (event->start > week_view->day_starts[span->start_day])
+ min_text_x += time_width
+ + E_WEEK_VIEW_EVENT_TIME_R_PAD;
+
+ text_x = MAX (text_x, min_text_x);
+
+ /* Now calculate the largest valid width, using the
+ calculated x position, and make sure we don't
+ exceed that. */
+ max_text_w = span_x + span_w - E_WEEK_VIEW_EVENT_R_PAD
+ - E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ - E_WEEK_VIEW_EVENT_TEXT_X_PAD - text_x;
+ if (event->end < week_view->day_starts[span->start_day
+ + span->num_days])
+ max_text_w -= time_width
+ + E_WEEK_VIEW_EVENT_TIME_R_PAD;
+
+ text_w = MIN (width, max_text_w);
+
+ /* Now take out the space for the icons. */
+ text_x += icons_width;
+ text_w -= icons_width;
+ }
+ }
+
+ text_w = MAX (text_w, 0);
+ gnome_canvas_item_set (span->text_item,
+ "x", (gdouble) text_x,
+ "y", (gdouble) text_y,
+ "clip_width", (gdouble) text_w,
+ "clip_height", (gdouble) text_h,
+ NULL);
+}
+
+
+/* Finds the day containing the given time.
+ If include_midnight_in_prev_day is TRUE then if the time exactly
+ matches the start of a day the previous day is returned. This is useful
+ when calculating the end day of an event. */
+static gint
+e_week_view_find_day (EWeekView *week_view,
+ time_t time_to_find,
+ gboolean include_midnight_in_prev_day)
+{
+ gint num_days, day;
+ time_t *day_starts;
+
+ num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ day_starts = week_view->day_starts;
+
+ if (time_to_find < day_starts[0])
+ return -1;
+ if (time_to_find > day_starts[num_days])
+ return num_days;
+
+ for (day = 1; day <= num_days; day++) {
+ if (time_to_find <= day_starts[day]) {
+ if (time_to_find == day_starts[day]
+ && !include_midnight_in_prev_day)
+ return day;
+ return day - 1;
+ }
+ }
+
+ g_assert_not_reached ();
+ return num_days;
+}
+
+
+/* This returns the last day in the same span as the given day. A span is all
+ the days which are displayed next to each other from left to right.
+ In the week view all spans are only 1 day, since Tuesday is below Monday
+ rather than beside it etc. In the month view, if the weekends are not
+ compressed then each week is a span, otherwise Monday to Saturday of each
+ week is a span, and the Sundays are separate spans. */
+static gint
+e_week_view_find_span_end (EWeekView *week_view,
+ gint day)
+{
+ gint week, day_of_week, end_day;
+
+ if (week_view->display_month) {
+ week = day / 7;
+ day_of_week = day % 7;
+ if (week_view->compress_weekend && day_of_week <= 5)
+ end_day = 5;
+ else
+ end_day = 6;
+ return week * 7 + end_day;
+ } else {
+ return day;
+ }
+}
+
+
+static void
+e_week_view_on_adjustment_changed (GtkAdjustment *adjustment,
+ EWeekView *week_view)
+{
+ GDate date;
+ gint week_offset;
+ struct tm tm;
+ time_t lower, start, end;
+ guint32 old_first_day_julian, new_first_day_julian;
+
+ /* If we don't have a valid date set yet, just return. */
+ if (!g_date_valid (&week_view->first_day_shown))
+ return;
+
+ /* Determine the first date shown. */
+ date = week_view->base_date;
+ week_offset = floor (adjustment->value + 0.5);
+ g_date_add_days (&date, week_offset * 7);
+
+ /* Convert the old & new first days shown to julian values. */
+ old_first_day_julian = g_date_julian (&week_view->first_day_shown);
+ new_first_day_julian = g_date_julian (&date);
+
+ /* If we are already showing the date, just return. */
+ if (old_first_day_julian == new_first_day_julian)
+ return;
+
+ /* Set the new first day shown. */
+ week_view->first_day_shown = date;
+
+ /* Convert it to a time_t. */
+ g_date_to_struct_tm (&date, &tm);
+ lower = mktime (&tm);
+ lower = time_day_begin (lower);
+
+ e_week_view_recalc_day_starts (week_view, lower);
+ e_week_view_reload_events (week_view);
+
+ /* Update the selection, if needed. */
+ if (week_view->selection_start_day != -1) {
+ start = week_view->day_starts[week_view->selection_start_day];
+ end = week_view->day_starts[week_view->selection_end_day + 1];
+ gnome_calendar_set_selected_time_range (week_view->calendar,
+ start, end);
+ }
+
+ gtk_widget_queue_draw (week_view->main_canvas);
+}
+
+
+void
+e_week_view_start_editing_event (EWeekView *week_view,
+ gint event_num,
+ gint span_num,
+ gchar *initial_text)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ ETextEventProcessor *event_processor = NULL;
+ ETextEventProcessorCommand command;
+
+ /* If we are already editing the event, just return. */
+ if (event_num == week_view->editing_event_num
+ && span_num == week_view->editing_span_num)
+ return;
+
+ event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + span_num);
+
+ /* If the event is not shown, don't try to edit it. */
+ if (!span->text_item)
+ return;
+
+ if (initial_text) {
+ gnome_canvas_item_set (span->text_item,
+ "text", initial_text,
+ NULL);
+ }
+
+ e_canvas_item_grab_focus (span->text_item);
+
+ /* Try to move the cursor to the end of the text. */
+ gtk_object_get (GTK_OBJECT (span->text_item),
+ "event_processor", &event_processor,
+ NULL);
+ if (event_processor) {
+ command.action = E_TEP_MOVE;
+ command.position = E_TEP_END_OF_BUFFER;
+ gtk_signal_emit_by_name (GTK_OBJECT (event_processor),
+ "command", &command);
+ }
+}
+
+
+/* This stops the current edit. If accept is TRUE the event summary is update,
+ else the edit is cancelled. */
+void
+e_week_view_stop_editing_event (EWeekView *week_view)
+{
+ GtkWidget *toplevel;
+
+ /* Check we are editing an event. */
+ if (week_view->editing_event_num == -1)
+ return;
+
+ /* Set focus to the toplevel so the item loses focus. */
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (week_view));
+ if (toplevel && GTK_IS_WINDOW (toplevel))
+ gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
+}
+
+
+static gboolean
+e_week_view_on_text_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EWeekView *week_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_week_view_on_editing_started (week_view, item);
+ else
+ e_week_view_on_editing_stopped (week_view, item);
+
+ return FALSE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_week_view_on_editing_started (EWeekView *week_view,
+ GnomeCanvasItem *item)
+{
+ gint event_num, span_num;
+
+ if (!e_week_view_find_event_from_item (week_view, item,
+ &event_num, &span_num))
+ return;
+
+ week_view->editing_event_num = event_num;
+ week_view->editing_span_num = span_num;
+
+ /* We need to reshape long events so the whole width is used while
+ editing. */
+ if (!e_week_view_is_one_day_event (week_view, event_num)) {
+ e_week_view_reshape_event_span (week_view, event_num,
+ span_num);
+ }
+}
+
+
+static void
+e_week_view_on_editing_stopped (EWeekView *week_view,
+ GnomeCanvasItem *item)
+{
+ gint event_num, span_num;
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ 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. */
+ event_num = week_view->editing_event_num;
+ span_num = week_view->editing_span_num;
+
+ /* If no item is being edited, just return. */
+ if (event_num == -1)
+ return;
+
+ event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + span_num);
+
+ /* Reset the edit fields. */
+ week_view->editing_event_num = -1;
+
+ gtk_object_get (GTK_OBJECT (span->text_item),
+ "text", &text,
+ NULL);
+
+ /* Only update the summary if necessary. */
+ if (text && event->ico->summary
+ && !strcmp (text, event->ico->summary)) {
+ g_free (text);
+ if (!e_week_view_is_one_day_event (week_view, event_num))
+ e_week_view_reshape_event_span (week_view, event_num,
+ span_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 (week_view->calendar, event->ico,
+ CHANGE_SUMMARY);
+}
+
+
+static gboolean
+e_week_view_find_event_from_item (EWeekView *week_view,
+ GnomeCanvasItem *item,
+ gint *event_num_return,
+ gint *span_num_return)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ gint event_num, span_num, num_events;
+
+ num_events = week_view->events->len;
+ for (event_num = 0; event_num < num_events; event_num++) {
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ event_num);
+ for (span_num = 0; span_num < event->num_spans; span_num++) {
+ span = &g_array_index (week_view->spans,
+ EWeekViewEventSpan,
+ event->spans_index + span_num);
+ if (span->text_item == item) {
+ *event_num_return = event_num;
+ *span_num_return = span_num;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_week_view_find_event_from_ico (EWeekView *week_view,
+ iCalObject *ico,
+ gint *event_num_return)
+{
+ EWeekViewEvent *event;
+ gint event_num, num_events;
+
+ num_events = week_view->events->len;
+ for (event_num = 0; event_num < num_events; event_num++) {
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ event_num);
+ if (event->ico == ico) {
+ *event_num_return = event_num;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+gboolean
+e_week_view_is_one_day_event (EWeekView *week_view,
+ gint event_num)
+{
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+
+ event = &g_array_index (week_view->events, EWeekViewEvent, event_num);
+ if (event->num_spans != 1)
+ return FALSE;
+
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index);
+
+ if (event->start == week_view->day_starts[span->start_day]
+ && event->end == week_view->day_starts[span->start_day + 1])
+ return FALSE;
+
+ if (span->num_days == 1
+ && event->start >= week_view->day_starts[span->start_day]
+ && event->end <= week_view->day_starts[span->start_day + 1])
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static gint
+e_week_view_key_press (GtkWidget *widget, GdkEventKey *event)
+{
+ EWeekView *week_view;
+ iCalObject *ico;
+ gint event_num;
+ gchar *initial_text;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ week_view = E_WEEK_VIEW (widget);
+
+ /* The Escape key aborts a resize operation. */
+#if 0
+ if (week_view->resize_drag_pos != E_WEEK_VIEW_POS_NONE) {
+ if (event->keyval == GDK_Escape) {
+ e_week_view_abort_resize (week_view, event->time);
+ }
+ return FALSE;
+ }
+#endif
+
+ if (week_view->selection_start_day == -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;
+ ico->dtstart = week_view->day_starts[week_view->selection_start_day];
+ ico->dtend = week_view->day_starts[week_view->selection_end_day + 1];
+
+ gnome_calendar_add_object (week_view->calendar, ico);
+
+ /* gnome_calendar_add_object() should have resulted in a call to
+ e_week_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_week_view_find_event_from_ico (week_view, ico, &event_num)) {
+ /* Start editing the new event. */
+ e_week_view_start_editing_event (week_view, event_num, 0,
+ initial_text);
+ }
+
+ return TRUE;
+}
+
+
+static void
+e_week_view_show_popup_menu (EWeekView *week_view,
+ GdkEventButton *event,
+ gint day)
+{
+ static struct menu_item items[] = {
+ { N_("New appointment..."), (GtkSignalFunc) e_week_view_on_new_appointment, NULL, TRUE }
+ };
+
+ items[0].data = week_view;
+
+ week_view->popup_event_day = day;
+ week_view->popup_event_num = -1;
+ popup_menu (items, 1, event);
+}
+
+
+static void
+e_week_view_on_new_appointment (GtkWidget *widget, gpointer data)
+{
+ EWeekView *week_view;
+ GtkWidget *event_editor;
+ iCalObject *ico;
+
+ week_view = E_WEEK_VIEW (data);
+
+ ico = ical_new ("", user_name, "");
+ ico->new = 1;
+ ico->dtstart = week_view->day_starts[week_view->popup_event_day];
+ ico->dtend = week_view->day_starts[week_view->popup_event_day + 1];
+
+ event_editor = event_editor_new (week_view->calendar, ico);
+ gtk_widget_show (event_editor);
+}
+
+