From 9179758e50aa2e094ceb14c6d16b0d46f5d8bb7d Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Wed, 16 May 2012 23:01:39 +0200 Subject: Bug #206484 - Add year scrolling to date picker --- widgets/misc/e-calendar-item.c | 105 +++++++++++++++++---- widgets/misc/e-calendar-item.h | 6 +- widgets/misc/e-calendar.c | 206 ++++++++++++++++++++++++++++++++++++----- widgets/misc/e-calendar.h | 2 + 4 files changed, 277 insertions(+), 42 deletions(-) (limited to 'widgets') diff --git a/widgets/misc/e-calendar-item.c b/widgets/misc/e-calendar-item.c index 94c1759076..d6fe7c55c1 100644 --- a/widgets/misc/e-calendar-item.c +++ b/widgets/misc/e-calendar-item.c @@ -1177,7 +1177,7 @@ e_calendar_item_draw_month (ECalendarItem *calitem, gint year, month; gint month_x, month_y, month_w, month_h; gint min_x, max_x, text_x, text_y; - gint day, day_index, cells_x, cells_y, min_cell_width, text_width; + gint day, day_index, cells_x, cells_y, min_cell_width, text_width, arrow_button_size; gint clip_width, clip_height; gchar buffer[64]; PangoContext *pango_context; @@ -1205,6 +1205,12 @@ e_calendar_item_draw_month (ECalendarItem *calitem, PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)); xthickness = style->xthickness; ythickness = style->ythickness; + arrow_button_size = + PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) + + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) + + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME + + 2 * xthickness; pango_font_metrics_unref (font_metrics); @@ -1238,7 +1244,7 @@ e_calendar_item_draw_month (ECalendarItem *calitem, min_x = E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME; max_x = month_w; - if (row == 0 && col == calitem->cols - 1) + if (row == 0 && col == 0) max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON; else max_x -= E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME; @@ -1265,23 +1271,69 @@ e_calendar_item_draw_month (ECalendarItem *calitem, gdk_cairo_rectangle (cr, &clip_rect); cairo_clip (cr); - /* This is a strftime() format. %B = Month name, %Y = Year. */ - e_utf8_strftime (buffer, sizeof (buffer), _("%B %Y"), &tmp_tm); + gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]); - pango_layout_set_font_description (layout, font_desc); - pango_layout_set_text (layout, buffer, -1); + if (row == 0 && col == 0) { + PangoLayout *layout_yr; + gchar buffer_yr[64]; + gdouble max_width; - /* Ideally we place the text centered in the month, but we - * won't go to the left of the minimum x position. */ - pango_layout_get_pixel_size (layout, &text_width, NULL); - text_x = (calitem->month_width - text_width) / 2; - text_x = MAX (min_x, text_x); + layout_yr = gtk_widget_create_pango_layout (widget, NULL); - gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]); - cairo_move_to (cr, - month_x + text_x, - text_y); - pango_cairo_show_layout (cr, layout); + /* This is a strftime() format. %B = Month name. */ + e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B"), &tmp_tm); + /* This is a strftime() format. %Y = Year. */ + e_utf8_strftime (buffer_yr, sizeof (buffer_yr), C_("CalItem", "%Y"), &tmp_tm); + + pango_layout_set_font_description (layout, font_desc); + pango_layout_set_text (layout, buffer, -1); + + pango_layout_set_font_description (layout_yr, font_desc); + pango_layout_set_text (layout_yr, buffer_yr, -1); + + if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL) { + max_width = calitem->max_month_name_width; + pango_layout_get_pixel_size (layout, &text_width, NULL); + + cairo_move_to (cr, month_x + min_x + arrow_button_size + (max_width - text_width) / 2, text_y); + pango_cairo_show_layout (cr, layout); + + max_width = calitem->max_digit_width * 5; + pango_layout_get_pixel_size (layout_yr, &text_width, NULL); + + cairo_move_to (cr, month_x + month_w - arrow_button_size - (max_width - text_width) / 2 - text_width - min_x, text_y); + pango_cairo_show_layout (cr, layout_yr); + } else { + max_width = calitem->max_digit_width * 5; + pango_layout_get_pixel_size (layout_yr, &text_width, NULL); + + cairo_move_to (cr, month_x + min_x + arrow_button_size + (max_width - text_width) / 2, text_y); + pango_cairo_show_layout (cr, layout_yr); + + max_width = calitem->max_month_name_width; + pango_layout_get_pixel_size (layout, &text_width, NULL); + + cairo_move_to (cr, month_x + month_w - arrow_button_size - (max_width - text_width) / 2 - text_width - min_x, text_y); + pango_cairo_show_layout (cr, layout); + } + + g_object_unref (layout_yr); + } else { + /* This is a strftime() format. %B = Month name, %Y = Year. */ + e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B %Y"), &tmp_tm); + + pango_layout_set_font_description (layout, font_desc); + pango_layout_set_text (layout, buffer, -1); + + /* Ideally we place the text centered in the month, but we + * won't go to the left of the minimum x position. */ + pango_layout_get_pixel_size (layout, &text_width, NULL); + text_x = (calitem->month_width - text_width) / 2; + text_x = MAX (min_x, text_x); + + cairo_move_to (cr, month_x + text_x, text_y); + pango_cairo_show_layout (cr, layout); + } cairo_restore (cr); } @@ -1980,6 +2032,8 @@ e_calendar_item_recalc_sizes (ECalendarItem *calitem) GtkStyle *style; gint day, max_day_width, digit, max_digit_width, max_week_number_digit_width; gint char_height, width, min_cell_width, min_cell_height; + gchar buffer[64]; + struct tm tmp_tm; PangoFontDescription *font_desc, *wkfont_desc; PangoContext *pango_context; PangoFontMetrics *font_metrics; @@ -2072,6 +2126,23 @@ e_calendar_item_recalc_sizes (ECalendarItem *calitem) + E_CALENDAR_ITEM_YPAD_ABOVE_CELLS + min_cell_height * 6 + E_CALENDAR_ITEM_YPAD_BELOW_CELLS; + calitem->max_month_name_width = 50; + memset (&tmp_tm, 0, sizeof (tmp_tm)); + tmp_tm.tm_year = 2000 - 100; + tmp_tm.tm_mday = 1; + tmp_tm.tm_isdst = -1; + for (tmp_tm.tm_mon = 0; tmp_tm.tm_mon < 12 ; tmp_tm.tm_mon++) { + mktime (&tmp_tm); + + e_utf8_strftime (buffer, sizeof (buffer), C_("CalItem", "%B"), &tmp_tm); + + pango_layout_set_text (layout, buffer, -1); + pango_layout_get_pixel_size (layout, &width, NULL); + + if (width > calitem->max_month_name_width) + calitem->max_month_name_width = width; + } + g_object_unref (layout); g_object_unref (pango_context); pango_font_metrics_unref (font_metrics); @@ -3266,6 +3337,8 @@ e_calendar_item_style_set (GtkWidget *widget, color = &style->fg[GTK_STATE_INSENSITIVE]; calitem->colors[E_CALENDAR_ITEM_COLOR_PREV_OR_NEXT_MONTH_FG] = *color; + + e_calendar_item_recalc_sizes (calitem); } void diff --git a/widgets/misc/e-calendar-item.h b/widgets/misc/e-calendar-item.h index 19f096c5b2..d8fc5f7277 100644 --- a/widgets/misc/e-calendar-item.h +++ b/widgets/misc/e-calendar-item.h @@ -59,10 +59,10 @@ G_BEGIN_DECLS #define E_CALENDAR_ITEM_YPAD_BELOW_CELLS 2 /* Horizontal padding in the heading bars. */ -#define E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON 16 +#define E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME_WITH_BUTTON 10 #define E_CALENDAR_ITEM_XPAD_BEFORE_MONTH_NAME 3 #define E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME 3 -#define E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON 16 +#define E_CALENDAR_ITEM_XPAD_AFTER_MONTH_NAME_WITH_BUTTON 10 /* Horizontal padding in the month displays. */ #define E_CALENDAR_ITEM_XPAD_BEFORE_WEEK_NUMBERS 4 @@ -241,6 +241,8 @@ struct _ECalendarItem gint week_number_digit_widths[10]; gint max_week_number_digit_width; + gint max_month_name_width; + /* Fonts for drawing text. If font isn't set it uses the font from the * canvas widget. If week_number_font isn't set it uses font. */ PangoFontDescription *font_desc; diff --git a/widgets/misc/e-calendar.c b/widgets/misc/e-calendar.c index 1899d7c61f..c5c6283f81 100644 --- a/widgets/misc/e-calendar.c +++ b/widgets/misc/e-calendar.c @@ -95,11 +95,20 @@ static void e_calendar_on_prev_clicked (ECalendar *cal); static void e_calendar_on_next_pressed (ECalendar *cal); static void e_calendar_on_next_released (ECalendar *cal); static void e_calendar_on_next_clicked (ECalendar *cal); - -static void e_calendar_start_auto_move (ECalendar *cal, - gboolean moving_forward); -static gboolean e_calendar_auto_move_handler (gpointer data); -static void e_calendar_stop_auto_move (ECalendar *cal); +static void e_calendar_on_prev_year_pressed (ECalendar *cal); +static void e_calendar_on_prev_year_released (ECalendar *cal); +static void e_calendar_on_prev_year_clicked (ECalendar *cal); +static void e_calendar_on_next_year_pressed (ECalendar *cal); +static void e_calendar_on_next_year_released (ECalendar *cal); +static void e_calendar_on_next_year_clicked (ECalendar *cal); + +static void e_calendar_start_auto_move (ECalendar *cal, + gboolean moving_forward); +static gboolean e_calendar_auto_move_handler (gpointer data); +static void e_calendar_start_auto_move_year (ECalendar *cal, + gboolean moving_forward); +static gboolean e_calendar_auto_move_year_handler (gpointer data); +static void e_calendar_stop_auto_move (ECalendar *cal); G_DEFINE_TYPE ( ECalendar, @@ -175,7 +184,7 @@ e_calendar_init (ECalendar *cal) "widget", button, NULL); a11y = gtk_widget_get_accessible (button); - atk_object_set_name (a11y, _("Previous")); + atk_object_set_name (a11y, _("Previous month")); button = gtk_button_new (); gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); @@ -199,7 +208,50 @@ e_calendar_init (ECalendar *cal) "widget", button, NULL); a11y = gtk_widget_get_accessible (button); - atk_object_set_name (a11y, _("Next")); + atk_object_set_name (a11y, _("Next month")); + + /* Create the arrow buttons to move to the previous/next year. */ + button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_widget_show (button); + g_signal_connect_swapped (button, "pressed", + G_CALLBACK (e_calendar_on_prev_year_pressed), cal); + g_signal_connect_swapped (button, "released", + G_CALLBACK (e_calendar_on_prev_year_released), cal); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (e_calendar_on_prev_year_clicked), cal); + + pixmap = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE); + gtk_widget_show (pixmap); + gtk_container_add (GTK_CONTAINER (button), pixmap); + + cal->prev_item_year = gnome_canvas_item_new (canvas_group, + gnome_canvas_widget_get_type (), + "widget", button, + NULL); + a11y = gtk_widget_get_accessible (button); + atk_object_set_name (a11y, _("Previous year")); + + button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_widget_show (button); + g_signal_connect_swapped (button, "pressed", + G_CALLBACK (e_calendar_on_next_year_pressed), cal); + g_signal_connect_swapped (button, "released", + G_CALLBACK (e_calendar_on_next_year_released), cal); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (e_calendar_on_next_year_clicked), cal); + + pixmap = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE); + gtk_widget_show (pixmap); + gtk_container_add (GTK_CONTAINER (button), pixmap); + + cal->next_item_year = gnome_canvas_item_new (canvas_group, + gnome_canvas_widget_get_type (), + "widget", button, + NULL); + a11y = gtk_widget_get_accessible (button); + atk_object_set_name (a11y, _("Next year")); cal->min_rows = 1; cal->min_cols = 1; @@ -331,7 +383,8 @@ e_calendar_size_allocate (GtkWidget *widget, PangoContext *pango_context; PangoFontMetrics *font_metrics; gdouble old_x2, old_y2, new_x2, new_y2; - gdouble xthickness, ythickness, arrow_button_size; + gdouble xthickness, ythickness, arrow_button_size, current_x, month_width; + gboolean is_rtl; cal = E_CALENDAR (widget); style = gtk_widget_get_style (widget); @@ -364,32 +417,57 @@ e_calendar_size_allocate (GtkWidget *widget, "y2", new_y2, NULL); + if (cal->calitem->month_width > 0) + month_width = cal->calitem->month_width; + else + month_width = new_x2; + month_width -= E_CALENDAR_ITEM_MIN_CELL_XPAD + E_CALENDAR_ARROW_BUTTON_X_PAD; + /* Position the arrow buttons. */ arrow_button_size = PANGO_PIXELS (pango_font_metrics_get_ascent (font_metrics)) + PANGO_PIXELS (pango_font_metrics_get_descent (font_metrics)) + E_CALENDAR_ITEM_YPAD_ABOVE_MONTH_NAME + E_CALENDAR_ITEM_YPAD_BELOW_MONTH_NAME - - E_CALENDAR_ARROW_BUTTON_Y_PAD * 2; + - E_CALENDAR_ARROW_BUTTON_Y_PAD * 2 - 2; + + is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + current_x = is_rtl ? + (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size) : + (xthickness); gnome_canvas_item_set (cal->prev_item, - "x", (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - ? new_x2 + 1 - xthickness * 2 - E_CALENDAR_ARROW_BUTTON_X_PAD - - arrow_button_size - : xthickness * 2 + E_CALENDAR_ARROW_BUTTON_X_PAD, - "y", ythickness * 2 - + E_CALENDAR_ARROW_BUTTON_Y_PAD, + "x", current_x, + "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD, "width", arrow_button_size, "height", arrow_button_size, NULL); + current_x += (is_rtl ? -1.0 : +1.0) * (cal->calitem->max_month_name_width - xthickness + 2 * arrow_button_size); + gnome_canvas_item_set (cal->next_item, - "x", (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - ? xthickness * 2 + E_CALENDAR_ARROW_BUTTON_X_PAD - : new_x2 + 1 - xthickness * 2 - E_CALENDAR_ARROW_BUTTON_X_PAD - - arrow_button_size, - "y", ythickness * 2 - + E_CALENDAR_ARROW_BUTTON_Y_PAD, + "x", current_x, + "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD, + "width", arrow_button_size, + "height", arrow_button_size, + NULL); + + current_x = is_rtl ? + (xthickness) : + (month_width - 2 * xthickness - E_CALENDAR_ARROW_BUTTON_X_PAD - arrow_button_size); + + gnome_canvas_item_set (cal->next_item_year, + "x", current_x, + "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD, + "width", arrow_button_size, + "height", arrow_button_size, + NULL); + + current_x += (is_rtl ? +1.0 : -1.0) * (cal->calitem->max_digit_width * 5 - xthickness + 2 * arrow_button_size); + + gnome_canvas_item_set (cal->prev_item_year, + "x", current_x, + "y", ythickness + E_CALENDAR_ARROW_BUTTON_Y_PAD, "width", arrow_button_size, "height", arrow_button_size, NULL); @@ -469,6 +547,18 @@ e_calendar_on_next_pressed (ECalendar *cal) e_calendar_start_auto_move (cal, TRUE); } +static void +e_calendar_on_prev_year_pressed (ECalendar *cal) +{ + e_calendar_start_auto_move_year (cal, FALSE); +} + +static void +e_calendar_on_next_year_pressed (ECalendar *cal) +{ + e_calendar_start_auto_move_year (cal, TRUE); +} + static void e_calendar_start_auto_move (ECalendar *cal, gboolean moving_forward) @@ -483,6 +573,46 @@ e_calendar_start_auto_move (ECalendar *cal, } +static void +e_calendar_start_auto_move_year (ECalendar *cal, + gboolean moving_forward) +{ + if (cal->timeout_id == 0) { + cal->timeout_id = g_timeout_add (E_CALENDAR_AUTO_MOVE_TIMEOUT, + e_calendar_auto_move_year_handler, + cal); + } + cal->timeout_delay = E_CALENDAR_AUTO_MOVE_TIMEOUT_DELAY; + cal->moving_forward = moving_forward; +} + +static gboolean +e_calendar_auto_move_year_handler (gpointer data) +{ + ECalendar *cal; + ECalendarItem *calitem; + gint offset; + + g_return_val_if_fail (E_IS_CALENDAR (data), FALSE); + + cal = E_CALENDAR (data); + calitem = cal->calitem; + + GDK_THREADS_ENTER (); + + if (cal->timeout_delay > 0) { + cal->timeout_delay--; + } else { + offset = cal->moving_forward ? 12 : -12; + e_calendar_item_set_first_month (calitem, calitem->year, + calitem->month + offset); + } + + GDK_THREADS_LEAVE (); + + return TRUE; +} + static gboolean e_calendar_auto_move_handler (gpointer data) { @@ -521,6 +651,18 @@ e_calendar_on_next_released (ECalendar *cal) e_calendar_stop_auto_move (cal); } +static void +e_calendar_on_prev_year_released (ECalendar *cal) +{ + e_calendar_stop_auto_move (cal); +} + +static void +e_calendar_on_next_year_released (ECalendar *cal) +{ + e_calendar_stop_auto_move (cal); +} + static void e_calendar_stop_auto_move (ECalendar *cal) { @@ -534,14 +676,28 @@ static void e_calendar_on_prev_clicked (ECalendar *cal) { e_calendar_item_set_first_month (cal->calitem, cal->calitem->year, - cal->calitem->month - 1); + cal->calitem->month - 1); } static void e_calendar_on_next_clicked (ECalendar *cal) { e_calendar_item_set_first_month (cal->calitem, cal->calitem->year, - cal->calitem->month + 1); + cal->calitem->month + 1); +} + +static void +e_calendar_on_prev_year_clicked (ECalendar *cal) +{ + e_calendar_item_set_first_month (cal->calitem, cal->calitem->year, + cal->calitem->month - 12); +} + +static void +e_calendar_on_next_year_clicked (ECalendar *cal) +{ + e_calendar_item_set_first_month (cal->calitem, cal->calitem->year, + cal->calitem->month + 12); } static gint @@ -580,7 +736,7 @@ static gboolean e_calendar_focus (GtkWidget *widget, GtkDirectionType direction) { -#define E_CALENDAR_FOCUS_CHILDREN_NUM 3 +#define E_CALENDAR_FOCUS_CHILDREN_NUM 5 ECalendar *cal; GnomeCanvas *canvas; GnomeCanvasItem *children[E_CALENDAR_FOCUS_CHILDREN_NUM]; @@ -598,6 +754,8 @@ e_calendar_focus (GtkWidget *widget, children[0] = GNOME_CANVAS_ITEM (cal->calitem); children[1] = cal->prev_item; children[2] = cal->next_item; + children[3] = cal->prev_item_year; + children[4] = cal->next_item_year; /* get current focused item, if e-calendar has had focus */ if (gtk_widget_has_focus (widget) || e_calendar_button_has_focus (cal)) diff --git a/widgets/misc/e-calendar.h b/widgets/misc/e-calendar.h index 027a1c1fc3..bdf77786f2 100644 --- a/widgets/misc/e-calendar.h +++ b/widgets/misc/e-calendar.h @@ -66,6 +66,8 @@ struct _ECalendar { GnomeCanvasItem *prev_item; GnomeCanvasItem *next_item; + GnomeCanvasItem *prev_item_year; + GnomeCanvasItem *next_item_year; gint min_rows; gint min_cols; -- cgit