diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css index d82bb943..87f2bff0 100644 --- a/data/theme/gtk-styles.css +++ b/data/theme/gtk-styles.css @@ -100,13 +100,17 @@ font: 6; } -/* this is supposed be changed in sync with .calendar-view.current */ +/* these two are supposed be changed in sync with .calendar-view */ .year-navigator.current { background-color: @theme_fg_color; border-radius: 2px; color: white; font: bold; } +.year-navigator:selected { + color: @theme_selected_bg_color; + font-weight: bold; +} GcalEventWidget { border-radius: 6px; diff --git a/data/ui/year-view.ui b/data/ui/year-view.ui index 276cbdc7..1fad753f 100644 --- a/data/ui/year-view.ui +++ b/data/ui/year-view.ui @@ -6,11 +6,14 @@ True True + GDK_POINTER_MOTION_MASK|GDK_BUTTON_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_STRUCTURE_MASK + + + - 0 diff --git a/src/gcal-year-view.c b/src/gcal-year-view.c index 9d48c1a8..28688778 100644 --- a/src/gcal-year-view.c +++ b/src/gcal-year-view.c @@ -22,11 +22,25 @@ #include "gcal-utils.h" #include +#include #define NAVIGATOR_CELL_WIDTH (210 + 15) #define NAVIGATOR_CELL_HEIGHT 210 #define SIDEBAR_PREFERRED_WIDTH 200 +typedef struct +{ + /* month span from 0 to 11 */ + gint start_day, start_month; + gint end_day, end_month; +} ButtonData; + +typedef struct +{ + gint box_side; + GdkPoint coordinates [12]; +} GridData; + struct _GcalYearViewPrivate { /* composite, GtkBuilder's widgets */ @@ -38,8 +52,13 @@ struct _GcalYearViewPrivate /* manager singleton */ GcalManager *manager; + /* geometry info */ + GridData *navigator_grid; + /* state flags */ gboolean popover_mode; + gboolean button_pressed; + ButtonData *selected_data; /** * first day of the week according to user locale, being @@ -90,34 +109,111 @@ update_date (GcalYearView *year_view, gtk_widget_queue_draw (GTK_WIDGET (year_view)); } +static gboolean +calculate_day_month_for_coord (GcalYearView *year_view, + gdouble x, + gdouble y, + gint *out_day, + gint *out_month) +{ + GcalYearViewPrivate *priv = year_view->priv; + gint row, column, i, sw, box_side, clicked_cell, day, month; + + row = -1; + column = -1; + box_side = priv->navigator_grid->box_side; + sw = 1 - 2 * priv->k; + + /* y selection */ + for (i = 0; i < 4 && ((row == -1) || (column == -1)); i++) + { + if (row == -1 && + y > priv->navigator_grid->coordinates[i * 4].y + box_side && + y < priv->navigator_grid->coordinates[i * 4].y + box_side * 7) + { + row = i; + } + if (column == -1 && + x > priv->navigator_grid->coordinates[i].x + box_side * 0.5 && + x < priv->navigator_grid->coordinates[i].x + box_side * 7.5) + { + column = i; + } + } + + if (row == -1 || column == -1) + return FALSE; + + month = row * 4 + column; + row = (y - (priv->navigator_grid->coordinates[month].y + box_side)) / box_side; + column = (x - (priv->navigator_grid->coordinates[month].x + box_side * 0.5)) / box_side; + clicked_cell = row * 7 + column; + day = 7 * ((clicked_cell + 7 * priv->k) / 7) + sw * (clicked_cell % 7) + (1 - priv->k); + day -= ((time_day_of_week (1, month, priv->date->year) - priv->first_weekday + 7) % 7); + + if (day < 1 || day > time_days_in_month (priv->date->year, month)) + return FALSE; + + *out_day = day; + *out_month = month; + return TRUE; +} + +static void +order_selected_data (ButtonData *selected_data) +{ + gint swap; + if (selected_data->end_month < selected_data->start_month) + { + swap = selected_data->start_month; + selected_data->start_month = selected_data->end_month; + selected_data->end_month = swap; + + swap = selected_data->start_day; + selected_data->start_day = selected_data->end_day; + selected_data->end_day = swap; + } + else if (selected_data->start_month == selected_data->end_month && selected_data->end_day < selected_data->start_day) + { + swap = selected_data->start_day; + selected_data->start_day = selected_data->end_day; + selected_data->end_day = swap; + } +} + static void draw_month_grid (GcalYearView *year_view, GtkWidget *widget, cairo_t *cr, gint month_nr, - gint *weeks_counter, - gint block_side, - gdouble x, - gdouble y) + gint *weeks_counter) { GcalYearViewPrivate *priv = year_view->priv; GtkStyleContext *context; GtkStateFlags state_flags; - PangoLayout *layout; - PangoFontDescription *font_desc; + PangoLayout *layout, *slayout; + PangoFontDescription *font_desc, *sfont_desc; GdkRGBA color; gint layout_width, layout_height, i, j, sw; - gint column, row, box_padding_top, box_padding_start; + gint x, y, column, row, box_side, box_padding_top, box_padding_start; gint days_delay, days, shown_rows, sunday_idx; gchar *str, *nr_day, *nr_week; + gboolean selected_day; cairo_save (cr); context = gtk_widget_get_style_context (widget); state_flags = gtk_widget_get_state_flags (widget); sw = 1 - 2 * priv->k; + box_side = priv->navigator_grid->box_side; + x = priv->navigator_grid->coordinates[month_nr].x; + y = priv->navigator_grid->coordinates[month_nr].y; + + gtk_style_context_get (context, state_flags | GTK_STATE_FLAG_SELECTED, "font", &sfont_desc, NULL); + slayout = gtk_widget_create_pango_layout (widget, NULL); + pango_layout_set_font_description (slayout, sfont_desc); /* header */ gtk_style_context_save (context); @@ -129,11 +225,11 @@ draw_month_grid (GcalYearView *year_view, layout = gtk_widget_create_pango_layout (widget, str); pango_layout_set_font_description (layout, font_desc); pango_layout_get_pixel_size (layout, &layout_width, &layout_height); - gtk_render_layout (context, cr, x + (block_side * 8 - layout_width) / 2, y + (block_side - layout_height) / 2, + gtk_render_layout (context, cr, x + (box_side * 8 - layout_width) / 2, y + (box_side - layout_height) / 2, layout); gtk_render_background (context, cr, - x + (block_side * 8 - layout_width) / 2, y + (block_side - layout_height) / 2, + x + (box_side * 8 - layout_width) / 2, y + (box_side - layout_height) / 2, layout_width, layout_width); pango_font_description_free (font_desc); @@ -147,8 +243,8 @@ draw_month_grid (GcalYearView *year_view, gtk_style_context_get_color (context, state_flags, &color); cairo_set_line_width (cr, 0.2); gdk_cairo_set_source_rgba (cr, &color); - cairo_move_to (cr, x + block_side / 2, y + block_side + 0.4); - cairo_rel_line_to (cr, 7 * block_side, 0); + cairo_move_to (cr, x + box_side / 2, y + box_side + 0.4); + cairo_rel_line_to (cr, 7 * box_side, 0); cairo_stroke (cr); gtk_style_context_restore (context); @@ -180,8 +276,31 @@ draw_month_grid (GcalYearView *year_view, nr_day = g_strdup_printf ("%d", j); pango_layout_set_text (layout, nr_day, -1); pango_layout_get_pixel_size (layout, &layout_width, &layout_height); - box_padding_top = (block_side - layout_height) / 2 > 0 ? (block_side - layout_height) / 2 : 0; - box_padding_start = (block_side - layout_width) / 2 > 0 ? (block_side - layout_width) / 2 : 0; + box_padding_top = (box_side - layout_height) / 2 > 0 ? (box_side - layout_height) / 2 : 0; + box_padding_start = (box_side - layout_width) / 2 > 0 ? (box_side - layout_width) / 2 : 0; + + selected_day = FALSE; + if (priv->selected_data->start_day != 0) + { + ButtonData selected_data = *(priv->selected_data); + order_selected_data (&selected_data); + if (month_nr > selected_data.start_month && month_nr < selected_data.end_month) + { + selected_day = TRUE; + } + else if (month_nr == selected_data.start_month && month_nr == selected_data.end_month) + { + selected_day = j >= selected_data.start_day && j <= selected_data.end_day; + } + else if (month_nr == selected_data.start_month && j >= selected_data.start_day) + { + selected_day = TRUE; + } + else if (month_nr == selected_data.end_month && j <= selected_data.end_day) + { + selected_day = TRUE; + } + } if (priv->date->year == priv->current_date->year && month_nr + 1 == priv->current_date->month && j == priv->current_date->day) @@ -196,35 +315,53 @@ draw_month_grid (GcalYearView *year_view, gtk_style_context_get (context, state_flags, "font", &cfont_desc, NULL); pango_layout_set_font_description (clayout, cfont_desc); pango_layout_get_pixel_size (clayout, &layout_width, &layout_height); + box_padding_top = (box_side - layout_height) / 2 > 0 ? (box_side - layout_height) / 2 : 0; + box_padding_start = (box_side - layout_width) / 2 > 0 ? (box_side - layout_width) / 2 : 0; /* FIXME: hardcoded padding of the number background */ gtk_render_background (context, cr, - block_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width - 2.0, - block_side * (row + 1) + y + box_padding_top - 1.0, + box_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width - 2.0, + box_side * (row + 1) + y + box_padding_top - 1.0, layout_width + 4.0, layout_height + 2.0); gtk_render_layout (context, cr, - block_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, - block_side * (row + 1) + y + box_padding_top, + box_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, + box_side * (row + 1) + y + box_padding_top, clayout); gtk_style_context_restore (context); pango_font_description_free (cfont_desc); g_object_unref (clayout); } + else if (selected_day) + { + gtk_style_context_set_state (context, state_flags | GTK_STATE_FLAG_SELECTED); + + pango_layout_set_text (slayout, nr_day, -1); + pango_layout_get_pixel_size (slayout, &layout_width, &layout_height); + box_padding_top = (box_side - layout_height) / 2 > 0 ? (box_side - layout_height) / 2 : 0; + box_padding_start = (box_side - layout_width) / 2 > 0 ? (box_side - layout_width) / 2 : 0; + + gtk_render_layout (context, cr, + box_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, + box_side * (row + 1) + y + box_padding_top, + slayout); + + gtk_style_context_set_state (context, state_flags); + } else if (column == sunday_idx) { gtk_style_context_add_class (context, "sunday"); gtk_render_layout (context, cr, - block_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, - block_side * (row + 1) + y + box_padding_top, + box_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, + box_side * (row + 1) + y + box_padding_top, layout); gtk_style_context_remove_class (context, "sunday"); } else { gtk_render_layout (context, cr, - block_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, - block_side * (row + 1) + y + box_padding_top, + box_side * (column + 0.5 + priv->k) + x + sw * box_padding_start - priv->k * layout_width, + box_side * (row + 1) + y + box_padding_top, layout); } @@ -254,19 +391,22 @@ draw_month_grid (GcalYearView *year_view, pango_layout_set_text (layout, nr_week, -1); pango_layout_get_pixel_size (layout, &layout_width, &layout_height); - box_padding_top = (block_side - layout_height) / 2 > 0 ? (block_side - layout_height) / 2 : 0; - box_padding_start = ((block_side / 2) - layout_width) / 2 > 0 ? ((block_side / 2) - layout_width) / 2 : 0; + box_padding_top = (box_side - layout_height) / 2 > 0 ? (box_side - layout_height) / 2 : 0; + box_padding_start = ((box_side / 2) - layout_width) / 2 > 0 ? ((box_side / 2) - layout_width) / 2 : 0; gtk_render_layout (context, cr, - x + sw * box_padding_start + priv->k * (8 * block_side - layout_width), - block_side * (i + 1) + y + box_padding_top, + x + sw * box_padding_start + priv->k * (8 * box_side - layout_width), + box_side * (i + 1) + y + box_padding_top, layout); g_free (nr_week); } - pango_font_description_free (font_desc); gtk_style_context_restore (context); + pango_font_description_free (sfont_desc); + g_object_unref (slayout); + + pango_font_description_free (font_desc); g_object_unref (layout); cairo_restore (cr); } @@ -282,15 +422,13 @@ draw_navigator (GcalYearView *year_view, GtkStateFlags state_flags; gint header_padding_left, header_padding_top, header_height, layout_width, layout_height; - gint width, height, real_padding_left, real_padding_top, block_side, i, sw, weeks_counter; + gint width, height, box_side, real_padding_left, real_padding_top, i, sw, weeks_counter; gchar *header_str; PangoLayout *header_layout; PangoFontDescription *font_desc; - GdkRGBA color; - priv = year_view->priv; context = gtk_widget_get_style_context (GTK_WIDGET (year_view)); state_flags = gtk_widget_get_state_flags (GTK_WIDGET (year_view)); @@ -325,34 +463,106 @@ draw_navigator (GcalYearView *year_view, g_free (header_str); header_height = header_padding_top * 2 + layout_height; - - /* FIXME: debug statement, drawing frame */ - gtk_style_context_get_color (context, state_flags, &color); - height = gtk_widget_get_allocated_height (widget) - header_height; if (((width / 4) / 8) < ((height / 3) / 7)) - block_side = (width / 4) / 8; + box_side = (width / 4) / 8; else - block_side = (height / 3) / 7; + box_side = (height / 3) / 7; - real_padding_left = (width - (8 * 4 * block_side)) / 5; - real_padding_top = (height - (7 * 3 * block_side)) / 5; + real_padding_left = (width - (8 * 4 * box_side)) / 5; + real_padding_top = (height - (7 * 3 * box_side)) / 4; + priv->navigator_grid->box_side = box_side; weeks_counter = 1; for (i = 0; i < 12; i++) { gint row = i / 4; gint column = priv->k * 3 + sw * (i % 4); - draw_month_grid (year_view, widget, cr, i, &weeks_counter, block_side, - (column + 1) * real_padding_left + column * block_side * 8, - (row + 1) * real_padding_top + row * block_side * 7 + header_height); + priv->navigator_grid->coordinates[i].x = (column + 1) * real_padding_left + column * box_side * 8; + priv->navigator_grid->coordinates[i].y = (row + 1) * real_padding_top + row * box_side * 7 + header_height; + draw_month_grid (year_view, widget, cr, i, &weeks_counter); } return FALSE; } +static gboolean +navigator_button_press_cb (GcalYearView *year_view, + GdkEventButton *event, + GtkWidget *widget) +{ + gint day, month; + if (!calculate_day_month_for_coord (year_view, event->x, event->y, &day, &month)) + return FALSE; + + year_view->priv->button_pressed = TRUE; + year_view->priv->selected_data->start_day = day; + year_view->priv->selected_data->start_month = month; + + return TRUE; +} + +static gboolean +navigator_button_release_cb (GcalYearView *year_view, + GdkEventButton *event, + GtkWidget *widget) +{ + GcalYearViewPrivate *priv = year_view->priv; + gint day, month; + + if (!priv->button_pressed) + return FALSE; + + if (!calculate_day_month_for_coord (year_view, event->x, event->y, &day, &month)) + goto fail; + + priv->button_pressed = FALSE; + priv->selected_data->end_day = day; + priv->selected_data->end_month = month; + gtk_widget_queue_draw (widget); + + /* FIXME: */ + //update events on listbox + return TRUE; + +fail: + priv->button_pressed = FALSE; + memset (priv->selected_data, 0, sizeof (ButtonData)); + gtk_widget_queue_draw (widget); + + /* FIXME: */ + //reset listbox to today + return TRUE; +} + +static gboolean +navigator_motion_notify_cb (GcalYearView *year_view, + GdkEventMotion *event, + GtkWidget *widget) +{ + GcalYearViewPrivate *priv = year_view->priv; + gint day, month; + + if (!priv->button_pressed) + return FALSE; + + if (!calculate_day_month_for_coord (year_view, event->x, event->y, &day, &month)) + goto fail; + + priv->selected_data->end_day = day; + priv->selected_data->end_month = month; + gtk_widget_queue_draw (widget); + return TRUE; + +fail: + priv->selected_data->end_day = priv->selected_data->start_day; + priv->selected_data->end_month = priv->selected_data->start_month; + gtk_widget_queue_draw (widget); + return TRUE; +} + static void add_event_clicked_cb (GcalYearView *year_view, GtkButton *button) @@ -365,6 +575,9 @@ gcal_year_view_finalize (GObject *object) { GcalYearViewPrivate *priv = GCAL_YEAR_VIEW (object)->priv; + g_free (priv->navigator_grid); + g_free (priv->selected_data); + if (priv->date != NULL) g_free (priv->date); @@ -520,6 +733,9 @@ gcal_year_view_class_init (GcalYearViewClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GcalYearView, events_listbox); gtk_widget_class_bind_template_callback (widget_class, draw_navigator); + gtk_widget_class_bind_template_callback (widget_class, navigator_button_press_cb); + gtk_widget_class_bind_template_callback (widget_class, navigator_button_release_cb); + gtk_widget_class_bind_template_callback (widget_class, navigator_motion_notify_cb); gtk_widget_class_bind_template_callback (widget_class, add_event_clicked_cb); } @@ -535,6 +751,9 @@ gcal_year_view_init (GcalYearView *self) self->priv->k = 0; else if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) self->priv->k = 1; + + self->priv->navigator_grid = g_new0 (GridData, 1); + self->priv->selected_data = g_new0 (ButtonData, 1); } static void