From 052e64eb533d5d3fac16eab23465e107b57a5951 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 13 May 2015 11:39:14 -0300 Subject: [PATCH 1/8] window: add accelerators to change date Left for previous date Down or t for today Right for next date More expected shortcuts are being discussed and registered at https://etherpad.gnome.org/p/calendar-keynav. --- data/ui/window.ui | 6 +++--- src/gcal-window.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/data/ui/window.ui b/data/ui/window.ui index dbcccf23..cc36d3af 100644 --- a/data/ui/window.ui +++ b/data/ui/window.ui @@ -206,7 +206,7 @@ True True True - + win.previous True @@ -227,7 +227,7 @@ True True True - + win.today False @@ -240,7 +240,7 @@ True True True - + win.next True diff --git a/src/gcal-window.c b/src/gcal-window.c index 2467ce0d..6f720bc0 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -130,6 +130,10 @@ enum #define FAST_REFRESH_TIMEOUT 900000 /* ms */ #define SLOW_REFRESH_TIMEOUT 3600000 /* ms */ +static void on_date_action_activated (GSimpleAction *action, + GVariant *param, + gpointer user_data); + static gboolean key_pressed (GtkWidget *widget, GdkEvent *event, gpointer user_data); @@ -256,6 +260,33 @@ static gboolean gcal_window_state_event (GtkWidget *w G_DEFINE_TYPE_WITH_PRIVATE (GcalWindow, gcal_window, GTK_TYPE_APPLICATION_WINDOW) +static const GActionEntry actions[] = { + {"next", on_date_action_activated }, + {"previous", on_date_action_activated }, + {"today", on_date_action_activated } +}; + +static void +on_date_action_activated (GSimpleAction *action, + GVariant *param, + gpointer user_data) +{ + GcalWindowPrivate *priv; + const gchar *action_name; + + g_return_if_fail (GCAL_IS_WINDOW (user_data)); + + priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); + action_name = g_action_get_name (G_ACTION (action)); + + if (g_strcmp0 (action_name, "next") == 0) + date_updated (GTK_BUTTON (priv->forward_button), user_data); + else if (g_strcmp0 (action_name, "previous") == 0) + date_updated (GTK_BUTTON (priv->back_button), user_data); + else if (g_strcmp0 (action_name, "today") == 0) + date_updated (GTK_BUTTON (priv->today_button), user_data); +} + static gboolean key_pressed (GtkWidget *widget, GdkEvent *event, @@ -1374,6 +1405,12 @@ gcal_window_constructed (GObject *object) // Prevents nameless events' creation g_signal_handlers_block_by_func (priv->new_event_what_entry, create_event, object); + // Setup actions + g_action_map_add_action_entries (G_ACTION_MAP (object), + actions, + G_N_ELEMENTS (actions), + object); + /* header_bar: menu */ builder = gtk_builder_new (); gtk_builder_add_from_resource (builder, @@ -1578,12 +1615,20 @@ gcal_window_new_with_view_and_date (GcalApplication *app, { GcalWindow *win; GcalManager *manager; + const gchar *next_accel[] = {"Right", NULL}; + const gchar *previous_accel[] = {"Left", NULL}; + const gchar *today_accel[] = {"Down", "t", NULL}; manager = gcal_application_get_manager (GCAL_APPLICATION (app)); win = g_object_new (GCAL_TYPE_WINDOW, "application", GTK_APPLICATION (app), "manager", manager, "active-date", date, NULL); + /* setup accels */ + gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.next", next_accel); + gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.previous", previous_accel); + gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.today", today_accel); + /* loading size */ load_geometry (win); From a75d458f69435b0560badaf9d0ceed322da5599e Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 13 May 2015 15:22:37 -0300 Subject: [PATCH 2/8] window: use a macro to simplify code --- src/gcal-window.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/gcal-window.c b/src/gcal-window.c index 6f720bc0..f86d09d6 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -130,6 +130,12 @@ enum #define FAST_REFRESH_TIMEOUT 900000 /* ms */ #define SLOW_REFRESH_TIMEOUT 3600000 /* ms */ +#define gcal_window_add_accelerator(app,action,accel) {\ + const gchar *tmp[] = {accel, NULL};\ + gtk_application_set_accels_for_action (GTK_APPLICATION (app), action, tmp);\ +} + + static void on_date_action_activated (GSimpleAction *action, GVariant *param, gpointer user_data); @@ -1615,9 +1621,6 @@ gcal_window_new_with_view_and_date (GcalApplication *app, { GcalWindow *win; GcalManager *manager; - const gchar *next_accel[] = {"Right", NULL}; - const gchar *previous_accel[] = {"Left", NULL}; - const gchar *today_accel[] = {"Down", "t", NULL}; manager = gcal_application_get_manager (GCAL_APPLICATION (app)); @@ -1625,9 +1628,13 @@ gcal_window_new_with_view_and_date (GcalApplication *app, NULL); /* setup accels */ - gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.next", next_accel); - gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.previous", previous_accel); - gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.today", today_accel); + gcal_window_add_accelerator (app, "win.next", "Right"); + gcal_window_add_accelerator (app, "win.next", "Right"); + gcal_window_add_accelerator (app, "win.previous", "Left"); + gcal_window_add_accelerator (app, "win.previous", "Left"); + gcal_window_add_accelerator (app, "win.today", "Down"); + gcal_window_add_accelerator (app, "win.today", "Down"); + gcal_window_add_accelerator (app, "win.today", "t"); /* loading size */ load_geometry (win); From 6a017cca8b1fcb76e9c5e6561406f6da611f70ca Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 13 May 2015 20:13:18 -0300 Subject: [PATCH 3/8] window: add view switching shortcuts PgUp to go to the previous view PgDn to go to the next view 2 to go to month view 3 to go to year view --- src/gcal-window.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/gcal-window.c b/src/gcal-window.c index f86d09d6..8b3b682f 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -140,6 +140,10 @@ static void on_date_action_activated (GSimpleAction *a GVariant *param, gpointer user_data); +static void on_view_action_activated (GSimpleAction *action, + GVariant *param, + gpointer user_data); + static gboolean key_pressed (GtkWidget *widget, GdkEvent *event, gpointer user_data); @@ -269,7 +273,8 @@ G_DEFINE_TYPE_WITH_PRIVATE (GcalWindow, gcal_window, GTK_TYPE_APPLICATION_WINDOW static const GActionEntry actions[] = { {"next", on_date_action_activated }, {"previous", on_date_action_activated }, - {"today", on_date_action_activated } + {"today", on_date_action_activated }, + {"change-view", on_view_action_activated, "i" }, }; static void @@ -293,6 +298,31 @@ on_date_action_activated (GSimpleAction *action, date_updated (GTK_BUTTON (priv->today_button), user_data); } +static void +on_view_action_activated (GSimpleAction *action, + GVariant *param, + gpointer user_data) +{ + GcalWindowPrivate *priv; + guint view; + + g_return_if_fail (GCAL_IS_WINDOW (user_data)); + + priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); + view = g_variant_get_int32 (param); + + // -1 means next view + if (view == -1) + view = ++(priv->active_view); + else if (view == -2) + view = --(priv->active_view); + + priv->active_view = CLAMP (view, GCAL_WINDOW_VIEW_MONTH, GCAL_WINDOW_VIEW_YEAR); + gtk_stack_set_visible_child (GTK_STACK (priv->views_stack), priv->views[priv->active_view]); + + g_object_notify (G_OBJECT (user_data), "active-view"); +} + static gboolean key_pressed (GtkWidget *widget, GdkEvent *event, @@ -1636,6 +1666,11 @@ gcal_window_new_with_view_and_date (GcalApplication *app, gcal_window_add_accelerator (app, "win.today", "Down"); gcal_window_add_accelerator (app, "win.today", "t"); + gcal_window_add_accelerator (app, "win.change-view(-1)", "Page_Down"); + gcal_window_add_accelerator (app, "win.change-view(-2)", "Page_Up"); + gcal_window_add_accelerator (app, "win.change-view(2)", "2"); + gcal_window_add_accelerator (app, "win.change-view(3)", "3"); + /* loading size */ load_geometry (win); From 66c464d92eccdb73ae631331bcd0767e5a96c3d8 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 14 May 2015 15:35:38 -0300 Subject: [PATCH 4/8] window: remove unneeded shortcuts --- src/gcal-window.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gcal-window.c b/src/gcal-window.c index 8b3b682f..5cd4bb9a 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -1659,11 +1659,8 @@ gcal_window_new_with_view_and_date (GcalApplication *app, /* setup accels */ gcal_window_add_accelerator (app, "win.next", "Right"); - gcal_window_add_accelerator (app, "win.next", "Right"); gcal_window_add_accelerator (app, "win.previous", "Left"); - gcal_window_add_accelerator (app, "win.previous", "Left"); gcal_window_add_accelerator (app, "win.today", "Down"); - gcal_window_add_accelerator (app, "win.today", "Down"); gcal_window_add_accelerator (app, "win.today", "t"); gcal_window_add_accelerator (app, "win.change-view(-1)", "Page_Down"); From bbcf8fddad6c4971bf0cbad2791cc5f961ac26c7 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 17 May 2015 09:55:22 -0300 Subject: [PATCH 5/8] month-view: initial keyboard focus highlight --- src/gcal-month-view.c | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index be659254..cf781b67 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -45,6 +45,11 @@ typedef struct */ gint days_delay; + /* + * The cell whose keyboard focus is on. + */ + gint keyboard_cell; + /** * first day of the week according to user locale, being * 0 for Sunday, 1 for Monday and so on */ @@ -108,6 +113,9 @@ static void update_list_box_headers (GtkListBoxRow *row GtkListBoxRow *before, gpointer user_data); +static gboolean gcal_month_view_key_press (GtkWidget *widget, + GdkEventKey *event); + static void add_new_event_button_cb (GtkWidget *button, gpointer user_data); @@ -510,6 +518,55 @@ update_list_box_headers (GtkListBoxRow *row, } } +static gboolean +gcal_month_view_key_press (GtkWidget *widget, + GdkEventKey *event) +{ + GcalMonthViewPrivate *priv; + + g_return_val_if_fail (GCAL_IS_MONTH_VIEW (widget), FALSE); + + priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); + + if (!(event->state & GDK_MODIFIER_MASK)) + { + switch (event->keyval) + { + case GDK_KEY_Up: + priv->keyboard_cell -= 7; + break; + + case GDK_KEY_Down: + priv->keyboard_cell += 7; + break; + + case GDK_KEY_Left: + priv->keyboard_cell--; + break; + + case GDK_KEY_Right: + priv->keyboard_cell++; + break; + + case GDK_KEY_Return: + + break; + + default: + return FALSE; + } + + priv->keyboard_cell = CLAMP (priv->keyboard_cell, priv->days_delay, + (priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year) - 1)); + + gtk_widget_queue_draw (widget); + + return TRUE; + } + + return FALSE; +} + static void add_new_event_button_cb (GtkWidget *button, gpointer user_data) @@ -554,6 +611,7 @@ gcal_month_view_class_init (GcalMonthViewClass *klass) widget_class->motion_notify_event = gcal_month_view_motion_notify_event; widget_class->button_release_event = gcal_month_view_button_release; widget_class->direction_changed = gcal_month_view_direction_changed; + widget_class->key_press_event = gcal_month_view_key_press; subscriber_view_class = GCAL_SUBSCRIBER_VIEW_CLASS (klass); subscriber_view_class->is_child_multicell = gcal_month_view_is_child_multiday; @@ -640,6 +698,7 @@ gcal_month_view_set_property (GObject *object, priv->date = g_value_dup_boxed (value); priv->days_delay = (time_day_of_week (1, priv->date->month - 1, priv->date->year) - priv->first_weekday + 7) % 7; + priv->keyboard_cell = -1; gtk_widget_queue_draw (GTK_WIDGET (object)); break; } @@ -1396,6 +1455,17 @@ gcal_month_view_draw (GtkWidget *widget, cairo_stroke (cr); } + if (priv->keyboard_cell > priv->days_delay - 1) + { + gint x; + gint y; + + x = cell_width * (priv->keyboard_cell % 7); + y = cell_height * (priv->keyboard_cell / 7 + first_row_gap) + start_grid_y; + + gtk_render_focus (context, cr, x, y, cell_width, cell_height); + } + g_object_unref (layout); if (GTK_WIDGET_CLASS (gcal_month_view_parent_class)->draw != NULL) @@ -1525,6 +1595,8 @@ gcal_month_view_button_release (GtkWidget *widget, days = priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year); sw = 1 - 2 * priv->k; + priv->keyboard_cell = -1; + released = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &released_indicator, &x, &y); j = 7 * ((released + 7 * priv->k) / 7) + sw * (released % 7) + (1 - priv->k); From e4b5da7f504717011f6c085de5768e55294fddb3 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 4 Jun 2015 12:48:50 -0300 Subject: [PATCH 6/8] month-view: implement keyboard navigation For this to be made, some significant changes on the inners of GcalMonthView had to be done. Later on, I'll do a complete rewrite of it, deprecating the use of 'sw' and 'k'. --- src/gcal-month-view.c | 380 ++++++++++++++++++++++++++---------------- 1 file changed, 238 insertions(+), 142 deletions(-) diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index cf781b67..0a67a47c 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -75,7 +75,7 @@ typedef struct gboolean use_24h_format; /* text direction factors */ - gint k; + gboolean k; icaltimetype *current_date; @@ -89,6 +89,8 @@ enum PROP_DATE, /* active-date inherited property */ }; +#define MIRROR(val,start,end) (start + (val / end) * end + (end - cell % end)) + static gint gather_button_event_data (GcalMonthView *view, gdouble x, gdouble y, @@ -180,6 +182,118 @@ G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view,GCAL_TYPE_SUBSCRIBER_VIE G_ADD_PRIVATE (GcalMonthView) G_IMPLEMENT_INTERFACE (GCAL_TYPE_VIEW, gcal_view_interface_init)); +static gint +real_cell (gint cell, + gboolean rtl) +{ + return rtl ? MIRROR (cell, 0, 7) - 1 : cell; +} + +static void +get_cell_position (GcalMonthView *view, + gint cell, + gdouble *out_x, + gdouble *out_y) +{ + GcalMonthViewPrivate *priv; + GtkWidget *widget; + + gdouble start_grid_y; + + gint shown_rows; + gdouble first_row_gap; + + gdouble cell_width; + gdouble cell_height; + + priv = gcal_month_view_get_instance_private (view); + widget = GTK_WIDGET (view); + + start_grid_y = get_start_grid_y (widget); + + shown_rows = ceil ((priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year)) / 7.0); + first_row_gap = (6 - shown_rows) * 0.5; /* invalid area before the actual rows */ + + cell_width = gtk_widget_get_allocated_width (widget) / 7.0; + cell_height = (gtk_widget_get_allocated_height (widget) - start_grid_y) / 6.0; + + if (out_x != NULL) + *out_x = cell_width * ((cell % 7) + 0.5); + + if (out_y != NULL) + *out_y = cell_height * ((cell / 7) + first_row_gap + 0.5) + start_grid_y; +} + +static gboolean +show_popover_for_position (GcalMonthView *view, + gdouble x, + gdouble y, + gboolean on_indicator) +{ + GcalSubscriberViewPrivate *ppriv; + GcalMonthViewPrivate *priv; + GtkWidget *widget; + + gint start_day, end_day; + + icaltimetype *start_date; + icaltimetype *end_date = NULL; + + widget = GTK_WIDGET (view); + priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); + ppriv = GCAL_SUBSCRIBER_VIEW (widget)->priv; + + if (priv->start_mark_cell < 0) + return FALSE; + + start_day = priv->start_mark_cell - priv->days_delay + 1; + end_day = priv->end_mark_cell - priv->days_delay + 1; + + if (priv->pressed_overflow_indicator != -1 && priv->start_mark_cell == priv->end_mark_cell && + g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (priv->pressed_overflow_indicator))) + { + priv->hovered_overflow_indicator = priv->pressed_overflow_indicator; + + rebuild_popover_for_day (GCAL_MONTH_VIEW (widget), end_day); + gtk_widget_show_all (priv->overflow_popover); + + gtk_widget_queue_draw (widget); + priv->pressed_overflow_indicator = -1; + priv->start_mark_cell = -1; + priv->end_mark_cell = -1; + return TRUE; + } + else + { + start_date = gcal_dup_icaltime (priv->date); + start_date->day = start_day; + start_date->is_date = 1; + + end_date = gcal_dup_icaltime (priv->date); + end_date->day = end_day; + end_date->is_date = 1; + + if (start_date->day > end_date->day) + { + gint day = start_date->day; + start_date->day = end_date->day; + end_date->day = day; + } + + end_date->day += 1; + + g_signal_emit_by_name (GCAL_VIEW (widget), "create-event", start_date, end_date, x, y); + + g_free (start_date); + g_free (end_date); + } + + gtk_widget_queue_draw (widget); + priv->pressed_overflow_indicator = -1; + + return FALSE; +} + static gint gather_button_event_data (GcalMonthView *view, gdouble x, @@ -362,6 +476,7 @@ rebuild_popover_for_day (GcalMonthView *view, gdouble start_grid_y, cell_width, cell_height; gint shown_rows; + gint real_clicked_popover_cell; GtkStyleContext *context; GtkStateFlags state; @@ -434,23 +549,24 @@ rebuild_popover_for_day (GcalMonthView *view, pango_font_description_free (ofont_desc); g_object_unref (overflow_layout); - rect.y = cell_height * ((priv->pressed_overflow_indicator / 7) + 1.0 + (6 - shown_rows) * 0.5) + start_grid_y - padding_bottom - font_height / 2; + real_clicked_popover_cell = real_cell (priv->pressed_overflow_indicator, priv->k); + rect.y = cell_height * ((real_clicked_popover_cell / 7) + 1.0 + (6 - shown_rows) * 0.5) + start_grid_y - padding_bottom - font_height / 2; rect.width = 1; rect.height = 1; - if (priv->pressed_overflow_indicator % 7 < 3) + if (real_clicked_popover_cell % 7 < 3) { - rect.x = cell_width * ((priv->pressed_overflow_indicator % 7) + 1.0); - gtk_popover_set_position (GTK_POPOVER (priv->overflow_popover), priv->k == 0 ? GTK_POS_RIGHT : GTK_POS_LEFT); + rect.x = cell_width * ((real_clicked_popover_cell % 7) + 1.0); + gtk_popover_set_position (GTK_POPOVER (priv->overflow_popover), priv->k ? GTK_POS_LEFT : GTK_POS_RIGHT); } - else if (priv->pressed_overflow_indicator % 7 > 3) + else if (real_clicked_popover_cell % 7 > 3) { - rect.x = cell_width * ((priv->pressed_overflow_indicator % 7)); - gtk_popover_set_position (GTK_POPOVER (priv->overflow_popover), priv->k == 0 ? GTK_POS_LEFT : GTK_POS_RIGHT); + rect.x = cell_width * ((real_clicked_popover_cell % 7)); + gtk_popover_set_position (GTK_POPOVER (priv->overflow_popover), priv->k ? GTK_POS_RIGHT : GTK_POS_LEFT); } else { - rect.x = cell_width * ((priv->pressed_overflow_indicator % 7) + 1.0 - priv->k); + rect.x = cell_width * ((real_clicked_popover_cell % 7) + 1.0 - priv->k); gtk_popover_set_position (GTK_POPOVER (priv->overflow_popover), GTK_POS_RIGHT); } gtk_popover_set_pointing_to (GTK_POPOVER (priv->overflow_popover), &rect); @@ -523,48 +639,86 @@ gcal_month_view_key_press (GtkWidget *widget, GdkEventKey *event) { GcalMonthViewPrivate *priv; + gboolean selection, valid_key; + gdouble x, y; + gint min, max, diff; g_return_val_if_fail (GCAL_IS_MONTH_VIEW (widget), FALSE); priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); + selection = event->state & GDK_SHIFT_MASK; + valid_key = FALSE; + diff = 0; + min = priv->days_delay; + max = priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year) - 1; - if (!(event->state & GDK_MODIFIER_MASK)) + /* + * If it's starting the selection right now, it should mark the current keyboard + * focused cell as the start, and then update the end mark after updating the + * focused cell. + */ + if (selection && priv->start_mark_cell == -1) { - switch (event->keyval) - { - case GDK_KEY_Up: - priv->keyboard_cell -= 7; - break; - - case GDK_KEY_Down: - priv->keyboard_cell += 7; - break; - - case GDK_KEY_Left: - priv->keyboard_cell--; - break; - - case GDK_KEY_Right: - priv->keyboard_cell++; - break; - - case GDK_KEY_Return: - - break; - - default: - return FALSE; - } - - priv->keyboard_cell = CLAMP (priv->keyboard_cell, priv->days_delay, - (priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year) - 1)); - - gtk_widget_queue_draw (widget); - - return TRUE; + priv->start_mark_cell = priv->keyboard_cell; } - return FALSE; + switch (event->keyval) + { + case GDK_KEY_Up: + valid_key = TRUE; + diff = -7; + break; + + case GDK_KEY_Down: + valid_key = TRUE; + diff = 7; + break; + + case GDK_KEY_Left: + valid_key = TRUE; + diff = priv->k ? 1 : -1; + break; + + case GDK_KEY_Right: + valid_key = TRUE; + diff = priv->k ? -1 : 1; + break; + + case GDK_KEY_Return: + /* + * If it's not on the selection mode (i.e. shift is not pressed), we should + * simulate it by changing the start & end selected cells = keyboard cell. + */ + if (!selection && priv->start_mark_cell == -1 && priv->end_mark_cell == -1) + priv->start_mark_cell = priv->end_mark_cell = real_cell (priv->keyboard_cell, priv->k); + + get_cell_position (GCAL_MONTH_VIEW (widget), priv->start_mark_cell, &x, &y); + show_popover_for_position (GCAL_MONTH_VIEW (widget), x, y, FALSE); + break; + + default: + return FALSE; + } + + if (priv->keyboard_cell + diff <= max && priv->keyboard_cell + diff >= min) + { + priv->keyboard_cell += diff; + } + + if (selection) + { + priv->end_mark_cell = priv->keyboard_cell; + } + else if (!selection && valid_key) + { + /* Cancel selection if SHIFT is not pressed */ + priv->start_mark_cell = priv->end_mark_cell = -1; + } + + if (diff != 0) + gtk_widget_queue_draw (widget); + + return TRUE; } static void @@ -640,10 +794,7 @@ gcal_month_view_init (GcalMonthView *self) gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "calendar-view"); - if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR) - priv->k = 0; - else if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) - priv->k = 1; + priv->k = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; priv->overflow_popover = gtk_popover_new (GTK_WIDGET (self)); gtk_style_context_add_class (gtk_widget_get_style_context (priv->overflow_popover), "events"); @@ -698,7 +849,9 @@ gcal_month_view_set_property (GObject *object, priv->date = g_value_dup_boxed (value); priv->days_delay = (time_day_of_week (1, priv->date->month - 1, priv->date->year) - priv->first_weekday + 7) % 7; - priv->keyboard_cell = -1; + priv->keyboard_cell = priv->days_delay; + priv->start_mark_cell = -1; + priv->end_mark_cell = -1; gtk_widget_queue_draw (GTK_WIDGET (object)); break; } @@ -1151,10 +1304,11 @@ gcal_month_view_draw (GtkWidget *widget, sw = 1 - 2 * priv->k; /* active cells */ - if (priv->start_mark_cell != -1 && priv->end_mark_cell != -1) + if (priv->start_mark_cell > -1 && priv->end_mark_cell > -1) { - lower_mark = 7 * ((priv->start_mark_cell + 7 * priv->k) / 7) + sw * (priv->start_mark_cell % 7) + (1 - priv->k); - upper_mark = 7 * ((priv->end_mark_cell + 7 * priv->k) / 7) + sw * (priv->end_mark_cell % 7) + (1 - priv->k); + lower_mark = priv->start_mark_cell - priv->days_delay + 1; + upper_mark = priv->end_mark_cell - priv->days_delay + 1; + if (lower_mark > upper_mark) { gint cell; @@ -1162,9 +1316,6 @@ gcal_month_view_draw (GtkWidget *widget, lower_mark = upper_mark; upper_mark = cell; } - - lower_mark -= priv->days_delay; - upper_mark -= priv->days_delay; } /* view headers */ @@ -1238,6 +1389,7 @@ gcal_month_view_draw (GtkWidget *widget, gchar *nr_day; gint column = i % 7; gint row = i / 7; + gint real_i = real_cell (i, priv->k); j = 7 * ((i + 7 * priv->k) / 7) + sw * (i % 7) + (1 - priv->k); if (j <= priv->days_delay) @@ -1248,7 +1400,7 @@ gcal_month_view_draw (GtkWidget *widget, nr_day = g_strdup_printf ("%d", j); - if (g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (i))) + if (g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (real_i))) { GList *l; PangoLayout *overflow_layout; @@ -1256,7 +1408,7 @@ gcal_month_view_draw (GtkWidget *widget, gchar *overflow_str; gdouble y_value; - l = g_hash_table_lookup (ppriv->overflow_cells, GINT_TO_POINTER (i)); + l = g_hash_table_lookup (ppriv->overflow_cells, GINT_TO_POINTER (real_i)); /* TODO: Warning in some languages this string can be too long and may overlap wit the number */ overflow_str = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Other event", "Other %d events", g_list_length (l)), @@ -1264,7 +1416,7 @@ gcal_month_view_draw (GtkWidget *widget, gtk_style_context_save (context); gtk_style_context_add_class (context, "overflow"); - if (priv->hovered_overflow_indicator == i) + if (priv->hovered_overflow_indicator == real_i) { gtk_style_context_set_state (context, state | GTK_STATE_FLAG_PRELIGHT); gtk_style_context_get (context, state | GTK_STATE_FLAG_PRELIGHT, "font", &ofont_desc, NULL); @@ -1282,7 +1434,7 @@ gcal_month_view_draw (GtkWidget *widget, pango_layout_get_pixel_size (overflow_layout, &font_width, &font_height); y_value = cell_height * (row + 1 + first_row_gap) - font_height - padding.bottom + start_grid_y; - if ((!gtk_widget_is_visible (priv->overflow_popover) && priv->hovered_overflow_indicator == i) || + if ((!gtk_widget_is_visible (priv->overflow_popover) && priv->hovered_overflow_indicator == real_i) || gtk_widget_is_visible (priv->overflow_popover)) { gtk_render_background (context, cr, cell_width * column, y_value - padding.bottom, @@ -1395,12 +1547,13 @@ gcal_month_view_draw (GtkWidget *widget, gint first_mark, last_mark; gint pos_x2, pos_y2; - first_mark = priv->start_mark_cell; - last_mark = priv->end_mark_cell; - if (priv->start_mark_cell > priv->end_mark_cell) + first_mark = real_cell (priv->start_mark_cell, priv->k); + last_mark = real_cell (priv->end_mark_cell, priv->k); + if (first_mark > last_mark) { - last_mark = priv->start_mark_cell; - first_mark = priv->end_mark_cell; + gint tmp = first_mark; + first_mark = last_mark; + last_mark = tmp; } gtk_style_context_get_color (context, state | GTK_STATE_FLAG_SELECTED, &color); @@ -1455,12 +1608,16 @@ gcal_month_view_draw (GtkWidget *widget, cairo_stroke (cr); } + /* Draw focus rectangle */ if (priv->keyboard_cell > priv->days_delay - 1) { + gint localized_cell; gint x; gint y; - x = cell_width * (priv->keyboard_cell % 7); + localized_cell = real_cell (priv->keyboard_cell % 7, priv->k); + + x = cell_width * (localized_cell); y = cell_height * (priv->keyboard_cell / 7 + first_row_gap) + start_grid_y; gtk_render_focus (context, cr, x, y, cell_width, cell_height); @@ -1481,21 +1638,20 @@ gcal_month_view_button_press (GtkWidget *widget, GcalSubscriberViewPrivate *ppriv; GcalMonthViewPrivate *priv; - gint days, j, sw, clicked_cell; + gint days, clicked_cell; gboolean pressed_indicator = FALSE; priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); ppriv = GCAL_SUBSCRIBER_VIEW (widget)->priv; days = priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year); - sw = 1 - 2 * priv->k; clicked_cell = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &pressed_indicator, NULL, NULL); - j = 7 * ((clicked_cell + 7 * priv->k) / 7) + sw * (clicked_cell % 7) + (1 - priv->k); + clicked_cell = real_cell (clicked_cell, priv->k); - if (j > priv->days_delay && j <= days) + if (clicked_cell >= priv->days_delay && clicked_cell <= days) priv->start_mark_cell = clicked_cell; if (pressed_indicator && g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (clicked_cell))) @@ -1521,22 +1677,22 @@ gcal_month_view_motion_notify_event (GtkWidget *widget, GcalMonthViewPrivate *priv; gint days; - gint j, sw; gint new_end_cell, new_hovered_cell; gboolean hovered_indicator = FALSE; priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); days = priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year); - sw = 1 - 2 * priv->k; new_end_cell = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &hovered_indicator, NULL, NULL); - - j = 7 * ((new_end_cell + 7 * priv->k) / 7) + sw * (new_end_cell % 7) + (1 - priv->k); + new_end_cell = real_cell (new_end_cell, priv->k); if (priv->start_mark_cell != -1) { - if (j > priv->days_delay && j <= days) + if (!(event->state & GDK_BUTTON_PRESS_MASK)) + return TRUE; + + if (new_end_cell >= priv->days_delay && new_end_cell < days) { if (priv->end_mark_cell != priv->start_mark_cell) priv->hovered_overflow_indicator = -1; @@ -1572,89 +1728,29 @@ static gboolean gcal_month_view_button_release (GtkWidget *widget, GdkEventButton *event) { - GcalSubscriberViewPrivate *ppriv; GcalMonthViewPrivate *priv; - + gdouble x, y; gint days; - - gint j, sw; - gint released; gboolean released_indicator = FALSE; - gdouble x, y; - - icaltimetype *start_date; - icaltimetype *end_date = NULL; - priv = gcal_month_view_get_instance_private (GCAL_MONTH_VIEW (widget)); - ppriv = GCAL_SUBSCRIBER_VIEW (widget)->priv; - - if (priv->start_mark_cell == -1) - return FALSE; - days = priv->days_delay + icaltime_days_in_month (priv->date->month, priv->date->year); - sw = 1 - 2 * priv->k; - priv->keyboard_cell = -1; + priv->end_mark_cell = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &released_indicator, &x, &y); + priv->end_mark_cell = real_cell (priv->end_mark_cell, priv->k); - released = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &released_indicator, &x, &y); - - j = 7 * ((released + 7 * priv->k) / 7) + sw * (released % 7) + (1 - priv->k); - - if (j <= priv->days_delay || j > days) + if (priv->end_mark_cell >= priv->days_delay && priv->end_mark_cell < days) { - priv->pressed_overflow_indicator = -1; - priv->start_mark_cell = -1; - priv->end_mark_cell = -1; - gtk_widget_queue_draw (widget); - - return TRUE; - } - - priv->end_mark_cell = released; - - if (priv->pressed_overflow_indicator != -1 && priv->start_mark_cell == priv->end_mark_cell && - g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (priv->pressed_overflow_indicator))) - { - priv->hovered_overflow_indicator = priv->pressed_overflow_indicator; - - rebuild_popover_for_day (GCAL_MONTH_VIEW (widget), j - priv->days_delay); - gtk_widget_show_all (priv->overflow_popover); - - gtk_widget_queue_draw (widget); - priv->pressed_overflow_indicator = -1; - priv->start_mark_cell = -1; - priv->end_mark_cell = -1; - return TRUE; + return show_popover_for_position (GCAL_MONTH_VIEW (widget), x, y, released_indicator); } else { - start_date = gcal_dup_icaltime (priv->date); - start_date->day = 7 * ((priv->start_mark_cell + 7 * priv->k) / 7) + sw * (priv->start_mark_cell % 7) + (1 - priv->k); - start_date->day -= priv->days_delay; - start_date->is_date = 1; + /* If the button is released over an invalid cell, entirely cancel the selection */ + priv->start_mark_cell = priv->end_mark_cell = -1; + gtk_widget_queue_draw (widget); - end_date = gcal_dup_icaltime (priv->date); - end_date->day = j - priv->days_delay; - end_date->is_date = 1; - - if (start_date->day > end_date->day) - { - gint day = start_date->day; - start_date->day = end_date->day; - end_date->day = day; - } - end_date->day += 1 ; - - g_signal_emit_by_name (GCAL_VIEW (widget), "create-event", start_date, end_date, x, y); - - g_free (start_date); - g_free (end_date); + return FALSE; } - - gtk_widget_queue_draw (widget); - priv->pressed_overflow_indicator = -1; - return TRUE; } static void From 5381dc77a4abe12dce9f0c8b240237aad4c02b65 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 4 Jun 2015 13:02:47 -0300 Subject: [PATCH 7/8] month-view: update focused cell on mouse events Also, quit selection mode when Escape is pressed --- src/gcal-month-view.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index 0a67a47c..6010c7f8 100644 --- a/src/gcal-month-view.c +++ b/src/gcal-month-view.c @@ -690,12 +690,16 @@ gcal_month_view_key_press (GtkWidget *widget, * simulate it by changing the start & end selected cells = keyboard cell. */ if (!selection && priv->start_mark_cell == -1 && priv->end_mark_cell == -1) - priv->start_mark_cell = priv->end_mark_cell = real_cell (priv->keyboard_cell, priv->k); + priv->start_mark_cell = priv->end_mark_cell = priv->keyboard_cell; - get_cell_position (GCAL_MONTH_VIEW (widget), priv->start_mark_cell, &x, &y); + get_cell_position (GCAL_MONTH_VIEW (widget), real_cell (priv->end_mark_cell, priv->k), &x, &y); show_popover_for_position (GCAL_MONTH_VIEW (widget), x, y, FALSE); break; + case GDK_KEY_Escape: + priv->start_mark_cell = priv->end_mark_cell = -1; + break; + default: return FALSE; } @@ -715,8 +719,7 @@ gcal_month_view_key_press (GtkWidget *widget, priv->start_mark_cell = priv->end_mark_cell = -1; } - if (diff != 0) - gtk_widget_queue_draw (widget); + gtk_widget_queue_draw (widget); return TRUE; } @@ -1652,7 +1655,10 @@ gcal_month_view_button_press (GtkWidget *widget, clicked_cell = real_cell (clicked_cell, priv->k); if (clicked_cell >= priv->days_delay && clicked_cell <= days) - priv->start_mark_cell = clicked_cell; + { + priv->start_mark_cell = clicked_cell; + priv->keyboard_cell = clicked_cell; + } if (pressed_indicator && g_hash_table_contains (ppriv->overflow_cells, GINT_TO_POINTER (clicked_cell))) priv->pressed_overflow_indicator = clicked_cell; @@ -1694,6 +1700,8 @@ gcal_month_view_motion_notify_event (GtkWidget *widget, if (new_end_cell >= priv->days_delay && new_end_cell < days) { + priv->keyboard_cell = new_end_cell; + if (priv->end_mark_cell != priv->start_mark_cell) priv->hovered_overflow_indicator = -1; if (priv->end_mark_cell != new_end_cell) @@ -1741,6 +1749,7 @@ gcal_month_view_button_release (GtkWidget *widget, if (priv->end_mark_cell >= priv->days_delay && priv->end_mark_cell < days) { + priv->keyboard_cell = priv->end_mark_cell; return show_popover_for_position (GCAL_MONTH_VIEW (widget), x, y, released_indicator); } else From 1f666ba40527363e6777e7817ca4e5ce290ba437 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 4 Jun 2015 15:44:48 -0300 Subject: [PATCH 8/8] window: move forward/backward according to locale --- src/gcal-window.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gcal-window.c b/src/gcal-window.c index 5cd4bb9a..a0d89cc9 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -97,6 +97,7 @@ typedef struct icaltimetype *active_date; icaltimetype *current_date; + gboolean rtl; /* states */ gboolean new_event_mode; @@ -429,8 +430,10 @@ date_updated (GtkButton *button, icaltimetype *new_date; gboolean move_back, move_today; + gint factor; priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); + factor = priv->rtl ? - 1 : 1; move_today = priv->today_button == (GtkWidget*) button; move_back = priv->back_button == (GtkWidget*) button; @@ -446,17 +449,17 @@ date_updated (GtkButton *button, switch (priv->active_view) { case GCAL_WINDOW_VIEW_DAY: - new_date->day += 1 * (move_back ? -1 : 1); + new_date->day += 1 * factor * (move_back ? -1 : 1); break; case GCAL_WINDOW_VIEW_WEEK: - new_date->day += 7 * (move_back ? -1 : 1); + new_date->day += 7 * factor * (move_back ? -1 : 1); break; case GCAL_WINDOW_VIEW_MONTH: new_date->day = 1; - new_date->month += 1 * (move_back ? -1 : 1); + new_date->month += 1 * factor * (move_back ? -1 : 1); break; case GCAL_WINDOW_VIEW_YEAR: - new_date->year += 1 * (move_back ? -1 : 1); + new_date->year += 1 * factor * (move_back ? -1 : 1); break; case GCAL_WINDOW_VIEW_LIST: case GCAL_WINDOW_VIEW_SEARCH: @@ -1410,6 +1413,7 @@ gcal_window_init (GcalWindow *self) GcalWindowPrivate *priv = gcal_window_get_instance_private (self); priv->active_date = g_new0 (icaltimetype, 1); + priv->rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; gtk_widget_init_template (GTK_WIDGET (self)); }