Catch home and end keys and select text buffer
[gcalctool.git] / gcalctool / gtk.c
blob4252ca00362c2af0ffb13296d1933d26fa0f4c8e
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.
21 #include "config.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27 #include <sys/param.h>
28 #include <sys/stat.h>
29 #include <netdb.h>
30 #include "calctool.h"
31 #include "extern.h"
32 #include "dsdefs.h"
33 #include "functions.h"
34 #include "lr_parser.h"
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <glade/glade.h>
39 #include <gconf/gconf-client.h>
40 #include "ce_parser.h"
41 #include "mpmath.h"
43 /* Popup menu types. */
44 /* FIXME: This enum could be removed */
45 enum menu_type { M_ACC, M_CON, M_EXCH, M_FUN, M_LSHF,
46 M_RCL, M_RSHF, M_STO, M_NONE };
47 #define MAXMENUS 9 /* Maximum number of popup menus. */
49 #define MAX_ACCELERATORS 8
50 struct button_widget {
51 int key;
52 char *widget_name;
53 enum menu_type mtype;
54 guint accelerator_mods[MAX_ACCELERATORS];
55 guint accelerator_keys[MAX_ACCELERATORS];
58 /* This table shows the keyboard values that are currently being used:
60 * | 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
61 *-----------+-----------------------------------------------------
62 * Lower: | a b c d e f i l m n p r s t u v x
63 * Upper: | A C D E F G J K L M N P R S T X Y
64 * Numeric: | 0 1 2 3 4 5 6 7 8 9
65 * Other: | @ . + - * / = % ( ) # < > [ ] { } | & ~ ^ ? ! :
66 * | BackSpace Delete Return
67 *-----------+-----------------------------------------------------
70 static struct button_widget button_widgets[] = {
71 {KEY_0, "0", M_NONE,
72 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
73 { GDK_0, GDK_0, GDK_KP_0, GDK_KP_Insert, 0 }},
75 {KEY_1, "1", M_NONE,
76 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
77 { GDK_1, GDK_1, GDK_KP_1, GDK_KP_End, GDK_R13, 0 }},
79 {KEY_2, "2", M_NONE,
80 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
81 { GDK_2, GDK_2, GDK_KP_2, GDK_KP_Down, 0 }},
83 {KEY_3, "3", M_NONE,
84 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
85 { GDK_3, GDK_3, GDK_KP_3, GDK_KP_Page_Down, GDK_R15, 0 }},
87 {KEY_4, "4", M_NONE,
88 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
89 { GDK_4, GDK_4, GDK_KP_4, GDK_KP_Left, 0 }},
91 {KEY_5, "5", M_NONE,
92 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
93 { GDK_5, GDK_5, GDK_KP_5, GDK_KP_Begin, GDK_R11, 0 }},
95 {KEY_6, "6", M_NONE,
96 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
97 { GDK_6, GDK_6, GDK_KP_6, GDK_KP_Right, 0 }},
99 {KEY_7, "7", M_NONE,
100 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
101 { GDK_7, GDK_7, GDK_KP_7, GDK_KP_Home, GDK_R7, 0 }},
103 {KEY_8, "8", M_NONE,
104 { 0, GDK_SHIFT_MASK, 0, 0, 0 },
105 { GDK_8, GDK_8, GDK_KP_8, GDK_KP_Up, 0 }},
107 {KEY_9, "9", M_NONE,
108 { 0, GDK_SHIFT_MASK, 0, 0, 0, 0 },
109 { GDK_9, GDK_9, GDK_KP_9, GDK_KP_Page_Up, GDK_R9, 0 }},
111 {KEY_A, "a", M_NONE,
112 { 0, 0 },
113 { GDK_a, 0 }},
115 {KEY_B, "b", M_NONE,
116 { 0, 0 },
117 { GDK_b, 0 }},
119 {KEY_C, "c", M_NONE,
120 { 0, 0 },
121 { GDK_c, 0 }},
123 {KEY_D, "d", M_NONE,
124 { 0, 0 },
125 { GDK_d, 0 }},
127 {KEY_E, "e", M_NONE,
128 { 0, 0 },
129 { GDK_e, 0 }},
131 {KEY_F, "f", M_NONE,
132 { 0, 0 },
133 { GDK_f, 0 }},
135 {KEY_CLEAR, "clear_simple", M_NONE,
136 { 0, 0 },
137 { GDK_Delete, 0 }},
139 {KEY_CLEAR, "clear_advanced", M_NONE,
140 { 0, 0 },
141 { GDK_Delete, 0 }},
143 {KEY_LEFT_SHIFT, "shift_left", M_LSHF,
144 { GDK_SHIFT_MASK, 0 },
145 { GDK_less, 0 }},
147 {KEY_RIGHT_SHIFT, "shift_right", M_RSHF,
148 { GDK_SHIFT_MASK, 0 },
149 { GDK_greater, 0 }},
151 {KEY_SET_ACCURACY, "accuracy", M_ACC,
152 { GDK_SHIFT_MASK, 0 },
153 { GDK_A, 0 }},
155 {KEY_CONSTANT, "constants", M_CON,
156 { GDK_SHIFT_MASK, 0, 0 },
157 { GDK_numbersign, GDK_numbersign, 0 }},
159 {KEY_FUNCTION, "functions", M_FUN,
160 { GDK_SHIFT_MASK, 0 },
161 { GDK_F, 0 }},
163 {KEY_STORE, "store", M_STO,
164 { GDK_SHIFT_MASK, 0 },
165 { GDK_S, 0 }},
167 {KEY_RECALL, "recall", M_RCL,
168 { GDK_SHIFT_MASK, 0 },
169 { GDK_R, 0 }},
171 {KEY_EXCHANGE, "exchange", M_EXCH,
172 { GDK_SHIFT_MASK, 0 },
173 { GDK_X, 0 }},
175 {KEY_CLEAR_ENTRY, "clear_entry_simple", M_NONE,
176 { GDK_CONTROL_MASK, 0, 0 },
177 { GDK_BackSpace, GDK_Escape, 0 }},
179 {KEY_CLEAR_ENTRY, "clear_entry_advanced", M_NONE,
180 { GDK_CONTROL_MASK, 0, 0 },
181 { GDK_BackSpace, GDK_Escape, 0 }},
183 {KEY_BACKSPACE, "backspace_simple", M_NONE,
184 { 0, 0 },
185 { GDK_BackSpace, 0 }},
187 {KEY_BACKSPACE, "backspace_advanced", M_NONE,
188 { 0, 0 },
189 { GDK_BackSpace, 0 }},
191 {KEY_NUMERIC_POINT, "numeric_point", M_NONE,
192 { 0, 0, 0, 0 },
193 { GDK_period, GDK_KP_Decimal, GDK_KP_Delete, GDK_KP_Separator, 0 }},
195 {KEY_CALCULATE, "result", M_NONE,
196 { 0, 0, 0, GDK_SHIFT_MASK, 0 },
197 { GDK_equal, GDK_KP_Enter, GDK_Return, GDK_equal, 0 }},
199 {KEY_START_BLOCK, "start_group", M_NONE,
200 { GDK_SHIFT_MASK, 0 },
201 { GDK_parenleft, 0 }},
203 {KEY_END_BLOCK, "end_group", M_NONE,
204 { GDK_SHIFT_MASK, 0 },
205 { GDK_parenright, 0 }},
207 {KEY_ADD, "add", M_NONE,
208 { GDK_SHIFT_MASK, 0, 0, 0 },
209 { GDK_plus, GDK_plus, GDK_KP_Add, 0 }},
211 {KEY_SUBTRACT, "subtract", M_NONE,
212 { 0, 0, 0, 0 },
213 { GDK_minus, GDK_KP_Subtract, GDK_R4, 0 }},
215 {KEY_MULTIPLY, "multiply", M_NONE,
216 { GDK_SHIFT_MASK, 0, 0, 0, 0 },
217 { GDK_asterisk, GDK_KP_Multiply, GDK_x, GDK_R6, 0 }},
219 {KEY_DIVIDE, "divide", M_NONE,
220 { 0, GDK_SHIFT_MASK, 0, 0, GDK_SHIFT_MASK, 0 },
221 { GDK_slash, GDK_slash, GDK_KP_Divide, GDK_R5, GDK_slash, 0 }},
223 {KEY_CHANGE_SIGN, "change_sign_simple", M_NONE,
224 { GDK_SHIFT_MASK, 0 },
225 { GDK_C, 0 }},
227 {KEY_CHANGE_SIGN, "change_sign_advanced", M_NONE,
228 { GDK_SHIFT_MASK, 0 },
229 { GDK_C, 0 }},
231 {KEY_INTEGER, "integer_portion", M_NONE,
232 { 0, 0 },
233 { GDK_i, 0 }},
235 {KEY_FRACTION, "fractional_portion", M_NONE,
236 { GDK_SHIFT_MASK, 0 },
237 { GDK_colon, 0 }},
239 {KEY_PERCENTAGE, "percentage", M_NONE,
240 { GDK_SHIFT_MASK, 0 },
241 { GDK_percent, 0 }},
243 {KEY_SQUARE, "square", M_NONE,
244 { GDK_SHIFT_MASK, 0 },
245 { GDK_at, 0 }},
247 {KEY_SQUARE_ROOT, "sqrt", M_NONE,
248 { 0, 0 },
249 { GDK_s, 0 }},
251 {KEY_RECIPROCAL, "reciprocal", M_NONE,
252 { 0, 0 },
253 { GDK_r, 0 }},
255 {KEY_ABSOLUTE_VALUE, "abs", M_NONE,
256 { 0, 0 },
257 { GDK_u, 0 }},
259 {KEY_MASK_16, "mask_16", M_NONE,
260 { 0, 0 },
261 { GDK_bracketright, 0 }},
263 {KEY_MASK_32, "mask_32", M_NONE,
264 { 0, 0 },
265 { GDK_bracketleft, 0 }},
267 {KEY_MODULUS_DIVIDE, "modulus_divide", M_NONE,
268 { GDK_SHIFT_MASK, 0 },
269 { GDK_M, 0 }},
271 {KEY_EXPONENTIAL, "exponential", M_NONE,
272 { GDK_SHIFT_MASK, 0 },
273 { GDK_E, 0 }},
275 {KEY_E_POW_X, "pow_e", M_NONE,
276 { GDK_SHIFT_MASK, 0 },
277 { GDK_braceleft, 0 }},
279 {KEY_10_POW_X, "pow_10", M_NONE,
280 { GDK_SHIFT_MASK, 0 },
281 { GDK_braceright, 0 }},
283 {KEY_X_POW_Y, "x_pow_y", M_NONE,
284 { GDK_SHIFT_MASK, GDK_SHIFT_MASK, 0 },
285 { GDK_caret, GDK_asciicircum, 0 }},
287 {KEY_NATURAL_LOGARITHM, "natural_logarithm", M_NONE,
288 { GDK_SHIFT_MASK, 0 },
289 { GDK_N, 0 }},
291 {KEY_LOGARITHM, "logarithm", M_NONE,
292 { GDK_SHIFT_MASK, 0 },
293 { GDK_G, 0 }},
295 {KEY_FACTORIAL, "factorial", M_NONE,
296 { GDK_SHIFT_MASK, 0 },
297 { GDK_exclam, 0 }},
299 {KEY_RANDOM, "random", M_NONE,
300 { GDK_SHIFT_MASK, 0 },
301 { GDK_question, 0 }},
303 {KEY_SINE, "sine", M_NONE,
304 { GDK_SHIFT_MASK, 0 },
305 { GDK_K, 0 }},
307 {KEY_COSINE, "cosine", M_NONE,
308 { GDK_SHIFT_MASK, 0 },
309 { GDK_J, 0 }},
311 {KEY_TANGENT, "tangent", M_NONE,
312 { GDK_SHIFT_MASK, 0 },
313 { GDK_L, 0 }},
315 {KEY_NOT, "not", M_NONE,
316 { GDK_SHIFT_MASK, 0 },
317 { GDK_asciitilde, 0 }},
319 {KEY_OR, "or", M_NONE,
320 { GDK_SHIFT_MASK, 0 },
321 { GDK_bar, 0 }},
323 {KEY_AND, "and", M_NONE,
324 { GDK_SHIFT_MASK, 0 },
325 { GDK_ampersand, 0 }},
327 {KEY_XOR, "xor", M_NONE,
328 { 0 },
329 { 0 }},
331 {KEY_XNOR, "xnor", M_NONE,
332 { 0, 0 },
333 { GDK_n, 0 }},
335 {KEY_FINC_CTRM, "finc_compounding_term", M_NONE,
336 { 0, 0 },
337 { GDK_m, 0 }},
339 {KEY_FINC_DDB, "finc_double_declining_depreciation", M_NONE,
340 { GDK_SHIFT_MASK, 0 },
341 { GDK_D, 0 }},
343 {KEY_FINC_FV, "finc_future_value", M_NONE,
344 { 0, 0 },
345 { GDK_v, 0 }},
347 {KEY_FINC_PMT, "finc_periodic_payment", M_NONE,
348 { GDK_SHIFT_MASK, 0 },
349 { GDK_P, 0 }},
351 {KEY_FINC_PV, "finc_present_value", M_NONE,
352 { 0, 0 },
353 { GDK_p, 0 }},
355 {KEY_FINC_RATE, "finc_periodic_interest_rate", M_NONE,
356 { GDK_SHIFT_MASK, 0 },
357 { GDK_T, 0 }},
359 {KEY_FINC_SLN, "finc_straight_line_depreciation", M_NONE,
360 { 0, 0 },
361 { GDK_l, 0 }},
363 {KEY_FINC_SYD, "finc_sum_of_the_years_digits_depreciation", M_NONE,
364 { 0, 0 },
365 { GDK_Y, 0 }},
367 {KEY_FINC_TERM, "finc_term", M_NONE,
368 { 0, 0 },
369 { GDK_T, 0 }},
371 #define NBUTTONS (sizeof(button_widgets) / sizeof(struct button_widget))
373 #define UI_FILE PACKAGE_GLADE_DIR "/gcalctool.glade"
375 #define MAXBITS 64 /* Bit panel: number of bit fields. */
377 #define GET_WIDGET(name) \
378 glade_xml_get_widget(X->ui, (name))
380 #define SET_MENUBAR_ITEM_STATE(name, state) \
381 g_object_set_data(G_OBJECT(GET_WIDGET(name)), "sensitive", \
382 GINT_TO_POINTER(state));
384 #define CONNECT_SIGNAL(name) glade_xml_signal_connect(X->ui, #name, \
385 G_CALLBACK(name))
387 struct Xobject { /* Gtk+/Xlib graphics object. */
388 GdkAtom clipboard_atom;
389 GdkAtom primary_atom;
390 GConfClient *client;
392 GladeXML *ui;
394 GtkWidget *kframe; /* Main window. */
396 GtkWidget *cm_dialog; /* Change Mode dialog. */
397 GtkTreeModel *constants_model;
398 GtkWidget *con_dialog; /* Edit constants dialog. */
400 GtkTreeModel *functions_model;
401 GtkWidget *fun_dialog; /* Edit functions dialog. */
402 GtkWidget *menubar; // FIXME: Why is this needed?
404 GtkWidget *bit_panel;
405 GtkWidget *bits[MAXBITS]; /* The 0/1 labels in the bit panel. */
407 GtkWidget *status_image; /* Statusbar image */
408 GtkWidget *statusbar;
410 GtkWidget *undo; /* Undo menuitem */
411 GtkWidget *redo; /* Redo menuitem */
412 GtkWidget *copy; /* Copy menuitem */
413 GtkWidget *paste; /* Paste menuitem */
415 GtkWidget *aframe; /* ASCII window. */
416 GtkWidget *aframe_ch;
418 GtkWidget *display_item; /* Calculator display. */
419 GtkTextBuffer *display_buffer; /* Buffer used in display */
420 GtkWidget *scrolledwindow; /* Scrolled window for display_item. */
422 GtkWidget *rframe; /* Register window. */
423 GtkWidget *regs[MAXREGS]; /* Memory registers. */
425 GtkWidget *spframe; /* Set Precision window. */
426 GtkWidget *spframe_val;
427 GtkWidget *menus[MAXMENUS];
429 GtkWidget *buttons[NBUTTONS];
430 GtkWidget *digit_buttons[16];
431 GtkWidget *clear_buttons[2];
433 GtkWidget *bas_panel; /* Panel containing basic mode widgets. */
434 GtkWidget *adv_panel; /* Panel containing advanced mode widgets. */
435 GtkWidget *fin_panel; /* Panel containing financial mode widgets. */
436 GtkWidget *sci_panel; /* Panel containing scientific mode widgets. */
437 GtkWidget *mode_panel; /* Panel containing scientific mode widgets. */
439 /* Labels for popup menus */
440 GtkWidget *constant_menu_items[MAXCONFUN];
441 GtkWidget *function_menu_items[MAXCONFUN];
442 GtkWidget *memory_store_items[MAXREGS];
443 GtkWidget *memory_recall_items[MAXREGS];
444 GtkWidget *memory_exchange_items[MAXREGS];
446 /* Scientific mode widgets */
447 GtkWidget *hyp; /* Hyperbolic mode. */
448 GtkWidget *inv; /* Inverse mode. */
449 GtkWidget *base[MAXBASES]; /* Numeric base radio buttons. */
450 GtkWidget *disp[MAXDISPMODES]; /* Numeric display mode. */
451 GtkWidget *trig[MAXTRIGMODES]; /* Trigonometric mode. */
453 int mode; /* The new mode. */
454 int menuval; /* Index to button array at menu time. */
455 char *lnp; /* Localized numerical point (UTF8 format) */
456 struct button *mrec[MAXMENUS];
459 typedef struct Xobject *XVars;
461 enum {
462 COLUMN_NUMBER,
463 COLUMN_VALUE,
464 COLUMN_DESCRIPTION,
465 COLUMN_EDITABLE,
466 NUM_COLUMNS
469 static XVars X;
472 main(int argc, char **argv)
474 v = (Vars) LINT_CAST(calloc(1, sizeof(struct calcVars)));
475 X = (XVars) LINT_CAST(calloc(1, sizeof(struct Xobject)));
477 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
478 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
479 textdomain(GETTEXT_PACKAGE);
481 gtk_init(&argc, &argv);
483 X->lnp = get_localized_numeric_point();
485 gtk_rc_get_default_files();
487 v->home = (char *) g_get_home_dir();
488 gtk_rc_parse(g_build_path(v->home, RCNAME, NULL));
490 gtk_window_set_default_icon_name("gnome-calculator");
492 do_calctool(argc, argv);
494 return(0);
498 void
499 update_statusbar(gchar *text, const gchar *imagename)
501 GtkImage *image = GTK_IMAGE(X->status_image);
503 assert(text);
504 assert(imagename);
505 assert(image);
507 gtk_image_set_from_stock(image, imagename, GTK_ICON_SIZE_BUTTON);
508 gtk_statusbar_pop(GTK_STATUSBAR(X->statusbar), 0);
509 gtk_statusbar_push(GTK_STATUSBAR(X->statusbar), 0, text);
513 static void
514 bin_str(int MP_value[MP_SIZE], char *str, int maxbits)
516 int i, MP0[MP_SIZE], MP1[MP_SIZE], MP2[MP_SIZE], MP3[MP_SIZE];
517 int neg = 0;
519 MPstr_to_num("0", DEC, MP0);
520 MPstr_to_num("1", DEC, MP1);
521 MPstr_to_num("2", DEC, MP2);
523 if (mplt(MP_value, MP0)) {
524 mpcmim(MP_value, MP0);
525 mpadd(MP0, MP1, MP0);
526 neg = 1;
527 } else {
528 mpcmim(MP_value, MP0);
531 for (i = 0; i < maxbits; i++) {
532 double lsb; /* Least significant bit. */
533 calc_and(MP3, MP0, MP1);
534 mpcmd(MP3, &lsb);
536 if (lsb == 0) {
537 str[maxbits - i -1] = (neg) ? '1' : '0';
538 } else {
539 str[maxbits - i -1] = (neg) ? '0' : '1';
542 mpdiv(MP0, MP2, MP3);
543 mpcmim(MP3, MP0);
546 return;
550 /* Set new title for a window. */
552 void
553 set_title(enum fcp_type fcptype, char *str)
555 GtkWidget *f = NULL;
557 if (fcptype == FCP_KEY) {
558 f = X->kframe;
559 } else if (fcptype == FCP_REG) {
560 f = X->rframe;
562 gtk_window_set_title(GTK_WINDOW(f), _(str));
566 static void
567 set_bit_panel()
569 int bit_str_len, i, MP1[MP_SIZE], MP2[MP_SIZE];
570 int MP[MP_SIZE];
571 char str[64], label[3];
572 int ret = usable_num(MP);
574 switch (v->syntax) {
575 case npa:
576 MPstr_to_num(v->display, v->base, MP1);
577 mpcmim(MP1, MP2);
578 if (mpeq(MP1, MP2)) {
579 char *bit_str, label[3], tmp[MAXLINE];
580 int toclear = (v->current == KEY_CLEAR_ENTRY)
581 ? TRUE : FALSE;
583 bit_str = make_fixed(MP1, tmp, BIN, MAXLINE, toclear);
584 bit_str_len = strlen(bit_str);
585 if (bit_str_len <= MAXBITS) {
586 gtk_widget_set_sensitive(X->bit_panel, TRUE);
588 STRCPY(label, " 0");
589 for (i = 0; i < MAXBITS; i++) {
590 label[1] = (i < bit_str_len) ? bit_str[bit_str_len-i-1] : '0';
591 gtk_label_set_text(GTK_LABEL(X->bits[MAXBITS - i - 1]), label);
594 return;
597 gtk_widget_set_sensitive(X->bit_panel, FALSE);
598 break;
600 case exprs:
601 if (ret || !is_integer(MP)) {
602 gtk_widget_set_sensitive(X->bit_panel, FALSE);
603 return;
605 bin_str(MP, str, 64);
606 gtk_widget_set_sensitive(X->bit_panel, TRUE);
608 STRCPY(label, " 0");
609 for (i = 0; i < 64; i++) {
610 label[1] = str[64 - i - 1];
611 gtk_label_set_text(GTK_LABEL(X->bits[64 - i - 1]), label);
613 break;
618 static void
619 scroll_right()
621 if (!v->started) {
622 return;
625 if (GTK_WIDGET_VISIBLE(
626 GTK_SCROLLED_WINDOW(X->scrolledwindow)->hscrollbar)) {
627 GtkAdjustment *set;
629 set = gtk_scrolled_window_get_hadjustment(
630 GTK_SCROLLED_WINDOW(X->scrolledwindow));
631 gtk_adjustment_set_value(set, set->upper);
632 gtk_scrolled_window_set_hadjustment(
633 GTK_SCROLLED_WINDOW(X->scrolledwindow), set);
638 void
639 set_display(char *str, int minimize_changes)
641 char localized[MAX_LOCALIZED];
642 GtkTextIter start, end;
643 gchar *text;
644 gint diff;
645 gint len1, len2;
646 gboolean done;
648 if (str == NULL || *str == 0) {
649 str = " ";
650 } else {
651 if (!v->noparens) {
652 localize_number(localized, str);
653 str = localized;
656 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
657 text = gtk_text_buffer_get_text(X->display_buffer, &start, &end, TRUE);
658 diff = strcmp (text, str);
659 if (diff != 0) {
660 len1 = strlen(text);
661 len2 = strlen(str);
663 done = FALSE;
664 if (minimize_changes) {
665 if (len1 < len2 && strncmp(text, str, len1) == 0) {
666 /* Text insertion */
667 gtk_text_buffer_insert(X->display_buffer, &end, str + len1, -1);
668 done = TRUE;
669 } else if (len1 > len2 && strncmp(text, str, len2) == 0) {
670 /* Text deletion */
671 gtk_text_buffer_get_iter_at_offset (X->display_buffer, &start, len2);
672 gtk_text_buffer_delete(X->display_buffer, &start, &end);
673 done = TRUE;
677 if (!done) {
678 gtk_text_buffer_delete(X->display_buffer, &start, &end);
679 gtk_text_buffer_insert(X->display_buffer, &end, str, -1);
682 scroll_right();
683 g_free(text);
687 void
688 write_display(char *str)
690 gchar *text;
691 GtkTextIter start, end;
693 if (str == NULL ) {
694 str = " ";
697 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
698 text = gtk_text_buffer_get_text(X->display_buffer, &start, &end, TRUE);
700 gtk_text_buffer_delete(X->display_buffer, &start, &end);
702 gtk_text_buffer_insert(X->display_buffer, &end, str, -1);
703 scroll_right();
704 g_free(text);
708 /* When an error condition occurs:
710 * - make insensitive all buttons except Clr.
711 * - make all Scientific mode toggles and checkboxes insensitive.
712 * - make all menubar items insensitive except:
713 * Calculator->Quit
714 * Help->Contents
716 * When the error condition is cleared, resensitise everything, setting
717 * the numeric base buttons correctly.
720 void
721 set_error_state(int error)
723 int i;
725 v->error = error;
727 for (i = 0; i < NBUTTONS; i++) {
728 gtk_widget_set_sensitive(X->buttons[i], !v->error);
730 /* Clr button always sensitive. */
731 gtk_widget_set_sensitive(X->clear_buttons[0], TRUE);
732 gtk_widget_set_sensitive(X->clear_buttons[1], TRUE);
734 if (!v->error) {
735 grey_buttons(v->base);
738 gtk_widget_set_sensitive(X->mode_panel, !v->error);
740 // FIXME: Isn't this missing a whole lot of widgets?
741 SET_MENUBAR_ITEM_STATE("copy_menu", !v->error);
742 SET_MENUBAR_ITEM_STATE("paste_menu", !v->error);
743 SET_MENUBAR_ITEM_STATE("insert_ascii_menu", !v->error);
744 SET_MENUBAR_ITEM_STATE("view_basic_menu", !v->error);
745 SET_MENUBAR_ITEM_STATE("view_advanced_menu", !v->error);
746 SET_MENUBAR_ITEM_STATE("view_financial_menu", !v->error);
747 SET_MENUBAR_ITEM_STATE("view_scientific_menu", !v->error);
748 SET_MENUBAR_ITEM_STATE("show_trailing_zeroes_menu",
749 !v->error && (v->modetype == SCIENTIFIC));
750 SET_MENUBAR_ITEM_STATE("show_thousands_separator_menu", !v->error);
751 SET_MENUBAR_ITEM_STATE("show_registers_menu", !v->error);
752 SET_MENUBAR_ITEM_STATE("arithmetic_precedence_menu", !v->error);
753 SET_MENUBAR_ITEM_STATE("about_menu", !v->error);
757 void
758 set_hyp_item(int state)
760 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->hyp), state);
764 void
765 set_inv_item(int state)
767 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->inv), state);
771 static void
772 set_memory_toggle(int state)
774 GtkWidget *radio = GET_WIDGET("show_registers_menu");
776 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(radio), state);
780 void
781 set_mode(enum mode_type mode)
783 GtkRequisition *r;
784 gint w, h;
786 switch (mode) {
787 case BASIC:
788 gtk_widget_show(X->bas_panel);
789 gtk_widget_hide(X->adv_panel);
790 gtk_widget_hide(X->fin_panel);
791 gtk_widget_hide(X->mode_panel);
792 gtk_widget_hide(X->bit_panel);
793 gtk_widget_hide(X->sci_panel);
794 break;
796 case ADVANCED:
797 gtk_widget_hide(X->bas_panel);
798 gtk_widget_show(X->adv_panel);
799 gtk_widget_hide(X->fin_panel);
800 gtk_widget_hide(X->mode_panel);
801 gtk_widget_hide(X->bit_panel);
802 gtk_widget_hide(X->sci_panel);
803 break;
805 case FINANCIAL:
806 gtk_widget_hide(X->bas_panel);
807 gtk_widget_show(X->adv_panel);
808 gtk_widget_show(X->fin_panel);
809 gtk_widget_hide(X->mode_panel);
810 gtk_widget_hide(X->bit_panel);
811 gtk_widget_hide(X->sci_panel);
812 break;
814 case SCIENTIFIC:
815 gtk_widget_hide(X->bas_panel);
816 gtk_widget_show(X->adv_panel);
817 gtk_widget_hide(X->fin_panel);
818 gtk_widget_show_all(X->mode_panel);
819 if (v->bitcalculating_mode) {
820 gtk_widget_show_all(X->bit_panel);
821 } else {
822 gtk_widget_hide(X->bit_panel);
824 gtk_widget_show(X->sci_panel);
825 break;
828 r = g_new0(GtkRequisition, 1);
829 gtk_widget_size_request(X->menubar, r);
830 w = r->width;
831 h = r->height;
832 gtk_widget_size_request(X->display_item, r);
833 w = MAX(w, r->width);
834 h += r->height;
836 if (GTK_WIDGET_VISIBLE(X->fin_panel)) {
837 gtk_widget_size_request(X->fin_panel, r);
838 w = MAX(w, r->width);
839 h += r->height;
842 if (GTK_WIDGET_VISIBLE(X->mode_panel)) {
843 gtk_widget_size_request(X->mode_panel, r);
844 w = MAX(w, r->width);
845 h += r->height;
848 if (GTK_WIDGET_VISIBLE(X->sci_panel)) {
849 gtk_widget_size_request(X->sci_panel, r);
850 w = MAX(w, r->width);
851 h += r->height;
854 /* For initial display. */
855 gtk_window_set_default_size(GTK_WINDOW(X->kframe), w, h);
856 gtk_window_resize(GTK_WINDOW(X->kframe), w, h);
858 g_free(r);
862 static void
863 set_item(enum item_type itemtype, int val)
865 if (!v->started) {
866 return;
869 switch (itemtype) {
870 case BASEITEM:
871 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->base[val]), 1);
872 break;
874 case NUMITEM:
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->disp[val]), 1);
876 break;
878 case TTYPEITEM:
879 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(X->trig[val]), 1);
880 break;
882 default:
883 break;
888 /*ARGSUSED*/
889 static void
890 about_cb(GtkWidget *widget)
892 const gchar *authors[] = {
893 "Rich Burridge <rich.burridge@sun.com>",
894 "Sami Pietila <sampie@ariana-dsl.utu.fi>",
895 "Robert Ancell <robert.ancell@gmail.com>",
896 NULL
898 const gchar *documenters[] = {
899 "Sun Microsystems",
900 NULL
902 const gchar *translator_credits = _("translator-credits");
904 const char *license[] = {
905 N_("Gcalctool is free software; you can redistribute it and/or modify\n"
906 "it under the terms of the GNU General Public License as published by\n"
907 "the Free Software Foundation; either version 2 of the License, or\n"
908 "(at your option) any later version.\n"),
909 N_("Gcalctool is distributed in the hope that it will be useful,\n"
910 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
911 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
912 "GNU General Public License for more details.\n"),
913 N_("You should have received a copy of the GNU General Public License\n"
914 "along with Gcalctool; if not, write to the Free Software Foundation, Inc.,\n"
915 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n")
918 char *license_trans = g_strconcat(_(license[0]), "\n",
919 _(license[1]), "\n",
920 _(license[2]), "\n",
921 NULL);
923 gtk_show_about_dialog(GTK_WINDOW(X->kframe),
924 "name",_("Gcalctool"),
925 "version", VERSION,
926 "copyright", _("\xc2\xa9 1986-2007 The Gcalctool authors"),
927 "license", license_trans,
928 "comments", _("Calculator with financial and scientific modes."),
929 "authors", authors,
930 "documenters", documenters,
931 "translator_credits", translator_credits,
932 "logo-icon-name", "gnome-calculator",
933 NULL);
937 static void
938 cell_edited(GtkCellRendererText *cell, const gchar *path_string,
939 const gchar *new_text, gpointer data)
941 GtkTreeModel *model = (GtkTreeModel *) data;
942 GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
943 GtkTreeIter iter;
944 gchar *old_text;
945 gint *column;
947 column = g_object_get_data(G_OBJECT(cell), "column");
949 gtk_tree_model_get_iter(model, &iter, path);
951 switch (GPOINTER_TO_INT(column)) {
952 case COLUMN_VALUE:
953 gtk_tree_model_get(model, &iter, column, &old_text, -1);
954 g_free(old_text);
955 gtk_list_store_set(GTK_LIST_STORE(model), &iter, column,
956 g_strdup(new_text), -1);
957 break;
959 case COLUMN_DESCRIPTION:
960 gtk_tree_model_get(model, &iter, column, &old_text, -1);
961 g_free(old_text);
962 gtk_list_store_set(GTK_LIST_STORE(model), &iter, column,
963 g_strdup(new_text), -1);
964 break;
967 gtk_tree_path_free(path);
971 static void
972 add_cf_column(GtkTreeView *treeview, gchar *name, gint colno, gboolean editable)
974 GtkCellRenderer *renderer;
975 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
977 renderer = gtk_cell_renderer_text_new();
978 if (editable) {
979 g_signal_connect(G_OBJECT(renderer), "edited",
980 G_CALLBACK(cell_edited), model);
982 g_object_set_data(G_OBJECT(renderer), "column", GINT_TO_POINTER(colno));
984 if (editable) {
985 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
986 -1, name, renderer,
987 "text", colno,
988 "editable", COLUMN_EDITABLE,
989 NULL);
990 } else {
991 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
992 -1, name, renderer,
993 "text", colno,
994 NULL);
999 /*ARGSUSED*/
1000 static void
1001 aframe_cancel_cb(GtkButton *button, gpointer user_data)
1003 gtk_widget_hide(X->aframe);
1007 /*ARGSUSED*/
1008 static gboolean
1009 aframe_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1011 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
1013 if (event->keyval == GDK_Escape) {
1014 gtk_widget_hide(X->aframe);
1017 return(FALSE);
1021 /*ARGSUSED*/
1022 static void
1023 aframe_ok_cb(GtkButton *button, gpointer user_data)
1025 char *ch;
1026 int val;
1028 ch = (char *) gtk_entry_get_text(GTK_ENTRY(X->aframe_ch));
1029 val = ch[0];
1030 mpcim(&val, v->MPdisp_val);
1031 show_display(v->MPdisp_val);
1032 gtk_widget_hide(X->aframe);
1036 /*ARGSUSED*/
1037 void
1038 base_cb(GtkWidget *widget)
1040 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1041 do_base((enum base_type) g_object_get_data(G_OBJECT(widget),
1042 "response_id"));
1046 void
1047 beep()
1049 gdk_beep();
1052 static void do_button(struct button *n, int arg)
1054 struct exprm_state *e;
1056 switch (v->syntax) {
1057 case npa:
1058 process_item(n, arg);
1059 set_bit_panel();
1060 if (v->new_input && v->dtype == FIX) {
1061 STRNCPY(v->fnum, v->display, MAX_DIGITS - 1);
1062 set_display(v->fnum, TRUE);
1064 break;
1066 case exprs:
1067 e = get_state();
1068 e->value = arg;
1069 MEMCPY(&(e->button), n, sizeof(struct button));
1070 new_state();
1071 do_expression();
1072 set_bit_panel();
1073 break;
1075 default:
1076 assert(0);
1081 static void
1082 help_display(void)
1084 GError *error = NULL;
1085 char *command;
1086 const char *lang;
1087 char *uri = NULL;
1088 GdkScreen *gscreen;
1090 int i;
1092 const char * const * langs = g_get_language_names ();
1094 for (i = 0; langs[i]; i++) {
1095 lang = langs[i];
1096 if (strchr (lang, '.')) {
1097 continue;
1100 uri = g_build_filename(PACKAGE_DATA_DIR,
1101 "/gnome/help/gcalctool/",
1102 lang,
1103 "/gcalctool.xml",
1104 NULL);
1106 if (g_file_test (uri, G_FILE_TEST_EXISTS)) {
1107 break;
1111 command = g_strconcat ("gnome-open ghelp://", uri, NULL);
1112 gscreen = gdk_screen_get_default();
1113 gdk_spawn_command_line_on_screen (gscreen, command, &error);
1114 if (error) {
1115 GtkWidget *d;
1117 d = gtk_message_dialog_new(NULL,
1118 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1119 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
1120 error->message);
1121 gtk_dialog_run(GTK_DIALOG(d));
1122 gtk_widget_destroy(d);
1123 g_error_free(error);
1124 error = NULL;
1127 g_free (command);
1128 g_free (uri);
1132 static void
1133 put_constant(int n, char *con_value, char *con_name)
1135 char key[MAXLINE];
1136 char *cstr = g_strdup(con_value);
1138 /* Constants are written out with no thousands seaparator and with a radix
1139 * character of ".".
1142 SNPRINTF(key, MAXLINE, "/apps/%s/constant%1dvalue", v->appname, n);
1143 gconf_client_set_string(X->client, key, cstr, NULL);
1144 g_free(cstr);
1146 SNPRINTF(key, MAXLINE, "/apps/%s/constant%1dname", v->appname, n);
1147 gconf_client_set_string(X->client, key, con_name, NULL);
1151 static void
1152 put_function(int n, char *fun_value, char *fun_name)
1154 char key[MAXLINE];
1156 SNPRINTF(key, MAXLINE, "/apps/%s/function%1dvalue", v->appname, n);
1157 gconf_client_set_string(X->client, key, fun_value, NULL);
1159 SNPRINTF(key, MAXLINE, "/apps/%s/function%1dname", v->appname, n);
1160 gconf_client_set_string(X->client, key, fun_name, NULL);
1164 /*ARGSUSED*/
1165 static void
1166 menu_proc_cb(GtkMenuItem *mi, gpointer user_data)
1168 struct button *n = (struct button *) g_object_get_data(G_OBJECT(mi), "button");
1169 do_button(n, GPOINTER_TO_INT(user_data));
1173 /* Put gcalctool resource into deskset database. */
1175 void
1176 put_resource(enum res_type rtype, char *value)
1178 char cstr[MAXLINE], key[MAXLINE];
1180 STRNCPY(key, calc_res[(int) rtype], MAXLINE - 1);
1181 SNPRINTF(cstr, MAXLINE, "/apps/%s/%s", v->appname, key);
1182 gconf_client_set_string(X->client, cstr, value, NULL);
1186 /* Create popup window for editing constants/functions. */
1188 static void
1189 update_popup_label(GtkWidget *menu_item, gchar *text)
1191 GtkWidget *label;
1192 label = (GtkWidget *)gtk_container_get_children(GTK_CONTAINER(menu_item))->data;
1193 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), text);
1196 static void
1197 update_constants_menu(void)
1199 char mline[MAXLINE];
1200 int i;
1202 for (i = 0; i < MAXCONFUN; i++) {
1203 SNPRINTF(mline, MAXLINE,
1204 "<span weight=\"bold\">%s_%1d:</span> %s [%s]", _("C"), i,
1205 make_number(v->MPcon_vals[i], DEC, TRUE),
1206 v->con_names[i]);
1207 update_popup_label(X->constant_menu_items[i], mline);
1212 static void
1213 update_functions_menu(void)
1215 char mline[MAXLINE];
1216 int i;
1218 for (i = 0; i < MAXCONFUN; i++) {
1219 if (strlen(v->fun_vals[i]) != 0) {
1220 SNPRINTF(mline, MAXLINE,
1221 "<span weight=\"bold\">%s_%1d:</span> %s [%s]",
1222 _("F"), i, v->fun_vals[i], v->fun_names[i]);
1223 gtk_widget_show(X->function_menu_items[i]);
1224 update_popup_label(X->function_menu_items[i], mline);
1226 else
1227 gtk_widget_hide(X->function_menu_items[i]);
1232 static void
1233 edit_constants_response_cb(GtkDialog *dialog, gint id)
1235 GtkTreeIter iter;
1236 gint number;
1237 gchar *value;
1238 gchar *description;
1240 if (id == GTK_RESPONSE_HELP) {
1241 help_display();
1244 if (id == GTK_RESPONSE_ACCEPT) {
1245 if (gtk_tree_model_get_iter_first(X->constants_model, &iter)) {
1246 do {
1247 gtk_tree_model_get(X->constants_model, &iter,
1248 COLUMN_NUMBER, &number,
1249 COLUMN_VALUE, &value,
1250 COLUMN_DESCRIPTION, &description, -1);
1251 MPstr_to_num(value, DEC, v->MPcon_vals[number]);
1252 STRNCPY(v->con_names[number], description, MAXLINE - 1);
1253 put_constant(number, value, description);
1254 } while (gtk_tree_model_iter_next(X->constants_model, &iter));
1258 gtk_widget_hide(GTK_WIDGET(dialog));
1262 static void
1263 edit_functions_response_cb(GtkDialog *dialog, gint id)
1265 GtkTreeIter iter;
1266 gint number;
1267 gchar *value;
1268 gchar *description;
1270 if (id == GTK_RESPONSE_HELP) {
1271 help_display();
1274 if (id == GTK_RESPONSE_ACCEPT) {
1275 if (gtk_tree_model_get_iter_first(X->functions_model, &iter)) {
1276 do {
1277 gtk_tree_model_get(X->functions_model, &iter,
1278 COLUMN_NUMBER, &number,
1279 COLUMN_VALUE, &value,
1280 COLUMN_DESCRIPTION, &description, -1);
1281 STRNCPY(v->fun_vals[number], convert(value), MAXLINE - 1);
1282 STRNCPY(v->fun_names[number], description, MAXLINE - 1);
1283 put_function(number, value, description);
1284 } while (gtk_tree_model_iter_next(X->functions_model, &iter));
1288 gtk_widget_hide(GTK_WIDGET(dialog));
1292 /*ARGSUSED*/
1293 static GtkTreeModel *
1294 create_cf_model(enum menu_type mtype, GtkWidget *dialog)
1296 gint i = 0;
1297 GtkListStore *model;
1298 GtkTreeIter iter;
1299 gchar *value, *description;
1301 model = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
1302 G_TYPE_STRING, G_TYPE_BOOLEAN);
1303 for (i = 0; i < MAXCONFUN; i++) {
1304 gtk_list_store_append(model, &iter);
1306 if (mtype == M_CON) {
1307 value = g_strdup(make_number(v->MPcon_vals[i], DEC, TRUE));
1308 description = g_strdup(v->con_names[i]);
1309 } else {
1310 value = g_strdup(v->fun_vals[i]);
1311 description = g_strdup(v->fun_names[i]);
1313 gtk_list_store_set(model, &iter,
1314 COLUMN_NUMBER, i,
1315 COLUMN_VALUE, value,
1316 COLUMN_DESCRIPTION, description,
1317 COLUMN_EDITABLE, TRUE,
1318 -1);
1321 return(GTK_TREE_MODEL(model));
1325 static void
1326 update_mode_widgets(int mode)
1328 GtkWidget *widget;
1330 switch (mode) {
1331 case FINANCIAL:
1332 widget = GET_WIDGET("view_financial_menu");
1333 break;
1335 case SCIENTIFIC:
1336 widget = GET_WIDGET("view_scientific_menu");
1337 break;
1339 case ADVANCED:
1340 widget = GET_WIDGET("view_advanced_menu");
1341 break;
1343 default:
1344 widget = GET_WIDGET("view_advanced_menu");
1345 break;
1347 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
1351 void
1352 update_accuracy(int accuracy)
1354 GtkWidget *label;
1355 char text[MAXLINE];
1356 char *desc, *current, *tooltip;
1358 SNPRINTF(text, MAXLINE, _("Other (%d) ..."), accuracy);
1359 label = gtk_bin_get_child(GTK_BIN(GET_WIDGET("acc_item_other")));
1360 gtk_label_set_text(GTK_LABEL(label), text);
1362 desc = g_strdup_printf(ngettext("Set accuracy from 0 to %d numeric places.",
1363 "Set accuracy from 0 to %d numeric places.",
1364 MAXACC),
1365 MAXACC);
1367 /* Translator: This refers to the current accuracy setting */
1368 current = g_strdup_printf(ngettext("Currently set to %d places.",
1369 "Currently set to %d places.",
1370 accuracy),
1371 accuracy);
1372 tooltip = g_strdup_printf ("%s %s [A]", desc, current);
1373 gtk_widget_set_tooltip_text (GET_WIDGET("calc_accuracy_button"), tooltip);
1374 g_free(desc);
1375 g_free(current);
1376 g_free(tooltip);
1380 static void
1381 set_accuracy_toggle(int val)
1383 char name[MAXLINE];
1384 GtkWidget *radio;
1386 if (val >= 0 && val <= 9) {
1387 SNPRINTF(name, MAXLINE, "acc_item%d", val);
1388 radio = GET_WIDGET(name);
1389 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(radio), TRUE);
1394 static void
1395 set_show_tsep_toggle(int state)
1397 GtkWidget *mi;
1399 mi = GET_WIDGET("show_thousands_separator_menu");
1400 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), state);
1404 static void
1405 set_show_bitcalculating_toggle(int state)
1407 GtkWidget *mi;
1409 mi = GET_WIDGET("show_bitcalculating_menu");
1410 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), state);
1414 static void
1415 set_show_zeroes_toggle(int state)
1417 GtkWidget *menu;
1419 v->doing_mi = 1; /* Hack to get mstz_proc() to just return. */
1420 menu = GET_WIDGET("show_trailing_zeroes_menu");
1421 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), state);
1422 menu = GET_WIDGET("acc_trailing_zeroes_item");
1423 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), state);
1424 v->doing_mi = 0;
1428 static void
1429 reset_mode_display(int toclear)
1431 GtkWidget *radio;
1433 /* If the new mode is BASIC, then we need to dismiss the memory register
1434 * window (if it's being displayed), as there is no way to interact with it.
1437 radio = GET_WIDGET("show_trailing_zeroes_menu");
1438 gtk_widget_set_sensitive(radio, v->modetype == SCIENTIFIC);
1439 radio = GET_WIDGET("show_registers_menu");
1440 gtk_widget_set_sensitive(radio, v->modetype != BASIC);
1442 if (v->modetype == BASIC) {
1443 gtk_dialog_response(GTK_DIALOG(X->rframe), GTK_RESPONSE_CLOSE);
1446 switch (v->syntax) {
1447 case npa:
1448 show_display(v->MPdisp_val);
1449 break;
1451 case exprs:
1452 refresh_display();
1453 break;
1456 make_registers();
1457 do_mode(toclear);
1461 static void
1462 save_win_position()
1464 char intval[MAXLINE];
1465 int x, y;
1467 (void) gdk_window_get_origin(X->kframe->window, &x, &y);
1468 SNPRINTF(intval, MAXLINE, "%d", x);
1469 put_resource(R_XPOS, intval);
1470 SNPRINTF(intval, MAXLINE, "%d", y);
1471 put_resource(R_YPOS, intval);
1475 static void
1476 change_mode(int mode)
1478 X->mode = mode;
1479 v->modetype = mode;
1480 set_item(BASEITEM, DEC);
1481 grey_buttons(v->base);
1482 set_item(NUMITEM, FIX);
1483 v->accuracy = 9;
1484 set_accuracy_toggle(v->accuracy);
1486 v->show_tsep = FALSE;
1487 set_show_tsep_toggle(v->show_tsep);
1488 put_resource(R_TSEP, set_bool(v->show_tsep == TRUE));
1490 v->show_zeroes = FALSE;
1491 set_show_zeroes_toggle(v->show_zeroes);
1492 put_resource(R_ZEROES, set_bool(v->show_zeroes == TRUE));
1494 reset_mode_display(TRUE);
1498 /*ARGSUSED*/
1499 static void
1500 cm_response_cb(GtkDialog *dialog, int response)
1502 if (response == GTK_RESPONSE_ACCEPT) {
1503 change_mode(X->mode);
1504 } else {
1505 update_mode_widgets(v->modetype);
1508 gtk_widget_destroy(X->cm_dialog);
1509 X->cm_dialog = NULL;
1513 /*ARGSUSED*/
1514 static void
1515 cm_warning_cb(GtkWidget *button)
1517 v->warn_change_mode = !gtk_toggle_button_get_active(
1518 GTK_TOGGLE_BUTTON(button));
1522 static void
1523 create_change_mode_dialog()
1525 GtkWidget *check, *button;
1527 X->cm_dialog = gtk_message_dialog_new(GTK_WINDOW(X->kframe),
1528 GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
1529 GTK_MESSAGE_WARNING,
1530 GTK_BUTTONS_CANCEL,
1531 "%s",
1532 _("Changing Modes Clears Calculation"));
1533 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(X->cm_dialog),
1534 "%s",
1535 _("When you change modes, the current calculation "
1536 "will be cleared, and the base will be reset to "
1537 "decimal."));
1539 check = gtk_check_button_new_with_mnemonic(_("_Do not warn me again"));
1540 gtk_box_pack_end(GTK_BOX(GTK_MESSAGE_DIALOG(X->cm_dialog)->label->parent),
1541 check, FALSE, FALSE, 0);
1542 gtk_widget_show(check);
1544 button = gtk_dialog_add_button(GTK_DIALOG(X->cm_dialog),
1545 _("C_hange Mode"), GTK_RESPONSE_ACCEPT);
1546 gtk_button_set_image(GTK_BUTTON(button),
1547 gtk_image_new_from_stock(GTK_STOCK_REFRESH,
1548 GTK_ICON_SIZE_BUTTON));
1549 /* Set default focus on affirmative button */
1550 gtk_widget_grab_focus(button);
1552 gtk_dialog_set_alternative_button_order(GTK_DIALOG(X->cm_dialog),
1553 GTK_RESPONSE_ACCEPT,
1554 GTK_RESPONSE_CANCEL,
1555 -1);
1557 g_signal_connect((gpointer) check, "toggled",
1558 G_CALLBACK(cm_warning_cb), NULL);
1559 g_signal_connect(X->cm_dialog, "response",
1560 G_CALLBACK(cm_response_cb), NULL);
1562 gtk_window_set_position(GTK_WINDOW(X->cm_dialog),
1563 GTK_WIN_POS_CENTER_ON_PARENT);
1567 void
1568 show_change_mode_dialog()
1570 if (X->cm_dialog == NULL) {
1571 create_change_mode_dialog();
1574 gtk_window_present(GTK_WINDOW(X->cm_dialog));
1578 /*ARGSUSED*/
1579 static gboolean
1580 bit_toggled(GtkWidget *event_box, GdkEventButton *event)
1582 double number;
1583 unsigned long long lval;
1584 int n, MP1[MP_SIZE], index;
1586 index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(event_box),
1587 "widget_index"));
1588 n = MAXBITS - index - 1;
1589 MPstr_to_num(v->display, v->base, MP1);
1590 mpcmd(MP1, &number);
1591 lval = (long long) number;
1593 if (lval & (1LL << n)) {
1594 lval &= ~(1LL << n);
1595 gtk_label_set_text(GTK_LABEL(X->bits[index]), " 0");
1596 } else {
1597 lval |= (1LL << n);
1598 gtk_label_set_text(GTK_LABEL(X->bits[index]), " 1");
1601 number = (double) lval;
1602 mpcdm(&number, v->MPdisp_val);
1603 show_display(v->MPdisp_val);
1604 v->toclear = 0;
1606 return(TRUE);
1610 void
1611 set_redo_and_undo_button_sensitivity(int undo, int redo)
1613 gtk_widget_set_sensitive(X->undo, undo);
1614 gtk_widget_set_sensitive(X->redo, redo);
1618 static void
1619 menu_item_select_cb(GtkWidget *widget)
1621 GtkStatusbar *statusbar = GTK_STATUSBAR(X->statusbar);
1622 gchar *tooltip;
1623 guint context_id;
1625 context_id = gtk_statusbar_get_context_id(statusbar, "menuhelp");
1627 tooltip = (gchar *)g_object_get_data(G_OBJECT(widget), "tooltip");
1628 if (tooltip) {
1629 gtk_statusbar_push(statusbar, context_id, tooltip);
1634 /*ARGSUSED*/
1635 static void
1636 menu_item_deselect_cb(GtkWidget *widget)
1638 GtkStatusbar *statusbar = GTK_STATUSBAR(X->statusbar);
1639 guint context_id;
1641 context_id = gtk_statusbar_get_context_id(statusbar, "menuhelp");
1642 gtk_statusbar_pop(statusbar, context_id);
1646 static void
1647 update_copy_paste_status()
1649 gboolean can_paste;
1650 gboolean can_copy;
1652 can_copy = gtk_text_buffer_get_has_selection(X->display_buffer);
1653 can_paste = gtk_clipboard_wait_is_text_available(
1654 gtk_clipboard_get(X->clipboard_atom));
1656 gtk_widget_set_sensitive(GTK_WIDGET(X->copy), can_copy);
1657 gtk_widget_set_sensitive(GTK_WIDGET(X->paste), can_paste);
1661 static void
1662 set_menubar_tooltip(gchar *menu_name)
1664 GtkWidget *menu;
1665 gchar *tooltip;
1667 menu = GET_WIDGET(menu_name);
1668 tooltip = gtk_widget_get_tooltip_text(menu);
1669 g_object_set_data(G_OBJECT(menu), "tooltip", tooltip);
1670 gtk_widget_set_tooltip_text(menu, NULL);
1674 static void
1675 update_memory_menus()
1677 char mstr[MAXLINE];
1678 int i;
1680 for (i = 0; i < MAXREGS; i++) {
1681 SNPRINTF(mstr, MAXLINE, "<span weight=\"bold\">%s_%d:</span> %s",
1682 /* translators: R is the short form of register used inter alia
1683 in popup menus */
1684 _("R"), i, make_number(v->MPmvals[i], v->base, TRUE));
1685 update_popup_label(X->memory_store_items[i], mstr);
1686 update_popup_label(X->memory_recall_items[i], mstr);
1687 update_popup_label(X->memory_exchange_items[i], mstr);
1692 /*ARGSUSED*/
1693 static void
1694 mem_response(GtkDialog *dialog, int response)
1696 set_memory_toggle(FALSE);
1697 put_resource(R_REGS, "false");
1698 gtk_widget_hide(X->rframe);
1702 /*ARGSUSED*/
1703 static gboolean
1704 dismiss_aframe(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1706 gtk_widget_hide(widget);
1708 return(TRUE);
1712 /*ARGSUSED*/
1713 static gboolean
1714 dismiss_rframe(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1716 gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_DELETE_EVENT);
1718 return(TRUE);
1722 /*ARGSUSED*/
1723 static gboolean
1724 dismiss_spframe(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1726 gtk_widget_hide(X->spframe);
1728 return(TRUE);
1732 /*ARGSUSED*/
1733 void
1734 disp_cb(GtkWidget *widget)
1736 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
1737 do_numtype((enum num_type) g_object_get_data(G_OBJECT(widget), "response_id"));
1741 void
1742 get_constant(int n)
1744 char nkey[MAXLINE], *nline;
1745 char vkey[MAXLINE], *vline;
1747 SNPRINTF(nkey, MAXLINE, "/apps/%s/constant%1dname", v->appname, n);
1748 if ((nline = gconf_client_get_string(X->client, nkey, NULL)) == NULL) {
1749 return;
1752 SNPRINTF(vkey, MAXLINE, "/apps/%s/constant%1dvalue", v->appname, n);
1753 if ((vline = gconf_client_get_string(X->client, vkey, NULL)) == NULL) {
1754 return;
1757 MPstr_to_num(vline, DEC, v->MPcon_vals[n]);
1758 STRNCPY(v->con_names[n], nline, MAXLINE - 1);
1762 static void
1763 get_display() /* The Copy function key has been pressed. */
1765 gchar *string = NULL;
1766 GtkTextIter start, end;
1768 if (gtk_text_buffer_get_selection_bounds(X->display_buffer, &start, &end) == TRUE) {
1769 string = gtk_text_buffer_get_text(X->display_buffer, &start, &end, FALSE);
1770 } else {
1771 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
1772 string = gtk_text_buffer_get_text(X->display_buffer, &start, &end, FALSE);
1775 if (v->shelf != NULL) {
1776 free(v->shelf);
1778 v->shelf = g_locale_from_utf8(string, strlen(string), NULL, NULL, NULL);
1779 g_free(string);
1781 gtk_clipboard_set_text(gtk_clipboard_get(X->clipboard_atom), v->shelf, -1);
1785 void
1786 get_function(int n)
1788 char nkey[MAXLINE], *nline;
1789 char vkey[MAXLINE], *vline;
1791 SNPRINTF(nkey, MAXLINE, "/apps/%s/function%1dname", v->appname, n);
1792 if ((nline = gconf_client_get_string(X->client, nkey, NULL)) == NULL) {
1793 return;
1796 SNPRINTF(vkey, MAXLINE, "/apps/%s/function%1dvalue", v->appname, n);
1797 if ((vline = gconf_client_get_string(X->client, vkey, NULL)) == NULL) {
1798 return;
1801 STRNCPY(v->fun_vals[n], convert(vline), MAXLINE - 1);
1802 STRNCPY(v->fun_names[n], nline, MAXLINE - 1);
1806 char *
1807 get_localized_numeric_point(void)
1809 const char *decimal_point;
1811 decimal_point = localeconv()->decimal_point;
1813 return(g_locale_to_utf8(decimal_point, -1, NULL, NULL, NULL));
1818 get_menu_entry(enum menu_type mtype, int offset)
1820 switch (mtype) {
1821 case M_ACC :
1822 return(offset + '0');
1824 case M_LSHF :
1825 case M_RSHF :
1826 return((offset < 10) ? offset + '0' : offset + 'A' - 10);
1828 default:
1829 FPRINTF(stderr, "need to handle menu type %d\n", mtype);
1832 return(0);
1836 /*ARGSUSED*/
1837 static void
1838 get_proc(GtkClipboard *clipboard, const gchar *buffer, gpointer data)
1840 gchar *dstp, *end_buffer, *srcp, *text;
1842 if (buffer == NULL) {
1843 return;
1846 end_buffer = (gchar *) (buffer + strlen(buffer));
1847 text = malloc(strlen(buffer)+1);
1849 srcp = (gchar *) buffer;
1850 dstp = text;
1851 while (srcp < end_buffer) {
1853 /* If the clipboard buffer contains any occurances of the "thousands
1854 * separator", remove them.
1856 if (*srcp == v->tsep[0]) {
1857 if (strstr(srcp, v->tsep) == srcp) {
1858 srcp += strlen(v->tsep);
1859 } else {
1860 *dstp++ = *srcp++;
1862 } else {
1864 /* If an "A", "B", "C", "D" or "F" character is encountered, it
1865 * will be converted to its lowercase equivalent. If an "E" is
1866 * found, and the next character is a "-" or a "+", then it
1867 * remains as an upper case "E" (it's assumed to be a possible
1868 * exponential number), otherwise its converted to a lower case
1869 * "e". See bugs #455889 and #469245 for more details.
1871 switch (*srcp) {
1872 case 'A':
1873 case 'B':
1874 case 'C':
1875 case 'D':
1876 case 'F': *dstp++ = tolower(*srcp);
1877 srcp++;
1878 break;
1880 case 'E': if (srcp < (end_buffer-1)) {
1881 if (*(srcp+1) != '-' &&
1882 *(srcp+1) != '+') {
1883 *dstp++ = tolower(*srcp);
1884 srcp++;
1885 break;
1888 /*FALLTHROUGH*/
1890 default: *dstp++ = *srcp++;
1894 *dstp++ = '\0';
1896 switch (v->syntax) {
1897 case npa: {
1898 int ret = lr_parse((char *) text, v->MPdisp_val);
1900 if (!ret) {
1901 show_display(v->MPdisp_val);
1902 } else {
1903 update_statusbar(_("Clipboard contained malformed calculation"),
1904 "gtk-dialog-error");
1906 break;
1909 case exprs:
1910 exp_append((char *) text);
1911 refresh_display();
1912 break;
1914 default:
1915 assert(0);
1917 free(text);
1921 /* Get gcalctool resource from merged database. */
1923 char *
1924 get_resource(enum res_type rtype)
1926 char cstr[MAXLINE], key[MAXLINE];
1928 STRNCPY(key, calc_res[(int) rtype], MAXLINE - 1);
1929 SNPRINTF(cstr, MAXLINE, "/apps/%s/%s", v->appname, key);
1931 return(gconf_client_get_string(X->client, cstr, NULL));
1935 void
1936 grey_buttons(enum base_type base)
1938 int i, baseval = basevals[(int) base];
1940 for (i = 0; i < 16; i++) {
1941 gtk_widget_set_sensitive(X->digit_buttons[i], i < baseval);
1946 static void
1947 handle_selection() /* Handle the GET function key being pressed. */
1949 gtk_clipboard_request_text(gtk_clipboard_get(X->clipboard_atom),
1950 get_proc, NULL);
1954 /*ARGSUSED*/
1955 static void
1956 popup_paste_cb(GtkMenuItem *menuitem)
1958 handle_selection();
1962 /*ARGSUSED*/
1963 static void
1964 for_each_menu(GtkWidget *widget, gpointer data)
1966 /* Find the "Paste" entry and activate it (see bug #317786). */
1967 if (strcmp(G_OBJECT_TYPE_NAME(widget), "GtkImageMenuItem") == 0) {
1968 GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget));
1970 if (strcmp(gtk_label_get_text(GTK_LABEL(label)), _("Paste")) == 0) {
1971 if (gtk_clipboard_wait_is_text_available(
1972 gtk_clipboard_get(X->clipboard_atom))) {
1973 gtk_widget_set_sensitive(GTK_WIDGET(widget), TRUE);
1974 g_signal_connect(GTK_OBJECT(widget), "activate",
1975 G_CALLBACK(popup_paste_cb), NULL);
1982 /*ARGSUSED*/
1983 static void
1984 buffer_populate_popup_cb(GtkTextView *textview, GtkMenu *menu)
1986 gtk_container_foreach(GTK_CONTAINER(menu), for_each_menu, NULL);
1990 /*ARGSUSED*/
1991 static void
1992 help_cb(GtkWidget *widget)
1994 if (v->started)
1995 help_display();
1999 /*ARGSUSED*/
2000 void
2001 hyp_cb(GtkWidget *widget)
2003 v->hyperbolic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2007 /*ARGSUSED*/
2008 void
2009 inv_cb(GtkWidget *button)
2011 v->inverse = !v->inverse;
2015 static gboolean
2016 check_for_localized_numeric_point(int keyval)
2018 gchar outbuf[10]; /* Minumum size 6. */
2019 gunichar ch;
2021 ch = gdk_keyval_to_unicode(keyval);
2022 g_return_val_if_fail(g_unichar_validate(ch), FALSE);
2024 outbuf[g_unichar_to_utf8(ch, outbuf)] = '\0';
2026 return(strcmp(outbuf, X->lnp) == 0);
2030 void
2031 get_expr_from_display()
2033 char *text;
2034 GtkTextIter start, end;
2036 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
2038 text = gtk_text_buffer_get_text(X->display_buffer,
2039 &start,
2040 &end,
2041 FALSE);
2042 exp_replace(text);
2046 void
2047 delete_from_cursor()
2049 GtkTextIter start, end, loc;
2050 gint pos;
2052 gtk_text_buffer_get_bounds(X->display_buffer, &start, &end);
2053 g_object_get(G_OBJECT(X->display_buffer), "cursor-position", &pos, NULL);
2055 gtk_text_buffer_get_iter_at_offset(X->display_buffer,
2056 &loc,
2057 pos);
2059 gtk_text_buffer_backspace(X->display_buffer, &loc, TRUE, TRUE);
2064 void
2065 insert_to_cursor(char *text)
2067 gtk_text_buffer_insert_at_cursor(X->display_buffer,
2068 text,
2069 strlen(text));
2073 /*ARGSUSED*/
2074 static gboolean
2075 display_focus_out_cb(GtkWidget *widget, GdkEventKey *event)
2077 if (v->syntax == exprs) {
2078 get_expr_from_display();
2081 return(FALSE);
2085 /*ARGSUSED*/
2086 static gboolean
2087 display_focus_in_cb(GtkWidget *widget, GdkEventKey *event)
2089 v->ghost_zero = 0;
2091 return(FALSE);
2095 /*ARGSUSED*/
2096 static void
2097 menu_pos_func(GtkMenu *menu, gint *x, gint *y,
2098 gboolean *push_in, gpointer user_data)
2100 GdkPoint *loc = (GdkPoint *) user_data;
2102 *x = loc->x;
2103 *y = loc->y;
2107 /*ARGSUSED*/
2108 static void
2109 button_cb(GtkWidget *widget, GdkEventButton *event)
2111 struct button *n;
2112 enum menu_type mtype;
2113 GtkWidget *menu;
2114 GdkPoint loc;
2116 n = (struct button *) g_object_get_data(G_OBJECT(widget), "button");
2117 mtype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "mtype"));
2119 if (mtype == M_NONE) {
2120 do_button(n, 0);
2121 } else {
2122 /* If gcalctool is being driven by gok, the on-screen keyboard
2123 * assistive technology, it's possible that the event returned by
2124 * gtk_get_current_event() is NULL. If this is the case, we need
2125 * to fudge the popping up on the menu associated with this menu
2126 * button.
2129 update_constants_menu();
2130 update_functions_menu();
2131 update_memory_menus();
2133 menu = X->menus[mtype];
2134 if (event == NULL) {
2135 gdk_window_get_origin(widget->window, &loc.x, &loc.y);
2136 loc.x += widget->allocation.x;
2137 loc.y += widget->allocation.y;
2138 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, menu_pos_func,
2139 (gpointer) &loc, 0, gtk_get_current_event_time());
2140 } else if (event->button == 1) {
2141 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
2142 event->button, event->time);
2148 /*ARGSUSED*/
2149 static void
2150 select_display_entry(int offset)
2152 GtkTextIter iter;
2154 gtk_text_buffer_get_iter_at_offset(X->display_buffer, &iter, offset);
2155 gtk_text_buffer_place_cursor(X->display_buffer, &iter);
2156 gtk_widget_grab_focus(X->display_item);
2160 /*ARGSUSED*/
2161 static gboolean
2162 kframe_key_press_cb(GtkWidget *widget, GdkEventKey *event)
2164 int i, j, state;
2165 GtkWidget *button;
2167 if (check_for_localized_numeric_point(event->keyval) == TRUE) {
2168 event->state = 0;
2169 event->keyval = GDK_KP_Decimal;
2172 state = event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK);
2174 /* Accuracy shortcuts */
2175 if (state == GDK_CONTROL_MASK && v->modetype == SCIENTIFIC)
2177 switch (event->keyval) {
2178 case GDK_0:
2179 do_accuracy(0);
2180 return (TRUE);
2181 case GDK_1:
2182 do_accuracy(1);
2183 return (TRUE);
2184 case GDK_2:
2185 do_accuracy(2);
2186 return (TRUE);
2187 case GDK_3:
2188 do_accuracy(3);
2189 return (TRUE);
2190 case GDK_4:
2191 do_accuracy(4);
2192 return (TRUE);
2193 case GDK_5:
2194 do_accuracy(5);
2195 return (TRUE);
2196 case GDK_6:
2197 do_accuracy(6);
2198 return (TRUE);
2199 case GDK_7:
2200 do_accuracy(7);
2201 return (TRUE);
2202 case GDK_8:
2203 do_accuracy(8);
2204 return (TRUE);
2205 case GDK_9:
2206 do_accuracy(9);
2207 return (TRUE);
2211 /* Connect home and end keys to move into the display entry */
2212 if (event->keyval == GDK_Home)
2214 select_display_entry(0);
2215 return (TRUE);
2216 } else if (event->keyval == GDK_End)
2218 select_display_entry(-1);
2219 return (TRUE);
2222 for (i = 0; i < NBUTTONS; i++) {
2223 button = X->buttons[i];
2225 /* Check any parent widgets are visible */
2226 if (!GTK_WIDGET_VISIBLE(gtk_widget_get_parent(button)) ||
2227 !GTK_WIDGET_VISIBLE(button) || !GTK_WIDGET_IS_SENSITIVE(button)) {
2228 continue;
2231 j = 0;
2232 while (button_widgets[i].accelerator_keys[j] != 0) {
2233 if (button_widgets[i].accelerator_keys[j] == event->keyval &&
2234 (button_widgets[i].accelerator_mods[j] & ~GDK_SHIFT_MASK) == state) {
2235 button_cb(button, NULL);
2236 return(TRUE);
2238 j++;
2242 return(FALSE);
2246 void
2247 load_resources() /* Load gconf configuration database for gcalctool. */
2249 char str[MAXLINE];
2251 SNPRINTF(str, MAXLINE, "/apps/%s", v->appname);
2252 X->client = gconf_client_get_default();
2253 gconf_client_add_dir(X->client, str, GCONF_CLIENT_PRELOAD_NONE, NULL);
2257 static char *
2258 make_hostname(Display *dpy)
2260 char client_hostname[MAXHOSTNAMELEN + 4];
2261 char hostname[MAXHOSTNAMELEN];
2262 char *display = DisplayString(dpy);
2263 char *scanner = display;
2265 GETHOSTNAME(hostname, MAXHOSTNAMELEN);
2267 while (*scanner) {
2268 scanner++;
2271 while (*scanner != ':') {
2272 scanner--;
2275 *scanner = '\0';
2277 if (strcmp(display, hostname) &&
2278 strcmp(display, "localhost") &&
2279 strcmp(display, "unix") &&
2280 strcmp(display, "")) {
2281 SPRINTF(client_hostname, " [%s] ", hostname);
2282 } else {
2283 STRCPY(client_hostname, "");
2286 *scanner = ':';
2288 return(strdup(client_hostname));
2292 /*ARGSUSED*/
2293 static gboolean
2294 mouse_button_cb(GtkWidget *widget, GdkEventButton *event)
2296 if (event->button == 2) {
2297 gtk_clipboard_request_text(gtk_clipboard_get(X->primary_atom),
2298 get_proc, NULL);
2301 return(FALSE);
2305 static void
2306 set_win_position()
2308 int intval, screen_height, screen_width;
2309 int x = 0, y = 0;
2311 screen_width = gdk_screen_get_width(gdk_screen_get_default());
2312 screen_height = gdk_screen_get_height(gdk_screen_get_default());
2314 if (get_int_resource(R_XPOS, &intval)) {
2315 x = intval;
2316 if (x < 0 || x > screen_width) {
2317 x = 0;
2321 if (get_int_resource(R_YPOS, &intval)) {
2322 y = intval;
2323 if (y < 0 || y > screen_height) {
2324 y = 0;
2328 gtk_window_move(GTK_WINDOW(X->kframe), x, y);
2331 static void
2332 show_ascii_frame() /* Display ASCII popup. */
2334 if (!GTK_WIDGET_VISIBLE(X->aframe)) {
2335 ds_position_popup(X->kframe, X->aframe, DS_POPUP_LEFT);
2337 gtk_window_set_focus(GTK_WINDOW(X->kframe), GTK_WIDGET(X->aframe_ch));
2338 gtk_widget_show(X->aframe);
2342 static void
2343 show_precision_frame() /* Display Set Precision popup. */
2345 if (!GTK_WIDGET_VISIBLE(X->spframe)) {
2346 ds_position_popup(X->kframe, X->spframe, DS_POPUP_LEFT);
2348 gtk_window_set_focus(GTK_WINDOW(X->spframe), GTK_WIDGET(X->spframe_val));
2349 gtk_widget_show(X->spframe);
2353 void
2354 make_reg(int n, char *str)
2356 gtk_entry_set_width_chars(GTK_ENTRY(X->regs[n]), strlen(str));
2357 gtk_entry_set_text(GTK_ENTRY(X->regs[n]), str);
2360 /* Handle menu bar menu selection. */
2362 /*ARGSUSED*/
2363 static void
2364 edit_cb(GtkWidget *widget)
2366 if (v->started) {
2367 update_copy_paste_status();
2372 /*ARGSUSED*/
2373 static void
2374 copy_cb(GtkWidget *widget)
2376 if (v->started) {
2377 get_display();
2382 /*ARGSUSED*/
2383 static void
2384 paste_cb(GtkWidget *widget)
2386 if (v->started) {
2387 handle_selection();
2392 /*ARGSUSED*/
2393 static void
2394 undo_cb(GtkWidget *widget)
2396 if (v->started) {
2397 perform_undo();
2398 refresh_display();
2403 /*ARGSUSED*/
2404 static void
2405 redo_cb(GtkWidget *widget)
2407 if (v->started) {
2408 perform_redo();
2409 refresh_display();
2414 /*ARGSUSED*/
2415 static void
2416 insert_ascii_cb(GtkWidget *widget)
2418 if (v->started) {
2419 show_ascii_frame();
2424 static void
2425 shift_left_cb(GtkWidget *widget)
2427 int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
2428 "shiftcount"));
2430 if (v->started) {
2431 do_button(&buttons[KEY_LEFT_SHIFT], count);
2436 static void
2437 shift_right_cb(GtkWidget *widget)
2439 int count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
2440 "shiftcount"));
2442 if (v->started) {
2443 do_button(&buttons[KEY_RIGHT_SHIFT], count);
2447 /*ARGSUSED*/
2448 static void
2449 show_bitcalculating_cb(GtkWidget *widget)
2451 if (v->started) {
2452 v->bitcalculating_mode = v->bitcalculating_mode ^ 1;
2453 set_mode(v->modetype);
2454 put_resource(R_BITCALC, Rcstr[v->bitcalculating_mode]);
2459 /*ARGSUSED*/
2460 static void
2461 show_registers_cb(GtkWidget *widget)
2463 if (v->started) {
2464 v->rstate = !v->rstate;
2465 do_memory();
2470 /*ARGSUSED*/
2471 static void
2472 arithmetic_mode_cb(GtkWidget *widget)
2474 if (!v->started) {
2475 return;
2478 /* TODO: Always do clear things when mode is changed. */
2480 v->syntax = v->syntax ^ 1;
2481 switch (v->syntax) {
2482 case npa:
2483 v->noparens = 0;
2484 MPstr_to_num("0", DEC, v->MPdisp_val);
2485 show_display(v->MPdisp_val);
2486 update_statusbar(_("Activated no operator precedence mode"), "");
2487 clear_undo_history();
2488 break;
2490 case exprs: {
2491 struct exprm_state *e = get_state();
2493 MPstr_to_num("0", DEC, e->ans);
2494 exp_del();
2495 show_display(e->ans);
2496 update_statusbar(
2497 _("Activated expression mode with operator precedence"), "");
2499 break;
2501 default:
2502 assert(0);
2504 put_resource(R_SYNTAX, Rsstr[v->syntax]);
2505 set_mode(v->modetype);
2506 gtk_text_view_set_editable(GTK_TEXT_VIEW(X->display_item),
2507 (v->syntax == exprs));
2511 /*ARGSUSED*/
2512 static void
2513 mode_radio_cb(GtkWidget *radio)
2515 int immediate = 0; /* Set if we can change mode without warning user. */
2516 int complete = 0; /* Set if the user has completed a calculation. */
2518 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(radio))) {
2519 return;
2522 if (!v->started) {
2523 return;
2526 X->mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(radio), "calcmode"));
2528 /* If the user has completed a calculation and we are going to a
2529 * new mode that is "compatible" with this one, then just change
2530 * modes. Otherwise display a dialog warning the user that the
2531 * current calculation will be cleared.
2533 * Incompatible modes are:
2535 * Scientific -> Basic
2536 * Scientific -> Advanced
2537 * Scientific -> Financial
2539 * (unless we are in Scientific mode with Decimal numeric base and Fixed).
2542 switch (v->syntax) {
2543 case npa:
2544 if (v->old_cal_value < 0 ||
2545 v->old_cal_value == KEY_CALCULATE) {
2546 complete = 1; /* Calculation is complete. */
2548 break;
2550 case exprs: {
2551 struct exprm_state *e = get_state();
2552 if (!e->expression || !strcmp(e->expression, "Ans")) {
2553 complete = 1; /* Calculation is complete. */
2558 if (complete) {
2559 if ((v->modetype != SCIENTIFIC) ||
2560 (v->dtype == FIX && v->base == DEC)) {
2561 immediate = 1;
2565 if (immediate) {
2566 v->modetype = X->mode;
2567 reset_mode_display(FALSE);
2569 } else {
2570 if (v->warn_change_mode) {
2571 show_change_mode_dialog();
2572 } else {
2573 change_mode(X->mode);
2579 /*ARGSUSED*/
2580 static void
2581 accuracy_radio_cb(GtkWidget *widget)
2583 int count;
2585 count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "accuracy"));
2587 if (v->started &&
2588 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
2589 do_accuracy(count);
2594 /*ARGSUSED*/
2595 static void
2596 accuracy_other_cb(GtkWidget *widget)
2598 if (v->started) {
2599 show_precision_frame();
2604 /*ARGSUSED*/
2605 static void
2606 show_trailing_zeroes_cb(GtkWidget *widget)
2608 if (!v->doing_mi) {
2609 v->show_zeroes = gtk_check_menu_item_get_active(
2610 GTK_CHECK_MENU_ITEM(widget));
2612 syntaxdep_show_display();
2613 put_resource(R_ZEROES, set_bool(v->show_zeroes == TRUE));
2614 make_registers();
2616 set_show_zeroes_toggle(v->show_zeroes);
2621 /*ARGSUSED*/
2622 static void
2623 quit_cb(GtkWidget *widget)
2625 save_win_position();
2626 gtk_main_quit();
2630 /*ARGSUSED*/
2631 static void
2632 spframe_cancel_cb(GtkButton *button)
2634 gtk_widget_hide(X->spframe);
2638 /*ARGSUSED*/
2639 static gboolean
2640 spframe_key_cb(GtkWidget *widget, GdkEventKey *event)
2642 if (event->keyval == GDK_minus) {
2643 update_statusbar(_("Accuracy value out of range"),
2644 "gtk-dialog-error");
2645 beep();
2646 } else if (event->keyval == GDK_Escape) {
2647 gtk_widget_hide(X->spframe);
2650 return(FALSE);
2654 /*ARGSUSED*/
2655 static void
2656 spframe_ok_cb(GtkButton *button)
2658 char intval[MAXLINE];
2659 int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(X->spframe_val));
2661 v->accuracy = val;
2663 SNPRINTF(intval, MAXLINE, "%d", v->accuracy);
2664 put_resource(R_ACCURACY, intval);
2666 update_accuracy(v->accuracy);
2667 set_accuracy_toggle(v->accuracy);
2669 make_registers();
2670 refresh_display();
2672 gtk_widget_hide(X->spframe);
2676 /*ARGSUSED*/
2677 void
2678 trig_cb(GtkWidget *widget)
2680 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2681 do_trigtype((enum trig_type) g_object_get_data(G_OBJECT(widget),
2682 "response_id"));
2686 void
2687 start_tool()
2689 v->started = 1;
2690 set_item(BASEITEM, v->base);
2691 set_item(TTYPEITEM, v->ttype);
2692 set_item(NUMITEM, v->dtype);
2694 gtk_widget_show(X->kframe);
2696 switch (v->syntax) {
2697 case npa:
2698 break;
2700 case exprs: {
2701 /* Init expression mode.
2702 * This must be executed after do_base is called at init.
2703 * FIXME: The init code here is duplicated elsewhere.
2705 struct exprm_state *e = get_state();
2707 MPstr_to_num("0", DEC, e->ans);
2708 exp_del();
2709 show_display(e->ans);
2711 break;
2713 default:
2714 assert(0);
2717 gtk_main();
2721 /*ARGSUSED*/
2722 static void
2723 show_thousands_separator_cb(GtkWidget *widget)
2725 if (!v->started) {
2726 return;
2729 v->show_tsep = !v->show_tsep;
2731 syntaxdep_show_display();
2732 put_resource(R_TSEP, set_bool(v->show_tsep == TRUE));
2733 make_registers();
2737 void
2738 win_display(enum fcp_type fcptype, int state)
2740 GtkWidget *f = NULL;
2742 if (fcptype == FCP_REG) {
2743 v->rstate = state;
2744 f = X->rframe;
2747 gtk_widget_realize(f);
2748 if (state && gdk_window_is_visible(f->window)) {
2749 gdk_window_raise(f->window);
2750 return;
2752 if (state) {
2753 if (fcptype == FCP_REG) {
2754 ds_position_popup(X->kframe, f, DS_POPUP_ABOVE);
2757 if (state) {
2758 gtk_widget_show(f);
2759 } else {
2760 gtk_widget_hide(f);
2765 /*ARGSUSED*/
2766 static void
2767 edit_constants_cb(GtkMenuItem *item)
2769 gtk_widget_show(X->con_dialog);
2773 /*ARGSUSED*/
2774 static void
2775 edit_functions_cb(GtkMenuItem *item)
2777 gtk_widget_show(X->fun_dialog);
2781 static void
2782 create_kframe()
2784 int i;
2785 char *hn, name[MAXLINE];
2786 GtkWidget *widget;
2787 PangoFontDescription *font_desc;
2788 GtkSizeGroup *size_group;
2789 GtkAccelGroup *accel_group;
2790 GdkGeometry geometry;
2791 GtkWidget *treeview;
2793 v->tool_label = NULL;
2794 if (v->titleline == NULL) {
2795 hn = make_hostname(GDK_DISPLAY());
2796 v->tool_label = malloc(MAXLINE);
2798 SNPRINTF(v->tool_label, MAXLINE, "%s %s", _("Calculator"), hn);
2799 g_free(hn);
2800 } else {
2801 read_str(&v->tool_label, v->titleline);
2805 X->ui = glade_xml_new(UI_FILE, NULL, NULL);
2806 if (X->ui == NULL) {
2807 GtkWidget *dialog;
2809 dialog = gtk_message_dialog_new(NULL, 0,
2810 GTK_MESSAGE_ERROR,
2811 GTK_BUTTONS_NONE,
2812 N_("Error loading user interface"));
2813 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
2814 N_("The user interace file %s is missing or unable to be loaded. Please check your installation."), UI_FILE);
2815 gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT, NULL);
2817 gtk_dialog_run(GTK_DIALOG(dialog));
2818 exit(0);
2821 X->kframe = GET_WIDGET("calc_window");
2823 X->aframe = GET_WIDGET("ascii_window");
2824 gtk_window_set_transient_for(GTK_WINDOW(X->aframe), GTK_WINDOW(X->kframe));
2825 X->aframe_ch = GET_WIDGET("ascii_entry");
2827 X->spframe = GET_WIDGET("precision_dialog");
2828 X->spframe_val = GET_WIDGET("spframe_spin");
2829 gtk_window_set_transient_for(GTK_WINDOW(X->spframe), GTK_WINDOW(X->kframe));
2830 gtk_spin_button_set_value(GTK_SPIN_BUTTON(X->spframe_val),
2831 (double) v->accuracy);
2832 gtk_entry_set_max_length(GTK_ENTRY(X->spframe_val), 2);
2834 X->rframe = GET_WIDGET("register_dialog");
2835 g_object_set_data(G_OBJECT(X->rframe), "rframe", X->rframe);
2836 gtk_window_set_transient_for(GTK_WINDOW(X->rframe), GTK_WINDOW(X->kframe));
2838 for (i = 0; i < MAXREGS; i++) {
2839 SNPRINTF(name, MAXLINE, "register_entry_%d", i);
2840 X->regs[i] = GET_WIDGET(name);
2841 gtk_entry_set_text(GTK_ENTRY(X->regs[i]),
2842 make_number(v->MPmvals[i], v->base, TRUE));
2845 X->con_dialog = GET_WIDGET("edit_constants_dialog");
2846 treeview = GET_WIDGET("edit_constants_treeview");
2847 gtk_dialog_set_default_response(GTK_DIALOG(X->con_dialog),
2848 GTK_RESPONSE_ACCEPT);
2849 gtk_window_set_transient_for(GTK_WINDOW(X->con_dialog),
2850 GTK_WINDOW(X->kframe));
2851 gtk_window_set_geometry_hints(GTK_WINDOW(X->con_dialog), X->con_dialog,
2852 &geometry, GDK_HINT_MIN_SIZE);
2853 X->constants_model = create_cf_model(M_CON, X->con_dialog);
2854 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), X->constants_model);
2855 gtk_tree_selection_set_mode(
2856 gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
2857 GTK_SELECTION_SINGLE);
2858 add_cf_column(GTK_TREE_VIEW(treeview), _("No."),
2859 COLUMN_NUMBER, FALSE);
2860 add_cf_column(GTK_TREE_VIEW(treeview), _("Value"),
2861 COLUMN_VALUE, TRUE);
2862 add_cf_column(GTK_TREE_VIEW(treeview), _("Description"),
2863 COLUMN_DESCRIPTION, TRUE);
2865 X->fun_dialog = GET_WIDGET("edit_functions_dialog");
2866 treeview = GET_WIDGET("edit_functions_treeview");
2867 gtk_dialog_set_default_response(GTK_DIALOG(X->fun_dialog),
2868 GTK_RESPONSE_ACCEPT);
2869 gtk_window_set_transient_for(GTK_WINDOW(X->fun_dialog),
2870 GTK_WINDOW(X->kframe));
2871 gtk_window_set_geometry_hints(GTK_WINDOW(X->fun_dialog), X->fun_dialog,
2872 &geometry, GDK_HINT_MIN_SIZE);
2873 X->functions_model = create_cf_model(M_FUN, X->fun_dialog);
2874 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), X->functions_model);
2875 gtk_tree_selection_set_mode(
2876 gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)),
2877 GTK_SELECTION_SINGLE);
2878 add_cf_column(GTK_TREE_VIEW(treeview), _("No."),
2879 COLUMN_NUMBER, FALSE);
2880 add_cf_column(GTK_TREE_VIEW(treeview), _("Value"),
2881 COLUMN_VALUE, TRUE);
2882 add_cf_column(GTK_TREE_VIEW(treeview), _("Description"),
2883 COLUMN_DESCRIPTION, TRUE);
2885 /* When connecting up signals, would ideally use autoconnect but not
2886 * sure how to get the build process working.
2887 * See http://library.gnome.org/devel/libglade/unstable and
2888 * http://www.jamesh.id.au/software/libglade/
2889 * for some information on how to get this to work
2890 * glade_xml_signal_autoconnect(X->ui);
2892 CONNECT_SIGNAL(kframe_key_press_cb);
2893 CONNECT_SIGNAL(button_cb);
2894 CONNECT_SIGNAL(menu_item_select_cb);
2895 CONNECT_SIGNAL(menu_item_deselect_cb);
2896 CONNECT_SIGNAL(mode_radio_cb);
2897 CONNECT_SIGNAL(inv_cb);
2898 CONNECT_SIGNAL(hyp_cb);
2899 CONNECT_SIGNAL(trig_cb);
2900 CONNECT_SIGNAL(base_cb);
2901 CONNECT_SIGNAL(disp_cb);
2902 CONNECT_SIGNAL(quit_cb);
2903 CONNECT_SIGNAL(edit_cb);
2904 CONNECT_SIGNAL(copy_cb);
2905 CONNECT_SIGNAL(paste_cb);
2906 CONNECT_SIGNAL(insert_ascii_cb);
2907 CONNECT_SIGNAL(undo_cb);
2908 CONNECT_SIGNAL(redo_cb);
2909 CONNECT_SIGNAL(help_cb);
2910 CONNECT_SIGNAL(about_cb);
2911 CONNECT_SIGNAL(show_trailing_zeroes_cb);
2912 CONNECT_SIGNAL(show_thousands_separator_cb);
2913 CONNECT_SIGNAL(show_bitcalculating_cb);
2914 CONNECT_SIGNAL(show_registers_cb);
2915 CONNECT_SIGNAL(accuracy_radio_cb);
2916 CONNECT_SIGNAL(accuracy_other_cb);
2917 CONNECT_SIGNAL(arithmetic_mode_cb);
2918 CONNECT_SIGNAL(mouse_button_cb);
2919 CONNECT_SIGNAL(display_focus_in_cb);
2920 CONNECT_SIGNAL(display_focus_out_cb);
2921 /* Detect when populating the right-click menu to enable pasting */
2922 CONNECT_SIGNAL(buffer_populate_popup_cb);
2923 CONNECT_SIGNAL(shift_left_cb);
2924 CONNECT_SIGNAL(shift_right_cb);
2925 CONNECT_SIGNAL(bit_toggled);
2926 CONNECT_SIGNAL(dismiss_aframe);
2927 CONNECT_SIGNAL(aframe_ok_cb);
2928 CONNECT_SIGNAL(aframe_cancel_cb);
2929 CONNECT_SIGNAL(aframe_key_cb);
2930 CONNECT_SIGNAL(dismiss_spframe);
2931 CONNECT_SIGNAL(spframe_ok_cb);
2932 CONNECT_SIGNAL(spframe_cancel_cb);
2933 CONNECT_SIGNAL(spframe_key_cb);
2934 CONNECT_SIGNAL(dismiss_rframe);
2935 CONNECT_SIGNAL(mem_response);
2936 CONNECT_SIGNAL(edit_constants_cb);
2937 CONNECT_SIGNAL(edit_functions_cb);
2938 CONNECT_SIGNAL(edit_constants_response_cb);
2939 CONNECT_SIGNAL(edit_functions_response_cb);
2941 widget = GET_WIDGET("kvbox");
2942 gtk_widget_set_direction(widget, GTK_TEXT_DIR_LTR );
2944 X->menubar = GET_WIDGET("menubar");
2946 gtk_widget_set_sensitive(GET_WIDGET("show_registers_menu"),
2947 (v->modetype != BASIC));
2949 X->scrolledwindow = GET_WIDGET("display_scroll"),
2951 X->display_item = GET_WIDGET("displayitem"),
2952 X->display_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(X->display_item));
2953 gtk_widget_ensure_style(X->display_item);
2954 font_desc = pango_font_description_copy(X->display_item->style->font_desc);
2955 pango_font_description_set_size(font_desc, 16 * PANGO_SCALE);
2956 gtk_widget_modify_font(X->display_item, font_desc);
2957 pango_font_description_free(font_desc);
2958 gtk_widget_set_name(X->display_item, "displayitem");
2959 gtk_text_view_set_editable(GTK_TEXT_VIEW(X->display_item),
2960 (v->syntax == exprs));
2962 atk_object_set_role(gtk_widget_get_accessible(X->display_item),
2963 ATK_ROLE_EDITBAR);
2964 set_display("0.00", FALSE);
2966 gtk_widget_realize(X->kframe);
2967 gtk_window_set_title(GTK_WINDOW(X->kframe), _(v->tool_label));
2968 set_win_position();
2970 X->bas_panel = GET_WIDGET("basic_panel");
2971 X->sci_panel = GET_WIDGET("scientific_panel");
2972 X->adv_panel = GET_WIDGET("advanced_panel");
2973 X->fin_panel = GET_WIDGET("financial_panel");
2974 gtk_widget_set_direction(X->fin_panel, GTK_TEXT_DIR_LTR);
2976 size_group = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
2977 for (i = 0; i < NBUTTONS; i++) {
2978 SNPRINTF(name, MAXLINE, "calc_%s_button",
2979 button_widgets[i].widget_name);
2980 X->buttons[i] = GET_WIDGET(name);
2981 assert(X->buttons[i] != NULL);
2983 gtk_size_group_add_widget(size_group, X->buttons[i]);
2985 g_object_set_data(G_OBJECT(X->buttons[i]), "button",
2986 &buttons[button_widgets[i].key]);
2987 g_object_set_data(G_OBJECT(X->buttons[i]), "mtype",
2988 GINT_TO_POINTER(button_widgets[i].mtype));
2991 for (i = 0; i < 16; i++) {
2992 SNPRINTF(name, MAXLINE, "calc_%x_button", i);
2993 X->digit_buttons[i] = GET_WIDGET(name);
2996 SNPRINTF(name, MAXLINE, "calc_%d_button", i);
2997 X->clear_buttons[0] = GET_WIDGET("calc_clear_simple_button");
2998 X->clear_buttons[1] = GET_WIDGET("calc_clear_advanced_button");
3000 X->mode_panel = GET_WIDGET("mode_panel");
3001 X->trig[0] = GET_WIDGET("degrees_radio");
3002 X->trig[1] = GET_WIDGET("gradians_radio");
3003 X->trig[2] = GET_WIDGET("radians_radio");
3004 X->base[0] = GET_WIDGET("binary_radio");
3005 X->base[1] = GET_WIDGET("octal_radio");
3006 X->base[2] = GET_WIDGET("decimal_radio");
3007 X->base[3] = GET_WIDGET("hexadecimal_radio");
3008 X->disp[0] = GET_WIDGET("engineering_radio");
3009 X->disp[1] = GET_WIDGET("fixed_point_radio");
3010 X->disp[2] = GET_WIDGET("scientific_radio");
3011 X->inv = GET_WIDGET("inverse_check");
3012 X->hyp = GET_WIDGET("hyperbolic_check");
3013 for (i = 0; i < 3; i++)
3014 g_object_set_data(G_OBJECT(X->trig[i]),
3015 "response_id", GINT_TO_POINTER(i));
3016 for (i = 0; i < 4; i++)
3017 g_object_set_data(G_OBJECT(X->base[i]),
3018 "response_id", GINT_TO_POINTER(i));
3019 for (i = 0; i < 3; i++)
3020 g_object_set_data(G_OBJECT(X->disp[i]),
3021 "response_id", GINT_TO_POINTER(i));
3023 X->bit_panel = GET_WIDGET("bit_panel");
3024 //gtk_widget_set_direction(table, GTK_TEXT_DIR_LTR);
3025 for (i = 0; i < MAXBITS; i++)
3027 SNPRINTF(name, MAXLINE, "bit_label_%d", i);
3028 X->bits[i] = GET_WIDGET(name);
3029 SNPRINTF(name, MAXLINE, "bit_eventbox_%d", i);
3030 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
3031 "widget_index", GINT_TO_POINTER(i));
3034 grey_buttons(v->base);
3035 if (v->modetype == BASIC) {
3036 gtk_window_set_focus(GTK_WINDOW(X->kframe),
3037 GTK_WIDGET(X->clear_buttons[0]));
3038 } else {
3039 gtk_window_set_focus(GTK_WINDOW(X->kframe),
3040 GTK_WIDGET(X->clear_buttons[1]));
3043 X->statusbar = GET_WIDGET("statusbar");
3044 X->status_image = gtk_image_new_from_stock("", GTK_ICON_SIZE_BUTTON);
3045 gtk_widget_show(X->status_image);
3046 gtk_box_pack_start(GTK_BOX(X->statusbar), X->status_image, FALSE, TRUE, 0);
3048 update_mode_widgets(v->modetype);
3050 /* Use loaded Arithmetic Precedence mode setting. */
3051 if (v->syntax == exprs) {
3052 widget = GET_WIDGET("arithmetic_precedence_menu");
3053 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
3054 } else {
3055 widget = GET_WIDGET("ltr_precedence_menu");
3056 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
3059 X->undo = GET_WIDGET("undo_menu");
3060 X->redo = GET_WIDGET("redo_menu");
3061 X->copy = GET_WIDGET("copy_menu");
3062 X->paste = GET_WIDGET("paste_menu");
3064 /* Make menu tooltips displayed in the status bar */
3065 set_menubar_tooltip("quit_menu");
3066 set_menubar_tooltip("copy_menu");
3067 set_menubar_tooltip("paste_menu");
3068 set_menubar_tooltip("insert_ascii_menu");
3069 set_menubar_tooltip("undo_menu");
3070 set_menubar_tooltip("redo_menu");
3071 set_menubar_tooltip("view_basic_menu");
3072 set_menubar_tooltip("view_advanced_menu");
3073 set_menubar_tooltip("view_financial_menu");
3074 set_menubar_tooltip("view_scientific_menu");
3075 set_menubar_tooltip("show_trailing_zeroes_menu");
3076 set_menubar_tooltip("show_thousands_separator_menu");
3077 set_menubar_tooltip("show_bitcalculating_menu");
3078 set_menubar_tooltip("show_registers_menu");
3079 set_menubar_tooltip("ltr_precedence_menu");
3080 set_menubar_tooltip("arithmetic_precedence_menu");
3081 set_menubar_tooltip("help_menu");
3082 set_menubar_tooltip("about_menu");
3084 set_redo_and_undo_button_sensitivity(0, 0);
3086 /* Connect up menu callbacks */
3087 for (i = 1; i < 16; i++) {
3088 SNPRINTF(name, MAXLINE, "shift_left%d_menu", i);
3089 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
3090 "shiftcount", GINT_TO_POINTER(i));
3091 SNPRINTF(name, MAXLINE, "shift_right%d_menu", i);
3092 g_object_set_data(G_OBJECT(GET_WIDGET(name)),
3093 "shiftcount", GINT_TO_POINTER(i));
3095 g_object_set_data(G_OBJECT(GET_WIDGET("view_basic_menu")),
3096 "calcmode", GINT_TO_POINTER(BASIC));
3097 g_object_set_data(G_OBJECT(GET_WIDGET("view_advanced_menu")),
3098 "calcmode", GINT_TO_POINTER(ADVANCED));
3099 g_object_set_data(G_OBJECT(GET_WIDGET("view_financial_menu")),
3100 "calcmode", GINT_TO_POINTER(FINANCIAL));
3101 g_object_set_data(G_OBJECT(GET_WIDGET("view_scientific_menu")),
3102 "calcmode", GINT_TO_POINTER(SCIENTIFIC));
3104 /* Make shortcuts for accuracy menus */
3105 accel_group = gtk_accel_group_new();
3106 gtk_window_add_accel_group(GTK_WINDOW(X->kframe), accel_group);
3107 for (i = 0; i < 10; i++) {
3108 SNPRINTF(name, MAXLINE, "acc_item%d", i);
3109 widget = GET_WIDGET(name);
3110 g_object_set_data(G_OBJECT(widget), "accuracy", GINT_TO_POINTER(i));
3115 static GtkWidget *
3116 create_menu_item_with_markup(char *label, int menu_no, int index)
3118 GtkWidget *accel_label;
3119 GtkWidget *menu_item;
3121 accel_label = gtk_label_new(NULL);
3122 gtk_label_set_markup_with_mnemonic(GTK_LABEL(accel_label), label);
3123 gtk_misc_set_alignment(GTK_MISC(accel_label), 0.0, 0.5);
3124 menu_item = gtk_menu_item_new();
3125 gtk_container_add(GTK_CONTAINER(menu_item), accel_label);
3126 gtk_widget_show(accel_label);
3127 gtk_widget_show(menu_item);
3129 g_object_set_data(G_OBJECT(menu_item), "button", X->mrec[menu_no]);
3130 gtk_menu_shell_append(GTK_MENU_SHELL(X->menus[menu_no]), menu_item);
3132 g_signal_connect(G_OBJECT(menu_item), "activate",
3133 G_CALLBACK(menu_proc_cb), GINT_TO_POINTER(index));
3135 return menu_item;
3139 void
3140 make_frames()
3142 int i;
3144 X->clipboard_atom = gdk_atom_intern("CLIPBOARD", FALSE);
3145 X->primary_atom = gdk_atom_intern("PRIMARY", FALSE);
3146 create_kframe(); /* Create main gcalctool window. */
3147 set_mode(v->modetype);
3149 X->menus[M_ACC] = GET_WIDGET("accuracy_popup");
3150 X->mrec[M_ACC] = &buttons[KEY_SET_ACCURACY];
3151 update_accuracy(v->accuracy);
3152 X->menus[M_LSHF] = GET_WIDGET("left_shift_popup");
3153 X->mrec[M_LSHF] = &buttons[KEY_LEFT_SHIFT];
3154 X->menus[M_RSHF] = GET_WIDGET("right_shift_popup");
3155 X->mrec[M_RSHF] = &buttons[KEY_RIGHT_SHIFT];
3157 X->menus[M_CON] = GET_WIDGET("constants_popup");
3158 X->mrec[M_CON] = &buttons[KEY_CONSTANT];
3159 X->menus[M_FUN] = GET_WIDGET("functions_popup");
3160 X->mrec[M_FUN] = &buttons[KEY_FUNCTION];
3161 for (i = 0; i < MAXCONFUN; i++) {
3162 X->constant_menu_items[i] = create_menu_item_with_markup("", M_CON, i);
3163 X->function_menu_items[i] = create_menu_item_with_markup("", M_FUN, i);
3166 X->menus[M_STO] = GET_WIDGET("memory_store_popup");
3167 X->mrec[M_STO] = &buttons[KEY_STORE];
3168 X->menus[M_RCL] = GET_WIDGET("memory_recall_popup");
3169 X->mrec[M_RCL] = &buttons[KEY_RECALL];
3170 X->menus[M_EXCH] = GET_WIDGET("memory_exchange_popup");
3171 X->mrec[M_EXCH] = &buttons[KEY_EXCHANGE];
3172 for (i = 0; i < MAXREGS; i++) {
3173 // FIXME: These labels should be replaced with GtkTreeModel labels
3174 X->memory_store_items[i] = create_menu_item_with_markup("", M_STO, i);
3175 X->memory_recall_items[i] = create_menu_item_with_markup("", M_RCL, i);
3176 X->memory_exchange_items[i] = create_menu_item_with_markup("", M_EXCH, i);
3179 // This matches existing behaviour we can't do in Glade. But is it needed?
3180 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_ACC]), 1);
3181 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_LSHF]), 1);
3182 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_RSHF]), 1);
3183 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_CON]), 1);
3184 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_FUN]), 1);
3185 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_STO]), 1);
3186 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_RCL]), 1);
3187 gtk_container_set_border_width(GTK_CONTAINER(X->menus[M_EXCH]), 1);
3189 set_accuracy_toggle(v->accuracy);
3190 set_show_tsep_toggle(v->show_tsep);
3191 set_show_zeroes_toggle(v->show_zeroes);
3192 set_show_bitcalculating_toggle(v->bitcalculating_mode);
3193 set_memory_toggle(v->rstate);