Commit c2f2e55f authored by Arturo Espinosa's avatar Arturo Espinosa

Redid the format parsing engine to be closer to the Excel formatting



Redid the format parsing engine to be closer to the Excel formatting
engine.  This basically is just a better front end to Clahey's number
rendering engine.

This now supports quoted strings in a format;  quoted characters;
spacing control formats;  color specification;  plus some ammount
of pre-parsing.

Getting it Excel compliant will be a bit more difficult than we
expected, as for example this format shows:

	000-0000-0000

should render 12345670987 as: 123-4567-0987
parent 3c3c906b
1998-08-28 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Rewrote the format parser. I
took Chris's number formatting routine and made it a
number-rendering only thing to support the complexities of the
Excel formating codes.
1998-08-27 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Add support for text quoting
inside a format (double quotes and single character quotes); Add
support for space skipping (_) and concatenation of the allowed
characters.
Added color lookup.
* src/sheet-view.c, src/sheet.c, src/gnumeric.c: Massive changes
to the structure of the code to accomodate the fact that we should
be able to have multiple views for a single sheet.
......
1998-08-28 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Rewrote the format parser. I
took Chris's number formatting routine and made it a
number-rendering only thing to support the complexities of the
Excel formating codes.
1998-08-27 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Add support for text quoting
inside a format (double quotes and single character quotes); Add
support for space skipping (_) and concatenation of the allowed
characters.
Added color lookup.
* src/sheet-view.c, src/sheet.c, src/gnumeric.c: Massive changes
to the structure of the code to accomodate the fact that we should
be able to have multiple views for a single sheet.
......
1998-08-28 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Rewrote the format parser. I
took Chris's number formatting routine and made it a
number-rendering only thing to support the complexities of the
Excel formating codes.
1998-08-27 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Add support for text quoting
inside a format (double quotes and single character quotes); Add
support for space skipping (_) and concatenation of the allowed
characters.
Added color lookup.
* src/sheet-view.c, src/sheet.c, src/gnumeric.c: Massive changes
to the structure of the code to accomodate the fact that we should
be able to have multiple views for a single sheet.
......
1998-08-28 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Rewrote the format parser. I
took Chris's number formatting routine and made it a
number-rendering only thing to support the complexities of the
Excel formating codes.
1998-08-27 Miguel de Icaza <miguel@nuclecu.unam.mx>
* src/format.c (format_number): Add support for text quoting
inside a format (double quotes and single character quotes); Add
support for space skipping (_) and concatenation of the allowed
characters.
Added color lookup.
* src/sheet-view.c, src/sheet.c, src/gnumeric.c: Massive changes
to the structure of the code to accomodate the fact that we should
be able to have multiple views for a single sheet.
......
/* format.c - attempts to emulate excel's number formatting ability.
* Copyright (C) 1998 Chris Lahey
*
* Redid the format parsing routine to make it accept more of the Excel
* formats. The number rendeing code from Chris has not been touched,
* that routine is pretty good.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
......@@ -276,12 +280,49 @@ static int append_half( GString *string, gchar *format, struct tm *time_split )
else return 1;
}
/*
* Does some analisis before hand on the format
*/
static void
pre_parse_format (StyleFormatEntry *style)
{
char *format;
style->want_am_pm = 0;
for (format = style->format; *format; format++){
switch (*format){
case '"':
for (format++; *format && *format != '"'; format++)
;
if (*format)
format++;
break;
case '\\':
if (*(format+1))
format++;
else
return;
break;
case 'a':
case 'p':
case 'A':
case 'P':
if ((*(format+1) == 'm') ||
(*(format+1) == 'M'))
style->want_am_pm = 1;
break;
}
}
}
typedef struct
{
int decimal;
int timeformat;
int hasnumbers;
} format_info;
} xformat_info;
/* This routine should always return, it cant fail, in the worst
* case it should just downgrade to stupid formatting
......@@ -353,6 +394,7 @@ format_compile (StyleFormat *format)
{
temp = g_new( StyleFormatEntry, 1 );
*temp = standard_entries[i];
pre_parse_format (temp);
format->format_list = g_list_append( format->format_list, temp );
}
g_string_free( string, TRUE );
......@@ -361,42 +403,191 @@ format_compile (StyleFormat *format)
static void
style_entry_free(gpointer data, gpointer user_data)
{
StyleFormatEntry *entry = data;
g_free( entry->format );
g_free( entry );
StyleFormatEntry *entry = data;
g_free (entry->format);
g_free (entry );
}
void
format_destroy (StyleFormat *format)
{
/* This routine is invoked when the last user of the
* format is gone (ie, refcount has reached zero) just
* before the StyleFormat structure is actually released.
*
* resources allocated in format_compile should be disposed here
*/
g_list_foreach( format->format_list, style_entry_free, NULL );
g_list_free( format->format_list );
format->format_list = NULL;
/* This routine is invoked when the last user of the
* format is gone (ie, refcount has reached zero) just
* before the StyleFormat structure is actually released.
*
* resources allocated in format_compile should be disposed here
*/
g_list_foreach (format->format_list, style_entry_free, NULL);
g_list_free (format->format_list);
format->format_list = NULL;
}
static char *format_colors [] = {
N_("black"),
N_("blue"),
N_("cyan"),
N_("green"),
N_("magenta"),
N_("red"),
N_("white"),
N_("yellow"),
NULL
};
static char *
lookup_color (char *str, char *end)
{
int i;
for (i = 0; format_colors [i]; i++){
int len = strlen (format_colors [i]);
if ((strncasecmp (format_colors [i], str, len) == 0) ||
(strncasecmp (_(format_colors [i]), str, len) == 0)){
return format_colors [i];
}
}
return NULL;
}
static GString *
render_number (gdouble number,
int left_req,
int right_req,
int left_spaces,
int right_spaces,
int right_allowed,
int use_commas,
int negative,
int supress_minus,
int decimal,
char *show_decimal)
{
GString *number_string = g_string_new ("");
gint zero_count, nine_count;
gdouble temp;
int group = 0;
for (temp = number; temp >= 1.0; temp /= 10.0){
gint digit = floor (temp);
if (use_commas){
group++;
if (group == 4){
group = 1;
g_string_prepend_c (number_string, ',');
}
}
digit %= 10;
g_string_prepend_c (number_string, digit + '0');
if (left_req > 0)
left_req --;
if (left_spaces > 0)
left_spaces --;
}
for (; left_req > 0; left_req--, left_spaces--)
g_string_prepend_c (number_string, '0');
for (; left_spaces > 0; left_spaces--)
g_string_prepend_c (number_string, ' ');
if (negative && !supress_minus)
g_string_prepend_c (number_string, '-');
if (decimal > 0)
g_string_append (number_string, ".");
else
g_string_append (number_string, show_decimal);
temp = number - floor (number);
for ( ; right_req > 0; right_req --, right_allowed --, right_spaces -- )
{
gint digit;
temp *= 10.0;
digit = floor( temp );
temp -= floor( temp );
if ( right_allowed == 1 && floor( temp * 10.0 ) >= 5 )
{
if ( digit < 9 )
digit ++;
else
{
digit = 0;
do_roundup( number_string );
}
}
g_string_append_c( number_string, digit + '0' );
}
zero_count = 0;
nine_count = 0;
for (; right_allowed > 0; right_allowed --)
{
gint digit;
temp *= 10.0;
digit = floor (temp);
temp -= floor (temp);
if (right_allowed == 1 && floor (temp * 10.0) >= 5)
{
if (digit < 9)
digit ++;
else
{
digit = 0;
right_spaces -= zero_count;
zero_count = nine_count;
right_spaces += zero_count;
do_roundup (number_string);
}
}
if (digit == 0)
zero_count ++;
else
{
right_spaces -= zero_count + 1;
zero_count = 0;
}
if (digit == 9)
nine_count ++;
else
nine_count = 0;
g_string_append_c (number_string, digit + '0');
}
g_string_truncate (number_string, number_string->len - zero_count);
for (; right_spaces > 0; right_spaces--)
{
g_string_append_c (number_string, ' ');
}
return number_string;
}
#if 0
static gchar *
format_number(gdouble number, StyleFormatEntry *style_format_entry, char **color_name )
old_format_number(gdouble number, StyleFormatEntry *style_format_entry)
{
gint left_req = 0, right_req = 0;
gint left_spaces = 0, right_spaces = 0;
gint right_allowed = 0;
gint i = 0;
gdouble temp;
gboolean negative = FALSE;
GString *string = g_string_new( "" );
GString *number_string = g_string_new( "" );
GString *number_string;
gchar *format = style_format_entry->format;
gint length = strlen(format);
gchar *returnvalue;
gint zero_count;
gint nine_count;
format_info info;
gboolean minute_mode = FALSE;
gboolean done = FALSE;
......@@ -482,15 +673,16 @@ format_number(gdouble number, StyleFormatEntry *style_format_entry, char **color
i += append_half( string, format + i, time_split ) - 1;
minute_mode = FALSE;
break;
case '0':
case '?':
case '#':
case '.':
done = any = TRUE;
break;
default:
g_string_append_c( string, format[i] );
break;
process_format_char (string, format, &i, length);
}
}
......@@ -532,11 +724,10 @@ format_number(gdouble number, StyleFormatEntry *style_format_entry, char **color
number /= 1000.0;
}
if ( number < 0.0 )
{
if (number < 0.0){
number = - number;
negative = TRUE;
}
}
#if 0
length = ceil( log10( number ) );
if ( log10( number ) == ceil( log10( number ) ) )
......@@ -547,111 +738,318 @@ format_number(gdouble number, StyleFormatEntry *style_format_entry, char **color
g_string_maybe_expand( number_string, length );
#endif
number_string = render_number (
number,
left_req, right_req,
left_spaces, right_spaces,
right_allowed, negative,
FALSE, info);
g_string_append (string, number_string->str);
g_string_free (number_string, TRUE);
}
returnvalue = g_malloc (string->len + 1);
strncpy (returnvalue, string->str, string->len );
returnvalue[string->len] = 0;
for ( temp = number; temp >= 1.0; temp /= 10.0 )
{
gint digit = floor( temp );
digit %= 10;
g_string_prepend_c( number_string, digit + '0' );
if ( left_req > 0 )
left_req --;
if ( left_spaces > 0 )
left_spaces --;
}
for ( ; left_req > 0; left_req --, left_spaces -- )
{
g_string_prepend_c( number_string, '0' );
}
g_string_free (string, TRUE);
return returnvalue;
}
#endif
for ( ; left_spaces > 0; left_spaces -- )
{
g_string_prepend_c( number_string, ' ' );
}
typedef struct {
char *decimal_point, *append_after_number;
int right_optional, right_spaces, right_req, right_allowed;
int left_spaces, left_req;
int scientific;
int scientific_shows_plus;
int scientific_exp;
int rendered;
int negative;
int decimal_separator_seen;
int supress_minus;
int comma_separator_seen;
} format_info_t;
if( negative )
g_string_prepend_c( number_string, '-' );
static char *
do_render_number (gdouble number, format_info_t *info)
{
GString *res;
char *result;
char *decimal_point;
info->rendered = 1;
if( info.decimal >= 0 )
g_string_append_c( number_string, '.' );
/*
* If the format contains only "#"s to the left of the decimal
* point, number in the [0.0,1.0] range are prefixed with a
* decimal point
*/
if (number > 0.0 && number < 1.0 && info->right_allowed == 0 && info->right_optional > 0)
decimal_point = ".";
else
decimal_point = "";
temp = number - floor( number );
#ifdef 0
printf ("Rendering: %g with:\n", number);
printf ("left_req: %d\n"
"right_req: %d\n"
"left_spaces: %d\n"
"right_spaces:%d\n"
"right_allow: %d\n"
"negative: %d\n"
"supress: %d\n"
"decimalseen: %d\n"
"decimalp: %s\n",
info->left_req,
info->right_req,
info->left_spaces,
info->right_spaces,
info->right_allowed + info->right_optional,
info->negative,
info->supress_minus,
info->decimal_separator_seen,
decimal_point);
#endif
res = render_number (
number,
info->left_req,
info->right_req,
info->left_spaces,
info->right_spaces,
info->right_allowed + info->right_optional,
info->comma_separator_seen,
info->negative,
info->supress_minus,
info->decimal_separator_seen,
decimal_point);
for ( ; right_req > 0; right_req --, right_allowed --, right_spaces -- )
{
gint digit;
temp *= 10.0;
digit = floor( temp );
temp -= floor( temp );
if ( right_allowed == 1 && floor( temp * 10.0 ) >= 5 )
{
if ( digit < 9 )
digit ++;
else
{
digit = 0;
do_roundup( number_string );
}
}
g_string_append_c( number_string, digit + '0' );
if (info->append_after_number)
g_string_append (res, info->append_after_number);
result = g_strdup (res->str);
g_string_free (res, TRUE);
return result;
}
static gchar *
format_number (gdouble number, StyleFormatEntry *style_format_entry)
{
GString *result = g_string_new ("");
char *format = style_format_entry->format;
format_info_t info;
int can_render_number = 0;
int hour_seen = 0;
time_t timec;
struct tm *time_split;
gdouble date;
char *res;
memset (&info, 0, sizeof (info));
date = number;
date -= 25569.0;
date *= 86400.0;
timec = date;
time_split = localtime (&timec);
if (number < 0.0){
info.negative = TRUE;
number = -number;
}
while (*format){
switch (*format){
case '#':
can_render_number = 1;
if (info.decimal_separator_seen)
info.right_optional++;
else
;
break;
case '?':
can_render_number = 1;
if (info.decimal_separator_seen)
info.right_spaces++;
else
info.left_spaces++;
break;
case '0':
can_render_number = 1;
if (info.decimal_separator_seen){
info.right_req++;
info.right_allowed++;
info.right_spaces++;
} else {
info.left_spaces++;
info.left_req++;
}
break;
case '.': {
int c = *(format+1);
can_render_number = 1;
if (c && (c != '0' && c != '#' && c != '?'))
number /= 1000;
else
info.decimal_separator_seen = TRUE;
break;
}
case 'E': case 'e':
can_render_number = 1;
info.scientific = TRUE;
format++;
for (format++; *format;){
if (*format == '+'){
info.scientific_shows_plus = TRUE;
format++;
} else if (*format == '-')
format++;
else if (*format == '0'){
info.scientific_exp++;
format++;
} else
break;
}
/* FIXME: this is a gross hack */
{
char buffer [40];
sprintf (buffer, "%g", number);
g_string_append (result, buffer);
goto finish;
}
/* percent */
case '%':
can_render_number = 1;
number *= 100;
info.append_after_number = "%";
break;
case '\\':
if (*(format+1)){
if (can_render_number && !info.rendered)
g_string_append (result, do_render_number (number, &info));
g_string_append_c (result, *format);
}
break;
case '"': {
if (can_render_number && !info.rendered)
g_string_append (result, do_render_number (number, &info));
for (format++; *format && *format != '"'; format++)
g_string_append_c (result, *format);
break;
zero_count = 0;
nine_count = 0;
for ( ; right_allowed > 0; right_allowed -- )
{
gint digit;
temp *= 10.0;
digit = floor( temp );
temp -= floor( temp );
if ( right_allowed == 1 && floor( temp * 10.0 ) >= 5 )
{
if ( digit < 9 )
digit ++;
else
{
digit = 0;
right_spaces -= zero_count;
zero_count = nine_count;
right_spaces += zero_count;
do_roundup( number_string );