goffice r2346 - in trunk: . goffice/graph goffice/utils



Author: mortenw
Date: Thu Mar 26 02:57:46 2009
New Revision: 2346
URL: http://svn.gnome.org/viewvc/goffice?rev=2346&view=rev

Log:
2009-03-25  Morten Welinder  <terra gnome org>

	* goffice/graph/gog-axis.c (map_polar_auto_bound): Split from
	map_linear_auto_bound.
	(map_time_auto_bound): New function.
	(map_linear_auto_bound): Call map_polar_auto_bound or
	map_time_auto_bound as appropriate.

	* goffice/utils/go-format.c (go_format_is_time,
	go_format_has_minute): New functions.



Modified:
   trunk/ChangeLog
   trunk/NEWS
   trunk/goffice/graph/gog-axis.c
   trunk/goffice/utils/go-format.c
   trunk/goffice/utils/go-format.h

Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS	(original)
+++ trunk/NEWS	Thu Mar 26 02:57:46 2009
@@ -2,6 +2,7 @@
 
 Morten:
 	* Add new go_format_is_time function.
+	* Pick sane bounds for time-formatted axes.  [Part of #574681]
 
 --------------------------------------------------------------------------
 goffice 0.7.4:

Modified: trunk/goffice/graph/gog-axis.c
==============================================================================
--- trunk/goffice/graph/gog-axis.c	(original)
+++ trunk/goffice/graph/gog-axis.c	Thu Mar 26 02:57:46 2009
@@ -78,8 +78,8 @@
 	GogAxisType	 type;
 	GSList		*contributors;
 
-	GogDatasetElement source [GOG_AXIS_ELEM_CROSS_POINT];
-	double		  auto_bound [GOG_AXIS_ELEM_CROSS_POINT];
+	GogDatasetElement source[GOG_AXIS_ELEM_CROSS_POINT];
+	double		  auto_bound[GOG_AXIS_ELEM_CROSS_POINT];
 	gboolean inverted; /* apply to all map type */
 
 	double		min_val, max_val;
@@ -126,6 +126,15 @@
 	return ticks;
 }
 
+static GOFormat *
+get_axis_format (GogAxis const *axis)
+{
+	if (axis->assigned_format &&
+	    !go_format_is_general (axis->assigned_format))
+		return axis->assigned_format;
+	return axis->format;
+}
+
 /*****************************************************************************/
 
 struct _GogAxisMap {
@@ -233,16 +242,16 @@
 			 double *bound)
 {
 	if ((maximum - minimum) > GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR) 
-		bound [GOG_AXIS_ELEM_MAJOR_TICK] = 
-		bound [GOG_AXIS_ELEM_MINOR_TICK] =
+		bound[GOG_AXIS_ELEM_MAJOR_TICK] = 
+		bound[GOG_AXIS_ELEM_MINOR_TICK] =
 			go_fake_ceil ((maximum - minimum + 1.0) / 
 			      (double) GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR); 
 	else
-		bound [GOG_AXIS_ELEM_MAJOR_TICK] = 
-		bound [GOG_AXIS_ELEM_MINOR_TICK] = 1.;
+		bound[GOG_AXIS_ELEM_MAJOR_TICK] = 
+		bound[GOG_AXIS_ELEM_MINOR_TICK] = 1.;
 
-	bound [GOG_AXIS_ELEM_MIN] = minimum;
-	bound [GOG_AXIS_ELEM_MAX] = maximum;
+	bound[GOG_AXIS_ELEM_MIN] = minimum;
+	bound[GOG_AXIS_ELEM_MAX] = maximum;
 }
 
 static void
@@ -393,16 +402,91 @@
 }
 
 static void
