2 * auto-format.c: Suggest formats for expressions.
4 * NOTE: If you were looking for the code to automatically put style on a
5 * region, you are in the wrong place. See the files file-autoft.c and
6 * dialogs/dialog-autoformat.c instead.
9 * Morten Welinder <terra@gnome.org>
12 #include <gnumeric-config.h>
14 #include <auto-format.h>
16 #include <compilation.h>
21 #include <expr-impl.h>
24 #include <goffice/goffice.h>
26 /* ------------------------------------------------------------------------- */
28 * An important note about correctness.
30 * For some functions it is easy to tell what correct behaviour is;
31 * if the evaluation of the percent operator yields anything but x/100
34 * This function is not that simple.
36 * If we fail to suggest a format when one might have been deduced, that
37 * is really not a big deal. So the fact that "=date(2000,1,1)^1" is not
38 * recognised as a date bothers no-one.
40 * If we occasionally suggest a format where none is reasonable, that is
41 * not a problem either. "=pv(1,2,3,4,5)*today()" has no reasonable
42 * format, but we assign one. Tough.
44 * On the other hand, if we suggest a bad format for a function that does
45 * have a good format, this is bad. (Since the user will just select
46 * another format, it isn't critical, just bad.)
48 * Please resist the temptation of making this ridiculously smart. For
49 * example, avoid too much algebra here and don't look at actual numbers
50 * encountered. Let the evaluator do that. One reason for this is that
51 * if you are entering a range of similar data, you really want the same
52 * format. You don't want a different number of decimals for "22%" and
55 * (The problem here is actually more a physics problem -- what are the
56 * units -- than a math problem.)
58 /* ------------------------------------------------------------------------- */
60 #define AF_EXPLICIT ((GnmFuncFlags)(GNM_FUNC_AUTO_MASK + 1))
62 static GnmFuncFlags
do_af_suggest_list (int argc
,
63 GnmExprConstPtr
const *argv
,
64 GnmEvalPos
const *epos
,
65 GOFormat
const **explicit);
67 struct cb_af_suggest
{ GnmFuncFlags typ
; GOFormat
const **explicit; };
70 cb_af_suggest (GnmCellIter
const *iter
, gpointer user
)
72 struct cb_af_suggest
*data
= user
;
74 *(data
->explicit) = gnm_cell_get_format (iter
->cell
);
75 if (*(data
->explicit)) {
76 data
->typ
= AF_EXPLICIT
;
77 return VALUE_TERMINATE
;
83 is_date (GnmFuncFlags typ
, GOFormat
const *explicit)
85 return (typ
== GNM_FUNC_AUTO_DATE
||
86 (typ
== AF_EXPLICIT
&& go_format_is_date (explicit)));
90 do_af_suggest (GnmExpr
const *expr
, GnmEvalPos
const *epos
, GOFormat
const **explicit)
92 switch (GNM_EXPR_GET_OPER (expr
)) {
93 case GNM_EXPR_OP_EQUAL
:
98 case GNM_EXPR_OP_NOT_EQUAL
:
99 return GNM_FUNC_AUTO_UNITLESS
; /* Close enough. */
101 case GNM_EXPR_OP_MULT
:
102 /* Fall through. This isn't quite right, but good enough. */
103 case GNM_EXPR_OP_ADD
: {
104 /* Return the first interesting type we see. */
107 typ
= do_af_suggest (expr
->binary
.value_a
, epos
, explicit);
108 if (typ
!= GNM_FUNC_AUTO_UNKNOWN
&& typ
!= GNM_FUNC_AUTO_UNITLESS
)
111 return do_af_suggest (expr
->binary
.value_b
, epos
, explicit);
114 case GNM_EXPR_OP_SUB
: {
115 GnmFuncFlags typ1
, typ2
;
116 GOFormat
const *explicit1
= NULL
, *explicit2
= NULL
;
118 typ1
= do_af_suggest (expr
->binary
.value_a
, epos
, &explicit1
);
119 typ2
= do_af_suggest (expr
->binary
.value_b
, epos
, &explicit2
);
121 if (is_date (typ1
, explicit1
) && is_date (typ2
, explicit2
))
122 return GNM_FUNC_AUTO_UNITLESS
;
123 else if (typ1
!= GNM_FUNC_AUTO_UNKNOWN
&& typ1
!= GNM_FUNC_AUTO_UNITLESS
) {
124 *explicit = explicit1
;
127 *explicit = explicit2
;
132 case GNM_EXPR_OP_DIV
:
133 /* Check the left-hand side only. */
134 return do_af_suggest (expr
->binary
.value_a
, epos
, explicit);
136 case GNM_EXPR_OP_FUNCALL
: {
137 GnmFuncFlags typ
= (gnm_func_get_flags (expr
->func
.func
) &
141 case GNM_FUNC_AUTO_FIRST
:
142 return do_af_suggest_list (expr
->func
.argc
,
146 case GNM_FUNC_AUTO_SECOND
:
147 return do_af_suggest_list (expr
->func
.argc
- 1,
156 case GNM_EXPR_OP_CONSTANT
: {
157 GnmValue
const *v
= expr
->constant
.value
;
159 switch (v
->v_any
.type
) {
162 return GNM_FUNC_AUTO_UNKNOWN
;
164 case VALUE_CELLRANGE
: {
165 struct cb_af_suggest closure
;
167 /* If we don't have a sheet, we cannot look up vars. */
168 if (epos
->sheet
== NULL
)
169 return GNM_FUNC_AUTO_UNKNOWN
;
171 closure
.typ
= GNM_FUNC_AUTO_UNKNOWN
;
172 closure
.explicit = explicit;
173 workbook_foreach_cell_in_range (epos
, v
,
174 CELL_ITER_IGNORE_BLANK
,
175 &cb_af_suggest
, &closure
);
180 return GNM_FUNC_AUTO_UNITLESS
;
184 case GNM_EXPR_OP_CELLREF
: {
186 GnmCellRef
const *ref
;
190 ref
= &expr
->cellref
.ref
;
191 sheet
= eval_sheet (ref
->sheet
, epos
->sheet
);
192 /* If we don't have a sheet, we cannot look up vars. */
194 return GNM_FUNC_AUTO_UNKNOWN
;
196 gnm_cellpos_init_cellref (&pos
, ref
, &epos
->eval
, sheet
);
197 cell
= sheet_cell_get (sheet
, pos
.col
, pos
.row
);
199 return GNM_FUNC_AUTO_UNKNOWN
;
201 *explicit = gnm_cell_get_format (cell
);
202 return *explicit ? AF_EXPLICIT
: GNM_FUNC_AUTO_UNKNOWN
;
205 case GNM_EXPR_OP_PAREN
:
206 case GNM_EXPR_OP_UNARY_NEG
:
207 case GNM_EXPR_OP_UNARY_PLUS
:
208 return do_af_suggest (expr
->unary
.value
, epos
, explicit);
210 case GNM_EXPR_OP_PERCENTAGE
:
211 return GNM_FUNC_AUTO_PERCENT
;
213 case GNM_EXPR_OP_EXP
:
214 case GNM_EXPR_OP_CAT
:
215 case GNM_EXPR_OP_NAME
:
216 case GNM_EXPR_OP_ARRAY_CORNER
:
217 case GNM_EXPR_OP_ARRAY_ELEM
:
219 return GNM_FUNC_AUTO_UNKNOWN
;
224 do_af_suggest_list (int argc
, GnmExprConstPtr
const *argv
,
225 GnmEvalPos
const *epos
, GOFormat
const **explicit)
229 for (i
= 0; i
< argc
; i
++) {
230 GnmFuncFlags typ
= do_af_suggest (argv
[i
], epos
, explicit);
231 if (typ
!= GNM_FUNC_AUTO_UNKNOWN
&&
232 typ
!= GNM_FUNC_AUTO_UNITLESS
)
236 return GNM_FUNC_AUTO_UNKNOWN
;
239 /* ------------------------------------------------------------------------- */
241 GNM_BEGIN_KILL_SWITCH_WARNING
244 * gnm_auto_style_format_suggest:
245 * @texpr: A #GnmExprTop
246 * @epos: A #GnmEvalPos
248 * Suggest a format for @texpr.
250 * Returns: (transfer full) (nullable): Suggested format.
253 gnm_auto_style_format_suggest (GnmExprTop
const *texpr
, GnmEvalPos
const *epos
)
255 GOFormat
const *explicit = NULL
;
257 g_return_val_if_fail (texpr
!= NULL
, NULL
);
258 g_return_val_if_fail (epos
!= NULL
, NULL
);
260 switch (do_af_suggest (texpr
->expr
, epos
, &explicit)) {
264 case GNM_FUNC_AUTO_DATE
: /* FIXME: any better idea? */
265 explicit = go_format_default_date ();
268 case GNM_FUNC_AUTO_TIME
: /* FIXME: any better idea? */
269 explicit = go_format_default_time ();
272 case GNM_FUNC_AUTO_PERCENT
: /* FIXME: any better idea? */
273 explicit = go_format_default_percentage ();
276 case GNM_FUNC_AUTO_MONETARY
: /* FIXME: any better idea? */
277 explicit = go_format_default_money ();
280 case GNM_FUNC_AUTO_FIRST
:
281 case GNM_FUNC_AUTO_SECOND
:
282 g_assert_not_reached ();
289 go_format_ref (explicit);
294 GNM_END_KILL_SWITCH_WARNING