aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/gui
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/gui')
-rw-r--r--calendar/gui/Makefile.am33
-rw-r--r--calendar/gui/calendar-commands.c78
-rw-r--r--calendar/gui/dayview.xpm42
-rw-r--r--calendar/gui/e-day-view-main-item.c1
-rw-r--r--calendar/gui/e-day-view-time-item.c103
-rw-r--r--calendar/gui/e-day-view-top-item.c12
-rw-r--r--calendar/gui/e-day-view.c497
-rw-r--r--calendar/gui/e-day-view.h26
-rw-r--r--calendar/gui/e-week-view-event-item.c736
-rw-r--r--calendar/gui/e-week-view-event-item.h70
-rw-r--r--calendar/gui/e-week-view-main-item.c366
-rw-r--r--calendar/gui/e-week-view-main-item.h66
-rw-r--r--calendar/gui/e-week-view-titles-item.c300
-rw-r--r--calendar/gui/e-week-view-titles-item.h66
-rw-r--r--calendar/gui/e-week-view.c2265
-rw-r--r--calendar/gui/e-week-view.h357
-rw-r--r--calendar/gui/gnome-cal.c397
-rw-r--r--calendar/gui/gnome-cal.h27
-rw-r--r--calendar/gui/goto.c4
-rw-r--r--calendar/gui/monthview.xpm46
-rw-r--r--calendar/gui/quick-view.h1
-rw-r--r--calendar/gui/weekview.xpm41
-rw-r--r--calendar/gui/workweekview.xpm43
-rw-r--r--calendar/gui/yearview.xpm45
24 files changed, 5332 insertions, 290 deletions
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index 2f0dab260a..07e8ca1531 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -40,17 +40,17 @@ evolution_calendar_SOURCES = \
e-day-view-top-item.h \
e-day-view.c \
e-day-view.h \
+ e-week-view-event-item.c \
+ e-week-view-event-item.h \
+ e-week-view-main-item.c \
+ e-week-view-main-item.h \
+ e-week-view-titles-item.c \
+ e-week-view-titles-item.h \
+ e-week-view.c \
+ e-week-view.h \
eventedit.c \
eventedit.h \
getdate.y \
- gncal-day-panel.c \
- gncal-day-panel.h \
- gncal-day-view.c \
- gncal-day-view.h \
- gncal-full-day.c \
- gncal-full-day.h \
- gncal-week-view.c \
- gncal-week-view.h \
gncal-todo.c \
gncal-todo.h \
gnome-month-item.c \
@@ -58,20 +58,14 @@ evolution_calendar_SOURCES = \
gnome-cal.c \
gnome-cal.h \
goto.c \
- layout.c \
- layout.h \
mark.c \
mark.h \
- month-view.c \
- month-view.h \
popup-menu.c \
popup-menu.h \
prop.c \
quick-view.c \
quick-view.h \
todo-conduit.h \
- view-utils.h \
- view-utils.c \
year-view.c \
year-view.h \
calendar-commands.c \
@@ -182,9 +176,14 @@ gnorba_DATA = calendar-control.gnorba
#Conduits_second_DATA = $(Conduits_DATA)
#endif
-EXTRA_DIST = \
- bell.xpm \
- recur.xpm
+EXTRA_DIST = \
+ bell.xpm \
+ recur.xpm \
+ dayview.xpm \
+ workweekview.xpm \
+ weekview.xpm \
+ monthview.xpm \
+ yearview.xpm
# gnome-calendar-conduit.png
# todo-conduit-control-applet.desktop
diff --git a/calendar/gui/calendar-commands.c b/calendar/gui/calendar-commands.c
index d6e10f214b..4820c2892e 100644
--- a/calendar/gui/calendar-commands.c
+++ b/calendar/gui/calendar-commands.c
@@ -25,6 +25,11 @@
#include "gnome-cal.h"
#include "calendar-commands.h"
+#include "dayview.xpm"
+#include "workweekview.xpm"
+#include "weekview.xpm"
+#include "monthview.xpm"
+#include "yearview.xpm"
/* The username, used to set the `owner' field of the event */
char *user_name;
@@ -44,6 +49,9 @@ int week_starts_on_monday;
/* If true, enable debug output for alarms */
int debug_alarms = 0;
+/* If AM/PM indicators should be used. This may not be supported by the new
+ views. */
+int am_pm_flag = 0;
/* The array of color properties -- keep in sync with the enumeration defined in main.h. The color
* values specified here are the defaults for the program.
@@ -181,11 +189,12 @@ display_objedit (BonoboUIHandler *uih, void *user_data, const char *path)
iCalObject *ico;
GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
- /* Default to the day the user is looking at */
+ /* FIXME: Should get the selection time from the view, since they
+ may not be using the gcal's times. */
ico = ical_new ("", user_name, "");
ico->new = 1;
- ico->dtstart = time_add_minutes (gcal->current_display, day_begin * 60);
- ico->dtend = time_add_minutes (ico->dtstart, day_begin * 60 + 30 );
+ ico->dtstart = gcal->selection_start_time;
+ ico->dtend = gcal->selection_end_time;
ee = event_editor_new (gcal, ico);
gtk_widget_show (ee);
@@ -283,6 +292,41 @@ goto_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
}
static void
+show_day_view_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
+{
+ GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
+ gnome_calendar_set_view (gcal, "dayview");
+}
+
+static void
+show_work_week_view_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
+{
+ GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
+ gnome_calendar_set_view (gcal, "workweekview");
+}
+
+static void
+show_week_view_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
+{
+ GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
+ gnome_calendar_set_view (gcal, "weekview");
+}
+
+static void
+show_month_view_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
+{
+ GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
+ gnome_calendar_set_view (gcal, "monthview");
+}
+
+static void
+show_year_view_clicked (BonoboUIHandler *uih, void *user_data, const char *path)
+{
+ GnomeCalendar *gcal = GNOME_CALENDAR (user_data);
+ gnome_calendar_set_view (gcal, "yearview");
+}
+
+static void
new_calendar_cmd (BonoboUIHandler *uih, void *user_data, const char *path)
{
new_calendar (full_name, NULL, NULL, FALSE);
@@ -417,6 +461,28 @@ properties_cmd (BonoboUIHandler *uih, void *user_data, const char *path)
}
+static GnomeUIInfo gnome_toolbar_view_buttons [] = {
+ GNOMEUIINFO_RADIOITEM (N_("Day"), N_("Show 1 day"),
+ show_day_view_clicked,
+ dayview_xpm),
+ GNOMEUIINFO_RADIOITEM (N_("5 Days"), N_("Show 5 days"),
+ show_work_week_view_clicked,
+ workweekview_xpm),
+ GNOMEUIINFO_RADIOITEM (N_("Week"), N_("Show 1 week"),
+ show_week_view_clicked,
+ weekview_xpm),
+ GNOMEUIINFO_RADIOITEM (N_("Month"), N_("Show 1 month"),
+ show_month_view_clicked,
+ monthview_xpm),
+#if 0
+ GNOMEUIINFO_RADIOITEM (N_("Year"), N_("Show 1 year"),
+ show_year_view_clicked,
+ yearview_xpm),
+#endif
+ GNOMEUIINFO_END
+};
+
+
static GnomeUIInfo gnome_toolbar [] = {
GNOMEUIINFO_ITEM_STOCK (N_("New"), N_("Create a new appointment"), display_objedit, GNOME_STOCK_PIXMAP_NEW),
@@ -430,6 +496,10 @@ static GnomeUIInfo gnome_toolbar [] = {
GNOMEUIINFO_ITEM_STOCK (N_("Go to"), N_("Go to a specific date"), goto_clicked, GNOME_STOCK_PIXMAP_JUMP_TO),
+ GNOMEUIINFO_SEPARATOR,
+
+ GNOMEUIINFO_RADIOLIST (gnome_toolbar_view_buttons),
+
GNOMEUIINFO_END
};
@@ -479,7 +549,7 @@ calendar_control_activate (BonoboControl *control,
gnome_toolbar, &uibdata,
/*app->accel_group*/ NULL);
- gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
+ /*gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));*/
gtk_widget_show_all (toolbar);
diff --git a/calendar/gui/dayview.xpm b/calendar/gui/dayview.xpm
new file mode 100644
index 0000000000..bc8e74e4d6
--- /dev/null
+++ b/calendar/gui/dayview.xpm
@@ -0,0 +1,42 @@
+/* XPM */
+static char * dayview_xpm[] = {
+"24 24 15 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #D1D1D1",
+"# c #AAAFE2",
+"$ c #C7CAEB",
+"% c #A3A6C7",
+"& c #0010A8",
+"* c #555FC5",
+"= c #1725AC",
+"- c #4550B5",
+"; c #E3E4F5",
+"> c #8B90C3",
+", c #3945BB",
+"' c #BABBCC",
+" ",
+" ",
+" ",
+" ",
+" ...................... ",
+" .............+.++.++.. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" .@@@@@@@@@@@@@@@@@@@@. ",
+" .++@++@++@#$@++@++@++. ",
+" .++@++@++%&*@++@++@++. ",
+" .@@@@@@@%=&-@@@@@@@@@. ",
+" .++@++@++@&*@++@++@++. ",
+" .++@++@++@&*@++@++@++. ",
+" .@@@@@@@@@&-@@@@@@@@@. ",
+" .++@++@++@&*@++@++@++. ",
+" .++@++@+;>&,>++@++@++. ",
+" .@@@@@@@'>>>>@@@@@@@@. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" ...................... ",
+" ",
+" ",
+" "};
diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c
index 5d002b5945..1abff57c97 100644
--- a/calendar/gui/e-day-view-main-item.c
+++ b/calendar/gui/e-day-view-main-item.c
@@ -27,6 +27,7 @@
* data in the main Day/Work Week display.
*/
+#include <config.h>
#include "e-day-view-main-item.h"
static void e_day_view_main_item_class_init (EDayViewMainItemClass *class);
diff --git a/calendar/gui/e-day-view-time-item.c b/calendar/gui/e-day-view-time-item.c
index aa86cc2ca8..149da55830 100644
--- a/calendar/gui/e-day-view-time-item.c
+++ b/calendar/gui/e-day-view-time-item.c
@@ -27,6 +27,9 @@
* the EDayView.
*/
+#include <config.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkradiomenuitem.h>
#include "e-day-view-time-item.h"
@@ -62,7 +65,12 @@ static double e_day_view_time_item_point (GnomeCanvasItem *item,
double x, double y,
int cx, int cy,
GnomeCanvasItem **actual_item);
-
+static gint e_day_view_time_item_event (GnomeCanvasItem *item,
+ GdkEvent *event);
+static void e_day_view_time_item_show_popup_menu (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event);
+static void e_day_view_time_item_on_set_divisions (GtkWidget *item,
+ EDayViewTimeItem *dvtmitem);
static GnomeCanvasItemClass *parent_class;
@@ -120,6 +128,7 @@ e_day_view_time_item_class_init (EDayViewTimeItemClass *class)
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;
+ item_class->event = e_day_view_time_item_event;
}
@@ -310,3 +319,95 @@ e_day_view_time_item_point (GnomeCanvasItem *item, double x, double y,
*actual_item = item;
return 0.0;
}
+
+
+static gint
+e_day_view_time_item_event (GnomeCanvasItem *item,
+ GdkEvent *event)
+{
+ EDayViewTimeItem *dvtmitem;
+
+ dvtmitem = E_DAY_VIEW_TIME_ITEM (item);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 3) {
+ e_day_view_time_item_show_popup_menu (dvtmitem, event);
+ return TRUE;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+
+ case GDK_MOTION_NOTIFY:
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_day_view_time_item_show_popup_menu (EDayViewTimeItem *dvtmitem,
+ GdkEvent *event)
+{
+ static gint divisions[] = { 60, 30, 15, 10, 5 };
+ EDayView *day_view;
+ gint num_divisions = sizeof (divisions) / sizeof (divisions[0]);
+ GtkWidget *menu, *item;
+ gchar buffer[256];
+ GSList *group = NULL;
+ gint current_divisions, i;
+
+ g_print ("In e_day_view_time_item_show_popup_menu\n");
+
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ current_divisions = e_day_view_get_mins_per_row (day_view);
+
+ menu = gtk_menu_new ();
+
+ for (i = 0; i < num_divisions; i++) {
+ sprintf (buffer, _("%02i minute divisions"), divisions[i]);
+ item = gtk_radio_menu_item_new_with_label (group, buffer);
+ group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (item));
+ gtk_widget_show (item);
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ if (current_divisions == divisions[i])
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+
+ gtk_object_set_data (GTK_OBJECT (item), "divisions",
+ GINT_TO_POINTER (divisions[i]));
+
+ gtk_signal_connect (GTK_OBJECT (item), "toggled",
+ e_day_view_time_item_on_set_divisions,
+ dvtmitem);
+ }
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
+ event->button.button, event->button.time);
+
+ /* FIXME: Use e-util function to destroy menu when hidden. */
+}
+
+
+static void
+e_day_view_time_item_on_set_divisions (GtkWidget *item,
+ EDayViewTimeItem *dvtmitem)
+{
+ EDayView *day_view;
+ gint divisions;
+
+ day_view = dvtmitem->day_view;
+ g_return_if_fail (day_view != NULL);
+
+ if (!GTK_CHECK_MENU_ITEM (item)->active)
+ return;
+
+ divisions = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (item),
+ "divisions"));
+ e_day_view_set_mins_per_row (day_view, divisions);
+}
diff --git a/calendar/gui/e-day-view-top-item.c b/calendar/gui/e-day-view-top-item.c
index fe19c6b491..6d5ea60854 100644
--- a/calendar/gui/e-day-view-top-item.c
+++ b/calendar/gui/e-day-view-top-item.c
@@ -26,6 +26,7 @@
* EDayViewTopItem - displays the top part of the Day/Work Week calendar view.
*/
+#include <config.h>
#include "e-day-view-top-item.h"
static void e_day_view_top_item_class_init (EDayViewTopItemClass *class);
@@ -496,6 +497,7 @@ e_day_view_top_item_draw_triangle (EDayViewTopItem *dvtitem,
GtkStyle *style;
GdkGC *fg_gc, *bg_gc;
GdkPoint points[3];
+ gint c1, c2;
day_view = dvtitem->day_view;
@@ -510,9 +512,15 @@ e_day_view_top_item_draw_triangle (EDayViewTopItem *dvtitem,
points[2].x = x;
points[2].y = y + h - 1;
+ /* If the height is odd we can use the same central point for both
+ lines. If it is even we use different end-points. */
+ c1 = c2 = y + (h / 2);
+ if (h % 2 == 0)
+ c1--;
+
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));
+ gdk_draw_line (drawable, fg_gc, x, y, x + w, c1);
+ gdk_draw_line (drawable, fg_gc, x, y + h - 1, x + w, c2);
}
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
index 99a9817a33..2ec44e2ac6 100644
--- a/calendar/gui/e-day-view.c
+++ b/calendar/gui/e-day-view.c
@@ -85,6 +85,7 @@ static void e_day_view_style_set (GtkWidget *widget,
GtkStyle *previous_style);
static void e_day_view_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
+static gboolean e_day_view_update_scroll_regions (EDayView *day_view);
static gint e_day_view_focus_in (GtkWidget *widget,
GdkEventFocus *event);
static gint e_day_view_focus_out (GtkWidget *widget,
@@ -111,6 +112,7 @@ static gboolean e_day_view_on_main_canvas_button_press (GtkWidget *widget,
static gboolean e_day_view_on_main_canvas_button_release (GtkWidget *widget,
GdkEventButton *event,
EDayView *day_view);
+static void e_day_view_update_calendar_selection_time (EDayView *day_view);
static gboolean e_day_view_on_main_canvas_motion (GtkWidget *widget,
GdkEventMotion *event,
EDayView *day_view);
@@ -166,6 +168,8 @@ static void e_day_view_on_event_right_click (EDayView *day_view,
gint day,
gint event_num);
+static void e_day_view_recalc_day_starts (EDayView *day_view,
+ time_t start_time);
static void e_day_view_recalc_num_rows (EDayView *day_view);
static EDayViewPosition e_day_view_convert_position_in_top_canvas (EDayView *day_view,
@@ -255,6 +259,10 @@ static void e_day_view_get_selection_range (EDayView *day_view,
static time_t e_day_view_convert_grid_position_to_time (EDayView *day_view,
gint col,
gint row);
+static gboolean e_day_view_convert_time_to_grid_position (EDayView *day_view,
+ time_t time,
+ gint *col,
+ gint *row);
static void e_day_view_check_auto_scroll (EDayView *day_view,
gint event_y);
@@ -411,16 +419,25 @@ e_day_view_init (EDayView *day_view)
day_view->need_reshape[day] = FALSE;
}
- /* FIXME: Initialize lower, upper, day_starts. */
+ /* These indicate that the times haven't been set. */
+ day_view->lower = 0;
+ day_view->upper = 0;
+
+ /* FIXME: Initialize 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;
+
+ /* Note that these don't work yet. It would need a few fixes to the
+ way event->start_minute and event->end_minute are used, and there
+ may be problems with events that go outside the visible times. */
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);
@@ -791,6 +808,9 @@ e_day_view_destroy (GtkObject *object)
e_day_view_stop_auto_scroll (day_view);
+ if (day_view->large_font)
+ gdk_font_unref (day_view->large_font);
+
gdk_cursor_destroy (day_view->normal_cursor);
gdk_cursor_destroy (day_view->move_cursor);
gdk_cursor_destroy (day_view->resize_width_cursor);
@@ -841,7 +861,7 @@ e_day_view_style_set (GtkWidget *widget,
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 hour, max_large_hour_width, month_width;
gint minute, max_minute_width, i;
GDate date;
gchar buffer[128];
@@ -871,16 +891,17 @@ e_day_view_style_set (GtkWidget *widget,
g_date_clear (&date, 1);
g_date_set_dmy (&date, 20, 1, 2000);
max_month_width = 0;
+ max_abbr_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));
+ month_width = gdk_string_width (font, buffer);
+ max_month_width = MAX (max_month_width, month_width);
g_date_strftime (buffer, 128, "%b", &date);
- max_abbr_month_width = MAX (max_abbr_month_width,
- gdk_string_width (font, buffer));
+ month_width = gdk_string_width (font, buffer);
+ max_abbr_month_width = MAX (max_abbr_month_width, month_width);
}
number_width = gdk_string_width (font, "31 ");
day_view->long_format_width = number_width + max_month_width
@@ -889,6 +910,8 @@ e_day_view_style_set (GtkWidget *widget,
+ max_abbr_month_width + E_DAY_VIEW_DATE_X_PAD;
/* Calculate the widths of all the time strings necessary. */
+ day_view->max_small_hour_width = 0;
+ max_large_hour_width = 0;
for (hour = 0; hour < 24; hour++) {
sprintf (buffer, "%02i", hour);
day_view->small_hour_widths[hour] = gdk_string_width (font, buffer);
@@ -898,6 +921,7 @@ e_day_view_style_set (GtkWidget *widget,
}
day_view->max_large_hour_width = max_large_hour_width;
+ max_minute_width = 0;
for (minute = 0, i = 0; minute < 60; minute += 5, i++) {
sprintf (buffer, "%02i", minute);
day_view->minute_widths[i] = gdk_string_width (font, buffer);
@@ -918,12 +942,13 @@ 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;
+ gint col, day, scroll_y;
+ gboolean need_reshape;
+ gdouble old_x2, old_y2, new_x2, new_y2;
+#if 0
g_print ("In e_day_view_size_allocate\n");
-
+#endif
day_view = E_DAY_VIEW (widget);
(*GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
@@ -954,31 +979,14 @@ e_day_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
/* 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)
+ NULL, NULL, &old_x2, &old_y2);
+ new_x2 = day_view->top_canvas->allocation.width - 1;
+ new_y2 = day_view->top_canvas->allocation.height - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
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);
+ 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 (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);
+ need_reshape = e_day_view_update_scroll_regions (day_view);
/* Scroll to the start of the working day, if this is the initial
allocation. */
@@ -991,9 +999,7 @@ e_day_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
/* 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");
-
+ if (need_reshape) {
day_view->long_events_need_reshape = TRUE;
for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
day_view->need_reshape[day] = TRUE;
@@ -1010,8 +1016,6 @@ e_day_view_focus_in (GtkWidget *widget, GdkEventFocus *event)
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);
@@ -1028,8 +1032,6 @@ e_day_view_focus_out (GtkWidget *widget, GdkEventFocus *event)
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);
@@ -1048,6 +1050,8 @@ void
e_day_view_set_calendar (EDayView *day_view,
GnomeCalendar *calendar)
{
+ g_return_if_fail (E_IS_DAY_VIEW (day_view));
+
day_view->calendar = calendar;
/* FIXME: free current events? */
@@ -1063,7 +1067,13 @@ e_day_view_update_event (EDayView *day_view,
g_return_if_fail (E_IS_DAY_VIEW (day_view));
+#if 0
g_print ("In e_day_view_update_event\n");
+#endif
+
+ /* If our time hasn't been set yet, just return. */
+ if (day_view->lower == 0 && day_view->upper == 0)
+ return;
/* We only care about events. */
if (ico && ico->type != ICAL_EVENT)
@@ -1115,14 +1125,21 @@ e_day_view_update_event_label (EDayView *day_view,
{
EDayViewEvent *event;
gchar *text;
- gboolean free_text;
+ gboolean free_text = FALSE, editing_event = FALSE;
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) {
+ text = event->ico->summary ? event->ico->summary : "";
+
+ if (day_view->editing_event_day == day
+ && day_view->editing_event_num == event_num)
+ editing_event = TRUE;
+
+ if (!editing_event
+ && (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;
@@ -1132,15 +1149,12 @@ e_day_view_update_event_label (EDayView *day_view,
start_minute % 60,
end_minute / 60,
end_minute % 60,
- event->ico->summary);
+ text);
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 : "",
+ "text", text,
NULL);
if (free_text)
@@ -1243,51 +1257,102 @@ e_day_view_find_event_from_ico (EDayView *day_view,
}
-/* Note that the times must be the start and end of days. */
+/* This sets the selected time range. The EDayView will show the day or week
+ corresponding to the start time. If the start_time & end_time are not equal
+ and are both visible in the view, then the selection is set to those times,
+ otherwise it is set to 1 hour from the start of the working day. */
void
-e_day_view_set_interval (EDayView *day_view,
- time_t lower,
- time_t upper)
+e_day_view_set_selected_time_range (EDayView *day_view,
+ time_t start_time,
+ time_t end_time)
{
- time_t tmp_lower, day_starts[E_DAY_VIEW_MAX_DAYS + 1];
- gint day;
+ GDate date;
+ time_t lower;
+ gint start_row, start_col, end_row, end_col;
+ gboolean need_redraw = FALSE, start_in_grid, end_in_grid;
g_return_if_fail (E_IS_DAY_VIEW (day_view));
- g_print ("In e_day_view_set_interval\n");
+ /* Calculate the first day that should be shown, based on start_time
+ and the days_shown setting. If we are showing 1 day it is just the
+ start of the day given by start_time, otherwise it is the previous
+ Monday. */
+ if (day_view->days_shown == 1)
+ lower = time_day_begin (start_time);
+ else {
+ g_date_clear (&date, 1);
+ g_date_set_time (&date, start_time);
+ g_date_subtract_days (&date, g_date_weekday (&date) - 1);
+ lower = time_from_day (g_date_year (&date),
+ g_date_month (&date) - 1,
+ g_date_day (&date));
+ }
- if (lower == day_view->lower && upper == day_view->upper)
- return;
+ /* See if we need to change the days shown. */
+ if (lower != day_view->lower) {
+ e_day_view_recalc_day_starts (day_view, lower);
+ e_day_view_reload_events (day_view);
+ need_redraw = TRUE;
+ }
- /* 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;
- }
+ /* Set the selection. */
+ start_in_grid = e_day_view_convert_time_to_grid_position (day_view,
+ start_time,
+ &start_col,
+ &start_row);
+ end_in_grid = e_day_view_convert_time_to_grid_position (day_view,
+ end_time - 60,
+ &end_col,
+ &end_row);
+
+ /* If either of the times isn't in the grid, or the selection covers
+ an entire day, we set the selection to 1 row from the start of the
+ working day, in the day corresponding to the start time. */
+ if (!start_in_grid || !end_in_grid
+ || (start_row == 0 && end_row == day_view->rows - 1)) {
+ end_col = start_col;
+
+ start_row = e_day_view_convert_time_to_row (day_view, day_view->work_day_start_hour, day_view->work_day_start_minute);
+ start_row = CLAMP (start_row, 0, day_view->rows - 1);
+ end_row = start_row;
+ }
- /* Check that we haven't gone past the upper time. */
- g_return_if_fail (day_starts[day] < upper);
+ if (start_row != day_view->selection_start_row
+ || start_col != day_view->selection_start_col) {
+ need_redraw = TRUE;
+ day_view->selection_in_top_canvas = FALSE;
+ day_view->selection_start_row = start_row;
+ day_view->selection_start_col = start_col;
+ }
+
+ if (end_row != day_view->selection_end_row
+ || end_col != day_view->selection_end_col) {
+ need_redraw = TRUE;
+ day_view->selection_in_top_canvas = FALSE;
+ day_view->selection_end_row = end_row;
+ day_view->selection_end_col = end_col;
+ }
+
+ if (need_redraw) {
+ gtk_widget_queue_draw (day_view->top_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
}
+}
- /* 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];
+static void
+e_day_view_recalc_day_starts (EDayView *day_view,
+ time_t start_time)
+{
+ gint day;
+
+ day_view->day_starts[0] = start_time;
+ for (day = 1; day <= day_view->days_shown; day++) {
+ day_view->day_starts[day] = time_add_day (day_view->day_starts[day - 1], 1);
}
- e_day_view_reload_events (day_view);
+ day_view->lower = start_time;
+ day_view->upper = day_view->day_starts[day_view->days_shown];
}
@@ -1329,6 +1394,8 @@ void
e_day_view_set_mins_per_row (EDayView *day_view,
gint mins_per_row)
{
+ gint day;
+
g_return_if_fail (E_IS_DAY_VIEW (day_view));
if (mins_per_row != 5 && mins_per_row != 10 && mins_per_row != 15
@@ -1337,11 +1404,58 @@ e_day_view_set_mins_per_row (EDayView *day_view,
return;
}
- if (day_view->mins_per_row != mins_per_row) {
- day_view->mins_per_row = mins_per_row;
+ if (day_view->mins_per_row == mins_per_row)
+ return;
+
+ day_view->mins_per_row = mins_per_row;
+ e_day_view_recalc_num_rows (day_view);
+
+ /* If we aren't visible, we'll sort it out later. */
+ if (!GTK_WIDGET_VISIBLE (day_view))
+ return;
- /* FIXME: Update positions & display. */
+ for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++)
+ day_view->need_layout[day] = TRUE;
+
+ /* We must layout the events before updating the scroll region, since
+ that will result in a redraw which would crash otherwise. */
+ e_day_view_check_layout (day_view);
+ gtk_widget_queue_draw (day_view->time_canvas);
+ gtk_widget_queue_draw (day_view->main_canvas);
+
+ e_day_view_update_scroll_regions (day_view);
+}
+
+
+static gboolean
+e_day_view_update_scroll_regions (EDayView *day_view)
+{
+ gdouble old_x2, old_y2, new_x2, new_y2;
+ gboolean need_reshape = FALSE;
+
+ /* 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_x2, &old_y2);
+ new_x2 = day_view->time_canvas->allocation.width - 1;
+ new_y2 = MAX (day_view->rows * day_view->row_height,
+ day_view->main_canvas->allocation.height) - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2)
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->time_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 (day_view->main_canvas),
+ NULL, NULL, &old_x2, &old_y2);
+ new_x2 = day_view->main_canvas->allocation.width - 1;
+ if (old_x2 != new_x2 || old_y2 != new_y2) {
+ need_reshape = TRUE;
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (day_view->main_canvas),
+ 0, 0, new_x2, new_y2);
}
+
+ return need_reshape;
}
@@ -1360,7 +1474,9 @@ e_day_view_recalc_num_rows (EDayView *day_view)
}
-/* Converts an hour and minute to a row in the canvas. */
+/* Converts an hour and minute to a row in the canvas. Note that if we aren't
+ showing all 24 hours of the day, the returned row may be negative or
+ greater than day_view->rows. */
gint
e_day_view_convert_time_to_row (EDayView *day_view,
gint hour,
@@ -1372,8 +1488,10 @@ e_day_view_convert_time_to_row (EDayView *day_view,
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;
+ if (offset < 0)
+ return -1;
+ else
+ return offset / day_view->mins_per_row;
}
@@ -1402,8 +1520,6 @@ e_day_view_on_top_canvas_button_press (GtkWidget *widget,
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,
@@ -1503,7 +1619,7 @@ e_day_view_convert_event_coords (EDayView *day_view,
*y_return = event_y;
if (event_window != window)
- g_print ("Couldn't find event window\n");
+ g_warning ("Couldn't find event window\n");
return (event_window == window) ? TRUE : FALSE;
}
@@ -1517,8 +1633,6 @@ e_day_view_on_main_canvas_button_press (GtkWidget *widget,
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,
@@ -1645,8 +1759,6 @@ e_day_view_on_long_event_click (EDayView *day_view,
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);
@@ -1702,7 +1814,6 @@ e_day_view_on_long_event_click (EDayView *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);
}
}
@@ -1719,8 +1830,6 @@ e_day_view_on_event_click (EDayView *day_view,
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);
@@ -1778,8 +1887,6 @@ e_day_view_on_event_click (EDayView *day_view,
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);
}
}
@@ -1868,7 +1975,9 @@ e_day_view_on_event_double_click (EDayView *day_view,
gint day,
gint event_num)
{
+#if 0
g_print ("In e_day_view_on_event_double_click\n");
+#endif
}
@@ -1903,8 +2012,6 @@ e_day_view_on_event_right_click (EDayView *day_view,
{ 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) {
@@ -2067,12 +2174,10 @@ 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);
+ e_day_view_update_calendar_selection_time (day_view);
} 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);
@@ -2094,13 +2199,11 @@ 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);
+ e_day_view_update_calendar_selection_time (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);
@@ -2118,6 +2221,17 @@ e_day_view_on_main_canvas_button_release (GtkWidget *widget,
}
+static void
+e_day_view_update_calendar_selection_time (EDayView *day_view)
+{
+ time_t start, end;
+
+ e_day_view_get_selection_range (day_view, &start, &end);
+ gnome_calendar_set_selected_time_range (day_view->calendar,
+ start, end);
+}
+
+
static gboolean
e_day_view_on_top_canvas_motion (GtkWidget *widget,
GdkEventMotion *mevent,
@@ -2160,8 +2274,6 @@ e_day_view_on_top_canvas_motion (GtkWidget *widget,
} 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;
@@ -2246,8 +2358,6 @@ e_day_view_on_main_canvas_motion (GtkWidget *widget,
} 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;
@@ -2347,7 +2457,7 @@ e_day_view_update_long_event_resize (EDayView *day_view,
gint event_num;
gboolean need_reshape = FALSE;
-#if 1
+#if 0
g_print ("Updating resize Day:%i\n", day);
#endif
@@ -2429,9 +2539,6 @@ 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);
@@ -2460,9 +2567,6 @@ 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,
@@ -2478,7 +2582,6 @@ e_day_view_finish_resize (EDayView *day_view)
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);
@@ -2501,8 +2604,6 @@ e_day_view_abort_resize (EDayView *day_view,
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);
@@ -2552,7 +2653,11 @@ e_day_view_reload_events (EDayView *day_view)
day_view);
}
+ /* We need to do this to make sure the top canvas is resized. */
+ day_view->long_events_need_layout = TRUE;
+
e_day_view_check_layout (day_view);
+ e_day_view_reshape_main_canvas_resize_bars (day_view);
gtk_widget_queue_draw (day_view->top_canvas);
gtk_widget_queue_draw (day_view->main_canvas);
@@ -2598,7 +2703,7 @@ e_day_view_add_event (iCalObject *ico,
{
EDayView *day_view;
EDayViewEvent event;
- gint day;
+ gint day, offset;
struct tm start_tm, end_tm;
day_view = E_DAY_VIEW (data);
@@ -2616,10 +2721,12 @@ e_day_view_add_event (iCalObject *ico,
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;
+ /* Calculate the start & end minute, relative to the top of the
+ display. */
+ offset = day_view->first_hour_shown * 60
+ + day_view->first_minute_shown;
+ event.start_minute = start_tm.tm_hour * 60 + start_tm.tm_min - offset;
+ event.end_minute = end_tm.tm_hour * 60 + end_tm.tm_min - offset;
event.start_row_or_col = -1;
event.num_columns = -1;
@@ -2810,11 +2917,12 @@ e_day_view_reshape_long_event (EDayView *day_view,
gint event_num)
{
EDayViewEvent *event;
+ GdkFont *font;
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;
+ gint min_text_x, max_text_w, text_width, line_len;
+ gchar *text, *end_of_line;
gboolean show_icons = TRUE, use_max_width = FALSE;
if (!e_day_view_get_long_event_position (day_view, event_num,
@@ -2840,6 +2948,7 @@ e_day_view_reshape_long_event (EDayView *day_view,
draw them on top of the resize rect. Nor when editing. */
num_icons = 0;
ico = event->ico;
+ font = GTK_WIDGET (day_view)->style->font;
if (day_view->resize_drag_pos != E_DAY_VIEW_POS_NONE
&& day_view->resize_event_day == E_DAY_VIEW_LONG_EVENT
@@ -2848,7 +2957,6 @@ e_day_view_reshape_long_event (EDayView *day_view,
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;
}
@@ -2894,8 +3002,18 @@ e_day_view_reshape_long_event (EDayView *day_view,
} else {
/* Get the requested size of the label. */
gtk_object_get (GTK_OBJECT (event->canvas_item),
- "text_width", &text_width,
+ "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);
+ }
width = text_width + icons_width;
text_x = item_x + (item_w - width) / 2;
@@ -3033,6 +3151,16 @@ e_day_view_layout_day_event (EDayView *day_view,
start_row = event->start_minute / day_view->mins_per_row;
end_row = (event->end_minute - 1) / day_view->mins_per_row;
+ event->num_columns = 0;
+
+ /* If the event can't currently be seen, just return. */
+ if (start_row >= day_view->rows || end_row < 0)
+ return;
+
+ /* Make sure we don't go outside the visible times. */
+ start_row = CLAMP (start_row, 0, day_view->rows - 1);
+ end_row = CLAMP (end_row, 0, day_view->rows - 1);
+
/* Try each column until we find a free one. */
for (col = 0; col < E_DAY_VIEW_MAX_COLUMNS; col++) {
free_col = col;
@@ -3047,11 +3175,9 @@ e_day_view_layout_day_event (EDayView *day_view,
break;
}
- /* If we can't find space for the event, mark it as not displayed. */
- if (free_col == -1) {
- event->num_columns = 0;
+ /* If we can't find space for the event, just return. */
+ if (free_col == -1)
return;
- }
/* The event is assigned 1 col initially, but may be expanded later. */
event->start_row_or_col = free_col;
@@ -3143,8 +3269,6 @@ e_day_view_reshape_day_events (EDayView *day_view,
{
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);
@@ -3353,8 +3477,6 @@ e_day_view_key_press (GtkWidget *widget, GdkEventKey *event)
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) {
@@ -3409,6 +3531,8 @@ e_day_view_start_editing_event (EDayView *day_view,
gchar *initial_text)
{
EDayViewEvent *event;
+ ETextEventProcessor *event_processor = NULL;
+ ETextEventProcessorCommand command;
/* If we are already editing the event, just return. */
if (day == day_view->editing_event_day
@@ -3427,13 +3551,28 @@ e_day_view_start_editing_event (EDayView *day_view,
if (!event->canvas_item)
return;
+ /* We must grab the focus before setting the initial text, since
+ grabbing the focus will result in a call to
+ e_day_view_on_editing_started(), which will reset the text to get
+ rid of the start and end times. */
+ e_canvas_item_grab_focus (event->canvas_item);
+
if (initial_text) {
gnome_canvas_item_set (event->canvas_item,
"text", initial_text,
NULL);
}
- e_canvas_item_grab_focus (event->canvas_item);
+ /* Try to move the cursor to the end of the text. */
+ gtk_object_get (GTK_OBJECT (event->canvas_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);
+ }
}
@@ -3493,8 +3632,18 @@ e_day_view_on_editing_started (EDayView *day_view,
&day, &event_num))
return;
+#if 0
g_print ("In e_day_view_on_editing_started Day:%i Event:%i\n",
day, event_num);
+#endif
+
+ /* FIXME: This is a temporary workaround for a bug which seems to stop
+ us getting focus_out signals. It is not a complete fix since if we
+ don't get focus_out signals we don't save the appointment text so
+ this may be lost. */
+ if (day_view->editing_event_day == day
+ && day_view->editing_event_num == event_num)
+ return;
day_view->editing_event_day = day;
day_view->editing_event_num = event_num;
@@ -3504,6 +3653,7 @@ e_day_view_on_editing_started (EDayView *day_view,
} else {
day_view->resize_bars_event_day = day;
day_view->resize_bars_event_num = event_num;
+ e_day_view_update_event_label (day_view, day, event_num);
e_day_view_reshape_main_canvas_resize_bars (day_view);
}
}
@@ -3528,8 +3678,10 @@ e_day_view_on_editing_stopped (EDayView *day_view,
if (day == -1)
return;
+#if 0
g_print ("In e_day_view_on_editing_stopped Day:%i Event:%i\n",
day, event_num);
+#endif
if (day == E_DAY_VIEW_LONG_EVENT) {
editing_long_event = TRUE;
@@ -3631,13 +3783,51 @@ e_day_view_convert_grid_position_to_time (EDayView *day_view,
}
+static gboolean
+e_day_view_convert_time_to_grid_position (EDayView *day_view,
+ time_t time,
+ gint *col,
+ gint *row)
+{
+ struct tm *tmp_tm;
+ gint day, minutes;
+
+ *col = *row = 0;
+
+ if (time < day_view->lower || time >= day_view->upper)
+ return FALSE;
+
+ /* We can find the column easily using the day_starts array. */
+ for (day = 1; day <= day_view->days_shown; day++) {
+ if (time < day_view->day_starts[day]) {
+ *col = day - 1;
+ break;
+ }
+ }
+
+ /* To find the row we need to convert the time to a struct tm,
+ calculate the offset in minutes from the top of the display and
+ divide it by the mins per row setting. */
+ tmp_tm = localtime (&time);
+ minutes = tmp_tm->tm_hour * 60 + tmp_tm->tm_min;
+ minutes -= day_view->first_hour_shown * 60
+ + day_view->first_minute_shown;
+
+ *row = minutes / day_view->mins_per_row;
+
+ if (*row < 0 || *row >= day_view->rows)
+ return FALSE;
+
+ return TRUE;
+}
+
+
/* 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
@@ -3995,8 +4185,6 @@ e_day_view_on_top_canvas_drag_motion (GtkWidget *widget,
{
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;
@@ -4084,8 +4272,6 @@ e_day_view_update_top_canvas_drag (EDayView *day_view,
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,
@@ -4137,8 +4323,6 @@ e_day_view_on_main_canvas_drag_motion (GtkWidget *widget,
{
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;
@@ -4233,8 +4417,6 @@ e_day_view_update_main_canvas_drag (EDayView *day_view,
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,
@@ -4293,8 +4475,6 @@ e_day_view_on_top_canvas_drag_leave (GtkWidget *widget,
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);
@@ -4308,8 +4488,6 @@ e_day_view_on_main_canvas_drag_leave (GtkWidget *widget,
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);
@@ -4335,8 +4513,6 @@ e_day_view_on_drag_begin (GtkWidget *widget,
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;
@@ -4365,8 +4541,6 @@ e_day_view_on_drag_end (GtkWidget *widget,
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;
@@ -4405,8 +4579,6 @@ e_day_view_on_drag_data_get (GtkWidget *widget,
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;
@@ -4443,11 +4615,9 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
{
EDayViewEvent *event;
EDayViewPosition pos;
- gint day, /* row, scroll_x, scroll_y,*/ start_day, end_day, num_days;
+ gint day, 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,
@@ -4475,8 +4645,6 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
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");
@@ -4517,8 +4685,6 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
gint day, row, start_row, end_row, num_rows, scroll_x, scroll_y;
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;
@@ -4547,9 +4713,6 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
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");
diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h
index b829e76384..ac967f030c 100644
--- a/calendar/gui/e-day-view.h
+++ b/calendar/gui/e-day-view.h
@@ -332,7 +332,8 @@ struct _EDayView
gint selection_start_row;
gint selection_end_row;
- /* This is TRUE if the user is selecting a region on the calendar. */
+ /* This specifies which end of the selection is being dragged, or is
+ E_DAY_VIEW_DRAG_NONE if the selection isn't being dragged. */
EDayViewDragPosition selection_drag_pos;
/* This is TRUE if the selection is in the top canvas only (i.e. if the
@@ -405,21 +406,32 @@ 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);
+/* This sets the selected time range. The EDayView will show the day or week
+ corresponding to the start time. If the start_time & end_time are not equal
+ and are both visible in the view, then the selection is set to those times,
+ otherwise it is set to 1 hour from the start of the working day. */
+void e_day_view_set_selected_time_range (EDayView *day_view,
+ time_t start_time,
+ time_t end_time);
-void e_day_view_update_event (EDayView *fullday,
+
+/* This is called when one or more events have been updated, either within the
+ EDayView itself, or via another calendar view or application. If only one
+ event has changed, it is passed in the ico argument and the flags indicate
+ the change - whether it is a new event, or just the summary has changed. */
+void e_day_view_update_event (EDayView *day_view,
iCalObject *ico,
int flags);
-
-
+/* The number of days shown in the EDayView, from 1 to 7. This is normally
+ either 1 or 5 (for the Work-Week view). */
gint e_day_view_get_days_shown (EDayView *day_view);
void e_day_view_set_days_shown (EDayView *day_view,
gint days_shown);
+/* This specifies how many minutes are represented by one row in the display.
+ It can be 60, 30, 15, 10 or 5. The default is 30. */
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);
diff --git a/calendar/gui/e-week-view-event-item.c b/calendar/gui/e-week-view-event-item.c
new file mode 100644
index 0000000000..e22fd944bf
--- /dev/null
+++ b/calendar/gui/e-week-view-event-item.c
@@ -0,0 +1,736 @@
+/* -*- 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
+ */
+
+/*
+ * EWeekViewEventItem - displays the background, times and icons for an event
+ * in the week/month views. A separate EText canvas item is used to display &
+ * edit the text.
+ */
+
+#include <config.h>
+#include "../widgets/e-text/e-text.h"
+#include "e-week-view-event-item.h"
+
+static void e_week_view_event_item_class_init (EWeekViewEventItemClass *class);
+static void e_week_view_event_item_init (EWeekViewEventItem *wveitem);
+
+static void e_week_view_event_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_week_view_event_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+static void e_week_view_event_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_week_view_event_item_draw_icons (EWeekViewEventItem *wveitem,
+ GdkDrawable *drawable,
+ gint icon_x,
+ gint icon_y,
+ gint x2,
+ gboolean right_align);
+static void e_week_view_event_item_draw_triangle (EWeekViewEventItem *wveitem,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint w,
+ gint h);
+static double e_week_view_event_item_point (GnomeCanvasItem *item,
+ double x,
+ double y,
+ int cx,
+ int cy,
+ GnomeCanvasItem **actual_item);
+static gint e_week_view_event_item_event (GnomeCanvasItem *item,
+ GdkEvent *event);
+static gboolean e_week_view_event_item_button_press (EWeekViewEventItem *wveitem,
+ GdkEvent *event);
+static gboolean e_week_view_event_item_button_release (EWeekViewEventItem *wveitem,
+ GdkEvent *event);
+static EWeekViewPosition e_week_view_event_item_get_position (EWeekViewEventItem *wveitem,
+ gdouble x,
+ gdouble y);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+/* The arguments we take */
+enum {
+ ARG_0,
+ ARG_EVENT_NUM,
+ ARG_SPAN_NUM
+};
+
+
+GtkType
+e_week_view_event_item_get_type (void)
+{
+ static GtkType e_week_view_event_item_type = 0;
+
+ if (!e_week_view_event_item_type) {
+ GtkTypeInfo e_week_view_event_item_info = {
+ "EWeekViewEventItem",
+ sizeof (EWeekViewEventItem),
+ sizeof (EWeekViewEventItemClass),
+ (GtkClassInitFunc) e_week_view_event_item_class_init,
+ (GtkObjectInitFunc) e_week_view_event_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_week_view_event_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_week_view_event_item_info);
+ }
+
+ return e_week_view_event_item_type;
+}
+
+
+static void
+e_week_view_event_item_class_init (EWeekViewEventItemClass *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 ("EWeekViewEventItem::event_num",
+ GTK_TYPE_INT, GTK_ARG_WRITABLE,
+ ARG_EVENT_NUM);
+ gtk_object_add_arg_type ("EWeekViewEventItem::span_num",
+ GTK_TYPE_INT, GTK_ARG_WRITABLE,
+ ARG_SPAN_NUM);
+
+ object_class->set_arg = e_week_view_event_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_week_view_event_item_update;
+ item_class->draw = e_week_view_event_item_draw;
+ item_class->point = e_week_view_event_item_point;
+ item_class->event = e_week_view_event_item_event;
+}
+
+
+static void
+e_week_view_event_item_init (EWeekViewEventItem *wveitem)
+{
+ wveitem->event_num = -1;
+ wveitem->span_num = -1;
+}
+
+
+static void
+e_week_view_event_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EWeekViewEventItem *wveitem;
+ gboolean needs_update = FALSE;
+
+ item = GNOME_CANVAS_ITEM (o);
+ wveitem = E_WEEK_VIEW_EVENT_ITEM (o);
+
+ switch (arg_id){
+ case ARG_EVENT_NUM:
+ wveitem->event_num = GTK_VALUE_INT (*arg);
+ needs_update = TRUE;
+ break;
+ case ARG_SPAN_NUM:
+ wveitem->span_num = GTK_VALUE_INT (*arg);
+ needs_update = TRUE;
+ break;
+ }
+
+ if (needs_update)
+ gnome_canvas_item_request_update (item);
+}
+
+
+static void
+e_week_view_event_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags)
+{
+ EWeekViewEventItem *wveitem;
+ EWeekView *week_view;
+ gint span_x, span_y, span_w;
+
+#if 0
+ g_print ("In e_week_view_event_item_update\n");
+#endif
+
+ wveitem = E_WEEK_VIEW_EVENT_ITEM (item);
+ week_view = E_WEEK_VIEW (GTK_WIDGET (item->canvas)->parent);
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (* GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, clip_path, flags);
+
+ item->x1 = 0;
+ item->y1 = 0;
+ item->x2 = 0;
+ item->y2 = 0;
+
+ if (wveitem->event_num != -1 && wveitem->span_num != -1) {
+ if (e_week_view_get_span_position (week_view,
+ wveitem->event_num,
+ wveitem->span_num,
+ &span_x, &span_y,
+ &span_w)) {
+#if 0
+ g_print (" Event:%i Span:%i %i,%i W:%i\n",
+ wveitem->event_num, wveitem->span_num,
+ span_x, span_y, span_w);
+#endif
+ item->x1 = span_x;
+ item->y1 = span_y;
+ item->x2 = span_x + span_w - 1;
+ item->y2 = span_y + week_view->row_height - 1;
+ }
+ }
+}
+
+
+/*
+ * DRAWING ROUTINES - functions to paint the canvas item.
+ */
+
+static void
+e_week_view_event_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EWeekViewEventItem *wveitem;
+ EWeekView *week_view;
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ GtkStyle *style;
+ GdkGC *fg_gc, *gc;
+ GdkFont *font;
+ gint x1, y1, x2, y2, time_x, time_y, time_y_small_min;
+ gint icon_x, icon_y, time_width, min_end_time_x;
+ gint rect_x, rect_w, rect_x2;
+ gboolean one_day_event;
+ gint start_minute, end_minute;
+ gchar buffer[128];
+ gboolean draw_start_triangle = FALSE, draw_end_triangle = FALSE;
+ GdkRectangle clip_rect;
+
+#if 0
+ g_print ("In e_week_view_event_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+
+ wveitem = E_WEEK_VIEW_EVENT_ITEM (canvas_item);
+ week_view = E_WEEK_VIEW (GTK_WIDGET (canvas_item->canvas)->parent);
+ g_return_if_fail (E_IS_WEEK_VIEW (week_view));
+
+ if (wveitem->event_num == -1 || wveitem->span_num == -1)
+ return;
+
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ wveitem->event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + wveitem->span_num);
+
+ style = GTK_WIDGET (week_view)->style;
+ font = style->font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ gc = week_view->main_gc;
+
+ x1 = canvas_item->x1 - x;
+ y1 = canvas_item->y1 - y;
+ x2 = canvas_item->x2 - x;
+ y2 = canvas_item->y2 - y;
+
+ if (x1 == x2 || y1 == y2)
+ return;
+
+ icon_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT + E_WEEK_VIEW_ICON_Y_PAD;
+ start_minute = event->start_minute;
+ end_minute = event->end_minute;
+ time_y = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
+ + E_WEEK_VIEW_EVENT_TEXT_Y_PAD + font->ascent;
+ if (week_view->small_font)
+ time_y_small_min = y1 + E_WEEK_VIEW_EVENT_BORDER_HEIGHT
+ + E_WEEK_VIEW_EVENT_TEXT_Y_PAD
+ + week_view->small_font->ascent;
+ 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;
+
+ one_day_event = e_week_view_is_one_day_event (week_view,
+ wveitem->event_num);
+ if (one_day_event) {
+ time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
+
+ /* Convert the time into a string. We use different parts of
+ the string for the different time formats. Notice that the
+ string is always 11 characters long. */
+ sprintf (buffer, "%02i:%02i %02i:%02i",
+ start_minute / 60, start_minute % 60,
+ end_minute / 60, end_minute % 60);
+
+ /* Draw the start and end times, as required. */
+ switch (week_view->time_format) {
+ case E_WEEK_VIEW_TIME_BOTH_SMALL_MIN:
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 2);
+ gdk_draw_text (drawable, week_view->small_font, fg_gc,
+ time_x + week_view->digit_width * 2,
+ time_y_small_min, buffer + 3, 2);
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x + week_view->digit_width * 4 - 2,
+ time_y, buffer + 6, 2);
+ gdk_draw_text (drawable, week_view->small_font, fg_gc,
+ time_x + week_view->digit_width * 6 - 2,
+ time_y_small_min, buffer + 9, 2);
+
+ icon_x = x1 + time_width * 2 + week_view->space_width
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_START_SMALL_MIN:
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 2);
+ gdk_draw_text (drawable, week_view->small_font, fg_gc,
+ time_x + week_view->digit_width * 2,
+ time_y_small_min, buffer + 3, 2);
+
+ icon_x = x1 + time_width
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_BOTH:
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 11);
+ icon_x = x1 + time_width * 2 + week_view->space_width
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_START:
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 5);
+ icon_x = x1 + time_width
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+ break;
+ case E_WEEK_VIEW_TIME_NONE:
+ icon_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
+ break;
+ }
+
+ /* Draw the icons. */
+ e_week_view_event_item_draw_icons (wveitem, drawable,
+ icon_x, icon_y,
+ x2, FALSE);
+
+ } else {
+ rect_x = x1 + E_WEEK_VIEW_EVENT_L_PAD;
+ rect_w = x2 - x1 - E_WEEK_VIEW_EVENT_L_PAD
+ - E_WEEK_VIEW_EVENT_R_PAD + 1;
+
+ /* Draw the triangles at the start & end, if needed. */
+ if (event->start < week_view->day_starts[span->start_day]) {
+ draw_start_triangle = TRUE;
+ rect_x += 2;
+ rect_w -= 2;
+ }
+
+ if (event->end > week_view->day_starts[span->start_day
+ + span->num_days]) {
+ draw_end_triangle = TRUE;
+ rect_w -= 2;
+ }
+
+ gdk_gc_set_foreground (gc, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
+ gdk_draw_rectangle (drawable, gc, TRUE,
+ rect_x, y1 + 1, rect_w, y2 - y1 - 1);
+
+ gdk_gc_set_foreground (gc, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
+ rect_x2 = rect_x + rect_w - 1;
+ gdk_draw_line (drawable, gc, rect_x, y1, rect_x2, y1);
+ gdk_draw_line (drawable, gc, rect_x, y2, rect_x2, y2);
+
+ if (draw_start_triangle) {
+ e_week_view_event_item_draw_triangle (wveitem, drawable, x1 + E_WEEK_VIEW_EVENT_L_PAD + 2, y1, -3, y2 - y1 + 1);
+ } else {
+ gdk_draw_line (drawable, gc, rect_x, y1, rect_x, y2);
+ }
+
+ if (draw_end_triangle) {
+ e_week_view_event_item_draw_triangle (wveitem, drawable, x2 - E_WEEK_VIEW_EVENT_R_PAD - 2, y1, 3, y2 - y1 + 1);
+ } else {
+ gdk_draw_line (drawable, gc, rect_x2, y1, rect_x2, y2);
+ }
+
+
+ /* Draw the start & end times, if necessary. */
+ min_end_time_x = x1 + 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]) {
+ sprintf (buffer, "%02i:%02i",
+ start_minute / 60, start_minute % 60);
+ time_x = x1 + E_WEEK_VIEW_EVENT_L_PAD
+ + E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD;
+
+ clip_rect.x = x1;
+ clip_rect.y = y1;
+ clip_rect.width = x2 - x1 - E_WEEK_VIEW_EVENT_R_PAD
+ - E_WEEK_VIEW_EVENT_BORDER_WIDTH + 1;
+ clip_rect.height = y2 - y1 + 1;
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rect);
+
+ if (week_view->use_small_font
+ && week_view->small_font) {
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 2);
+ gdk_draw_text (drawable, week_view->small_font,
+ fg_gc,
+ time_x + week_view->digit_width * 2,
+ time_y_small_min,
+ buffer + 3, 2);
+ } else {
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y, buffer, 5);
+ }
+
+ gdk_gc_set_clip_rectangle (fg_gc, NULL);
+
+ min_end_time_x += time_width + 2;
+ }
+
+ if (event->end < week_view->day_starts[span->start_day
+ + span->num_days]) {
+ sprintf (buffer, "%02i:%02i",
+ end_minute / 60, end_minute % 60);
+ time_x = x2 - E_WEEK_VIEW_EVENT_R_PAD
+ - E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ - E_WEEK_VIEW_EVENT_TEXT_X_PAD - 1
+ - time_width;
+
+ if (time_x >= min_end_time_x) {
+ if (week_view->use_small_font
+ && week_view->small_font) {
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y,
+ buffer, 2);
+ gdk_draw_text (drawable,
+ week_view->small_font,
+ fg_gc,
+ time_x + week_view->digit_width * 2,
+ time_y_small_min,
+ buffer + 3, 2);
+ } else {
+ gdk_draw_text (drawable, font, fg_gc,
+ time_x, time_y,
+ buffer, 5);
+ }
+ }
+ }
+
+ /* Draw the icons. */
+ if (span->text_item) {
+ icon_x = span->text_item->x1;
+ e_week_view_event_item_draw_icons (wveitem, drawable,
+ icon_x, icon_y,
+ x2, TRUE);
+ }
+ }
+}
+
+
+static void
+e_week_view_event_item_draw_icons (EWeekViewEventItem *wveitem,
+ GdkDrawable *drawable,
+ gint icon_x,
+ gint icon_y,
+ gint x2,
+ gboolean right_align)
+{
+ EWeekView *week_view;
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ iCalObject *ico;
+ GdkGC *gc;
+ gint num_icons = 0, icon_x_inc;
+ gboolean draw_reminder_icon = FALSE, draw_recurrence_icon = FALSE;
+
+ week_view = E_WEEK_VIEW (GTK_WIDGET (GNOME_CANVAS_ITEM (wveitem)->canvas)->parent);
+
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ wveitem->event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + wveitem->span_num);
+ ico = event->ico;
+
+ gc = week_view->main_gc;
+
+ 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++;
+ }
+
+ icon_x_inc = E_WEEK_VIEW_ICON_WIDTH + E_WEEK_VIEW_ICON_X_PAD;
+
+ if (right_align)
+ icon_x -= icon_x_inc * num_icons;
+
+ if (draw_reminder_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, week_view->reminder_mask);
+ gdk_draw_pixmap (drawable, gc,
+ week_view->reminder_icon,
+ 0, 0, icon_x, icon_y,
+ E_WEEK_VIEW_ICON_WIDTH,
+ E_WEEK_VIEW_ICON_HEIGHT);
+ icon_x += icon_x_inc;
+ }
+
+ if (draw_recurrence_icon && icon_x + E_WEEK_VIEW_ICON_WIDTH <= x2) {
+ gdk_gc_set_clip_origin (gc, icon_x, icon_y);
+ gdk_gc_set_clip_mask (gc, week_view->recurrence_mask);
+ gdk_draw_pixmap (drawable, gc,
+ week_view->recurrence_icon,
+ 0, 0, icon_x, icon_y,
+ E_WEEK_VIEW_ICON_WIDTH,
+ E_WEEK_VIEW_ICON_HEIGHT);
+ icon_x += icon_x_inc;
+ }
+
+ gdk_gc_set_clip_mask (gc, NULL);
+}
+
+
+/* This draws a little triangle to indicate that an event extends past
+ the days visible on screen. */
+static void
+e_week_view_event_item_draw_triangle (EWeekViewEventItem *wveitem,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint w,
+ gint h)
+{
+ EWeekView *week_view;
+ GdkGC *gc;
+ GdkPoint points[3];
+ gint c1, c2;
+
+ week_view = E_WEEK_VIEW (GTK_WIDGET (GNOME_CANVAS_ITEM (wveitem)->canvas)->parent);
+
+ gc = week_view->main_gc;
+
+ 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_gc_set_foreground (gc, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BACKGROUND]);
+ gdk_draw_polygon (drawable, gc, TRUE, points, 3);
+
+ gdk_gc_set_foreground (gc, &week_view->colors[E_WEEK_VIEW_COLOR_EVENT_BORDER]);
+
+ /* If the height is odd we can use the same central point for both
+ lines. If it is even we use different end-points. */
+ c1 = c2 = y + (h / 2);
+ if (h % 2 == 0)
+ c1--;
+
+ gdk_draw_line (drawable, gc, x, y, x + w, c1);
+ gdk_draw_line (drawable, gc, x, y + h - 1, x + w, c2);
+}
+
+
+/* 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_week_view_event_item_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.0;
+}
+
+
+static gint
+e_week_view_event_item_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ EWeekViewEventItem *wveitem;
+
+ wveitem = E_WEEK_VIEW_EVENT_ITEM (item);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ return e_week_view_event_item_button_press (wveitem, event);
+ case GDK_BUTTON_RELEASE:
+ return e_week_view_event_item_button_release (wveitem, event);
+ case GDK_MOTION_NOTIFY:
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_week_view_event_item_button_press (EWeekViewEventItem *wveitem,
+ GdkEvent *bevent)
+{
+ EWeekView *week_view;
+ EWeekViewPosition pos;
+ EWeekViewEvent *event;
+ EWeekViewEventSpan *span;
+ GnomeCanvasItem *item;
+
+ item = GNOME_CANVAS_ITEM (wveitem);
+
+ week_view = E_WEEK_VIEW (GTK_WIDGET (item->canvas)->parent);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
+
+ event = &g_array_index (week_view->events, EWeekViewEvent,
+ wveitem->event_num);
+ span = &g_array_index (week_view->spans, EWeekViewEventSpan,
+ event->spans_index + wveitem->span_num);
+
+#if 0
+ g_print ("In e_week_view_event_item_button_press\n");
+#endif
+
+ pos = e_week_view_event_item_get_position (wveitem, bevent->button.x,
+ bevent->button.y);
+
+ /* Ignore clicks on the event while editing. */
+ if (pos == E_WEEK_VIEW_POS_EVENT && E_TEXT (span->text_item)->editing)
+ return FALSE;
+
+ if (pos == E_WEEK_VIEW_POS_EVENT) {
+ /* Remember the item clicked and the mouse position,
+ so we can start a drag if the mouse moves. */
+ week_view->pressed_event_num = wveitem->event_num;
+ week_view->pressed_span_num = wveitem->span_num;
+
+ week_view->drag_event_x = bevent->button.x;
+ week_view->drag_event_y = bevent->button.y;
+
+ /* FIXME: Remember the day offset from the start of the event.
+ */
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+e_week_view_event_item_button_release (EWeekViewEventItem *wveitem,
+ GdkEvent *event)
+{
+ EWeekView *week_view;
+ GnomeCanvasItem *item;
+
+ item = GNOME_CANVAS_ITEM (wveitem);
+
+ week_view = E_WEEK_VIEW (GTK_WIDGET (item->canvas)->parent);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), FALSE);
+
+#if 0
+ g_print ("In e_week_view_event_item_button_release\n");
+#endif
+
+ if (week_view->pressed_event_num != -1
+ && week_view->pressed_event_num == wveitem->event_num
+ && week_view->pressed_span_num == wveitem->span_num) {
+ e_week_view_start_editing_event (week_view,
+ wveitem->event_num,
+ wveitem->span_num,
+ NULL);
+ week_view->pressed_event_num = -1;
+ return TRUE;
+ }
+
+ week_view->pressed_event_num = -1;
+
+ return FALSE;
+}
+
+
+static EWeekViewPosition
+e_week_view_event_item_get_position (EWeekViewEventItem *wveitem,
+ gdouble x,
+ gdouble y)
+{
+ EWeekView *week_view;
+ GnomeCanvasItem *item;
+
+ item = GNOME_CANVAS_ITEM (wveitem);
+
+ week_view = E_WEEK_VIEW (GTK_WIDGET (item->canvas)->parent);
+ g_return_val_if_fail (E_IS_WEEK_VIEW (week_view), E_WEEK_VIEW_POS_NONE);
+
+#if 0
+ g_print ("In e_week_view_event_item_get_position item: %g,%g %g,%g point: %g,%g\n", item->x1, item->y1, item->x2, item->y2, x, y);
+#endif
+
+ if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
+ || x >= item->x2 - E_WEEK_VIEW_EVENT_R_PAD)
+ return E_WEEK_VIEW_POS_NONE;
+
+ /* Support left/right edge for long events only. */
+ if (!e_week_view_is_one_day_event (week_view, wveitem->event_num)) {
+ if (x < item->x1 + E_WEEK_VIEW_EVENT_L_PAD
+ + E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ + E_WEEK_VIEW_EVENT_TEXT_X_PAD)
+ return E_WEEK_VIEW_POS_LEFT_EDGE;
+
+ if (x >= item->x2 - E_WEEK_VIEW_EVENT_R_PAD
+ - E_WEEK_VIEW_EVENT_BORDER_WIDTH
+ - E_WEEK_VIEW_EVENT_TEXT_X_PAD)
+ return E_WEEK_VIEW_POS_RIGHT_EDGE;
+ }
+
+ return E_WEEK_VIEW_POS_EVENT;
+}
diff --git a/calendar/gui/e-week-view-event-item.h b/calendar/gui/e-week-view-event-item.h
new file mode 100644
index 0000000000..cfe58699b0
--- /dev/null
+++ b/calendar/gui/e-week-view-event-item.h
@@ -0,0 +1,70 @@
+/* -*- 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_WEEK_VIEW_EVENT_ITEM_H_
+#define _E_WEEK_VIEW_EVENT_ITEM_H_
+
+#include "e-week-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EWeekViewEventItem - displays the background, times and icons for an event
+ * in the week/month views. A separate EText canvas item is used to display &
+ * edit the text.
+ */
+
+#define E_WEEK_VIEW_EVENT_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_week_view_event_item_get_type (), EWeekViewEventItem))
+#define E_WEEK_VIEW_EVENT_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_week_view_event_item_get_type ()))
+#define E_IS_WEEK_VIEW_EVENT_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_week_view_event_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The event index in the EWeekView events array. */
+ gint event_num;
+
+ /* The span index within the event. */
+ gint span_num;
+} EWeekViewEventItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EWeekViewEventItemClass;
+
+
+GtkType e_week_view_event_item_get_type (void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_WEEK_VIEW_EVENT_ITEM_H_ */
diff --git a/calendar/gui/e-week-view-main-item.c b/calendar/gui/e-week-view-main-item.c
new file mode 100644
index 0000000000..7101cbef46
--- /dev/null
+++ b/calendar/gui/e-week-view-main-item.c
@@ -0,0 +1,366 @@
+/* -*- 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
+ */
+
+/*
+ * EWeekViewMainItem - displays the background grid and dates for the Week and
+ * Month calendar views.
+ */
+
+#include <config.h>
+#include "e-week-view-main-item.h"
+
+static void e_week_view_main_item_class_init (EWeekViewMainItemClass *class);
+static void e_week_view_main_item_init (EWeekViewMainItem *wvmitem);
+
+static void e_week_view_main_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_week_view_main_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+static void e_week_view_main_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static void e_week_view_main_item_draw_day (EWeekViewMainItem *wvmitem,
+ gint day,
+ GDate *date,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+static double e_week_view_main_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_WEEK_VIEW
+};
+
+
+GtkType
+e_week_view_main_item_get_type (void)
+{
+ static GtkType e_week_view_main_item_type = 0;
+
+ if (!e_week_view_main_item_type) {
+ GtkTypeInfo e_week_view_main_item_info = {
+ "EWeekViewMainItem",
+ sizeof (EWeekViewMainItem),
+ sizeof (EWeekViewMainItemClass),
+ (GtkClassInitFunc) e_week_view_main_item_class_init,
+ (GtkObjectInitFunc) e_week_view_main_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_week_view_main_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_week_view_main_item_info);
+ }
+
+ return e_week_view_main_item_type;
+}
+
+
+static void
+e_week_view_main_item_class_init (EWeekViewMainItemClass *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 ("EWeekViewMainItem::week_view",
+ GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
+ ARG_WEEK_VIEW);
+
+ object_class->set_arg = e_week_view_main_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_week_view_main_item_update;
+ item_class->draw = e_week_view_main_item_draw;
+ item_class->point = e_week_view_main_item_point;
+}
+
+
+static void
+e_week_view_main_item_init (EWeekViewMainItem *wvmitem)
+{
+ wvmitem->week_view = NULL;
+}
+
+
+static void
+e_week_view_main_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EWeekViewMainItem *wvmitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ wvmitem = E_WEEK_VIEW_MAIN_ITEM (o);
+
+ switch (arg_id){
+ case ARG_WEEK_VIEW:
+ wvmitem->week_view = GTK_VALUE_POINTER (*arg);
+ break;
+ }
+}
+
+
+static void
+e_week_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_week_view_main_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EWeekViewMainItem *wvmitem;
+ EWeekView *week_view;
+ GDate date;
+ gint num_days, day, day_x, day_y, day_w, day_h;
+
+#if 0
+ g_print ("In e_week_view_main_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+
+ wvmitem = E_WEEK_VIEW_MAIN_ITEM (canvas_item);
+ week_view = wvmitem->week_view;
+ g_return_if_fail (week_view != NULL);
+
+ /* Step through each of the days. */
+ date = week_view->first_day_shown;
+
+ /* If no date has been set, we just use Dec 1999/January 2000. */
+ if (!g_date_valid (&date))
+ g_date_set_dmy (&date, 27, 12, 1999);
+
+ num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7;
+ for (day = 0; day < num_days; day++) {
+ e_week_view_get_day_position (week_view, day,
+ &day_x, &day_y,
+ &day_w, &day_h);
+ /* Skip any days which are outside the area. */
+ if (day_x < x + width && day_x + day_w >= x
+ && day_y < y + height && day_y + day_h >= y) {
+ e_week_view_main_item_draw_day (wvmitem, day, &date,
+ drawable,
+ day_x - x, day_y - y,
+ day_w, day_h);
+ }
+ g_date_add_days (&date, 1);
+ }
+}
+
+
+static void
+e_week_view_main_item_draw_day (EWeekViewMainItem *wvmitem,
+ gint day,
+ GDate *date,
+ GdkDrawable *drawable,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ EWeekView *week_view;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc, *light_gc, *dark_gc, *gc, *date_gc;
+ GdkGC *selected_fg_gc, *selected_bg_gc;
+ GdkFont *font;
+ gint right_edge, bottom_edge, date_width, date_x, line_y;
+ gboolean show_day_name, show_month_name, selected;
+ gchar buffer[128], *format_string;
+ gint month, day_of_month, max_width;
+ GdkColor *bg_color;
+
+#if 0
+ g_print ("Drawing Day:%i at %i,%i\n", day, x, y);
+#endif
+ week_view = wvmitem->week_view;
+ style = GTK_WIDGET (week_view)->style;
+ font = style->font;
+ fg_gc = style->fg_gc[GTK_STATE_NORMAL];
+ bg_gc = style->bg_gc[GTK_STATE_PRELIGHT];
+ light_gc = style->light_gc[GTK_STATE_NORMAL];
+ dark_gc = style->dark_gc[GTK_STATE_NORMAL];
+ selected_fg_gc = style->fg_gc[GTK_STATE_SELECTED];
+ selected_bg_gc = style->bg_gc[GTK_STATE_SELECTED];
+ gc = week_view->main_gc;
+
+ month = g_date_month (date);
+ day_of_month = g_date_day (date);
+ line_y = y + E_WEEK_VIEW_DATE_T_PAD + font->ascent
+ + font->descent + E_WEEK_VIEW_DATE_LINE_T_PAD;
+
+ /* Draw the background of the day. In the month view odd months are
+ one color and even months another, so you can easily see when each
+ month starts (defaults are white for odd - January, March, ... and
+ light gray for even). In the week view the background is always the
+ same color, the color used for the odd months in the month view. */
+ if (week_view->display_month && (month % 2 == 0))
+ bg_color = &week_view->colors[E_WEEK_VIEW_COLOR_EVEN_MONTHS];
+ else
+ bg_color = &week_view->colors[E_WEEK_VIEW_COLOR_ODD_MONTHS];
+
+ gdk_gc_set_foreground (gc, bg_color);
+ gdk_draw_rectangle (drawable, gc, TRUE, x, y, width, height);
+
+ /* Draw the lines on the right and bottom of the cell. The canvas is
+ sized so that the lines on the right & bottom edges will be off the
+ edge of the canvas, so we don't have to worry about them. */
+ right_edge = x + width - 1;
+ bottom_edge = y + height - 1;
+
+ gdk_draw_line (drawable, fg_gc,
+ right_edge, y, right_edge, bottom_edge);
+ gdk_draw_line (drawable, fg_gc,
+ x, bottom_edge, right_edge, bottom_edge);
+
+ /* If the day is selected, draw the blue background. */
+ selected = TRUE;
+ if (week_view->selection_start_day == -1
+ || week_view->selection_start_day > day
+ || week_view->selection_end_day < day)
+ selected = FALSE;
+ if (selected) {
+ if (week_view->display_month)
+ gdk_draw_rectangle (drawable, selected_bg_gc, TRUE,
+ x + 2, y + 1,
+ width - 5,
+ E_WEEK_VIEW_DATE_T_PAD - 1
+ + font->ascent + font->descent);
+ else
+ gdk_draw_rectangle (drawable, selected_bg_gc, TRUE,
+ x + 2, y + 1,
+ width - 5, line_y - y);
+ }
+
+ /* Display the date in the top of the cell.
+ In the week view, display the long format "10 January" in all cells,
+ or abbreviate it to "10 Jan" or "10" if that doesn't fit.
+ In the month view, only use the long format for the first cell and
+ the 1st of each month, otherwise use "10". */
+ show_day_name = FALSE;
+ show_month_name = FALSE;
+ if (!week_view->display_month) {
+ show_day_name = TRUE;
+ show_month_name = TRUE;
+ } else if (day == 0 || day_of_month == 1) {
+ show_month_name = TRUE;
+ }
+
+ /* Now find the longest form of the date that will fit. */
+ max_width = width - 4;
+ format_string = NULL;
+ if (show_day_name) {
+ if (week_view->max_abbr_day_width +
+ week_view->digit_width * 2 + week_view->space_width * 2
+ + week_view->month_widths[month - 1] < max_width)
+ format_string = "%a %d %B";
+ }
+ if (!format_string && show_month_name) {
+ if (week_view->digit_width * 2 + week_view->space_width
+ + week_view->month_widths[month - 1] < max_width)
+ format_string = "%d %B";
+ else if (week_view->digit_width * 2 + week_view->space_width
+ + week_view->abbr_month_widths[month - 1] < max_width)
+ format_string = "%d %b";
+ }
+
+ g_date_strftime (buffer, 128, format_string ? format_string : "%d",
+ date);
+ date_width = gdk_string_width (font, buffer);
+ date_x = x + width - date_width - E_WEEK_VIEW_DATE_R_PAD;
+ date_x = MAX (date_x, x + 1);
+
+ if (selected)
+ date_gc = selected_fg_gc;
+ else
+ date_gc = fg_gc;
+ gdk_draw_string (drawable, font, date_gc,
+ date_x, y + E_WEEK_VIEW_DATE_T_PAD + font->ascent,
+ buffer);
+
+ /* Draw the line under the date. */
+ if (!week_view->display_month) {
+ gdk_draw_line (drawable, fg_gc,
+ x + E_WEEK_VIEW_DATE_LINE_L_PAD, line_y,
+ right_edge, line_y);
+ }
+}
+
+
+
+
+/* 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_week_view_main_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-week-view-main-item.h b/calendar/gui/e-week-view-main-item.h
new file mode 100644
index 0000000000..f75dcb0ec9
--- /dev/null
+++ b/calendar/gui/e-week-view-main-item.h
@@ -0,0 +1,66 @@
+/* -*- 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_WEEK_VIEW_MAIN_ITEM_H_
+#define _E_WEEK_VIEW_MAIN_ITEM_H_
+
+#include "e-week-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EWeekViewMainItem - displays the background grid and dates for the Week and
+ * Month calendar views.
+ */
+
+#define E_WEEK_VIEW_MAIN_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_week_view_main_item_get_type (), EWeekViewMainItem))
+#define E_WEEK_VIEW_MAIN_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_week_view_main_item_get_type ()))
+#define E_IS_WEEK_VIEW_MAIN_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_week_view_main_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The parent EWeekView widget. */
+ EWeekView *week_view;
+} EWeekViewMainItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EWeekViewMainItemClass;
+
+
+GtkType e_week_view_main_item_get_type (void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_WEEK_VIEW_MAIN_ITEM_H_ */
diff --git a/calendar/gui/e-week-view-titles-item.c b/calendar/gui/e-week-view-titles-item.c
new file mode 100644
index 0000000000..14f47e4bcf
--- /dev/null
+++ b/calendar/gui/e-week-view-titles-item.c
@@ -0,0 +1,300 @@
+/* -*- 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
+ */
+
+/*
+ * EWeekViewTitlesItem - displays the 'Monday', 'Tuesday' etc. at the top of
+ * the Month calendar view.
+ */
+
+#include <config.h>
+#include "e-week-view-titles-item.h"
+
+static void e_week_view_titles_item_class_init (EWeekViewTitlesItemClass *class);
+static void e_week_view_titles_item_init (EWeekViewTitlesItem *wvtitem);
+
+static void e_week_view_titles_item_set_arg (GtkObject *o,
+ GtkArg *arg,
+ guint arg_id);
+static void e_week_view_titles_item_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+static void e_week_view_titles_item_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+static double e_week_view_titles_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_WEEK_VIEW
+};
+
+
+GtkType
+e_week_view_titles_item_get_type (void)
+{
+ static GtkType e_week_view_titles_item_type = 0;
+
+ if (!e_week_view_titles_item_type) {
+ GtkTypeInfo e_week_view_titles_item_info = {
+ "EWeekViewTitlesItem",
+ sizeof (EWeekViewTitlesItem),
+ sizeof (EWeekViewTitlesItemClass),
+ (GtkClassInitFunc) e_week_view_titles_item_class_init,
+ (GtkObjectInitFunc) e_week_view_titles_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ e_week_view_titles_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &e_week_view_titles_item_info);
+ }
+
+ return e_week_view_titles_item_type;
+}
+
+
+static void
+e_week_view_titles_item_class_init (EWeekViewTitlesItemClass *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 ("EWeekViewTitlesItem::week_view",
+ GTK_TYPE_POINTER, GTK_ARG_WRITABLE,
+ ARG_WEEK_VIEW);
+
+ object_class->set_arg = e_week_view_titles_item_set_arg;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->update = e_week_view_titles_item_update;
+ item_class->draw = e_week_view_titles_item_draw;
+ item_class->point = e_week_view_titles_item_point;
+}
+
+
+static void
+e_week_view_titles_item_init (EWeekViewTitlesItem *wvtitem)
+{
+ wvtitem->week_view = NULL;
+}
+
+
+static void
+e_week_view_titles_item_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ EWeekViewTitlesItem *wvtitem;
+
+ item = GNOME_CANVAS_ITEM (o);
+ wvtitem = E_WEEK_VIEW_TITLES_ITEM (o);
+
+ switch (arg_id){
+ case ARG_WEEK_VIEW:
+ wvtitem->week_view = GTK_VALUE_POINTER (*arg);
+ break;
+ }
+}
+
+
+static void
+e_week_view_titles_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_week_view_titles_item_draw (GnomeCanvasItem *canvas_item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ EWeekViewTitlesItem *wvtitem;
+ EWeekView *week_view;
+ GtkStyle *style;
+ GdkGC *fg_gc, *bg_gc, *light_gc, *dark_gc;
+ GdkFont *font;
+ gint canvas_width, canvas_height, col_width, col, date_width, date_x;
+ gchar buffer[128], *date_format;
+ GDate date;
+ GdkRectangle clip_rect;
+ gboolean long_format;
+
+#if 0
+ g_print ("In e_week_view_titles_item_draw %i,%i %ix%i\n",
+ x, y, width, height);
+#endif
+
+ wvtitem = E_WEEK_VIEW_TITLES_ITEM (canvas_item);
+ week_view = wvtitem->week_view;
+ g_return_if_fail (week_view != NULL);
+
+ style = GTK_WIDGET (week_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;
+
+ /* Draw the shadow around the dates. */
+ gdk_draw_line (drawable, light_gc,
+ 1 - x, 1 - y,
+ canvas_width - 2 - x, 1 - y);
+ gdk_draw_line (drawable, light_gc,
+ 1 - x, 2 - y,
+ 1 - x, canvas_height - 1 - y);
+
+ gdk_draw_rectangle (drawable, dark_gc, FALSE,
+ 0 - x, 0 - y,
+ canvas_width - 1, canvas_height);
+
+ /* Determine the format to use. */
+ col_width = canvas_width / week_view->columns;
+ if (col_width > week_view->max_day_width + 2) {
+ date_format = "%A";
+ long_format = TRUE;
+ } else {
+ date_format = "%a";
+ long_format = FALSE;
+ }
+
+ /* Shift right one pixel to account for the shadow around the main
+ canvas. */
+ x--;
+
+ /* Draw the date. Set a clipping rectangle so we don't draw over the
+ next day. */
+ g_date_clear (&date, 1);
+ g_date_set_dmy (&date, 27, 3, 2000); /* Must be a Monday. */
+ for (col = 0; col < week_view->columns; col++) {
+ if (col == 5 && week_view->compress_weekend) {
+ g_date_strftime (buffer, 128, "%a/", &date);
+ g_date_add_days (&date, 1);
+ g_date_strftime (buffer + strlen (buffer), 100,
+ "%a", &date);
+ } else {
+ g_date_strftime (buffer, 128, date_format, &date);
+ }
+
+ clip_rect.x = week_view->col_offsets[col] - x;
+ clip_rect.y = 2 - y;
+ clip_rect.width = week_view->col_widths[col];
+ clip_rect.height = canvas_height - 2;
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rect);
+
+ if (col == 5 && week_view->compress_weekend)
+ date_width = week_view->abbr_day_widths[5]
+ + week_view->slash_width
+ + week_view->abbr_day_widths[6];
+ else if (long_format)
+ date_width = week_view->day_widths[col];
+ else
+ date_width = week_view->abbr_day_widths[col];
+
+ date_x = week_view->col_offsets[col]
+ + (week_view->col_widths[col] - date_width) / 2;
+ date_x = MAX (date_x, week_view->col_offsets[col]);
+ 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 (col != 0) {
+ gdk_draw_line (drawable, light_gc,
+ week_view->col_offsets[col] - x,
+ 4 - y,
+ week_view->col_offsets[col] - x,
+ canvas_height - 4 - y);
+
+ gdk_draw_line (drawable, dark_gc,
+ week_view->col_offsets[col] - 1 - x,
+ 4 - y,
+ week_view->col_offsets[col] - 1 - x,
+ canvas_height - 4 - y);
+ }
+
+ /* Draw the lines between each column. */
+ if (col != 0) {
+ gdk_draw_line (drawable, style->black_gc,
+ week_view->col_offsets[col] - x,
+ canvas_height - y,
+ week_view->col_offsets[col] - x,
+ canvas_height - y);
+ }
+
+ g_date_add_days (&date, 1);
+ }
+}
+
+
+/* This is supposed to return the nearest item the the point and the distance.
+ Since we are the only item we just return ourself and 0 for the distance.
+ This is needed so that we get button/motion events. */
+static double
+e_week_view_titles_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-week-view-titles-item.h b/calendar/gui/e-week-view-titles-item.h
new file mode 100644
index 0000000000..7ce1ccd386
--- /dev/null
+++ b/calendar/gui/e-week-view-titles-item.h
@@ -0,0 +1,66 @@
+/* -*- 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_WEEK_VIEW_TITLES_ITEM_H_
+#define _E_WEEK_VIEW_TITLES_ITEM_H_
+
+#include "e-week-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EWeekViewTitlesItem - displays the 'Monday', 'Tuesday' etc. at the top of
+ * the Month calendar view.
+ */
+
+#define E_WEEK_VIEW_TITLES_ITEM(obj) (GTK_CHECK_CAST((obj), \
+ e_week_view_titles_item_get_type (), EWeekViewTitlesItem))
+#define E_WEEK_VIEW_TITLES_ITEM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k),\
+ e_week_view_titles_item_get_type ()))
+#define E_IS_WEEK_VIEW_TITLES_ITEM(o) (GTK_CHECK_TYPE((o), \
+ e_week_view_titles_item_get_type ()))
+
+typedef struct {
+ GnomeCanvasItem canvas_item;
+
+ /* The parent EWeekView widget. */
+ EWeekView *week_view;
+} EWeekViewTitlesItem;
+
+typedef struct {
+ GnomeCanvasItemClass parent_class;
+
+} EWeekViewTitlesItemClass;
+
+
+GtkType e_week_view_titles_item_get_type (void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_WEEK_VIEW_TITLES_ITEM_H_ */
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);
+}
+
+
diff --git a/calendar/gui/e-week-view.h b/calendar/gui/e-week-view.h
new file mode 100644
index 0000000000..eac23565c2
--- /dev/null
+++ b/calendar/gui/e-week-view.h
@@ -0,0 +1,357 @@
+/* -*- 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_WEEK_VIEW_H_
+#define _E_WEEK_VIEW_H_
+
+#include <gtk/gtktable.h>
+#include <libgnomeui/gnome-canvas.h>
+
+#include "gnome-cal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * EWeekView - displays the Week & Month views of the calendar.
+ */
+
+/* The maximum number of weeks we show. 5 is usually enough for 1 month. */
+#define E_WEEK_VIEW_MAX_WEEKS 5
+
+/* The size of the reminder & recurrence icons, and padding around them. */
+#define E_WEEK_VIEW_ICON_WIDTH 16
+#define E_WEEK_VIEW_ICON_HEIGHT 16
+#define E_WEEK_VIEW_ICON_X_PAD 0
+#define E_WEEK_VIEW_ICON_Y_PAD 0
+
+/* The space on the left & right of the event. (The triangle to indicate the
+ event continues is displayed in this space). */
+#define E_WEEK_VIEW_EVENT_L_PAD 2
+#define E_WEEK_VIEW_EVENT_R_PAD 3
+
+/* The vertical spacing between rows of events. */
+#define E_WEEK_VIEW_EVENT_Y_SPACING 1
+
+/* The size of the border around the event. */
+#define E_WEEK_VIEW_EVENT_BORDER_WIDTH 1
+#define E_WEEK_VIEW_EVENT_BORDER_HEIGHT 1
+
+/* The padding on each side of the event text. */
+#define E_WEEK_VIEW_EVENT_TEXT_X_PAD 4
+#define E_WEEK_VIEW_EVENT_TEXT_Y_PAD 1
+
+/* The space on the right of the time string, if it is shown. */
+#define E_WEEK_VIEW_EVENT_TIME_R_PAD 2
+
+/* The padding above and on the right of the date string at the top of each
+ cell. */
+#define E_WEEK_VIEW_DATE_T_PAD 2
+#define E_WEEK_VIEW_DATE_R_PAD 4
+
+/* The padding above and below the line under the date string, in the Week
+ view, and also the space on the left of it. */
+#define E_WEEK_VIEW_DATE_LINE_T_PAD 1
+#define E_WEEK_VIEW_DATE_LINE_B_PAD 1
+#define E_WEEK_VIEW_DATE_LINE_L_PAD 10
+
+/* The padding below the date string in the Month view. */
+#define E_WEEK_VIEW_DATE_B_PAD 1
+
+/* These index our colors array. */
+typedef enum
+{
+ E_WEEK_VIEW_COLOR_EVEN_MONTHS,
+ E_WEEK_VIEW_COLOR_ODD_MONTHS,
+ E_WEEK_VIEW_COLOR_EVENT_BACKGROUND,
+ E_WEEK_VIEW_COLOR_EVENT_BORDER,
+
+ E_WEEK_VIEW_COLOR_LAST
+} EWeekViewColors;
+
+/* These specify which part of the selection we are dragging, if any. */
+typedef enum
+{
+ E_WEEK_VIEW_DRAG_NONE,
+ E_WEEK_VIEW_DRAG_START,
+ E_WEEK_VIEW_DRAG_END
+} EWeekViewDragPosition;
+
+/* These specify which times are shown for the 1-day events. We use the small
+ font for the minutes if it can be loaded and the option is on. */
+typedef enum
+{
+ E_WEEK_VIEW_TIME_NONE,
+ E_WEEK_VIEW_TIME_START,
+ E_WEEK_VIEW_TIME_BOTH,
+ E_WEEK_VIEW_TIME_START_SMALL_MIN,
+ E_WEEK_VIEW_TIME_BOTH_SMALL_MIN
+} EWeekViewTimeFormat;
+
+/* Specifies the position of the mouse. */
+typedef enum
+{
+ E_WEEK_VIEW_POS_OUTSIDE,
+ E_WEEK_VIEW_POS_NONE,
+ E_WEEK_VIEW_POS_EVENT,
+ E_WEEK_VIEW_POS_LEFT_EDGE,
+ E_WEEK_VIEW_POS_RIGHT_EDGE
+} EWeekViewPosition;
+
+
+typedef struct _EWeekViewEventSpan EWeekViewEventSpan;
+struct _EWeekViewEventSpan {
+ guint start_day : 6;
+ guint num_days : 3;
+ guint row : 7;
+ GnomeCanvasItem *background_item;
+ GnomeCanvasItem *text_item;
+};
+
+typedef struct _EWeekViewEvent EWeekViewEvent;
+struct _EWeekViewEvent {
+ iCalObject *ico;
+ time_t start;
+ time_t end;
+ guint16 start_minute; /* Minutes from the start of the day. */
+ guint16 end_minute;
+ gint spans_index;
+ guint num_spans;
+};
+
+
+#define E_WEEK_VIEW(obj) GTK_CHECK_CAST (obj, e_week_view_get_type (), EWeekView)
+#define E_WEEK_VIEW_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_week_view_get_type (), EWeekViewClass)
+#define E_IS_WEEK_VIEW(obj) GTK_CHECK_TYPE (obj, e_week_view_get_type ())
+
+
+typedef struct _EWeekView EWeekView;
+typedef struct _EWeekViewClass EWeekViewClass;
+
+struct _EWeekView
+{
+ GtkTable table;
+
+ /* The top canvas where the dates are shown. */
+ GtkWidget *titles_canvas;
+ GnomeCanvasItem *titles_canvas_item;
+
+ /* The main canvas where the appointments are shown. */
+ GtkWidget *main_canvas;
+ GnomeCanvasItem *main_canvas_item;
+
+ GtkWidget *vscrollbar;
+
+ /* The calendar we are associated with. */
+ GnomeCalendar *calendar;
+
+ /* The array of EWeekViewEvent elements. */
+ GArray *events;
+ gboolean events_sorted;
+ gboolean events_need_layout;
+ gboolean events_need_reshape;
+
+ /* An array of EWeekViewEventSpan elements. Each event has its own
+ space within this array, and uses the spans_index and num_spans
+ fields of the EWeekViewEvent struct to access it. */
+ GArray *spans;
+
+ /* The start of each day displayed. */
+ time_t day_starts[E_WEEK_VIEW_MAX_WEEKS * 7 + 1];
+
+ /* The base date, where the adjustment value is 0. */
+ GDate base_date;
+
+ /* The first day shown in the view. */
+ GDate first_day_shown;
+
+ /* If we are displaying 1 week or 1 month. */
+ gboolean display_month;
+
+ /* If Sat & Sun are compressed. Only applicable in month view, since
+ they are always compressed into 1 cell in the week view. */
+ gboolean compress_weekend;
+
+ /* The vertical offset of the events from the top of the cells. */
+ gint events_y_offset;
+
+ /* The height of the events, not including spacing between them. */
+ gint row_height;
+
+ /* The number of rows of events in each cell. */
+ gint rows_per_cell;
+ gint rows_per_compressed_cell;
+
+ /* If the small font is used for displaying the minutes. */
+ gboolean use_small_font;
+
+ /* Small font to display the minutes. */
+ GdkFont *small_font;
+
+ /* The widths of various pieces of text, used to determine which of
+ several date formats to display, set in e_week_view_style_set(). */
+ gint space_width; /* One space character ' '. */
+ gint colon_width; /* Size of ':' in the font. */
+ gint slash_width; /* Size of '/' in the font. */
+ gint digit_width; /* Size of a '0' digit. */
+ gint small_digit_width; /* Size of a small_font '0' digit. */
+ gint day_widths[7]; /* Monday first. */
+ gint max_day_width;
+ gint abbr_day_widths[7];
+ gint max_abbr_day_width;
+ gint month_widths[12];
+ gint max_month_width;
+ gint abbr_month_widths[12];
+ gint max_abbr_month_width;
+
+ /* The size of the main grid of days and of the cells. Note that the
+ offsets arrays have one more element than the widths/heights arrays
+ since they also contain the right/bottom edge. */
+ gint rows;
+ gint columns;
+ gint col_widths[7];
+ gint col_offsets[8];
+ gint row_heights[10];
+ gint row_offsets[11];
+
+ /* This specifies which times we are showing for the events, depending
+ on how much room is available. */
+ EWeekViewTimeFormat time_format;
+
+ /* 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_WEEK_VIEW_COLOR_LAST];
+
+ /* The normal & resizing cursors. */
+ GdkCursor *normal_cursor;
+ GdkCursor *move_cursor;
+ GdkCursor *resize_width_cursor;
+
+ /* This remembers the last cursor set on the window. */
+ GdkCursor *last_cursor_set;
+
+ /* The currently selected region, in days from the first day shown.
+ If selection_start_day is -1 there is no current selection. */
+ gint selection_start_day;
+ gint selection_end_day;
+
+ /* This specifies which end of the selection is being dragged, or is
+ E_WEEK_VIEW_DRAG_NONE if the selection isn't being dragged. */
+ EWeekViewDragPosition selection_drag_pos;
+
+ /* 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_num;
+ gint pressed_span_num;
+
+ /* The event span currently being edited. The num is -1 if no event is
+ being edited. */
+ gint editing_event_num;
+ gint editing_span_num;
+
+ /* The day or event that the context menu is for. */
+ gint popup_event_day;
+ gint popup_event_num;
+
+ /* The last mouse position when dragging, in the entire canvas. */
+ gint drag_event_x;
+ gint drag_event_y;
+};
+
+struct _EWeekViewClass
+{
+ GtkTableClass parent_class;
+};
+
+
+GtkType e_week_view_get_type (void);
+GtkWidget* e_week_view_new (void);
+
+void e_week_view_set_calendar (EWeekView *week_view,
+ GnomeCalendar *calendar);
+
+/* 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);
+
+/* Whether to display 1 week or 1 month (5 weeks). It defaults to 1 week. */
+gboolean e_week_view_get_display_month (EWeekView *week_view);
+void e_week_view_set_display_month (EWeekView *week_view,
+ gboolean display_month);
+
+/* Whether the weekend (Sat/Sun) should be compressed into 1 cell in the Month
+ view. In the Week view they are always compressed. */
+gboolean e_week_view_get_compress_weekend (EWeekView *week_view);
+void e_week_view_set_compress_weekend (EWeekView *week_view,
+ gboolean compress);
+
+/* This is called when one or more events have been updated, either within the
+ EWeekView itself, or via another calendar view or application. If only one
+ event has changed, it is passed in the ico argument and the flags indicate
+ the change - whether it is a new event, or just the summary has changed. */
+void e_week_view_update_event (EWeekView *week_view,
+ iCalObject *ico,
+ int flags);
+
+
+/*
+ * Internal functions called by the associated canvas items.
+ */
+void e_week_view_get_day_position (EWeekView *week_view,
+ gint day,
+ gint *day_x,
+ gint *day_y,
+ gint *day_w,
+ gint *day_h);
+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);
+gboolean e_week_view_is_one_day_event (EWeekView *week_view,
+ gint event_num);
+void e_week_view_start_editing_event (EWeekView *week_view,
+ gint event_num,
+ gint span_num,
+ gchar *initial_text);
+void e_week_view_stop_editing_event (EWeekView *week_view);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _E_WEEK_VIEW_H_ */
diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c
index 6229dbf30b..0627d1bd89 100644
--- a/calendar/gui/gnome-cal.c
+++ b/calendar/gui/gnome-cal.c
@@ -11,22 +11,34 @@
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtkhpaned.h>
+#include <gtk/gtklabel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtknotebook.h>
-#include <gtk/gtkframe.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkvpaned.h>
#include <libgnomeui/gnome-messagebox.h>
#include <cal-util/timeutil.h>
#include "alarm.h"
+#include "e-day-view.h"
+#include "e-week-view.h"
+#include "gncal-todo.h"
#include "gnome-cal.h"
-#include "gncal-day-panel.h"
-#include "gncal-week-view.h"
-#include "month-view.h"
#include "year-view.h"
#include "calendar-commands.h"
-GnomeApp *parent_class;
+static void gnome_calendar_update_view_times (GnomeCalendar *gcal,
+ GtkWidget *page);
+static void gnome_calendar_update_gtk_calendar (GnomeCalendar *gcal);
+static void gnome_calendar_on_day_selected (GtkCalendar *calendar,
+ GnomeCalendar *gcal);
+static void gnome_calendar_on_month_changed (GtkCalendar *calendar,
+ GnomeCalendar *gcal);
+
+static GtkVBoxClass *parent_class;
guint
gnome_calendar_get_type (void)
@@ -46,8 +58,9 @@ gnome_calendar_get_type (void)
gnome_calendar_type = gtk_type_unique(gnome_app_get_type(), &gnome_calendar_info);
parent_class = gtk_type_class (gnome_app_get_type());
*/
- gnome_calendar_type = gtk_type_unique (gtk_frame_get_type (), &gnome_calendar_info);
- parent_class = gtk_type_class (gtk_frame_get_type ());
+ gnome_calendar_type = gtk_type_unique (gtk_vbox_get_type (),
+ &gnome_calendar_info);
+ parent_class = gtk_type_class (gtk_vbox_get_type ());
}
return gnome_calendar_type;
}
@@ -55,42 +68,116 @@ gnome_calendar_get_type (void)
static void
setup_widgets (GnomeCalendar *gcal)
{
- time_t now;
-
- now = time (NULL);
-
- gcal->notebook = gtk_notebook_new ();
- gcal->day_view = gncal_day_panel_new (gcal, now);
- gcal->week_view = gncal_week_view_new (gcal, now);
- gcal->month_view = month_view_new (gcal, now);
- gcal->year_view = year_view_new (gcal, now);
-
+ GtkWidget *vpane, *w;
+
+ /* The Main Notebook. */
+ gcal->main_notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (gcal->main_notebook),
+ FALSE);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (gcal->main_notebook), FALSE);
+ gtk_widget_show (gcal->main_notebook);
+ gtk_box_pack_start (GTK_BOX (gcal), gcal->main_notebook,
+ TRUE, TRUE, 0);
+
+ /* The First Page of the Main Notebook, containing a HPaned with the
+ Sub-Notebook on the left and the GtkCalendar and ToDo list on the
+ right. */
+ gcal->hpane = gtk_hpaned_new ();
+ gtk_widget_show (gcal->hpane);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
+ gcal->hpane, gtk_label_new (""));
+
+ /* The Sub-Notebook, to contain the Day, Work-Week & Week views. */
+ gcal->sub_notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (gcal->sub_notebook),
+ FALSE);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (gcal->sub_notebook), FALSE);
+ gtk_widget_show (gcal->sub_notebook);
+ gtk_paned_pack1 (GTK_PANED (gcal->hpane), gcal->sub_notebook,
+ TRUE, TRUE);
+
+ /* The VPaned widget, to contain the GtkCalendar & ToDo list. */
+ vpane = gtk_vpaned_new ();
+ gtk_widget_show (vpane);
+ gtk_paned_pack2 (GTK_PANED (gcal->hpane), vpane, FALSE, TRUE);
+
+ /* The GtkCalendar. */
+ w = gtk_calendar_new ();
+ gcal->gtk_calendar = GTK_CALENDAR (w);
+ gtk_widget_show (w);
+ gtk_paned_pack1 (GTK_PANED (vpane), w, FALSE, TRUE);
+ gcal->day_selected_id = gtk_signal_connect (GTK_OBJECT (gcal->gtk_calendar),
+ "day_selected",
+ (GtkSignalFunc) gnome_calendar_on_day_selected,
+ gcal);
+ gtk_signal_connect (GTK_OBJECT (gcal->gtk_calendar), "month_changed",
+ GTK_SIGNAL_FUNC (gnome_calendar_on_month_changed),
+ gcal);
+
+ /* The ToDo list. */
+ gcal->todo = gncal_todo_new (gcal);
+ gtk_paned_pack2 (GTK_PANED (vpane), gcal->todo, TRUE, TRUE);
+ gtk_widget_show (gcal->todo);
+
+
+ /* The Day View. */
+ gcal->day_view = e_day_view_new ();
+ e_day_view_set_calendar (E_DAY_VIEW (gcal->day_view), gcal);
+ gtk_widget_show (gcal->day_view);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
+ gcal->day_view, gtk_label_new (""));
+
+ /* The Work Week View. */
+ gcal->work_week_view = e_day_view_new ();
+ e_day_view_set_days_shown (E_DAY_VIEW (gcal->work_week_view), 5);
+ e_day_view_set_calendar (E_DAY_VIEW (gcal->work_week_view), gcal);
+ gtk_widget_show (gcal->work_week_view);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
+ gcal->work_week_view, gtk_label_new (""));
+
+ /* The Week View. */
+ gcal->week_view = e_week_view_new ();
+ e_week_view_set_calendar (E_WEEK_VIEW (gcal->week_view), gcal);
+ gtk_widget_show (gcal->week_view);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->sub_notebook),
+ gcal->week_view, gtk_label_new (""));
+
+ /* The Month View. */
+ gcal->month_view = e_week_view_new ();
+ e_week_view_set_calendar (E_WEEK_VIEW (gcal->month_view), gcal);
+ e_week_view_set_display_month (E_WEEK_VIEW (gcal->month_view), TRUE);
+ gtk_widget_show (gcal->month_view);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
+ gcal->month_view, gtk_label_new (""));
+
+ /* The Year View. */
+ gcal->year_view = year_view_new (gcal, gcal->selection_start_time);
+#if 0
+ gtk_widget_show (gcal->year_view);
+#endif
gcal->year_view_sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_show (gcal->year_view_sw);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (gcal->year_view_sw),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
- gtk_container_add (GTK_CONTAINER (gcal->year_view_sw), gcal->year_view);
+ gtk_container_add (GTK_CONTAINER (gcal->year_view_sw),
+ gcal->year_view);
GTK_LAYOUT (gcal->year_view)->vadjustment->step_increment = 10.0;
- gtk_adjustment_changed (GTK_ADJUSTMENT (GTK_LAYOUT (gcal->year_view)->vadjustment));
-
- gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->day_view, gtk_label_new (_("Day View")));
- gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->week_view, gtk_label_new (_("Week View")));
- gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->month_view, gtk_label_new (_("Month View")));
- gtk_notebook_append_page (GTK_NOTEBOOK (gcal->notebook), gcal->year_view_sw, gtk_label_new (_("Year View")));
-
- gtk_widget_show_all (gcal->notebook);
-
- /*gnome_app_set_contents (GNOME_APP (gcal), gcal->notebook);*/
- gtk_container_add (GTK_CONTAINER (gcal), gcal->notebook);
-
-
- gtk_widget_show (GTK_WIDGET (gcal));
+ gtk_adjustment_changed (GTK_LAYOUT (gcal->year_view)->vadjustment);
+ gtk_notebook_append_page (GTK_NOTEBOOK (gcal->main_notebook),
+ gcal->year_view_sw, gtk_label_new (""));
}
static GtkWidget *
get_current_page (GnomeCalendar *gcal)
{
- return GTK_NOTEBOOK (gcal->notebook)->cur_page->child;
+ GtkWidget *page;
+
+ page = GTK_NOTEBOOK (gcal->main_notebook)->cur_page->child;
+ if (page == gcal->hpane)
+ return GTK_NOTEBOOK (gcal->sub_notebook)->cur_page->child;
+ else
+ return page;
}
char *
@@ -105,6 +192,8 @@ gnome_calendar_get_current_view_name (GnomeCalendar *gcal)
if (page == gcal->day_view)
return "dayview";
+ else if (page == gcal->work_week_view)
+ return "workweekview";
else if (page == gcal->week_view)
return "weekview";
else if (page == gcal->month_view)
@@ -118,45 +207,62 @@ gnome_calendar_get_current_view_name (GnomeCalendar *gcal)
void
gnome_calendar_goto (GnomeCalendar *gcal, time_t new_time)
{
- GtkWidget *current;
-
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
g_return_if_fail (new_time != -1);
- current = get_current_page (gcal);
- new_time = time_day_begin (new_time);
-
- if (current == gcal->day_view)
- gncal_day_panel_set (GNCAL_DAY_PANEL (gcal->day_view), new_time);
- else if (current == gcal->week_view)
- gncal_week_view_set (GNCAL_WEEK_VIEW (gcal->week_view), new_time);
- else if (current == gcal->month_view)
- month_view_set (MONTH_VIEW (gcal->month_view), new_time);
- else if (current == gcal->year_view_sw)
- year_view_set (YEAR_VIEW (gcal->year_view), new_time);
+ gcal->selection_start_time = time_day_begin (new_time);
+ gcal->selection_end_time = time_add_day (gcal->selection_start_time,
+ 1);
+ gnome_calendar_update_view_times (gcal, NULL);
+ gnome_calendar_update_gtk_calendar (gcal);
+}
+
+
+static void
+gnome_calendar_update_view_times (GnomeCalendar *gcal,
+ GtkWidget *page)
+{
+ if (page == NULL)
+ page = get_current_page (gcal);
+
+ if (page == gcal->day_view
+ || page == gcal->work_week_view)
+ e_day_view_set_selected_time_range (E_DAY_VIEW (page),
+ gcal->selection_start_time,
+ gcal->selection_end_time);
+ else if (page == gcal->week_view
+ || page == gcal->month_view)
+ e_week_view_set_selected_time_range (E_WEEK_VIEW (page),
+ gcal->selection_start_time,
+ gcal->selection_end_time);
+ else if (page == gcal->year_view_sw)
+ year_view_set (YEAR_VIEW (gcal->year_view),
+ gcal->selection_start_time);
else {
g_warning ("My penguin is gone!");
g_assert_not_reached ();
}
-
- gcal->current_display = new_time;
}
static void
gnome_calendar_direction (GnomeCalendar *gcal, int direction)
{
GtkWidget *cp = get_current_page (gcal);
- time_t new_time;
+ time_t current_time, new_time;
+
+ current_time = gcal->selection_start_time;
if (cp == gcal->day_view)
- new_time = time_add_day (time_day_begin (gcal->current_display), 1 * direction);
+ new_time = time_add_day (current_time, direction);
+ else if (cp == gcal->work_week_view)
+ new_time = time_add_week (current_time, direction);
else if (cp == gcal->week_view)
- new_time = time_add_week (time_week_begin (gcal->current_display), 1 * direction);
+ new_time = time_add_week (current_time, direction);
else if (cp == gcal->month_view)
- new_time = time_add_month (time_month_begin (gcal->current_display), 1 * direction);
+ new_time = time_add_month (current_time, direction);
else if (cp == gcal->year_view_sw)
- new_time = time_add_year (time_year_begin (gcal->current_display), 1 * direction);
+ new_time = time_add_year (current_time, direction);
else {
g_warning ("Weee! Where did the penguin go?");
g_assert_not_reached ();
@@ -190,7 +296,7 @@ gnome_calendar_dayjump (GnomeCalendar *gcal, time_t time)
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
- gtk_notebook_set_page (GTK_NOTEBOOK (gcal->notebook), 0);
+ gnome_calendar_set_view (gcal, "dayview");
gnome_calendar_goto (gcal, time);
}
@@ -203,37 +309,65 @@ gnome_calendar_goto_today (GnomeCalendar *gcal)
gnome_calendar_goto (gcal, time (NULL));
}
+
+/* This sets which view is currently shown. It also updates the selection time
+ of the view so it shows the appropriate days. */
void
gnome_calendar_set_view (GnomeCalendar *gcal, char *page_name)
{
- int page = 0;
+ GtkWidget *page;
+ int main_page = 0, sub_page = -1;
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
g_return_if_fail (page_name != NULL);
+ if (strcmp (page_name, "dayview") == 0) {
+ page = gcal->day_view;
+ sub_page = 0;
+ } else if (strcmp (page_name, "workweekview") == 0) {
+ page = gcal->work_week_view;
+ sub_page = 1;
+ } else if (strcmp (page_name, "weekview") == 0) {
+ page = gcal->week_view;
+ sub_page = 2;
+ } else if (strcmp (page_name, "monthview") == 0) {
+ page = gcal->month_view;
+ main_page = 1;
+ } else if (strcmp (page_name, "yearview") == 0) {
+ page = gcal->year_view_sw;
+ main_page = 2;
+ } else {
+ g_warning ("Unknown calendar view: %s", page_name);
+ return;
+ }
- if (strcmp (page_name, "dayview") == 0)
- page = 0;
- else if (strcmp (page_name, "weekview") == 0)
- page = 1;
- else if (strcmp (page_name, "monthview") == 0)
- page = 2;
- else if (strcmp (page_name, "yearview") == 0)
- page = 3;
- gtk_notebook_set_page (GTK_NOTEBOOK (gcal->notebook), page);
+ gnome_calendar_update_view_times (gcal, page);
+
+ if (sub_page != -1)
+ gtk_notebook_set_page (GTK_NOTEBOOK (gcal->sub_notebook),
+ sub_page);
+ gtk_notebook_set_page (GTK_NOTEBOOK (gcal->main_notebook), main_page);
+
+ gnome_calendar_update_gtk_calendar (gcal);
}
static void
gnome_calendar_update_all (GnomeCalendar *cal, iCalObject *object, int flags)
{
- gncal_day_panel_update (GNCAL_DAY_PANEL (cal->day_view),
- object, flags);
- gncal_week_view_update (GNCAL_WEEK_VIEW (cal->week_view),
- object, flags);
- month_view_update (MONTH_VIEW (cal->month_view), object, flags);
+ e_day_view_update_event (E_DAY_VIEW (cal->day_view),
+ object, flags);
+ e_day_view_update_event (E_DAY_VIEW (cal->work_week_view),
+ object, flags);
+ e_week_view_update_event (E_WEEK_VIEW (cal->week_view),
+ object, flags);
+ e_week_view_update_event (E_WEEK_VIEW (cal->month_view),
+ object, flags);
year_view_update (YEAR_VIEW (cal->year_view), object, flags);
+
+ gncal_todo_update (GNCAL_TODO (cal->todo), object, flags);
+ gnome_calendar_tag_calendar (cal, cal->gtk_calendar);
}
@@ -268,11 +402,16 @@ gnome_calendar_new (char *title)
retval = gtk_type_new (gnome_calendar_get_type ());
gcal = GNOME_CALENDAR (retval);
- gcal->current_display = time_day_begin (time (NULL));
+
+ gcal->selection_start_time = time_day_begin (time (NULL));
+ gcal->selection_end_time = time_add_day (gcal->selection_start_time,
+ 1);
gcal->client = cal_client_new ();
setup_widgets (gcal);
+ gnome_calendar_set_view (gcal, "dayview");
+
gtk_signal_connect (GTK_OBJECT (gcal->client), "obj_updated",
gnome_calendar_object_updated_cb, gcal);
gtk_signal_connect (GTK_OBJECT (gcal->client), "obj_removed",
@@ -676,34 +815,35 @@ gnome_calendar_tag_calendar (GnomeCalendar *cal, GtkCalendar *gtk_cal)
gtk_calendar_thaw (gtk_cal);
}
+/* This is called when the day begin & end times, the AM/PM flag, or the
+ week_starts_on_monday flags are changed.
+ FIXME: Which of these options do we want the new views to support? */
void
gnome_calendar_time_format_changed (GnomeCalendar *gcal)
{
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
- /* FIXME: the queue resizes will do until we rewrite those views... */
-
- gncal_day_panel_time_format_changed (GNCAL_DAY_PANEL (gcal->day_view));
- gtk_widget_queue_resize (gcal->day_view);
- gncal_week_view_time_format_changed (GNCAL_WEEK_VIEW (gcal->week_view));
- gtk_widget_queue_resize (gcal->week_view);
- month_view_time_format_changed (MONTH_VIEW (gcal->month_view));
year_view_time_format_changed (YEAR_VIEW (gcal->year_view));
+
+ gtk_calendar_display_options (gcal->gtk_calendar,
+ (week_starts_on_monday
+ ? (gcal->gtk_calendar->display_flags
+ | GTK_CALENDAR_WEEK_START_MONDAY)
+ : (gcal->gtk_calendar->display_flags
+ & ~GTK_CALENDAR_WEEK_START_MONDAY)));
}
+/* This is called when any of the color settings are changed.
+ FIXME: Need to update for the new views. */
void
gnome_calendar_colors_changed (GnomeCalendar *gcal)
{
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
- /* FIXME: add day and week view when they are done */
-
- month_view_colors_changed (MONTH_VIEW (gcal->month_view));
- year_view_colors_changed (YEAR_VIEW (gcal->year_view));
todo_style_changed = 1;
- todo_list_properties_changed (GNCAL_DAY_PANEL (gcal->day_view));
+ gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, 0);
}
void
@@ -712,8 +852,95 @@ gnome_calendar_todo_properties_changed (GnomeCalendar *gcal)
g_return_if_fail (gcal != NULL);
g_return_if_fail (GNOME_IS_CALENDAR (gcal));
- /* FIXME: add day and week view when they are done */
-
todo_style_changed = 1;
- todo_list_properties_changed (GNCAL_DAY_PANEL (gcal->day_view));
+ gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, 0);
}
+
+
+void
+gnome_calendar_set_selected_time_range (GnomeCalendar *gcal,
+ time_t start_time,
+ time_t end_time)
+{
+ gcal->selection_start_time = start_time;
+ gcal->selection_end_time = end_time;
+
+ gnome_calendar_update_gtk_calendar (gcal);
+}
+
+
+/* This updates the month shown and the day selected in the calendar, if
+ necessary. */
+static void
+gnome_calendar_update_gtk_calendar (GnomeCalendar *gcal)
+{
+ GDate date;
+ guint current_year, current_month, current_day;
+ guint new_year, new_month, new_day;
+ gboolean set_day = FALSE;
+
+ /* If the GtkCalendar isn't visible, we just return. */
+ if (!GTK_WIDGET_VISIBLE (gcal->gtk_calendar))
+ return;
+
+ gtk_calendar_get_date (gcal->gtk_calendar, &current_year,
+ &current_month, &current_day);
+
+ g_date_clear (&date, 1);
+ g_date_set_time (&date, gcal->selection_start_time);
+ new_year = g_date_year (&date);
+ new_month = g_date_month (&date) - 1;
+ new_day = g_date_day (&date);
+
+ /* Block the "day_selected" signal while we update the calendar. */
+ gtk_signal_handler_block (GTK_OBJECT (gcal->gtk_calendar),
+ gcal->day_selected_id);
+
+ /* If the year & month don't match, update it. */
+ if (new_year != current_year || new_month != current_month) {
+ /* FIXME: GtkCalendar bug workaround. If we select a month
+ which has less days than the currently selected day, it
+ causes a problem next time we set the day. */
+ if (current_day > 28) {
+ gtk_calendar_select_day (gcal->gtk_calendar, 28);
+ set_day = TRUE;
+ }
+ gtk_calendar_select_month (gcal->gtk_calendar, new_month,
+ new_year);
+ }
+
+ /* If the day doesn't match, update it. */
+ if (new_day != current_day || set_day)
+ gtk_calendar_select_day (gcal->gtk_calendar, new_day);
+
+ gtk_signal_handler_unblock (GTK_OBJECT (gcal->gtk_calendar),
+ gcal->day_selected_id);
+}
+
+static void
+gnome_calendar_on_day_selected (GtkCalendar *calendar,
+ GnomeCalendar *gcal)
+{
+ gint y, m, d;
+ struct tm tm;
+
+ gtk_calendar_get_date (calendar, &y, &m, &d);
+
+ tm.tm_year = y - 1900;
+ tm.tm_mon = m;
+ tm.tm_mday = d;
+ tm.tm_hour = 5; /* for daylight savings time fix */
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+
+ gnome_calendar_goto (gcal, mktime (&tm));
+}
+
+
+static void
+gnome_calendar_on_month_changed (GtkCalendar *calendar,
+ GnomeCalendar *gcal)
+{
+ gnome_calendar_tag_calendar (gcal, gcal->gtk_calendar);
+}
+
diff --git a/calendar/gui/gnome-cal.h b/calendar/gui/gnome-cal.h
index 75406e1c71..a512bbc9b4 100644
--- a/calendar/gui/gnome-cal.h
+++ b/calendar/gui/gnome-cal.h
@@ -12,7 +12,7 @@
#include <time.h>
#include <libgnome/gnome-defs.h>
#include <gtk/gtkcalendar.h>
-#include <libgnomeui/gnome-app.h>
+#include <gtk/gtkvbox.h>
#include <cal-client/cal-client.h>
#include <bonobo.h>
@@ -25,24 +25,37 @@ BEGIN_GNOME_DECLS
#define GNOME_IS_CALENDAR(obj) GTK_CHECK_TYPE(obj, gnome_calendar_get_type())
typedef struct {
- GnomeApp gnome_app;
+ GtkVBox vbox;
+
CalClient *client;
- time_t current_display;
BonoboPropertyBag *properties;
BonoboControl *control;
- GtkWidget *notebook;
+ time_t selection_start_time;
+ time_t selection_end_time;
+
+ GtkWidget *main_notebook;
+ GtkWidget *sub_notebook;
+ GtkWidget *hpane;
+ GtkCalendar *gtk_calendar;
+ GtkWidget *todo;
+
GtkWidget *day_view;
+ GtkWidget *work_week_view;
GtkWidget *week_view;
GtkWidget *month_view;
GtkWidget *year_view;
GtkWidget *year_view_sw;
+
void *event_editor;
+
+ /* The signal handler id for our GtkCalendar "day_selected" handler. */
+ guint day_selected_id;
} GnomeCalendar;
typedef struct {
- GnomeAppClass parent_class;
+ GtkVBoxClass parent_class;
} GnomeCalendarClass;
@@ -78,6 +91,10 @@ char *gnome_calendar_get_current_view_name (GnomeCalendar *gcal);
void gnome_calendar_set_view (GnomeCalendar *gcal,
char *page_name);
+void gnome_calendar_set_selected_time_range (GnomeCalendar *gcal,
+ time_t start_time,
+ time_t end_time);
+
/* Flags is a bitmask of CalObjectChange values */
void gnome_calendar_object_changed (GnomeCalendar *gcal,
iCalObject *obj,
diff --git a/calendar/gui/goto.c b/calendar/gui/goto.c
index 1e7bc474ba..d6c69016a1 100644
--- a/calendar/gui/goto.c
+++ b/calendar/gui/goto.c
@@ -120,7 +120,7 @@ create_months (int month)
struct tm tm;
char buf[100];
- tm = *localtime (&gnome_calendar->current_display);
+ tm = *localtime (&gnome_calendar->selection_start_time);
table = gtk_table_new (2, 6, TRUE);
@@ -259,7 +259,7 @@ goto_dialog (GnomeCalendar *gcal)
gnome_calendar = gcal;
current_index = -1;
- tm = *localtime (&gnome_calendar->current_display);
+ tm = *localtime (&gnome_calendar->selection_start_time);
goto_win = gnome_dialog_new (_("Go to date"),
GNOME_STOCK_BUTTON_CANCEL,
diff --git a/calendar/gui/monthview.xpm b/calendar/gui/monthview.xpm
new file mode 100644
index 0000000000..1a1b1d936a
--- /dev/null
+++ b/calendar/gui/monthview.xpm
@@ -0,0 +1,46 @@
+/* XPM */
+static char * monthview_xpm[] = {
+"24 24 19 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #D1D1D1",
+"# c #BABBCC",
+"$ c #AAAFE2",
+"% c #8B90C3",
+"& c #E3E4F5",
+"* c #A3A6C7",
+"= c #5D66BA",
+"- c #3945BB",
+"; c #555FC5",
+"> c #2E3BB1",
+", c #727ACE",
+"' c #C7CAEB",
+") c #0010A8",
+"! c #4550B5",
+"~ c #1725AC",
+"{ c #8E95D8",
+" ",
+" ",
+" ",
+" ",
+" ...................... ",
+" .............+.++.++.. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" .@@@@@@@@@@@@@@@@@@@@. ",
+" .++@++#$$%&+@+$*++@++. ",
+" .++@+&=-;>,+@')!++@++. ",
+" .@@@@%)%@%)@*~)!@@@@@. ",
+" .++@++@'$=-+@+)!++@++. ",
+" .++@++@{;>,+@+)!++@++. ",
+" .@@@@@@@@%)@@@)!@@@@@. ",
+" .++@+$)$+%)+@+)!++@++. ",
+" .++@+'>,$=-+#$)>$+@++. ",
+" .@@@@@#%%%#@#%%%%@@@@. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" ...................... ",
+" ",
+" ",
+" "};
diff --git a/calendar/gui/quick-view.h b/calendar/gui/quick-view.h
index c6b2cf8814..4cb19c63fd 100644
--- a/calendar/gui/quick-view.h
+++ b/calendar/gui/quick-view.h
@@ -9,6 +9,7 @@
#define QUICK_VIEW_H
#include <libgnome/gnome-defs.h>
+#include <gtk/gtkwindow.h>
#include "gnome-cal.h"
diff --git a/calendar/gui/weekview.xpm b/calendar/gui/weekview.xpm
new file mode 100644
index 0000000000..f4900856eb
--- /dev/null
+++ b/calendar/gui/weekview.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char * weekview_xpm[] = {
+"24 24 14 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #D1D1D1",
+"# c #E3E4F5",
+"$ c #8B90C3",
+"% c #AAAFE2",
+"& c #C7CAEB",
+"* c #4550B5",
+"= c #555FC5",
+"- c #2E3BB1",
+"; c #0010A8",
+"> c #BABBCC",
+", c #A3A6C7",
+" ",
+" ",
+" ",
+" ",
+" ...................... ",
+" .............+.++.++.. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" .@@@@@@@@@@@@@@@@@@@@. ",
+" .++@++@+#$%%$%+@++@++. ",
+" .++@++@+&*==-;+@++@++. ",
+" .@@@@@@@@@@@$;@@@@@@@. ",
+" .++@++@++@+%;%+@++@++. ",
+" .++@++@++@+%;%+@++@++. ",
+" .@@@@@@@@@@-->@@@@@@@. ",
+" .++@++@++@+;*++@++@++. ",
+" .++@++@++@+;*++@++@++. ",
+" .@@@@@@@@@@$,@@@@@@@@. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" ...................... ",
+" ",
+" ",
+" "};
diff --git a/calendar/gui/workweekview.xpm b/calendar/gui/workweekview.xpm
new file mode 100644
index 0000000000..f47061589c
--- /dev/null
+++ b/calendar/gui/workweekview.xpm
@@ -0,0 +1,43 @@
+/* XPM */
+static char * workweekview_xpm[] = {
+"24 24 16 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #D1D1D1",
+"# c #E3E4F5",
+"$ c #8B90C3",
+"% c #AAAFE2",
+"& c #0010A8",
+"* c #3945BB",
+"= c #555FC5",
+"- c #4550B5",
+"; c #727ACE",
+"> c #C7CAEB",
+", c #2E3BB1",
+"' c #5D66BA",
+") c #BABBCC",
+" ",
+" ",
+" ",
+" ",
+" ...................... ",
+" .............+.++.++.. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" .@@@@@@@@@@@@@@@@@@@@. ",
+" .++@++@+#$%%$%+@++@++. ",
+" .++@++@+%&*=-=+@++@++. ",
+" .@@@@@@@$&$@@@@@@@@@@. ",
+" .++@++@+%&;%$#+@++@++. ",
+" .++@++@+>-==,;+@++@++. ",
+" .@@@@@@@@@@@$&@@@@@@@. ",
+" .++@++@+#$#+$&+@++@++. ",
+" .++@++@+>,;%'*+@++@++. ",
+" .@@@@@@@@)$$$)@@@@@@@. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" ...................... ",
+" ",
+" ",
+" "};
diff --git a/calendar/gui/yearview.xpm b/calendar/gui/yearview.xpm
new file mode 100644
index 0000000000..9629150417
--- /dev/null
+++ b/calendar/gui/yearview.xpm
@@ -0,0 +1,45 @@
+/* XPM */
+static char * yearview_xpm[] = {
+"24 24 18 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #D1D1D1",
+"# c #BABBCC",
+"$ c #AAAFE2",
+"% c #8B90C3",
+"& c #E3E4F5",
+"* c #C7CAEB",
+"= c #5D66BA",
+"- c #3945BB",
+"; c #555FC5",
+"> c #2E3BB1",
+", c #727ACE",
+"' c #4550B5",
+") c #0010A8",
+"! c #A3A6C7",
+"~ c #8E95D8",
+" ",
+" ",
+" ",
+" ",
+" ...................... ",
+" .............+.++.++.. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" .@@@@@@@@@@@@@@@@@@@@. ",
+" .++#$$%&+@*$%&&%$$%$+. ",
+" .+&=-;>,+#-;'*$)-;';+. ",
+" .@%)%@%)!>=@@@%)%@@@@. ",
+" .++@*$=-$),$%&$),$%&+. ",
+" .++@~;>,$)-;>,*';;>,+. ",
+" .@@@@@%)%)%@%)@@@@%)@. ",
+" .+$)$+%)$)$+%)&%&+%)+. ",
+" .+*>,$=-*>,$=-*>,$=-+. ",
+" .@@#%%%#@#%%%#@#%%%#@. ",
+" .++@++@++@++@++@++@++. ",
+" .++@++@++@++@++@++@++. ",
+" ...................... ",
+" ",
+" ",
+" "};