diff options
Diffstat (limited to 'calendar/gui')
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, ¤t_year, + ¤t_month, ¤t_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", +" ", +" ", +" ", +" ", +" ...................... ", +" .............+.++.++.. ", +" .++@++@++@++@++@++@++. ", +" .++@++@++@++@++@++@++. ", +" .@@@@@@@@@@@@@@@@@@@@. ", +" .++#$$%&+@*$%&&%$$%$+. ", +" .+&=-;>,+#-;'*$)-;';+. ", +" .@%)%@%)!>=@@@%)%@@@@. ", +" .++@*$=-$),$%&$),$%&+. ", +" .++@~;>,$)-;>,*';;>,+. ", +" .@@@@@%)%)%@%)@@@@%)@. ", +" .+$)$+%)$)$+%)&%&+%)+. ", +" .+*>,$=-*>,$=-*>,$=-+. ", +" .@@#%%%#@#%%%#@#%%%#@. ", +" .++@++@++@++@++@++@++. ", +" .++@++@++@++@++@++@++. ", +" ...................... ", +" ", +" ", +" "}; |