1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * auto-format.c: Suggest formats for expressions.
5 * NOTE: If you were looking for the code to automatically put style on a
6 * region, you are in the wrong place. See the files file-autoft.c and
7 * dialogs/dialog-autoformat.c instead.
10 * Morten Welinder <terra@gnome.org>
13 #include <gnumeric-config.h>
15 #include "auto-format.h"
17 #include "compilation.h"
22 #include "expr-impl.h"
25 #include <goffice/goffice.h>
27 /* ------------------------------------------------------------------------- */
29 * An important note about correctness.
31 * For some functions it is easy to tell what correct behaviour is;
32 * if the evaluation of the percent operator yields anything but x/100
35 * This function is not that simple.
37 * If we fail to suggest a format when one might have been deduced, that
38 * is really not a big deal. So the fact that "=date(2000,1,1)^1" is not
39 * recognised as a date bothers no-one.
41 * If we occasionally suggest a format where none is reasonable, that is
42 * not a problem either. "=pv(1,2,3,4,5)*today()" has no reasonable
43 * format, but we assign one. Tough.
45 * On the other hand, if we suggest a bad format for a function that does
46 * have a good format, this is bad. (Since the user will just select
47 * another format, it isn't critical, just bad.)
49 * Please resist the temptation of making this ridiculously smart. For
50 * example, avoid too much algebra here and don't look at actual numbers
51 * encountered. Let the evaluator do that. One reason for this is that
52 * if you are entering a range of similar data, you really want the same
53 * format. You don't want a different number of decimals for "22%" and
56 * (The problem here is actually more a physics problem -- what are the
57 * units -- than a math problem.)
59 /* ------------------------------------------------------------------------- */
61 #define AF_EXPLICIT ((GnmFuncFlags)(GNM_FUNC_AUTO_MASK + 1))
63 static GnmFuncFlags
do_af_suggest_list (int argc
,
64 GnmExprConstPtr
const *argv
,
65 GnmEvalPos
const *epos
,
66 GOFormat
const **explicit);
68 struct cb_af_suggest
{ GnmFuncFlags typ
; GOFormat
const **explicit; };
71 cb_af_suggest (GnmCellIter
const *iter
, gpointer user
)
73 struct cb_af_suggest
*data
= user
;
75 *(data
->explicit) = gnm_cell_get_format (iter
->cell
);
76 if (*(data
->explicit)) {
77 data
->typ
= AF_EXPLICIT
;
78 return VALUE_TERMINATE
;
84 is_date (GnmFuncFlags typ
, GOFormat
const *explicit)
86 return (typ
== GNM_FUNC_AUTO_DATE
||
87 (typ
== AF_EXPLICIT
&& go_format_is_date (explicit)));
91 do_af_suggest (GnmExpr
const *expr
, GnmEvalPos
const *epos
, GOFormat
const **explicit)
93 switch (GNM_EXPR_GET_OPER (expr
)) {
94 case GNM_EXPR_OP_EQUAL
:
99 case GNM_EXPR_OP_NOT_EQUAL
:
100 return GNM_FUNC_AUTO_UNITLESS
; /* Close enough. */
102 case GNM_EXPR_OP_MULT
:
103 /* Fall through. This isn't quite right, but good enough. */
104 case GNM_EXPR_OP_ADD
: {
105 /* Return the first interesting type we see. */
108 typ
= do_af_suggest (expr
->binary
.value_a
, epos
, explicit);
109 if (typ
!= GNM_FUNC_AUTO_UNKNOWN
&& typ
!= GNM_FUNC_AUTO_UNITLESS
)
112 return do_af_suggest (expr
->binary
.value_b
, epos
, explicit);
115 case GNM_EXPR_OP_SUB
: {
116 GnmFuncFlags typ1
, typ2
;
117 GOFormat
const *explicit1
= NULL
, *explicit2
= NULL
;
119 typ1
= do_af_suggest (expr
->binary
.value_a
, epos
, &explicit1
);
120 typ2
= do_af_suggest (expr
->binary
.value_b
, epos
, &explicit2
);
122 if (is_date (typ1
, explicit1
) && is_date (typ2
, explicit2
))
123 return GNM_FUNC_AUTO_UNITLESS
;
124 else if (typ1
!= GNM_FUNC_AUTO_UNKNOWN
&& typ1
!= GNM_FUNC_AUTO_UNITLESS
) {
125 *explicit = explicit1
;
128 *explicit = explicit2
;
133 case GNM_EXPR_OP_DIV
:
134 /* Check the left-hand side only. */
135 return do_af_suggest (expr
->binary
.value_a
, epos
, explicit);
137 case GNM_EXPR_OP_FUNCALL
: {
139 (expr
->func
.func
->flags
& GNM_FUNC_AUTO_MASK
);
142 case GNM_FUNC_AUTO_FIRST
:
143 return do_af_suggest_list (expr
->func
.argc
,
147 case GNM_FUNC_AUTO_SECOND
:
148 return do_af_suggest_list (expr
->func
.argc
- 1,
157 case GNM_EXPR_OP_CONSTANT
: {
158 GnmValue
const *v
= expr
->constant
.value
;
160 switch (v
->v_any
.type
) {
163 return GNM_FUNC_AUTO_UNKNOWN
;
165 case VALUE_CELLRANGE
: {
166 struct cb_af_suggest closure
;
168 /* If we don't have a sheet, we cannot look up vars. */
169 if (epos
->sheet
== NULL
)
170 return GNM_FUNC_AUTO_UNKNOWN
;
172 closure
.typ
= GNM_FUNC_AUTO_UNKNOWN
;
173 closure
.explicit = explicit;
174 workbook_foreach_cell_in_range (epos
, v
,
175 CELL_ITER_IGNORE_BLANK
,
176 &cb_af_suggest
, &closure
);
181 return GNM_FUNC_AUTO_UNITLESS
;
185 case GNM_EXPR_OP_CELLREF
: {
187 GnmCellRef
const *ref
;
191 ref
= &expr
->cellref
.ref
;
192 sheet
= eval_sheet (ref
->sheet
, epos
->sheet
);
193 /* If we don't have a sheet, we cannot look up vars. */
195 return GNM_FUNC_AUTO_UNKNOWN
;
197 gnm_cellpos_init_cellref (&pos
, ref
, &epos
->eval
, sheet
);
198 cell
= sheet_cell_get (sheet
, pos
.col
, pos
.row
);
200 return GNM_FUNC_AUTO_UNKNOWN
;
202 *explicit = gnm_cell_get_format (cell
);
203 return *explicit ? AF_EXPLICIT
: GNM_FUNC_AUTO_UNKNOWN
;
206 case GNM_EXPR_OP_PAREN
:
207 case GNM_EXPR_OP_UNARY_NEG
:
208 case GNM_EXPR_OP_UNARY_PLUS
:
209 return do_af_suggest (expr
->unary
.value
, epos
, explicit);
211 case GNM_EXPR_OP_PERCENTAGE
:
212 return GNM_FUNC_AUTO_PERCENT
;
214 case GNM_EXPR_OP_EXP
:
215 case GNM_EXPR_OP_CAT
:
216 case GNM_EXPR_OP_NAME
:
217 case GNM_EXPR_OP_ARRAY_CORNER
:
218 case GNM_EXPR_OP_ARRAY_ELEM
:
220 return GNM_FUNC_AUTO_UNKNOWN
;
225 do_af_suggest_list (int argc
, GnmExprConstPtr
const *argv
,
226 GnmEvalPos
const *epos
, GOFormat
const **explicit)
230 for (i
= 0; i
< argc
; i
++) {
231 GnmFuncFlags typ
= do_af_suggest (argv
[i
], epos
, explicit);
232 if (typ
!= GNM_FUNC_AUTO_UNKNOWN
&&
233 typ
!= GNM_FUNC_AUTO_UNITLESS
)
237 return GNM_FUNC_AUTO_UNKNOWN
;
240 /* ------------------------------------------------------------------------- */
242 GNM_BEGIN_KILL_SWITCH_WARNING
245 gnm_auto_style_format_suggest (GnmExprTop
const *texpr
, GnmEvalPos
const *epos
)
247 GOFormat
const *explicit = NULL
;
249 g_return_val_if_fail (texpr
!= NULL
, NULL
);
250 g_return_val_if_fail (epos
!= NULL
, NULL
);
252 switch (do_af_suggest (texpr
->expr
, epos
, &explicit)) {
256 case GNM_FUNC_AUTO_DATE
: /* FIXME: any better idea? */
257 explicit = go_format_default_date ();
260 case GNM_FUNC_AUTO_TIME
: /* FIXME: any better idea? */
261 explicit = go_format_default_time ();
264 case GNM_FUNC_AUTO_PERCENT
: /* FIXME: any better idea? */
265 explicit = go_format_default_percentage ();
268 case GNM_FUNC_AUTO_MONETARY
: /* FIXME: any better idea? */
269 explicit = go_format_default_money ();
272 case GNM_FUNC_AUTO_FIRST
:
273 case GNM_FUNC_AUTO_SECOND
:
274 g_assert_not_reached ();
281 go_format_ref (explicit);
286 GNM_END_KILL_SWITCH_WARNING