+map_polar_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+	bound[GOG_AXIS_ELEM_MIN] = polar_units[axis->polar_unit].auto_minimum;
+	bound[GOG_AXIS_ELEM_MAX] = polar_units[axis->polar_unit].auto_maximum;
+	bound[GOG_AXIS_ELEM_MAJOR_TICK] = polar_units[axis->polar_unit].auto_major;
+	bound[GOG_AXIS_ELEM_MINOR_TICK] = polar_units[axis->polar_unit].auto_minor;
+}
+
+static void
+map_time_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+	enum { U_Second, U_Minute, U_Hour, U_Day } u;
+	static const double invunits[4] = { 24* 60 * 60, 24 * 60, 24, 1 };
+	GOFormat *fmt = gog_axis_get_format (axis);
+	int is_time = go_format_is_time (fmt);
+	double range = fabs (maximum - minimum);
+	double iu, step;
+
+	if (go_format_has_hour (fmt))
+		u = U_Hour;
+	else if (go_format_has_minute (fmt))
+		u = U_Minute;
+	else
+		u = U_Second;
+
+retry:
+	iu = invunits[u];
+	step = pow (10, go_fake_floor (log10 (range * iu)));
+
+	if (is_time == 1) {
+		switch (u) {
+		case U_Hour:
+			if (step == 10) {
+				/* Step=10 is unusually bad... */
+				if (range <= 24)
+					step = 4;
+				else
+					step = 24;
+				break;
+			}
+			/* Fall through */
+		case U_Minute:
+		case U_Second:
+			if (step >= 100) {
+				u++;
+				goto retry;
+			} else if (step < 0.10001 && u != U_Second) {
+				u--;
+				goto retry;
+			}
+			break;
+		case U_Day:
+		default:
+			break;
+		}
+	} else {
+		/* ??? */
+	}
+	if (range * iu / step < 3)
+		step /= 2;
+
+	step /= iu;
+
+	bound[GOG_AXIS_ELEM_MIN] = step * go_fake_floor (minimum / step);
+	bound[GOG_AXIS_ELEM_MAX] = step * go_fake_ceil (maximum / step);
+	bound[GOG_AXIS_ELEM_MAJOR_TICK] = step;
+	bound[GOG_AXIS_ELEM_MINOR_TICK] = step / 2;
+
+#if 0
+	g_printerr ("min=%g (%g)  max=%g (%g) step=%g (%g)\n",
+		    minimum, bound[GOG_AXIS_ELEM_MIN],
+		    maximum, bound[GOG_AXIS_ELEM_MAX],
+		    step, bound[GOG_AXIS_ELEM_MAJOR_TICK]);
+#endif
+}
+
+static void
 map_linear_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
 {
 	double step, range, mant;
 	int expon;
+	GOFormat *fmt;
 
 	if (gog_axis_get_atype (axis) == GOG_AXIS_CIRCULAR) {
-		bound[GOG_AXIS_ELEM_MIN] = polar_units[axis->polar_unit].auto_minimum;
-		bound[GOG_AXIS_ELEM_MAX] = polar_units[axis->polar_unit].auto_maximum;
-		bound[GOG_AXIS_ELEM_MAJOR_TICK] = polar_units[axis->polar_unit].auto_major;
-		bound[GOG_AXIS_ELEM_MINOR_TICK] = polar_units[axis->polar_unit].auto_minor;
+		map_polar_auto_bound (axis, minimum, maximum, bound);
 		return;
 	}	
 
@@ -422,36 +506,42 @@
 		range = fabs (maximum - minimum);
 	}
 
-	step  = pow (10, go_fake_floor (log10 (range)));
-	if (range/step < 1.6)
+	fmt = gog_axis_get_format (axis);
+	if (fmt && go_format_is_time (fmt) > 0) {
+		map_time_auto_bound (axis, minimum, maximum, bound);
+		return;
+	}
+
+	step = pow (10, go_fake_floor (log10 (range)));
+	if (range / step < 1.6)
 		step /= 5.;	/* .2 .4 .6 */
-	else if (range/step < 3)
+	else if (range / step < 3)
 		step /= 2.;	/* 0 5 10 */
-	else if (range/step > 8)
+	else if (range / step > 8)
 		step *= 2.;	/* 2 4 6 */
 
 	/* we want the bounds to be loose so jump up a step if we get too close */
 	mant = frexp (minimum / step, &expon);
-	bound [GOG_AXIS_ELEM_MIN] = step * floor (ldexp (mant - DBL_EPSILON, expon));
+	bound[GOG_AXIS_ELEM_MIN] = step * floor (ldexp (mant - DBL_EPSILON, expon));
 	mant = frexp (maximum / step, &expon);
-	bound [GOG_AXIS_ELEM_MAX] = step * ceil (ldexp (mant + DBL_EPSILON, expon));
-	bound [GOG_AXIS_ELEM_MAJOR_TICK] = step;
-	bound [GOG_AXIS_ELEM_MINOR_TICK] = step / 5.;
+	bound[GOG_AXIS_ELEM_MAX] = step * ceil (ldexp (mant + DBL_EPSILON, expon));
+	bound[GOG_AXIS_ELEM_MAJOR_TICK] = step;
+	bound[GOG_AXIS_ELEM_MINOR_TICK] = step / 5.;
 
 	/* pull to zero if its nearby (do not pull both directions to 0) */
