diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml
index ccfe72d9..1c11d23f 100644
--- a/data/calendar.gresource.xml
+++ b/data/calendar.gresource.xml
@@ -8,6 +8,7 @@
ui/edit-dialog.ui
ui/event-widget.ui
ui/month-cell.ui
+ ui/month-popover.ui
ui/month-view.ui
ui/multi-choice.ui
ui/online-account-row.ui
diff --git a/data/meson.build b/data/meson.build
index baa50cba..f99ff608 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -55,6 +55,7 @@ resource_data = files(
'ui/help-overlay.ui',
'ui/menus.ui',
'ui/month-cell.ui',
+ 'ui/month-popover.ui',
'ui/month-view.ui',
'ui/multi-choice.ui',
'ui/quick-add-popover.ui',
diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css
index 01e5bfb2..4cecb1ea 100644
--- a/data/theme/gtk-styles.css
+++ b/data/theme/gtk-styles.css
@@ -443,6 +443,10 @@ monthcell label {
font-size: 0.9rem;
}
+monthcell label.day-label {
+ font-size: 1rem;
+}
+
monthcell.out-of-month label {
color: alpha(@theme_fg_color, 0.66);
}
@@ -461,3 +465,17 @@ monthcell button {
border-bottom: none;
border-right: none;
}
+
+/*
+ * Month popover
+ */
+
+monthpopover { background: transparent; }
+
+monthpopover > box {
+ margin: 4px;
+ border: 1px solid @borders;
+ box-shadow: 0px 0px 2px @wm_shadow;
+ padding: 12px;
+ background: @theme_bg_color;
+}
diff --git a/data/ui/month-cell.ui b/data/ui/month-cell.ui
index fa4aaf2d..5af53880 100644
--- a/data/ui/month-cell.ui
+++ b/data/ui/month-cell.ui
@@ -11,6 +11,32 @@
True
True
+
+
+
+
+
+
+
+
+
+
+
+ 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/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
-
-
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')
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)
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-cell.c b/src/views/gcal-month-cell.c
index 623fd3a3..d55216db 100644
--- a/src/views/gcal-month-cell.c
+++ b/src/views/gcal-month-cell.c
@@ -32,6 +32,7 @@ struct _GcalMonthCell
guint n_overflow;
GtkLabel *day_label;
+ GtkWidget *header_box;
GtkWidget *overflow_button;
GtkWidget *overflow_label;
GtkWidget *overlay;
@@ -422,6 +423,7 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/month-cell.ui");
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, day_label);
+ gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, header_box);
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_button);
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overflow_label);
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, overlay);
@@ -561,12 +563,12 @@ gcal_month_cell_set_overflow (GcalMonthCell *self,
self->n_overflow = n_overflow;
- text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Other event", "Other %d events", n_overflow), n_overflow);
+ text = g_strdup_printf ("+%d", n_overflow);
gtk_label_set_text (GTK_LABEL (self->overflow_label), text);
}
gint
-gcal_month_cell_get_real_height (GcalMonthCell *self)
+gcal_month_cell_get_content_space (GcalMonthCell *self)
{
GtkStyleContext *context;
GtkBorder padding;
@@ -579,11 +581,39 @@ gcal_month_cell_get_real_height (GcalMonthCell *self)
gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
return gtk_widget_get_allocated_height (GTK_WIDGET (self)) -
- gtk_widget_get_allocated_height (self->overflow_button) -
+ gcal_month_cell_get_header_height (self) -
padding.top - padding.bottom -
border.top - border.bottom;
}
+gint
+gcal_month_cell_get_header_height (GcalMonthCell *self)
+{
+ GtkStyleContext *context;
+ GtkBorder padding;
+ GtkBorder border;
+
+ g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self->header_box));
+ gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
+ gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
+
+ return gtk_widget_get_allocated_height (self->header_box) +
+ gtk_widget_get_margin_top (self->header_box) +
+ gtk_widget_get_margin_bottom (self->header_box) +
+ padding.top + padding.bottom +
+ border.top + border.bottom;
+}
+
+gint
+gcal_month_cell_get_overflow_height (GcalMonthCell *self)
+{
+ g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1);
+
+ return gtk_widget_get_allocated_height (self->overflow_button);
+}
+
gboolean
gcal_month_cell_get_selected (GcalMonthCell *self)
{
diff --git a/src/views/gcal-month-cell.h b/src/views/gcal-month-cell.h
index ccea347c..bdf32fec 100644
--- a/src/views/gcal-month-cell.h
+++ b/src/views/gcal-month-cell.h
@@ -51,7 +51,11 @@ guint gcal_month_cell_get_overflow (GcalMonthCell
void gcal_month_cell_set_overflow (GcalMonthCell *self,
guint n_overflow);
-gint gcal_month_cell_get_real_height (GcalMonthCell *self);
+gint gcal_month_cell_get_content_space (GcalMonthCell *self);
+
+gint gcal_month_cell_get_header_height (GcalMonthCell *self);
+
+gint gcal_month_cell_get_overflow_height (GcalMonthCell *self);
gboolean gcal_month_cell_get_selected (GcalMonthCell *self);
diff --git a/src/views/gcal-month-popover.c b/src/views/gcal-month-popover.c
new file mode 100644
index 00000000..cdc103df
--- /dev/null
+++ b/src/views/gcal-month-popover.c
@@ -0,0 +1,886 @@
+/* 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);
+}
+
+
+/*
+ * 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
+ */
+
+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);
+
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox), sort_func, NULL, NULL);
+}
+
+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 a1485159..b679ca51 100644
--- a/src/views/gcal-month-view.c
+++ b/src/views/gcal-month-view.c
@@ -1,12 +1,11 @@
-/* -*- mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/*
- * gcal-month-view.c
+/* gcal-month-view.c
*
- * Copyright (C) 2015 - Erick Pérez Castellanos
+ * Copyright © 2015 - Erick Pérez Castellanos
+ * 2017 Georges Basile Stavracas Neto
*
- * This program is free software; you can redistribute it and/or modify
+ * 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
+ * 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,
@@ -28,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"
@@ -42,9 +42,7 @@ struct _GcalMonthView
{
GtkContainer parent;
- GtkWidget *events_box;
- GtkWidget *overflow_popover;
- GtkWidget *popover_title;
+ GcalMonthPopover *overflow_popover;
GdkWindow *event_window;
@@ -89,11 +87,6 @@ struct _GcalMonthView
*/
GHashTable *overflow_cells;
- /*
- * Set containing the master widgets hidden for delete;
- */
- GHashTable *hidden_as_overflow;
-
/*
* the cell on which its drawn the first day of the month, in the first row, 0 for the first
* cell, 1 for the second, and so on, this takes first_weekday into account already.
@@ -127,15 +120,14 @@ struct _GcalMonthView
/* Storage for the accumulated scrolling */
gdouble scroll_value;
-
guint update_grid_id;
/* property */
icaltimetype *date;
GcalManager *manager;
-};
-#define MIRROR(val,start,end) (start + (val / end) * end + (end - val % end))
+ gboolean pending_event_allocation;
+};
static void gcal_view_interface_init (GcalViewInterface *iface);
@@ -171,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)
{
@@ -189,11 +171,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);
}
@@ -205,7 +188,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);
}
@@ -306,22 +289,30 @@ get_month_cell_at_position (GcalMonthView *self,
return NULL;
}
-static gboolean
-get_widget_parts (gint first_cell,
- gint last_cell,
- gint natural_height,
- gdouble *vertical_cell_space,
- gdouble *size_left,
- GArray *cells,
- GArray *lengths)
+static void
+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;
gdouble y;
gint current_part_length;
gint i;
+ GCAL_ENTRY;
+
old_y = -1.0;
current_part_length = 0;
+ was_visible = FALSE;
if (last_cell < first_cell)
{
@@ -330,28 +321,58 @@ get_widget_parts (gint first_cell,
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++)
{
- if (size_left[i] < natural_height)
- return FALSE;
+ gboolean visible_at_range;
+ gboolean different_row;
+ gboolean will_overflow;
+ gdouble real_height;
+ gint remaining_events;
+
+ real_height = size_left[i];
+
+ /* Count this event at this cell */
+ different_row = i / 7 != (i - 1) / 7;
+ remaining_events = events_at_day[i] - allocated_events_at_day[i];
+ will_overflow = remaining_events * natural_height > real_height;
+
+ if (will_overflow)
+ real_height -= gcal_month_cell_get_overflow_height (GCAL_MONTH_CELL (self->month_cell[i / 7][i % 7]));
+
+ visible_at_range = real_height >= natural_height;
+
+ if (visible_at_range)
+ allocated_events_at_day[i]++;
y = vertical_cell_space[i] - size_left[i];
- if (old_y == -1.0 || y != old_y)
+ /* At the first iteration, make was_visible and visible_at_range equal */
+ if (i == first_cell)
+ was_visible = visible_at_range;
+
+ 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;
+
g_array_append_val (cells, i);
g_array_append_val (lengths, current_part_length);
- old_y = y;
+ g_array_append_val (length_visible, visible_at_range);
}
else
{
current_part_length++;
g_array_index (lengths, gint, lengths->len - 1) = current_part_length;
}
+
+ was_visible = visible_at_range;
}
- return TRUE;
+ GCAL_EXIT;
}
static void
@@ -359,78 +380,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
@@ -498,6 +456,7 @@ update_month_cells (GcalMonthView *self)
/* Cell date */
cell_date = g_date_time_add_days (dt, row * 7 + col - self->days_delay);
+
gcal_month_cell_set_date (cell, cell_date);
/* Different month */
@@ -718,16 +677,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)
@@ -757,12 +706,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);
@@ -880,11 +829,13 @@ gcal_month_view_component_added (ECalDataModelSubscriber *subscriber,
ECalClient *client,
ECalComponent *comp)
{
+ GcalMonthView *self;
GtkWidget *event_widget;
GcalEvent *event;
GError *error;
error = NULL;
+ self = GCAL_MONTH_VIEW (subscriber);
event = gcal_event_new (e_client_get_source (E_CLIENT (client)), comp, &error);
if (error)
@@ -900,6 +851,8 @@ gcal_month_view_component_added (ECalDataModelSubscriber *subscriber,
gtk_widget_show (event_widget);
gtk_container_add (GTK_CONTAINER (subscriber), event_widget);
+ self->pending_event_allocation = TRUE;
+
g_clear_object (&event);
}
@@ -944,6 +897,8 @@ gcal_month_view_component_modified (ECalDataModelSubscriber *subscriber,
gtk_widget_destroy (new_widget);
}
+ self->pending_event_allocation = TRUE;
+
g_clear_object (&event);
}
@@ -978,6 +933,8 @@ gcal_month_view_component_removed (ECalDataModelSubscriber *subscriber,
}
gtk_widget_destroy (l->data);
+
+ self->pending_event_allocation = TRUE;
}
static void
@@ -1176,8 +1133,6 @@ gcal_month_view_remove (GtkContainer *container,
g_hash_table_remove (self->children, uuid);
else
g_hash_table_replace (self->children, g_strdup (uuid), l);
-
- g_hash_table_remove (self->hidden_as_overflow, uuid);
}
out:
@@ -1296,7 +1251,6 @@ gcal_month_view_finalize (GObject *object)
g_clear_pointer (&self->children, g_hash_table_destroy);
g_clear_pointer (&self->single_cell_children, g_hash_table_destroy);
g_clear_pointer (&self->overflow_cells, g_hash_table_destroy);
- g_clear_pointer (&self->hidden_as_overflow, g_hash_table_destroy);
g_clear_pointer (&self->multi_cell_children, g_list_free);
g_clear_object (&self->manager);
@@ -1398,16 +1352,19 @@ remove_cell_border_and_padding (GtkWidget *cell,
GtkStyleContext *cell_context;
GtkBorder cell_padding;
GtkBorder cell_border;
+ gint header_height;
cell_context = gtk_widget_get_style_context (cell);
gtk_style_context_get_border (cell_context, gtk_style_context_get_state (cell_context), &cell_border);
gtk_style_context_get_padding (cell_context, gtk_style_context_get_state (cell_context), &cell_padding);
+ header_height = gcal_month_cell_get_header_height (GCAL_MONTH_CELL (cell));
+
if (x)
*x += cell_border.left + cell_padding.left;
if (y)
- *y += cell_border.top + cell_padding.top;
+ *y += cell_border.top + cell_padding.top + header_height;
if (width)
{
@@ -1416,6 +1373,92 @@ remove_cell_border_and_padding (GtkWidget *cell,
}
}
+static void
+count_events_per_day (GcalMonthView *self,
+ gint *events_per_day)
+{
+ GHashTableIter iter;
+ gpointer key;
+ GList *children;
+ GList *l;
+
+ /* Multiday events */
+ for (l = self->multi_cell_children; l; l = l->next)
+ {
+ g_autoptr (GDateTime) start_date = NULL;
+ g_autoptr (GDateTime) end_date = NULL;
+ GcalEvent *event;
+ gboolean all_day;
+ gint first_cell;
+ gint last_cell;
+ gint i;
+
+ event = gcal_event_widget_get_event (l->data);
+ all_day = gcal_event_get_all_day (event);
+
+ /* Start 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 (start_date) == self->date->year &&
+ g_date_time_get_month (start_date) == self->date->month)
+ {
+ first_cell = g_date_time_get_day_of_month (start_date);
+ }
+
+ 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.
+ */
+ 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_year (start_date) == self->date->year &&
+ g_date_time_get_month (end_date) == self->date->month)
+ {
+ 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)
+ last_cell--;
+ }
+
+ last_cell += self->days_delay - 1;
+
+ for (i = first_cell; i <= last_cell; i++)
+ events_per_day[i]++;
+ }
+
+ /* Single day events */
+ g_hash_table_iter_init (&iter, self->single_cell_children);
+ while (g_hash_table_iter_next (&iter, &key, (gpointer*) &children))
+ {
+ gint cell;
+
+ cell = GPOINTER_TO_INT (key) + self->days_delay - 1;
+
+ for (l = children; l; l = l->next)
+ events_per_day[cell]++;
+ }
+}
+
+static gdouble
+get_real_event_widget_height (GtkWidget *widget)
+{
+ gint min_height;
+
+ gtk_widget_get_preferred_height (widget, &min_height, NULL);
+
+ min_height += gtk_widget_get_margin_top (widget);
+ min_height += gtk_widget_get_margin_bottom (widget);
+
+ return min_height;
+}
+
static void
gcal_month_view_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
@@ -1424,6 +1467,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;
@@ -1433,28 +1477,20 @@ gcal_month_view_size_allocate (GtkWidget *widget,
const gchar *uuid;
gdouble vertical_cell_space [42];
gdouble size_left [42];
+ gint allocated_events_at_day [42] = { 0, };
+ gint events_at_day [42] = { 0, };
gdouble pos_x, pos_y;
gint minimum_height;
gint header_height;
gint grid_height;
- gint i, j, sw;
+ gint i, j;
- self = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget));
+ GCAL_ENTRY;
- /* 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);
+ self = GCAL_MONTH_VIEW (widget);
/* Allocate the widget */
+ gtk_widget_get_allocation (widget, &old_alloc);
gtk_widget_set_allocation (widget, allocation);
if (gtk_widget_get_realized (widget))
@@ -1480,29 +1516,60 @@ 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)
+ {
+ GCAL_TRACE_MSG ("Events didn't change, stopping here");
+ GCAL_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);
+
for (i = 0; i < 42; i++)
{
- gint h = gcal_month_cell_get_real_height (GCAL_MONTH_CELL (self->month_cell[i / 7][i % 7]));
+ gint h = gcal_month_cell_get_content_space (GCAL_MONTH_CELL (self->month_cell[i / 7][i % 7]));
vertical_cell_space[i] = h;
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
* to represent the multiple sections of the event.
*/
- for (l = self->multi_cell_children; l != NULL; l = g_list_next (l))
+ for (l = self->multi_cell_children; l; l = g_list_next (l))
{
- GDateTime *date;
+ 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) blocks = NULL;
GcalEvent *event;
- GArray *cells, *lengths;
- gboolean visible, all_day;
- gint first_cell, last_cell, first_row, last_row, start, end;
+ gboolean all_day;
+ gint event_block;
+ gint first_cell;
+ gint last_cell;
child_widget = (GtkWidget*) l->data;
event = gcal_event_widget_get_event (l->data);
@@ -1510,189 +1577,189 @@ gcal_month_view_size_allocate (GtkWidget *widget,
all_day = gcal_event_get_all_day (event);
child_context = gtk_widget_get_style_context (l->data);
- if (!gtk_widget_is_visible (child_widget) && !g_hash_table_contains (self->hidden_as_overflow, uuid))
- continue;
-
- gtk_widget_show (child_widget);
- gtk_widget_get_preferred_height (child_widget, &minimum_height, NULL);
-
/*
* 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_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;
- visible = TRUE;
- 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 (i = first_row; i <= last_row && visible; i++)
+ /* 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);
+
+ 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);
+
+ for (event_block = 0; event_block < blocks->len; event_block++)
{
- start = i * 7 + self->k * 6;
- end = i * 7 + (1 - self->k) * 6;
+ g_autoptr (GDateTime) dt_start = NULL;
+ g_autoptr (GDateTime) dt_end = NULL;
+ gboolean visible;
+ gdouble width;
+ gint last_block_cell;
+ gint length;
+ gint cell;
+ gint day;
- if (i == first_row)
- start = first_cell;
- if (i == last_row)
- end = last_cell;
+ 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;
- visible = get_widget_parts (start, end, minimum_height, vertical_cell_space, size_left, cells, lengths);
- }
-
- if (visible)
- {
- for (i = 0; i < cells->len; i++)
+ /*
+ * 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 (event_block != 0)
{
- GDateTime *dt_start, *dt_end;
- gdouble width;
- 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 - self->days_delay + 1;
+ child_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (child_widget));
+ gtk_widget_show (child_widget);
- /* 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;
+ setup_child_widget (self, child_widget);
- if (i != 0)
- {
- child_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (child_widget));
+ child_context = gtk_widget_get_style_context (child_widget);
- setup_child_widget (self, child_widget);
- gtk_widget_show (child_widget);
-
- child_context = gtk_widget_get_style_context (child_widget);
-
- aux = g_hash_table_lookup (self->children, uuid);
- aux = g_list_append (aux, child_widget);
- }
-
- /* Retrieve the cell widget */
- if (self->k)
- month_cell = self->month_cell[row][6 - column];
- else
- month_cell = self->month_cell[row][column];
-
- gtk_widget_get_allocation (month_cell, &cell_alloc);
-
- gtk_widget_get_preferred_height (child_widget, &minimum_height, NULL);
-
- /*
- * 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),
- self->date->year,
- self->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 (child_context),
- &margin);
-
-
- pos_x = cell_alloc.x + margin.left;
- pos_y = cell_alloc.y + margin.top;
- width = cell_alloc.width * length;
-
- remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
-
- child_allocation.x = pos_x;
- child_allocation.y = pos_y + vertical_cell_space[cell_idx] - size_left[cell_idx];
- child_allocation.width = width - (margin.left + margin.right);
- child_allocation.height = minimum_height;
-
- gtk_widget_size_allocate (child_widget, &child_allocation);
- g_hash_table_remove (self->hidden_as_overflow, uuid);
-
- /* 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;
- }
-
- g_clear_pointer (&dt_start, g_date_time_unref);
- g_clear_pointer (&dt_end, g_date_time_unref);
- }
- }
- else
- {
- gtk_widget_hide (child_widget);
- g_hash_table_add (self->hidden_as_overflow, g_strdup (uuid));
-
- for (i = first_cell; i <= last_cell; i++)
- {
- aux = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (i));
+ aux = g_hash_table_lookup (self->children, uuid);
aux = g_list_append (aux, child_widget);
+ }
- if (g_list_length (aux) == 1)
- g_hash_table_insert (self->overflow_cells, GINT_TO_POINTER (i), aux);
- else
- g_hash_table_replace (self->overflow_cells, GINT_TO_POINTER (i), g_list_copy (aux));
+ /* No space left, add to the overflow and continue */
+ if (!visible)
+ {
+ gint idx;
+
+ gtk_widget_set_child_visible (child_widget, FALSE);
+
+ 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);
+
+ if (g_list_length (aux) == 1)
+ g_hash_table_insert (self->overflow_cells, GINT_TO_POINTER (idx), aux);
+ else
+ g_hash_table_replace (self->overflow_cells, GINT_TO_POINTER (idx), g_list_copy (aux));
+ }
+
+ continue;
+ }
+
+
+ /*
+ * 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[last_block_cell / 7][last_block_cell % 7];
+ else
+ month_cell = self->month_cell[cell / 7][cell % 7];
+
+ gtk_widget_get_allocation (month_cell, &cell_alloc);
+
+ gtk_widget_set_child_visible (child_widget, TRUE);
+
+ /*
+ * 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),
+ self->date->year,
+ self->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 (child_context),
+ &margin);
+
+
+ pos_x = cell_alloc.x + margin.left;
+ pos_y = cell_alloc.y + margin.top;
+ width = cell_alloc.width * length;
+
+ remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
+
+ /*
+ * We can only get the minimum height after making all these calculations,
+ * otherwise GTK complains about allocating without calling get_preferred_height.
+ */
+ minimum_height = get_real_event_widget_height (child_widget);
+
+ child_allocation.x = pos_x;
+ 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;
+
+ gtk_widget_size_allocate (child_widget, &child_allocation);
+
+ /* update size_left */
+ for (j = 0; j < length; j++)
+ {
+ size_left[cell + j] -= minimum_height;
+ size_left[cell + j] -= margin.top + margin.bottom;
}
}
-
- g_array_free (cells, TRUE);
- g_array_free (lengths, TRUE);
}
/*
@@ -1704,72 +1771,84 @@ gcal_month_view_size_allocate (GtkWidget *widget,
while (g_hash_table_iter_next (&iter, &key, &value))
{
- j = GPOINTER_TO_INT (key);
- j += self->days_delay;
- i = 7 * ((j - 1) / 7) + 6 * self->k + sw * ((j - 1) % 7);
+ gboolean will_overflow;
+ gint cell;
+ gint day;
- if (self->k)
- month_cell = self->month_cell[i / 7][6 - i % 7];
- else
- month_cell = self->month_cell[i / 7][i % 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);
l = (GList*) value;
- for (aux = l; aux != NULL; aux = g_list_next (aux))
+ for (aux = l; aux; aux = g_list_next (aux))
{
GcalEvent *event;
+ gdouble real_height;
gdouble width;
+ gint remaining_events;
child_widget = aux->data;
+ child_context = gtk_widget_get_style_context (child_widget);
event = gcal_event_widget_get_event (aux->data);
uuid = gcal_event_get_uid (event);
- if (!gtk_widget_is_visible (child_widget) && !g_hash_table_contains (self->hidden_as_overflow, uuid))
- continue;
+ gtk_style_context_get_margin (child_context, gtk_style_context_get_state (child_context), &margin);
+ minimum_height = get_real_event_widget_height (child_widget) + margin.top + margin.bottom;
- gtk_widget_show (child_widget);
- gtk_widget_get_preferred_height (child_widget, &minimum_height, NULL);
+ /* Check for overflow */
+ remaining_events = events_at_day[cell] - allocated_events_at_day[cell];
+ will_overflow = remaining_events * minimum_height >= size_left[cell];
+ real_height = size_left[cell];
- if (size_left[i] > minimum_height)
+ if (will_overflow)
+ 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)
{
- child_context = gtk_widget_get_style_context (child_widget);
+ gtk_widget_set_child_visible (child_widget, FALSE);
- gtk_style_context_get_margin (child_context, gtk_style_context_get_state (child_context), &margin);
-
- pos_x = cell_alloc.x + margin.left;
- pos_y = cell_alloc.y + margin.top;
- width = cell_alloc.width;
-
- remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
-
- child_allocation.x = pos_x;
- child_allocation.y = pos_y + vertical_cell_space[i] - size_left[i];
- child_allocation.width = width - (margin.left + margin.right);
- child_allocation.height = minimum_height;
- gtk_widget_show (child_widget);
- gtk_widget_size_allocate (child_widget, &child_allocation);
- g_hash_table_remove (self->hidden_as_overflow, uuid);
-
- size_left[i] -= minimum_height + margin.top + margin.bottom;
- }
- else
- {
- gtk_widget_hide (child_widget);
- g_hash_table_add (self->hidden_as_overflow, g_strdup (uuid));
-
- l = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (i));
+ l = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (cell));
l = g_list_append (l, child_widget);
if (g_list_length (l) == 1)
- g_hash_table_insert (self->overflow_cells, GINT_TO_POINTER (i), l);
+ g_hash_table_insert (self->overflow_cells, GINT_TO_POINTER (cell), l);
else
- g_hash_table_replace (self->overflow_cells, GINT_TO_POINTER (i), g_list_copy (l));
+ g_hash_table_replace (self->overflow_cells, GINT_TO_POINTER (cell), g_list_copy (l));
+
+ continue;
}
+
+ allocated_events_at_day[cell]++;
+
+ gtk_widget_set_child_visible (child_widget, TRUE);
+
+ pos_x = cell_alloc.x + margin.left;
+ pos_y = cell_alloc.y + margin.top;
+ width = cell_alloc.width;
+
+ remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
+
+ child_allocation.x = pos_x;
+ 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;
+
+ gtk_widget_set_child_visible (child_widget, TRUE);
+ gtk_widget_size_allocate (child_widget, &child_allocation);
+
+ size_left[cell] -= minimum_height + margin.top + margin.bottom;
}
}
+out:
queue_update_month_cells (self);
+
+ self->pending_event_allocation = FALSE;
+
+ GCAL_EXIT;
}
static gboolean
@@ -1791,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);
@@ -1850,7 +1927,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);
}
@@ -1964,7 +2041,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);
@@ -1973,14 +2049,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
@@ -1993,21 +2068,10 @@ gcal_month_view_init (GcalMonthView *self)
self->children = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_list_free);
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->hidden_as_overflow = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->pending_event_allocation = FALSE;
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;
@@ -2018,6 +2082,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 */