month-view: add keyboard navigation

This big patch implements keyboard navigation for
Month view and the main window. Also, refactor
some of it's drawing code.
This commit is contained in:
Georges Basile Stavracas Neto
2015-07-15 20:40:55 -03:00
3 changed files with 377 additions and 112 deletions
+3 -3
View File
@@ -190,7 +190,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="date_updated" swapped="no"/>
<property name="action_name">win.previous</property>
<child>
<object class="GtkImage" id="go_back_image">
<property name="visible">True</property>
@@ -205,7 +205,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="date_updated" object="GcalWindow" swapped="no"/>
<property name="action_name">win.today</property>
</object>
<packing>
<property name="position">1</property>
@@ -216,7 +216,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="date_updated" swapped="no"/>
<property name="action_name">win.next</property>
<child>
<object class="GtkImage" id="go_next_image">
<property name="visible">True</property>
+282 -105
View File
@@ -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
+92 -4
View File
@@ -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", "<Alt>Right");
gcal_window_add_accelerator (app, "win.previous", "<Alt>Left");
gcal_window_add_accelerator (app, "win.today", "<Alt>Down");
gcal_window_add_accelerator (app, "win.today", "<Ctrl>t");
gcal_window_add_accelerator (app, "win.change-view(-1)", "<Ctrl>Page_Down");
gcal_window_add_accelerator (app, "win.change-view(-2)", "<Ctrl>Page_Up");
gcal_window_add_accelerator (app, "win.change-view(2)", "<Ctrl>2");
gcal_window_add_accelerator (app, "win.change-view(3)", "<Ctrl>3");
/* loading size */
load_geometry (win);