From 5b23fea95f21f8729bd3cbb1a5c48f7a19363629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erick=20P=C3=A9rez=20Castellanos?= Date: Mon, 20 Aug 2012 23:45:58 -0400 Subject: [PATCH] Added create-event overlay. Added new-event mark to month-view. Added navigation arrows to month-view. Initially moved edit-event to a dialog. --- .gitignore | 3 + data/theme/gtk-styles.css | 7 +- po/POTFILES.in | 13 +- src/Makefile.am | 4 + src/gcal-arrow-container.c | 744 +++++++++++++++++++++++++++++++++++++ src/gcal-arrow-container.h | 59 +++ src/gcal-event-overlay.c | 562 ++++++++++++++++++++++++++++ src/gcal-event-overlay.h | 82 ++++ src/gcal-marshalers.list | 1 + src/gcal-month-view.c | 453 +++++++++++++++++----- src/gcal-month-view.h | 3 + src/gcal-window.c | 334 ++++++++++++----- 12 files changed, 2078 insertions(+), 187 deletions(-) create mode 100644 src/gcal-arrow-container.c create mode 100644 src/gcal-arrow-container.h create mode 100644 src/gcal-event-overlay.c create mode 100644 src/gcal-event-overlay.h diff --git a/.gitignore b/.gitignore index 6e8309e3..a2490ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,6 @@ Debug* data/org.gnome.calendar.enums.xml src/gcal-enum-types.c src/gcal-enum-types.h +src/gcal-marshalers.c +src/gcal-marshalers.h +src/stamp-gcal-marshalers.h diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css index dc70e03f..035c8bb1 100644 --- a/data/theme/gtk-styles.css +++ b/data/theme/gtk-styles.css @@ -108,10 +108,9 @@ GtkOverlay > GtkButton { .new-event-view { padding: 18px; - border-radius: 3px; - border: darkblue 1px solid; - color: @theme_fg_color; + border: darker(@borders) 1px solid; + color: @theme_fg_color; background-color: @theme_bg_color; -GcalArrowContainer-arrow-size: 12px; - -GcalArrowContainer-shadow-span: 6px; + -GcalArrowContainer-shadow-span: 3px; } diff --git a/po/POTFILES.in b/po/POTFILES.in index 4178203a..df23466c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,16 +1,17 @@ # List of source files containing translatable strings. -src/main.c src/gcal-application.c -src/gcal-window.c -src/gcal-toolbar.c -src/gcal-month-view.c -src/gcal-week-view.c -src/gcal-event-view.c src/gcal-editable-date.c src/gcal-editable-reminder.c +src/gcal-event-overlay.c +src/gcal-event-view.c src/gcal-manager.c +src/gcal-month-view.c +src/gcal-toolbar.c src/gcal-utils.c +src/gcal-week-view.c +src/gcal-window.c +src/main.c data/gnome-calendar.desktop.in.in data/org.gnome.calendar.gschema.xml.in diff --git a/src/Makefile.am b/src/Makefile.am index f38f72b8..b9a7886c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,8 @@ gnome_calendar_SOURCES = \ main.c \ gcal-application.h \ gcal-application.c \ + gcal-arrow-container.c \ + gcal-arrow-container.h \ gcal-floating-container.c \ gcal-floating-container.h \ gcal-toolbar.h \ @@ -41,6 +43,8 @@ gnome_calendar_SOURCES = \ gcal-event-widget.h \ gcal-event-view.c \ gcal-event-view.h \ + gcal-event-overlay.c \ + gcal-event-overlay.h \ gcal-editable.c \ gcal-editable.h \ gcal-editable-entry.c \ diff --git a/src/gcal-arrow-container.c b/src/gcal-arrow-container.c new file mode 100644 index 00000000..47496836 --- /dev/null +++ b/src/gcal-arrow-container.c @@ -0,0 +1,744 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * gcal-arrow-container.c + * Copyright (C) 2012 Erick Pérez Castellanos + * + * gnome-calendar 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. + * + * gnome-calendar 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 . + */ + +#include "gcal-arrow-container.h" +#include + +struct _GcalArrowContainerPrivate +{ + guint arrow_size; +}; + +static void gcal_arrow_container_get_preferred_width (GtkWidget *widget, + gint *minimum_width, + gint *natural_width); + +static void gcal_arrow_container_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum_height, + gint *natural_height); + +static void gcal_arrow_container_get_preferred_height (GtkWidget *widget, + gint *minimum_height, + gint *natural_height); + +static void gcal_arrow_container_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum_width, + gint *natural_width); + +static void gcal_arrow_container_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +static gboolean gcal_arrow_container_draw (GtkWidget *widget, + cairo_t *cr); + +static void gcal_arrow_container_draw_arrow (GtkWidget *widget, + cairo_t *cr); + +static void gcal_arrow_container_draw_shadow (GtkWidget *widget, + cairo_t *cr); + +static void gcal_arrow_container_get_padding_and_border (GtkWidget *widget, + GtkBorder *border, + guint *border_width, + guint *arrow_size, + guint *shadow); + +G_DEFINE_TYPE(GcalArrowContainer, gcal_arrow_container, GTK_TYPE_BIN) + +static void +gcal_arrow_container_class_init (GcalArrowContainerClass *klass) +{ + GtkWidgetClass* widget_class; + + widget_class = GTK_WIDGET_CLASS (klass); + + widget_class->get_preferred_width = + gcal_arrow_container_get_preferred_width; + widget_class->get_preferred_height_for_width = + gcal_arrow_container_get_preferred_height_for_width; + widget_class->get_preferred_height = + gcal_arrow_container_get_preferred_height; + widget_class->get_preferred_width_for_height = + gcal_arrow_container_get_preferred_width_for_height; + widget_class->size_allocate = gcal_arrow_container_size_allocate; + widget_class->draw = gcal_arrow_container_draw; + + /* Style properties */ + gtk_widget_class_install_style_property ( + widget_class, + g_param_spec_uint ("arrow-size", + "Arrow width and height", + "The width and height of the arrow", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE)); + + gtk_widget_class_install_style_property ( + widget_class, + g_param_spec_uint ("shadow-span", + "Shadow span", + "Shadow span", + 0, + G_MAXUINT32, + 0, + G_PARAM_READABLE)); + + g_type_class_add_private ((gpointer)klass, sizeof(GcalArrowContainerPrivate)); +} + +static void +gcal_arrow_container_init (GcalArrowContainer *self) +{ + gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GCAL_TYPE_ARROW_CONTAINER, + GcalArrowContainerPrivate); +} + +static void +gcal_arrow_container_get_preferred_width (GtkWidget *widget, + gint *minimum_width, + gint *natural_width) +{ + gint child_min, child_nat; + GtkWidget *child; + GtkBorder padding; + gint minimum, natural; + guint shadow; + + gcal_arrow_container_get_padding_and_border (widget, + &padding, + NULL, + NULL, + &shadow); + + minimum = 0; + natural = 0; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child && gtk_widget_get_visible (child)) + { + gtk_widget_get_preferred_width (child, + &child_min, &child_nat); + minimum += child_min; + natural += child_nat; + } + + minimum += padding.left + padding.right + 2 * shadow; + natural += padding.left + padding.right + 2 * shadow; + + if (minimum_width) + *minimum_width = minimum; + + if (natural_width) + *natural_width = natural; +} + +static void +gcal_arrow_container_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum_height, + gint *natural_height) +{ + gint child_min, child_nat, child_width; + GtkWidget *child; + GtkBorder padding; + guint shadow; + guint arrow_size; + gint minimum; + gint natural; + + gcal_arrow_container_get_padding_and_border (widget, + &padding, + NULL, + &arrow_size, + &shadow); + + minimum = 0; + natural = 0; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child && gtk_widget_get_visible (child)) + { + child_width = width - padding.left - padding.top - 2 * shadow; + + gtk_widget_get_preferred_height_for_width (child, child_width, + &child_min, &child_nat); + minimum = MAX (minimum, child_min); + natural = MAX (natural, child_nat); + } + + minimum += padding.top + padding.bottom + arrow_size + 2 * shadow; + natural += padding.top + padding.bottom + arrow_size + 2 * shadow; + + if (minimum_height) + *minimum_height = minimum; + + if (natural_height) + *natural_height = natural; +} + +static void +gcal_arrow_container_get_preferred_height (GtkWidget *widget, + gint *minimum_height, + gint *natural_height) +{ + gint child_min, child_nat; + GtkWidget *child; + GtkBorder padding; + guint shadow; + guint arrow_size; + gint minimum; + gint natural; + + gcal_arrow_container_get_padding_and_border (widget, + &padding, + NULL, + &arrow_size, + &shadow); + + minimum = 0; + natural = 0; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child && gtk_widget_get_visible (child)) + { + gtk_widget_get_preferred_height (child, + &child_min, &child_nat); + minimum = child_min; + natural = child_nat; + } + + minimum += padding.top + padding.bottom + arrow_size + 2 * shadow; + natural += padding.top + padding.bottom + arrow_size + 2 * shadow; + + if (minimum_height) + *minimum_height = minimum; + + if (natural_height) + *natural_height = natural; +} + +static void +gcal_arrow_container_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum_width, + gint *natural_width) +{ + gint child_min, child_nat, child_height; + GtkWidget *child; + GtkBorder padding; + guint shadow; + gint minimum; + gint natural; + + gcal_arrow_container_get_padding_and_border (widget, + &padding, + NULL, + NULL, + &shadow); + + minimum = 0; + natural = 0; + + child_height = height - padding.top - padding.bottom - 2 * shadow; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child && gtk_widget_get_visible (child)) + { + gtk_widget_get_preferred_width_for_height (child, child_height, + &child_min, &child_nat); + minimum += child_min; + natural += child_nat; + } + + minimum += padding.left + padding.right + 2 * shadow; + natural += padding.left + padding.right + 2 * shadow; + + if (minimum_width) + *minimum_width = minimum; + + if (natural_width) + *natural_width = natural; +} + +static void +gcal_arrow_container_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkAllocation child_allocation; + GtkBorder padding; + GtkWidget *child; + guint arrow_size; + guint shadow; + + gtk_widget_set_allocation (widget, allocation); + + gcal_arrow_container_get_padding_and_border (widget, + &padding, + NULL, + &arrow_size, + &shadow); + + if (gtk_widget_get_realized (widget)) + { + gdk_window_move_resize (gtk_widget_get_window (widget), + allocation->x, + allocation->y, + allocation->width, + allocation->height); + } + + child_allocation.x = padding.left + shadow; + child_allocation.y = padding.top + shadow; + child_allocation.height = + MAX (1, + allocation->height - padding.top - padding.bottom - 2 * shadow - arrow_size); + + child_allocation.width = + MAX (1, + allocation->width - padding.left - padding.right - 2 * shadow); + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child && gtk_widget_get_visible (child)) + gtk_widget_size_allocate (child, &child_allocation); +} + +static gboolean +gcal_arrow_container_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkBorder border; + guint border_width; + guint width; + guint height; + guint arrow_size; + guint shadow; + + gtk_style_context_get_border ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &border); + + gcal_arrow_container_get_padding_and_border (widget, + NULL, + &border_width, + &arrow_size, + &shadow); + + width = gtk_widget_get_allocated_width (widget) - 2 * border_width - 2 * shadow; + height = gtk_widget_get_allocated_height (widget) - 2 * border_width - arrow_size - 2 * shadow; + + gcal_arrow_container_draw_shadow (widget, cr); + + gtk_render_background (gtk_widget_get_style_context (widget), + cr, + border_width + shadow, border_width + shadow, + width, height); + gtk_render_frame_gap (gtk_widget_get_style_context (widget), + cr, + border_width + shadow, border_width + shadow, + width, height, + GTK_POS_BOTTOM, + width / 2 - arrow_size - border.bottom, + width / 2 + arrow_size + border.bottom); + + gcal_arrow_container_draw_arrow (widget, cr); + + GTK_WIDGET_CLASS (gcal_arrow_container_parent_class)->draw (widget, cr); + + return FALSE; +} + +static void +gcal_arrow_container_draw_arrow (GtkWidget *widget, + cairo_t *cr) +{ + GdkRGBA color; + GtkBorder border; + guint arrow_size; + guint border_width; + guint shadow; + guint width; + guint height; + + gtk_style_context_get_border ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &border); + + gcal_arrow_container_get_padding_and_border (widget, + NULL, + &border_width, + &arrow_size, + &shadow); + + width = gtk_widget_get_allocated_width (widget) - 2 * border_width - 2 * shadow; + height = gtk_widget_get_allocated_height (widget) - 2 * border_width - arrow_size - 2 * shadow; + + cairo_save (cr); + cairo_move_to (cr, + border_width + shadow + width / 2 - arrow_size, + border_width + shadow + height - border.bottom); + cairo_rel_line_to (cr, 0, border.bottom); + cairo_rel_line_to (cr, arrow_size, arrow_size); + cairo_rel_line_to (cr, arrow_size, - arrow_size); + cairo_rel_line_to (cr, 0, - border.bottom); + + gtk_style_context_get_background_color ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &color); + cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha); + cairo_fill (cr); + + cairo_set_line_width (cr, border.bottom); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_move_to (cr, + border_width + shadow + width / 2 - arrow_size, + border_width + shadow + height); + cairo_rel_line_to (cr, arrow_size, arrow_size); + cairo_rel_line_to (cr, arrow_size, - arrow_size); + + gtk_style_context_get_border_color ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &color); + cairo_set_source_rgb (cr, color.red, color.green, color.blue); + cairo_stroke (cr); + cairo_restore (cr); +} + +static void +gcal_arrow_container_draw_shadow (GtkWidget *widget, + cairo_t *cr) +{ + cairo_pattern_t *pattern; + gdouble inner_alpha; + gint width; + gint height; + guint arrow_size; + guint shadow_span; + guint border_width; + gdouble shadow_span_tan; + + gcal_arrow_container_get_padding_and_border (widget, + NULL, + &border_width, + &arrow_size, + &shadow_span); + + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + shadow_span_tan = shadow_span / tan (G_PI_4 / 2 + G_PI_4); + + inner_alpha = 0.8; + + cairo_save (cr); + + /* Top border */ + pattern = cairo_pattern_create_linear (0, border_width, + 0, border_width + 3 * shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, inner_alpha); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + border_width + shadow_span, border_width, + width - 2 * border_width - 2 * shadow_span, shadow_span); + cairo_fill (cr); + + /* Left border */ + pattern = cairo_pattern_create_linear (border_width, 0, + border_width + 3 * shadow_span, 0); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, inner_alpha); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + border_width, border_width + shadow_span, + shadow_span, height - 2 * border_width - 2 * shadow_span - arrow_size); + cairo_fill (cr); + + /* Right border */ + pattern = cairo_pattern_create_linear (width - (border_width + 3 * shadow_span), 0, + width - border_width, 0); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + width - (border_width + shadow_span), border_width + shadow_span, + shadow_span, height - 2 * border_width - 2 * shadow_span - arrow_size); + cairo_fill (cr); + + /* Bottom border */ + pattern = cairo_pattern_create_linear (0, height - border_width - 3 * shadow_span - arrow_size, + 0, height - border_width - arrow_size); + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + border_width + shadow_span, height - border_width - shadow_span - arrow_size, + width / 2 - arrow_size - border_width - shadow_span - shadow_span_tan, shadow_span); + cairo_rectangle (cr, + width / 2 + arrow_size + shadow_span_tan, height - border_width - shadow_span - arrow_size, + width / 2 - arrow_size - border_width - shadow_span - shadow_span_tan, shadow_span); + cairo_fill (cr); + + /* NW corner */ + pattern = cairo_pattern_create_radial ( + border_width + shadow_span, border_width + shadow_span, 0, + border_width + shadow_span, border_width + shadow_span, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + border_width, border_width, + shadow_span, shadow_span); + cairo_fill (cr); + + /* SE corner */ + pattern = cairo_pattern_create_radial ( + border_width + shadow_span, height - border_width - shadow_span - arrow_size, 0, + border_width + shadow_span, height - border_width - shadow_span - arrow_size, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + border_width, height - border_width - shadow_span - arrow_size, + shadow_span, shadow_span); + cairo_fill (cr); + + /* SW corner */ + pattern = cairo_pattern_create_radial ( + width - border_width - shadow_span, height - border_width - shadow_span - arrow_size, 0, + width - border_width - shadow_span, height - border_width - shadow_span - arrow_size, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + width - border_width - shadow_span, height - border_width - shadow_span - arrow_size, + shadow_span, shadow_span); + cairo_fill (cr); + + /* Arrow left joint */ + pattern = cairo_pattern_create_radial ( + width / 2 - arrow_size - shadow_span_tan, height - border_width - arrow_size, 0, + width / 2 - arrow_size - shadow_span_tan, height - border_width - arrow_size, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, inner_alpha / 3); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_move_to (cr, + width / 2 - arrow_size - shadow_span_tan, + height - border_width - arrow_size); + cairo_arc (cr, + width / 2 - arrow_size - shadow_span_tan, + height - border_width - arrow_size, + shadow_span + 2, + G_PI_2 + G_PI, + 2 * G_PI - G_PI_4); + + cairo_fill (cr); + + /* Arrow right joint */ + pattern = cairo_pattern_create_radial ( + width / 2 + arrow_size + shadow_span_tan, height - border_width - arrow_size, 0, + width / 2 + arrow_size + shadow_span_tan, height - border_width - arrow_size, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, inner_alpha / 3); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_move_to (cr, + width / 2 + arrow_size + shadow_span_tan, + height - border_width - arrow_size); + cairo_arc (cr, + width / 2 + arrow_size + shadow_span_tan, + height - border_width - arrow_size, + shadow_span + 2, + G_PI_4 + G_PI, + G_PI + G_PI_2); + + cairo_fill (cr); + + /* Arrow left side */ + cairo_save (cr); + cairo_translate (cr, + width / 2 - arrow_size, + height - border_width - shadow_span - arrow_size); + cairo_rotate(cr, G_PI_4); + + pattern = cairo_pattern_create_linear ( + 0, 0, + 0, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + shadow_span_tan, 0, + arrow_size * G_SQRT2 - shadow_span_tan, shadow_span); + + cairo_fill (cr); + cairo_restore (cr); + + /* Arrow right side */ + cairo_save (cr); + cairo_translate (cr, + width / 2, + height - border_width - shadow_span); + cairo_rotate (cr, - G_PI_4); + + pattern = cairo_pattern_create_linear ( + 0, 0, + 0, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + 0, + 0, + arrow_size * G_SQRT2 - shadow_span_tan, + shadow_span); + + cairo_fill (cr); + cairo_restore (cr); + + /* Arrow point */ + pattern = cairo_pattern_create_radial ( + width / 2, height - border_width - shadow_span, 0, + width / 2, height - border_width - shadow_span, shadow_span); + + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0, 0, 0, inner_alpha / 3); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0, 0, 0, 0.0); + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_move_to (cr, + width / 2, + height - border_width - shadow_span); + cairo_arc (cr, + width / 2, + height - border_width - shadow_span, + shadow_span, + G_PI_4, + G_PI_2 + G_PI_4); + cairo_fill (cr); + + cairo_restore (cr); +} + +static void +gcal_arrow_container_get_padding_and_border (GtkWidget *widget, + GtkBorder *border, + guint *border_width, + guint *arrow_size, + guint *shadow) +{ + GtkBorder tmp; + guint bd_width; + + bd_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); + if (border != NULL) + { + gtk_style_context_get_padding ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + border); + + gtk_style_context_get_border ( + gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &tmp); + + border->top += tmp.top + bd_width; + border->right += tmp.right + bd_width; + border->bottom += tmp.bottom + bd_width; + border->left += tmp.left + bd_width; + } + + if (arrow_size != NULL) + { + gtk_style_context_get_style ( + gtk_widget_get_style_context (widget), + "arrow-size", arrow_size, + NULL); + } + + if (border_width != NULL) + *border_width = bd_width; + + if (shadow != NULL) + { + gtk_style_context_get_style ( + gtk_widget_get_style_context (widget), + "shadow-span", shadow, + NULL); + } +} + +GtkWidget* +gcal_arrow_container_new (void) +{ + return g_object_new (GCAL_TYPE_ARROW_CONTAINER, NULL); +} diff --git a/src/gcal-arrow-container.h b/src/gcal-arrow-container.h new file mode 100644 index 00000000..422e5060 --- /dev/null +++ b/src/gcal-arrow-container.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * gcal-arrow-container.h + * Copyright (C) 2012 Erick Pérez Castellanos + * + * gnome-calendar 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. + * + * gnome-calendar 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_ARROW_CONTAINER_H__ +#define __GCAL_ARROW_CONTAINER_H__ + +#include + +G_BEGIN_DECLS + + +#define GCAL_TYPE_ARROW_CONTAINER (gcal_arrow_container_get_type ()) +#define GCAL_ARROW_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCAL_TYPE_ARROW_CONTAINER, GcalArrowContainer)) +#define GCAL_ARROW_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCAL_TYPE_ARROW_CONTAINER, GcalArrowContainerClass)) +#define GCAL_IS_ARROW_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCAL_TYPE_ARROW_CONTAINER)) +#define GCAL_IS_ARROW_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCAL_TYPE_ARROW_CONTAINER)) +#define GCAL_ARROW_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCAL_TYPE_ARROW_CONTAINER, GcalArrowContainerClass)) + +typedef struct _GcalArrowContainer GcalArrowContainer; +typedef struct _GcalArrowContainerClass GcalArrowContainerClass; +typedef struct _GcalArrowContainerPrivate GcalArrowContainerPrivate; + +struct _GcalArrowContainer +{ + GtkBin parent; + /* add your public declarations here */ + + GcalArrowContainerPrivate *priv; +}; + +struct _GcalArrowContainerClass +{ + GtkBinClass parent_class; +}; + + +GType gcal_arrow_container_get_type (void); +GtkWidget* gcal_arrow_container_new (void); + + +G_END_DECLS + +#endif /* __GCAL_ARROW_CONTAINER_H__ */ diff --git a/src/gcal-event-overlay.c b/src/gcal-event-overlay.c new file mode 100644 index 00000000..51efa29e --- /dev/null +++ b/src/gcal-event-overlay.c @@ -0,0 +1,562 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * gcal-event-overlay.c + * Copyright (C) 2012 Erick Pérez Castellanos + * + * gnome-calendar 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. + * + * gnome-calendar 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 . + */ + +#include "gcal-event-overlay.h" +#include "gcal-arrow-container.h" +#include "gcal-utils.h" +#include "gcal-marshalers.h" + +#include +#include + +#define CLOSE_IMAGE_FILE UI_DATA_DIR "/close-window.svg" + +struct _GcalEventOverlayPrivate +{ + GtkWidget *container; + GtkWidget *title_label; + GtkWidget *what_entry; + GtkWidget *calendar_button; + + GtkListStore *sources_model; + GtkTreeIter *active_iter; + + icaltimetype *start_span; + icaltimetype *end_span; +}; + +enum +{ + CANCELLED = 1, + CREATED, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS] = { 0, }; + +static void gcal_event_overlay_constructed (GObject *object); + +static void gcal_event_overlay_set_calendars_menu (GcalEventOverlay *widget); + +static gboolean gcal_event_overlay_gather_data (GcalEventOverlay *widget, + GcalNewEventData **new_data); + +static void gcal_event_overlay_create_button_clicked (GtkWidget *widget, + gpointer user_data); + +static void gcal_event_overlay_close_button_clicked (GtkWidget *widget, + gpointer user_data); + +static void gcal_event_overlay_calendar_selected (GtkWidget *menu_item, + gpointer user_data); + +static GdkPixbuf* gcal_event_overlay_get_pixbuf_for_color (GdkColor *color); + +G_DEFINE_TYPE(GcalEventOverlay, gcal_event_overlay, GTK_CLUTTER_TYPE_ACTOR) + +static void +gcal_event_overlay_class_init (GcalEventOverlayClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->constructed = gcal_event_overlay_constructed; + + signals[CANCELLED] = g_signal_new ("cancelled", + GCAL_TYPE_EVENT_OVERLAY, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GcalEventOverlayClass, + cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + signals[CREATED] = g_signal_new ("created", + GCAL_TYPE_EVENT_OVERLAY, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GcalEventOverlayClass, + created), + NULL, NULL, + _gcal_marshal_VOID__POINTER_BOOLEAN, + G_TYPE_NONE, + 2, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); + + g_type_class_add_private ((gpointer)klass, sizeof(GcalEventOverlayPrivate)); +} + +static void +gcal_event_overlay_init (GcalEventOverlay *self) +{ + GcalEventOverlayPrivate *priv; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GCAL_TYPE_EVENT_OVERLAY, + GcalEventOverlayPrivate); + priv = self->priv; + + priv->active_iter = NULL; + priv->start_span = NULL; + priv->end_span = NULL; +} + +static void +gcal_event_overlay_constructed (GObject* object) +{ + GcalEventOverlayPrivate *priv; + GtkWidget *main_grid; + GtkWidget *row_grid; + GtkWidget *overlay; + GtkWidget *create_button; + GtkWidget *details_button; + GtkWidget *close_image; + GtkWidget *close_button; + GtkWidget *calendar_image; + + priv = GCAL_EVENT_OVERLAY (object)->priv; + + /* chaining up */ + G_OBJECT_CLASS (gcal_event_overlay_parent_class)->constructed (object); + + overlay = gtk_overlay_new (); + + priv->container = gcal_arrow_container_new (); + gtk_container_set_border_width (GTK_CONTAINER (priv->container), 14); + gtk_style_context_add_class ( + gtk_widget_get_style_context (priv->container), + "new-event-view"); + gtk_container_add (GTK_CONTAINER (overlay), priv->container); + + main_grid = gtk_grid_new (); + g_object_set (main_grid, + "row-spacing", 18, + "column-spacing", 12, + "column-homogeneous", TRUE, + NULL); + + priv->title_label = gtk_label_new (_("New Event")); + gtk_grid_attach (GTK_GRID (main_grid), + priv->title_label, + 0, 0, + 2, 1); + row_grid = gtk_grid_new (); + gtk_grid_attach (GTK_GRID (main_grid), + row_grid, + 0, 1, + 2, 1); + create_button = gtk_button_new_with_label (_("Create")); + g_object_set_data (G_OBJECT (create_button), "open-details", GUINT_TO_POINTER (0)); + gtk_style_context_add_class ( + gtk_widget_get_style_context (create_button), + "suggested-action"); + gtk_grid_attach (GTK_GRID (main_grid), + create_button, + 0, 2, + 1, 1); + details_button = gtk_button_new_with_label (_("More details")); + g_object_set_data (G_OBJECT (details_button), "open-details", GUINT_TO_POINTER (1)); + gtk_grid_attach (GTK_GRID (main_grid), + details_button, + 1, 2, + 1, 1); + + g_object_set (row_grid, + "orientation", GTK_ORIENTATION_HORIZONTAL, + "column-spacing", 6, + NULL); + priv->what_entry = gtk_entry_new (); + g_object_set_data (G_OBJECT (priv->what_entry), "open-details", GUINT_TO_POINTER (0)); + gtk_entry_set_placeholder_text (GTK_ENTRY (priv->what_entry), + _("What (e.g. Alien Invasion)")); + g_object_set (priv->what_entry, + "valign", GTK_ALIGN_CENTER, + "expand", TRUE, + NULL); + gtk_container_add (GTK_CONTAINER (row_grid), + priv->what_entry); + + priv->calendar_button = gtk_menu_button_new (); + calendar_image = gtk_image_new_from_icon_name ("x-office-calendar-symbolic", + GTK_ICON_SIZE_MENU); + g_object_set (priv->calendar_button, + "vexpand", TRUE, + "always-show-image", TRUE, + "image", calendar_image, + "menu", gtk_menu_new (), + NULL); + + gtk_container_add (GTK_CONTAINER (row_grid), + priv->calendar_button); + + gtk_container_add (GTK_CONTAINER (priv->container), + main_grid); + + close_button = gtk_button_new (); + close_image = gtk_image_new_from_file (CLOSE_IMAGE_FILE); + gtk_container_add (GTK_CONTAINER (close_button), close_image); + g_object_set (close_button, + "relief", GTK_RELIEF_NONE, + "focus-on-click", FALSE, + "halign", GTK_ALIGN_END, + "valign", GTK_ALIGN_START, + NULL); + + gtk_overlay_add_overlay (GTK_OVERLAY (overlay), close_button); + + gtk_widget_show_all (overlay); + gtk_container_add ( + GTK_CONTAINER (gtk_clutter_actor_get_widget (GTK_CLUTTER_ACTOR (object))), + overlay); + + /* Hooking signals */ + g_signal_connect (priv->what_entry, + "activate", + G_CALLBACK (gcal_event_overlay_create_button_clicked), + object); + + g_signal_connect (create_button, + "clicked", + G_CALLBACK (gcal_event_overlay_create_button_clicked), + object); + + g_signal_connect (details_button, + "clicked", + G_CALLBACK (gcal_event_overlay_create_button_clicked), + object); + + g_signal_connect (close_button, + "clicked", + G_CALLBACK (gcal_event_overlay_close_button_clicked), + object); +} + +static void +gcal_event_overlay_set_calendars_menu (GcalEventOverlay *widget) +{ + GcalEventOverlayPrivate *priv; + GtkMenu *menu; + GtkWidget *item; + GList *children; + + gboolean valid; + GtkTreeIter iter; + + priv = widget->priv; + + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->sources_model), + &iter); + if (! valid) + return; + + menu = gtk_menu_button_get_menu (GTK_MENU_BUTTON (priv->calendar_button)); + children = gtk_container_get_children (GTK_CONTAINER (menu)); + g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL); + + while (valid) + { + /* Walk through the list, reading each row */ + gchar *name; + GdkColor *color; + GtkWidget *cal_image; + GdkPixbuf *pix; + + gtk_tree_model_get (GTK_TREE_MODEL (priv->sources_model), &iter, + 1, &name, + 3, &color, + -1); + + item = gtk_image_menu_item_new (); + g_object_set_data_full (G_OBJECT (item), + "sources-iter", + gtk_tree_iter_copy (&iter), + (GDestroyNotify) gtk_tree_iter_free); + g_signal_connect (item, + "activate", + G_CALLBACK (gcal_event_overlay_calendar_selected), + widget); + + pix = gcal_event_overlay_get_pixbuf_for_color (color); + + cal_image = gtk_image_new_from_pixbuf (pix); + g_object_set (item, + "always-show-image", TRUE, + "image", cal_image, + "label", name, + NULL); + + gtk_container_add (GTK_CONTAINER (menu), item); + + g_object_unref (pix); + g_free (name); + gdk_color_free (color); + + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->sources_model), + &iter); + } + + gtk_widget_show_all (GTK_WIDGET (menu)); +/* + if (! had_menu) + { + gtk_menu_button_set_menu (GTK_MENU_BUTTON (priv->calendar_button), + menu); + }*/ +} + +static gboolean +gcal_event_overlay_gather_data (GcalEventOverlay *widget, + GcalNewEventData **new_data) +{ + GcalEventOverlayPrivate *priv; + GcalNewEventData *data; + gchar *uid; + + priv = widget->priv; + if (priv->active_iter == NULL) + return FALSE; + if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (priv->what_entry)), "") == 0) + return FALSE; + + data = g_new0 (GcalNewEventData, 1); + data->summary = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->what_entry))); + gtk_tree_model_get (GTK_TREE_MODEL (priv->sources_model), priv->active_iter, + 0, &uid, + -1); + data->calendar_uid = uid; + data->start_date = priv->start_span; + data->end_date = priv->end_span; + + *new_data = data; + return TRUE; +} + +static void +gcal_event_overlay_create_button_clicked (GtkWidget *widget, + gpointer user_data) +{ + GcalNewEventData *data; + guint open_details; + + open_details = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), + "open-details")); + + /* Maybe some kind of notification */ + if (! gcal_event_overlay_gather_data (GCAL_EVENT_OVERLAY (user_data), &data)) + return; + + g_signal_emit (GCAL_EVENT_OVERLAY (user_data), + signals[CREATED], + 0, + data, open_details == 1); + + g_free (data->calendar_uid); + g_free (data->summary); + g_free (data); +} + +static void +gcal_event_overlay_close_button_clicked (GtkWidget *widget, + gpointer user_data) +{ + GcalEventOverlay *self; + self = GCAL_EVENT_OVERLAY (user_data); + + g_signal_emit (self, signals[CANCELLED], 0); +} + +static void +gcal_event_overlay_calendar_selected (GtkWidget *menu_item, + gpointer user_data) +{ + GcalEventOverlayPrivate *priv; + GtkTreeIter *iter; + GdkColor *color; + + GdkPixbuf *pix; + GtkWidget *cal_image; + + priv = GCAL_EVENT_OVERLAY (user_data)->priv; + iter = g_object_get_data (G_OBJECT (menu_item), "sources-iter"); + + gtk_tree_model_get (GTK_TREE_MODEL (priv->sources_model), iter, + 3, &color, + -1); + + if (priv->active_iter != NULL) + gtk_tree_iter_free (priv->active_iter); + priv->active_iter = gtk_tree_iter_copy (iter); + + pix = gcal_event_overlay_get_pixbuf_for_color (color); + cal_image = gtk_image_new_from_pixbuf (pix); + gtk_button_set_image (GTK_BUTTON (priv->calendar_button), cal_image); + + gdk_color_free (color); + g_object_unref (pix); +} + +static GdkPixbuf* +gcal_event_overlay_get_pixbuf_for_color (GdkColor *color) +{ + cairo_surface_t *surface; + cairo_t *cr; + gint width, height; + GdkPixbuf *pix; + + /* TODO: review size here, maybe not hardcoded */ + width = height = 10; + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create (surface); + + cairo_set_source_rgb (cr, + color->red / 65535.0, + color->green / 65535.0, + color->blue / 65535.0); + cairo_rectangle (cr, 0, 0, width, height); + cairo_fill (cr); + cairo_destroy (cr); + pix = gdk_pixbuf_get_from_surface (surface, + 0, 0, + width, height); + cairo_surface_destroy (surface); + return pix; +} + +/* Public API */ + +ClutterActor* +gcal_event_overlay_new (void) +{ + return g_object_new (GCAL_TYPE_EVENT_OVERLAY, NULL); +} + +void +gcal_event_overlay_reset (GcalEventOverlay *widget) +{ + GcalEventOverlayPrivate *priv; + + g_return_if_fail (GCAL_IS_EVENT_OVERLAY (widget)); + priv = widget->priv; + + if (priv->active_iter != NULL) + { + gtk_tree_iter_free (priv->active_iter); + priv->active_iter = NULL; + } + if (priv->start_span != NULL) + { + g_free (priv->start_span); + priv->start_span = NULL; + } + if (priv->end_span != NULL) + { + g_free (priv->end_span); + priv->end_span = NULL; + } + + gtk_label_set_text (GTK_LABEL (priv->title_label), _("New Event")); + gtk_entry_set_text (GTK_ENTRY (priv->what_entry), ""); + + gtk_button_set_image (GTK_BUTTON (priv->calendar_button), + gtk_image_new_from_icon_name ("x-office-calendar-symbolic", + GTK_ICON_SIZE_MENU)); +} + +/** + * gcal_event_overlay_set_span: + * + * @widget: self + * @start_date: The initial date + * @end_date: The end date or NULL if the date is the same as the initial + */ +void +gcal_event_overlay_set_span (GcalEventOverlay *widget, + icaltimetype *start_date, + icaltimetype *end_date) +{ + GcalEventOverlayPrivate *priv; + struct tm tm_date; + gchar start[64]; + gchar end[64]; + gchar *title_date; + icaltimetype *real_end_date; + + g_return_if_fail (GCAL_IS_EVENT_OVERLAY (widget)); + priv = widget->priv; + + if (priv->start_span == NULL) + priv->start_span = gcal_dup_icaltime (start_date); + else + *(priv->start_span) = *start_date; + + real_end_date = end_date == NULL ? start_date : end_date; + if (priv->end_span == NULL) + priv->end_span = gcal_dup_icaltime (real_end_date); + else + *(priv->end_span) = *real_end_date; + + /* Updating labels */ + /* TODO: add as many posibles fixes for cases here, since this is when we + * format the dates for the label */ + tm_date = icaltimetype_to_tm (start_date); + e_utf8_strftime_fix_am_pm (start, 64, "%B %d", &tm_date); + + if (end_date == NULL) + { + title_date = g_strdup_printf (_("New Event on %s"), start); + } + else + { + tm_date = icaltimetype_to_tm (real_end_date); + + /* Fix 1: Not repeating the month */ + if (start_date->month == end_date->month && + start_date->year == end_date->year) + { + e_utf8_strftime_fix_am_pm (end, 64, "%d", &tm_date); + } + else + { + e_utf8_strftime_fix_am_pm (end, 64, "%B %d", &tm_date); + } + + title_date = g_strdup_printf (_("New Event from %s to %s"), + start, + end); + } + + gtk_label_set_text (GTK_LABEL (priv->title_label), title_date); + g_free (title_date); +} + +void +gcal_event_overlay_set_sources_model (GcalEventOverlay *widget, + GtkListStore *sources_model) +{ + GcalEventOverlayPrivate *priv; + + g_return_if_fail (GCAL_IS_EVENT_OVERLAY (widget)); + g_return_if_fail (GTK_IS_LIST_STORE (sources_model)); + priv = widget->priv; + + priv->sources_model = sources_model; + + gcal_event_overlay_set_calendars_menu (widget); +} diff --git a/src/gcal-event-overlay.h b/src/gcal-event-overlay.h new file mode 100644 index 00000000..5577bfe8 --- /dev/null +++ b/src/gcal-event-overlay.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * gcal-event-overlay.h + * Copyright (C) 2012 Erick Pérez Castellanos + * + * gnome-calendar 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. + * + * gnome-calendar 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_EVENT_OVERLAY_H__ +#define __GCAL_EVENT_OVERLAY_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GcalNewEventData GcalNewEventData; + +struct _GcalNewEventData +{ + icaltimetype *start_date; + icaltimetype *end_date; + gchar *calendar_uid; + gchar *summary; +}; + +#define GCAL_TYPE_EVENT_OVERLAY (gcal_event_overlay_get_type ()) +#define GCAL_EVENT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCAL_TYPE_EVENT_OVERLAY, GcalEventOverlay)) +#define GCAL_EVENT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCAL_TYPE_EVENT_OVERLAY, GcalEventOverlayClass)) +#define GCAL_IS_EVENT_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCAL_TYPE_EVENT_OVERLAY)) +#define GCAL_IS_EVENT_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCAL_TYPE_EVENT_OVERLAY)) +#define GCAL_EVENT_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCAL_TYPE_EVENT_OVERLAY, GcalEventOverlayClass)) + +typedef struct _GcalEventOverlay GcalEventOverlay; +typedef struct _GcalEventOverlayClass GcalEventOverlayClass; +typedef struct _GcalEventOverlayPrivate GcalEventOverlayPrivate; + +struct _GcalEventOverlay +{ + GtkClutterActor parent; + + GcalEventOverlayPrivate *priv; +}; + +struct _GcalEventOverlayClass +{ + GtkClutterActorClass parent_class; + + /* Signals */ + void (*cancelled) (GcalEventOverlay *widget); + void (*created) (GcalEventOverlay *widget, GcalNewEventData *new_data, gboolean open_details); +}; + + +GType gcal_event_overlay_get_type (void); + +ClutterActor* gcal_event_overlay_new (void); + +void gcal_event_overlay_reset (GcalEventOverlay *widget); + +void gcal_event_overlay_set_span (GcalEventOverlay *widget, + icaltimetype *start_date, + icaltimetype *end_date); + +void gcal_event_overlay_set_sources_model (GcalEventOverlay *widget, + GtkListStore *sources_model); + + +G_END_DECLS + +#endif /* __GCAL_EVENT_OVERLAY_H__ */ diff --git a/src/gcal-marshalers.list b/src/gcal-marshalers.list index d205cadf..808c9531 100644 --- a/src/gcal-marshalers.list +++ b/src/gcal-marshalers.list @@ -1 +1,2 @@ VOID:POINTER,POINTER,DOUBLE,DOUBLE +VOID:POINTER,BOOLEAN diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index 0fda484d..91d93331 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -27,12 +27,6 @@ #include -enum -{ - PROP_0, - PROP_DATE //active-date inherited property -}; - struct _GcalViewChild { GtkWidget *widget; @@ -55,12 +49,26 @@ struct _GcalMonthViewPrivate gint clicked_cell; - gint selected_cell; + gint start_mark_cell; + gint end_mark_cell; + gint actual_day_cell; gint days_delay; /* property */ icaltimetype *date; + + /* helpers */ + GdkRectangle *prev_rectangle; + GdkRectangle *next_rectangle; + gboolean clicked_prev; + gboolean clicked_next; +}; + +enum +{ + PROP_0, + PROP_DATE //active-date inherited property }; static void gcal_view_interface_init (GcalViewIface *iface); @@ -94,6 +102,9 @@ static gboolean gcal_month_view_draw (GtkWidget *wid static gboolean gcal_month_view_button_press (GtkWidget *widget, GdkEventButton *event); +static gboolean gcal_month_view_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event); + static gboolean gcal_month_view_button_release (GtkWidget *widget, GdkEventButton *event); @@ -136,6 +147,8 @@ static void gcal_month_view_remove_by_uuid (GcalView *vie static GtkWidget* gcal_month_view_get_by_uuid (GcalView *view, const gchar *uuid); +static void gcal_month_view_clear_selection (GcalView *view); + G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view, GTK_TYPE_CONTAINER, @@ -163,6 +176,7 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) widget_class->size_allocate = gcal_month_view_size_allocate; widget_class->draw = gcal_month_view_draw; widget_class->button_press_event = gcal_month_view_button_press; + widget_class->motion_notify_event = gcal_month_view_motion_notify_event; widget_class->button_release_event = gcal_month_view_button_release; object_class = G_OBJECT_CLASS (klass); @@ -190,6 +204,17 @@ gcal_month_view_init (GcalMonthView *self) GcalMonthViewPrivate); priv = self->priv; + priv->clicked_cell = -1; + + priv->start_mark_cell = -1; + priv->end_mark_cell = -1; + + priv->prev_rectangle = NULL; + priv->next_rectangle = NULL; + + priv->clicked_prev = FALSE; + priv->clicked_next = FALSE; + for (i = 0; i < 35; i++) { priv->days[i] = NULL; @@ -209,6 +234,8 @@ gcal_view_interface_init (GcalViewIface *iface) iface->contains = gcal_month_view_contains; iface->remove_by_uuid = gcal_month_view_remove_by_uuid; iface->get_by_uuid = gcal_month_view_get_by_uuid; + + iface->clear_selection = gcal_month_view_clear_selection; } static void @@ -261,6 +288,11 @@ gcal_month_view_finalize (GObject *object) if (priv->date != NULL) g_free (priv->date); + if (priv->prev_rectangle != NULL) + g_free (priv->prev_rectangle); + if (priv->next_rectangle != NULL) + g_free (priv->next_rectangle); + /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (gcal_month_view_parent_class)->finalize (object); } @@ -468,38 +500,97 @@ gcal_month_view_button_press (GtkWidget *widget, GdkEventButton *event) { GcalMonthViewPrivate *priv; - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding; - gint x, y; + gdouble x, y; gint width, height; gdouble start_grid_y; priv = GCAL_MONTH_VIEW (widget)->priv; - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - gtk_style_context_get_padding (context, state, &padding); x = event->x; y = event->y; - if (event->window != priv->event_window) - { - gpointer source_widget; - gdk_window_get_user_data (event->window, &source_widget); - gtk_widget_translate_coordinates (GTK_WIDGET (source_widget), - widget, - event->x, - event->y, - &x, - &y); - } - //FIXME calculations for clicks, if needed width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); start_grid_y = gcal_month_view_get_start_grid_y (widget); + + if (y - start_grid_y < 0) + { + if (priv->prev_rectangle->x < x && + x < priv->prev_rectangle->x + priv->prev_rectangle->width && + priv->prev_rectangle->y < y && + y < priv->prev_rectangle->y + priv->prev_rectangle->height) + { + priv->clicked_prev = TRUE; + } + else if (priv->next_rectangle->x < x && + x < priv->next_rectangle->x + priv->next_rectangle->width && + priv->next_rectangle->y < y && + y < priv->next_rectangle->y + priv->next_rectangle->height) + { + priv->clicked_next = TRUE; + } + + return TRUE; + } + y = y - start_grid_y; + priv->clicked_cell = 7 * ( (gint ) ( y / ((height - start_grid_y) / 5) )) + ((gint) ( x / (width / 7) )); + priv->start_mark_cell = priv->clicked_cell; + + if (event->type == GDK_2BUTTON_PRESS) + { + icaltimetype *start_date; + + priv->end_mark_cell = priv->start_mark_cell; + x = (width / 7) * (( priv->start_mark_cell % 7) + 0.5); + y = start_grid_y + ((height - start_grid_y) / 5) * (( priv->start_mark_cell / 7) + 0.5); + + gtk_widget_queue_draw (widget); + + start_date = gcal_dup_icaltime (priv->date); + start_date->day = priv->start_mark_cell + priv->days_delay; + + g_signal_emit_by_name (GCAL_VIEW (widget), + "create-event", + start_date, NULL, + x, y); + g_free (start_date); + } + + return TRUE; +} + +static gboolean +gcal_month_view_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + GcalMonthViewPrivate *priv; + gint width, height; + gint y; + gdouble start_grid_y; + + priv = GCAL_MONTH_VIEW (widget)->priv; + + if (priv->clicked_cell == -1) + return FALSE; + + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + start_grid_y = gcal_month_view_get_start_grid_y (widget); + + y = event->y - start_grid_y; + if (y < 0) + return FALSE; + + priv->end_mark_cell = 7 * ( (gint ) ( y / ((height - start_grid_y) / 5) )) + ((gint) ( event->x / (width / 7) )); + if (priv->end_mark_cell == priv->start_mark_cell) + { + priv->end_mark_cell = -1; + return FALSE; + } + + gtk_widget_queue_draw (widget); return TRUE; } @@ -509,54 +600,93 @@ gcal_month_view_button_release (GtkWidget *widget, GdkEventButton *event) { GcalMonthViewPrivate *priv; - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding; - gint x, y; + gdouble x, y; gint width, height; gdouble start_grid_y; gint released; - icaltimetype *new_date; - priv = GCAL_MONTH_VIEW (widget)->priv; - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - - gtk_style_context_get_padding (context, state, &padding); x = event->x; y = event->y; - if (event->window != priv->event_window) - { - gpointer source_widget; - gdk_window_get_user_data (event->window, &source_widget); - gtk_widget_translate_coordinates (GTK_WIDGET (source_widget), - widget, - event->x, - event->y, - &x, - &y); - } - //FIXME calculations for clicks, if needed width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); start_grid_y = gcal_month_view_get_start_grid_y (widget); + + if (y - start_grid_y < 0) + { + if (priv->prev_rectangle->x < x && + x < priv->prev_rectangle->x + priv->prev_rectangle->width && + priv->prev_rectangle->y < y && + y < priv->prev_rectangle->y + priv->prev_rectangle->height && + priv->clicked_prev) + { + icaltimetype *prev_month; + prev_month = gcal_view_get_date (GCAL_VIEW (widget)); + icaltime_adjust (prev_month, + - icaltime_days_in_month (prev_month->month, + prev_month->year), + 0, 0, 0); + + g_signal_emit_by_name (GCAL_VIEW (widget), "updated", prev_month); + + g_free (prev_month); + } + else if (priv->next_rectangle->x < x && + x < priv->next_rectangle->x + priv->next_rectangle->width && + priv->next_rectangle->y < y && + y < priv->next_rectangle->y + priv->next_rectangle->height && + priv->clicked_next) + { + icaltimetype *next_month; + next_month = gcal_view_get_date (GCAL_VIEW (widget)); + + icaltime_adjust (next_month, + icaltime_days_in_month (next_month->month, + next_month->year), + 0, 0, 0); + + g_signal_emit_by_name (GCAL_VIEW (widget), "updated", next_month); + + g_free (next_month); + } + + priv->clicked_cell = -1; + priv->clicked_prev = FALSE; + priv->clicked_next = FALSE; + return TRUE; + } + y = y - start_grid_y; + released = 7 * ( (gint ) ( y / ((height - start_grid_y) / 5) )) + ((gint) ( x / (width / 7) )); - if (priv->clicked_cell == released) + if (priv->clicked_cell != released) { - priv->selected_cell = priv->clicked_cell; - priv->clicked_cell = -1; - gtk_widget_queue_resize (widget); + icaltimetype *start_date; + icaltimetype *end_date; - new_date = gcal_dup_icaltime (priv->date); - new_date->day = priv->selected_cell + priv->days_delay; - gcal_view_set_date (GCAL_VIEW (widget), new_date); - g_free (new_date); + x = (width / 7) * (( priv->end_mark_cell % 7) + 0.5); + y = start_grid_y + ((height - start_grid_y) / 5) * (( priv->end_mark_cell / 7) + 0.5); + + start_date = gcal_dup_icaltime (priv->date); + start_date->day = priv->start_mark_cell + priv->days_delay; + + end_date = gcal_dup_icaltime (priv->date); + end_date->day = priv->end_mark_cell + priv->days_delay; + + g_signal_emit_by_name (GCAL_VIEW (widget), + "create-event", + start_date, end_date, + x, y); + g_free (start_date); + g_free (end_date); } + + priv->clicked_cell = -1; + priv->clicked_prev = FALSE; + priv->clicked_next = FALSE; return TRUE; } @@ -610,28 +740,28 @@ gcal_month_view_remove (GtkContainer *container, GtkWidget *widget) { GcalMonthViewPrivate *priv; - icaltimetype *date; - gint day; + gint i; GList *l; g_return_if_fail (GCAL_IS_MONTH_VIEW (container)); g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (container)); priv = GCAL_MONTH_VIEW (container)->priv; - date = gcal_event_widget_get_date (GCAL_EVENT_WIDGET (widget)); - day = date->day + ( - priv->days_delay); - g_free (date); - - for (l = priv->days[day]; l != NULL; l = l->next) + for (i = 0; i < 35; i++) { - GcalViewChild *child; - - child = (GcalViewChild*) l->data; - if (child->widget == widget) + for (l = priv->days[i]; l != NULL; l = l->next) { - priv->days[day] = g_list_remove (priv->days[day], child); - g_free (child); - break; + GcalViewChild *child; + + child = (GcalViewChild*) l->data; + if (child->widget == widget) + { + priv->days[i] = g_list_remove (priv->days[i], child); + g_free (child); + + i = 36; + break; + } } } gtk_widget_unparent (widget); @@ -690,13 +820,13 @@ gcal_month_view_set_date (GcalMonthView *view, g_free (first_of_month); /* if have_selection: yes this one have */ - priv->selected_cell = priv->date->day - priv->days_delay; + priv->actual_day_cell = priv->date->day - priv->days_delay; if (will_resize) { to_remove = NULL; - for (i = 0; i < 34; i++) + for (i = 0; i < 35; i++) { for (l = priv->days[i]; l != NULL; l = l->next) { @@ -713,6 +843,7 @@ gcal_month_view_set_date (GcalMonthView *view, g_list_foreach (to_remove, (GFunc) gtk_widget_destroy, NULL); gtk_widget_queue_resize (GTK_WIDGET (view)); + gtk_widget_queue_draw (GTK_WIDGET (view)); } } @@ -726,9 +857,12 @@ gcal_month_view_draw_header (GcalMonthView *view, GtkStyleContext *context; GtkStateFlags state; GdkRGBA color; + GtkBorder header_padding; PangoLayout *layout; gint layout_width; + gint header_width; + gint layout_height; gchar *left_header; gchar *right_header; @@ -736,6 +870,9 @@ gcal_month_view_draw_header (GcalMonthView *view, struct tm tm_date; + GtkIconTheme *icon_theme; + GdkPixbuf *pixbuf; + g_return_if_fail (GCAL_IS_MONTH_VIEW (view)); priv = view->priv; @@ -745,6 +882,8 @@ gcal_month_view_draw_header (GcalMonthView *view, gtk_style_context_save (context); gtk_style_context_add_region (context, "header", 0); + gtk_style_context_get_padding (context, state, &header_padding); + gtk_style_context_get_color (context, state, &color); cairo_set_source_rgb (cr, color.red, color.green, color.blue); @@ -752,9 +891,14 @@ gcal_month_view_draw_header (GcalMonthView *view, pango_layout_set_font_description (layout, gtk_style_context_get_font (context, state)); - gtk_style_context_remove_region (context, "header"); gtk_style_context_restore (context); + /* Here translators should put the widgest letter in their alphabet, this + * taken to make it align with week-view header */ + pango_layout_set_text (layout, _("WWW 99 - WWW 99"), -1); + pango_cairo_update_layout (cr, layout); + pango_layout_get_pixel_size (layout, &layout_width, &layout_height); + tm_date = icaltimetype_to_tm (priv->date); e_utf8_strftime_fix_am_pm (str_date, 64, "%B", &tm_date); @@ -763,10 +907,56 @@ gcal_month_view_draw_header (GcalMonthView *view, pango_layout_set_text (layout, left_header, -1); pango_cairo_update_layout (cr, layout); + pango_layout_get_pixel_size (layout, &header_width, NULL); - cairo_move_to (cr, alloc->x + padding->left, alloc->y + padding->top); + cairo_move_to (cr, + alloc->x + header_padding.left + ((layout_width - header_width) / 2), + alloc->y + header_padding.top); pango_cairo_show_layout (cr, layout); + /* Drawing arrows */ + icon_theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "go-previous-symbolic", + layout_height, + 0, + NULL); + + gdk_cairo_set_source_pixbuf (cr, + pixbuf, + alloc->x + layout_width + 2 * header_padding.left, + alloc->y + header_padding.top); + g_object_unref (pixbuf); + cairo_paint (cr); + + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "go-next-symbolic", + layout_height, + 0, + NULL); + + gdk_cairo_set_source_pixbuf (cr, + pixbuf, + alloc->x + layout_width + 2 * header_padding.left + layout_height, + alloc->y + header_padding.top); + g_object_unref (pixbuf); + cairo_paint (cr); + + /* allocating rects */ + if (priv->prev_rectangle == NULL) + priv->prev_rectangle = g_new0 (GdkRectangle, 1); + priv->prev_rectangle->x = alloc->x + layout_width + 2 * header_padding.left; + priv->prev_rectangle->y = alloc->y + header_padding.top; + priv->prev_rectangle->width = layout_height; + priv->prev_rectangle->height = layout_height; + + if (priv->next_rectangle == NULL) + priv->next_rectangle = g_new0 (GdkRectangle, 1); + priv->next_rectangle->x = alloc->x + layout_width + 2 * header_padding.left + layout_height; + priv->next_rectangle->y = alloc->y + header_padding.top; + priv->next_rectangle->width = layout_height; + priv->next_rectangle->height = layout_height; + gtk_style_context_get_color (context, state | GTK_STATE_FLAG_INSENSITIVE, &color); @@ -777,8 +967,8 @@ gcal_month_view_draw_header (GcalMonthView *view, pango_layout_get_pixel_size (layout, &layout_width, NULL); cairo_move_to (cr, - alloc->width - padding->right - layout_width, - alloc->y + padding->top); + alloc->width - header_padding.right - layout_width, + alloc->y + header_padding.top); pango_cairo_show_layout (cr, layout); g_free (left_header); @@ -800,6 +990,8 @@ gcal_month_view_draw_grid (GcalMonthView *view, GdkRGBA color; GdkRGBA ligther_color; GdkRGBA selected_color; + GdkRGBA background_selected_color; + GtkBorder header_padding; gint i, j; gint font_width; @@ -829,6 +1021,10 @@ gcal_month_view_draw_grid (GcalMonthView *view, &selected_color); selected_font = gtk_style_context_get_font (context, state); + gtk_style_context_get_background_color (context, + state | GTK_STATE_FLAG_SELECTED, + &background_selected_color); + gtk_style_context_get_color (context, state | GTK_STATE_FLAG_INSENSITIVE, &ligther_color); @@ -841,6 +1037,69 @@ gcal_month_view_draw_grid (GcalMonthView *view, n_days_in_month = icaltime_days_in_month (priv->date->month, priv->date->year); + gtk_style_context_save (context); + gtk_style_context_add_region (context, "header", 0); + + gtk_style_context_get_padding (context, state, &header_padding); + + gtk_style_context_restore (context); + + /* drawing new-event mark */ + if (priv->start_mark_cell != -1 && + priv->end_mark_cell != -1) + { + gint first_cell; + gint last_cell; + gint rows; + + cairo_save (cr); + if (priv->start_mark_cell < priv->end_mark_cell) + { + first_cell = priv->start_mark_cell; + last_cell = priv->end_mark_cell; + } + else + { + first_cell = priv->end_mark_cell; + last_cell = priv->start_mark_cell; + } + + cairo_set_source_rgba (cr, + background_selected_color.red, + background_selected_color.green, + background_selected_color.blue, + background_selected_color.alpha); + + for (rows = 0; rows < (last_cell / 7) - (first_cell / 7) + 1; rows++) + { + gint first_point; + gint last_point; + + first_point = (rows == 0) ? first_cell : ((first_cell / 7) + rows) * 7; + cairo_move_to ( + cr, + (alloc->width / 7) * ( first_point % 7), + start_grid_y + ((alloc->height - start_grid_y) / 5) * ( first_point / 7) + 1); + + last_point = (rows == (last_cell / 7 - first_cell / 7)) ? last_cell : ((first_cell / 7) + rows) * 7 + 6; + cairo_rel_line_to (cr, + (alloc->width / 7) * (last_point - first_point + 1), + 0); + cairo_rel_line_to (cr, + 0, + (alloc->height - start_grid_y) / 5); + cairo_rel_line_to (cr, + - (alloc->width / 7) * (last_point - first_point + 1), + 0); + cairo_rel_line_to (cr, + 0, + - (alloc->height - start_grid_y) / 5); + cairo_fill (cr); + } + + cairo_restore (cr); + } + /* drawing grid text */ bold_font = pango_font_description_copy (font); pango_font_description_set_weight (bold_font, PANGO_WEIGHT_SEMIBOLD); @@ -853,9 +1112,8 @@ gcal_month_view_draw_grid (GcalMonthView *view, pango_cairo_update_layout (cr, layout); pango_layout_get_pixel_size (layout, &font_width, &font_height); - /* 6: is padding around the grid-header */ cairo_move_to (cr, - (alloc->width / 7) * i + padding->left, + (alloc->width / 7) * i + header_padding.left, start_grid_y - padding->bottom - font_height); pango_cairo_show_layout (cr, layout); @@ -873,7 +1131,7 @@ gcal_month_view_draw_grid (GcalMonthView *view, continue; /* drawing selected_day */ - if (priv->selected_cell == n_day - 1) + if (priv->actual_day_cell == n_day - priv->days_delay) { cairo_set_source_rgb (cr, selected_color.red, @@ -888,12 +1146,12 @@ gcal_month_view_draw_grid (GcalMonthView *view, pango_layout_get_pixel_size (layout, &font_width, NULL); cairo_move_to ( cr, - (alloc->width / 7) * i + padding->left, + (alloc->width / 7) * i + header_padding.left, start_grid_y + ((alloc->height - start_grid_y) / 5) * j + padding->top); pango_cairo_show_layout (cr, layout); /* unsetting selected flag */ - if (priv->selected_cell == n_day - 1) + if (priv->actual_day_cell == n_day - priv->days_delay) { cairo_set_source_rgb (cr, ligther_color.red, @@ -931,7 +1189,7 @@ gcal_month_view_draw_grid (GcalMonthView *view, cairo_stroke (cr); - /* drawing selected_cell */ + /* drawing actual_day_cell */ //FIXME: What to do when the selected date isn't on the in the month cairo_set_source_rgb (cr, selected_color.red, @@ -941,10 +1199,11 @@ gcal_month_view_draw_grid (GcalMonthView *view, /* Two pixel line on the selected day cell */ cairo_set_line_width (cr, 2.0); cairo_move_to (cr, - (alloc->width / 7) * ( priv->selected_cell % 7), - start_grid_y + ((alloc->height - start_grid_y) / 5) * ( priv->selected_cell / 7) + 1); + (alloc->width / 7) * ( priv->actual_day_cell % 7), + start_grid_y + ((alloc->height - start_grid_y) / 5) * ( priv->actual_day_cell / 7) + 1); cairo_rel_line_to (cr, (alloc->width / 7), 0); cairo_stroke (cr); + } static gdouble @@ -952,6 +1211,7 @@ gcal_month_view_get_start_grid_y (GtkWidget *widget) { GtkStyleContext *context; GtkBorder padding; + GtkBorder header_padding; PangoLayout *layout; gint font_height; @@ -964,6 +1224,10 @@ gcal_month_view_get_start_grid_y (GtkWidget *widget) gtk_style_context_save (context); gtk_style_context_add_region (context, "header", 0); + gtk_style_context_get_padding (gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &header_padding); + pango_layout_set_font_description ( layout, gtk_style_context_get_font (context, @@ -971,8 +1235,7 @@ gcal_month_view_get_start_grid_y (GtkWidget *widget) pango_layout_get_pixel_size (layout, NULL, &font_height); /* 6: is padding around the header */ - start_grid_y = font_height; - gtk_style_context_remove_region (context, "header"); + start_grid_y = header_padding.top + font_height + header_padding.bottom; gtk_style_context_restore (context); /* init grid values */ @@ -986,7 +1249,7 @@ gcal_month_view_get_start_grid_y (GtkWidget *widget) gtk_widget_get_state_flags (widget), &padding); - start_grid_y += 3 * padding.top + font_height; + start_grid_y += padding.top + font_height; g_object_unref (layout); return start_grid_y; } @@ -1046,8 +1309,8 @@ gcal_month_view_contains (GcalView *view, if (priv->date == NULL) return FALSE; - if (priv->date->month == date->month - || priv->date->year == date->year) + if (priv->date->month == date->month && + priv->date->year == date->year) { return TRUE; } @@ -1114,6 +1377,18 @@ gcal_month_view_get_by_uuid (GcalView *view, return NULL; } +static void +gcal_month_view_clear_selection (GcalView *view) +{ + GcalMonthViewPrivate *priv; + + priv = GCAL_MONTH_VIEW (view)->priv; + + priv->start_mark_cell = -1; + priv->end_mark_cell = -1; + gtk_widget_queue_draw (GTK_WIDGET (view)); +} + /* Public API */ /** * gcal_month_view_new: diff --git a/src/gcal-month-view.h b/src/gcal-month-view.h index 6fa73035..8c9570d5 100644 --- a/src/gcal-month-view.h +++ b/src/gcal-month-view.h @@ -48,6 +48,9 @@ struct _GcalMonthView struct _GcalMonthViewClass { GtkContainerClass parent_class; + + /* signals */ + void (*new_event) (gint new_day); }; GType gcal_month_view_get_type (void); diff --git a/src/gcal-window.c b/src/gcal-window.c index b9fde97b..22024794 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -28,6 +28,7 @@ #include "gcal-event-view.h" #include "gcal-enum-types.h" #include "gtk-notification.h" +#include "gcal-event-overlay.h" #include #include @@ -45,10 +46,13 @@ struct _GcalWindowPrivate ClutterActor *sources_actor; ClutterActor *notification_actor; + ClutterActor *new_event_actor; + GtkWidget *notebook; GtkWidget *sources_view; GtkWidget *views [5]; - GtkWidget *add_view; + GtkWidget *edit_dialog; + GtkWidget *edit_widget; GcalWindowViewType active_view; icaltimetype *active_date; @@ -85,7 +89,7 @@ static void gcal_window_set_active_view (GcalWindow *w static void gcal_window_set_sources_view (GcalWindow *window); -static void gcal_window_init_event_view (GcalWindow *window); +static void gcal_window_init_edit_dialog (GcalWindow *window); static void gcal_window_view_changed (GcalToolbar *main_toolbar, GcalWindowViewType view_type, @@ -101,12 +105,6 @@ static void gcal_window_add_event (GcalToolbar *m static void gcal_window_back_last_view (GtkWidget *widget, gpointer user_data); -static void gcal_window_edit_event (GcalToolbar *main_toolbar, - gpointer user_data); - -static void gcal_window_done_edit_event (GcalToolbar *main_toolbar, - gpointer user_data); - static void gcal_window_sources_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, @@ -133,6 +131,30 @@ static void gcal_window_remove_event (GtkNotification *n static void gcal_window_undo_remove_event (GtkButton *button, gpointer user_data); +static void gcal_window_view_updated (GcalView *view, + gpointer date, + gpointer user_data); + +static void gcal_window_event_overlay_shown (GcalView *view, + gpointer start_span, + gpointer end_span, + gdouble x, + gdouble y, + gpointer user_data); + +static void gcal_window_event_overlay_closed (GcalEventOverlay *widget, + gpointer user_data); + +static void gcal_window_create_event (GcalEventOverlay *widget, + GcalNewEventData *new_data, + gboolean open_details, + gpointer user_data); + +static void gcal_window_show_hide_actor_cb (ClutterActor *actor, + gchar *name, + gboolean is_finished, + gpointer user_data); + G_DEFINE_TYPE(GcalWindow, gcal_window, GTK_TYPE_APPLICATION_WINDOW) static void @@ -195,10 +217,10 @@ gcal_window_constructed (GObject *object) priv = GCAL_WINDOW (object)->priv; - /* internal data init*/ + /* internal data init */ priv->event_to_delete = NULL; - /* FIXME: here read the data from somewehere */ + /* FIXME: here read the initial date from somewehere */ priv->active_date = g_new (icaltimetype, 1); *(priv->active_date) = icaltime_from_timet (time (NULL), 0); @@ -315,10 +337,13 @@ gcal_window_constructed (GObject *object) /* notifications manager */ priv->notification_actor = gtk_clutter_actor_new (); - clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (contents_layout_manager), - priv->notification_actor, - CLUTTER_BIN_ALIGNMENT_CENTER, - CLUTTER_BIN_ALIGNMENT_START); + g_object_set (priv->notification_actor, + "x-expand", TRUE, + "y-expand", TRUE, + "x-align", CLUTTER_ACTOR_ALIGN_CENTER, + "y-align", CLUTTER_ACTOR_ALIGN_START, + NULL); + clutter_actor_add_child (priv->contents_actor, priv->notification_actor); context = gtk_widget_get_style_context ( @@ -327,6 +352,23 @@ gcal_window_constructed (GObject *object) gtk_style_context_add_class (context, "overlay"); clutter_actor_hide (priv->notification_actor); + /* event-overlay */ + priv->new_event_actor = gcal_event_overlay_new (); + g_object_set (priv->new_event_actor, + "opacity", 0, + "x-align", CLUTTER_ACTOR_ALIGN_FILL, + "y-align", CLUTTER_ACTOR_ALIGN_FILL, + NULL); + + context = + gtk_widget_get_style_context ( + gtk_clutter_actor_get_widget ( + GTK_CLUTTER_ACTOR (priv->new_event_actor))); + gtk_style_context_add_class (context, "overlay"); + + clutter_actor_add_child (priv->contents_actor, priv->new_event_actor); + clutter_actor_hide (priv->new_event_actor); + /* signals connection/handling */ g_signal_connect (priv->main_toolbar, "view-changed", @@ -346,13 +388,14 @@ gcal_window_constructed (GObject *object) G_CALLBACK (gcal_window_back_last_view), object); - g_signal_connect (priv->main_toolbar, - "edit-event", - G_CALLBACK (gcal_window_edit_event), + g_signal_connect (priv->new_event_actor, + "cancelled", + G_CALLBACK (gcal_window_event_overlay_closed), object); - g_signal_connect (priv->main_toolbar, - "done-edit", - G_CALLBACK (gcal_window_done_edit_event), + + g_signal_connect (priv->new_event_actor, + "created", + G_CALLBACK (gcal_window_create_event), object); gtk_widget_show (embed); @@ -466,10 +509,22 @@ gcal_window_set_active_view (GcalWindow *window, g_debug ("Unimplemented view yet"); return; } + + /* Bindings properties and signals */ g_object_bind_property (priv->views[view_type], "active-date", window, "active-date", G_BINDING_DEFAULT); + g_signal_connect (priv->views[view_type], + "create-event", + G_CALLBACK (gcal_window_event_overlay_shown), + window); + + g_signal_connect (priv->views[view_type], + "updated", + G_CALLBACK (gcal_window_view_updated), + window); + priv->active_view = view_type; gtk_widget_show (priv->views[priv->active_view]); @@ -485,7 +540,8 @@ gcal_window_set_active_view (GcalWindow *window, update_range = ! gcal_view_contains (GCAL_VIEW (priv->views[priv->active_view]), priv->active_date); - gcal_view_set_date (GCAL_VIEW (priv->views[priv->active_view]), priv->active_date); + gcal_view_set_date (GCAL_VIEW (priv->views[priv->active_view]), + priv->active_date); if (update_range) { first_day = gcal_view_get_initial_date ( @@ -570,32 +626,46 @@ gcal_window_set_sources_view (GcalWindow *window) } static void -gcal_window_init_event_view (GcalWindow *window) +gcal_window_init_edit_dialog (GcalWindow *window) { GcalWindowPrivate *priv; + GcalManager *manager; + + GtkWidget *content_area; + GtkWidget *action_area; g_return_if_fail (GCAL_IS_WINDOW (window)); priv = window->priv; - priv->add_view = gcal_event_view_new_with_manager ( - gcal_window_get_manager (window)); - g_object_set (priv->add_view, - "hexpand", TRUE, - "vexpand", TRUE, - "margin-top", 10, - "margin-left", 20, - "margin-right", 20, - NULL); + priv->edit_dialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (priv->edit_dialog), _("Edit Event")); + gtk_window_set_modal (GTK_WINDOW (priv->edit_dialog), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (priv->edit_dialog), TRUE); + gtk_window_set_resizable (GTK_WINDOW (priv->edit_dialog), FALSE); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (priv->edit_dialog)); + manager = gcal_window_get_manager (window); + + priv->edit_widget = gcal_event_view_new_with_manager (manager); + gtk_container_add (GTK_CONTAINER (content_area), + priv->edit_widget); + + gtk_widget_show_all (content_area); + + gtk_dialog_add_button (GTK_DIALOG (priv->edit_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT); + gtk_dialog_add_button (GTK_DIALOG (priv->edit_dialog), + _("Done"), + GTK_RESPONSE_ACCEPT); + action_area = gtk_dialog_get_action_area (GTK_DIALOG (priv->edit_dialog)); + gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_EDGE); + gtk_widget_show_all (action_area); + /* FIXME: hook delete signal g_signal_connect (priv->add_view, "will-delete", G_CALLBACK (gcal_window_will_remove_event), - window); - - gtk_widget_show (priv->add_view); - gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), - priv->add_view, - NULL); + window);*/ } static void @@ -646,18 +716,11 @@ gcal_window_add_event (GcalToolbar *main_toolbar, GcalWindowPrivate *priv; priv = GCAL_WINDOW (user_data)->priv; - if (priv->add_view == NULL) - gcal_window_init_event_view (GCAL_WINDOW (user_data)); + if (priv->edit_dialog == NULL) + gcal_window_init_edit_dialog (GCAL_WINDOW (user_data)); - //TODO: Reload/clean/reinitialize status - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (priv->notebook), - gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), priv->add_view)); - - gcal_toolbar_set_mode (GCAL_TOOLBAR (priv->main_toolbar), - GCAL_TOOLBAR_VIEW_EVENT); - - gcal_event_view_load_new (GCAL_EVENT_VIEW (priv->add_view)); + /* FIXME: load + gcal_event_view_load_new (GCAL_EVENT_VIEW (priv->add_view));*/ } /* @@ -685,39 +748,11 @@ gcal_window_back_last_view (GtkWidget *widget, } else { - //FIXME: there's something that needs to be done here. + /* FIXME: there's something that needs to be done here. */ g_warning ("Your app has gone crazy"); } } -static void -gcal_window_edit_event (GcalToolbar *main_toolbar, - gpointer user_data) -{ - GcalWindowPrivate *priv; - - priv = GCAL_WINDOW (user_data)->priv; - if (priv->add_view == NULL) - gcal_window_init_event_view (GCAL_WINDOW (user_data)); - - //FIXME: check here, maybe this isn't need since edit-event - //can be triggered only from add_view - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (priv->notebook), - gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), priv->add_view)); - gcal_event_view_enter_edit_mode (GCAL_EVENT_VIEW (priv->add_view)); -} - -static void -gcal_window_done_edit_event (GcalToolbar *main_toolbar, - gpointer user_data) -{ - GcalWindowPrivate *priv; - - priv = GCAL_WINDOW (user_data)->priv; - gcal_event_view_leave_edit_mode (GCAL_EVENT_VIEW (priv->add_view)); -} - static void gcal_window_sources_row_activated (GtkTreeView *tree_view, GtkTreePath *path, @@ -801,7 +836,8 @@ gcal_window_events_added (GcalManager *manager, event_uid)); gtk_widget_show (event); - //FIXME: add event to every instantiated view + /* FIXME: add event-widget to every instantiated view, not pretty sure + * about this. */ gtk_container_add ( GTK_CONTAINER (priv->views[priv->active_view]), event); @@ -856,18 +892,14 @@ gcal_window_event_activated (GcalEventWidget *event_widget, g_return_if_fail (GCAL_IS_WINDOW (user_data)); priv = GCAL_WINDOW (user_data)->priv; - if (priv->add_view == NULL) - gcal_window_init_event_view (GCAL_WINDOW (user_data)); + if (priv->edit_dialog == NULL) + gcal_window_init_edit_dialog (GCAL_WINDOW (user_data)); - gtk_notebook_set_current_page ( - GTK_NOTEBOOK (priv->notebook), - gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), priv->add_view)); - - gcal_event_view_load_event (GCAL_EVENT_VIEW (priv->add_view), + gcal_event_view_load_event (GCAL_EVENT_VIEW (priv->edit_widget), gcal_event_widget_peek_uuid (event_widget)); - gcal_toolbar_set_mode (GCAL_TOOLBAR (priv->main_toolbar), - GCAL_TOOLBAR_VIEW_EVENT); + gtk_dialog_run (GTK_DIALOG (priv->edit_dialog)); + gtk_widget_hide (priv->edit_dialog); } static void @@ -898,7 +930,7 @@ gcal_window_will_remove_event (GcalEventView *view, } else { - //FIXME: there's something that needs to be done here. + /* FIXME: there's something that needs to be done here. */ g_warning ("Your app has gone crazy"); } @@ -983,6 +1015,132 @@ gcal_window_undo_remove_event (GtkButton *button, } +static void +gcal_window_view_updated (GcalView *view, + gpointer date, + gpointer user_data) +{ + GcalWindowPrivate *priv; + icaltimetype *first_day; + icaltimetype *last_day; + + priv = GCAL_WINDOW (user_data)->priv; + + gcal_view_set_date (GCAL_VIEW (priv->views[priv->active_view]), + date); + + first_day = gcal_view_get_initial_date ( + GCAL_VIEW (priv->views[priv->active_view])); + last_day = gcal_view_get_final_date ( + GCAL_VIEW (priv->views[priv->active_view])); + + gcal_manager_set_new_range ( + gcal_window_get_manager (GCAL_WINDOW (user_data)), + first_day, + last_day); + + g_free (first_day); + g_free (last_day); +} + +static void +gcal_window_event_overlay_shown (GcalView *view, + gpointer start_span, + gpointer end_span, + gdouble x, + gdouble y, + gpointer user_data) +{ + GcalWindowPrivate *priv; + GcalManager *manager; + gint width, height; + + g_return_if_fail (user_data); + priv = GCAL_WINDOW (user_data)->priv; + + manager = gcal_window_get_manager (GCAL_WINDOW (user_data)); + /* squeezed in here, reload on every show */ + gcal_event_overlay_set_sources_model ( + GCAL_EVENT_OVERLAY (priv->new_event_actor), + gcal_manager_get_sources_model (manager)); + + if (start_span != NULL) + { + gcal_event_overlay_set_span (GCAL_EVENT_OVERLAY (priv->new_event_actor), + (icaltimetype*) start_span, + (icaltimetype*) end_span); + } + + clutter_actor_show (priv->new_event_actor); + + width = clutter_actor_get_width (priv->new_event_actor); + height = clutter_actor_get_height (priv->new_event_actor); + + x = x - width / 2; + y = y - height; + + clutter_actor_set_x (priv->new_event_actor, x); + clutter_actor_set_y (priv->new_event_actor, y); + + clutter_actor_save_easing_state (priv->new_event_actor); + clutter_actor_set_opacity (priv->new_event_actor, 255); + clutter_actor_restore_easing_state (priv->new_event_actor); +} + +static void +gcal_window_event_overlay_closed (GcalEventOverlay *widget, + gpointer user_data) +{ + GcalWindowPrivate *priv; + + g_return_if_fail (user_data); + priv = GCAL_WINDOW (user_data)->priv; + + /* reset and hide */ + gcal_view_clear_selection (GCAL_VIEW (priv->views[priv->active_view])); + gcal_event_overlay_reset (GCAL_EVENT_OVERLAY (priv->new_event_actor)); + + clutter_actor_save_easing_state (priv->new_event_actor); + clutter_actor_set_opacity (priv->new_event_actor, 0); + clutter_actor_restore_easing_state (priv->new_event_actor); + + g_signal_connect (priv->new_event_actor, + "transition-stopped::opacity", + G_CALLBACK (gcal_window_show_hide_actor_cb), + NULL); +} + +static void +gcal_window_create_event (GcalEventOverlay *widget, + GcalNewEventData *new_data, + gboolean open_details, + gpointer user_data) +{ + GcalWindowPrivate *priv; + + g_return_if_fail (user_data); + priv = GCAL_WINDOW (user_data)->priv; + + /* reset and hide */ + gcal_window_event_overlay_closed (widget, user_data); + + /* create the event */ + g_debug ("NOW CREATE THE EVENT"); + if (open_details) + g_debug ("Opening details window NOW"); +} + +static void +gcal_window_show_hide_actor_cb (ClutterActor *actor, + gchar *name, + gboolean is_finished, + gpointer user_data) +{ + if (CLUTTER_ACTOR_IS_VISIBLE (actor) && + clutter_actor_get_opacity (actor) == 0) + clutter_actor_hide (actor); +} + /* Public API */ GtkWidget* gcal_window_new_with_view (GcalApplication *app,