-	if (bound [GOG_AXIS_ELEM_MIN] > 0 &&
-	    (bound [GOG_AXIS_ELEM_MIN] - 10. * step) < 0)
-		bound [GOG_AXIS_ELEM_MIN] = 0;
-	else if (bound [GOG_AXIS_ELEM_MAX] < 0 &&
-	    (bound [GOG_AXIS_ELEM_MAX] + 10. * step) > 0)
-		bound [GOG_AXIS_ELEM_MAX] = 0;
+	if (bound[GOG_AXIS_ELEM_MIN] > 0 &&
+	    (bound[GOG_AXIS_ELEM_MIN] - 10. * step) < 0)
+		bound[GOG_AXIS_ELEM_MIN] = 0;
+	else if (bound[GOG_AXIS_ELEM_MAX] < 0 &&
+	    (bound[GOG_AXIS_ELEM_MAX] + 10. * step) > 0)
+		bound[GOG_AXIS_ELEM_MAX] = 0;
 
 	/* The epsilon shift can pull us away from a zero we want to
-	 * keep (eg percentage bars withno negative elements) */
-	if (bound [GOG_AXIS_ELEM_MIN] < 0 && minimum >= 0.)
-		bound [GOG_AXIS_ELEM_MIN] = 0;
-	else if (bound [GOG_AXIS_ELEM_MAX] > 0 && maximum <= 0.)
-		bound [GOG_AXIS_ELEM_MAX] = 0;
+	 * keep (eg percentage bars with no negative elements) */
+	if (bound[GOG_AXIS_ELEM_MIN] < 0 && minimum >= 0.)
+		bound[GOG_AXIS_ELEM_MIN] = 0;
+	else if (bound[GOG_AXIS_ELEM_MAX] > 0 && maximum <= 0.)
+		bound[GOG_AXIS_ELEM_MAX] = 0;
 }
 
 static void
@@ -493,13 +583,8 @@
 		ratio = ticks[i].position / major_tick;
 		if (fabs (ratio - go_rint (ratio)) < 1E-3) {
 			ticks[i].type = GOG_AXIS_TICK_MAJOR;
-			if (axis->assigned_format == NULL || 
-			    go_format_is_general (axis->assigned_format))
-				ticks[i].label = go_format_value (axis->format, 
-								  ticks[i].position);
-			else
-				ticks[i].label = go_format_value (axis->assigned_format, 
-								  ticks[i].position);
+			ticks[i].label = go_format_value (get_axis_format (axis), 
+							  ticks[i].position);
 		}
 		else {
 			ticks[i].type = GOG_AXIS_TICK_MINOR;
@@ -645,10 +730,10 @@
 	step = go_fake_ceil ((maximum - minimum + 1.0) / 
 		     (double) GOG_AXIS_LOG_AUTO_MAX_MAJOR_TICK_NBR); 
 
-	bound [GOG_AXIS_ELEM_MIN] = pow ( 10.0, minimum);
-	bound [GOG_AXIS_ELEM_MAX] = pow ( 10.0, maximum);
-	bound [GOG_AXIS_ELEM_MAJOR_TICK] = step;
-	bound [GOG_AXIS_ELEM_MINOR_TICK] = 8;
+	bound[GOG_AXIS_ELEM_MIN] = pow ( 10.0, minimum);
+	bound[GOG_AXIS_ELEM_MAX] = pow ( 10.0, maximum);
+	bound[GOG_AXIS_ELEM_MAJOR_TICK] = step;
+	bound[GOG_AXIS_ELEM_MINOR_TICK] = 8;
 }
 
 static void
@@ -690,13 +775,8 @@
 			ticks[count].position = position;
 			if (i % major_label == 0) {
 				ticks[count].type = GOG_AXIS_TICK_MAJOR;
-				if (axis->assigned_format == NULL || 
-				    go_format_is_general (axis->assigned_format))
-					ticks[count].label = go_format_value (axis->format,
-									      ticks[count].position);
-				else
-					ticks[count].label = go_format_value (axis->assigned_format, 
-									      ticks[count].position);
+				ticks[count].label = go_format_value (get_axis_format (axis),
+								      ticks[count].position);
 				count++;
 			}
 			else {
@@ -854,7 +934,7 @@
  * @map : a #GogAxisMap
  * @value : value to map to plot space.
  * 
- * Converts @value to plot coordinates. A value in [0,1.0] range means a data 
+ * Converts @value to plot coordinates. A value in[0,1.0] range means a data 
  * within axis bounds.
  *
  * Returns: mapped value.
@@ -1192,8 +1272,8 @@
 		go_format_unref (fmt);
 		return FALSE;
 	}
