From a7d7726a357b837613f2aa7e8749035e5ecf4fff Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 25 Mar 2017 11:19:51 -0300 Subject: [PATCH] year-view: optimize drawing The current Year view drawing routine gathers events every single time we draw the navigator. This is, to the best case, slow to death. In fact, this is why year view's navigator is so memory and processor consuming. To fix that, cache the added components and simply query them at drawing time. --- src/views/gcal-year-view.c | 217 ++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 76 deletions(-) diff --git a/src/views/gcal-year-view.c b/src/views/gcal-year-view.c index feea6575..d374b9fd 100644 --- a/src/views/gcal-year-view.c +++ b/src/views/gcal-year-view.c @@ -107,6 +107,13 @@ struct _GcalYearView /* date property */ icaltimetype *date; + + /* + * Array with the events at every month. Events + * that span multiple months are added multiple + * times to the array. + */ + GPtrArray *events[12]; }; enum { @@ -676,6 +683,45 @@ calculate_day_month_for_coord (GcalYearView *year_view, *out_day = day; return TRUE; } +static guint +count_events_at_day (GcalYearView *self, + icaltimetype *today) +{ + g_autoptr (GDateTime) today_start, today_end; + GPtrArray *events; + guint i, n_events; + + events = self->events[today->month - 1]; + n_events = 0; + today_start = icaltime_to_datetime (today); + today_end = g_date_time_add_days (today_start, 1); + + for (i = 0; i < events->len; i++) + { + g_autoptr (GDateTime) event_start, event_end; + GcalEvent *event; + + event = g_ptr_array_index (events, i); + + event_start = g_date_time_ref (gcal_event_get_date_start (event)); + + if (gcal_event_get_all_day (event)) + event_end = g_date_time_add_days (gcal_event_get_date_end (event), -1); + else + event_end = g_date_time_ref (gcal_event_get_date_end (event)); + + + if (datetime_compare_date (event_start, today_end) >= 0 || + datetime_compare_date (event_end, today_start) < 0) + { + continue; + } + + n_events++; + } + + return n_events; +} static void draw_month_grid (GcalYearView *year_view, @@ -699,8 +745,7 @@ draw_month_grid (GcalYearView *year_view, gint days_delay, days, shown_rows, sunday_idx; gchar *str, *nr_day, *nr_week; gboolean selected_day; - GList *events; - icaltimetype start_date, end_date; + icaltimetype today; now = g_date_time_new_now_local (); @@ -768,18 +813,13 @@ draw_month_grid (GcalYearView *year_view, shown_rows = ceil (days / 7.0); sunday_idx = year_view->k * 7 + sw * ((7 - year_view->first_weekday) % 7) + year_view->show_week_numbers * (1 - year_view->k); - start_date.day = 1; - start_date.month = month_nr + 1; - start_date.year = year_view->date->year; - start_date.hour = 0; - start_date.minute = 0; - start_date.second = 0; - end_date.day = 1; - end_date.month = month_nr + 1; - end_date.year = year_view->date->year; - end_date.hour = 23; - end_date.minute = 59; - end_date.second = 59; + today.year = year_view->date->year; + today.month = month_nr + 1; + today.day = 1; + today.hour = 0; + today.minute = 0; + today.second = 0; + today.zone = gcal_manager_get_system_timezone (year_view->manager); for (i = 0; i < 7 * shown_rows; i++) { @@ -930,23 +970,25 @@ draw_month_grid (GcalYearView *year_view, layout); } - start_date.day = end_date.day = j; - events = gcal_manager_get_events (year_view->manager, &start_date, &end_date); - if (events != NULL) + /* Update the current day, so we can count the events at this day */ + today.day = j; + + if (count_events_at_day (year_view, &today) > 0) { gtk_style_context_save (context); gtk_style_context_add_class (context, "with-events"); + if (selected_day) gtk_style_context_add_class (context, "with-events-selected"); else if (column == sunday_idx) gtk_style_context_add_class (context, "with-events-sunday"); - box_padding_start = (box_side - VISUAL_CLUES_SIDE) / 2 > 0 ? (box_side - VISUAL_CLUES_SIDE) / 2 : 0; + + box_padding_start = MAX (0, (box_side - VISUAL_CLUES_SIDE) / 2); gtk_render_background (context, cr, box_side * column + x + sw * box_padding_start - year_view->k * VISUAL_CLUES_SIDE, box_side * (row + 1) + y + box_padding_top + layout_height + 2.0, VISUAL_CLUES_SIDE, VISUAL_CLUES_SIDE); gtk_style_context_restore (context); - g_list_free_full (events, g_object_unref); } g_free (nr_day); @@ -1503,6 +1545,7 @@ static void gcal_year_view_finalize (GObject *object) { GcalYearView *year_view = GCAL_YEAR_VIEW (object); + guint i; g_free (year_view->navigator_grid); g_free (year_view->selected_data); @@ -1514,6 +1557,9 @@ gcal_year_view_finalize (GObject *object) g_clear_object (&year_view->calendar_settings); + for (i = 0; i < 12; i++) + g_clear_pointer (&year_view->events[i], g_ptr_array_unref); + G_OBJECT_CLASS (gcal_year_view_parent_class)->finalize (object); } @@ -1700,17 +1746,12 @@ gcal_year_view_component_added (ECalDataModelSubscriber *subscriber, ECalClient *client, ECalComponent *comp) { - GcalYearView *year_view = GCAL_YEAR_VIEW (subscriber); - + GcalYearView *self = GCAL_YEAR_VIEW (subscriber); + GDateTime *event_start, *event_end; GcalEvent *event; GError *error; - GList **days_widgets_array; GList *l; - gint i, days_span; - - ECalComponentDateTime date; - time_t event_start, event_end, range_start, range_end; - icaltimezone *zone; + guint i, start_month, end_month; error = NULL; event = gcal_event_new (e_client_get_source (E_CLIENT (client)), comp, &error); @@ -1722,57 +1763,27 @@ gcal_year_view_component_added (ECalDataModelSubscriber *subscriber, return; } - update_selected_dates_from_button_data (year_view); - days_span = icaltime_day_of_year(*(year_view->end_selected_date)) - icaltime_day_of_year(*(year_view->start_selected_date)) + 1; - days_widgets_array = g_new0 (GList*, days_span); + event_start = gcal_event_get_date_start (event); + event_end = gcal_event_get_date_end (event); - /* check if event belongs to range */ - zone = gcal_manager_get_system_timezone (year_view->manager); - range_start = icaltime_as_timet_with_zone (*(year_view->start_selected_date), zone); - range_end = icaltime_as_timet_with_zone (*(year_view->end_selected_date), zone); + /* Calculate the start & end months */ + start_month = g_date_time_get_month (event_start) - 1; + end_month = g_date_time_get_month (event_end) - 1; - e_cal_component_get_dtstart (comp, &date); - event_start = icaltime_as_timet_with_zone (*(date.value), date.value->zone != NULL ? date.value->zone : zone); - e_cal_component_free_datetime (&date); + if (g_date_time_get_year (event_start) < self->date->year) + start_month = 0; - e_cal_component_get_dtend (comp, &date); - if (date.value != NULL) - event_end = icaltime_as_timet_with_zone (*(date.value), date.value->zone != NULL ? date.value->zone : zone); - else - event_end = event_start; - e_cal_component_free_datetime (&date); + if (g_date_time_get_year (event_end) > self->date->year) + end_month = 11; - if (!((event_start <= range_start && event_end >= range_end) || - (event_start >= range_start && event_end <= range_end) || - (event_start >= range_start && event_start <= range_end) || - (event_end >= range_start && event_end <= range_end))) - { - goto out; - } + /* Add the event to the cache */ + for (i = start_month; i <= end_month; i++) + g_ptr_array_add (self->events[i], event); - add_event_to_day_array (year_view, event, days_widgets_array, days_span); - gtk_stack_set_visible_child_name (GTK_STACK (year_view->navigator_stack), "events-list"); + update_selected_dates_from_button_data (self); + update_sidebar (self); - for (i = 0; i < days_span; i++) - { - GList *current_day = days_widgets_array[i]; - for (l = current_day; l != NULL; l = g_list_next (l)) - { - GtkWidget *child_widget = l->data; - gtk_widget_show (child_widget); - g_signal_connect (child_widget, "activate", G_CALLBACK (event_activated), year_view); - g_object_set_data (G_OBJECT (child_widget), "shift", GINT_TO_POINTER (i)); - gtk_container_add (GTK_CONTAINER (year_view->events_sidebar), child_widget); - } - - g_list_free (current_day); - } - - gtk_widget_queue_draw (year_view->navigator); - -out: - g_clear_object (&event); - g_free (days_widgets_array); + gtk_widget_queue_draw (GTK_WIDGET (self->navigator)); } static void @@ -1780,12 +1791,15 @@ gcal_year_view_component_changed (ECalDataModelSubscriber *subscriber, ECalClient *client, ECalComponent *comp) { - GcalYearView *year_view = GCAL_YEAR_VIEW (subscriber); + GcalYearView *self = GCAL_YEAR_VIEW (subscriber); GList *children, *l; gchar *uuid; + guint i; uuid = get_uuid_from_component (e_client_get_source (E_CLIENT (client)), comp); - children = gtk_container_get_children (GTK_CONTAINER (year_view->events_sidebar)); + children = gtk_container_get_children (GTK_CONTAINER (self->events_sidebar)); + + /* Remove from the sidebar */ for (l = children; l != NULL; l = g_list_next (l)) { GcalEventWidget *child_widget; @@ -1794,9 +1808,31 @@ gcal_year_view_component_changed (ECalDataModelSubscriber *subscriber, child_widget = GCAL_EVENT_WIDGET (gtk_bin_get_child (GTK_BIN (l->data))); event = gcal_event_widget_get_event (child_widget); - if (child_widget != NULL && g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0) - gtk_widget_destroy (GTK_WIDGET (l->data)); + if (child_widget && g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0) + gtk_widget_destroy (l->data); } + + /* Also remove from the cached list of events */ + for (i = 0; i < 12; i++) + { + GPtrArray *events; + guint j; + + events = self->events[i]; + + for (j = 0; j < events->len; j++) + { + GcalEvent *event; + + event = g_ptr_array_index (events, j); + + if (g_strcmp0 (gcal_event_get_uid (event), uuid) != 0) + continue; + + g_ptr_array_remove (events, event); + } + } + g_list_free (children); g_free (uuid); @@ -1813,6 +1849,7 @@ gcal_year_view_component_removed (ECalDataModelSubscriber *subscriber, GList *children, *l; ESource *source; gchar *uuid; + guint i; gint number_of_children; source = e_client_get_source (E_CLIENT (client)); @@ -1849,6 +1886,29 @@ gcal_year_view_component_removed (ECalDataModelSubscriber *subscriber, gtk_stack_set_visible_child_name (GTK_STACK (year_view->navigator_stack), "no-events"); } + /* Also remove from the cached list of events */ + for (i = 0; i < 12; i++) + { + GPtrArray *events; + guint j; + + events = year_view->events[i]; + + for (j = 0; j < events->len; j++) + { + GcalEvent *event; + + event = g_ptr_array_index (events, j); + + if (g_strcmp0 (gcal_event_get_uid (event), uuid) != 0) + continue; + + g_ptr_array_remove (events, event); + } + } + + gtk_widget_queue_draw (GTK_WIDGET (year_view->navigator)); + g_list_free (children); g_free (uuid); } @@ -1923,6 +1983,11 @@ gcal_year_view_class_init (GcalYearViewClass *klass) static void gcal_year_view_init (GcalYearView *self) { + guint i; + + for (i = 0; i < 12; i++) + self->events[i] = g_ptr_array_new (); + gtk_widget_init_template (GTK_WIDGET (self)); if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)