simplify change mode
[gcalctool.git] / gcalctool / display.c
blob5974ba1f3b2187b68ae3fc879b76ba1fff1917c9
2 /* $Header$
4 * Copyright (c) 1987-2007 Sun Microsystems, Inc. All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
26 #include "display.h"
28 #include "mp.h"
29 #include "mpmath.h"
30 #include "functions.h"
31 #include "ui.h"
33 static char digits[] = "0123456789ABCDEF";
35 static double max_fix[MAXBASES] = {
36 1.298074214e+33, /* Binary. */
37 2.037035976e+90, /* Octal. */
38 1.000000000e+100, /* Decimal */
39 2.582249878e+120 /* Hexadecimal. */
42 static char *make_eng_sci(int *, int);
44 /* Add in the thousand separators characters if required and if we are
45 * currently in the decimal numeric base, use the "right" radix character.
48 void
49 localize_number(char *dest, const char *src)
51 char tnum[MAX_LOCALIZED], *dstp;
53 if (!v->error && v->show_tsep && v->base == DEC) {
54 const char *radixp, *srcp;
55 int n, i;
56 size_t tsep_len;
58 /* Process the fractional part (if any). */
59 srcp = src + strlen(src) - 1;
60 dstp = tnum;
61 if ((radixp = strchr(src, '.')) != NULL) {
62 while (srcp != radixp) {
63 *dstp++ = *srcp--;
65 *dstp++ = *srcp--;
68 /* Process the integer part, add in thousand separators. */
69 tsep_len = strlen(v->tsep);
70 n = 0;
71 while (srcp >= src) {
72 *dstp++ = *srcp--;
73 n++;
74 if (n == 3 && srcp >= src && *srcp != '-') {
75 for (i = tsep_len - 1; i >= 0; i--) {
76 *dstp++ = v->tsep[i];
78 n = 0;
81 *dstp++ = '\0';
83 /* Move from scratch pad to fnum, reversing the character order. */
84 srcp = tnum + strlen(tnum) - 1;
85 dstp = dest;
86 while (srcp >= tnum) {
87 *dstp++ = *srcp--;
89 *dstp++ = '\0';
90 } else {
91 STRCPY(dest, src);
93 dstp = strchr(dest, '.');
94 if (dstp != NULL) {
95 size_t radix_len;
97 radix_len = strlen(v->radix);
98 if (radix_len != 1) {
99 memmove(dstp + radix_len, dstp + 1, strlen (dstp + 1) + 1);
101 MEMCPY(dstp, v->radix, radix_len);
106 static int
107 char_val(char chr)
109 if (chr >= '0' && chr <= '9') {
110 return(chr - '0');
111 } else if (chr >= 'a' && chr <= 'f') {
112 return(chr - 'a' + 10);
113 } else if (chr >= 'A' && chr <= 'F') {
114 return(chr - 'A' + 10);
115 } else {
116 return(-1);
121 void
122 clear_display(int initialise)
124 int i;
126 v->pointed = 0;
127 v->toclear = 1;
128 i = 0;
129 mpcim(&i, v->MPdisp_val);
130 STRNCPY(v->display, make_number(v->MPdisp_val, v->base, FALSE),
131 MAXLINE - 1);
132 ui_set_display(v->display, FALSE);
133 v->ghost_zero = 1;
135 if (initialise == TRUE) {
136 v->show_paren = 0;
137 v->opsptr = 0; /* Clear parentheses stacks. */
138 v->numsptr = 0;
139 v->noparens = 0;
140 ui_set_hyperbolic_state(FALSE); /* Also clears v->hyperbolic. */
141 ui_set_inverse_state(FALSE); /* Also clears v->inverse. */
146 void
147 initialise()
149 /* TODO: perhaps this function should be renamed to reset. */
151 int i;
152 struct exprm_state *e;
154 v->error = 0; /* Currently no display error. */
155 v->cur_op = -1; /* No arithmetic operator defined yet. */
156 v->old_cal_value = -1;
157 i = 0;
158 mpcim(&i, v->MPresult); /* No previous result yet. */
159 mpcim(&i, v->MPdisp_val);
160 mpcim(&i, v->MPlast_input);
162 v->new_input = 1; /* Value zero is on calculator display */
164 e = get_state();
165 free(e->expression);
166 e->expression = NULL;
170 /* Convert MP number to fixed number string in the given base to the
171 * maximum number of digits specified.
174 char *
175 make_fixed(int *MPnumber, char *str, int base, int cmax, int toclear)
177 char half[4], *optr;
178 int MP1base[MP_SIZE], MP1[MP_SIZE], MP2[MP_SIZE], MPval[MP_SIZE];
179 int ndig; /* Total number of digits to generate. */
180 int ddig; /* Number of digits to left of decimal sep. */
181 int dval, n;
183 optr = str;
184 mpabs(MPnumber, MPval);
185 n = 0;
186 mpcim(&n, MP1);
187 if (mplt(MPnumber, MP1)) {
188 *optr++ = '-';
191 mpcim(&basevals[base], MP1base);
193 mppwr(MP1base, &v->accuracy, MP1);
194 /* FIXME: string const. if MPstr_to_num can get it */
195 SPRINTF(half, "0.5");
196 MPstr_to_num(half, DEC, MP2);
197 mpdiv(MP2, MP1, MP1);
198 mpadd(MPval, MP1, MPval);
200 n = 1;
201 mpcim(&n, MP2);
202 if (mplt(MPval, MP2)) {
203 ddig = 0;
204 *optr++ = '0';
205 cmax--;
206 } else {
207 for (ddig = 0; mpge(MPval, MP2); ddig++) {
208 mpdiv(MPval, MP1base, MPval);
212 ndig = MIN(ddig + v->accuracy, --cmax);
214 while (ndig-- > 0) {
215 if (ddig-- == 0) {
216 *optr++ = '.';
218 mpmul(MPval, MP1base, MPval);
219 mpcmi(MPval, &dval);
221 if (dval > basevals[base]-1) {
222 dval = basevals[base]-1;
225 *optr++ = digits[dval];
226 dval = -dval;
227 mpaddi(MPval, &dval, MPval);
229 *optr++ = '\0';
230 if (toclear == TRUE) {
231 v->toclear = 1;
233 v->pointed = 0;
235 if (!v->show_zeroes && v->accuracy != 0) {
236 optr = str + strlen(str) - 1;
237 while (*optr == '0') {
238 optr--;
240 if (optr < str || *optr != '.') {
241 optr++;
243 *optr = '\0';
246 return(str);
250 /* Convert MP number to character string in the given base. */
252 char *
253 make_number(int *MPnumber, int base, int ignoreError)
255 double number, val;
257 /* NOTE: make_number can currently set v->error when converting to a double.
258 * This is to provide the same look&feel as V3 even though gcalctool
259 * now does internal arithmetic to "infinite" precision.
261 * XXX: Needs to be improved. Shouldn't need to convert to a double in
262 * order to do these tests.
265 mpcmd(MPnumber, &number);
266 val = fabs(number);
267 if (v->error && !ignoreError) {
268 return(_("Error"));
270 if ((v->dtype == ENG) ||
271 (v->dtype == SCI) ||
272 (v->dtype == FIX && val != 0.0 && (val > max_fix[base]))) {
273 return(make_eng_sci(MPnumber, base));
274 } else {
275 return(make_fixed(MPnumber, v->fnum, base, MAX_DIGITS, TRUE));
280 /* Convert engineering or scientific number in the given base. */
282 static char *
283 make_eng_sci(int *MPnumber, int base)
285 char half[4], fixed[MAX_DIGITS], *optr;
286 int MP1[MP_SIZE], MPatmp[MP_SIZE], MPval[MP_SIZE];
287 int MP1base[MP_SIZE], MP3base[MP_SIZE], MP10base[MP_SIZE];
288 int i, dval, len, n;
289 int MPmant[MP_SIZE]; /* Mantissa. */
290 int ddig; /* Number of digits in exponent. */
291 int eng = 0; /* Set if this is an engineering number. */
292 int exp = 0; /* Exponent */
294 if (v->dtype == ENG) {
295 eng = 1;
297 optr = v->snum;
298 mpabs(MPnumber, MPval);
299 n = 0;
300 mpcim(&n, MP1);
301 if (mplt(MPnumber, MP1)) {
302 *optr++ = '-';
304 mpstr(MPval, MPmant);
306 mpcim(&basevals[base], MP1base);
307 n = 3;
308 mppwr(MP1base, &n, MP3base);
310 n = 10;
311 mppwr(MP1base, &n, MP10base);
313 n = 1;
314 mpcim(&n, MP1);
315 mpdiv(MP1, MP10base, MPatmp);
317 n = 0;
318 mpcim(&n, MP1);
319 if (!mpeq(MPmant, MP1)) {
320 while (!eng && mpge(MPmant, MP10base)) {
321 exp += 10;
322 mpmul(MPmant, MPatmp, MPmant);
325 while ((!eng && mpge(MPmant, MP1base)) ||
326 (eng && (mpge(MPmant, MP3base) || exp % 3 != 0))) {
327 exp += 1;
328 mpdiv(MPmant, MP1base, MPmant);
331 while (!eng && mplt(MPmant, MPatmp)) {
332 exp -= 10;
333 mpmul(MPmant, MP10base, MPmant);
336 n = 1;
337 mpcim(&n, MP1);
338 while (mplt(MPmant, MP1) || (eng && exp % 3 != 0)) {
339 exp -= 1;
340 mpmul(MPmant, MP1base, MPmant);
344 STRCPY(fixed, make_fixed(MPmant, v->fnum, base, MAX_DIGITS-6, TRUE));
345 len = strlen(fixed);
346 for (i = 0; i < len; i++) {
347 *optr++ = fixed[i];
350 *optr++ = 'e';
352 if (exp < 0) {
353 exp = -exp;
354 *optr++ = '-';
355 } else {
356 *optr++ = '+';
359 SPRINTF(half, "0.5");
360 MPstr_to_num(half, DEC, MP1);
361 mpaddi(MP1, &exp, MPval);
362 n = 1;
363 mpcim(&n, MP1);
364 for (ddig = 0; mpge(MPval, MP1); ddig++) {
365 mpdiv(MPval, MP1base, MPval);
368 if (ddig == 0) {
369 *optr++ = '0';
372 while (ddig-- > 0) {
373 mpmul(MPval, MP1base, MPval);
374 mpcmi(MPval, &dval);
375 *optr++ = digits[dval];
376 dval = -dval;
377 mpaddi(MPval, &dval, MPval);
379 *optr++ = '\0';
380 v->toclear = 1;
381 v->pointed = 0;
383 return(v->snum);
387 /* Convert string into an MP number, in the given base
390 void
391 MPstr_to_num(char *str, enum base_type base, int *MPval)
393 char *optr;
394 int MP1[MP_SIZE], MP2[MP_SIZE], MPbase[MP_SIZE];
395 int i, inum;
396 int exp = 0;
397 int exp_sign = 1;
398 int negate = 0;
399 char *lnp = ui_get_localized_numeric_point();
400 assert(lnp);
402 i = 0;
403 mpcim(&i, MPval);
404 mpcim(&basevals[(int) base], MPbase);
406 optr = str;
408 /* Remove any initial spaces or tabs. */
409 while (*optr == ' ' || *optr == '\t') {
410 optr++;
413 /* Check if this is a negative number. */
414 if (*optr == '-') {
415 negate = 1;
416 optr++;
419 while ((inum = char_val(*optr)) >= 0) {
420 mpmul(MPval, MPbase, MPval);
421 mpaddi(MPval, &inum, MPval);
422 optr++;
425 if (*optr == '.' || *optr == *lnp) {
426 optr++;
427 for (i = 1; (inum = char_val(*optr)) >= 0; i++) {
428 mppwr(MPbase, &i, MP1);
429 mpcim(&inum, MP2);
430 mpdiv(MP2, MP1, MP1);
431 mpadd(MPval, MP1, MPval);
432 optr++;
436 while (*optr == ' ') {
437 optr++;
440 if (*optr != '\0') {
441 if (*optr == '-') {
442 exp_sign = -1;
445 while ((inum = char_val(*++optr)) >= 0) {
446 exp = exp * basevals[(int) base] + inum;
449 exp *= exp_sign;
451 if (v->key_exp) {
452 mppwr(MPbase, &exp, MP1);
453 mpmul(MPval, MP1, MPval);
456 if (negate == 1) {
457 mpneg(MPval, MPval);
462 /* Append the latest parenthesis char to the display item. */
464 void
465 paren_disp(int key)
467 int i, n;
468 char *text;
470 /* If the character is a Delete, clear the whole line, and exit parenthesis
471 * processing.
473 * If the character is a Back Space, remove the last character. If the last
474 * character was a left parenthesis, decrement the parentheses count. If
475 * the parentheses count is zero, exit parenthesis processing.
477 * Otherwise just append the character.
480 n = strlen(v->display);
481 text = buttons[key].symname;
482 switch (key) {
483 case -1:
484 case KEY_CLEAR:
485 v->noparens = v->opsptr = v->numsptr = 0;
486 v->cur_op = -1;
487 i = 0;
488 mpcim(&i, v->MPdisp_val);
489 show_display(v->MPdisp_val);
490 return;
491 case KEY_BACKSPACE:
492 if (!n) {
493 return;
496 if (v->display[n-1] == ')') {
497 v->noparens++;
498 } else if (v->display[n-1] == '(') {
499 v->noparens--;
500 if (!v->noparens) {
501 v->opsptr = v->numsptr = 0;
502 v->cur_op = -1;
503 show_display(v->MPdisp_val);
504 return;
506 } else if (v->display[n-1] == ')') v->noparens++;
507 v->display[n-1] = '\0';
508 break;
510 case KEY_START_BLOCK:
512 /* If this is the first left parenthesis being displayed and there is no
513 * current arithmetic operand, then the current display is initially cleared
514 * to avoid the confusion of showing something like "0(".
517 if (v->noparens == 1 && v->cur_op == -1) {
518 n = 0;
519 v->display[0] = '\0';
521 text = "(";
522 break;
524 case KEY_END_BLOCK:
525 text = ")";
526 break;
529 if (text) {
530 SNPRINTF(v->display+n, MAXLINE-n, "%s", text);
533 n = (n < MAX_DIGITS) ? 0 : n - MAX_DIGITS;
534 v->show_paren = 1; /* Hack to get ui_set_display to really display it. */
535 ui_set_display(&v->display[n], FALSE);
536 v->show_paren = 0;
540 void
541 process_item(struct button *button, int arg)
543 v->current = button->id;
545 if (v->error) {
546 /* Must press a valid key first. */
547 if (v->current != KEY_CLEAR) {
548 return;
550 ui_set_error_state(FALSE);
553 if (v->noparens > 0) {
554 do_paren();
555 return;
558 STRCPY(v->opstr, v->op_item_text);
560 (*button->func)(arg);
564 void
565 show_display(int *MPval)
567 if (!v->error) {
568 STRNCPY(v->display, make_number(MPval, v->base, FALSE), MAXLINE - 1);
569 ui_set_display(v->display, FALSE);
574 /* In arithmetic precedence mode this routine should be called to redraw
575 * the display.
578 void
579 refresh_display()
581 char localized[MAX_LOCALIZED];
582 struct exprm_state *e;
584 switch (v->syntax) {
585 case npa:
586 show_display(v->MPdisp_val);
587 break;
589 case exprs:
590 e = get_state();
592 if (e->expression && strlen(e->expression)) {
593 char *str = gc_strdup(e->expression);
594 char *ans = make_number(e->ans, v->base, TRUE);
596 localize_number(localized, ans);
597 str_replace(&str, "Ans", localized);
599 { /* Replace registers with values. */
600 char reg[3];
601 int n = '0';
602 int i;
604 for (i = 0; i < 10; i++) {
605 char *reg_val;
606 int MP_reg[MP_SIZE];
608 SNPRINTF(reg, 3, "R%c", n+i);
609 do_rcl_reg(i, MP_reg);
610 reg_val = make_number(MP_reg, v->base, FALSE);
611 str_replace(&str, reg, reg_val);
614 ui_write_display(str);
615 free(str);
616 v->ghost_zero = 0;
617 } else {
618 int MP1[MP_SIZE];
620 do_zero(MP1);
621 show_display(MP1);
622 v->ghost_zero = 1;
624 break;
626 default:
627 assert(0);