diff --git a/data/ui/window.ui b/data/ui/window.ui index 6c36b03f..01519411 100644 --- a/data/ui/window.ui +++ b/data/ui/window.ui @@ -190,7 +190,7 @@ True True True - + win.previous True @@ -205,7 +205,7 @@ True True True - + win.today 1 @@ -216,7 +216,7 @@ True True True - + win.next True diff --git a/src/gcal-month-view.c b/src/gcal-month-view.c index 5a8a0b9d..c999de92 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 */ @@ -70,7 +75,7 @@ typedef struct gboolean use_24h_format; /* text direction factors */ - gint k; + gboolean k; icaltimetype *current_date; @@ -84,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, @@ -108,6 +115,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); @@ -172,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, @@ -354,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; @@ -426,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); @@ -510,6 +634,96 @@ update_list_box_headers (GtkListBoxRow *row, } } +static gboolean +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 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) + { + priv->start_mark_cell = priv->keyboard_cell; + } + + 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 = priv->keyboard_cell; + + 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; + } + + 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; + } + + gtk_widget_queue_draw (widget); + + return TRUE; +} + static void add_new_event_button_cb (GtkWidget *button, gpointer user_data) @@ -554,6 +768,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; @@ -582,10 +797,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"); @@ -640,6 +852,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 = priv->days_delay; + priv->start_mark_cell = -1; + priv->end_mark_cell = -1; gtk_widget_queue_draw (GTK_WIDGET (object)); break; } @@ -1092,10 +1307,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; @@ -1103,9 +1319,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 */ @@ -1179,6 +1392,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) @@ -1189,7 +1403,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; @@ -1197,7 +1411,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 with the number */ overflow_str = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Other event", "Other %d events", g_list_length (l)), @@ -1205,7 +1419,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); @@ -1223,7 +1437,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, @@ -1336,12 +1550,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); @@ -1396,6 +1611,21 @@ 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; + + 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); + } + g_object_unref (layout); if (GTK_WIDGET_CLASS (gcal_month_view_parent_class)->draw != NULL) @@ -1411,22 +1641,24 @@ 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) - priv->start_mark_cell = clicked_cell; + if (clicked_cell >= priv->days_delay && clicked_cell <= days) + { + 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; @@ -1451,23 +1683,25 @@ 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) { + 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) @@ -1502,87 +1736,30 @@ 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; - released = gather_button_event_data (GCAL_MONTH_VIEW (widget), event->x, event->y, &released_indicator, &x, &y); + 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); - 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; + priv->keyboard_cell = priv->end_mark_cell; + 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 diff --git a/src/gcal-window.c b/src/gcal-window.c index eab36b7b..6e17232e 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -98,6 +98,7 @@ typedef struct icaltimetype *active_date; icaltimetype *current_date; + gboolean rtl; /* states */ gboolean new_event_mode; @@ -132,6 +133,20 @@ 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); + +static void on_view_action_activated (GSimpleAction *action, + GVariant *param, + gpointer user_data); + static gboolean key_pressed (GtkWidget *widget, GdkEvent *event, gpointer user_data); @@ -279,6 +294,59 @@ 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 }, + {"change-view", on_view_action_activated, "i" }, +}; + +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 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, @@ -385,8 +453,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; @@ -402,17 +472,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: @@ -1455,6 +1525,7 @@ gcal_window_init (GcalWindow *self) G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); 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)); } @@ -1486,6 +1557,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, @@ -1697,6 +1774,17 @@ gcal_window_new_with_view_and_date (GcalApplication *app, win = g_object_new (GCAL_TYPE_WINDOW, "application", GTK_APPLICATION (app), "manager", manager, "active-date", date, NULL); + /* setup accels */ + gcal_window_add_accelerator (app, "win.next", "Right"); + gcal_window_add_accelerator (app, "win.previous", "Left"); + 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);