diff options
Diffstat (limited to 'calendar/gui')
-rw-r--r-- | calendar/gui/calendar-commands.c | 2 | ||||
-rw-r--r-- | calendar/gui/e-day-view-main-item.c | 3 | ||||
-rw-r--r-- | calendar/gui/e-day-view-top-item.c | 3 | ||||
-rw-r--r-- | calendar/gui/e-day-view.c | 302 | ||||
-rw-r--r-- | calendar/gui/e-day-view.h | 20 | ||||
-rw-r--r-- | calendar/gui/e-week-view-event-item.c | 16 | ||||
-rw-r--r-- | calendar/gui/e-week-view-main-item.c | 3 | ||||
-rw-r--r-- | calendar/gui/e-week-view.c | 531 | ||||
-rw-r--r-- | calendar/gui/e-week-view.h | 29 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.c | 75 |
10 files changed, 812 insertions, 172 deletions
diff --git a/calendar/gui/calendar-commands.c b/calendar/gui/calendar-commands.c index 254030c675..e3ae6a4bc6 100644 --- a/calendar/gui/calendar-commands.c +++ b/calendar/gui/calendar-commands.c @@ -805,6 +805,8 @@ calendar_iterate (GnomeCalendar *cal, break; } + /* FIXME: add g_free (obj_string) ? */ + g_free (l->data); } g_list_free (uids); diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c index 1abff57c97..2ce8f9201f 100644 --- a/calendar/gui/e-day-view-main-item.c +++ b/calendar/gui/e-day-view-main-item.c @@ -228,7 +228,8 @@ e_day_view_main_item_draw (GnomeCanvasItem *canvas_item, GdkDrawable *drawable, work_day_w, height - work_day_end_y); /* Paint the selection background. */ - if (day_view->selection_start_col != -1 + if (GTK_WIDGET_HAS_FOCUS (day_view) + && day_view->selection_start_col != -1 && !day_view->selection_in_top_canvas) { for (day = day_view->selection_start_col; day <= day_view->selection_end_col; diff --git a/calendar/gui/e-day-view-top-item.c b/calendar/gui/e-day-view-top-item.c index 6d5ea60854..ac2d6bfb62 100644 --- a/calendar/gui/e-day-view-top-item.c +++ b/calendar/gui/e-day-view-top-item.c @@ -229,7 +229,8 @@ e_day_view_top_item_draw (GnomeCanvasItem *canvas_item, item_height - 3); /* Draw the selection background. */ - if (day_view->selection_start_col != -1) { + if (GTK_WIDGET_HAS_FOCUS (day_view) + && day_view->selection_start_col != -1) { gint start_col, end_col, rect_x, rect_y, rect_w, rect_h; start_col = day_view->selection_start_col; diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index 65151e49a6..25b026407e 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -187,11 +187,21 @@ static gboolean e_day_view_find_event_from_item (EDayView *day_view, GnomeCanvasItem *item, gint *day_return, gint *event_num_return); -static gboolean e_day_view_find_event_from_ico (EDayView *day_view, - iCalObject *ico, +static gboolean e_day_view_find_event_from_uid (EDayView *day_view, + const gchar *uid, gint *day_return, gint *event_num_return); +typedef gboolean (* EDayViewForeachEventCallback) (EDayView *day_view, + gint day, + gint event_num, + gpointer data); + +static void e_day_view_foreach_event_with_uid (EDayView *day_view, + const gchar *uid, + EDayViewForeachEventCallback callback, + gpointer data); + static void e_day_view_reload_events (EDayView *day_view); static void e_day_view_free_events (EDayView *day_view); static void e_day_view_free_event_array (EDayView *day_view, @@ -338,6 +348,14 @@ static void e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget guint info, guint time, EDayView *day_view); +static gboolean e_day_view_update_event_cb (EDayView *day_view, + gint day, + gint event_num, + gpointer data); +static gboolean e_day_view_remove_event_cb (EDayView *day_view, + gint day, + gint event_num, + gpointer data); static GtkTableClass *parent_class; @@ -449,6 +467,7 @@ e_day_view_init (EDayView *day_view) day_view->editing_event_day = -1; day_view->editing_event_num = -1; + day_view->editing_new_event = FALSE; day_view->resize_bars_event_day = -1; day_view->resize_bars_event_num = -1; @@ -1012,12 +1031,18 @@ e_day_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation) static gint e_day_view_focus_in (GtkWidget *widget, GdkEventFocus *event) { + EDayView *day_view; + g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (E_IS_DAY_VIEW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); + day_view = E_DAY_VIEW (widget); + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); - gtk_widget_draw_focus (widget); + + gtk_widget_queue_draw (day_view->top_canvas); + gtk_widget_queue_draw (day_view->main_canvas); return FALSE; } @@ -1036,9 +1061,6 @@ e_day_view_focus_out (GtkWidget *widget, GdkEventFocus *event) GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); - /* Get rid of selection. */ - day_view->selection_start_col = -1; - gtk_widget_queue_draw (day_view->top_canvas); gtk_widget_queue_draw (day_view->main_canvas); @@ -1058,11 +1080,23 @@ e_day_view_set_calendar (EDayView *day_view, } +/* This reloads all calendar events. */ void -e_day_view_update_event (EDayView *day_view, - iCalObject *ico, - int flags) +e_day_view_update_all_events (EDayView *day_view) { + e_day_view_reload_events (day_view); +} + + +/* This is called when one event has been added or updated. */ +void +e_day_view_update_event (EDayView *day_view, + const gchar *uid) +{ + EDayViewEvent *event; + gchar *obj_string; + iCalObject *ico; + CalObjFindStatus status; gint day, event_num; g_return_if_fail (E_IS_DAY_VIEW (day_view)); @@ -1071,48 +1105,199 @@ e_day_view_update_event (EDayView *day_view, 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) + /* If our calendar or time hasn't been set yet, just return. */ + if (!day_view->calendar + || (day_view->lower == 0 && day_view->upper == 0)) return; + /* Get the event from the server. */ + obj_string = cal_client_get_object (day_view->calendar->client, uid); + status = ical_object_find_in_string (uid, obj_string, &ico); + + switch (status) { + case CAL_OBJ_FIND_SUCCESS: + /* Fall through. */ + break; + case CAL_OBJ_FIND_SYNTAX_ERROR: + g_warning ("syntax error uid=%s\n", uid); + return; + case CAL_OBJ_FIND_NOT_FOUND: + g_warning ("obj not found uid=%s\n", uid); + return; + } + /* We only care about events. */ if (ico && ico->type != ICAL_EVENT) return; - /* If one non-recurring event was added, we can just add it. */ - if (flags == CHANGE_NEW && ico && !ico->recur) { - if (ico->dtstart < day_view->upper - && ico->dtend > day_view->lower) { - e_day_view_add_event (ico, ico->dtstart, ico->dtend, - day_view); - e_day_view_check_layout (day_view); + /* If the event already exists and the dates didn't change, we can + update the event fairly easily without changing the events arrays + or computing a new layout. */ + if (e_day_view_find_event_from_uid (day_view, uid, &day, &event_num)) { + if (day == E_DAY_VIEW_LONG_EVENT) + event = &g_array_index (day_view->long_events, + EDayViewEvent, event_num); + else + event = &g_array_index (day_view->events[day], + EDayViewEvent, event_num); + if (ical_object_compare_dates (event->ico, ico)) { + e_day_view_foreach_event_with_uid (day_view, uid, e_day_view_update_event_cb, ico); gtk_widget_queue_draw (day_view->top_canvas); gtk_widget_queue_draw (day_view->main_canvas); + return; } - return; - /* If only the summary changed, we can update that easily. */ - } else if (!(flags & ~CHANGE_SUMMARY)) { - if (e_day_view_find_event_from_ico (day_view, ico, - &day, &event_num)) { - if (day == E_DAY_VIEW_LONG_EVENT) { - e_day_view_update_long_event_label (day_view, - event_num); - /* For long events we also have to reshape it - as the text is centered. */ - e_day_view_reshape_long_event (day_view, - event_num); - } else { - e_day_view_update_event_label (day_view, day, - event_num); + /* The dates have changed, so we need to remove the + old occurrrences before adding the new ones. */ + e_day_view_foreach_event_with_uid (day_view, uid, + e_day_view_remove_event_cb, + NULL); + } + + /* Add the occurrences of the event. */ + ical_object_generate_events (ico, day_view->lower, day_view->upper, + e_day_view_add_event, day_view); + + e_day_view_check_layout (day_view); + + gtk_widget_queue_draw (day_view->top_canvas); + gtk_widget_queue_draw (day_view->main_canvas); +} + + +static gboolean +e_day_view_update_event_cb (EDayView *day_view, + gint day, + gint event_num, + gpointer data) +{ + EDayViewEvent *event; + iCalObject *ico; + + ico = data; + + /* FIXME: When do ico's get freed? */ + if (day == E_DAY_VIEW_LONG_EVENT) { + event = &g_array_index (day_view->long_events, EDayViewEvent, + event_num); + event->ico = ico; + } else { + event = &g_array_index (day_view->events[day], EDayViewEvent, + event_num); + event->ico = ico; + } + + /* If we are editing an event which we have just created, we will get + an update_event callback from the server. But we need to ignore it + or we will lose the text the user has already typed in. */ + if (day_view->editing_new_event + && day_view->editing_event_day == day + && day_view->editing_event_num == event_num) { + return TRUE; + } + + if (day == E_DAY_VIEW_LONG_EVENT) { + e_day_view_update_long_event_label (day_view, event_num); + e_day_view_reshape_long_event (day_view, event_num); + } else { + e_day_view_update_event_label (day_view, day, event_num); + e_day_view_reshape_day_event (day_view, day, event_num); + } + return TRUE; +} + + +/* This calls a given function for each event instance that matches the given + uid. Note that it is safe for the callback to remove the event (since we + step backwards through the arrays). */ +static void +e_day_view_foreach_event_with_uid (EDayView *day_view, + const gchar *uid, + EDayViewForeachEventCallback callback, + gpointer data) +{ + EDayViewEvent *event; + gint day, event_num; + + for (day = 0; day < day_view->days_shown; day++) { + for (event_num = day_view->events[day]->len - 1; + event_num >= 0; + event_num--) { + event = &g_array_index (day_view->events[day], + EDayViewEvent, event_num); + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { + if (!(*callback) (day_view, day, event_num, + data)) + return; } + } + } - return; + for (event_num = day_view->long_events->len - 1; + event_num >= 0; + event_num--) { + event = &g_array_index (day_view->long_events, + EDayViewEvent, event_num); + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { + if (!(*callback) (day_view, day, event_num, data)) + return; } } +} - e_day_view_reload_events (day_view); + +/* This removes all the events associated with the given uid. Note that for + recurring events there may be more than one. If any events are found and + removed we need to layout the events again. */ +void +e_day_view_remove_event (EDayView *day_view, + const gchar *uid) +{ + g_return_if_fail (E_IS_DAY_VIEW (day_view)); + + e_day_view_foreach_event_with_uid (day_view, uid, + e_day_view_remove_event_cb, NULL); + + e_day_view_check_layout (day_view); + gtk_widget_queue_draw (day_view->top_canvas); + gtk_widget_queue_draw (day_view->main_canvas); +} + + +static gboolean +e_day_view_remove_event_cb (EDayView *day_view, + gint day, + gint event_num, + gpointer data) +{ + EDayViewEvent *event; + + if (day == E_DAY_VIEW_LONG_EVENT) + event = &g_array_index (day_view->long_events, + EDayViewEvent, event_num); + else + event = &g_array_index (day_view->events[day], + EDayViewEvent, event_num); + + /* We set the event's uid to NULL so we don't try to update it in + on_editing_stopped(). */ + g_free (event->ico->uid); + event->ico->uid = NULL; + + if (event->canvas_item) + gtk_object_destroy (GTK_OBJECT (event->canvas_item)); + + if (day == E_DAY_VIEW_LONG_EVENT) { + g_array_remove_index (day_view->long_events, event_num); + day_view->long_events_need_layout = TRUE; + } else { + g_array_remove_index (day_view->events[day], event_num); + day_view->need_layout[day] = TRUE; + } + return TRUE; } @@ -1221,8 +1406,8 @@ e_day_view_find_event_from_item (EDayView *day_view, If is is a long event, E_DAY_VIEW_LONG_EVENT is returned as the day. Returns TRUE if the event was found. */ static gboolean -e_day_view_find_event_from_ico (EDayView *day_view, - iCalObject *ico, +e_day_view_find_event_from_uid (EDayView *day_view, + const gchar *uid, gint *day_return, gint *event_num_return) { @@ -1234,10 +1419,8 @@ e_day_view_find_event_from_ico (EDayView *day_view, event_num++) { event = &g_array_index (day_view->events[day], EDayViewEvent, event_num); - //if (event->ico == ico) { - if (ico && ico->uid && - event && event->ico && event->ico->uid && - (strcmp (ico->uid, event->ico->uid) == 0)) { + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { *day_return = day; *event_num_return = event_num; return TRUE; @@ -1249,7 +1432,8 @@ e_day_view_find_event_from_ico (EDayView *day_view, event_num++) { event = &g_array_index (day_view->long_events, EDayViewEvent, event_num); - if (event->ico == ico) { + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { *day_return = E_DAY_VIEW_LONG_EVENT; *event_num_return = event_num; return TRUE; @@ -2015,7 +2199,8 @@ e_day_view_on_event_right_click (EDayView *day_view, { N_("New appointment..."), (GtkSignalFunc) e_day_view_on_new_appointment, NULL, TRUE } }; - have_selection = (day_view->selection_start_col != -1); + have_selection = GTK_WIDGET_HAS_FOCUS (day_view) + && day_view->selection_start_col != -1; if (event_num == -1) { items = 1; @@ -2648,7 +2833,10 @@ e_day_view_reload_events (EDayView *day_view) day_view->pressed_event_day = -1; day_view->drag_event_day = -1; - if (day_view->calendar) { + /* If both lower & upper are 0, then the time range hasn't been set, + so we don't try to load any events. */ + if (day_view->calendar + && (day_view->lower != 0 || day_view->upper != 0)) { calendar_iterate (day_view->calendar, day_view->lower, day_view->upper, @@ -3512,17 +3700,26 @@ e_day_view_key_press (GtkWidget *widget, GdkEventKey *event) e_day_view_get_selection_range (day_view, &ico->dtstart, &ico->dtend); - gnome_calendar_add_object (day_view->calendar, ico); + /* We add the event locally and start editing it. When we get the + "update_event" callback from the server, we basically ignore it. + If we were to wait for the "update_event" callback it wouldn't be + as responsive and we may lose a few keystrokes. */ + e_day_view_add_event (ico, ico->dtstart, ico->dtend, day_view); + e_day_view_check_layout (day_view); + gtk_widget_queue_draw (day_view->top_canvas); + gtk_widget_queue_draw (day_view->main_canvas); - /* gnome_calendar_add_object() should have resulted in a call to - e_day_view_update_event(), so the new event should now be layed out. - So we try to find it so we can start editing it. */ - if (e_day_view_find_event_from_ico (day_view, ico, &day, &event_num)) { - /* Start editing the new event. */ + if (e_day_view_find_event_from_uid (day_view, ico->uid, + &day, &event_num)) { e_day_view_start_editing_event (day_view, day, event_num, initial_text); + day_view->editing_new_event = TRUE; + } else { + g_warning ("Couldn't find event to start editing.\n"); } + gnome_calendar_add_object (day_view->calendar, ico); + return TRUE; } @@ -3702,10 +3899,15 @@ e_day_view_on_editing_stopped (EDayView *day_view, /* Reset the edit fields. */ day_view->editing_event_day = -1; day_view->editing_event_num = -1; + day_view->editing_new_event = FALSE; day_view->resize_bars_event_day = -1; day_view->resize_bars_event_num = -1; + /* Check that the event is still valid. */ + if (!event->ico->uid) + return; + gtk_object_get (GTK_OBJECT (event->canvas_item), "text", &text, NULL); diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h index ac967f030c..ea49fff0ee 100644 --- a/calendar/gui/e-day-view.h +++ b/calendar/gui/e-day-view.h @@ -314,6 +314,11 @@ struct _EDayView gint editing_event_day; gint editing_event_num; + /* This is TRUE if we are editing an event which we have just created. + We ignore the "update_event" callback which we will get from the + server when the event is added. */ + gboolean editing_new_event; + /* This is a GnomeCanvasRect which is placed around an item while it is being resized, so we can raise it above all other EText items. */ GnomeCanvasItem *resize_long_event_rect_item; @@ -414,15 +419,18 @@ void e_day_view_set_selected_time_range (EDayView *day_view, time_t start_time, time_t end_time); +/* This reloads all calendar events. */ +void e_day_view_update_all_events (EDayView *day_view); -/* 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. */ +/* This is called when one event has been added or updated. */ void e_day_view_update_event (EDayView *day_view, - iCalObject *ico, - int flags); + const gchar *uid); +/* This removes all the events associated with the given uid. Note that for + recurring events there may be more than one. If any events are found and + removed we need to layout the events again. */ +void e_day_view_remove_event (EDayView *day_view, + const gchar *uid); /* The number of days shown in the EDayView, from 1 to 7. This is normally either 1 or 5 (for the Work-Week view). */ diff --git a/calendar/gui/e-week-view-event-item.c b/calendar/gui/e-week-view-event-item.c index e22fd944bf..3bacdc68f9 100644 --- a/calendar/gui/e-week-view-event-item.c +++ b/calendar/gui/e-week-view-event-item.c @@ -637,18 +637,20 @@ e_week_view_event_item_button_press (EWeekViewEventItem *wveitem, span = &g_array_index (week_view->spans, EWeekViewEventSpan, event->spans_index + wveitem->span_num); -#if 0 +#if 1 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); + if (pos == E_WEEK_VIEW_POS_NONE) + return FALSE; /* Ignore clicks on the event while editing. */ - if (pos == E_WEEK_VIEW_POS_EVENT && E_TEXT (span->text_item)->editing) + if (E_TEXT (span->text_item)->editing) return FALSE; - if (pos == E_WEEK_VIEW_POS_EVENT) { + if (bevent->button.button == 1) { /* 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; @@ -659,9 +661,15 @@ e_week_view_event_item_button_press (EWeekViewEventItem *wveitem, /* FIXME: Remember the day offset from the start of the event. */ + } else if (bevent->button.button == 3) { + e_week_view_show_popup_menu (week_view, + (GdkEventButton*) bevent, + wveitem->event_num); + gtk_signal_emit_stop_by_name (GTK_OBJECT (item->canvas), + "button_press_event"); } - return FALSE; + return TRUE; } diff --git a/calendar/gui/e-week-view-main-item.c b/calendar/gui/e-week-view-main-item.c index 7101cbef46..49c2ca3d7d 100644 --- a/calendar/gui/e-week-view-main-item.c +++ b/calendar/gui/e-week-view-main-item.c @@ -277,7 +277,8 @@ e_week_view_main_item_draw_day (EWeekViewMainItem *wvmitem, /* If the day is selected, draw the blue background. */ selected = TRUE; - if (week_view->selection_start_day == -1 + if (!GTK_WIDGET_HAS_FOCUS (week_view) + || week_view->selection_start_day == -1 || week_view->selection_start_day > day || week_view->selection_end_day < day) selected = FALSE; diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c index 51a292041e..7f6e1c0c9c 100644 --- a/calendar/gui/e-week-view.c +++ b/calendar/gui/e-week-view.c @@ -125,18 +125,38 @@ 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, +static gboolean e_week_view_find_event_from_uid (EWeekView *week_view, + const gchar *uid, gint *event_num_return); +typedef gboolean (* EWeekViewForeachEventCallback) (EWeekView *week_view, + gint event_num, + gpointer data); + +static void e_week_view_foreach_event_with_uid (EWeekView *week_view, + const gchar *uid, + EWeekViewForeachEventCallback callback, + gpointer data); 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 void e_week_view_on_edit_appointment (GtkWidget *widget, + gpointer data); +static void e_week_view_on_delete_occurance (GtkWidget *widget, + gpointer data); +static void e_week_view_on_delete_appointment (GtkWidget *widget, + gpointer data); +static void e_week_view_on_unrecur_appointment (GtkWidget *widget, + gpointer data); + +static gboolean e_week_view_update_event_cb (EWeekView *week_view, + gint event_num, + gpointer data); +static gboolean e_week_view_remove_event_cb (EWeekView *week_view, + gint event_num, + gpointer data); static GtkTableClass *parent_class; @@ -230,6 +250,7 @@ e_week_view_init (EWeekView *week_view) week_view->pressed_event_num = -1; week_view->editing_event_num = -1; + week_view->editing_new_event = FALSE; /* Create the small font. */ week_view->use_small_font = TRUE; @@ -620,12 +641,19 @@ e_week_view_recalc_cell_sizes (EWeekView *week_view) static gint e_week_view_focus_in (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); + g_print ("In e_week_view_focus_in\n"); + + week_view = E_WEEK_VIEW (widget); + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); - gtk_widget_draw_focus (widget); + + gtk_widget_queue_draw (week_view->main_canvas); return FALSE; } @@ -640,13 +668,12 @@ e_week_view_focus_out (GtkWidget *widget, GdkEventFocus *event) g_return_val_if_fail (E_IS_WEEK_VIEW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); + g_print ("In e_week_view_focus_out\n"); + 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; @@ -905,69 +932,212 @@ e_week_view_set_compress_weekend (EWeekView *week_view, } +/* This reloads all calendar events. */ +void +e_week_view_update_all_events (EWeekView *week_view) +{ + e_week_view_reload_events (week_view); +} + + +/* This is called when one event has been added or updated. */ void -e_week_view_update_event (EWeekView *week_view, - iCalObject *ico, - int flags) +e_week_view_update_event (EWeekView *week_view, + const gchar *uid) { EWeekViewEvent *event; - EWeekViewEventSpan *span; - gint event_num, num_days, span_num; - gboolean one_day_event; + gint event_num, num_days; + gchar *obj_string; + iCalObject *ico; + CalObjFindStatus status; g_return_if_fail (E_IS_WEEK_VIEW (week_view)); +#if 0 + g_print ("In e_week_view_update_event\n"); +#endif + + /* If we don't have a calendar or valid date set yet, just return. */ + if (!week_view->calendar + || !g_date_valid (&week_view->first_day_shown)) + return; + + /* Get the event from the server. */ + obj_string = cal_client_get_object (week_view->calendar->client, uid); + status = ical_object_find_in_string (uid, obj_string, &ico); + + switch (status) { + case CAL_OBJ_FIND_SUCCESS: + /* Fall through. */ + break; + case CAL_OBJ_FIND_SYNTAX_ERROR: + g_warning ("syntax error uid=%s\n", uid); + return; + case CAL_OBJ_FIND_NOT_FOUND: + g_warning ("obj not found uid=%s\n", uid); + return; + } + /* 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 the event already exists and the dates didn't change, we can + update the event fairly easily without changing the events arrays + or computing a new layout. */ + if (e_week_view_find_event_from_uid (week_view, uid, &event_num)) { + event = &g_array_index (week_view->events, EWeekViewEvent, + event_num); - /* If one non-recurring event was added, we can just add it. */ - if (flags == CHANGE_NEW && ico && !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); + if (ical_object_compare_dates (event->ico, ico)) { + e_week_view_foreach_event_with_uid (week_view, uid, e_week_view_update_event_cb, ico); gtk_widget_queue_draw (week_view->main_canvas); + return; } - 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); - } - } + /* The dates have changed, so we need to remove the + old occurrrences before adding the new ones. */ + e_week_view_foreach_event_with_uid (week_view, uid, + e_week_view_remove_event_cb, + NULL); + } - return; + /* Add the occurrences of the event. */ + num_days = week_view->display_month ? E_WEEK_VIEW_MAX_WEEKS * 7 : 7; + ical_object_generate_events (ico, + 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 gboolean +e_week_view_update_event_cb (EWeekView *week_view, + gint event_num, + gpointer data) +{ + EWeekViewEvent *event; + EWeekViewEventSpan *span; + gint span_num; + gchar *text; + iCalObject *ico; + + ico = data; + + event = &g_array_index (week_view->events, EWeekViewEvent, event_num); + /* FIXME: When do ico's get freed? */ + event->ico = ico; + + /* If we are editing an event which we have just created, we will get + an update_event callback from the server. But we need to ignore it + or we will lose the text the user has already typed in. */ + if (week_view->editing_new_event + && week_view->editing_event_num == event_num) { + return TRUE; + } + + 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) { + text = event->ico->summary; + gnome_canvas_item_set (span->text_item, + "text", text ? text : "", + NULL); + + e_week_view_reshape_event_span (week_view, event_num, + span_num); } } - e_week_view_reload_events (week_view); + return TRUE; +} + + +/* This calls a given function for each event instance that matches the given + uid. Note that it is safe for the callback to remove the event (since we + step backwards through the arrays). */ +static void +e_week_view_foreach_event_with_uid (EWeekView *week_view, + const gchar *uid, + EWeekViewForeachEventCallback callback, + gpointer data) +{ + EWeekViewEvent *event; + gint event_num; + + for (event_num = week_view->events->len - 1; + event_num >= 0; + event_num--) { + event = &g_array_index (week_view->events, EWeekViewEvent, + event_num); + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { + if (!(*callback) (week_view, event_num, data)) + return; + } + } +} + + +/* This removes all the events associated with the given uid. Note that for + recurring events there may be more than one. If any events are found and + removed we need to layout the events again. */ +void +e_week_view_remove_event (EWeekView *week_view, + const gchar *uid) +{ + g_return_if_fail (E_IS_WEEK_VIEW (week_view)); + + e_week_view_foreach_event_with_uid (week_view, uid, + e_week_view_remove_event_cb, NULL); + + e_week_view_check_layout (week_view); + gtk_widget_queue_draw (week_view->main_canvas); +} + + +static gboolean +e_week_view_remove_event_cb (EWeekView *week_view, + gint event_num, + gpointer data) +{ + EWeekViewEvent *event; + EWeekViewEventSpan *span; + gint span_num; + + event = &g_array_index (week_view->events, EWeekViewEvent, event_num); + + /* We set the event's uid to NULL so we don't try to update it in + on_editing_stopped(). */ + g_free (event->ico->uid); + event->ico->uid = NULL; + + /* We leave the span elements in the array, but set the canvas item + pointers to NULL. */ + 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) { + gtk_object_destroy (GTK_OBJECT (span->text_item)); + span->text_item = NULL; + } + if (span->background_item) { + gtk_object_destroy (GTK_OBJECT (span->background_item)); + span->background_item = NULL; + } + } + g_array_remove_index (week_view->events, event_num); + week_view->events_need_layout = TRUE; + + return TRUE; } @@ -1152,7 +1322,7 @@ e_week_view_on_button_press (GtkWidget *widget, gtk_widget_queue_draw (week_view->main_canvas); } } else if (event->button == 3) { - e_week_view_show_popup_menu (week_view, event, day); + e_week_view_show_popup_menu (week_view, event, -1); } return FALSE; @@ -1989,19 +2159,74 @@ e_week_view_on_text_item_event (GnomeCanvasItem *item, GdkEvent *event, EWeekView *week_view) { + gint event_num, span_num; + switch (event->type) { case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: + if (!e_week_view_find_event_from_item (week_view, item, + &event_num, &span_num)) + return FALSE; + + if (event->button.button == 3) { + e_week_view_show_popup_menu (week_view, + (GdkEventButton*) event, + event_num); + gtk_signal_emit_stop_by_name (GTK_OBJECT (item->canvas), + "button_press_event"); + return TRUE; + } + /* Only let the EText handle the event while editing. */ - if (!E_TEXT (item)->editing) + if (!E_TEXT (item)->editing) { + gtk_signal_emit_stop_by_name (GTK_OBJECT (item), + "event"); + + + week_view->pressed_event_num = event_num; + week_view->pressed_span_num = span_num; + + if (event) { + week_view->drag_event_x = event->button.x; + week_view->drag_event_y = event->button.y; + } else + g_warning ("No GdkEvent"); + + /* FIXME: Remember the day offset from the start of + the event. */ + } + break; + case GDK_BUTTON_RELEASE: + if (!E_TEXT (item)->editing) { gtk_signal_emit_stop_by_name (GTK_OBJECT (item), "event"); + + if (!e_week_view_find_event_from_item (week_view, + item, + &event_num, + &span_num)) + return FALSE; + + if (week_view->pressed_event_num != -1 + && week_view->pressed_event_num == event_num + && week_view->pressed_span_num == span_num) { + e_week_view_start_editing_event (week_view, + event_num, + span_num, + NULL); + week_view->pressed_event_num = -1; + return TRUE; + } + } + week_view->pressed_event_num = -1; break; case GDK_FOCUS_CHANGE: - if (event->focus_change.in) + if (event->focus_change.in) { + g_print ("Item got keyboard focus\n"); e_week_view_on_editing_started (week_view, item); - else + } else { + g_print ("Item lost keyboard focus\n"); e_week_view_on_editing_stopped (week_view, item); + } return FALSE; default: @@ -2059,6 +2284,11 @@ e_week_view_on_editing_stopped (EWeekView *week_view, /* Reset the edit fields. */ week_view->editing_event_num = -1; + week_view->editing_new_event = FALSE; + + /* Check that the event is still valid. */ + if (!event->ico->uid) + return; gtk_object_get (GTK_OBJECT (span->text_item), "text", &text, @@ -2117,8 +2347,8 @@ e_week_view_find_event_from_item (EWeekView *week_view, static gboolean -e_week_view_find_event_from_ico (EWeekView *week_view, - iCalObject *ico, +e_week_view_find_event_from_uid (EWeekView *week_view, + const gchar *uid, gint *event_num_return) { EWeekViewEvent *event; @@ -2128,7 +2358,8 @@ e_week_view_find_event_from_ico (EWeekView *week_view, for (event_num = 0; event_num < num_events; event_num++) { event = &g_array_index (week_view->events, EWeekViewEvent, event_num); - if (event->ico == ico) { + if (event->ico && event->ico->uid + && !strcmp (uid, event->ico->uid)) { *event_num_return = event_num; return TRUE; } @@ -2212,35 +2443,93 @@ e_week_view_key_press (GtkWidget *widget, GdkEventKey *event) 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); + /* We add the event locally and start editing it. When we get the + "update_event" callback from the server, we basically ignore it. + If we were to wait for the "update_event" callback it wouldn't be + as responsive and we may lose a few keystrokes. */ + 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); - /* 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. */ + if (e_week_view_find_event_from_uid (week_view, ico->uid, + &event_num)) { e_week_view_start_editing_event (week_view, event_num, 0, initial_text); + week_view->editing_new_event = TRUE; + } else { + g_warning ("Couldn't find event to start editing.\n"); } + gnome_calendar_add_object (week_view->calendar, ico); + return TRUE; } -static void -e_week_view_show_popup_menu (EWeekView *week_view, - GdkEventButton *event, - gint day) +void +e_week_view_show_popup_menu (EWeekView *week_view, + GdkEventButton *bevent, + gint event_num) { + EWeekViewEvent *event; + int have_selection, not_being_edited, num_items, i; + struct menu_item *context_menu; + static struct menu_item items[] = { { N_("New appointment..."), (GtkSignalFunc) e_week_view_on_new_appointment, NULL, TRUE } }; - items[0].data = week_view; + static struct menu_item child_items[] = { + { N_("Edit this appointment..."), (GtkSignalFunc) e_week_view_on_edit_appointment, NULL, TRUE }, + { N_("Delete this appointment"), (GtkSignalFunc) e_week_view_on_delete_appointment, NULL, TRUE }, + { NULL, NULL, NULL, TRUE }, + { N_("New appointment..."), (GtkSignalFunc) e_week_view_on_new_appointment, NULL, TRUE } + }; + + static struct menu_item recur_child_items[] = { + { N_("Make this appointment movable"), (GtkSignalFunc) e_week_view_on_unrecur_appointment, NULL, TRUE }, + { N_("Edit this appointment..."), (GtkSignalFunc) e_week_view_on_edit_appointment, NULL, TRUE }, + { N_("Delete this occurance"), (GtkSignalFunc) e_week_view_on_delete_occurance, NULL, TRUE }, + { N_("Delete all occurances"), (GtkSignalFunc) e_week_view_on_delete_appointment, NULL, TRUE }, + { NULL, NULL, NULL, TRUE }, + { N_("New appointment..."), (GtkSignalFunc) e_week_view_on_new_appointment, NULL, TRUE } + }; + + have_selection = GTK_WIDGET_HAS_FOCUS (week_view) + && week_view->selection_start_day != -1; + + if (event_num == -1) { + num_items = 1; + context_menu = &items[0]; + context_menu[0].sensitive = have_selection; + } else { + event = &g_array_index (week_view->events, + EWeekViewEvent, event_num); + + /* Check if the event is being edited in the event editor. */ + not_being_edited = (event->ico->user_data == NULL); + + if (event->ico->recur) { + num_items = 6; + context_menu = &recur_child_items[0]; + context_menu[3].sensitive = not_being_edited; + context_menu[5].sensitive = have_selection; + } else { + num_items = 4; + context_menu = &child_items[0]; + context_menu[3].sensitive = have_selection; + } + /* These settings are common for each context sensitive menu */ + context_menu[0].sensitive = not_being_edited; + context_menu[1].sensitive = not_being_edited; + context_menu[2].sensitive = not_being_edited; + } + + for (i = 0; i < num_items; i++) + context_menu[i].data = week_view; - week_view->popup_event_day = day; - week_view->popup_event_num = -1; - popup_menu (items, 1, event); + week_view->popup_event_num = event_num; + popup_menu (context_menu, num_items, bevent); } @@ -2255,11 +2544,97 @@ e_week_view_on_new_appointment (GtkWidget *widget, gpointer 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]; + ico->dtstart = week_view->day_starts[week_view->selection_start_day]; + ico->dtend = week_view->day_starts[week_view->selection_end_day + 1]; event_editor = event_editor_new (week_view->calendar, ico); gtk_widget_show (event_editor); } +static void +e_week_view_on_edit_appointment (GtkWidget *widget, gpointer data) +{ + EWeekView *week_view; + EWeekViewEvent *event; + GtkWidget *event_editor; + + week_view = E_WEEK_VIEW (data); + + if (week_view->popup_event_num == -1) + return; + + event = &g_array_index (week_view->events, EWeekViewEvent, + week_view->popup_event_num); + + event_editor = event_editor_new (week_view->calendar, event->ico); + gtk_widget_show (event_editor); +} + + +static void +e_week_view_on_delete_occurance (GtkWidget *widget, gpointer data) +{ + EWeekView *week_view; + EWeekViewEvent *event; + + week_view = E_WEEK_VIEW (data); + + if (week_view->popup_event_num == -1) + return; + + event = &g_array_index (week_view->events, EWeekViewEvent, + week_view->popup_event_num); + + ical_object_add_exdate (event->ico, event->start); + gnome_calendar_object_changed (week_view->calendar, event->ico, + CHANGE_DATES); +} + + +static void +e_week_view_on_delete_appointment (GtkWidget *widget, gpointer data) +{ + EWeekView *week_view; + EWeekViewEvent *event; + + week_view = E_WEEK_VIEW (data); + + if (week_view->popup_event_num == -1) + return; + + event = &g_array_index (week_view->events, EWeekViewEvent, + week_view->popup_event_num); + + gnome_calendar_remove_object (week_view->calendar, event->ico); +} + + +static void +e_week_view_on_unrecur_appointment (GtkWidget *widget, gpointer data) +{ + EWeekView *week_view; + EWeekViewEvent *event; + iCalObject *ico; + + week_view = E_WEEK_VIEW (data); + + if (week_view->popup_event_num == -1) + return; + + event = &g_array_index (week_view->events, EWeekViewEvent, + week_view->popup_event_num); + + /* New object */ + ico = ical_object_duplicate (event->ico); + g_free (ico->recur); + ico->recur = 0; + ico->dtstart = event->start; + ico->dtend = event->end; + + /* Duplicate, and eliminate the recurrency fields */ + ical_object_add_exdate (event->ico, event->start); + gnome_calendar_object_changed (week_view->calendar, event->ico, + CHANGE_ALL); + gnome_calendar_add_object (week_view->calendar, ico); +} diff --git a/calendar/gui/e-week-view.h b/calendar/gui/e-week-view.h index eac23565c2..c940f736d4 100644 --- a/calendar/gui/e-week-view.h +++ b/calendar/gui/e-week-view.h @@ -279,8 +279,12 @@ struct _EWeekView gint editing_event_num; gint editing_span_num; - /* The day or event that the context menu is for. */ - gint popup_event_day; + /* This is TRUE if we are editing an event which we have just created. + We ignore the "update_event" callback which we will get from the + server when the event is added. */ + gboolean editing_new_event; + + /* The event that the context menu is for. */ gint popup_event_num; /* The last mouse position when dragging, in the entire canvas. */ @@ -318,13 +322,18 @@ 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. */ +/* This reloads all calendar events. */ +void e_week_view_update_all_events (EWeekView *week_view); + +/* This is called when one event has been added or updated. */ void e_week_view_update_event (EWeekView *week_view, - iCalObject *ico, - int flags); + const gchar *uid); + +/* This removes all the events associated with the given uid. Note that for + recurring events there may be more than one. If any events are found and + removed we need to layout the events again. */ +void e_week_view_remove_event (EWeekView *week_view, + const gchar *uid); /* @@ -350,6 +359,10 @@ void e_week_view_start_editing_event (EWeekView *week_view, gchar *initial_text); void e_week_view_stop_editing_event (EWeekView *week_view); +void e_week_view_show_popup_menu (EWeekView *week_view, + GdkEventButton *event, + gint event_num); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 763fd5eac4..cb37240ba4 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -385,20 +385,20 @@ gnome_calendar_set_view (GnomeCalendar *gcal, char *page_name) } +/* This tells all components to reload all calendar objects. */ static void gnome_calendar_update_all (GnomeCalendar *cal, iCalObject *object, int 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); + e_day_view_update_all_events (E_DAY_VIEW (cal->day_view)); + e_day_view_update_all_events (E_DAY_VIEW (cal->work_week_view)); + e_week_view_update_all_events (E_WEEK_VIEW (cal->week_view)); + e_week_view_update_all_events (E_WEEK_VIEW (cal->month_view)); + +#if 0 + year_view_update (YEAR_VIEW (cal->year_view), NULL, TRUE); +#endif + + gncal_todo_update (GNCAL_TODO (cal->todo), NULL, TRUE); gnome_calendar_tag_calendar (cal, cal->gtk_calendar); } @@ -410,7 +410,22 @@ gnome_calendar_object_updated_cb (GtkWidget *cal_client, { printf ("gnome-cal: got object changed_cb, uid='%s'\n", uid?uid:"<NULL>"); - gnome_calendar_update_all (gcal, NULL, CHANGE_NEW); + + /* FIXME: do we really want each view to reload the event itself? + Maybe we should keep track of events globally, maybe with ref + counts. We also need to sort out where they get freed. */ + e_day_view_update_event (E_DAY_VIEW (gcal->day_view), uid); + e_day_view_update_event (E_DAY_VIEW (gcal->work_week_view), uid); + e_week_view_update_event (E_WEEK_VIEW (gcal->week_view), uid); + e_week_view_update_event (E_WEEK_VIEW (gcal->month_view), uid); + + /* FIXME: optimize these? */ +#if 0 + year_view_update (YEAR_VIEW (gcal->year_view), NULL, TRUE); +#endif + + gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, TRUE); + gnome_calendar_tag_calendar (gcal, gcal->gtk_calendar); } @@ -421,7 +436,18 @@ gnome_calendar_object_removed_cb (GtkWidget *cal_client, { printf ("gnome-cal: got object removed _cb, uid='%s'\n", uid?uid:"<NULL>"); - gnome_calendar_update_all (gcal, NULL, CHANGE_ALL); + + e_day_view_remove_event (E_DAY_VIEW (gcal->day_view), uid); + e_day_view_remove_event (E_DAY_VIEW (gcal->work_week_view), uid); + e_week_view_remove_event (E_WEEK_VIEW (gcal->week_view), uid); + e_week_view_remove_event (E_WEEK_VIEW (gcal->month_view), uid); + + /* FIXME: optimize these? */ +#if 0 + year_view_update (YEAR_VIEW (gcal->year_view), NULL, CHANGE_ALL); +#endif + gncal_todo_update (GNCAL_TODO (gcal->todo), NULL, CHANGE_ALL); + gnome_calendar_tag_calendar (gcal, gcal->gtk_calendar); } @@ -794,20 +820,23 @@ static int mark_gtk_calendar_day (iCalObject *obj, time_t start, time_t end, void *c) { GtkCalendar *gtk_cal = c; - struct tm tm_s; - time_t t, day_end; + struct tm tm_s, tm_e; + gint start_day, end_day, day; tm_s = *localtime (&start); - day_end = time_day_end (end); + tm_e = *localtime (&end); - for (t = start; t <= day_end; t += 60*60*24){ - time_t new = mktime (&tm_s); - struct tm tm_day; + start_day = tm_s.tm_mday; + end_day = tm_e.tm_mday; + + /* If the event ends at midnight then really it ends on the previous + day (unless it started at the same time). */ + if (start != end && tm_e.tm_hour == 0 && tm_e.tm_min == 0) + end_day--; + + for (day = start_day; day <= end_day; day++) + gtk_calendar_mark_day (gtk_cal, day); - tm_day = *localtime (&new); - gtk_calendar_mark_day (gtk_cal, tm_day.tm_mday); - tm_s.tm_mday++; - } return TRUE; } |