-	if (axis->assigned_format != NULL)
-		go_format_unref (axis->assigned_format);
+
+	go_format_unref (axis->assigned_format);
 	axis->assigned_format = fmt;
 
 	gog_object_request_update (GOG_OBJECT (axis));
@@ -1249,10 +1329,8 @@
 		break;
 	case AXIS_PROP_ASSIGNED_FORMAT_STR_XL : {
 		char const *str = g_value_get_string (value);
-		resized = gog_axis_set_format (axis, (str != NULL)
-			? go_format_new_from_XL (str)
-			: NULL);
-		calc_ticks = resized;
+		GOFormat *newfmt = str ? go_format_new_from_XL (str) : NULL;
+		resized = calc_ticks = gog_axis_set_format (axis, newfmt);
 		break;
 	}
 	case AXIS_PROP_CIRCULAR_ROTATION:
@@ -1335,14 +1413,8 @@
 		/* this is for information only, no ref */
 		axis->plot_that_supplied_labels = NULL;
 	}
-	if (axis->assigned_format != NULL) {
-		go_format_unref (axis->assigned_format);
-		axis->assigned_format = NULL;
-	}
-	if (axis->format != NULL) {
-		go_format_unref (axis->format);
-		axis->format = NULL;
-	}
+	go_format_unref (axis->assigned_format);
+	go_format_unref (axis->format);
 
 	gog_axis_set_ticks (axis, 0, NULL);
 
@@ -1371,7 +1443,7 @@
 	g_return_val_if_fail (i >= GOG_AXIS_ELEM_MIN && i < GOG_AXIS_ELEM_MAX_ENTRY, go_nan);
 
 	if (i != GOG_AXIS_ELEM_CROSS_POINT)
-		dat = axis->source [i].data;
+		dat = axis->source[i].data;
 	else 
 		dat = GOG_AXIS_BASE (axis)->cross_location.data;
 
@@ -1385,7 +1457,7 @@
 	}
 
 	if (i != GOG_AXIS_ELEM_CROSS_POINT)
-		return axis->auto_bound [i];
+		return axis->auto_bound[i];
 	else
 		return 0.;
 }
@@ -1395,8 +1467,8 @@
 {
 	GSList *ptr;
 	GogAxis *axis = GOG_AXIS (obj);
-	double old_min = axis->auto_bound [GOG_AXIS_ELEM_MIN];
-	double old_max = axis->auto_bound [GOG_AXIS_ELEM_MAX];
+	double old_min = axis->auto_bound[GOG_AXIS_ELEM_MIN];
+	double old_max = axis->auto_bound[GOG_AXIS_ELEM_MAX];
 	GOData *labels;
 	GogPlotBoundInfo bounds;
 
@@ -1411,10 +1483,8 @@
 	axis->min_val  =  DBL_MAX;
 	axis->max_val  = -DBL_MAX;
 	axis->min_contrib = axis->max_contrib = NULL;
-	if (axis->format != NULL) {
-		go_format_unref (axis->format);
-		axis->format = NULL;
-	}
+	go_format_unref (axis->format);
+	axis->format = NULL;
 
 	/* everything else is initialized in gog_plot_get_axis_bounds */
 	bounds.fmt = NULL;
@@ -1456,16 +1526,16 @@
 	gog_axis_auto_bound (axis);
 
 	if (go_finite (axis->logical_min_val) &&
-	    axis->auto_bound [GOG_AXIS_ELEM_MIN] < axis->logical_min_val)
-		axis->auto_bound [GOG_AXIS_ELEM_MIN] = axis->logical_min_val;
+	    axis->auto_bound[GOG_AXIS_ELEM_MIN] < axis->logical_min_val)
+		axis->auto_bound[GOG_AXIS_ELEM_MIN] = axis->logical_min_val;
 	if (go_finite (axis->logical_max_val) &&
-	    axis->auto_bound [GOG_AXIS_ELEM_MAX] > axis->logical_max_val)
-		axis->auto_bound [GOG_AXIS_ELEM_MAX] = axis->logical_max_val;
+	    axis->auto_bound[GOG_AXIS_ELEM_MAX] > axis->logical_max_val)
+		axis->auto_bound[GOG_AXIS_ELEM_MAX] = axis->logical_max_val;
 
 	gog_axis_calc_ticks (axis);
 
