Update Spanish translation
[gnumeric.git] / src / auto-format.c
blob74da5309274fc9c7df5f7ee299e3668d21afaff9
1 /*
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.
8 * Authors:
9 * Morten Welinder <terra@gnome.org>
12 #include <gnumeric-config.h>
13 #include <gnumeric.h>
14 #include <auto-format.h>
16 #include <compilation.h>
17 #include <func.h>
18 #include <cell.h>
19 #include <value.h>
20 #include <expr.h>
21 #include <expr-impl.h>
22 #include <sheet.h>
23 #include <workbook.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
32 * that is bad.
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
53 * "22.5%".
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; };
69 static GnmValue *
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;
79 return NULL;
82 static gboolean
83 is_date (GnmFuncFlags typ, GOFormat const *explicit)
85 return (typ == GNM_FUNC_AUTO_DATE ||
86 (typ == AF_EXPLICIT && go_format_is_date (explicit)));
89 static GnmFuncFlags
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:
94 case GNM_EXPR_OP_GT:
95 case GNM_EXPR_OP_LT:
96 case GNM_EXPR_OP_GTE:
97 case GNM_EXPR_OP_LTE:
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. */
105 GnmFuncFlags typ;
107 typ = do_af_suggest (expr->binary.value_a, epos, explicit);
108 if (typ != GNM_FUNC_AUTO_UNKNOWN && typ != GNM_FUNC_AUTO_UNITLESS)
109 return typ;
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;
125 return typ1;
126 } else {
127 *explicit = explicit2;
128 return typ2;
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) &
138 GNM_FUNC_AUTO_MASK);
140 switch (typ) {
141 case GNM_FUNC_AUTO_FIRST:
142 return do_af_suggest_list (expr->func.argc,
143 expr->func.argv,
144 epos, explicit);
146 case GNM_FUNC_AUTO_SECOND:
147 return do_af_suggest_list (expr->func.argc - 1,
148 expr->func.argv + 1,
149 epos, explicit);
151 default:
152 return typ;
156 case GNM_EXPR_OP_CONSTANT: {
157 GnmValue const *v = expr->constant.value;
159 switch (v->v_any.type) {
160 case VALUE_STRING:
161 case VALUE_ERROR:
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);
176 return closure.typ;
179 default:
180 return GNM_FUNC_AUTO_UNITLESS;
184 case GNM_EXPR_OP_CELLREF: {
185 Sheet const *sheet;
186 GnmCellRef const *ref;
187 GnmCell const *cell;
188 GnmCellPos pos;
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. */
193 if (sheet == NULL)
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);
198 if (cell == NULL)
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:
218 default:
219 return GNM_FUNC_AUTO_UNKNOWN;
223 static GnmFuncFlags
224 do_af_suggest_list (int argc, GnmExprConstPtr const *argv,
225 GnmEvalPos const *epos, GOFormat const **explicit)
227 int i;
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)
233 return typ;
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.
252 GOFormat const *
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)) {
261 case AF_EXPLICIT:
262 break;
264 case GNM_FUNC_AUTO_DATE: /* FIXME: any better idea? */
265 explicit = go_format_default_date ();
266 break;
268 case GNM_FUNC_AUTO_TIME: /* FIXME: any better idea? */
269 explicit = go_format_default_time ();
270 break;
272 case GNM_FUNC_AUTO_PERCENT: /* FIXME: any better idea? */
273 explicit = go_format_default_percentage ();
274 break;
276 case GNM_FUNC_AUTO_MONETARY: /* FIXME: any better idea? */
277 explicit = go_format_default_money ();
278 break;
280 case GNM_FUNC_AUTO_FIRST:
281 case GNM_FUNC_AUTO_SECOND:
282 g_assert_not_reached ();
284 default:
285 explicit = NULL;
288 if (explicit)
289 go_format_ref (explicit);
291 return explicit;
294 GNM_END_KILL_SWITCH_WARNING