From 19681511140e26617fc2593704f86681a37c49cf Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 11 Nov 2017 17:33:49 -0200 Subject: [PATCH 01/16] project: add libdazzle dependency --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 7f59b6e9..e38a7ac2 100644 --- a/meson.build +++ b/meson.build @@ -170,6 +170,7 @@ libedataserverui_dep = dependency('libedataserverui-1.2', version: '>= 3.17.1') libedataserver_dep = dependency('libedataserver-1.2', version: '>= 3.17.1') libecal_dep = dependency('libecal-1.2', version: '>= 3.13.90') libsoup_dep = dependency('libsoup-2.4') +libdazzle_dep = dependency('libdazzle-1.0', version: '>= 3.26.1') glib_dep = dependency('glib-2.0', version: '>= 2.43.4') gtk_dep = dependency('gtk+-3.0', version: '>= 3.22.20') gio_dep = dependency('gio-2.0', version: '>= 2.43.4') From af90070cf4edc003f63af34e44cef68f8a36335e Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 11 Nov 2017 17:35:01 -0200 Subject: [PATCH 02/16] doap: remove Isaque and Erick as maintainers They don't maintain it anymore. --- gnome-calendar.doap | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/gnome-calendar.doap b/gnome-calendar.doap index 824bc0b5..fb85de60 100644 --- a/gnome-calendar.doap +++ b/gnome-calendar.doap @@ -13,13 +13,6 @@ C - - - Erick Pérez Castellanos - - erickpc - - Georges Basile Stavracas Neto @@ -27,11 +20,4 @@ gbsneto - - - Isaque Galdino - - igaldino - - From 4dd4c2410f00df7598bcb7afd9307f9c7909e48e Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 11 Nov 2017 17:48:59 -0200 Subject: [PATCH 03/16] month-cell: Move day label to top, split from overflow --- data/ui/month-cell.ui | 52 +++++++++++++++++++++---------------- src/views/gcal-month-cell.c | 11 ++++++++ src/views/gcal-month-cell.h | 2 ++ src/views/gcal-month-view.c | 5 +++- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/data/ui/month-cell.ui b/data/ui/month-cell.ui index fa4aaf2d..a3013cc5 100644 --- a/data/ui/month-cell.ui +++ b/data/ui/month-cell.ui @@ -11,6 +11,36 @@ True True + + + True + False + start + 6 + + + True + False + + + + end + + + + + False + gtk-missing-image + + + end + + + + + + + + true + + + + true + 12 + 0.0 + true + + + + + + + + + true + end + start + none + + + + true + window-close-symbolic + + + + + + + + + + + + false + 200 + slide-down + true + + + vertical + true + + + true + + + never + true + true + + + true + none + + + + + + + + + + + + + + + + true + none + New Event… + + + + + + + + diff --git a/data/ui/month-view.ui b/data/ui/month-view.ui index 7b2aa44c..afb6977a 100644 --- a/data/ui/month-view.ui +++ b/data/ui/month-view.ui @@ -142,65 +142,4 @@ - - - - false - bottom - - - - - - true - false - 6 - vertical - - - true - false - 6 - 6 - 6 - 0.0 - - - - - - - true - false - never - automatic - 400 - true - - - true - vertical - 2 - 2 - 2 - - - - - - - - true - true - Add Event… - - - - - - diff --git a/src/meson.build b/src/meson.build index b5a29e27..3b30e80b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,6 +8,7 @@ gcal_deps = [ libedataserver_dep, libevolution_dep, libecal_dep, + libdazzle_dep, libsoup_dep, libical_dep, glib_dep, @@ -19,6 +20,7 @@ gcal_deps = [ sources = files( 'views/gcal-month-cell.c', + 'views/gcal-month-popover.c', 'views/gcal-month-view.c', 'views/gcal-range-tree.c', 'views/gcal-view.c', diff --git a/src/views/gcal-month-popover.c b/src/views/gcal-month-popover.c new file mode 100644 index 00000000..cf0e8363 --- /dev/null +++ b/src/views/gcal-month-popover.c @@ -0,0 +1,865 @@ +/* gcal-month-popover.c + * + * Copyright © 2017 Georges Basile Stavracas Neto + * + * 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 3 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, see . + */ + +#define G_LOG_DOMAIN "GcalMonthPopover" + +#include "gcal-event-widget.h" +#include "gcal-month-popover.h" +#include "gcal-utils.h" + +#include + +#define RATIO_TO_RELATIVE 1.25 + +struct _GcalMonthPopover +{ + GtkWindow parent; + + GtkLabel *day_label; + GtkWidget *listbox; + GtkRevealer *revealer; + GtkWidget *scrolled_window; + + GtkWidget *relative_to; + GtkWindow *transient_for; + + GcalManager *manager; + + DzlAnimation *opacity_animation; + DzlAnimation *position_animation; + DzlAnimation *size_animation; + + gulong delete_event_handler; + gulong configure_event_handler; + gulong size_allocate_handler; + + /* Positioning flags - range from [0, 1] */ + gdouble x_ratio; + gdouble y_ratio; + + GDateTime *date; +}; + +static void event_activated_cb (GcalEventWidget *event_widget, + GcalMonthPopover *self); + +G_DEFINE_TYPE (GcalMonthPopover, gcal_month_popover, GTK_TYPE_WINDOW) + +enum +{ + PROP_0, + PROP_MANAGER, + PROP_RELATIVE_TO, + PROP_X, + PROP_Y, + N_PROPS +}; + +enum +{ + EVENT_ACTIVATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; +static GParamSpec *properties [N_PROPS] = { NULL, }; + + +/* + * Auxiliary functions + */ + +static void +adjust_margin (GcalMonthPopover *self, + GdkRectangle *area) +{ + GtkStyleContext *style_context; + GtkBorder margin; + + style_context = gtk_widget_get_style_context (gtk_bin_get_child (GTK_BIN (self))); + gtk_style_context_get_margin (style_context, + gtk_style_context_get_state (style_context), + &margin); + + area->x -= margin.right; + area->y -= margin.top; + area->width += margin.right + margin.left; + area->height += margin.top + margin.bottom; +} + +static void +animate_opacity (GcalMonthPopover *self, + gdouble target) +{ + DzlAnimation *animation; + + if (self->opacity_animation) + dzl_animation_stop (self->opacity_animation); + + /* Animate the opacity */ + animation = dzl_object_animate (self, + DZL_ANIMATION_EASE_IN_OUT_CUBIC, + 250, + gtk_widget_get_frame_clock (GTK_WIDGET (self)), + "opacity", target, + NULL); + dzl_set_weak_pointer (&self->opacity_animation, animation); +} + +static void +animate_position (GcalMonthPopover *self, + gdouble x_ratio, + gdouble y_ratio) +{ + DzlAnimation *animation; + + if (self->position_animation) + dzl_animation_stop (self->position_animation); + + /* Animate the opacity */ + animation = dzl_object_animate (self, + DZL_ANIMATION_EASE_IN_OUT_CUBIC, + 200, + gtk_widget_get_frame_clock (GTK_WIDGET (self)), + "x", x_ratio, + "y", y_ratio, + NULL); + dzl_set_weak_pointer (&self->position_animation, animation); +} + +static void +animate_size (GcalMonthPopover *self, + gint target_width, + gint target_height) +{ + DzlAnimation *animation; + + if (self->size_animation) + dzl_animation_stop (self->size_animation); + + /* Animate the opacity */ + animation = dzl_object_animate (self, + DZL_ANIMATION_EASE_IN_OUT_CUBIC, + 200, + gtk_widget_get_frame_clock (GTK_WIDGET (self)), + "width-request", target_width, + "height-request", target_height, + NULL); + dzl_set_weak_pointer (&self->size_animation, animation); +} + +void +update_maximum_height (GcalMonthPopover *self) +{ + GdkWindow *window; + gint clip_height; + + clip_height = 3000; + window = gtk_widget_get_window (GTK_WIDGET (self->relative_to)); + + if (window) + { + GdkDisplay *display; + GdkMonitor *monitor; + + display = gtk_widget_get_display (GTK_WIDGET (self)); + monitor = gdk_display_get_monitor_at_window (display, window); + + if (monitor) + { + GdkRectangle workarea; + + gdk_monitor_get_workarea (monitor, &workarea); + + clip_height = workarea.height / 2; + } + } + + g_object_set (self->scrolled_window, + "max-content-height", clip_height, + NULL); +} + +static void +update_position (GcalMonthPopover *self) +{ + GtkAllocation alloc; + GtkWidget *toplevel; + GdkWindow *window; + gint diff_w; + gint diff_h; + gint x; + gint y; + + if (!gtk_widget_get_realized (GTK_WIDGET (self))) + return; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->relative_to)); + window = gtk_widget_get_window (toplevel); + + gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc); + + alloc.x = 0; + alloc.y = 0; + + gtk_widget_translate_coordinates (GTK_WIDGET (self->relative_to), toplevel, 0, 0, &alloc.x, &alloc.y); + + gdk_window_get_position (window, &x, &y); + alloc.x += x; + alloc.y += y; + + adjust_margin (self, &alloc); + + diff_w = (RATIO_TO_RELATIVE - 1) * alloc.width * (1.0 - self->x_ratio); + diff_h = (RATIO_TO_RELATIVE - 1) * alloc.height * (1.0 - self->y_ratio); + + /* Animate the margins, not the (x, y) position) */ + gtk_widget_set_margin_top (gtk_bin_get_child (GTK_BIN (self)), diff_h / 2); + + if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) + gtk_widget_set_margin_end (gtk_bin_get_child (GTK_BIN (self)), diff_w / 2); + else + gtk_widget_set_margin_start (gtk_bin_get_child (GTK_BIN (self)), diff_w / 2); +} + +static void +reposition_popover (GcalMonthPopover *self, + gboolean animate) +{ + GtkAllocation alloc; + GtkWidget *toplevel; + GdkWindow *window; + gint diff_w; + gint diff_h; + gint x; + gint y; + + if (!gtk_widget_get_realized (GTK_WIDGET (self))) + return; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->relative_to)); + window = gtk_widget_get_window (toplevel); + + gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc); + + alloc.x = 0; + alloc.y = 0; + + gtk_widget_translate_coordinates (GTK_WIDGET (self->relative_to), toplevel, 0, 0, &alloc.x, &alloc.y); + + gdk_window_get_position (window, &x, &y); + alloc.x += x; + alloc.y += y; + + adjust_margin (self, &alloc); + update_maximum_height (self); + + diff_w = (RATIO_TO_RELATIVE - 1) * alloc.width; + diff_h = (RATIO_TO_RELATIVE - 1) * alloc.height; + + gtk_window_move (GTK_WINDOW (self), alloc.x - diff_w / 2, alloc.y - diff_h / 2); + + if (animate) + { + gtk_widget_set_size_request (GTK_WIDGET (self), alloc.width, alloc.height); + + animate_position (self, 1.0, 1.0); + animate_size (self, alloc.width * RATIO_TO_RELATIVE, alloc.height * RATIO_TO_RELATIVE); + } + else + { + gtk_widget_set_size_request (GTK_WIDGET (self), alloc.width * RATIO_TO_RELATIVE, alloc.height * RATIO_TO_RELATIVE); + } +} + +static void +update_event_list (GcalMonthPopover *self) +{ + g_autofree icaltimetype *start = NULL; + g_autofree icaltimetype *end = NULL; + g_autoptr (GDateTime) start_dt = NULL; + g_autoptr (GDateTime) end_dt = NULL; + g_autoptr (GList) events = NULL; + GList *l; + + gtk_container_foreach (GTK_CONTAINER (self->listbox), (GtkCallback) gtk_widget_destroy, NULL); + + if (!gtk_widget_get_realized (GTK_WIDGET (self)) || + !gtk_widget_get_visible (GTK_WIDGET (self)) || + !self->date) + { + return; + } + + start_dt = g_date_time_new_local (g_date_time_get_year (self->date), + g_date_time_get_month (self->date), + g_date_time_get_day_of_month (self->date), + 0, 0, 0); + end_dt = g_date_time_add_days (start_dt, 1); + + start = datetime_to_icaltime (start_dt); + end = datetime_to_icaltime (end_dt); + + events = gcal_manager_get_events (self->manager, start, end); + + for (l = events; l; l = l->next) + { + g_autoptr (GDateTime) event_start = NULL; + g_autoptr (GDateTime) event_end = NULL; + g_autoptr (GTimeZone) tz = NULL; + GtkWidget *event_widget; + GcalEvent *event; + + event = l->data; + + if (gcal_event_get_all_day (event)) + tz = g_time_zone_new_utc (); + else + tz = g_time_zone_new_local (); + + event_start = g_date_time_new (tz, + g_date_time_get_year (start_dt), + g_date_time_get_month (start_dt), + g_date_time_get_day_of_month (start_dt), + 0, 0, 0); + + event_end = g_date_time_add_days (event_start, 1); + + event_widget = gcal_event_widget_new (event); + gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (event_widget), event_start); + gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (event_widget), event_end); + + gtk_container_add (GTK_CONTAINER (self->listbox), event_widget); + + g_signal_connect (event_widget, + "activate", + G_CALLBACK (event_activated_cb), + self); + } + + gtk_widget_show_all (self->listbox); +} + + +/* + * Callbacks + */ + +static void +event_activated_cb (GcalEventWidget *event_widget, + GcalMonthPopover *self) +{ + g_signal_emit (self, signals[EVENT_ACTIVATED], 0, event_widget); +} + +static void +close_button_clicked_cb (GcalMonthPopover *self) +{ + gcal_month_popover_popdown (self); +} + +static void +new_event_button_clicked_cb (GtkWidget *button, + GcalMonthPopover *self) +{ + GActionMap *map; + GAction *action; + + map = G_ACTION_MAP (g_application_get_default ()); + action = g_action_map_lookup_action (map, "new"); + + g_action_activate (action, NULL); + + //gcal_month_popover_popdown (self); +} + +static void +revealer_notify_child_revealed_cb (GcalMonthPopover *self, + GParamSpec *pspec, + GtkRevealer *revealer) +{ + if (!gtk_revealer_get_reveal_child (self->revealer)) + gtk_widget_hide (GTK_WIDGET (self)); +} + +static gboolean +transient_for_configure_event_cb (GcalMonthPopover *self, + GdkEvent *event, + GtkWindow *toplevel) +{ + gtk_widget_hide (GTK_WIDGET (self)); + return FALSE; +} + +static gboolean +transient_for_delete_event_cb (GcalMonthPopover *self, + GdkEvent *event, + GtkWindow *toplevel) +{ + gtk_widget_hide (GTK_WIDGET (self)); + return FALSE; +} + +static void +transient_for_size_allocate_cb (GcalMonthPopover *self, + GtkAllocation *allocation, + GtkWindow *toplevel) +{ + reposition_popover (self, FALSE); +} + + +/* + * GtkWidget overrides + */ + +static void +gcal_month_popover_destroy (GtkWidget *widget) +{ + GcalMonthPopover *self = (GcalMonthPopover *)widget; + + if (self->transient_for) + { + g_signal_handler_disconnect (self->transient_for, self->size_allocate_handler); + g_signal_handler_disconnect (self->transient_for, self->configure_event_handler); + g_signal_handler_disconnect (self->transient_for, self->delete_event_handler); + + self->size_allocate_handler = 0; + self->configure_event_handler = 0; + self->delete_event_handler = 0; + + self->transient_for = NULL; + } + + if (self->opacity_animation) + { + dzl_animation_stop (self->opacity_animation); + dzl_clear_weak_pointer (&self->opacity_animation); + } + + if (self->position_animation) + { + dzl_animation_stop (self->position_animation); + dzl_clear_weak_pointer (&self->position_animation); + } + + if (self->size_animation) + { + dzl_animation_stop (self->size_animation); + dzl_clear_weak_pointer (&self->size_animation); + } + + gcal_month_popover_set_relative_to (self, NULL); + + GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->destroy (widget); +} + +static gboolean +gcal_month_popover_focus_out_event (GtkWidget *widget, + GdkEventFocus *event_focus) +{ + gcal_month_popover_popdown (GCAL_MONTH_POPOVER (widget)); + + return GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->focus_out_event (widget, event_focus); +} + +static void +gcal_month_popover_hide (GtkWidget *widget) +{ + GcalMonthPopover *self = (GcalMonthPopover *)widget; + + if (self->transient_for) + gtk_window_group_remove_window (gtk_window_get_group (self->transient_for), GTK_WINDOW (self)); + + g_signal_handler_disconnect (self->transient_for, self->delete_event_handler); + g_signal_handler_disconnect (self->transient_for, self->size_allocate_handler); + g_signal_handler_disconnect (self->transient_for, self->configure_event_handler); + + self->delete_event_handler = 0; + self->size_allocate_handler = 0; + self->configure_event_handler = 0; + + self->transient_for = NULL; + + gcal_month_popover_popdown (self); + + GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->hide (widget); +} + +static void +gcal_month_popover_show (GtkWidget *widget) +{ + GcalMonthPopover *self = (GcalMonthPopover *)widget; + + if (self->relative_to) + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_ancestor (GTK_WIDGET (self->relative_to), GTK_TYPE_WINDOW); + + if (GTK_IS_WINDOW (toplevel)) + { + self->transient_for = GTK_WINDOW (toplevel); + + gtk_window_group_add_window (gtk_window_get_group (self->transient_for), GTK_WINDOW (self)); + + self->delete_event_handler = + g_signal_connect_object (toplevel, + "delete-event", + G_CALLBACK (transient_for_delete_event_cb), + self, + G_CONNECT_SWAPPED); + + self->size_allocate_handler = + g_signal_connect_object (toplevel, + "size-allocate", + G_CALLBACK (transient_for_size_allocate_cb), + self, + G_CONNECT_SWAPPED | G_CONNECT_AFTER); + + self->configure_event_handler = + g_signal_connect_object (toplevel, + "configure-event", + G_CALLBACK (transient_for_configure_event_cb), + self, + G_CONNECT_SWAPPED); + + gtk_window_set_transient_for (GTK_WINDOW (self), self->transient_for); + + reposition_popover (self, FALSE); + } + } + + GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->show (widget); +} + +static void +gcal_month_popover_realize (GtkWidget *widget) +{ + GcalMonthPopover *self = (GcalMonthPopover *)widget; + GdkScreen *screen; + GdkVisual *visual; + + screen = gtk_widget_get_screen (widget); + visual = gdk_screen_get_rgba_visual (screen); + + if (visual) + gtk_widget_set_visual (widget, visual); + + GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->realize (widget); + + reposition_popover (self, TRUE); +} + + +/* + * GObject overrides + */ + +static void +gcal_month_popover_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalMonthPopover *self = GCAL_MONTH_POPOVER (object); + + switch (prop_id) + { + case PROP_MANAGER: + g_value_set_object (value, self->manager); + break; + + case PROP_RELATIVE_TO: + g_value_set_object (value, self->relative_to); + break; + + case PROP_X: + g_value_set_double (value, self->x_ratio); + break; + + case PROP_Y: + g_value_set_double (value, self->y_ratio); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_month_popover_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalMonthPopover *self = GCAL_MONTH_POPOVER (object); + + switch (prop_id) + { + case PROP_MANAGER: + self->manager = g_value_dup_object (value); + break; + + case PROP_RELATIVE_TO: + gcal_month_popover_set_relative_to (self, g_value_get_object (value)); + break; + + case PROP_X: + self->x_ratio = g_value_get_double (value); + update_position (self); + break; + + + case PROP_Y: + self->y_ratio = g_value_get_double (value); + update_position (self); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_month_popover_class_init (GcalMonthPopoverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = gcal_month_popover_get_property; + object_class->set_property = gcal_month_popover_set_property; + + widget_class->destroy = gcal_month_popover_destroy; + widget_class->focus_out_event = gcal_month_popover_focus_out_event; + widget_class->hide = gcal_month_popover_hide; + widget_class->show = gcal_month_popover_show; + widget_class->realize = gcal_month_popover_realize; + + properties [PROP_MANAGER] = g_param_spec_object ("manager", + "Manager", + "Manager", + GCAL_TYPE_MANAGER, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_RELATIVE_TO] = g_param_spec_object ("relative-to", + "Relative To", + "The widget to be relative to", + GTK_TYPE_WIDGET, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_X] = g_param_spec_double ("x", + "X Percent position", + "X Percent position", + 0.0, + 1.0, + 0.0, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_Y] = g_param_spec_double ("y", + "Y Percent position", + "Y Percent position", + 0.0, + 1.0, + 0.0, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + signals[EVENT_ACTIVATED] = g_signal_new ("event-activated", + GCAL_TYPE_MONTH_POPOVER, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + GCAL_TYPE_EVENT_WIDGET); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/month-popover.ui"); + + gtk_widget_class_bind_template_callback (widget_class, close_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, new_event_button_clicked_cb); + + gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, day_label); + gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, listbox); + gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, revealer); + gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, scrolled_window); + + gtk_widget_class_set_css_name (widget_class, "monthpopover"); +} + +static void +gcal_month_popover_init (GcalMonthPopover *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_window_set_type_hint (GTK_WINDOW (self), GDK_WINDOW_TYPE_HINT_COMBO); + gtk_window_set_decorated (GTK_WINDOW (self), FALSE); + gtk_window_set_resizable (GTK_WINDOW (self), FALSE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (self), TRUE); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (self), TRUE); + + g_signal_connect_object (self->revealer, + "notify::child-revealed", + G_CALLBACK (revealer_notify_child_revealed_cb), + self, + G_CONNECT_SWAPPED); +} + +GtkWidget* +gcal_month_popover_new (void) +{ + return g_object_new (GCAL_TYPE_MONTH_POPOVER, + "type", GTK_WINDOW_POPUP, + NULL); +} + +void +gcal_month_popover_set_relative_to (GcalMonthPopover *self, + GtkWidget *relative_to) +{ + g_return_if_fail (GCAL_MONTH_POPOVER (self)); + g_return_if_fail (!relative_to || GTK_IS_WIDGET (relative_to)); + + if (self->relative_to == relative_to) + return; + + if (self->relative_to) + { + g_signal_handlers_disconnect_by_func (self->relative_to, gtk_widget_destroyed, &self->relative_to); + self->relative_to = NULL; + } + + if (relative_to) + { + self->relative_to = relative_to; + g_signal_connect (self->relative_to, + "destroy", + G_CALLBACK (gtk_widget_destroyed), + &self->relative_to); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RELATIVE_TO]); +} + +void +gcal_month_popover_popup (GcalMonthPopover *self) +{ + gint duration = 250; + + g_return_if_fail (GCAL_IS_MONTH_POPOVER (self)); + + if (self->relative_to) + { + GtkAllocation alloc; + GdkDisplay *display; + GdkMonitor *monitor; + GdkWindow *window; + gint min_height; + gint nat_height; + + display = gtk_widget_get_display (GTK_WIDGET (self->relative_to)); + window = gtk_widget_get_window (GTK_WIDGET (self->relative_to)); + monitor = gdk_display_get_monitor_at_window (display, window); + + gtk_widget_get_preferred_height (GTK_WIDGET (self), &min_height, &nat_height); + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + + duration = dzl_animation_calculate_duration (monitor, alloc.height, nat_height); + } + + gtk_widget_show (GTK_WIDGET (self)); + + gtk_revealer_set_transition_duration (self->revealer, duration); + gtk_revealer_set_reveal_child (self->revealer, TRUE); + + update_event_list (self); + + gtk_widget_grab_focus (GTK_WIDGET (self)); + + /* Animations */ + animate_opacity (self, 1.0); + reposition_popover (self, TRUE); +} + +void +gcal_month_popover_popdown (GcalMonthPopover *self) +{ + GtkAllocation alloc; + GdkDisplay *display; + GdkMonitor *monitor; + GdkWindow *window; + guint duration; + + g_return_if_fail (GCAL_IS_MONTH_POPOVER (self)); + + if (!gtk_widget_get_realized (GTK_WIDGET (self))) + return; + + display = gtk_widget_get_display (GTK_WIDGET (self->relative_to)); + window = gtk_widget_get_window (GTK_WIDGET (self->relative_to)); + monitor = gdk_display_get_monitor_at_window (display, window); + + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + + duration = dzl_animation_calculate_duration (monitor, alloc.height, 0); + + gtk_revealer_set_transition_duration (self->revealer, duration); + gtk_revealer_set_reveal_child (self->revealer, FALSE); + + /* Animations */ + gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc); + gtk_widget_translate_coordinates (self->relative_to, + gtk_widget_get_toplevel (self->relative_to), 0, 0, &alloc.x, &alloc.y); + adjust_margin (self, &alloc); + + animate_opacity (self, 0.0); + animate_position (self, 0.0, 0.0); + animate_size (self, alloc.width, alloc.height); +} + +GDateTime* +gcal_month_popover_get_date (GcalMonthPopover *self) +{ + g_return_val_if_fail (GCAL_IS_MONTH_POPOVER (self), NULL); + + return self->date; +} + +void +gcal_month_popover_set_date (GcalMonthPopover *self, + GDateTime *date) +{ + g_return_if_fail (GCAL_IS_MONTH_POPOVER (self)); + + if (date && self->date && datetime_compare_date (self->date, date) == 0) + return; + + gcal_clear_datetime (&self->date); + + if (date) + { + g_autofree gchar *label; + + self->date = g_date_time_ref (date); + + label = g_strdup_printf ("%d", g_date_time_get_day_of_month (date)); + gtk_label_set_label (self->day_label, label); + } + + update_event_list (self); +} diff --git a/src/views/gcal-month-popover.h b/src/views/gcal-month-popover.h new file mode 100644 index 00000000..83c7b45d --- /dev/null +++ b/src/views/gcal-month-popover.h @@ -0,0 +1,50 @@ +/* gcal-month-popover.h + * + * Copyright © 2017 Georges Basile Stavracas Neto + * + * 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 3 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, see . + */ + +#ifndef GCAL_MONTH_POPOVER_H +#define GCAL_MONTH_POPOVER_H + +#include "gcal-manager.h" + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_MONTH_POPOVER (gcal_month_popover_get_type()) + +G_DECLARE_FINAL_TYPE (GcalMonthPopover, gcal_month_popover, GCAL, MONTH_POPOVER, GtkWindow) + +GtkWidget* gcal_month_popover_new (void); + + +void gcal_month_popover_set_relative_to (GcalMonthPopover *self, + GtkWidget *relative_to); + +void gcal_month_popover_popup (GcalMonthPopover *self); + +void gcal_month_popover_popdown (GcalMonthPopover *self); + +GDateTime* gcal_month_popover_get_date (GcalMonthPopover *self); + +void gcal_month_popover_set_date (GcalMonthPopover *self, + GDateTime *date); + +G_END_DECLS + +#endif /* GCAL_MONTH_POPOVER_H */ + diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c index daf246dc..7387e4e9 100644 --- a/src/views/gcal-month-view.c +++ b/src/views/gcal-month-view.c @@ -27,6 +27,7 @@ #include "gcal-debug.h" #include "gcal-month-cell.h" +#include "gcal-month-popover.h" #include "gcal-month-view.h" #include "gcal-utils.h" #include "gcal-view.h" @@ -41,9 +42,7 @@ struct _GcalMonthView { GtkContainer parent; - GtkWidget *events_box; - GtkWidget *overflow_popover; - GtkWidget *popover_title; + GcalMonthPopover *overflow_popover; GdkWindow *event_window; @@ -185,11 +184,12 @@ cancel_selection (GcalMonthView *self) } static void -event_activated (GcalEventWidget *widget, - GcalMonthView *self) +event_activated (GcalMonthView *self, + GcalEventWidget *widget, + gpointer unused) { cancel_selection (self); - gtk_widget_hide (self->overflow_popover); + gcal_month_popover_popdown (self->overflow_popover); g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget); } @@ -201,7 +201,7 @@ setup_child_widget (GcalMonthView *self, if (!gtk_widget_get_parent (widget)) gtk_widget_set_parent (widget, GTK_WIDGET (self)); - g_signal_connect (widget, "activate", G_CALLBACK (event_activated), self); + g_signal_connect_swapped (widget, "activate", G_CALLBACK (event_activated), self); g_signal_connect_swapped (widget, "hide", G_CALLBACK (gtk_widget_queue_resize), self); g_signal_connect_swapped (widget, "show", G_CALLBACK (gtk_widget_queue_resize), self); } @@ -385,78 +385,15 @@ show_overflow_popover_cb (GcalMonthCell *cell, GtkWidget *button, GcalMonthView *self) { - g_autofree gchar *label_title = NULL; - GtkWidget *child_widget; - GDateTime *dt; - GList *l; - gint day, cell_nr; + GcalMonthPopover *popover; - dt = gcal_month_cell_get_date (cell); - day = g_date_time_get_day_of_month (dt); - cell_nr = day + self->days_delay - 1; - - if (!g_hash_table_contains (self->overflow_cells, GINT_TO_POINTER (cell_nr))) - { - g_critical ("No overflow events for day %d. Something is wrong.", cell_nr); - return; - } - - /* Popover title */ - label_title = g_date_time_format (dt, _("%B %d")); - gtk_label_set_text (GTK_LABEL (self->popover_title), label_title); - - /* Clean all the widgets */ - gtk_container_foreach (GTK_CONTAINER (self->events_box), (GtkCallback) gtk_widget_destroy, NULL); - - l = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (cell_nr)); - - /* Setup the start & end dates of the events as the begin & end of day */ - for (; l != NULL; l = g_list_next (l)) - { - g_autoptr (GDateTime) current_date = NULL; - g_autoptr (GDateTime) dt_start = NULL; - g_autoptr (GDateTime) dt_end = NULL; - g_autoptr (GTimeZone) tz = NULL; - GtkWidget *cloned_event; - GcalEvent *event; - - event = gcal_event_widget_get_event (l->data); - - if (gcal_event_get_all_day (event)) - tz = g_time_zone_new_utc (); - else - tz = g_time_zone_new_local (); - - current_date = icaltime_to_datetime (self->date); - dt_start = g_date_time_new (tz, - g_date_time_get_year (current_date), - g_date_time_get_month (current_date), - day, 0, 0, 0); - - dt_end = g_date_time_add_days (dt_start, 1); - - cloned_event = gcal_event_widget_clone (l->data); - gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (cloned_event), dt_start); - gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (cloned_event), dt_end); - - gtk_container_add (GTK_CONTAINER (self->events_box), cloned_event); - setup_child_widget (self, cloned_event); - } - - gtk_popover_set_relative_to (GTK_POPOVER (self->overflow_popover), button); - - /* sizing hack */ - child_widget = gtk_bin_get_child (GTK_BIN (self->overflow_popover)); - gtk_widget_set_size_request (child_widget, 200, -1); - gtk_widget_show_all (child_widget); - - g_object_set_data (G_OBJECT (self->overflow_popover), - "selected-day", - GINT_TO_POINTER (day)); - - gtk_popover_popup (GTK_POPOVER (self->overflow_popover)); + popover = GCAL_MONTH_POPOVER (self->overflow_popover); cancel_selection (self); + + gcal_month_popover_set_relative_to (popover, GTK_WIDGET (cell)); + gcal_month_popover_set_date (popover, gcal_month_cell_get_date (cell)); + gcal_month_popover_popup (popover); } static void @@ -760,16 +697,6 @@ gcal_month_view_key_press (GtkWidget *widget, return GDK_EVENT_STOP; } -static gboolean -cancel_dnd_from_overflow_popover (GtkPopover *popover) -{ - GCAL_ENTRY; - - gtk_popover_popdown (popover); - - GCAL_RETURN (FALSE); -} - static gboolean gcal_month_view_scroll_event (GtkWidget *widget, GdkEventScroll *scroll_event) @@ -799,12 +726,12 @@ add_new_event_button_cb (GtkWidget *button, gpointer user_data) { GcalMonthView *self; - gint day; GDateTime *start_date; + gint day; self = GCAL_MONTH_VIEW (user_data); - gtk_widget_hide (self->overflow_popover); + gcal_month_popover_popdown (self->overflow_popover); day = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (self->overflow_popover), "selected-day")); start_date = g_date_time_new_local (self->date->year, self->date->month, day, 0, 0, 0); @@ -2045,7 +1972,7 @@ gcal_month_view_motion_notify_event (GtkWidget *widget, } else { - if (gtk_widget_is_visible (self->overflow_popover)) + if (gtk_widget_is_visible (GTK_WIDGET (self->overflow_popover))) GCAL_RETURN (GDK_EVENT_PROPAGATE); } @@ -2159,7 +2086,6 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/month-view.ui"); - gtk_widget_class_bind_template_child (widget_class, GcalMonthView, events_box); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_0); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_1); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_2); @@ -2168,14 +2094,13 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_5); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_6); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, month_label); - gtk_widget_class_bind_template_child (widget_class, GcalMonthView, overflow_popover); - gtk_widget_class_bind_template_child (widget_class, GcalMonthView, popover_title); gtk_widget_class_bind_template_child (widget_class, GcalMonthView, year_label); gtk_widget_class_bind_template_callback (widget_class, add_new_event_button_cb); - gtk_widget_class_bind_template_callback (widget_class, cancel_dnd_from_overflow_popover); gtk_widget_class_set_css_name (widget_class, "calendar-view"); + + g_type_ensure (GCAL_TYPE_MONTH_POPOVER); } static void @@ -2193,17 +2118,6 @@ gcal_month_view_init (GcalMonthView *self) self->k = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; - /* - * Also set the overflow popover as a drop destination, so we can hide - * it when the user starts dragging an event that is inside the overflow - * list. - */ - gtk_drag_dest_set (GTK_WIDGET (self->overflow_popover), - 0, - NULL, - 0, - GDK_ACTION_MOVE); - /* Weekday header labels */ self->weekday_label[0] = self->label_0; self->weekday_label[1] = self->label_1; @@ -2214,6 +2128,20 @@ gcal_month_view_init (GcalMonthView *self) self->weekday_label[6] = self->label_6; update_weekday_labels (self); + + /* Overflow popover */ + self->overflow_popover = (GcalMonthPopover*) gcal_month_popover_new (); + + g_object_bind_property (self, + "manager", + self->overflow_popover, + "manager", + G_BINDING_DEFAULT); + + g_signal_connect_swapped (self->overflow_popover, + "event-activated", + G_CALLBACK (event_activated), + self); } /* Public API */ From 72b9b3423586c77593b0a452060deb8e10b668fb Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 15 Nov 2017 02:31:03 -0200 Subject: [PATCH 13/16] month-view: win the fight against the sizing machinery We can now guarantee that the month cells unconditionally get their desired allocations, but the event widgets are only created when necessary. I won. --- src/views/gcal-month-view.c | 71 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c index 7387e4e9..2a553674 100644 --- a/src/views/gcal-month-view.c +++ b/src/views/gcal-month-view.c @@ -127,7 +127,6 @@ struct _GcalMonthView GcalManager *manager; gboolean pending_event_allocation; - gboolean pending_child_allocation; }; #define MIRROR(val,start,end) (start + (val / end) * end + (end - val % end)) @@ -437,10 +436,8 @@ static gboolean update_month_cells (GcalMonthView *self) { g_autoptr (GDateTime) dt; - gboolean changed; guint row, col; - changed = FALSE; dt = g_date_time_new_local (self->date->year, self->date->month, 1, 0, 0, 0); for (row = 0; row < 6; row++) @@ -464,26 +461,18 @@ update_month_cells (GcalMonthView *self) /* Cell date */ cell_date = g_date_time_add_days (dt, row * 7 + col - self->days_delay); - changed |= datetime_compare_date (gcal_month_cell_get_date (cell), cell_date) != 0; - gcal_month_cell_set_date (cell, cell_date); /* Different month */ different_month = day < self->days_delay || day - self->days_delay >= icaltime_days_in_month (self->date->month, self->date->year); - changed |= different_month != gcal_month_cell_get_different_month (cell); - gcal_month_cell_set_different_month (cell, different_month); if (different_month) { - changed |= gcal_month_cell_get_selected (cell) != FALSE; gcal_month_cell_set_selected (cell, FALSE); - - changed |= gcal_month_cell_get_overflow (cell) != 0; gcal_month_cell_set_overflow (cell, 0); - continue; } @@ -491,8 +480,6 @@ update_month_cells (GcalMonthView *self) if (g_hash_table_contains (self->overflow_cells, GINT_TO_POINTER (day))) l = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (day)); - changed |= gcal_month_cell_get_overflow (cell) != g_list_length (l); - gcal_month_cell_set_overflow (cell, l ? g_list_length (l) : 0); /* Selection */ @@ -516,14 +503,11 @@ update_month_cells (GcalMonthView *self) datetime_compare_date (gcal_month_cell_get_date (cell), selection_end) <= 0; } - changed |= gcal_month_cell_get_selected (cell) != selected; - gcal_month_cell_set_selected (cell, selected); } } self->update_grid_id = 0; - self->pending_child_allocation = changed; return G_SOURCE_REMOVE; } @@ -1487,6 +1471,7 @@ gcal_month_view_size_allocate (GtkWidget *widget, GHashTableIter iter; GtkAllocation child_allocation; GtkAllocation cell_alloc; + GtkAllocation old_alloc; GcalMonthView *self; GtkWidget *child_widget; GtkWidget *month_cell; @@ -1506,32 +1491,8 @@ gcal_month_view_size_allocate (GtkWidget *widget, self = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); - /* - * If nothing changed, and the month cells didn't request an allocation, - * and the size is the same, return early here. - */ - if (!self->pending_event_allocation && - !self->pending_child_allocation && - allocation->width == gtk_widget_get_allocated_width (widget) && - allocation->height == gtk_widget_get_allocated_height (widget)) - { - return; - } - - /* Remove every widget' parts, but the master widget */ - widgets = g_hash_table_get_values (self->children); - - for (aux = widgets; aux != NULL; aux = g_list_next (aux)) - l = g_list_concat (l, g_list_copy (g_list_next (aux->data))); - - g_list_free (widgets); - - g_list_free_full (l, (GDestroyNotify) gtk_widget_destroy); - - /* Clean overflow information */ - g_hash_table_remove_all (self->overflow_cells); - /* Allocate the widget */ + gtk_widget_get_allocation (widget, &old_alloc); gtk_widget_set_allocation (widget, allocation); if (gtk_widget_get_realized (widget)) @@ -1557,6 +1518,31 @@ gcal_month_view_size_allocate (GtkWidget *widget, gtk_widget_size_allocate (self->grid, &child_allocation); + /* + * At this point, the internal widgets are already allocated and happy. Thus, if no new event + * was added and the size is the same, we don't need to create and add new event widgets. This + * breaks the indirect allocation cycle. + */ + if (!self->pending_event_allocation && + allocation->width == old_alloc.width && + allocation->height == old_alloc.height) + { + goto out; + } + + /* Remove every widget' parts, but the master widget */ + widgets = g_hash_table_get_values (self->children); + + for (aux = widgets; aux != NULL; aux = g_list_next (aux)) + l = g_list_concat (l, g_list_copy (g_list_next (aux->data))); + + g_list_free (widgets); + + g_list_free_full (l, (GDestroyNotify) gtk_widget_destroy); + + /* Clean overflow information */ + g_hash_table_remove_all (self->overflow_cells); + /* Event widgets */ count_events_per_day (self, events_at_day); @@ -1888,10 +1874,10 @@ gcal_month_view_size_allocate (GtkWidget *widget, } } +out: queue_update_month_cells (self); self->pending_event_allocation = FALSE; - self->pending_child_allocation = FALSE; } static gboolean @@ -2114,7 +2100,6 @@ gcal_month_view_init (GcalMonthView *self) self->single_cell_children = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_list_free); self->overflow_cells = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_list_free); self->pending_event_allocation = FALSE; - self->pending_child_allocation = FALSE; self->k = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; From 09cd5d05cb0fd3411900ecdfe967a7990b8dc590 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 15 Nov 2017 03:17:04 -0200 Subject: [PATCH 14/16] month-popover: sort events --- src/views/gcal-month-popover.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/views/gcal-month-popover.c b/src/views/gcal-month-popover.c index cf0e8363..cdc103df 100644 --- a/src/views/gcal-month-popover.c +++ b/src/views/gcal-month-popover.c @@ -356,6 +356,25 @@ update_event_list (GcalMonthPopover *self) } +/* + * GtkListBox functions + */ + +static gint +sort_func (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + GcalEventWidget *event_a; + GcalEventWidget *event_b; + + event_a = GCAL_EVENT_WIDGET (gtk_bin_get_child (GTK_BIN (a))); + event_b = GCAL_EVENT_WIDGET (gtk_bin_get_child (GTK_BIN (b))); + + return gcal_event_widget_sort_events (event_a, event_b); +} + + /* * Callbacks */ @@ -718,6 +737,8 @@ gcal_month_popover_init (GcalMonthPopover *self) G_CALLBACK (revealer_notify_child_revealed_cb), self, G_CONNECT_SWAPPED); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox), sort_func, NULL, NULL); } GtkWidget* From b638a1537cd3f92cda0f1e211503dbf5d6506b7d Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 15 Nov 2017 15:31:27 -0200 Subject: [PATCH 15/16] event-widget: also compare by multiday --- src/gcal-event-widget.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gcal-event-widget.c b/src/gcal-event-widget.c index f4036748..5990199e 100644 --- a/src/gcal-event-widget.c +++ b/src/gcal-event-widget.c @@ -1136,6 +1136,11 @@ gcal_event_widget_sort_events (GcalEventWidget *widget1, gint diff; dt_time1 = dt_time2 = NULL; + diff = gcal_event_is_multiday (widget2->event) - gcal_event_is_multiday (widget1->event); + + if (diff != 0) + return diff; + diff = gcal_event_widget_compare_by_start_date (widget1, widget2); if (diff != 0) From 54839efbf4f47652f7441232cca4cde561d3aea2 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 15 Nov 2017 19:02:24 -0200 Subject: [PATCH 16/16] month-view: fix rtl This removes much of the subtle complexities of the size allocation function in month view, and finally delivers a readable and working implementation of the month view. This also introduces a small code refactoring, and fixes clicking on a cell on RTL. --- src/views/gcal-month-view.c | 213 +++++++++++++++--------------------- 1 file changed, 91 insertions(+), 122 deletions(-) diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c index 2a553674..b679ca51 100644 --- a/src/views/gcal-month-view.c +++ b/src/views/gcal-month-view.c @@ -129,8 +129,6 @@ struct _GcalMonthView gboolean pending_event_allocation; }; -#define MIRROR(val,start,end) (start + (val / end) * end + (end - val % end)) - static void gcal_view_interface_init (GcalViewInterface *iface); static void gtk_buildable_interface_init (GtkBuildableIface *iface); @@ -165,16 +163,6 @@ static guint signals[NUM_SIGNALS] = { 0, }; * Auxiliary functions */ -static inline gint -real_cell (gint cell, - gboolean rtl) -{ - if (cell < 0) - return cell; - - return rtl ? MIRROR (cell, 0, 7) - 1 : cell; -} - static inline void cancel_selection (GcalMonthView *self) { @@ -302,17 +290,17 @@ get_month_cell_at_position (GcalMonthView *self, } static void -get_widget_parts (GcalMonthView *self, - gint first_cell, - gint last_cell, - gint natural_height, - gdouble *vertical_cell_space, - gdouble *size_left, - GArray *cells, - GArray *lengths, - GArray *length_visible, - gint *events_at_day, - gint *allocated_events_at_day) +calculate_multiday_event_blocks (GcalMonthView *self, + gint first_cell, + gint last_cell, + gint natural_height, + gdouble *vertical_cell_space, + gdouble *size_left, + GArray *cells, + GArray *lengths, + GArray *length_visible, + gint *events_at_day, + gint *allocated_events_at_day) { gboolean was_visible; gdouble old_y; @@ -320,6 +308,8 @@ get_widget_parts (GcalMonthView *self, gint current_part_length; gint i; + GCAL_ENTRY; + old_y = -1.0; current_part_length = 0; was_visible = FALSE; @@ -331,6 +321,8 @@ get_widget_parts (GcalMonthView *self, first_cell = swap; } + GCAL_TRACE_MSG ("Calculating event blocks from cell %d to cell %d", first_cell, last_cell); + for (i = first_cell; i <= last_cell; i++) { gboolean visible_at_range; @@ -362,6 +354,8 @@ get_widget_parts (GcalMonthView *self, if (old_y == -1.0 || y != old_y || different_row || was_visible != visible_at_range) { + GCAL_TRACE_MSG ("Breaking event at cell %d (previous section was %d)", i, current_part_length); + current_part_length = 1; old_y = y; @@ -377,6 +371,8 @@ get_widget_parts (GcalMonthView *self, was_visible = visible_at_range; } + + GCAL_EXIT; } static void @@ -1487,9 +1483,11 @@ gcal_month_view_size_allocate (GtkWidget *widget, gint minimum_height; gint header_height; gint grid_height; - gint i, j, sw, row; + gint i, j; - self = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); + GCAL_ENTRY; + + self = GCAL_MONTH_VIEW (widget); /* Allocate the widget */ gtk_widget_get_allocation (widget, &old_alloc); @@ -1527,7 +1525,8 @@ gcal_month_view_size_allocate (GtkWidget *widget, allocation->width == old_alloc.width && allocation->height == old_alloc.height) { - goto out; + GCAL_TRACE_MSG ("Events didn't change, stopping here"); + GCAL_GOTO (out); } /* Remove every widget' parts, but the master widget */ @@ -1554,8 +1553,6 @@ gcal_month_view_size_allocate (GtkWidget *widget, size_left[i] = h; } - sw = 1 - 2 * self->k; - /* * Allocate multidays events before single day events, as they have a * higher priority. Multiday events have a master event, and clones @@ -1563,15 +1560,16 @@ gcal_month_view_size_allocate (GtkWidget *widget, */ for (l = self->multi_cell_children; l; l = g_list_next (l)) { + g_autoptr (GDateTime) start_date = NULL; + g_autoptr (GDateTime) end_date = NULL; g_autoptr (GArray) visible_at_length = NULL; g_autoptr (GArray) lengths = NULL; - g_autoptr (GArray) cells = NULL; - GDateTime *date; + g_autoptr (GArray) blocks = NULL; GcalEvent *event; gboolean all_day; - gint first_cell, last_cell; - gint first_row, last_row; - gint cell; + gint event_block; + gint first_cell; + gint last_cell; child_widget = (GtkWidget*) l->data; event = gcal_event_widget_get_event (l->data); @@ -1584,117 +1582,88 @@ gcal_month_view_size_allocate (GtkWidget *widget, * 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_get_date_start (event); - date = all_day ? g_date_time_ref (date) : g_date_time_to_local (date); + first_cell = 1; + start_date = gcal_event_get_date_start (event); + start_date = all_day ? g_date_time_ref (start_date) : g_date_time_to_local (start_date); - if (g_date_time_get_year (date) == self->date->year && - g_date_time_get_month (date) == self->date->month) + if (g_date_time_get_year (start_date) == self->date->year && + g_date_time_get_month (start_date) == self->date->month) { - j = g_date_time_get_day_of_month (date); + first_cell = g_date_time_get_day_of_month (start_date); } - j += self->days_delay; - - g_clear_pointer (&date, g_date_time_unref); - - /* - * Calculate the first cell position according to the locale - * and the start date. - */ - first_cell = 7 * ((j - 1) / 7)+ 6 * self->k + sw * ((j - 1) % 7); + first_cell += self->days_delay - 1; /* * 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 (self->date->month, self->date->year); - date = gcal_event_get_date_end (event); - date = all_day ? g_date_time_ref (date) : g_date_time_to_local (date); + last_cell = icaltime_days_in_month (self->date->month, self->date->year); + end_date = gcal_event_get_date_end (event); + end_date = all_day ? g_date_time_ref (end_date) : g_date_time_to_local (end_date); - if (g_date_time_get_month (date) == self->date->month) + if (g_date_time_get_month (end_date) == self->date->month) { - j = g_date_time_get_day_of_month (date); + last_cell = g_date_time_get_day_of_month (end_date); /* If the event is all day, we have to subtract 1 to find the the real date */ if (all_day) - j--; + last_cell--; } - j += self->days_delay; - - g_clear_pointer (&date, g_date_time_unref); - - last_cell = 7 * ((j - 1) / 7)+ 6 * self->k + sw * ((j - 1) % 7); + last_cell += self->days_delay - 1; /* * 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; - cells = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); + + GCAL_TRACE_MSG ("Positioning '%s' (multiday) from %d to %d", gcal_event_get_summary (event), first_cell, last_cell); + + blocks = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); lengths = g_array_sized_new (TRUE, TRUE, sizeof (gint), 16); visible_at_length = g_array_sized_new (TRUE, TRUE, sizeof (gboolean), 16); - for (row = first_row; row <= last_row; row++) - { - gint start, end; + /* Position and allocate the child widget */ + minimum_height = get_real_event_widget_height (child_widget); - /* Position and allocate the child widget */ - minimum_height = get_real_event_widget_height (child_widget); + gtk_style_context_get_margin (child_context, + gtk_style_context_get_state (child_context), + &margin); - gtk_style_context_get_margin (gtk_widget_get_style_context (child_widget), - gtk_style_context_get_state (child_context), - &margin); + calculate_multiday_event_blocks (self, + first_cell, last_cell, + minimum_height + margin.top + margin.bottom, + vertical_cell_space, + size_left, + blocks, + lengths, + visible_at_length, + events_at_day, + allocated_events_at_day); - start = row * 7 + self->k * 6; - end = row * 7 + (1 - self->k) * 6; - - if (row == first_row) - start = first_cell; - if (row == last_row) - end = last_cell; - - get_widget_parts (self, - start, end, - minimum_height + margin.top + margin.bottom, - vertical_cell_space, - size_left, - cells, - lengths, - visible_at_length, - events_at_day, - allocated_events_at_day); - } - - for (cell = 0; cell < cells->len; cell++) + for (event_block = 0; event_block < blocks->len; event_block++) { g_autoptr (GDateTime) dt_start = NULL; g_autoptr (GDateTime) dt_end = NULL; gboolean visible; gdouble width; - gint cell_idx; + gint last_block_cell; gint length; - gint column; + gint cell; gint day; - visible = g_array_index (visible_at_length, gboolean, cell); - cell_idx = g_array_index (cells, gint, cell); - length = g_array_index (lengths, gint, cell); - row = cell_idx / 7; - column = cell_idx % 7; - day = cell_idx - self->days_delay + 1; - - /* Day number is calculated differently on RTL languages */ - if (self->k) - day = 7 * row + MIRROR (cell_idx % 7, 0, 7) - self->days_delay - length + 1; + visible = g_array_index (visible_at_length, gboolean, event_block); + length = g_array_index (lengths, gint, event_block); + cell = g_array_index (blocks, gint, event_block); + last_block_cell = cell + length - 1; + day = cell - self->days_delay + 1; /* * Setup a new event widget when the cell index changes. This happens when * the event has a different y position, or when the row changed. */ - if (cell != 0) + if (event_block != 0) { child_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (child_widget)); gtk_widget_show (child_widget); @@ -1714,7 +1683,7 @@ gcal_month_view_size_allocate (GtkWidget *widget, gtk_widget_set_child_visible (child_widget, FALSE); - for (idx = cell_idx; idx < cell_idx + length; idx++) + for (idx = cell; idx < cell + length; idx++) { aux = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (idx)); aux = g_list_append (aux, child_widget); @@ -1728,11 +1697,15 @@ gcal_month_view_size_allocate (GtkWidget *widget, continue; } - /* Retrieve the cell widget */ + + /* + * Retrieve the cell widget. On RTL languages, we use the last month cell as the starting + * point. + */ if (self->k) - month_cell = self->month_cell[row][6 - column]; + month_cell = self->month_cell[last_block_cell / 7][last_block_cell % 7]; else - month_cell = self->month_cell[row][column]; + month_cell = self->month_cell[cell / 7][cell % 7]; gtk_widget_get_allocation (month_cell, &cell_alloc); @@ -1774,7 +1747,7 @@ gcal_month_view_size_allocate (GtkWidget *widget, minimum_height = get_real_event_widget_height (child_widget); child_allocation.x = pos_x; - child_allocation.y = pos_y + vertical_cell_space[cell_idx] - size_left[cell_idx]; + child_allocation.y = pos_y + vertical_cell_space[cell] - size_left[cell]; child_allocation.width = width - (margin.left + margin.right); child_allocation.height = minimum_height; @@ -1783,8 +1756,8 @@ gcal_month_view_size_allocate (GtkWidget *widget, /* update size_left */ for (j = 0; j < length; j++) { - size_left[cell_idx + j] -= minimum_height; - size_left[cell_idx + j] -= margin.top + margin.bottom; + size_left[cell + j] -= minimum_height; + size_left[cell + j] -= margin.top + margin.bottom; } } } @@ -1800,15 +1773,11 @@ gcal_month_view_size_allocate (GtkWidget *widget, { gboolean will_overflow; gint cell; + gint day; - j = GPOINTER_TO_INT (key); - j += self->days_delay; - cell = 7 * ((j - 1) / 7) + 6 * self->k + sw * ((j - 1) % 7); - - if (self->k) - month_cell = self->month_cell[cell / 7][6 - cell % 7]; - else - month_cell = self->month_cell[cell / 7][cell % 7]; + day = GPOINTER_TO_INT (key); + cell = day + self->days_delay - 1; + month_cell = self->month_cell[cell / 7][cell % 7]; gtk_widget_get_allocation (month_cell, &cell_alloc); @@ -1834,7 +1803,7 @@ gcal_month_view_size_allocate (GtkWidget *widget, real_height = size_left[cell]; if (will_overflow) - real_height -= gcal_month_cell_get_overflow_height (GCAL_MONTH_CELL (self->month_cell[cell / 7][cell % 7])); + real_height -= gcal_month_cell_get_overflow_height (GCAL_MONTH_CELL (month_cell)); /* No space left, add to the overflow and continue */ if (real_height < minimum_height) @@ -1878,6 +1847,8 @@ out: queue_update_month_cells (self); self->pending_event_allocation = FALSE; + + GCAL_EXIT; } static gboolean @@ -1899,8 +1870,6 @@ gcal_month_view_button_press (GtkWidget *widget, get_month_cell_at_position (self, x, y, &clicked_cell); - clicked_cell = real_cell (clicked_cell, self->k); - if (clicked_cell >= self->days_delay && clicked_cell < days) { g_clear_pointer (&self->start_mark_cell, g_date_time_unref);