4 * Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
5 * Copyright (c) 2008 Robert Ancell
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
34 #include "functions.h"
36 #include "mp-equation.h" // For mp_equation_parse()
39 static const char *display_types
[] = { "ENG", "FIX", "SCI", NULL
};
41 static GCDisplayState
*
42 get_state(GCDisplay
*display
)
44 return &(display
->h
.e
[display
->h
.current
]);
48 exp_has_postfix(char *str
, char *postfix
)
59 plen
= strlen(postfix
);
65 return strcasecmp(str
+ len
- plen
, postfix
) == 0;
69 str_replace(char *str
, char *from
, char *to
)
71 char output
[MAX_DISPLAY
];
74 int flen
= strlen(from
);
75 int tlen
= strlen(to
);
77 for (c
= str
; *c
&& offset
< MAX_DISPLAY
- 1; c
++, offset
++) {
78 if (strncasecmp(from
, c
, flen
) == 0) {
79 SNPRINTF(output
+ offset
, MAX_DISPLAY
- offset
, "%s", to
);
87 if (offset
>= MAX_DISPLAY
)
88 offset
= MAX_DISPLAY
- 1;
89 output
[offset
] = '\0';
93 return strdup(output
);
96 /* Add in the thousand separators characters if required and if we are
97 * currently in the decimal numeric base, use the "right" radix character.
100 /* Add in the thousand separators characters if required */
102 localize_expression(char *dest
, const char *src
, int dest_length
, int *cursor
)
106 int digit_count
= -1, read_cursor
, new_cursor
;
107 gboolean after_radix
= FALSE
;
110 new_cursor
= *cursor
;
115 /* Scan expression looking for numbers and inserting separators */
116 output
= g_string_sized_new(dest_length
);
117 for (c
= src
, read_cursor
= 1; *c
; c
++, read_cursor
++) {
118 /* Insert separators between digits */
119 if (*c
>= '0' && *c
<= '9') {
120 /* Read ahead to find the number of digits */
121 if (digit_count
< 0) {
123 for (d
= c
+ 1; *d
>= '0' && *d
<= '9'; d
++) {
128 g_string_append_c(output
, *c
);
130 /* Insert separator after nth digit */
131 if (v
->display
.show_tsep
&& v
->base
== DEC
&&
132 !after_radix
&& digit_count
> 1 && digit_count
% v
->tsep_count
== 1) {
133 g_string_append(output
, v
->tsep
);
134 if (new_cursor
> read_cursor
) {
141 /* Ignore digits after the radix */
142 else if (*c
== '.') {
145 g_string_append(output
, v
->radix
);
146 // FIXME: Handle cursor if radix is more than one character?
148 /* Reset when encountering other characters (e.g. '+') */
152 g_string_append_c(output
, *c
);
156 STRNCPY(dest
, output
->str
, dest_length
- 1);
157 g_string_free(output
, TRUE
);
159 if (cursor
!= NULL
&& *cursor
!= -1) {
160 *cursor
= new_cursor
;
166 display_clear(GCDisplay
*display
)
169 display_set_string(display
, "", -1);
174 display_get_text(GCDisplay
*display
)
176 return get_state(display
)->expression
;
180 gboolean
display_get_integer(GCDisplay
*display
, gint64
*value
)
183 char buf
[MAX_DISPLAY
];
185 guint bases
[] = {2, 8, 10, 16};
187 text
= display_get_text(display
);
188 if (text
[0] == '\0') {
191 else if (display_is_result(display
)) {
192 display_make_number(display
, buf
, MAX_DISPLAY
, display_get_answer(display
), v
->base
, FALSE
);
196 *value
= g_ascii_strtoll(text
, &endptr
, bases
[v
->base
]);
197 if(*endptr
!= '\0' || ((*value
== G_MAXINT64
|| *value
== G_MININT64
) && errno
== ERANGE
))
203 gboolean
display_get_unsigned_integer(GCDisplay
*display
, guint64
*value
)
206 char buf
[MAX_DISPLAY
];
208 guint bases
[] = {2, 8, 10, 16};
210 text
= display_get_text(display
);
211 if (text
[0] == '\0') {
214 else if (display_is_result(display
)) {
215 display_make_number(display
, buf
, MAX_DISPLAY
, display_get_answer(display
), v
->base
, FALSE
);
219 /* strtoull() treats the string like a 2's complement number which is not what we want */
223 *value
= g_ascii_strtoull(text
, &endptr
, bases
[v
->base
]);
224 if(*endptr
!= '\0' || (*value
== G_MAXUINT64
&& errno
== ERANGE
))
230 MPNumber
*display_get_answer(GCDisplay
*display
)
232 return &get_state(display
)->ans
;
237 display_get_cursor(GCDisplay
*display
)
239 return get_state(display
)->cursor
;
244 display_set_number(GCDisplay
*display
, const MPNumber
*MPval
)
246 char text
[MAX_DISPLAY
];
247 display_make_number(display
, text
, MAX_DISPLAY
, MPval
, v
->base
, FALSE
);
248 display_set_string(display
, text
, -1);
253 display_set_answer(GCDisplay
*display
)
255 display_set_string(display
, "Ans", -1);
260 display_refresh(GCDisplay
*display
)
264 char localized
[MAX_LOCALIZED
], temp
[MAX_LOCALIZED
], *str
, reg
[3];
266 int cursor
= display_get_cursor(display
);
268 e
= get_state(display
);
269 if (display_is_empty(display
)) {
270 mp_set_from_integer(0, &MP_reg
);
271 display_make_number(display
, temp
, MAX_LOCALIZED
, &MP_reg
, v
->base
, FALSE
);
274 str
= strdup(e
->expression
);
277 /* Substitute answer register */
278 display_make_number(display
, temp
, MAX_LOCALIZED
, &e
->ans
, v
->base
, TRUE
);
279 str
= str_replace(str
, "Ans", temp
);
281 /* Replace registers with values. */
282 for (i
= 0; i
< 10; i
++) {
283 SNPRINTF(reg
, 3, "R%d", i
);
284 register_get(i
, &MP_reg
);
285 display_make_number(display
, temp
, MAX_LOCALIZED
, &MP_reg
, v
->base
, FALSE
);
286 str
= str_replace(str
, reg
, temp
);
289 localize_expression(localized
, str
, MAX_LOCALIZED
, &cursor
);
290 ui_set_display(localized
, cursor
);
296 display_set_string(GCDisplay
*display
, const char *value
, int cursor
)
300 e
= get_state(display
);
302 e
->expression
= strdup(value
);
305 display_refresh(display
);
309 display_set_cursor(GCDisplay
*display
, int cursor
)
313 e
= get_state(display
);
315 display_refresh(display
);
319 display_set_error(GCDisplay
*display
, const char *message
)
321 ui_set_statusbar(message
, "gtk-dialog-error");
326 copy_state(GCDisplayState
*dst
, GCDisplayState
*src
)
328 memcpy(dst
, src
, sizeof(GCDisplayState
));
329 dst
->expression
= strdup(src
->expression
);
334 update_undo_redo_button_sensitivity(GCDisplay
*display
)
339 if (display
->h
.current
!= display
->h
.end
) {
343 if (display
->h
.current
!= display
->h
.begin
) {
347 ui_set_undo_enabled(undo
, redo
);
351 void display_clear_stack(GCDisplay
*display
)
353 int i
= display
->h
.begin
;
354 while (i
!= display
->h
.end
) {
355 if (i
!= display
->h
.current
) {
356 free(display
->h
.e
[i
].expression
);
357 display
->h
.e
[i
].expression
= NULL
;
359 i
= ((i
+ 1) % UNDO_HISTORY_LENGTH
);
361 display
->h
.begin
= display
->h
.end
= display
->h
.current
;
362 update_undo_redo_button_sensitivity(display
);
366 void display_push(GCDisplay
*display
)
370 if (display
->h
.current
!= display
->h
.end
) {
371 int i
= display
->h
.current
;
374 i
= ((i
+ 1) % UNDO_HISTORY_LENGTH
);
375 free(display
->h
.e
[i
].expression
);
376 display
->h
.e
[i
].expression
= strdup("Ans");
377 } while (i
!= display
->h
.end
);
380 display
->h
.end
= display
->h
.current
;
382 c
= display
->h
.current
;
383 display
->h
.end
= display
->h
.current
= ((display
->h
.current
+ 1) % UNDO_HISTORY_LENGTH
);
384 if (display
->h
.current
== display
->h
.begin
) {
385 free(display
->h
.e
[display
->h
.begin
].expression
);
386 display
->h
.e
[display
->h
.begin
].expression
= NULL
;
387 display
->h
.begin
= ((display
->h
.begin
+ 1) % UNDO_HISTORY_LENGTH
);
390 copy_state(&(display
->h
.e
[display
->h
.current
]), &(display
->h
.e
[c
]));
391 update_undo_redo_button_sensitivity(display
);
395 void display_pop(GCDisplay
*display
)
397 if (display
->h
.current
!= display
->h
.begin
) {
398 display
->h
.current
= ((display
->h
.current
- 1) % UNDO_HISTORY_LENGTH
);
399 ui_set_statusbar("", "");
401 ui_set_statusbar(_("No undo history"), "gtk-dialog-warning");
403 update_undo_redo_button_sensitivity(display
);
405 display_refresh(display
);
410 display_unpop(GCDisplay
*display
)
412 if (display
->h
.current
!= display
->h
.end
) {
413 display
->h
.current
= ((display
->h
.current
+ 1) % UNDO_HISTORY_LENGTH
);
414 ui_set_statusbar("", "");
416 ui_set_statusbar(_("No redo steps"), "gtk-dialog-warning");
418 update_undo_redo_button_sensitivity(display
);
419 get_state(display
)->cursor
= -1;
420 display_refresh(display
);
425 display_is_undo_step(GCDisplay
*display
)
427 return(display
->h
.current
!= display
->h
.begin
);
431 display_insert(GCDisplay
*display
, int cursor
, const char *text
)
433 char buf
[MAX_DISPLAY
], *currentText
;
436 SNPRINTF(buf
, MAX_DISPLAY
, "%s%s", display_get_text(display
), text
);
438 currentText
= ui_get_display();
439 SNPRINTF(buf
, MAX_DISPLAY
, "%.*s%s%s", cursor
, currentText
, text
, currentText
+ cursor
);
440 cursor
+= strlen(text
);
442 display_set_string(display
, buf
, cursor
);
446 display_insert_at_cursor(GCDisplay
*display
, const char *text
)
448 display_insert(display
, display_get_cursor(display
), text
);
452 display_insert_number(GCDisplay
*display
, int cursor
, const MPNumber
*value
)
454 char text
[MAX_DISPLAY
];
455 display_make_number(display
, text
, MAX_DISPLAY
, value
, v
->base
, FALSE
);
456 display_insert(display
, cursor
, text
);
460 display_insert_number_at_cursor(GCDisplay
*display
, const MPNumber
*value
)
462 display_insert_number(display
, display_get_cursor(display
), value
);
467 display_backspace(GCDisplay
*display
)
469 char buf
[MAX_DISPLAY
] = "", buf2
[MAX_DISPLAY
], *t
;
470 GCDisplayState
*e
= get_state(display
);
474 cursor
= display_get_cursor(display
);
476 /* If cursor is at end of the line then delete the last character preserving accuracy */
478 if (exp_has_postfix(e
->expression
, "Ans")) {
479 display_make_number(display
, buf
, MAX_DISPLAY
, &e
->ans
, v
->base
, FALSE
);
480 e
->expression
= str_replace(e
->expression
, "Ans", buf
);
482 for (i
= 0; i
< 10; i
++) {
483 SNPRINTF(buf
, MAX_DISPLAY
, "R%d", i
);
484 if (exp_has_postfix(e
->expression
, buf
)) {
485 register_get(i
, &MP_reg
);
486 display_make_number(display
, buf2
, MAX_DISPLAY
, &MP_reg
, v
->base
, FALSE
);
487 /* Remove "Rx" postfix and replace with backspaced number */
488 SNPRINTF(buf
, MAX_DISPLAY
, "%.*s%s", strlen(e
->expression
) - 2, e
->expression
- 3, buf2
);
489 display_set_string(display
, buf
, cursor
- 1);
495 SNPRINTF(buf
, MAX_DISPLAY
, "%.*s", strlen(e
->expression
) - 1, e
->expression
);
496 } else if (cursor
> 0) {
497 t
= ui_get_display();
498 SNPRINTF(buf
, MAX_DISPLAY
, "%.*s%s", cursor
- 1, t
, t
+ cursor
);
500 return; /* At the start of the line */
503 display_set_string(display
, buf
, cursor
- 1);
507 display_delete(GCDisplay
*display
)
509 char buf
[MAX_DISPLAY
] = "", *text
;
510 int cursor
= display_get_cursor(display
);
513 text
= ui_get_display();
514 SNPRINTF(buf
, MAX_DISPLAY
, "%.*s%s", cursor
, text
, text
+ cursor
+ 1);
515 display_set_string(display
, buf
, cursor
);
520 display_surround(GCDisplay
*display
, const char *prefix
, const char *suffix
)
522 char buffer
[MAX_DISPLAY
];
524 SNPRINTF(buffer
, MAX_DISPLAY
, "%s%s%s", prefix
, display_get_text(display
), suffix
);
525 display_set_string(display
, buffer
, -1);
530 display_is_empty(GCDisplay
*display
)
532 return strcmp(display_get_text(display
), "") == 0;
536 display_is_result(GCDisplay
*display
)
538 if (strcmp(display_get_text(display
), "Ans") == 0)
545 display_is_usable_number(GCDisplay
*display
, MPNumber
*MPnum
)
547 if (display_is_empty(display
)) {
548 return mp_equation_parse("0", MPnum
);
550 return mp_equation_parse(display_get_text(display
), MPnum
);
556 display_init(GCDisplay
*display
)
560 memset(display
, 0, sizeof(GCDisplay
));
564 if (get_boolean_resource(R_ZEROES
, &i
))
565 display
->show_zeroes
= i
;
567 display
->show_zeroes
= FALSE
;
569 if (get_boolean_resource(R_TSEP
, &i
))
570 display
->show_tsep
= i
;
572 display
->show_tsep
= FALSE
;
574 if (get_enumerated_resource(R_DISPLAY
, display_types
, &i
))
575 display
->format
= (DisplayFormat
) i
;
577 display
->format
= FIX
;
579 for (i
= 0; i
< UNDO_HISTORY_LENGTH
; i
++)
580 display
->h
.e
[i
].expression
= strdup("");
584 void display_set_accuracy(GCDisplay
*display
, int accuracy
)
586 set_int_resource(R_ACCURACY
, accuracy
);
587 get_state(display
)->cursor
= -1;
588 display_refresh(display
);
592 void display_set_show_thousands_separator(GCDisplay
*display
, gboolean visible
)
594 display
->show_tsep
= visible
;
595 set_boolean_resource(R_TSEP
, visible
);
596 display_set_cursor(display
, -1);
597 display_refresh(display
);
601 void display_set_show_trailing_zeroes(GCDisplay
*display
, gboolean visible
)
603 display
->show_zeroes
= visible
;
604 set_boolean_resource(R_ZEROES
, visible
);
605 get_state(display
)->cursor
= -1;
606 display_refresh(display
);
610 void display_set_base(GCDisplay
*display
, int base
)
612 display
->base
= base
;
613 get_state(display
)->cursor
= -1;
614 display_refresh(display
);
618 void display_set_format(GCDisplay
*display
, DisplayFormat type
)
620 v
->display
.format
= type
;
621 set_enumerated_resource(R_DISPLAY
, display_types
, (int) type
);
622 get_state(display
)->cursor
= -1;
623 display_refresh(display
);
628 display_solve(GCDisplay
*display
, MPNumber
*result
)
633 text
= display_get_text(display
);
634 errorCode
= mp_equation_parse(text
, result
);
640 /* Convert engineering or scientific number in the given base. */
642 make_eng_sci(GCDisplay
*display
, char *target
, int target_len
, const MPNumber
*MPnumber
, int base
)
644 static char digits
[] = "0123456789ABCDEF";
645 char fixed
[MAX_DIGITS
], *optr
;
646 MPNumber MP1
, MPatmp
, MPval
;
647 MPNumber MP1base
, MP3base
, MP10base
;
649 MPNumber MPmant
; /* Mantissa. */
650 int ddig
; /* Number of digits in exponent. */
651 int eng
= 0; /* Set if this is an engineering number. */
652 int exp
= 0; /* Exponent */
654 if (display
->format
== ENG
) {
658 mp_abs(MPnumber
, &MPval
);
659 mp_set_from_integer(0, &MP1
);
660 if (mp_is_less_than(MPnumber
, &MP1
)) {
663 mp_set_from_mp(&MPval
, &MPmant
);
665 mp_set_from_integer(basevals
[base
], &MP1base
);
666 mp_pwr_integer(&MP1base
, 3, &MP3base
);
668 mp_pwr_integer(&MP1base
, 10, &MP10base
);
670 mp_set_from_integer(1, &MP1
);
671 mp_divide(&MP1
, &MP10base
, &MPatmp
);
673 mp_set_from_integer(0, &MP1
);
674 if (!mp_is_equal(&MPmant
, &MP1
)) {
675 while (!eng
&& mp_is_greater_equal(&MPmant
, &MP10base
)) {
677 mp_multiply(&MPmant
, &MPatmp
, &MPmant
);
680 while ((!eng
&& mp_is_greater_equal(&MPmant
, &MP1base
)) ||
681 (eng
&& (mp_is_greater_equal(&MPmant
, &MP3base
) || exp
% 3 != 0))) {
683 mp_divide(&MPmant
, &MP1base
, &MPmant
);
686 while (!eng
&& mp_is_less_than(&MPmant
, &MPatmp
)) {
688 mp_multiply(&MPmant
, &MP10base
, &MPmant
);
691 mp_set_from_integer(1, &MP1
);
692 while (mp_is_less_than(&MPmant
, &MP1
) || (eng
&& exp
% 3 != 0)) {
694 mp_multiply(&MPmant
, &MP1base
, &MPmant
);
698 mp_cast_to_string(&MPmant
, basevals
[base
], v
->accuracy
, !v
->display
.show_zeroes
, fixed
, MAX_DIGITS
);
700 for (i
= 0; i
< len
; i
++) {
713 mp_set_from_string("0.5", 10, &MP1
);
714 mp_add_integer(&MP1
, exp
, &MPval
);
715 mp_set_from_integer(1, &MP1
);
716 for (ddig
= 0; mp_is_greater_equal(&MPval
, &MP1
); ddig
++) {
717 mp_divide(&MPval
, &MP1base
, &MPval
);
725 mp_multiply(&MPval
, &MP1base
, &MPval
);
726 dval
= mp_cast_to_int(&MPval
);
727 *optr
++ = digits
[dval
];
729 mp_add_integer(&MPval
, dval
, &MPval
);
735 /* Convert MP number to character string in the given base. */
737 display_make_number(GCDisplay
*display
, char *target
, int target_len
, const MPNumber
*MPnumber
, int base
, int ignoreError
)
739 static double max_fix
[MAXBASES
] = {
740 1.298074214e+33, /* Binary. */
741 2.037035976e+90, /* Octal. */
742 1.000000000e+100, /* Decimal */
743 2.582249878e+120 /* Hexadecimal. */
748 /* NOTE: display_make_number can currently set v->error when converting to a double.
749 * This is to provide the same look&feel as V3 even though gcalctool
750 * now does internal arithmetic to "infinite" precision.
752 * XXX: Needs to be improved. Shouldn't need to convert to a double in
753 * order to do these tests.
756 double number
= mp_cast_to_double(MPnumber
);
759 if (v
->error
&& !ignoreError
) {
763 // FIXME: Do this based on the number of digits, not actual values
764 if ((display
->format
== ENG
) ||
765 (display
->format
== SCI
) ||
766 (display
->format
== FIX
&& val
!= 0.0 && (val
> max_fix
[base
]))) {
767 make_eng_sci(display
, target
, target_len
, MPnumber
, base
);
769 mp_cast_to_string(MPnumber
, basevals
[base
], v
->accuracy
, !v
->display
.show_zeroes
, target
, target_len
);