From 87e36adbb6cf1aca8572fd41ad07b74bd9a13136 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 15 Feb 2016 20:40:32 -0200 Subject: [PATCH] month-view: rework allocation code GcalEventWidget added API to automatically change the widget rendering according to the initial and final dates. Month view, however, was still handling it manually (and wrongly). Fix that by reworking the allocation code, documenting it and using the apropriate API from GcalEventWidget. --- src/gcal-month-view.c | 141 ++++++++++++++++++++++--------------- src/gcal-subscriber-view.c | 2 +- 2 files changed, 87 insertions(+), 56 deletions(-) diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index a9357ffb..e0d46181 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -275,7 +275,6 @@ show_popover_for_position (GcalMonthView *view, end_date = gcal_dup_icaltime (priv->date); end_date->day = end_day; end_date->is_date = 1; - if (start_date->day > end_date->day) { gint day = start_date->day; @@ -1027,6 +1026,7 @@ gcal_month_view_size_allocate (GtkWidget *widget, context = gtk_widget_get_style_context (widget); ppriv = GCAL_SUBSCRIBER_VIEW (widget)->priv; + /* No need to relayout stuff if nothing changed */ if (!ppriv->children_changed && allocation->height == gtk_widget_get_allocated_height (widget) && allocation->width == gtk_widget_get_allocated_width (widget)) @@ -1045,12 +1045,17 @@ gcal_month_view_size_allocate (GtkWidget *widget, /* clean overflow information */ g_hash_table_remove_all (ppriv->overflow_cells); + /* Allocate the widget */ gtk_widget_set_allocation (widget, allocation); + if (gtk_widget_get_realized (widget)) gdk_window_move_resize (priv->event_window, allocation->x, allocation->y, allocation->width, allocation->height); - gtk_style_context_get (context, gtk_style_context_get_state (context), - "font", &font_desc, "padding-bottom", &padding_bottom, NULL); + gtk_style_context_get (context, + gtk_style_context_get_state (context), + "font", &font_desc, + "padding-bottom", &padding_bottom, + NULL); layout = gtk_widget_create_pango_layout (widget, _("Other events")); pango_layout_set_font_description (layout, font_desc); @@ -1070,11 +1075,15 @@ gcal_month_view_size_allocate (GtkWidget *widget, first_row_gap = (6 - shown_rows) * 0.5; sw = 1 - 2 * priv->k; - /* allocate multidays events */ + /* + * Allocate multidays events before single day events, as they have a + * higher priority. Multiday events have a master event, and clones + * to represent the multiple sections of the event. + */ for (l = ppriv->multi_cell_children; l != NULL; l = g_list_next (l)) { gint first_cell, last_cell, first_row, last_row, start, end; - gboolean visible, start_before = TRUE, end_after = TRUE; + gboolean visible; GDateTime *date; GcalEvent *event; @@ -1090,55 +1099,63 @@ gcal_month_view_size_allocate (GtkWidget *widget, gtk_widget_show (child_widget); gtk_widget_get_preferred_height (child_widget, NULL, &natural_height); + /* + * If the month of the event's start date is equal to the month + * we're visualizing, the first cell is the event's day of the + * month. Otherwise, the first cell is the 1st day of the month. + */ j = 1; - date = gcal_event_widget_get_date_start (GCAL_EVENT_WIDGET (child_widget)); + date = gcal_event_widget_get_date_start (l->data); + + if (g_date_time_get_month (date) == priv->date->month) + j = g_date_time_get_day_of_month (date); + + j += priv->days_delay; + + /* + * Calculate the first cell position according to the locale + * and the start date. + */ + first_cell = 7 * ((j - 1) / 7)+ 6 * priv->k + sw * ((j - 1) % 7); + + /* + * The logic for the end date is the same, except that we have to check + * if the event is all day or not. + */ + j = icaltime_days_in_month (priv->date->month, priv->date->year); + date = gcal_event_widget_get_date_end (GCAL_EVENT_WIDGET (child_widget)); + if (g_date_time_get_month (date) == priv->date->month) { j = g_date_time_get_day_of_month (date); - start_before = FALSE; - } - j += priv->days_delay; - first_cell = 7 * ((j - 1) / 7)+ 6 * priv->k + sw * ((j - 1) % 7); - j = icaltime_days_in_month (priv->date->month, priv->date->year); - date = gcal_event_widget_get_date_end (GCAL_EVENT_WIDGET (child_widget)); - if (gcal_event_get_all_day (event)) - { - if (g_date_time_get_month (date) == priv->date->month) - { - j = g_date_time_get_day_of_month (date) - 1; - end_after = FALSE; - } - } - else - { - if (g_date_time_get_month (date) == priv->date->month) - { - j = g_date_time_get_day_of_month (date); - end_after = FALSE; - } + /* If the event is all day, we have to subtract 1 to find the the real date */ + if (gcal_event_get_all_day (event)) + j--; } j += priv->days_delay; + last_cell = 7 * ((j - 1) / 7)+ 6 * priv->k + sw * ((j - 1) % 7); + /* + * Now that we found the start & end dates, we have to find out + * whether the event is visible or not. + */ first_row = first_cell / 7; last_row = last_cell / 7; visible = TRUE; cells = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); lengths = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); + for (i = first_row; i <= last_row && visible; i++) { start = i * 7 + priv->k * 6; end = i * 7 + (1 - priv->k) * 6; if (i == first_row) - { - start = first_cell; - } + start = first_cell; if (i == last_row) - { - end = last_cell; - } + end = last_cell; visible = get_widget_parts (start, end, natural_height, vertical_cell_space, size_left, cells, lengths); } @@ -1147,9 +1164,12 @@ gcal_month_view_size_allocate (GtkWidget *widget, { for (i = 0; i < cells->len; i++) { + GDateTime *dt_start, *dt_end; gint cell_idx = g_array_index (cells, gint, i); + gint length = g_array_index (lengths, gint, i); gint row = cell_idx / 7; gint column = cell_idx % 7; + gint day = cell_idx - priv->days_delay + 1; if (i != 0) { @@ -1158,29 +1178,31 @@ gcal_month_view_size_allocate (GtkWidget *widget, _gcal_subscriber_view_setup_child (GCAL_SUBSCRIBER_VIEW (widget), child_widget); gtk_widget_show (child_widget); - if (i != cells->len - 1 || end_after) - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted"); - else - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted-start"); - aux = g_hash_table_lookup (ppriv->children, uuid); aux = g_list_append (aux, child_widget); } - else if (i == 0 && cells->len == 1) - { - if (start_before) - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted-start"); - if (end_after) - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted-end"); - } - else if (i != cells->len - 1) - { - if (start_before) - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted"); - else - gtk_style_context_add_class (gtk_widget_get_style_context (child_widget), "slanted-end"); - } + /* XXX: silence Gtk+ complaining about preferred size */ + gtk_widget_get_preferred_height (child_widget, NULL, &natural_height); + + /* + * Setup the widget's start date as the first day of the row, + * and the widget's end date as the last day of the row. We don't + * have to worry about the dates, since GcalEventWidget performs + * some checks and only applies the dates when it's valid. + */ + dt_start = g_date_time_new (gcal_event_get_timezone (event), + priv->date->year, + priv->date->month, + day, + 0, 0, 0); + + dt_end = g_date_time_add_days (dt_start, length); + + gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (child_widget), dt_start); + gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (child_widget), dt_end); + + /* Position and allocate the child widget */ gtk_style_context_get_margin (gtk_widget_get_style_context (child_widget), gtk_style_context_get_state (context), &margin); @@ -1190,15 +1212,18 @@ gcal_month_view_size_allocate (GtkWidget *widget, child_allocation.x = pos_x; child_allocation.y = pos_y + vertical_cell_space - size_left[cell_idx]; - child_allocation.width = cell_width * g_array_index (lengths, gint, i) - (margin.left + margin.right); + child_allocation.width = cell_width * length - (margin.left + margin.right); child_allocation.height = natural_height; gtk_widget_size_allocate (child_widget, &child_allocation); g_hash_table_remove (ppriv->hidden_as_overflow, uuid); /* update size_left */ - for (j = 0; j < g_array_index (lengths, gint, i); j++) + for (j = 0; j < length; j++) size_left[cell_idx + j] -= natural_height + margin.top + margin.bottom; + + g_clear_pointer (&dt_start, g_date_time_unref); + g_clear_pointer (&dt_end, g_date_time_unref); } } else @@ -1222,7 +1247,13 @@ gcal_month_view_size_allocate (GtkWidget *widget, g_array_free (lengths, TRUE); } + /* + * Allocate single day events after multiday ones. For single + * day children, there's no need to calculate the start & end + * dates of the widgets. + */ g_hash_table_iter_init (&iter, ppriv->single_cell_children); + while (g_hash_table_iter_next (&iter, &key, &value)) { j = GPOINTER_TO_INT (key); @@ -1889,7 +1920,7 @@ gcal_month_view_get_final_date (GcalView *view) new_date->is_date = 0; new_date->hour = 23; new_date->minute = 59; - new_date->second = 0; + new_date->second = 59; return new_date; } diff --git a/src/gcal-subscriber-view.c b/src/gcal-subscriber-view.c index 43ae97fa..71f91833 100644 --- a/src/gcal-subscriber-view.c +++ b/src/gcal-subscriber-view.c @@ -239,7 +239,7 @@ gcal_subscriber_view_remove (GtkContainer *container, master_widget = (GtkWidget*) l->data; if (widget == master_widget) { - if (gcal_subscriber_view_is_child_multicell (GCAL_SUBSCRIBER_VIEW (container), GCAL_EVENT_WIDGET (widget))) + if (g_list_find (priv->multi_cell_children, widget) != NULL) { priv->multi_cell_children = g_list_remove (priv->multi_cell_children, widget);