-	if (old_min != axis->auto_bound [GOG_AXIS_ELEM_MIN] ||
-	    old_max != axis->auto_bound [GOG_AXIS_ELEM_MAX])
+	if (old_min != axis->auto_bound[GOG_AXIS_ELEM_MIN] ||
+	    old_max != axis->auto_bound[GOG_AXIS_ELEM_MAX])
 		gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
 }
 
@@ -1515,7 +1585,7 @@
 static void
 set_to_auto_value (ElemToggleData *closure)
 {
-	double bound = GOG_AXIS (closure->set)->auto_bound [closure->dim];
+	double bound = GOG_AXIS (closure->set)->auto_bound[closure->dim];
 
 	if (go_finite (bound) && DBL_MAX > bound && bound > -DBL_MAX) {
 		char *str = g_strdup_printf ("%g", bound);
@@ -1563,7 +1633,7 @@
 {
 	ElemToggleData *info;
 	GtkWidget *editor = gog_data_allocator_editor (dalloc, set, dim, GOG_DATA_SCALAR);
-	char *txt = g_strconcat (_(dim_name [dim]), ":", NULL);
+	char *txt = g_strconcat (_(dim_name[dim]), ":", NULL);
 	GtkWidget *toggle = gtk_check_button_new_with_mnemonic (txt);
 	g_free (txt);
 
@@ -1750,15 +1820,13 @@
 
 	    /* Format page */
 	    if (!axis->is_discrete) {
+		    GOFormat *fmt = get_axis_format (axis);
 		    w = go_format_sel_new_full (TRUE);
 		    state->format_selector = w;
 
-		    if (axis->assigned_format != NULL && !go_format_is_general (axis->assigned_format))
-			    go_format_sel_set_style_format (GO_FORMAT_SEL (w),
-				    axis->assigned_format);
-		    else if (axis->format != NULL)
+		    if (fmt)
 			    go_format_sel_set_style_format (GO_FORMAT_SEL (w),
-				    axis->format);
+							    fmt);
 
 		    gog_editor_add_page (editor, w, _("Format"));
 

Modified: trunk/goffice/utils/go-format.c
==============================================================================
--- trunk/goffice/utils/go-format.c	(original)
+++ trunk/goffice/utils/go-format.c	Thu Mar 26 02:57:46 2009
@@ -292,6 +292,7 @@
 			unsigned int date_dbm    : 1;  /* day, then month.  */
 			unsigned int has_time    : 1;
 			unsigned int has_hour    : 1;
+			unsigned int has_minute  : 1;
 			unsigned int has_elapsed : 1;
 			unsigned int fraction    : 1;
 			unsigned int scale_is_2  : 1;
@@ -1461,6 +1462,7 @@
 			*p++ = op;
 			fmt->u.number.has_time = TRUE;
 			fmt->u.number.has_hour = seen_hour;
+			fmt->u.number.has_minute = seen_minute;
 			fmt->u.number.has_elapsed = seen_elapsed;
 		}
 		if (pstate->has_general) {
@@ -4442,6 +4444,26 @@
 
 #ifdef DEFINE_COMMON
 /**
+ * go_format_has_minute:
+ * @fmt: Format to query
+ *
+ * Returns: TRUE if format is a number format with a minute specifier
+ * 	    FALSE otherwise.
+ **/
+gboolean
+go_format_has_minute (GOFormat const *fmt)
+{
+	g_return_val_if_fail (fmt != NULL, FALSE);
+
+	return (fmt->typ == GO_FMT_NUMBER &&
+		fmt->u.number.has_time &&
+		fmt->u.number.has_minute);
+}
+#endif
+
+
+#ifdef DEFINE_COMMON
+/**
  * go_format_get_magic:
  * @fmt: Format to query
  *

Modified: trunk/goffice/utils/go-format.h
==============================================================================
--- trunk/goffice/utils/go-format.h	(original)
+++ trunk/goffice/utils/go-format.h	Thu Mar 26 02:57:46 2009
@@ -122,6 +122,7 @@
 
 int       go_format_month_before_day    (GOFormat const *fmt);
 gboolean  go_format_has_hour            (GOFormat const *fmt);
+gboolean  go_format_has_minute          (GOFormat const *fmt);
 
 GOFormatMagic go_format_get_magic       (GOFormat const *fmt);
 GOFormat *go_format_new_magic           (GOFormatMagic m);



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