From f83143050c67626fbffccdfdb8141298930a30c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erick=20P=C3=A9rez=20Castellanos?= Date: Thu, 5 Jul 2012 00:46:57 -0400 Subject: [PATCH] Added undo notification system to event deletion. Added GcalEventView::will-delete to let thw window knows about the change. GcalWindow will handle the hiding of the event-widget till the notification goes away or the user click "Undo". Added method in GcalView interface to retrieve and event-widget by it's uuid. Added some cosmetic changes in GcalMonthView as well. --- data/gtk-styles.css | 2 +- src/gcal-event-view.c | 32 +++++---- src/gcal-event-view.h | 2 +- src/gcal-month-view.c | 131 ++++++++++++++++++++++-------------- src/gcal-view.c | 9 +++ src/gcal-view.h | 18 +++-- src/gcal-window.c | 153 ++++++++++++++++++++++++++++++++++++++++-- src/gcal-window.h | 2 + 8 files changed, 270 insertions(+), 79 deletions(-) diff --git a/data/gtk-styles.css b/data/gtk-styles.css index bf496d6d..085db3aa 100644 --- a/data/gtk-styles.css +++ b/data/gtk-styles.css @@ -57,7 +57,7 @@ padding-right: 8px; } -GtkClutterOffscreen.sources { +GtkClutterOffscreen.overlay { background-color: transparent; background-image: none; } diff --git a/src/gcal-event-view.c b/src/gcal-event-view.c index 132f36fe..8d21ba64 100644 --- a/src/gcal-event-view.c +++ b/src/gcal-event-view.c @@ -57,7 +57,7 @@ struct _GcalEventViewPrivate enum { - DONE = 1, + WILL_DELETE = 1, NUM_SIGNALS }; @@ -86,14 +86,16 @@ gcal_event_view_class_init(GcalEventViewClass *klass) object_class->constructed = gcal_event_view_constructed; object_class->finalize = gcal_event_view_finalize; - signals[DONE] = g_signal_new ("done", - GCAL_TYPE_EVENT_VIEW, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GcalEventViewClass, done), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); + signals[WILL_DELETE] = g_signal_new ("will-delete", + GCAL_TYPE_EVENT_VIEW, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GcalEventViewClass, + will_delete), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); g_type_class_add_private((gpointer)klass, sizeof(GcalEventViewPrivate)); } @@ -337,17 +339,19 @@ gcal_event_view_delete_event (GtkWidget *button, gpointer user_data) { GcalEventViewPrivate *priv; + gchar *event_uuid; g_return_if_fail (GCAL_IS_EVENT_VIEW (user_data)); priv = GCAL_EVENT_VIEW (user_data)->priv; - gcal_manager_remove_event (priv->manager, - priv->source_uid, - priv->event_uid); - + event_uuid = g_strdup_printf ("%s:%s", priv->source_uid, priv->event_uid); /* Reset the widget, and let the windows it work it's done */ gcal_event_view_load_new (GCAL_EVENT_VIEW (user_data)); - g_signal_emit (GCAL_EVENT_VIEW (user_data), signals[DONE], 0); + g_signal_emit (GCAL_EVENT_VIEW (user_data), + signals[WILL_DELETE], + 0, + event_uuid); + g_free (event_uuid); } /* Public API */ diff --git a/src/gcal-event-view.h b/src/gcal-event-view.h index 395e48fd..5f4a63e6 100644 --- a/src/gcal-event-view.h +++ b/src/gcal-event-view.h @@ -49,7 +49,7 @@ struct _GcalEventViewClass GtkGridClass parent_class; /* Signals */ - void (*done) (GcalEventView *view); + void (*will_delete) (GcalEventView *view, const gchar* event_uuid); }; GType gcal_event_view_get_type (void); diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index bd889fb9..525ce5d6 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -49,7 +49,7 @@ struct _GcalMonthViewPrivate icaltimetype *date; gint days_delay; - gint header_font_size; + gint grid_header_size; gdouble vertical_step; gdouble horizontal_step; }; @@ -66,63 +66,66 @@ const char* weekdays [] = "Sat" }; -static void gcal_view_interface_init (GcalViewIface *iface); +static void gcal_view_interface_init (GcalViewIface *iface); -static void gcal_month_view_constructed (GObject *object); +static void gcal_month_view_constructed (GObject *object); -static void gcal_month_view_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); +static void gcal_month_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); -static void gcal_month_view_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); +static void gcal_month_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); -static void gcal_month_view_finalize (GObject *object); +static void gcal_month_view_finalize (GObject *object); -static void gcal_month_view_realize (GtkWidget *widget); +static void gcal_month_view_realize (GtkWidget *widget); -static void gcal_month_view_unrealize (GtkWidget *widget); +static void gcal_month_view_unrealize (GtkWidget *widget); -static void gcal_month_view_map (GtkWidget *widget); +static void gcal_month_view_map (GtkWidget *widget); -static void gcal_month_view_unmap (GtkWidget *widget); +static void gcal_month_view_unmap (GtkWidget *widget); -static void gcal_month_view_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); +static void gcal_month_view_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); -static gboolean gcal_month_view_draw (GtkWidget *widget, - cairo_t *cr); +static gboolean gcal_month_view_draw (GtkWidget *widget, + cairo_t *cr); -static gboolean gcal_month_view_button_press (GtkWidget *widget, - GdkEventButton *event); +static gboolean gcal_month_view_button_press (GtkWidget *widget, + GdkEventButton *event); -static gboolean gcal_month_view_button_release (GtkWidget *widget, - GdkEventButton *event); +static gboolean gcal_month_view_button_release (GtkWidget *widget, + GdkEventButton *event); -static void gcal_month_view_add (GtkContainer *constainer, - GtkWidget *widget); +static void gcal_month_view_add (GtkContainer *constainer, + GtkWidget *widget); -static void gcal_month_view_remove (GtkContainer *constainer, - GtkWidget *widget); +static void gcal_month_view_remove (GtkContainer *constainer, + GtkWidget *widget); -static void gcal_month_view_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); +static void gcal_month_view_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); -static void gcal_month_view_draw_month_grid (GcalMonthView *mont_view, - cairo_t *cr, - gint x, - gint y); +static void gcal_month_view_draw_month_grid (GcalMonthView *view, + cairo_t *cr, + gint x, + gint y); -static gboolean gcal_month_view_is_in_range (GcalView *view, - icaltimetype *date); +static gboolean gcal_month_view_is_in_range (GcalView *view, + icaltimetype *date); -static void gcal_month_view_remove_by_uuid (GcalView *view, - const gchar *uuid); +static void gcal_month_view_remove_by_uuid (GcalView *view, + const gchar *uuid); + +static GtkWidget* gcal_month_view_get_by_uuid (GcalView *view, + const gchar *uuid); G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view, @@ -195,6 +198,7 @@ gcal_view_interface_init (GcalViewIface *iface) { iface->is_in_range = gcal_month_view_is_in_range; iface->remove_by_uuid = gcal_month_view_remove_by_uuid; + iface->get_by_uuid = gcal_month_view_get_by_uuid; } static void @@ -386,11 +390,11 @@ gcal_month_view_size_allocate (GtkWidget *widget, pango_layout_get_pixel_size (layout, NULL, &font_height); /* init values */ - priv->header_font_size = 12; + priv->grid_header_size = 12; priv->horizontal_step = (allocation->width - (allocation->x + padding.left + padding.right)) / 7; priv->vertical_step = - (allocation->height - (allocation->y + padding.top + padding.bottom + priv->header_font_size)) / 5; + (allocation->height - (allocation->y + padding.top + padding.bottom + priv->grid_header_size)) / 5; for (i = 0; i < 35; i++) { @@ -408,12 +412,14 @@ gcal_month_view_size_allocate (GtkWidget *widget, pos_x = ( i % 7 ) * priv->horizontal_step; pos_y = ( i / 7 ) * priv->vertical_step; + if (! gtk_widget_get_visible (GTK_WIDGET (l->data))) + continue; gtk_widget_get_preferred_height (GTK_WIDGET (l->data), &min_height, &natural_height); child_allocation.x = pos_x + allocation->x + padding.top; child_allocation.y = pos_y + font_height + allocation->y - + priv->header_font_size + padding.left; + + priv->grid_header_size + padding.left; child_allocation.width = priv->horizontal_step; child_allocation.height = MIN (natural_height, priv->vertical_step); if (added_height + font_height + child_allocation.height @@ -451,8 +457,8 @@ gcal_month_view_draw (GtkWidget *widget, gtk_style_context_get_padding (context, state, &padding); gtk_style_context_get_color (context, state, &color); - /* header_font_size is found through pango_get_size_in_pixels */ - priv->header_font_size = 12; + /* grid_header_size is found through pango_get_size_in_pixels */ + priv->grid_header_size = 12; /* getting allocation, and so on */ gtk_widget_get_allocation (widget, &alloc); @@ -461,7 +467,7 @@ gcal_month_view_draw (GtkWidget *widget, GCAL_MONTH_VIEW (widget), cr, alloc.x + padding.left, - alloc.y + padding.top + priv->header_font_size); + alloc.y + padding.top + priv->grid_header_size); if (GTK_WIDGET_CLASS (gcal_month_view_parent_class)->draw != NULL) GTK_WIDGET_CLASS (gcal_month_view_parent_class)->draw (widget, cr); @@ -500,7 +506,7 @@ gcal_month_view_button_press (GtkWidget *widget, } x = x - padding.left; - y = y - ( padding.top + priv->header_font_size ); + y = y - ( padding.top + priv->grid_header_size ); priv->clicked_cell = 7 * ( (gint ) ( y / priv->vertical_step )) + ((gint) ( x / priv->horizontal_step )); return TRUE; @@ -537,7 +543,7 @@ static gboolean gcal_month_view_button_release (GtkWidget *widget, } x = x - padding.left; - y = y - ( padding.top + priv->header_font_size ); + y = y - ( padding.top + priv->grid_header_size ); released = 7 * ( (gint ) ( y / priv->vertical_step )) + ((gint) ( x / priv->horizontal_step )); if (priv->clicked_cell == released) @@ -624,7 +630,7 @@ gcal_month_view_forall (GtkContainer *container, } static void -gcal_month_view_draw_month_grid (GcalMonthView *month_view, +gcal_month_view_draw_month_grid (GcalMonthView *view, cairo_t *cr, gint x, gint y) @@ -645,8 +651,8 @@ gcal_month_view_draw_month_grid (GcalMonthView *month_view, PangoLayout *layout; gchar *day; - priv = month_view->priv; - widget = GTK_WIDGET (month_view); + priv = view->priv; + widget = GTK_WIDGET (view); context = gtk_widget_get_style_context (widget); state = gtk_widget_get_state_flags (widget); @@ -753,6 +759,29 @@ gcal_month_view_remove_by_uuid (GcalView *view, } } +static GtkWidget* +gcal_month_view_get_by_uuid (GcalView *view, + const gchar *uuid) +{ + GcalMonthViewPrivate *priv; + gint i; + GList *l; + + g_return_val_if_fail (GCAL_IS_MONTH_VIEW (view), NULL); + priv = GCAL_MONTH_VIEW (view)->priv; + + for (i = 0; i < 35; i++) + { + for (l = priv->days[i]; l != NULL; l = l->next) + { + const gchar* widget_uuid = gcal_event_widget_peek_uuid (GCAL_EVENT_WIDGET (l->data)); + if (g_strcmp0 (uuid, widget_uuid) == 0) + return GTK_WIDGET (l->data); + } + } + return NULL; +} + /* Public API */ /** * gcal_month_view_new: diff --git a/src/gcal-view.c b/src/gcal-view.c index 2ef8e11d..44728a9d 100644 --- a/src/gcal-view.c +++ b/src/gcal-view.c @@ -79,3 +79,12 @@ gcal_view_remove_by_uuid (GcalView *view, GCAL_VIEW_GET_INTERFACE (view)->remove_by_uuid (view, uuid); } + +GtkWidget* +gcal_view_get_by_uuid (GcalView *view, + const gchar *uuid) +{ + g_return_val_if_fail (GCAL_IS_VIEW (view), NULL); + + return GCAL_VIEW_GET_INTERFACE (view)->get_by_uuid (view, uuid); +} diff --git a/src/gcal-view.h b/src/gcal-view.h index 6b0f006b..de2d3ef0 100644 --- a/src/gcal-view.h +++ b/src/gcal-view.h @@ -40,17 +40,21 @@ struct _GcalViewIface { GTypeInterface parent_iface; - gboolean (*is_in_range) (GcalView *view, icaltimetype *date); - void (*remove_by_uuid) (GcalView *view, const gchar *uuid); + gboolean (*is_in_range) (GcalView *view, icaltimetype *date); + void (*remove_by_uuid) (GcalView *view, const gchar *uuid); + GtkWidget* (*get_by_uuid) (GcalView *view, const gchar *uuid); }; -GType gcal_view_get_type (void); +GType gcal_view_get_type (void); -gboolean gcal_view_is_in_range (GcalView *view, - icaltimetype *date); +gboolean gcal_view_is_in_range (GcalView *view, + icaltimetype *date); -void gcal_view_remove_by_uuid (GcalView *view, - const gchar *uuid); +void gcal_view_remove_by_uuid (GcalView *view, + const gchar *uuid); + +GtkWidget* gcal_view_get_by_uuid (GcalView *view, + const gchar *uuid); G_END_DECLS diff --git a/src/gcal-window.c b/src/gcal-window.c index 1322f598..46b095bb 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -26,9 +26,11 @@ #include "gcal-event-widget.h" #include "gcal-event-view.h" #include "gcal-utils.h" +#include "gtk-notification.h" #include #include +#include #include @@ -48,6 +50,9 @@ struct _GcalWindowPrivate GtkWidget *add_view; GcalViewTypeEnum active_view; + + /* temp to keep the will_delete event uuid */ + gchar *event_to_delete; }; static void gcal_window_constructed (GObject *object); @@ -94,6 +99,16 @@ static void gcal_window_events_removed (GcalManager *manager static void gcal_window_event_activated (GcalEventWidget *event_widget, gpointer user_data); +static void gcal_window_will_remove_event (GcalEventView *view, + gchar *event_uuid, + gpointer user_data); + +static void gcal_window_remove_event (GtkNotification *notification, + gpointer user_data); + +static void gcal_window_undo_remove_event (GtkButton *button, + gpointer user_data); + G_DEFINE_TYPE(GcalWindow, gcal_window, GTK_TYPE_APPLICATION_WINDOW) static void @@ -131,6 +146,10 @@ gcal_window_constructed (GObject *object) priv = GCAL_WINDOW (object)->priv; + /* internal data init*/ + priv->event_to_delete = NULL; + + /* ui init */ embed = gtk_clutter_embed_new (); gtk_container_add (GTK_CONTAINER (object), embed); stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (embed)); @@ -240,8 +259,6 @@ gcal_window_constructed (GObject *object) /* notifications manager */ priv->notification_actor = gtk_clutter_actor_new (); - clutter_actor_set_opacity (priv->notification_actor, 0); - clutter_actor_set_clip_to_allocation (priv->notification_actor, TRUE); clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (contents_layout_manager), priv->notification_actor, CLUTTER_BIN_ALIGNMENT_CENTER, @@ -379,8 +396,8 @@ gcal_window_init_event_view (GcalWindow *window) NULL); g_signal_connect (priv->add_view, - "done", - G_CALLBACK (gcal_window_back_last_view), + "will-delete", + G_CALLBACK (gcal_window_will_remove_event), window); gtk_widget_show (priv->add_view); @@ -669,8 +686,120 @@ gcal_window_event_activated (GcalEventWidget *event_widget, GCAL_TOOLBAR_VIEW_EVENT); } -/* Public API */ +static void +gcal_window_will_remove_event (GcalEventView *view, + gchar *event_uuid, + gpointer user_data) +{ + GcalWindowPrivate *priv; + gint activated_page; + GtkWidget *noty; + GtkWidget *grid; + GtkWidget *undo_button; + + GtkWidget *event_widget; + + priv = GCAL_WINDOW (user_data)->priv; + + gcal_toolbar_set_mode (GCAL_TOOLBAR (priv->main_toolbar), + GCAL_TOOLBAR_OVERVIEW); + + if ((activated_page = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), + priv->views[priv->active_view])) + != -1) + { + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), + activated_page); + } + else + { + //FIXME: there's something that needs to be done here. + g_warning ("Your app has gone crazy"); + } + + noty = gtk_notification_new (); + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 12); + gtk_container_add (GTK_CONTAINER (grid), gtk_label_new (_("Event deleted"))); + + undo_button = gtk_button_new_from_stock (GTK_STOCK_UNDO); + gtk_container_add (GTK_CONTAINER (grid), undo_button); + + gtk_container_add (GTK_CONTAINER (noty), grid); + gtk_widget_show_all (noty); + gcal_window_show_notification (GCAL_WINDOW (user_data), noty); + + g_signal_connect (noty, + "dismissed", + G_CALLBACK (gcal_window_remove_event), + user_data); + g_signal_connect (undo_button, + "clicked", + G_CALLBACK (gcal_window_undo_remove_event), + user_data); + + priv->event_to_delete = g_strdup (event_uuid); + + /* hide widget of the event */ + event_widget = + gcal_view_get_by_uuid (GCAL_VIEW (priv->views[priv->active_view]), + priv->event_to_delete); + gtk_widget_hide (event_widget); +} + +static void +gcal_window_remove_event (GtkNotification *notification, + gpointer user_data) +{ + GcalWindowPrivate *priv; + GcalManager *manager; + gchar **tokens; + + g_return_if_fail (GCAL_IS_WINDOW (user_data)); + priv = GCAL_WINDOW (user_data)->priv; + + if (priv->event_to_delete != NULL) + { + manager = gcal_window_get_manager (GCAL_WINDOW (user_data)); + tokens = g_strsplit (priv->event_to_delete, ":", -1); + + gcal_manager_remove_event (manager, tokens[0], tokens[1]); + + g_strfreev (tokens); + g_free (priv->event_to_delete); + priv->event_to_delete = NULL; + } + + clutter_actor_hide (priv->notification_actor); +} + +static void +gcal_window_undo_remove_event (GtkButton *button, + gpointer user_data) +{ + GcalWindowPrivate *priv; + GtkWidget *event_widget; + + g_return_if_fail (GCAL_IS_WINDOW (user_data)); + priv = GCAL_WINDOW (user_data)->priv; + + if (priv->event_to_delete != NULL) + { + event_widget = gcal_view_get_by_uuid ( + GCAL_VIEW (priv->views[priv->active_view]), + priv->event_to_delete); + gtk_widget_show (event_widget); + + g_free (priv->event_to_delete); + priv->event_to_delete = NULL; + + gcal_window_hide_notification (GCAL_WINDOW (user_data)); + } + +} + +/* Public API */ GtkWidget* gcal_window_new (GcalApplication *app) { @@ -727,3 +856,17 @@ gcal_window_show_notification (GcalWindow *window, gtk_widget_show_all (notification); clutter_actor_show (priv->notification_actor); } + +void +gcal_window_hide_notification (GcalWindow *window) +{ + GcalWindowPrivate *priv; + GtkWidget *noty; + + g_return_if_fail (GCAL_IS_WINDOW (window)); + priv = window->priv; + + noty = gtk_clutter_actor_get_contents ( + GTK_CLUTTER_ACTOR (priv->notification_actor)); + gtk_notification_dismiss (GTK_NOTIFICATION (noty)); +} diff --git a/src/gcal-window.h b/src/gcal-window.h index 0ac0de67..6a399fa3 100644 --- a/src/gcal-window.h +++ b/src/gcal-window.h @@ -58,6 +58,8 @@ GtkWidget* gcal_window_new (GcalApplication *app); void gcal_window_show_notification (GcalWindow *window, GtkWidget *notification); +void gcal_window_hide_notification (GcalWindow *window); + G_END_DECLS #endif /* __GCAL_WINDOW_H__ */