[evolution] Bug #206484 - Add year scrolling to date picker



commit 9179758e50aa2e094ceb14c6d16b0d46f5d8bb7d
Author: Milan Crha <mcrha redhat com>
Date:   Wed May 16 23:01:39 2012 +0200

    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(-)
---
diff --git a/widgets/misc/e-calendar-item.c b/widgets/misc/e-calendar-item.c
index 94c1759..d6fe7c5 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 19f096c..d8fc5f7 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 1899d7c..c5c6283 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);
@@ -470,6 +548,18 @@ e_calendar_on_next_pressed (ECalendar *cal)
 }
 
 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)
 {
@@ -522,6 +652,18 @@ e_calendar_on_next_released (ECalendar *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)
 {
 	if (cal->timeout_id != 0) {
@@ -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 027a1c1..bdf7778 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;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]