simplify change mode dialog
[gcalctool.git] / gcalctool / gtk.c
blob0a1a67d61180fb603c2639487e52d561ce4cbc68
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 <ctype.h>
25 #include <limits.h>
26 #include <sys/param.h>
27 #include <sys/stat.h>
28 #include <netdb.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkx.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <glade/glade.h>
34 #include "ui.h"
36 #include "config.h"
37 #include "dsdefs.h"
38 #include "functions.h"
39 #include "lr_parser.h"
40 #include "ce_parser.h"
41 #include "mpmath.h"
42 #include "display.h"
43 #include "get.h"
45 /* Popup menu types. */
46 /* FIXME: This enum could be removed */
47 enum menu_type { M_ACC, M_CON, M_EXCH, M_FUN, M_LSHF,
48 M_RCL, M_RSHF, M_STO, M_NONE };
49 #define MAXMENUS 9 /* Maximum number of popup menus. */
51 #define MAX_ACCELERATORS 8
52 struct button_widget {
53 int key;
54 char *widget_name;
55 enum menu_type mtype;
56 guint accelerator_mods[MAX_ACCELERATORS];
57 guint accelerator_keys[MAX_ACCELERATORS];
60 /* Window titles dependant on mode */
61 static char *titles[] = {
62 N_("Calculator"), N_("Calculator - Advanced"), N_("Calculator - Financial"),
63 N_("Calculator - Scientific")
66 /* Window titles dependant on mode and hostname */
67 static char *hostname_titles[] = {
68 N_("Calculator [%s]"), N_("Calculator [%s] - Advanced"), N_("Calculator [%s] - Financial"),
69 N_("Calculator [%s] - Scientific")
72 /* This table shows the keyboard values that are currently being used:
74 * | a b c d e f g h i j k l m n o p q r s t u v w x y z
75 *-----------+-----------------------------------------------------
76 * Lower: | a b c d e f i l m n p r s t u v x
77 * Upper: | A C D E F G J K L M N P R S T X Y
78 * Numeric: | 0 1 2 3 4 5 6 7 8 9
79 * Other: | @ . + - * / = % ( ) # < > [ ] { } | & ~ ^ ? ! :
80 * | BackSpace Delete Return
81 *-----------+-----------------------------------------------------
84 static struct button_widget button_widgets[] = {
85 {KEY_0, "0", M_NONE,
86 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
87 { GDK_0, GDK_0, GDK_KP_0, GDK_KP_Insert, 0 }},
89 {KEY_1, "1", M_NONE,
90 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
91 { GDK_1, GDK_1, GDK_KP_1, GDK_KP_End, GDK_R13, 0 }},
93 {KEY_2, "2", M_NONE,
94 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
95 { GDK_2, GDK_2, GDK_KP_2, GDK_KP_Down, 0 }},
97 {KEY_3, "3", M_NONE,
98 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
99 { GDK_3, GDK_3, GDK_KP_3, GDK_KP_Page_Down, GDK_R15, 0 }},
101 {KEY_4, "4", M_NONE,
102 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
103 { GDK_4, GDK_4, GDK_KP_4, GDK_KP_Left, 0 }},
105 {KEY_5, "5", M_NONE,
106 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
107 { GDK_5, GDK_5, GDK_KP_5, GDK_KP_Begin, GDK_R11, 0 }},
109 {KEY_6, "6", M_NONE,
110 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
111 { GDK_6, GDK_6, GDK_KP_6, GDK_KP_Right, 0 }},
113 {KEY_7, "7", M_NONE,
114 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
115 { GDK_7, GDK_7, GDK_KP_7, GDK_KP_Home, GDK_R7, 0 }},
117 {KEY_8, "8", M_NONE,
118 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
119 { GDK_8, GDK_8, GDK_KP_8, GDK_KP_Up, 0 }},
121 {KEY_9, "9", M_NONE,
122 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
123 { GDK_9, GDK_9, GDK_KP_9, GDK_KP_Page_Up, GDK_R9, 0 }},
125 {KEY_A, "a", M_NONE,
126 { 0, 0 },
127 { GDK_a, 0 }},
129 {KEY_B, "b", M_NONE,
130 { 0, 0 },
131 { GDK_b, 0 }},
133 {KEY_C, "c", M_NONE,
134 { 0, 0 },
135 { GDK_c, 0 }},
137 {KEY_D, "d", M_NONE,
138 { 0, 0 },
139 { GDK_d, 0 }},
141 {KEY_E, "e", M_NONE,
142 { 0, 0 },
143 { GDK_e, 0 }},
145 {KEY_F, "f", M_NONE,
146 { 0, 0 },
147 { GDK_f, 0 }},
149 {KEY_CLEAR, "clear_simple", M_NONE,
150 { 0, 0 },
151 { GDK_Delete, 0 }},
153 {KEY_CLEAR, "clear_advanced", M_NONE,
154 { 0, 0 },
155 { GDK_Delete, 0 }},
157 {KEY_SHIFT, "shift_left", M_LSHF,
158 { GDK_SHIFT_MASK, 0 },
159 { GDK_less, 0 }},
161 {KEY_SHIFT, "shift_right", M_RSHF,
162 { GDK_SHIFT_MASK, 0 },
163 { GDK_greater, 0 }},
165 {KEY_SET_ACCURACY, "accuracy", M_ACC,
166 { GDK_SHIFT_MASK, 0 },
167 { GDK_A, 0 }},
169 {KEY_CONSTANT, "constants", M_CON,
170 { GDK_SHIFT_MASK, 0, 0 },
171 { GDK_numbersign, GDK_numbersign, 0 }},
173 {KEY_FUNCTION, "functions", M_FUN,
174 { GDK_SHIFT_MASK, 0 },
175 { GDK_F, 0 }},
177 {KEY_STORE, "store", M_STO,
178 { GDK_SHIFT_MASK, 0 },
179 { GDK_S, 0 }},
181 {KEY_RECALL, "recall", M_RCL,
182 { GDK_SHIFT_MASK, 0 },
183 { GDK_R, 0 }},
185 {KEY_EXCHANGE, "exchange", M_EXCH,
186 { GDK_SHIFT_MASK, 0 },
187 { GDK_X, 0 }},
189 {KEY_CLEAR_ENTRY, "clear_entry_simple", M_NONE,
190 { GDK_CONTROL_MASK, 0, 0 },
191 { GDK_BackSpace, GDK_Escape, 0 }},
193 {KEY_CLEAR_ENTRY, "clear_entry_advanced", M_NONE,
194 { GDK_CONTROL_MASK, 0, 0 },
195 { GDK_BackSpace, GDK_Escape, 0 }},
197 {KEY_BACKSPACE, "backspace_simple", M_NONE,
198 { 0, 0 },
199 { GDK_BackSpace, 0 }},
201 {KEY_BACKSPACE, "backspace_advanced", M_NONE,
202 { 0, 0 },
203 { GDK_BackSpace, 0 }},
205 {KEY_NUMERIC_POINT, "numeric_point", M_NONE,
206 { 0, 0, 0, 0 },
207 { GDK_period, GDK_KP_Decimal, GDK_KP_Delete, GDK_KP_Separator, 0 }},
209 {KEY_CALCULATE, "result", M_NONE,
210 { 0, 0, 0, GDK_SHIFT_MASK, 0 },
211 { GDK_equal, GDK_KP_Enter, GDK_Return, GDK_equal, 0 }},
213 {KEY_START_BLOCK, "start_group", M_NONE,
214 { GDK_SHIFT_MASK, 0 },
215 { GDK_parenleft, 0 }},
217 {KEY_END_BLOCK, "end_group", M_NONE,
218 { GDK_SHIFT_MASK, 0 },
219 { GDK_parenright, 0 }},
221 {KEY_ADD, "add", M_NONE,
222 { GDK_SHIFT_MASK, 0, 0, 0 },
223 { GDK_plus, GDK_plus, GDK_KP_Add, 0 }},
225 {KEY_SUBTRACT, "subtract", M_NONE,
226 { 0, 0, 0, 0 },
227 { GDK_minus, GDK_KP_Subtract, GDK_R4, 0 }},
229 {KEY_MULTIPLY, "multiply", M_NONE,
230 { GDK_SHIFT_MASK, 0, 0, 0, 0 },
231 { GDK_asterisk, GDK_KP_Multiply, GDK_x, GDK_R6, 0 }},
233 {KEY_DIVIDE, "divide", M_NONE,
234 { 0, GDK_SHIFT_MASK, 0, 0, GDK_SHIFT_MASK, 0 },
235 { GDK_slash, GDK_slash, GDK_KP_Divide, GDK_R5, GDK_slash, 0 }},
237 {KEY_CHANGE_SIGN, "change_sign_simple", M_NONE,
238 { GDK_SHIFT_MASK, 0 },
239 { GDK_C, 0 }},
241 {KEY_CHANGE_SIGN, "change_sign_advanced", M_NONE,
242 { GDK_SHIFT_MASK, 0 },
243 { GDK_C, 0 }},
245 {KEY_INTEGER, "integer_portion", M_NONE,
246 { 0, 0 },
247 { GDK_i, 0 }},
249 {KEY_FRACTION, "fractional_portion", M_NONE,
250 { GDK_SHIFT_MASK, 0 },
251 { GDK_colon, 0 }},
253 {KEY_PERCENTAGE, "percentage", M_NONE,
254 { GDK_SHIFT_MASK, 0 },
255 { GDK_percent, 0 }},
257 {KEY_SQUARE, "square", M_NONE,
258 { GDK_SHIFT_MASK, 0 },
259 { GDK_at, 0 }},
261 {KEY_SQUARE_ROOT, "sqrt", M_NONE,
262 { 0, 0 },
263 { GDK_s, 0 }},
265 {KEY_RECIPROCAL, "reciprocal", M_NONE,
266 { 0, 0 },
267 { GDK_r, 0 }},
269 {KEY_ABSOLUTE_VALUE, "abs", M_NONE,
270 { 0, 0 },
271 { GDK_u, 0 }},
273 {KEY_MASK_16, "mask_16", M_NONE,
274 { 0, 0 },
275 { GDK_bracketright, 0 }},
277 {KEY_MASK_32, "mask_32", M_NONE,
278 { 0, 0 },
279 { GDK_bracketleft, 0 }},
281 {KEY_MODULUS_DIVIDE, "modulus_divide", M_NONE,
282 { GDK_SHIFT_MASK, 0 },
283 { GDK_M, 0 }},
285 {KEY_EXPONENTIAL, "exponential", M_NONE,
286 { GDK_SHIFT_MASK, 0 },
287 { GDK_E, 0 }},
289 {KEY_E_POW_X, "pow_e", M_NONE,
290 { GDK_SHIFT_MASK, 0 },
291 { GDK_braceleft, 0 }},
293 {KEY_10_POW_X, "pow_10", M_NONE,
294 { GDK_SHIFT_MASK, 0 },
295 { GDK_braceright, 0 }},
297 {KEY_X_POW_Y, "x_pow_y", M_NONE,
298 { GDK_SHIFT_MASK, GDK_SHIFT_MASK, 0 },
299 { GDK_caret, GDK_asciicircum, 0 }},
301 {KEY_NATURAL_LOGARITHM, "natural_logarithm", M_NONE,
302 { GDK_SHIFT_MASK, 0 },
303 { GDK_N, 0 }},
305 {KEY_LOGARITHM, "logarithm", M_NONE,
306 { GDK_SHIFT_MASK, 0 },
307 { GDK_G, 0 }},
309 {KEY_FACTORIAL, "factorial", M_NONE,
310 { GDK_SHIFT_MASK, 0 },
311 { GDK_exclam, 0 }},
313 {KEY_RANDOM, "random", M_NONE,
314 { GDK_SHIFT_MASK, 0 },
315 { GDK_question, 0 }},
317 {KEY_SIN, "sine", M_NONE,
318 { GDK_SHIFT_MASK, 0 },
319 { GDK_K, 0 }},
321 {KEY_COS, "cosine", M_NONE,
322 { GDK_SHIFT_MASK, 0 },
323 { GDK_J, 0 }},
325 {KEY_TAN, "tangent", M_NONE,
326 { GDK_SHIFT_MASK, 0 },
327 { GDK_L, 0 }},
329 {KEY_NOT, "not", M_NONE,
330 { GDK_SHIFT_MASK, 0 },
331 { GDK_asciitilde, 0 }},
333 {KEY_OR, "or", M_NONE,
334 { GDK_SHIFT_MASK, 0 },
335 { GDK_bar, 0 }},
337 {KEY_AND, "and", M_NONE,
338 { GDK_SHIFT_MASK, 0 },
339 { GDK_ampersand, 0 }},
341 {KEY_XOR, "xor", M_NONE,
342 { 0 },
343 { 0 }},
345 {KEY_XNOR, "xnor", M_NONE,
346 { 0, 0 },
347 { GDK_n, 0 }},
349 {KEY_FINC_CTRM, "finc_compounding_term", M_NONE,
350 { 0, 0 },
351 { GDK_m, 0 }},
353 {KEY_FINC_DDB, "finc_double_declining_depreciation", M_NONE,
354 { GDK_SHIFT_MASK, 0 },
355 { GDK_D, 0 }},
357 {KEY_FINC_FV, "finc_future_value", M_NONE,
358 { 0, 0 },
359 { GDK_v, 0 }},
361 {KEY_FINC_PMT, "finc_periodic_payment", M_NONE,
362 { GDK_SHIFT_MASK, 0 },
363 { GDK_P, 0 }},
365 {KEY_FINC_PV, "finc_present_value", M_NONE,
366 { 0, 0 },
367 { GDK_p, 0 }},
369 {KEY_FINC_RATE, "finc_periodic_interest_rate", M_NONE,
370 { GDK_SHIFT_MASK, 0 },
371 { GDK_T, 0 }},
373 {KEY_FINC_SLN, "finc_straight_line_depreciation", M_NONE,
374 { 0, 0 },
375 { GDK_l, 0 }},
377 {KEY_FINC_SYD, "finc_sum_of_the_years_digits_depreciation", M_NONE,
378 { 0, 0 },
379 { GDK_Y, 0 }},
381 {KEY_FINC_TERM, "finc_term", M_NONE,
382 { 0, 0 },
383 { GDK_T, 0 }},
385 #define NBUTTONS (sizeof(button_widgets) / sizeof(struct button_widget))
387 #define UI_FILE PACKAGE_GLADE_DIR "/gcalctool.glade"
389 #define MAXBITS 64 /* Bit panel: number of bit fields. */
391 #define GET_WIDGET(name) \
392 glade_xml_get_widget(X->ui, (name))
394 #define SET_MENUBAR_ITEM_STATE(name, state) \
395 g_object_set_data(G_OBJECT(GET_WIDGET(name)), "sensitive", \
396 GINT_TO_POINTER(state));
398 #define CONNECT_SIGNAL(name) glade_xml_signal_connect(X->ui, #name, \
399 G_CALLBACK(name))
401 struct Xobject { /* Gtk+/Xlib graphics object. */
402 GdkAtom clipboard_atom;
403 GdkAtom primary_atom;
405 GladeXML *ui;
407 GtkWidget *kframe; /* Main window. */
409 GtkTreeModel *constants_model;
410 GtkWidget *con_dialog; /* Edit constants dialog. */
412 GtkTreeModel *functions_model;
413 GtkWidget *fun_dialog; /* Edit functions dialog. */
414 GtkWidget *menubar; // FIXME: Why is this needed?
416 GtkWidget *bit_panel;
417 GtkWidget *bits[MAXBITS]; /* The 0/1 labels in the bit panel. */
419 GtkWidget *status_image; /* Statusbar image */
420 GtkWidget *statusbar;
422 GtkWidget *undo; /* Undo menuitem */
423 GtkWidget *redo; /* Redo menuitem */
424 GtkWidget *copy; /* Copy menuitem */
425 GtkWidget *paste; /* Paste menuitem */
427 GtkWidget *aframe; /* ASCII window. */
428 GtkWidget *aframe_ch;
430 GtkWidget *display_item; /* Calculator display. */
431 GtkTextBuffer *display_buffer; /* Buffer used in display */
432 GtkWidget *scrolledwindow; /* Scrolled window for display_item. */
434 GtkWidget *rframe; /* Register window. */
435 GtkWidget *regs[MAXREGS]; /* Memory registers. */
437 GtkWidget *spframe; /* Set Precision window. */
438 GtkWidget *spframe_val;
439 GtkWidget *menus[MAXMENUS];
441 GtkWidget *buttons[NBUTTONS];
442 GtkWidget *digit_buttons[16];
443 GtkWidget *clear_buttons[2];
445 GtkWidget *bas_panel; /* Panel containing basic mode widgets. */
446 GtkWidget *adv_panel; /* Panel containing advanced mode widgets. */
447 GtkWidget *fin_panel; /* Panel containing financial mode widgets. */
448 GtkWidget *sci_panel; /* Panel containing scientific mode widgets. */
449 GtkWidget *mode_panel; /* Panel containing scientific mode widgets. */
451 /* Labels for popup menus */
452 GtkWidget *constant_menu_items[MAXCONFUN];
453 GtkWidget *function_menu_items[MAXCONFUN];
454 GtkWidget *memory_store_items[MAXREGS];
455 GtkWidget *memory_recall_items[MAXREGS];
456 GtkWidget *memory_exchange_items[MAXREGS];
458 /* Scientific mode widgets */
459 GtkWidget *hyp; /* Hyperbolic mode. */
460 GtkWidget *inv; /* Inverse mode. */
461 GtkWidget *base[MAXBASES]; /* Numeric base radio buttons. */
462 GtkWidget *disp[MAXDISPMODES]; /* Numeric display mode. */
463 GtkWidget *trig[MAXTRIGMODES]; /* Trigonometric mode. */
465 int mode; /* The new mode. */
466 int menuval; /* Index to button array at menu time. */
467 char *lnp; /* Localized numerical point (UTF8 format) */
468 struct button *mrec[MAXMENUS];
471 typedef struct Xobject *XVars;
473 enum {
474 COLUMN_NUMBER,
475 COLUMN_VALUE,
476 COLUMN_DESCRIPTION,
477 COLUMN_EDITABLE,
478 NUM_COLUMNS
481 static XVars X;
484 void
485 ui_set_accuracy(int accuracy)
487 GtkWidget *widget;
488 char text[MAXLINE];
489 char *desc, *current, *tooltip;
491 SNPRINTF(text, MAXLINE, _("Other (%d) ..."), accuracy);
492 widget = gtk_bin_get_child(GTK_BIN(GET_WIDGET("acc_item_other")));
493 gtk_label_set_text(GTK_LABEL(widget), text);
495 desc = g_strdup_printf(ngettext("Set accuracy from 0 to %d numeric places.",
496 "Set accuracy from 0 to %d numeric places.",
497 MAXACC),
498 MAXACC);
500 /* Translator: This refers to the current accuracy setting */
501 current = g_strdup_printf(ngettext("Currently set to %d places.",
502 "Currently set to %d places.",
503 accuracy),
504 accuracy);
505 tooltip = g_strdup_printf ("%s %s [A]", desc, current);
506 gtk_widget_set_tooltip_text (GET_WIDGET("calc_accuracy_button"), tooltip);
507 g_free(desc);
508 g_free(current);
509 g_free(tooltip);
511 if (accuracy >= 0 && accuracy <= 9) {
512 SNPRINTF(text, MAXLINE, "acc_item%d", accuracy);
513 widget = GET_WIDGET(text);
514 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
519 static void
520 ui_update_trig_mode()
522 static char *sine_labels[] = {N_("Sin"), N_("Sinh"),
523 N_("Sin<sup>-1</sup>"),
524 N_("Sinh<sup>-1</sup>")};
525 static int sine_functions[] = {KEY_SIN, KEY_SINH, KEY_ASIN, KEY_ASINH};
526 static char *cosine_labels[] = {N_("Cos"), N_("Cosh"),
527 N_("Cos<sup>-1</sup>"),
528 N_("Cosh<sup>-1</sup>")};
529 static int cosine_functions[] = {KEY_COS, KEY_COSH, KEY_ACOS, KEY_ACOSH};
530 static char *tangent_labels[] = {N_("Tan"), N_("Tanh"),
531 N_("Tan<sup>-1</sup>"),
532 N_("Tanh<sup>-1</sup>")};
533 static int tangent_functions[] = {KEY_TAN, KEY_TANH, KEY_ATAN, KEY_ATANH};
534 int index = 0;
536 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(X->hyp))) {
537 index |= 0x1;
539 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(X->inv))) {
540 index |= 0x2;
543 gtk_label_set_markup(GTK_LABEL(GET_WIDGET("sine_label")),
544 sine_labels[index]);
545 g_object_set_data(G_OBJECT(GET_WIDGET("calc_sine_button")), "button",
546 &buttons[sine_functions[index]]);
548 gtk_label_set_markup(GTK_LABEL(GET_WIDGET("cosine_label")),
549 cosine_labels[index]);
550 g_object_set_data(G_OBJECT(GET_WIDGET("calc_cosine_button")), "button",
551 &buttons[cosine_functions[index]]);
553 gtk_label_set_markup(GTK_LABEL(GET_WIDGET("tangent_label")),
554 tangent_labels[index]);
555 g_object_set_data(G_OBJECT(GET_WIDGET("calc_tangent_button")), "button",
556 &buttons[tangent_functions[index]]);
560 void
561 ui_set_hyperbolic_state(gboolean state)
563 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->hyp), state);
564 ui_update_trig_mode();
568 void
569 ui_set_inverse_state(gboolean state)
571 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->inv), state);
572 ui_update_trig_mode();
576 void
577 ui_set_trigonometric_mode(enum trig_type mode)
579 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->trig[mode]), 1);
583 void
584 ui_set_numeric_mode(enum base_type mode)
586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->disp[mode]), 1);
590 void
591 ui_set_undo_enabled(gboolean undo, gboolean redo)
593 gtk_widget_set_sensitive(X->undo, undo);
594 gtk_widget_set_sensitive(X->redo, redo);
598 static char *
599 make_hostname()
601 Display *dpy = GDK_DISPLAY();
602 char client_hostname[MAXHOSTNAMELEN + 4];
603 char hostname[MAXHOSTNAMELEN];
604 char *display = DisplayString(dpy);
605 char *scanner = display;
607 GETHOSTNAME(hostname, MAXHOSTNAMELEN);
609 while (*scanner) {
610 scanner++;
613 while (*scanner != ':') {
614 scanner--;
617 *scanner = '\0';
619 if (strcmp(display, hostname) &&
620 strcmp(display, "localhost") &&
621 strcmp(display, "unix") &&
622 strcmp(display, "")) {
623 SPRINTF(client_hostname, " [%s] ", hostname);
624 } else {
625 STRCPY(client_hostname, "");
628 *scanner = ':';
630 if (client_hostname[0] == '\0')
631 return NULL;
632 else
633 return(strdup(client_hostname));
637 void
638 ui_set_mode(enum mode_type mode)
640 GtkRequisition *r;
641 gint w, h;
642 char *hostname, title[MAXLINE];
643 GtkWidget *menu;
645 switch (mode) {
646 case BASIC:
647 gtk_widget_show(X->bas_panel);
648 gtk_widget_hide(X->adv_panel);
649 gtk_widget_hide(X->fin_panel);
650 gtk_widget_hide(X->mode_panel);
651 gtk_widget_hide(X->bit_panel);
652 gtk_widget_hide(X->sci_panel);
653 menu = GET_WIDGET("view_basic_menu");
654 break;
656 case ADVANCED:
657 gtk_widget_hide(X->bas_panel);
658 gtk_widget_show(X->adv_panel);
659 gtk_widget_hide(X->fin_panel);
660 gtk_widget_hide(X->mode_panel);
661 gtk_widget_hide(X->bit_panel);
662 gtk_widget_hide(X->sci_panel);
663 menu = GET_WIDGET("view_advanced_menu");
664 break;
666 case FINANCIAL:
667 gtk_widget_hide(X->bas_panel);
668 gtk_widget_show(X->adv_panel);
669 gtk_widget_show(X->fin_panel);
670 gtk_widget_hide(X->mode_panel);
671 gtk_widget_hide(X->bit_panel);
672 gtk_widget_hide(X->sci_panel);
673 menu = GET_WIDGET("view_financial_menu");
674 break;
676 case SCIENTIFIC:
677 gtk_widget_hide(X->bas_panel);
678 gtk_widget_show(X->adv_panel);
679 gtk_widget_hide(X->fin_panel);
680 gtk_widget_show_all(X->mode_panel);
681 if (v->bitcalculating_mode) {
682 gtk_widget_show_all(X->bit_panel);
683 } else {
684 gtk_widget_hide(X->bit_panel);
686 gtk_widget_show(X->sci_panel);
687 menu = GET_WIDGET("view_scientific_menu");
688 break;
690 default:
691 assert(FALSE);
692 return;
696 r = g_new0(GtkRequisition, 1);
697 gtk_widget_size_request(X->menubar, r);
698 w = r->width;
699 h = r->height;
700 gtk_widget_size_request(X->display_item, r);
701 w = MAX(w, r->width);
702 h += r->height;
704 if (GTK_WIDGET_VISIBLE(X->fin_panel)) {
705 gtk_widget_size_request(X->fin_panel, r);
706 w = MAX(w, r->width);
707 h += r->height;
710 if (GTK_WIDGET_VISIBLE(X->mode_panel)) {
711 gtk_widget_size_request(X->mode_panel, r);
712 w = MAX(w, r->width);
713 h += r->height;
716 if (GTK_WIDGET_VISIBLE(X->sci_panel)) {
717 gtk_widget_size_request(X->sci_panel, r);
718 w = MAX(w, r->width);
719 h += r->height;
722 /* For initial display. */
723 gtk_window_set_default_size(GTK_WINDOW(X->kframe), w, h);
724 gtk_window_resize(GTK_WINDOW(X->kframe), w, h);
726 g_free(r);
728 if((hostname = make_hostname())) {
729 SNPRINTF(title, MAXLINE, hostname_titles[mode], hostname);
730 g_free(hostname);
731 } else {
732 SNPRINTF(title, MAXLINE, titles[mode]);
734 gtk_window_set_title(GTK_WINDOW(X->kframe), title);
736 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
740 void
741 ui_set_statusbar(gchar *text, const gchar *imagename)
743 GtkImage *image = GTK_IMAGE(X->status_image);
745 assert(text);
746 assert(imagename);
747 assert(image);
749 gtk_image_set_from_stock(image, imagename, GTK_ICON_SIZE_BUTTON);
750 gtk_statusbar_pop(GTK_STATUSBAR(X->statusbar), 0);
751 gtk_statusbar_push(GTK_STATUSBAR(X->statusbar), 0, text);
755 static void
756 bin_str(int MP_value[MP_SIZE], char *str, int maxbits)
758 int i, MP0[MP_SIZE], MP1[MP_SIZE], MP2[MP_SIZE], MP3[MP_SIZE];
759 int neg = 0;
761 MPstr_to_num("0", DEC, MP0);
762 MPstr_to_num("1", DEC, MP1);
763 MPstr_to_num("2", DEC, MP2);
765 if (mplt(MP_value, MP0)) {
766 mpcmim(MP_value, MP0);
767 mpadd(MP0, MP1, MP0);
768 neg = 1;
769 } else {
770 mpcmim(MP_value, MP0);
773 for (i = 0; i < maxbits; i++) {
774 double lsb; /* Least significant bit. */
775 calc_and(MP3, MP0, MP1);
776 mpcmd(MP3, &lsb);
778 if (lsb == 0) {
779 str[maxbits - i -1] = (neg) ? '1' : '0';
780 } else {
781 str[maxbits - i -1] = (neg) ? '0' : '1';
784 mpdiv(MP0, MP2, MP3);
785 mpcmim(MP3, MP0);
788 return;
792 static void
793 set_bit_panel()
795 int bit_str_len, i, MP1[MP_SIZE], MP2[MP_SIZE];
796 int MP[MP_SIZE];
797 char str[64], label[3];
798 int ret = usable_num(MP);
800 switch (v->syntax) {
801 case NPA:
802 MPstr_to_num(v->display, v->base, MP1);
803 mpcmim(MP1, MP2);
804 if (mpeq(MP1, MP2)) {
805 char *bit_str, label[3], tmp[MAXLINE];
806 int toclear = (v->current == KEY_CLEAR_ENTRY)
807 ? TRUE : FALSE;
809 bit_str = make_fixed(MP1, tmp, BIN, MAXLINE, toclear);
810 bit_str_len = strlen(bit_str);
811 if (bit_str_len <= MAXBITS) {
812 gtk_widget_set_sensitive(X->bit_panel, TRUE);
814 STRCPY(label, " 0");
815 for (i = 0; i < MAXBITS; i++) {
816 label[1] = (i < bit_str_len) ? bit_str[bit_str_len-i-1] : '0';
817 gtk_label_set_text(GTK_LABEL(X->bits[MAXBITS - i - 1]), label);
820 return;
823 gtk_widget_set_sensitive(X->bit_panel, FALSE);
824 break;
826 case EXPRS:
827 if (ret || !is_integer(MP)) {
828 gtk_widget_set_sensitive(X->bit_panel, FALSE);
829 return;
831 bin_str(MP, str, 64);
832 gtk_widget_set_sensitive(X->bit_panel, TRUE);
834 STRCPY(label, " 0");
835 for (i = 0; i < 64; i++) {
836 label[1] = str[64 - i - 1];
837 gtk_label_set_text(GTK_LABEL(X->bits[64 - i - 1]), label);
839 break;
841 default:
842 assert(FALSE);
847 static void
848 scroll_right()
850 if (!v->started) {
851 return;
854 if (GTK_WIDGET_VISIBLE(
855 GTK_SCROLLED_WINDOW(X->scrolledwindow)->hscrollbar)) {
856 GtkAdjustment *set;
858 set = gtk_scrolled_window_get_hadjustment(
859 GTK_SCROLLED_WINDOW(X->scrolledwindow));
860 gtk_adjustment_set_value(set, set->upper);
861 gtk_scrolled_window_set_hadjustment(
862 GTK_SCROLLED_WINDOW(X->scrolledwindow), set);
867 void
868 ui_set_display(char *str, gboolean minimize_changes)
870 char localized[MAX_LOCALIZED];
871 GtkTextIter start, end;
872 gchar *text;
873 gint diff;
874 gint len1, len2;
875 gboolean done;
877 if (str == NULL || str[0] == '\0') {
878 str = " ";
879 } else {
880 if (v->noparens == 0) {
881 localize_number(localized, str);
882 str = localized;
885 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
886 text = gtk_text_buffer_get_text(X->display_buffer, &start, &end, TRUE);
887 diff = strcmp (text, str);
888 if (diff != 0) {
889 len1 = strlen(text);
890 len2 = strlen(str);
892 done = FALSE;
893 if (minimize_changes) {
894 if (len1 < len2 && strncmp(text, str, len1) == 0) {
895 /* Text insertion */
896 gtk_text_buffer_insert(X->display_buffer, &end, str + len1, -1);
897 done = TRUE;
898 } else if (len1 > len2 && strncmp(text, str, len2) == 0) {
899 /* Text deletion */
900 gtk_text_buffer_get_iter_at_offset (X->display_buffer, &start, len2);
901 gtk_text_buffer_delete(X->display_buffer, &start, &end);
902 done = TRUE;
906 if (!done) {
907 gtk_text_buffer_delete(X->display_buffer, &start, &end);
908 gtk_text_buffer_insert(X->display_buffer, &end, str, -1);
911 scroll_right();
912 g_free(text);
916 /* When an error condition occurs:
918 * - make insensitive all buttons except Clr.
919 * - make all Scientific mode toggles and checkboxes insensitive.
920 * - make all menubar items insensitive except:
921 * Calculator->Quit
922 * Help->Contents
924 * When the error condition is cleared, resensitise everything, setting
925 * the numeric base buttons correctly.
928 void
929 ui_set_error_state(gboolean error)
931 int i;
933 v->error = error;
935 for (i = 0; i < NBUTTONS; i++) {
936 gtk_widget_set_sensitive(X->buttons[i], !v->error);
938 /* Clr button always sensitive. */
939 gtk_widget_set_sensitive(X->clear_buttons[0], TRUE);
940 gtk_widget_set_sensitive(X->clear_buttons[1], TRUE);
942 if (!v->error) {
943 ui_set_base(v->base);
946 gtk_widget_set_sensitive(X->mode_panel, !v->error);
948 // FIXME: Isn't this missing a whole lot of widgets?
949 SET_MENUBAR_ITEM_STATE("copy_menu", !v->error);
950 SET_MENUBAR_ITEM_STATE("paste_menu", !v->error);
951 SET_MENUBAR_ITEM_STATE("insert_ascii_menu", !v->error);
952 SET_MENUBAR_ITEM_STATE("view_basic_menu", !v->error);
953 SET_MENUBAR_ITEM_STATE("view_advanced_menu", !v->error);
954 SET_MENUBAR_ITEM_STATE("view_financial_menu", !v->error);
955 SET_MENUBAR_ITEM_STATE("view_scientific_menu", !v->error);
956 SET_MENUBAR_ITEM_STATE("show_trailing_zeroes_menu",
957 !v->error && (v->modetype == SCIENTIFIC));
958 SET_MENUBAR_ITEM_STATE("show_thousands_separator_menu", !v->error);
959 SET_MENUBAR_ITEM_STATE("show_registers_menu", !v->error);
960 SET_MENUBAR_ITEM_STATE("arithmetic_precedence_menu", !v->error);
961 SET_MENUBAR_ITEM_STATE("about_menu", !v->error);
965 /*ARGSUSED*/
966 static void
967 about_cb(GtkWidget *widget)
969 const gchar *authors[] = {
970 "Rich Burridge <rich.burridge@sun.com>",
971 "Sami Pietila <sampie@ariana-dsl.utu.fi>",
972 "Robert Ancell <robert.ancell@gmail.com>",
973 NULL
975 const gchar *documenters[] = {
976 "Sun Microsystems",
977 NULL
979 const gchar *translator_credits = _("translator-credits");
981 const char *license[] = {
982 N_("Gcalctool is free software; you can redistribute it and/or modify\n"
983 "it under the terms of the GNU General Public License as published by\n"
984 "the Free Software Foundation; either version 2 of the License, or\n"
985 "(at your option) any later version.\n"),
986 N_("Gcalctool is distributed in the hope that it will be useful,\n"
987 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
988 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
989 "GNU General Public License for more details.\n"),
990 N_("You should have received a copy of the GNU General Public License\n"
991 "along with Gcalctool; if not, write to the Free Software Foundation, Inc.,\n"
992 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n")
995 char *license_trans = g_strconcat(_(license[0]), "\n",
996 _(license[1]), "\n",
997 _(license[2]), "\n",
998 NULL);
1000 gtk_show_about_dialog(GTK_WINDOW(X->kframe),
1001 "name",_("Gcalctool"),
1002 "version", VERSION,
1003 "copyright", _("\xc2\xa9 1986-2007 The Gcalctool authors"),
1004 "license", license_trans,
1005 "comments", _("Calculator with financial and scientific modes."),
1006 "authors", authors,
1007 "documenters", documenters,
1008 "translator_credits", translator_credits,
1009 "logo-icon-name", "gnome-calculator",
1010 NULL);
1014 static void
1015 cell_edited_cb(GtkCellRendererText *cell, const gchar *path_string,
1016 const gchar *new_text, gpointer data)
1018 GtkTreeModel *model = (GtkTreeModel *) data;
1019 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
1020 GtkTreeIter iter;
1021 gchar *old_text;
1022 gint *column;
1024 column = g_object_get_data(G_OBJECT(cell), "column");
1026 gtk_tree_model_get_iter(model, &iter, path);
1028 switch (GPOINTER_TO_INT(column)) {
1029 case COLUMN_VALUE:
1030 gtk_tree_model_get(model, &iter, column, &old_text, -1);
1031 g_free(old_text);
1032 gtk_list_store_set(GTK_LIST_STORE(model), &iter, column,
1033 g_strdup(new_text), -1);
1034 break;
1036 case COLUMN_DESCRIPTION:
1037 gtk_tree_model_get(model, &iter, column, &old_text, -1);
1038 g_free(old_text);
1039 gtk_list_store_set(GTK_LIST_STORE(model), &iter, column,
1040 g_strdup(new_text), -1);
1041 break;
1044 gtk_tree_path_free(path);
1048 static void
1049 add_cf_column(GtkTreeView *treeview, gchar *name, gint colno, gboolean editable)
1051 GtkCellRenderer *renderer;
1052 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
1054 renderer = gtk_cell_renderer_text_new();
1055 if (editable) {
1056 g_signal_connect(G_OBJECT(renderer), "edited",
1057 G_CALLBACK(cell_edited_cb), model);
1059 g_object_set_data(G_OBJECT(renderer), "column", GINT_TO_POINTER(colno));
1061 if (editable) {
1062 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
1063 -1, name, renderer,
1064 "text", colno,
1065 "editable", COLUMN_EDITABLE,
1066 NULL);
1067 } else {
1068 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
1069 -1, name, renderer,
1070 "text", colno,
1071 NULL);
1076 /*ARGSUSED*/
1077 static void
1078 aframe_cancel_cb(GtkButton *button, gpointer user_data)
1080 gtk_widget_hide(X->aframe);
1084 /*ARGSUSED*/
1085 static gboolean
1086 aframe_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1088 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
1090 if (event->keyval == GDK_Escape) {
1091 gtk_widget_hide(X->aframe);
1094 return(FALSE);
1098 /*ARGSUSED*/
1099 static void
1100 aframe_ok_cb(GtkButton *button, gpointer user_data)
1102 char *ch;
1103 int val;
1105 ch = (char *) gtk_entry_get_text(GTK_ENTRY(X->aframe_ch));
1106 val = ch[0];
1107 mpcim(&val, v->MPdisp_val);
1108 show_display(v->MPdisp_val);
1109 gtk_widget_hide(X->aframe);
1113 /*ARGSUSED*/
1114 void
1115 base_cb(GtkWidget *widget)
1117 enum base_type base;
1119 base = (enum base_type) g_object_get_data(G_OBJECT(widget),
1120 "base_mode");
1121 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1122 do_base(base);
1127 void
1128 ui_beep()
1130 gdk_beep();
1133 static void do_button(struct button *n, int arg)
1135 struct exprm_state *e;
1137 switch (v->syntax) {
1138 case NPA:
1139 process_item(n, arg);
1140 set_bit_panel();
1141 if (v->new_input && v->dtype == FIX) {
1142 STRNCPY(v->fnum, v->display, MAX_DIGITS - 1);
1143 ui_set_display(v->fnum, TRUE);
1145 break;
1147 case EXPRS:
1148 e = get_state();
1149 e->value = arg;
1150 MEMCPY(&(e->button), n, sizeof(struct button));
1151 new_state();
1152 do_expression();
1153 set_bit_panel();
1154 break;
1156 default:
1157 assert(0);
1162 static void
1163 help_display(void)
1165 GError *error = NULL;
1166 char *command;
1167 const char *lang;
1168 char *uri = NULL;
1169 GdkScreen *gscreen;
1171 int i;
1173 const char * const * langs = g_get_language_names ();
1175 for (i = 0; langs[i]; i++) {
1176 lang = langs[i];
1177 if (strchr (lang, '.')) {
1178 continue;
1181 uri = g_build_filename(PACKAGE_DATA_DIR,
1182 "/gnome/help/gcalctool/",
1183 lang,
1184 "/gcalctool.xml",
1185 NULL);
1187 if (g_file_test (uri, G_FILE_TEST_EXISTS)) {
1188 break;
1192 command = g_strconcat ("gnome-open ghelp://", uri, NULL);
1193 gscreen = gdk_screen_get_default();
1194 gdk_spawn_command_line_on_screen (gscreen, command, &error);
1195 if (error) {
1196 GtkWidget *d;
1198 d = gtk_message_dialog_new(NULL,
1199 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1200 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1201 error->message);
1202 gtk_dialog_run(GTK_DIALOG(d));
1203 gtk_widget_destroy(d);
1204 g_error_free(error);
1205 error = NULL;
1208 g_free (command);
1209 g_free (uri);
1213 static void
1214 put_constant(int n, char *con_value, char *con_name)
1216 char key[MAXLINE];
1217 char *cstr = g_strdup(con_value);
1219 /* Constants are written out with no thousands seaparator and with a radix
1220 * character of ".".
1223 SNPRINTF(key, MAXLINE, "constant%1dvalue", n);
1224 set_resource(key, cstr);
1225 g_free(cstr);
1227 SNPRINTF(key, MAXLINE, "constant%1dname", n);
1228 set_resource(key, con_name);
1232 static void
1233 put_function(int n, char *fun_value, char *fun_name)
1235 char key[MAXLINE];
1237 SNPRINTF(key, MAXLINE, "function%1dvalue", n);
1238 set_resource(key, fun_value);
1240 SNPRINTF(key, MAXLINE, "function%1dname", n);
1241 set_resource(key, fun_name);
1245 /*ARGSUSED*/
1246 static void
1247 menu_proc_cb(GtkMenuItem *mi, gpointer user_data)
1249 struct button *n = (struct button *) g_object_get_data(G_OBJECT(mi), "button");
1250 do_button(n, GPOINTER_TO_INT(user_data));
1254 /* Create popup window for editing constants/functions. */
1256 static void
1257 update_popup_label(GtkWidget *menu_item, gchar *text)
1259 GtkWidget *label;
1260 label = (GtkWidget *)gtk_container_get_children(GTK_CONTAINER(menu_item))->data;
1261 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text);
1264 static void
1265 update_constants_menu(void)
1267 char mline[MAXLINE];
1268 int i;
1270 for (i = 0; i < MAXCONFUN; i++) {
1271 SNPRINTF(mline, MAXLINE,
1272 "<span weight=\"bold\">%s_%1d:</span> %s [%s]", _("C"), i,
1273 make_number(v->MPcon_vals[i], DEC, TRUE),
1274 v->con_names[i]);
1275 update_popup_label(X->constant_menu_items[i], mline);
1280 static void
1281 update_functions_menu(void)
1283 char mline[MAXLINE];
1284 int i;
1286 for (i = 0; i < MAXCONFUN; i++) {
1287 if (strlen(v->fun_vals[i]) != 0) {
1288 SNPRINTF(mline, MAXLINE,
1289 "<span weight=\"bold\">%s_%1d:</span> %s [%s]",
1290 _("F"), i, v->fun_vals[i], v->fun_names[i]);
1291 gtk_widget_show(X->function_menu_items[i]);
1292 update_popup_label(X->function_menu_items[i], mline);
1294 else
1295 gtk_widget_hide(X->function_menu_items[i]);
1300 static void
1301 edit_constants_response_cb(GtkDialog *dialog, gint id)
1303 GtkTreeIter iter;
1304 gint number;
1305 gchar *value;
1306 gchar *description;
1308 if (id == GTK_RESPONSE_HELP) {
1309 help_display();
1312 if (id == GTK_RESPONSE_ACCEPT) {
1313 if (gtk_tree_model_get_iter_first(X->constants_model, &iter)) {
1314 do {
1315 gtk_tree_model_get(X->constants_model, &iter,
1316 COLUMN_NUMBER, &number,
1317 COLUMN_VALUE, &value,
1318 COLUMN_DESCRIPTION, &description, -1);
1319 MPstr_to_num(value, DEC, v->MPcon_vals[number]);
1320 STRNCPY(v->con_names[number], description, MAXLINE - 1);
1321 put_constant(number, value, description);
1322 } while (gtk_tree_model_iter_next(X->constants_model, &iter));
1326 gtk_widget_hide(GTK_WIDGET(dialog));
1330 static void
1331 edit_functions_response_cb(GtkDialog *dialog, gint id)
1333 GtkTreeIter iter;
1334 gint number;
1335 gchar *value;
1336 gchar *description;
1338 if (id == GTK_RESPONSE_HELP) {
1339 help_display();
1342 if (id == GTK_RESPONSE_ACCEPT) {
1343 if (gtk_tree_model_get_iter_first(X->functions_model, &iter)) {
1344 do {
1345 gtk_tree_model_get(X->functions_model, &iter,
1346 COLUMN_NUMBER, &number,
1347 COLUMN_VALUE, &value,
1348 COLUMN_DESCRIPTION, &description, -1);
1349 STRNCPY(v->fun_vals[number], convert(value), MAXLINE - 1);
1350 STRNCPY(v->fun_names[number], description, MAXLINE - 1);
1351 put_function(number, value, description);
1352 } while (gtk_tree_model_iter_next(X->functions_model, &iter));
1356 gtk_widget_hide(GTK_WIDGET(dialog));
1360 /*ARGSUSED*/
1361 static GtkTreeModel *
1362 create_cf_model(enum menu_type mtype, GtkWidget *dialog)
1364 gint i = 0;
1365 GtkListStore *model;
1366 GtkTreeIter iter;
1367 gchar *value, *description;
1369 model = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
1370 G_TYPE_STRING, G_TYPE_BOOLEAN);
1371 for (i = 0; i < MAXCONFUN; i++) {
1372 gtk_list_store_append(model, &iter);
1374 if (mtype == M_CON) {
1375 value = g_strdup(make_number(v->MPcon_vals[i], DEC, TRUE));
1376 description = g_strdup(v->con_names[i]);
1377 } else {
1378 value = g_strdup(v->fun_vals[i]);
1379 description = g_strdup(v->fun_names[i]);
1381 gtk_list_store_set(model, &iter,
1382 COLUMN_NUMBER, i,
1383 COLUMN_VALUE, value,
1384 COLUMN_DESCRIPTION, description,
1385 COLUMN_EDITABLE, TRUE,
1386 -1);
1389 return(GTK_TREE_MODEL(model));
1393 static void
1394 set_show_tsep_toggle(int state)
1396 GtkWidget *mi;
1398 mi = GET_WIDGET("show_thousands_separator_menu");
1399 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), state);
1403 static void
1404 set_show_bitcalculating_toggle(int state)
1406 GtkWidget *mi;
1408 mi = GET_WIDGET("show_bitcalculating_menu");
1409 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), state);
1413 static void
1414 set_show_zeroes_toggle(int state)
1416 GtkWidget *menu;
1418 menu = GET_WIDGET("show_trailing_zeroes_menu");
1419 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), state);
1420 menu = GET_WIDGET("acc_trailing_zeroes_item");
1421 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), state);
1425 void
1426 ui_make_registers() /* Calculate memory register frame values. */
1428 char *mval, key[MAXLINE];
1429 int n;
1431 for (n = 0; n < MAXREGS; n++) {
1432 mval = make_number(v->MPmvals[n], v->base, TRUE);
1433 gtk_entry_set_width_chars(GTK_ENTRY(X->regs[n]), strlen(mval));
1434 gtk_entry_set_text(GTK_ENTRY(X->regs[n]), mval);
1435 SNPRINTF(key, MAXLINE, "register%d", n);
1436 set_resource(key, mval);
1441 static void
1442 reset_mode_display(void)
1444 switch (v->syntax) {
1445 case NPA:
1446 show_display(v->MPdisp_val);
1447 break;
1449 case EXPRS:
1450 refresh_display();
1451 break;
1453 default:
1454 assert(FALSE);
1456 ui_make_registers();
1460 static void
1461 save_win_position()
1463 int x, y;
1465 (void) gdk_window_get_origin(X->kframe->window, &x, &y);
1466 set_int_resource(R_XPOS, x);
1467 set_int_resource(R_YPOS, y);
1471 static void
1472 change_mode(int mode)
1474 X->mode = mode;
1475 v->modetype = mode;
1476 v->base = DEC;
1477 ui_set_base(v->base);
1478 ui_set_numeric_mode(FIX);
1479 v->accuracy = 9;
1480 ui_set_accuracy(v->accuracy);
1482 v->show_tsep = FALSE;
1483 set_show_tsep_toggle(v->show_tsep);
1484 set_boolean_resource(R_TSEP, v->show_tsep == TRUE);
1486 v->show_zeroes = FALSE;
1487 set_show_zeroes_toggle(v->show_zeroes);
1488 set_boolean_resource(R_ZEROES, v->show_zeroes == TRUE);
1490 reset_mode_display();
1491 do_mode(TRUE);
1495 static gboolean
1496 request_change_mode()
1498 GtkWidget *dialog, *request_check, *button;
1499 gint response;
1501 if (!v->warn_change_mode)
1502 return TRUE;
1504 dialog = gtk_message_dialog_new(GTK_WINDOW(X->kframe),
1505 GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1506 GTK_MESSAGE_WARNING,
1507 GTK_BUTTONS_CANCEL,
1508 "%s",
1509 _("Changing Modes Clears Calculation"));
1510 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1511 "%s",
1512 _("When you change modes, the current calculation "
1513 "will be cleared, and the base will be reset to "
1514 "decimal."));
1516 request_check = gtk_check_button_new_with_mnemonic(_("_Do not warn me again"));
1517 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1518 request_check, FALSE, FALSE, 0);
1520 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
1521 _("C_hange Mode"), GTK_RESPONSE_ACCEPT);
1522 gtk_button_set_image(GTK_BUTTON(button),
1523 gtk_image_new_from_stock(GTK_STOCK_REFRESH,
1524 GTK_ICON_SIZE_BUTTON));
1525 /* Set default focus on affirmative button */
1526 gtk_widget_grab_focus(button);
1528 gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
1529 GTK_RESPONSE_ACCEPT,
1530 GTK_RESPONSE_CANCEL,
1531 -1);
1533 gtk_window_set_position(GTK_WINDOW(dialog),
1534 GTK_WIN_POS_CENTER_ON_PARENT);
1536 gtk_widget_show_all(dialog);
1537 response = gtk_dialog_run(GTK_DIALOG(dialog));
1539 // FIXME: Save this in GConf
1540 v->warn_change_mode = !gtk_toggle_button_get_active(
1541 GTK_TOGGLE_BUTTON(request_check));
1543 gtk_widget_destroy(dialog);
1545 return response == GTK_RESPONSE_ACCEPT;
1549 /*ARGSUSED*/
1550 static gboolean
1551 bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event)
1553 double number;
1554 unsigned long long lval;
1555 int n, MP1[MP_SIZE], index;
1557 index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(event_box),
1558 "bit_index"));
1559 n = MAXBITS - index - 1;
1560 MPstr_to_num(v->display, v->base, MP1);
1561 mpcmd(MP1, &number);
1562 lval = (long long) number;
1564 if (lval & (1LL << n)) {
1565 lval &= ~(1LL << n);
1566 gtk_label_set_text(GTK_LABEL(X->bits[index]), " 0");
1567 } else {
1568 lval |= (1LL << n);
1569 gtk_label_set_text(GTK_LABEL(X->bits[index]), " 1");
1572 number = (double) lval;
1573 mpcdm(&number, v->MPdisp_val);
1574 show_display(v->MPdisp_val);
1575 v->toclear = 0;
1577 return(TRUE);
1581 static void
1582 menu_item_select_cb(GtkWidget *widget)
1584 GtkStatusbar *statusbar = GTK_STATUSBAR(X->statusbar);
1585 gchar *tooltip;
1586 guint context_id;
1588 context_id = gtk_statusbar_get_context_id(statusbar, "menuhelp");
1590 tooltip = (gchar *)g_object_get_data(G_OBJECT(widget), "tooltip");
1591 if (tooltip) {
1592 gtk_statusbar_push(statusbar, context_id, tooltip);
1597 /*ARGSUSED*/
1598 static void
1599 menu_item_deselect_cb(GtkWidget *widget)
1601 GtkStatusbar *statusbar = GTK_STATUSBAR(X->statusbar);
1602 guint context_id;
1604 context_id = gtk_statusbar_get_context_id(statusbar, "menuhelp");
1605 gtk_statusbar_pop(statusbar, context_id);
1609 static void
1610 set_menubar_tooltip(gchar *menu_name)
1612 GtkWidget *menu;
1613 gchar *tooltip;
1615 menu = GET_WIDGET(menu_name);
1616 tooltip = gtk_widget_get_tooltip_text(menu);
1617 g_object_set_data(G_OBJECT(menu), "tooltip", tooltip);
1618 gtk_widget_set_tooltip_text(menu, NULL);
1622 static void
1623 update_memory_menus()
1625 char mstr[MAXLINE];
1626 int i;
1628 for (i = 0; i < MAXREGS; i++) {
1629 SNPRINTF(mstr, MAXLINE, "<span weight=\"bold\">%s_%d:</span> %s",
1630 /* translators: R is the short form of register used inter alia
1631 in popup menus */
1632 _("R"), i, make_number(v->MPmvals[i], v->base, TRUE));
1633 update_popup_label(X->memory_store_items[i], mstr);
1634 update_popup_label(X->memory_recall_items[i], mstr);
1635 update_popup_label(X->memory_exchange_items[i], mstr);
1640 /*ARGSUSED*/
1641 static void
1642 rframe_response_cb(GtkDialog *dialog, int response)
1644 ui_set_registers_visible(FALSE);
1648 /*ARGSUSED*/
1649 static gboolean
1650 aframe_delete_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1652 gtk_widget_hide(widget);
1654 return(TRUE);
1658 /*ARGSUSED*/
1659 static gboolean
1660 rframe_delete_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1662 gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_DELETE_EVENT);
1664 return(TRUE);
1668 /*ARGSUSED*/
1669 static gboolean
1670 spframe_delete_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1672 gtk_widget_hide(X->spframe);
1674 return(TRUE);
1678 /*ARGSUSED*/
1679 void
1680 disp_cb(GtkWidget *widget)
1682 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1683 do_numtype((enum num_type) g_object_get_data(G_OBJECT(widget), "numeric_mode"));
1687 static void
1688 get_constant(int n)
1690 char nkey[MAXLINE], *nline;
1691 char vkey[MAXLINE], *vline;
1693 SNPRINTF(nkey, MAXLINE, "constant%1dname", n);
1694 if ((nline = get_resource(nkey)) == NULL) {
1695 return;
1698 SNPRINTF(vkey, MAXLINE, "constant%1dvalue", n);
1699 if ((vline = get_resource(vkey)) == NULL) {
1700 return;
1703 MPstr_to_num(vline, DEC, v->MPcon_vals[n]);
1704 STRNCPY(v->con_names[n], nline, MAXLINE - 1);
1708 static void
1709 get_display() /* The Copy function key has been pressed. */
1711 gchar *string = NULL;
1712 GtkTextIter start, end;
1714 if (gtk_text_buffer_get_selection_bounds(X->display_buffer, &start, &end) == TRUE) {
1715 string = gtk_text_buffer_get_text(X->display_buffer, &start, &end, FALSE);
1716 } else {
1717 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
1718 string = gtk_text_buffer_get_text(X->display_buffer, &start, &end, FALSE);
1721 if (v->shelf != NULL) {
1722 free(v->shelf);
1724 v->shelf = g_locale_from_utf8(string, strlen(string), NULL, NULL, NULL);
1725 g_free(string);
1727 gtk_clipboard_set_text(gtk_clipboard_get(X->clipboard_atom), v->shelf, -1);
1731 static void
1732 get_function(int n)
1734 char nkey[MAXLINE], *nline;
1735 char vkey[MAXLINE], *vline;
1737 SNPRINTF(nkey, MAXLINE, "function%1dname", n);
1738 if ((nline = get_resource(nkey)) == NULL) {
1739 return;
1742 SNPRINTF(vkey, MAXLINE, "function%1dvalue", n);
1743 if ((vline = get_resource(vkey)) == NULL) {
1744 return;
1747 STRNCPY(v->fun_vals[n], convert(vline), MAXLINE - 1);
1748 STRNCPY(v->fun_names[n], nline, MAXLINE - 1);
1752 char *
1753 ui_get_localized_numeric_point(void)
1755 const char *decimal_point;
1757 decimal_point = localeconv()->decimal_point;
1759 return(g_locale_to_utf8(decimal_point, -1, NULL, NULL, NULL));
1764 get_menu_entry(enum menu_type mtype, int offset)
1766 switch (mtype) {
1767 case M_ACC :
1768 return(offset + '0');
1770 case M_LSHF :
1771 case M_RSHF :
1772 return((offset < 10) ? offset + '0' : offset + 'A' - 10);
1774 default:
1775 FPRINTF(stderr, "need to handle menu type %d\n", mtype);
1778 return(0);
1782 /*ARGSUSED*/
1783 static void
1784 get_proc(GtkClipboard *clipboard, const gchar *buffer, gpointer data)
1786 int ret;
1787 gchar *dstp, *end_buffer, *srcp, *text;
1789 if (buffer == NULL) {
1790 return;
1793 end_buffer = (gchar *) (buffer + strlen(buffer));
1794 text = malloc(strlen(buffer)+1);
1796 srcp = (gchar *) buffer;
1797 dstp = text;
1798 while (srcp < end_buffer) {
1800 /* If the clipboard buffer contains any occurances of the "thousands
1801 * separator", remove them.
1803 if (*srcp == v->tsep[0]) {
1804 if (strstr(srcp, v->tsep) == srcp) {
1805 srcp += strlen(v->tsep);
1806 } else {
1807 *dstp++ = *srcp++;
1809 } else {
1811 /* If an "A", "B", "C", "D" or "F" character is encountered, it
1812 * will be converted to its lowercase equivalent. If an "E" is
1813 * found, and the next character is a "-" or a "+", then it
1814 * remains as an upper case "E" (it's assumed to be a possible
1815 * exponential number), otherwise its converted to a lower case
1816 * "e". See bugs #455889 and #469245 for more details.
1818 switch (*srcp) {
1819 case 'A':
1820 case 'B':
1821 case 'C':
1822 case 'D':
1823 case 'F': *dstp++ = tolower(*srcp);
1824 srcp++;
1825 break;
1827 case 'E': if (srcp < (end_buffer-1)) {
1828 if (*(srcp+1) != '-' &&
1829 *(srcp+1) != '+') {
1830 *dstp++ = tolower(*srcp);
1831 srcp++;
1832 break;
1835 /*FALLTHROUGH*/
1837 default: *dstp++ = *srcp++;
1841 *dstp++ = '\0';
1843 switch (v->syntax) {
1844 case NPA:
1845 ret = lr_parse((char *) text, v->MPdisp_val);
1846 if (!ret) {
1847 show_display(v->MPdisp_val);
1848 } else {
1849 ui_set_statusbar(_("Clipboard contained malformed calculation"),
1850 "gtk-dialog-error");
1852 break;
1854 case EXPRS:
1855 exp_append((char *) text);
1856 refresh_display();
1857 break;
1859 default:
1860 assert(0);
1862 free(text);
1866 void
1867 ui_set_base(enum base_type base)
1869 int i, baseval = basevals[(int) base];
1871 for (i = 0; i < 16; i++) {
1872 gtk_widget_set_sensitive(X->digit_buttons[i], i < baseval);
1874 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->base[base]), 1);
1878 void
1879 ui_set_registers_visible(gboolean visible)
1881 GtkWidget *menu;
1882 ui_make_registers();
1884 menu = GET_WIDGET("show_registers_menu");
1885 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), visible);
1887 gtk_widget_realize(X->rframe);
1889 if (visible) {
1890 if (gdk_window_is_visible(X->rframe->window)) {
1891 gdk_window_raise(X->rframe->window);
1892 return;
1894 ds_position_popup(X->kframe, X->rframe, DS_POPUP_ABOVE);
1895 gtk_widget_show(X->rframe);
1896 } else {
1897 gtk_widget_hide(X->rframe);
1900 set_boolean_resource(R_REGS, visible);
1904 /*ARGSUSED*/
1905 static void
1906 help_cb(GtkWidget *widget)
1908 if (v->started)
1909 help_display();
1913 /*ARGSUSED*/
1914 void
1915 hyp_cb(GtkWidget *widget)
1917 ui_update_trig_mode();
1921 /*ARGSUSED*/
1922 void
1923 inv_cb(GtkWidget *widget)
1925 ui_update_trig_mode();
1929 static gboolean
1930 check_for_localized_numeric_point(int keyval)
1932 gchar outbuf[10]; /* Minumum size 6. */
1933 gunichar ch;
1935 ch = gdk_keyval_to_unicode(keyval);
1936 g_return_val_if_fail(g_unichar_validate(ch), FALSE);
1938 outbuf[g_unichar_to_utf8(ch, outbuf)] = '\0';
1940 return(strcmp(outbuf, X->lnp) == 0);
1944 static void
1945 ui_parse_display()
1947 char *text;
1948 GtkTextIter start, end;
1950 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
1952 text = gtk_text_buffer_get_text(X->display_buffer,
1953 &start,
1954 &end,
1955 FALSE);
1956 exp_replace(text);
1961 ui_get_cursor(void)
1963 gint pos;
1964 g_object_get(G_OBJECT(X->display_buffer), "cursor-position", &pos, NULL);
1965 return pos;
1969 /*ARGSUSED*/
1970 static gboolean
1971 display_focus_out_cb(GtkWidget *widget, GdkEventKey *event)
1973 if (v->syntax == EXPRS) {
1974 ui_parse_display();
1977 return(FALSE);
1981 /*ARGSUSED*/
1982 static gboolean
1983 display_focus_in_cb(GtkWidget *widget, GdkEventKey *event)
1985 v->ghost_zero = 0;
1987 return(FALSE);
1991 /*ARGSUSED*/
1992 static void
1993 menu_pos_func(GtkMenu *menu, gint *x, gint *y,
1994 gboolean *push_in, gpointer user_data)
1996 GdkPoint *loc = (GdkPoint *) user_data;
1998 *x = loc->x;
1999 *y = loc->y;
2003 /*ARGSUSED*/
2004 static void
2005 button_cb(GtkWidget *widget, GdkEventButton *event)
2007 struct button *n;
2008 enum menu_type mtype;
2009 GtkWidget *menu;
2010 GdkPoint loc;
2012 n = (struct button *) g_object_get_data(G_OBJECT(widget), "button");
2013 mtype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "mtype"));
2015 if (mtype == M_NONE) {
2016 do_button(n, 0);
2017 } else {
2018 /* If gcalctool is being driven by gok, the on-screen keyboard
2019 * assistive technology, it's possible that the event returned by
2020 * gtk_get_current_event() is NULL. If this is the case, we need
2021 * to fudge the popping up on the menu associated with this menu
2022 * button.
2025 update_constants_menu();
2026 update_functions_menu();
2027 update_memory_menus();
2029 menu = X->menus[mtype];
2030 if (event == NULL) {
2031 gdk_window_get_origin(widget->window, &loc.x, &loc.y);
2032 loc.x += widget->allocation.x;
2033 loc.y += widget->allocation.y;
2034 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, menu_pos_func,
2035 (gpointer) &loc, 0, gtk_get_current_event_time());
2036 } else if (event->button == 1) {
2037 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
2038 event->button, event->time);
2044 /*ARGSUSED*/
2045 static void
2046 select_display_entry(int offset)
2048 GtkTextIter iter;
2050 gtk_text_buffer_get_iter_at_offset(X->display_buffer, &iter, offset);
2051 gtk_text_buffer_place_cursor(X->display_buffer, &iter);
2052 gtk_widget_grab_focus(X->display_item);
2056 /*ARGSUSED*/
2057 static gboolean
2058 kframe_key_press_cb(GtkWidget *widget, GdkEventKey *event)
2060 int i, j, state;
2061 GtkWidget *button;
2063 if (check_for_localized_numeric_point(event->keyval) == TRUE) {
2064 event->state = 0;
2065 event->keyval = GDK_KP_Decimal;
2068 state = event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK);
2070 /* Accuracy shortcuts */
2071 if (state == GDK_CONTROL_MASK && v->modetype == SCIENTIFIC)
2073 switch (event->keyval) {
2074 case GDK_0:
2075 do_accuracy(0);
2076 return (TRUE);
2077 case GDK_1:
2078 do_accuracy(1);
2079 return (TRUE);
2080 case GDK_2:
2081 do_accuracy(2);
2082 return (TRUE);
2083 case GDK_3:
2084 do_accuracy(3);
2085 return (TRUE);
2086 case GDK_4:
2087 do_accuracy(4);
2088 return (TRUE);
2089 case GDK_5:
2090 do_accuracy(5);
2091 return (TRUE);
2092 case GDK_6:
2093 do_accuracy(6);
2094 return (TRUE);
2095 case GDK_7:
2096 do_accuracy(7);
2097 return (TRUE);
2098 case GDK_8:
2099 do_accuracy(8);
2100 return (TRUE);
2101 case GDK_9:
2102 do_accuracy(9);
2103 return (TRUE);
2107 /* Connect home and end keys to move into the display entry */
2108 if (event->keyval == GDK_Home)
2110 select_display_entry(0);
2111 return (TRUE);
2112 } else if (event->keyval == GDK_End)
2114 select_display_entry(-1);
2115 return (TRUE);
2118 for (i = 0; i < NBUTTONS; i++) {
2119 button = X->buttons[i];
2121 /* Check any parent widgets are visible */
2122 if (!GTK_WIDGET_VISIBLE(gtk_widget_get_parent(button)) ||
2123 !GTK_WIDGET_VISIBLE(button) || !GTK_WIDGET_IS_SENSITIVE(button)) {
2124 continue;
2127 j = 0;
2128 while (button_widgets[i].accelerator_keys[j] != 0) {
2129 if (button_widgets[i].accelerator_keys[j] == event->keyval &&
2130 (button_widgets[i].accelerator_mods[j] & ~GDK_SHIFT_MASK) == state) {
2131 button_cb(button, NULL);
2132 return(TRUE);
2134 j++;
2138 return(FALSE);
2142 /*ARGSUSED*/
2143 static gboolean
2144 mouse_button_cb(GtkWidget *widget, GdkEventButton *event)
2146 if (event->button == 2) {
2147 gtk_clipboard_request_text(gtk_clipboard_get(X->primary_atom),
2148 get_proc, NULL);
2151 return(FALSE);
2155 static void
2156 set_win_position()
2158 int intval, screen_height, screen_width;
2159 int x = 0, y = 0;
2161 screen_width = gdk_screen_get_width(gdk_screen_get_default());
2162 screen_height = gdk_screen_get_height(gdk_screen_get_default());
2164 if (get_int_resource(R_XPOS, &intval)) {
2165 x = intval;
2166 if (x < 0 || x > screen_width) {
2167 x = 0;
2171 if (get_int_resource(R_YPOS, &intval)) {
2172 y = intval;
2173 if (y < 0 || y > screen_height) {
2174 y = 0;
2178 gtk_window_move(GTK_WINDOW(X->kframe), x, y);
2181 static void
2182 show_ascii_frame() /* Display ASCII popup. */
2184 if (!GTK_WIDGET_VISIBLE(X->aframe)) {
2185 ds_position_popup(X->kframe, X->aframe, DS_POPUP_LEFT);
2187 gtk_window_set_focus(GTK_WINDOW(X->kframe), GTK_WIDGET(X->aframe_ch));
2188 gtk_widget_show(X->aframe);
2192 static void
2193 show_precision_frame() /* Display Set Precision popup. */
2195 if (!GTK_WIDGET_VISIBLE(X->spframe)) {
2196 ds_position_popup(X->kframe, X->spframe, DS_POPUP_LEFT);
2198 gtk_window_set_focus(GTK_WINDOW(X->spframe), GTK_WIDGET(X->spframe_val));
2199 gtk_widget_show(X->spframe);
2203 /* Handle menu bar menu selection. */
2205 /*ARGSUSED*/
2206 static void
2207 edit_cb(GtkWidget *widget)
2209 gboolean can_paste;
2210 gboolean can_copy;
2212 if (!v->started) {
2213 return;
2216 can_copy = gtk_text_buffer_get_has_selection(X->display_buffer);
2217 can_paste = gtk_clipboard_wait_is_text_available(
2218 gtk_clipboard_get(X->clipboard_atom));
2220 gtk_widget_set_sensitive(GTK_WIDGET(X->copy), can_copy);
2221 gtk_widget_set_sensitive(GTK_WIDGET(X->paste), can_paste);
2225 static void
2226 handle_selection() /* Handle the GET function key being pressed. */
2228 gtk_clipboard_request_text(gtk_clipboard_get(X->clipboard_atom),
2229 get_proc, NULL);
2233 /*ARGSUSED*/
2234 static void
2235 popup_paste_cb(GtkMenuItem *menuitem)
2237 handle_selection();
2241 /*ARGSUSED*/
2242 static void
2243 copy_cb(GtkWidget *widget)
2245 if (v->started) {
2246 get_display();
2251 /*ARGSUSED*/
2252 static void
2253 paste_cb(GtkWidget *widget)
2255 if (v->started) {
2256 handle_selection();
2261 /*ARGSUSED*/
2262 static void
2263 undo_cb(GtkWidget *widget)
2265 if (v->started) {
2266 perform_undo();
2267 refresh_display();
2272 /*ARGSUSED*/
2273 static void
2274 redo_cb(GtkWidget *widget)
2276 if (v->started) {
2277 perform_redo();
2278 refresh_display();
2283 /*ARGSUSED*/
2284 static void
2285 for_each_menu(GtkWidget *widget, gpointer data)
2287 /* Find the "Paste" entry and activate it (see bug #317786). */
2288 if (strcmp(G_OBJECT_TYPE_NAME(widget), "GtkImageMenuItem") == 0) {
2289 GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget));
2291 if (strcmp(gtk_label_get_text(GTK_LABEL(label)), _("Paste")) == 0) {
2292 if (gtk_clipboard_wait_is_text_available(
2293 gtk_clipboard_get(X->clipboard_atom))) {
2294 gtk_widget_set_sensitive(GTK_WIDGET(widget), TRUE);
2295 g_signal_connect(GTK_OBJECT(widget), "activate",
2296 G_CALLBACK(popup_paste_cb), NULL);
2303 /*ARGSUSED*/
2304 static void
2305 buffer_populate_popup_cb(GtkTextView *textview, GtkMenu *menu)
2307 gtk_container_foreach(GTK_CONTAINER(menu), for_each_menu, NULL);
2311 /*ARGSUSED*/
2312 static void
2313 insert_ascii_cb(GtkWidget *widget)
2315 if (v->started) {
2316 show_ascii_frame();
2321 static void
2322 shift_left_cb(GtkWidget *widget)
2324 int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
2325 "shiftcount"));
2327 if (v->started) {
2328 do_button(&buttons[KEY_SHIFT], count);
2333 static void
2334 shift_right_cb(GtkWidget *widget)
2336 int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
2337 "shiftcount"));
2339 if (v->started) {
2340 do_button(&buttons[KEY_SHIFT], -count);
2344 /*ARGSUSED*/
2345 static void
2346 show_bitcalculating_cb(GtkWidget *widget)
2348 if (v->started) {
2349 v->bitcalculating_mode = v->bitcalculating_mode ^ 1;
2350 ui_set_mode(v->modetype);
2351 set_resource(R_BITCALC, Rcstr[v->bitcalculating_mode]);
2356 /*ARGSUSED*/
2357 static void
2358 show_registers_cb(GtkWidget *widget)
2360 gboolean visible;
2362 visible = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
2364 if (v->started) {
2365 set_boolean_resource(R_REGS, visible);
2366 ui_set_registers_visible(visible);
2371 /*ARGSUSED*/
2372 static void
2373 arithmetic_mode_cb(GtkWidget *widget)
2375 struct exprm_state *e;
2377 if (!v->started) {
2378 return;
2381 /* TODO: Always do clear things when mode is changed. */
2383 v->syntax = v->syntax ^ 1;
2384 switch (v->syntax) {
2385 case NPA:
2386 v->noparens = 0;
2387 MPstr_to_num("0", DEC, v->MPdisp_val);
2388 show_display(v->MPdisp_val);
2389 ui_set_statusbar(_("Activated no operator precedence mode"), "");
2390 clear_undo_history();
2391 break;
2393 case EXPRS:
2394 e = get_state();
2395 MPstr_to_num("0", DEC, e->ans);
2396 exp_del();
2397 show_display(e->ans);
2398 ui_set_statusbar(
2399 _("Activated expression mode with operator precedence"), "");
2400 break;
2402 default:
2403 assert(0);
2405 set_resource(R_SYNTAX, Rsstr[v->syntax]);
2406 ui_set_mode(v->modetype);
2407 // FIXME: We can't allow the display to be editable. See bug #326938
2408 gtk_text_view_set_editable(GTK_TEXT_VIEW(X->display_item),
2409 (v->syntax == EXPRS));
2413 /*ARGSUSED*/
2414 static void
2415 mode_radio_cb(GtkWidget *menu)
2417 struct exprm_state *e;
2418 int immediate = 0; /* Set if we can change mode without warning user. */
2419 int complete = 0; /* Set if the user has completed a calculation. */
2421 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) {
2422 return;
2425 if (!v->started) {
2426 return;
2429 X->mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu), "calcmode"));
2431 /* If the user has completed a calculation and we are going to a
2432 * new mode that is "compatible" with this one, then just change
2433 * modes. Otherwise display a dialog warning the user that the
2434 * current calculation will be cleared.
2436 * Incompatible modes are:
2438 * Scientific -> Basic
2439 * Scientific -> Advanced
2440 * Scientific -> Financial
2442 * (unless we are in Scientific mode with Decimal numeric base and Fixed).
2445 switch (v->syntax) {
2446 case NPA:
2447 if (v->old_cal_value < 0 ||
2448 v->old_cal_value == KEY_CALCULATE) {
2449 complete = 1; /* Calculation is complete. */
2451 break;
2453 case EXPRS:
2454 e = get_state();
2455 if (!e->expression || !strcmp(e->expression, "Ans")) {
2456 complete = 1; /* Calculation is complete. */
2458 break;
2460 default:
2461 assert(FALSE);
2464 if (complete) {
2465 if ((v->modetype != SCIENTIFIC) ||
2466 (v->dtype == FIX && v->base == DEC)) {
2467 immediate = 1;
2471 if (immediate) {
2472 v->modetype = X->mode;
2473 reset_mode_display();
2474 do_mode(FALSE);
2475 } else if (request_change_mode()) {
2476 change_mode(X->mode);
2481 /*ARGSUSED*/
2482 static void
2483 accuracy_radio_cb(GtkWidget *widget)
2485 int count;
2487 count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "accuracy"));
2489 if (v->started &&
2490 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
2491 do_accuracy(count);
2496 /*ARGSUSED*/
2497 static void
2498 accuracy_other_cb(GtkWidget *widget)
2500 if (v->started) {
2501 show_precision_frame();
2506 /*ARGSUSED*/
2507 static void
2508 show_trailing_zeroes_cb(GtkWidget *widget)
2510 v->show_zeroes = gtk_check_menu_item_get_active(
2511 GTK_CHECK_MENU_ITEM(widget));
2513 syntaxdep_show_display();
2514 set_boolean_resource(R_ZEROES, v->show_zeroes);
2515 ui_make_registers();
2517 set_show_zeroes_toggle(v->show_zeroes);
2521 /*ARGSUSED*/
2522 static void
2523 quit_cb(GtkWidget *widget)
2525 save_win_position();
2526 gtk_main_quit();
2530 /*ARGSUSED*/
2531 static void
2532 spframe_cancel_cb(GtkButton *button)
2534 gtk_widget_hide(X->spframe);
2538 /*ARGSUSED*/
2539 static gboolean
2540 spframe_key_cb(GtkWidget *widget, GdkEventKey *event)
2542 if (event->keyval == GDK_minus) {
2543 ui_set_statusbar(_("Accuracy value out of range"),
2544 "gtk-dialog-error");
2545 ui_beep();
2546 } else if (event->keyval == GDK_Escape) {
2547 gtk_widget_hide(X->spframe);
2550 return(FALSE);
2554 /*ARGSUSED*/
2555 static void
2556 spframe_ok_cb(GtkButton *button)
2558 int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(X->spframe_val));
2560 v->accuracy = val;
2562 set_int_resource(R_ACCURACY, v->accuracy);
2564 ui_set_accuracy(v->accuracy);
2566 ui_make_registers();
2567 refresh_display();
2569 gtk_widget_hide(X->spframe);
2573 /*ARGSUSED*/
2574 void
2575 trig_cb(GtkWidget *widget)
2577 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2578 do_trigtype((enum trig_type) g_object_get_data(G_OBJECT(widget),
2579 "trig_mode"));
2583 void
2584 ui_start()
2586 struct exprm_state *e;
2588 v->started = 1;
2589 ui_set_base(v->base);
2590 ui_set_trigonometric_mode(v->ttype);
2591 ui_set_numeric_mode(v->dtype);
2593 gtk_widget_show(X->kframe);
2595 switch (v->syntax) {
2596 case NPA:
2597 break;
2599 case EXPRS:
2600 /* Init expression mode.
2601 * This must be executed after do_base is called at init.
2602 * FIXME: The init code here is duplicated elsewhere.
2604 e = get_state();
2605 MPstr_to_num("0", DEC, e->ans);
2606 exp_del();
2607 show_display(e->ans);
2608 break;
2610 default:
2611 assert(0);
2614 gtk_main();
2618 /*ARGSUSED*/
2619 static void
2620 show_thousands_separator_cb(GtkWidget *widget)
2622 if (!v->started) {
2623 return;
2626 v->show_tsep = !v->show_tsep;
2628 syntaxdep_show_display();
2629 set_boolean_resource(R_TSEP, v->show_tsep == TRUE);
2630 ui_make_registers();
2634 /*ARGSUSED*/
2635 static void
2636 edit_constants_cb(GtkMenuItem *item)
2638 gtk_widget_show(X->con_dialog);
2642 /*ARGSUSED*/
2643 static void
2644 edit_functions_cb(GtkMenuItem *item)
2646 gtk_widget_show(X->fun_dialog);
2650 static void
2651 create_kframe()
2653 int i;
2654 char name[MAXLINE];
2655 GtkWidget *widget;
2656 PangoFontDescription *font_desc;
2657 GtkSizeGroup *size_group;
2658 GtkAccelGroup *accel_group;
2659 GtkWidget *treeview;
2661 X->ui = glade_xml_new(UI_FILE, NULL, NULL);
2662 if (X->ui == NULL) {
2663 GtkWidget *dialog;
2665 dialog = gtk_message_dialog_new(NULL, 0,
2666 GTK_MESSAGE_ERROR,
2667 GTK_BUTTONS_NONE,
2668 N_("Error loading user interface"));
2669 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
2670 N_("The user interace file %s is missing or unable to be loaded. Please check your installation."), UI_FILE);
2671 gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT, NULL);
2673 gtk_dialog_run(GTK_DIALOG(dialog));
2674 exit(0);
2677 /* When connecting up signals, would ideally use autoconnect but not
2678 * sure how to get the build process working.
2679 * See http://library.gnome.org/devel/libglade/unstable and
2680 * http://www.jamesh.id.au/software/libglade/
2681 * for some information on how to get this to work
2682 * glade_xml_signal_autoconnect(X->ui);
2684 CONNECT_SIGNAL(kframe_key_press_cb);
2685 CONNECT_SIGNAL(button_cb);
2686 CONNECT_SIGNAL(menu_item_select_cb);
2687 CONNECT_SIGNAL(menu_item_deselect_cb);
2688 CONNECT_SIGNAL(mode_radio_cb);
2689 CONNECT_SIGNAL(inv_cb);
2690 CONNECT_SIGNAL(hyp_cb);
2691 CONNECT_SIGNAL(trig_cb);
2692 CONNECT_SIGNAL(base_cb);
2693 CONNECT_SIGNAL(disp_cb);
2694 CONNECT_SIGNAL(quit_cb);
2695 CONNECT_SIGNAL(edit_cb);
2696 CONNECT_SIGNAL(copy_cb);
2697 CONNECT_SIGNAL(paste_cb);
2698 CONNECT_SIGNAL(insert_ascii_cb);
2699 CONNECT_SIGNAL(undo_cb);
2700 CONNECT_SIGNAL(redo_cb);
2701 CONNECT_SIGNAL(help_cb);
2702 CONNECT_SIGNAL(about_cb);
2703 CONNECT_SIGNAL(show_trailing_zeroes_cb);
2704 CONNECT_SIGNAL(show_thousands_separator_cb);
2705 CONNECT_SIGNAL(show_bitcalculating_cb);
2706 CONNECT_SIGNAL(show_registers_cb);
2707 CONNECT_SIGNAL(accuracy_radio_cb);
2708 CONNECT_SIGNAL(accuracy_other_cb);
2709 CONNECT_SIGNAL(arithmetic_mode_cb);
2710 CONNECT_SIGNAL(mouse_button_cb);
2711 CONNECT_SIGNAL(display_focus_in_cb);
2712 CONNECT_SIGNAL(display_focus_out_cb);
2713 /* Detect when populating the right-click menu to enable pasting */
2714 CONNECT_SIGNAL(buffer_populate_popup_cb);
2715 CONNECT_SIGNAL(shift_left_cb);
2716 CONNECT_SIGNAL(shift_right_cb);
2717 CONNECT_SIGNAL(bit_toggle_cb);
2718 CONNECT_SIGNAL(aframe_delete_cb);
2719 CONNECT_SIGNAL(aframe_ok_cb);
2720 CONNECT_SIGNAL(aframe_cancel_cb);
2721 CONNECT_SIGNAL(aframe_key_cb);
2722 CONNECT_SIGNAL(spframe_delete_cb);
2723 CONNECT_SIGNAL(spframe_ok_cb);
2724 CONNECT_SIGNAL(spframe_cancel_cb);
2725 CONNECT_SIGNAL(spframe_key_cb);
2726 CONNECT_SIGNAL(rframe_delete_cb);
2727 CONNECT_SIGNAL(rframe_response_cb);
2728 CONNECT_SIGNAL(edit_constants_cb);
2729 CONNECT_SIGNAL(edit_functions_cb);
2730 CONNECT_SIGNAL(edit_constants_response_cb);
2731 CONNECT_SIGNAL(edit_functions_response_cb);
2733 X->kframe = GET_WIDGET("calc_window");
2734 X->aframe = GET_WIDGET("ascii_window");
2735 X->aframe_ch = GET_WIDGET("ascii_entry");
2736 X->spframe = GET_WIDGET("precision_dialog");
2737 X->spframe_val = GET_WIDGET("spframe_spin");
2738 X->rframe = GET_WIDGET("register_dialog");
2739 X->con_dialog = GET_WIDGET("edit_constants_dialog");
2740 X->fun_dialog = GET_WIDGET("edit_functions_dialog");
2741 X->menubar = GET_WIDGET("menubar");
2742 X->scrolledwindow = GET_WIDGET("display_scroll"),
2743 X->display_item = GET_WIDGET("displayitem"),
2744 X->bas_panel = GET_WIDGET("basic_panel");
2745 X->sci_panel = GET_WIDGET("scientific_panel");
2746 X->adv_panel = GET_WIDGET("advanced_panel");
2747 X->fin_panel = GET_WIDGET("financial_panel");
2748 X->clear_buttons[0] = GET_WIDGET("calc_clear_simple_button");
2749 X->clear_buttons[1] = GET_WIDGET("calc_clear_advanced_button");
2750 X->mode_panel = GET_WIDGET("mode_panel");
2751 X->trig[0] = GET_WIDGET("degrees_radio");
2752 X->trig[1] = GET_WIDGET("gradians_radio");
2753 X->trig[2] = GET_WIDGET("radians_radio");
2754 X->base[0] = GET_WIDGET("binary_radio");
2755 X->base[1] = GET_WIDGET("octal_radio");
2756 X->base[2] = GET_WIDGET("decimal_radio");
2757 X->base[3] = GET_WIDGET("hexadecimal_radio");
2758 X->disp[0] = GET_WIDGET("engineering_radio");
2759 X->disp[1] = GET_WIDGET("fixed_point_radio");
2760 X->disp[2] = GET_WIDGET("scientific_radio");
2761 X->inv = GET_WIDGET("inverse_check");
2762 X->hyp = GET_WIDGET("hyperbolic_check");
2763 X->bit_panel = GET_WIDGET("bit_panel");
2764 X->statusbar = GET_WIDGET("statusbar");
2765 X->undo = GET_WIDGET("undo_menu");
2766 X->redo = GET_WIDGET("redo_menu");
2767 X->copy = GET_WIDGET("copy_menu");
2768 X->paste = GET_WIDGET("paste_menu");
2769 for (i = 0; i < 16; i++) {
2770 SNPRINTF(name, MAXLINE, "calc_%x_button", i);
2771 X->digit_buttons[i] = GET_WIDGET(name);
2773 for (i = 0; i < MAXREGS; i++) {
2774 SNPRINTF(name, MAXLINE, "register_entry_%d", i);
2775 X->regs[i] = GET_WIDGET(name);
2778 /* Load buttons and set them all to be the same size */
2779 size_group = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
2780 for (i = 0; i < NBUTTONS; i++) {
2781 SNPRINTF(name, MAXLINE, "calc_%s_button",
2782 button_widgets[i].widget_name);
2783 X->buttons[i] = GET_WIDGET(name);
2784 assert(X->buttons[i] != NULL);
2786 gtk_size_group_add_widget(size_group, X->buttons[i]);
2788 g_object_set_data(G_OBJECT(X->buttons[i]), "button",
2789 &buttons[button_widgets[i].key]);
2790 g_object_set_data(G_OBJECT(X->buttons[i]), "mtype",
2791 GINT_TO_POINTER(button_widgets[i].mtype));
2794 /* Load bit panel */
2795 for (i = 0; i < MAXBITS; i++)
2797 SNPRINTF(name, MAXLINE, "bit_label_%d", i);
2798 X->bits[i] = GET_WIDGET(name);
2799 SNPRINTF(name, MAXLINE, "bit_eventbox_%d", i);
2800 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
2801 "bit_index", GINT_TO_POINTER(i));
2804 /* Make menu tooltips displayed in the status bar */
2805 set_menubar_tooltip("quit_menu");
2806 set_menubar_tooltip("copy_menu");
2807 set_menubar_tooltip("paste_menu");
2808 set_menubar_tooltip("insert_ascii_menu");
2809 set_menubar_tooltip("undo_menu");
2810 set_menubar_tooltip("redo_menu");
2811 set_menubar_tooltip("view_basic_menu");
2812 set_menubar_tooltip("view_advanced_menu");
2813 set_menubar_tooltip("view_financial_menu");
2814 set_menubar_tooltip("view_scientific_menu");
2815 set_menubar_tooltip("show_trailing_zeroes_menu");
2816 set_menubar_tooltip("show_thousands_separator_menu");
2817 set_menubar_tooltip("show_bitcalculating_menu");
2818 set_menubar_tooltip("show_registers_menu");
2819 set_menubar_tooltip("ltr_precedence_menu");
2820 set_menubar_tooltip("arithmetic_precedence_menu");
2821 set_menubar_tooltip("help_menu");
2822 set_menubar_tooltip("about_menu");
2824 // ???
2825 widget = GET_WIDGET("kvbox");
2826 gtk_widget_set_direction(widget, GTK_TEXT_DIR_LTR);
2827 gtk_widget_set_direction(X->fin_panel, GTK_TEXT_DIR_LTR);
2829 /* Make dialogs transient of the main window */
2830 gtk_window_set_transient_for(GTK_WINDOW(X->aframe), GTK_WINDOW(X->kframe));
2831 gtk_window_set_transient_for(GTK_WINDOW(X->spframe), GTK_WINDOW(X->kframe));
2832 gtk_window_set_transient_for(GTK_WINDOW(X->rframe), GTK_WINDOW(X->kframe));
2833 gtk_window_set_transient_for(GTK_WINDOW(X->con_dialog),
2834 GTK_WINDOW(X->kframe));
2836 gtk_spin_button_set_value(GTK_SPIN_BUTTON(X->spframe_val),
2837 (double) v->accuracy); // FIXME
2838 gtk_entry_set_max_length(GTK_ENTRY(X->spframe_val), 2);
2840 gtk_dialog_set_default_response(GTK_DIALOG(X->con_dialog),
2841 GTK_RESPONSE_ACCEPT);
2843 /* Make constant tree model */
2844 X->constants_model = create_cf_model(M_CON, X->con_dialog);
2845 treeview = GET_WIDGET("edit_constants_treeview");
2846 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), X->constants_model);
2847 gtk_tree_selection_set_mode(
2848 gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
2849 GTK_SELECTION_SINGLE);
2850 add_cf_column(GTK_TREE_VIEW(treeview), _("No."),
2851 COLUMN_NUMBER, FALSE);
2852 add_cf_column(GTK_TREE_VIEW(treeview), _("Value"),
2853 COLUMN_VALUE, TRUE);
2854 add_cf_column(GTK_TREE_VIEW(treeview), _("Description"),
2855 COLUMN_DESCRIPTION, TRUE);
2857 /* Make function tree model */
2858 X->functions_model = create_cf_model(M_FUN, X->fun_dialog);
2859 treeview = GET_WIDGET("edit_functions_treeview");
2860 gtk_dialog_set_default_response(GTK_DIALOG(X->fun_dialog),
2861 GTK_RESPONSE_ACCEPT);
2862 gtk_window_set_transient_for(GTK_WINDOW(X->fun_dialog),
2863 GTK_WINDOW(X->kframe));
2864 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), X->functions_model);
2865 gtk_tree_selection_set_mode(
2866 gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
2867 GTK_SELECTION_SINGLE);
2868 add_cf_column(GTK_TREE_VIEW(treeview), _("No."),
2869 COLUMN_NUMBER, FALSE);
2870 add_cf_column(GTK_TREE_VIEW(treeview), _("Value"),
2871 COLUMN_VALUE, TRUE);
2872 add_cf_column(GTK_TREE_VIEW(treeview), _("Description"),
2873 COLUMN_DESCRIPTION, TRUE);
2876 X->display_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(X->display_item));
2877 gtk_widget_ensure_style(X->display_item);
2878 font_desc = pango_font_description_copy(X->display_item->style->font_desc);
2879 pango_font_description_set_size(font_desc, 16 * PANGO_SCALE);
2880 gtk_widget_modify_font(X->display_item, font_desc);
2881 pango_font_description_free(font_desc);
2882 gtk_widget_set_name(X->display_item, "displayitem");
2883 // FIXME: We can't allow the display to be editable. See bug #326938
2884 gtk_text_view_set_editable(GTK_TEXT_VIEW(X->display_item),
2885 (v->syntax == EXPRS));
2886 atk_object_set_role(gtk_widget_get_accessible(X->display_item),
2887 ATK_ROLE_EDITBAR);
2889 gtk_widget_realize(X->kframe);
2890 set_win_position();
2892 for (i = 0; i < 3; i++)
2893 g_object_set_data(G_OBJECT(X->trig[i]),
2894 "trig_mode", GINT_TO_POINTER(i));
2895 for (i = 0; i < 4; i++)
2896 g_object_set_data(G_OBJECT(X->base[i]),
2897 "base_mode", GINT_TO_POINTER(i));
2898 for (i = 0; i < 3; i++)
2899 g_object_set_data(G_OBJECT(X->disp[i]),
2900 "numeric_mode", GINT_TO_POINTER(i));
2902 /* Put status image into statusbar (glade doesn't support child widgets
2903 * in statusbars) */
2904 X->status_image = gtk_image_new_from_stock("", GTK_ICON_SIZE_BUTTON);
2905 gtk_widget_show(X->status_image);
2906 gtk_box_pack_start(GTK_BOX(X->statusbar), X->status_image, FALSE, TRUE, 0);
2908 /* Set modes for menu items */
2909 for (i = 1; i < 16; i++) {
2910 SNPRINTF(name, MAXLINE, "shift_left%d_menu", i);
2911 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
2912 "shiftcount", GINT_TO_POINTER(i));
2913 SNPRINTF(name, MAXLINE, "shift_right%d_menu", i);
2914 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
2915 "shiftcount", GINT_TO_POINTER(i));
2917 g_object_set_data(G_OBJECT(GET_WIDGET("view_basic_menu")),
2918 "calcmode", GINT_TO_POINTER(BASIC));
2919 g_object_set_data(G_OBJECT(GET_WIDGET("view_advanced_menu")),
2920 "calcmode", GINT_TO_POINTER(ADVANCED));
2921 g_object_set_data(G_OBJECT(GET_WIDGET("view_financial_menu")),
2922 "calcmode", GINT_TO_POINTER(FINANCIAL));
2923 g_object_set_data(G_OBJECT(GET_WIDGET("view_scientific_menu")),
2924 "calcmode", GINT_TO_POINTER(SCIENTIFIC));
2926 /* Make shortcuts for accuracy menus */
2927 accel_group = gtk_accel_group_new();
2928 gtk_window_add_accel_group(GTK_WINDOW(X->kframe), accel_group);
2929 for (i = 0; i < 10; i++) {
2930 SNPRINTF(name, MAXLINE, "acc_item%d", i);
2931 widget = GET_WIDGET(name);
2932 g_object_set_data(G_OBJECT(widget), "accuracy", GINT_TO_POINTER(i));
2937 static GtkWidget *
2938 create_menu_item_with_markup(char *label, int menu_no, int index)
2940 GtkWidget *accel_label;
2941 GtkWidget *menu_item;
2943 accel_label = gtk_label_new(NULL);
2944 gtk_label_set_markup_with_mnemonic(GTK_LABEL(accel_label), label);
2945 gtk_misc_set_alignment(GTK_MISC(accel_label), 0.0, 0.5);
2946 menu_item = gtk_menu_item_new();
2947 gtk_container_add(GTK_CONTAINER(menu_item), accel_label);
2948 gtk_widget_show(accel_label);
2949 gtk_widget_show(menu_item);
2951 g_object_set_data(G_OBJECT(menu_item), "button", X->mrec[menu_no]);
2952 gtk_menu_shell_append(GTK_MENU_SHELL(X->menus[menu_no]), menu_item);
2954 g_signal_connect(G_OBJECT(menu_item), "activate",
2955 G_CALLBACK(menu_proc_cb), GINT_TO_POINTER(index));
2957 return menu_item;
2961 static void
2962 read_cfdefs() /* Read constant/function definitions. */
2964 int n;
2966 for (n = 0; n < MAXCONFUN; n++) {
2967 get_constant(n);
2968 STRCPY(v->fun_vals[n], ""); /* Initially empty function strings. */
2969 get_function(n);
2974 void
2975 ui_init(int *argc, char ***argv)
2977 X = (XVars) LINT_CAST(calloc(1, sizeof(struct Xobject)));
2979 gtk_init(argc, argv);
2981 X->lnp = ui_get_localized_numeric_point();
2983 gtk_rc_get_default_files();
2985 v->home = (char *) g_get_home_dir();
2986 gtk_rc_parse(g_build_path(v->home, RCNAME, NULL));
2988 gtk_window_set_default_icon_name("gnome-calculator");
2991 void
2992 ui_load()
2994 int i, boolval;
2995 GtkWidget *widget;
2997 read_cfdefs();
2999 X->clipboard_atom = gdk_atom_intern("CLIPBOARD", FALSE);
3000 X->primary_atom = gdk_atom_intern("PRIMARY", FALSE);
3001 create_kframe(); /* Create main gcalctool window. */
3003 X->menus[M_ACC] = GET_WIDGET("accuracy_popup");
3004 X->mrec[M_ACC] = &buttons[KEY_SET_ACCURACY];
3005 X->menus[M_LSHF] = GET_WIDGET("left_shift_popup");
3006 X->mrec[M_LSHF] = &buttons[KEY_SHIFT];
3007 X->menus[M_RSHF] = GET_WIDGET("right_shift_popup");
3008 X->mrec[M_RSHF] = &buttons[KEY_SHIFT];
3010 X->menus[M_CON] = GET_WIDGET("constants_popup");
3011 X->mrec[M_CON] = &buttons[KEY_CONSTANT];
3012 X->menus[M_FUN] = GET_WIDGET("functions_popup");
3013 X->mrec[M_FUN] = &buttons[KEY_FUNCTION];
3014 for (i = 0; i < MAXCONFUN; i++) {
3015 X->constant_menu_items[i] = create_menu_item_with_markup("", M_CON, i);
3016 X->function_menu_items[i] = create_menu_item_with_markup("", M_FUN, i);
3019 X->menus[M_STO] = GET_WIDGET("memory_store_popup");
3020 X->mrec[M_STO] = &buttons[KEY_STORE];
3021 X->menus[M_RCL] = GET_WIDGET("memory_recall_popup");
3022 X->mrec[M_RCL] = &buttons[KEY_RECALL];
3023 X->menus[M_EXCH] = GET_WIDGET("memory_exchange_popup");
3024 X->mrec[M_EXCH] = &buttons[KEY_EXCHANGE];
3025 for (i = 0; i < MAXREGS; i++) {
3026 // FIXME: These labels should be replaced with GtkTreeModel labels
3027 X->memory_store_items[i] = create_menu_item_with_markup("", M_STO, i);
3028 X->memory_recall_items[i] = create_menu_item_with_markup("", M_RCL, i);
3029 X->memory_exchange_items[i] = create_menu_item_with_markup("", M_EXCH, i);
3032 // This matches existing behaviour we can't do in Glade. But is it needed?
3033 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_ACC]), 1);
3034 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_LSHF]), 1);
3035 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_RSHF]), 1);
3036 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_CON]), 1);
3037 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_FUN]), 1);
3038 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_STO]), 1);
3039 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_RCL]), 1);
3040 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_EXCH]), 1);
3042 set_show_tsep_toggle(v->show_tsep);
3043 set_show_zeroes_toggle(v->show_zeroes);
3044 set_show_bitcalculating_toggle(v->bitcalculating_mode);
3046 /* Use loaded Arithmetic Precedence mode setting. */
3047 if (v->syntax == EXPRS) {
3048 widget = GET_WIDGET("arithmetic_precedence_menu");
3049 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
3050 } else {
3051 widget = GET_WIDGET("ltr_precedence_menu");
3052 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
3055 ui_set_display("0.00", FALSE);
3056 ui_set_mode(v->modetype);
3057 ui_set_numeric_mode(FIX);
3058 ui_set_base(v->base);
3059 ui_set_accuracy(v->accuracy);
3060 ui_set_undo_enabled(FALSE, FALSE);
3061 ui_update_trig_mode();
3063 /* Show the memory register window? */
3064 ui_make_registers();
3065 if (get_boolean_resource(R_REGS, &boolval))
3066 ui_set_registers_visible(boolval);
3068 /* Focus on the clear button */
3069 if (v->modetype == BASIC) {
3070 gtk_window_set_focus(GTK_WINDOW(X->kframe),
3071 GTK_WIDGET(X->clear_buttons[0]));
3072 } else {
3073 gtk_window_set_focus(GTK_WINDOW(X->kframe),
3074 GTK_WIDGET(X->clear_buttons[1]));