Move conversion bar into its own module
[gcalctool.git] / src / math-buttons.c
blobef54662cb7ed5dc866b55cca5da2cbba26c83040
1 /* Copyright (c) 2008-2009 Robert Ancell
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16 * 02111-1307, USA.
19 #include <glib/gi18n.h>
21 #include "math-buttons.h"
22 #include "math-converter.h"
23 #include "financial.h"
24 #include "currency.h"
25 #include "mp-serializer.h"
26 #include "units.h"
28 enum {
29 PROP_0,
30 PROP_EQUATION,
31 PROP_MODE
34 static GType button_mode_type;
36 #define MAXBITS 64 /* Bit panel: number of bit fields. */
38 struct MathButtonsPrivate
40 MathEquation *equation;
42 ButtonMode mode;
43 gint programming_base;
45 GtkBuilder *basic_ui, *advanced_ui, *financial_ui, *programming_ui;
47 GdkColor color_numbers, color_action, color_operator, color_function, color_memory, color_group;
49 GtkWidget *bas_panel, *adv_panel, *fin_panel, *prog_panel;
50 GtkWidget *active_panel;
52 GtkWidget *shift_left_menu, *shift_right_menu;
54 GtkWidget *function_menu;
56 GList *superscript_toggles;
57 GList *subscript_toggles;
59 GtkWidget *base_combo;
60 GtkWidget *base_label;
61 GtkWidget *bit_panel;
62 GtkWidget *bit_labels[MAXBITS];
64 GtkWidget *source_currency_combo;
65 GtkWidget *target_currency_combo;
66 MpSerializer *currency_serializer;
67 GtkWidget *currency_label;
69 GtkWidget *character_code_dialog;
70 GtkWidget *character_code_entry;
73 G_DEFINE_TYPE (MathButtons, math_buttons, GTK_TYPE_VBOX);
75 #define UI_BASIC_FILE UI_DIR "/buttons-basic.ui"
76 #define UI_ADVANCED_FILE UI_DIR "/buttons-advanced.ui"
77 #define UI_FINANCIAL_FILE UI_DIR "/buttons-financial.ui"
78 #define UI_PROGRAMMING_FILE UI_DIR "/buttons-programming.ui"
80 #define GET_WIDGET(ui, name) \
81 GTK_WIDGET(gtk_builder_get_object((ui), (name)))
83 #define WM_WIDTH_FACTOR 10
84 #define WM_HEIGHT_FACTOR 30
86 typedef enum
88 NUMBER,
89 NUMBER_BOLD,
90 OPERATOR,
91 FUNCTION,
92 MEMORY,
93 GROUP,
94 ACTION
95 } ButtonClass;
97 typedef struct {
98 const char *widget_name;
99 const char *data;
100 ButtonClass class;
101 const char *tooltip;
102 } ButtonData;
104 static ButtonData button_data[] = {
105 {"pi", "π", NUMBER,
106 /* Tooltip for the Pi button */
107 N_("Pi [Ctrl+P]")},
108 {"eulers_number", "e", NUMBER,
109 /* Tooltip for the Euler's Number button */
110 N_("Euler’s Number")},
111 {"imaginary", "i", NUMBER, NULL},
112 {"numeric_point", NULL, NUMBER, NULL},
113 {"subscript", NULL, NUMBER_BOLD,
114 /* Tooltip for the subscript button */
115 N_("Subscript mode [Alt]")},
116 {"superscript", NULL, NUMBER_BOLD,
117 /* Tooltip for the superscript button */
118 N_("Superscript mode [Ctrl]")},
119 {"exponential", NULL, NUMBER_BOLD,
120 /* Tooltip for the scientific exponent button */
121 N_("Scientific exponent [Ctrl+E]")},
122 {"add", "+", OPERATOR,
123 /* Tooltip for the add button */
124 N_("Add [+]")},
125 {"subtract", "−", OPERATOR,
126 /* Tooltip for the subtract button */
127 N_("Subtract [-]")},
128 {"multiply", "×", OPERATOR,
129 /* Tooltip for the multiply button */
130 N_("Multiply [*]")},
131 {"divide", "÷", OPERATOR,
132 /* Tooltip for the divide button */
133 N_("Divide [/]")},
134 {"modulus_divide", " mod ", OPERATOR,
135 /* Tooltip for the modulus divide button */
136 N_("Modulus divide")},
137 {"function", NULL, FUNCTION,
138 /* Tooltip for the additional functions button */
139 N_("Additional Functions")},
140 {"x_pow_y", "^", FUNCTION,
141 /* Tooltip for the exponent button */
142 N_("Exponent [^ or **]")},
143 {"x_squared", "²", FUNCTION,
144 /* Tooltip for the square button */
145 N_("Square [Ctrl+2]")},
146 {"percentage", "%", NUMBER,
147 /* Tooltip for the percentage button */
148 N_("Percentage [%]")},
149 {"factorial", "!", FUNCTION,
150 /* Tooltip for the factorial button */
151 N_("Factorial [!]")},
152 {"abs", "|", FUNCTION,
153 /* Tooltip for the absolute value button */
154 N_("Absolute value [|]")},
155 {"arg", "Arg ", FUNCTION,
156 /* Tooltip for the complex argument component button */
157 N_("Complex argument")},
158 {"conjugate", "conj ", FUNCTION,
159 /* Tooltip for the complex conjugate button */
160 N_("Complex conjugate")},
161 {"root", "√", FUNCTION,
162 /* Tooltip for the root button */
163 N_("Root [Ctrl+R]")},
164 {"square_root", "√", FUNCTION,
165 /* Tooltip for the square root button */
166 N_("Square root [Ctrl+R]")},
167 {"logarithm", "log ", FUNCTION,
168 /* Tooltip for the logarithm button */
169 N_("Logarithm")},
170 {"natural_logarithm", "ln ", FUNCTION,
171 /* Tooltip for the natural logarithm button */
172 N_("Natural Logarithm")},
173 {"sine", "sin ", FUNCTION,
174 /* Tooltip for the sine button */
175 N_("Sine")},
176 {"cosine", "cos ", FUNCTION,
177 /* Tooltip for the cosine button */
178 N_("Cosine")},
179 {"tangent", "tan ", FUNCTION,
180 /* Tooltip for the tangent button */
181 N_("Tangent")},
182 {"hyperbolic_sine", "sinh ", FUNCTION,
183 /* Tooltip for the hyperbolic sine button */
184 N_("Hyperbolic Sine")},
185 {"hyperbolic_cosine", "cosh ", FUNCTION,
186 /* Tooltip for the hyperbolic cosine button */
187 N_("Hyperbolic Cosine")},
188 {"hyperbolic_tangent", "tanh ", FUNCTION,
189 /* Tooltip for the hyperbolic tangent button */
190 N_("Hyperbolic Tangent")},
191 {"inverse", "⁻¹", FUNCTION,
192 /* Tooltip for the inverse button */
193 N_("Inverse [Ctrl+I]")},
194 {"and", "∧", OPERATOR,
195 /* Tooltip for the boolean AND button */
196 N_("Boolean AND")},
197 {"or", "∨", OPERATOR,
198 /* Tooltip for the boolean OR button */
199 N_("Boolean OR")},
200 {"xor", "⊻", OPERATOR,
201 /* Tooltip for the exclusive OR button */
202 N_("Boolean Exclusive OR")},
203 {"not", "¬", FUNCTION,
204 /* Tooltip for the boolean NOT button */
205 N_("Boolean NOT")},
206 {"integer_portion", "int ", FUNCTION,
207 /* Tooltip for the integer component button */
208 N_("Integer Component")},
209 {"fractional_portion", "frac ", FUNCTION,
210 /* Tooltip for the fractional component button */
211 N_("Fractional Component")},
212 {"real_portion", "Re ", FUNCTION,
213 /* Tooltip for the real component button */
214 N_("Real Component")},
215 {"imaginary_portion", "Im ", FUNCTION,
216 /* Tooltip for the imaginary component button */
217 N_("Imaginary Component")},
218 {"ones_complement", "ones ", FUNCTION,
219 /* Tooltip for the ones complement button */
220 N_("Ones Complement")},
221 {"twos_complement", "twos ", FUNCTION,
222 /* Tooltip for the twos complement button */
223 N_("Twos Complement")},
224 {"trunc", "trunc ", FUNCTION,
225 /* Tooltip for the truncate button */
226 N_("Truncate")},
227 {"start_group", "(", GROUP,
228 /* Tooltip for the start group button */
229 N_("Start Group [(]")},
230 {"end_group", ")", GROUP,
231 /* Tooltip for the end group button */
232 N_("End Group [)]")},
233 {"store", NULL, MEMORY,
234 /* Tooltip for the assign variable button */
235 N_("Assign Variable")},
236 {"recall", NULL, MEMORY,
237 /* Tooltip for the insert variable button */
238 N_("Insert Variable")},
239 {"character", NULL, MEMORY,
240 /* Tooltip for the insert character code button */
241 N_("Insert Character Code")},
242 {"result", NULL, ACTION,
243 /* Tooltip for the solve button */
244 N_("Calculate Result")},
245 {"factor", NULL, ACTION,
246 /* Tooltip for the factor button */
247 N_("Factorize [Ctrl+F]")},
248 {"clear", NULL, GROUP,
249 /* Tooltip for the clear button */
250 N_("Clear Display [Escape]")},
251 {"undo", NULL, GROUP,
252 /* Tooltip for the undo button */
253 N_("Undo [Ctrl+Z]")},
254 {"shift_left", NULL, ACTION,
255 /* Tooltip for the shift left button */
256 N_("Shift Left")},
257 {"shift_right", NULL, ACTION,
258 /* Tooltip for the shift right button */
259 N_("Shift Right")},
260 {"finc_compounding_term", NULL, FUNCTION,
261 /* Tooltip for the compounding term button */
262 N_("Compounding Term")},
263 {"finc_double_declining_depreciation", NULL, FUNCTION,
264 /* Tooltip for the double declining depreciation button */
265 N_("Double Declining Depreciation")},
266 {"finc_future_value", NULL, FUNCTION,
267 /* Tooltip for the future value button */
268 N_("Future Value")},
269 {"finc_term", NULL, FUNCTION,
270 /* Tooltip for the financial term button */
271 N_("Financial Term")},
272 {"finc_sum_of_the_years_digits_depreciation", NULL, FUNCTION,
273 /* Tooltip for the sum of the years digits depreciation button */
274 N_("Sum of the Years Digits Depreciation")},
275 {"finc_straight_line_depreciation", NULL, FUNCTION,
276 /* Tooltip for the straight line depreciation button */
277 N_("Straight Line Depreciation")},
278 {"finc_periodic_interest_rate", NULL, FUNCTION,
279 /* Tooltip for the periodic interest rate button */
280 N_("Periodic Interest Rate")},
281 {"finc_present_value", NULL, FUNCTION,
282 /* Tooltip for the present value button */
283 N_("Present Value")},
284 {"finc_periodic_payment", NULL, FUNCTION,
285 /* Tooltip for the periodic payment button */
286 N_("Periodic Payment")},
287 {"finc_gross_profit_margin", NULL, FUNCTION,
288 /* Tooltip for the gross profit margin button */
289 N_("Gross Profit Margin")},
290 {NULL, NULL, 0, NULL}
293 typedef enum {
294 CURRENCY_TARGET_UPPER,
295 CURRENCY_TARGET_LOWER
296 } CurrencyTargetRow;
298 /* The names of each field in the dialogs for the financial functions */
299 static char *finc_dialog_fields[][5] = {
300 {"ctrm_pint", "ctrm_fv", "ctrm_pv", NULL, NULL},
301 {"ddb_cost", "ddb_life", "ddb_period", NULL, NULL},
302 {"fv_pmt", "fv_pint", "fv_n", NULL, NULL},
303 {"gpm_cost", "gpm_margin", NULL, NULL, NULL},
304 {"pmt_prin", "pmt_pint", "pmt_n", NULL, NULL},
305 {"pv_pmt", "pv_pint", "pv_n", NULL, NULL},
306 {"rate_fv", "rate_pv", "rate_n", NULL, NULL},
307 {"sln_cost", "sln_salvage", "sln_life", NULL, NULL},
308 {"syd_cost", "syd_salvage", "syd_life", "syd_period", NULL},
309 {"term_pmt", "term_fv", "term_pint", NULL, NULL},
310 {NULL, NULL, NULL, NULL, NULL}
314 MathButtons *
315 math_buttons_new(MathEquation *equation)
317 return g_object_new(math_buttons_get_type(), "equation", equation, NULL);
321 static void
322 set_tint(GtkWidget *widget, GdkColor *tint, gint alpha)
324 GtkStyle *style;
325 int j;
327 if (!widget)
328 return;
330 gtk_widget_ensure_style(widget);
331 style = gtk_widget_get_style(widget);
333 for (j = 0; j < 5; j++) {
334 GdkColor color;
336 color.red = (style->bg[j].red * (10 - alpha) + tint->red * alpha) / 10;
337 color.green = (style->bg[j].green * (10 - alpha) + tint->green * alpha) / 10;
338 color.blue = (style->bg[j].blue * (10 - alpha) + tint->blue * alpha) / 10;
340 gtk_widget_modify_bg(widget, j, &color);
345 static void
346 set_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const char *value)
348 GObject *object;
349 object = gtk_builder_get_object(ui, object_name);
350 if (object)
351 g_object_set_data(object, name, GINT_TO_POINTER(value));
355 static void
356 set_int_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, gint value)
358 GObject *object;
359 object = gtk_builder_get_object(ui, object_name);
360 if (object)
361 g_object_set_data(object, name, GINT_TO_POINTER(value));
365 static void
366 load_finc_dialogs(MathButtons *buttons)
368 int i, j;
370 set_int_data(buttons->priv->financial_ui, "ctrm_dialog", "finc_dialog", FINC_CTRM_DIALOG);
371 set_int_data(buttons->priv->financial_ui, "ddb_dialog", "finc_dialog", FINC_DDB_DIALOG);
372 set_int_data(buttons->priv->financial_ui, "fv_dialog", "finc_dialog", FINC_FV_DIALOG);
373 set_int_data(buttons->priv->financial_ui, "gpm_dialog", "finc_dialog", FINC_GPM_DIALOG);
374 set_int_data(buttons->priv->financial_ui, "pmt_dialog", "finc_dialog", FINC_PMT_DIALOG);
375 set_int_data(buttons->priv->financial_ui, "pv_dialog", "finc_dialog", FINC_PV_DIALOG);
376 set_int_data(buttons->priv->financial_ui, "rate_dialog", "finc_dialog", FINC_RATE_DIALOG);
377 set_int_data(buttons->priv->financial_ui, "sln_dialog", "finc_dialog", FINC_SLN_DIALOG);
378 set_int_data(buttons->priv->financial_ui, "syd_dialog", "finc_dialog", FINC_SYD_DIALOG);
379 set_int_data(buttons->priv->financial_ui, "term_dialog", "finc_dialog", FINC_TERM_DIALOG);
381 for (i = 0; finc_dialog_fields[i][0] != NULL; i++) {
382 for (j = 0; finc_dialog_fields[i][j]; j++) {
383 GObject *o;
384 o = gtk_builder_get_object (buttons->priv->financial_ui, finc_dialog_fields[i][j]);
385 g_object_set_data(o, "finc_field", GINT_TO_POINTER(j));
386 g_object_set_data(o, "finc_dialog", GINT_TO_POINTER(i));
392 static void
393 update_bit_panel(MathButtons *buttons)
395 MPNumber x;
396 gboolean enabled;
397 guint64 bits;
398 int i;
399 GString *label;
400 gint base;
402 if (!buttons->priv->bit_panel)
403 return;
405 enabled = math_equation_get_number(buttons->priv->equation, &x);
407 if (enabled) {
408 MPNumber max, fraction;
410 mp_set_from_unsigned_integer(G_MAXUINT64, &max);
411 mp_fractional_part(&x, &fraction);
412 if (mp_is_negative(&x) || mp_is_greater_than(&x, &max) || !mp_is_zero(&fraction))
413 enabled = FALSE;
414 else
415 bits = mp_cast_to_unsigned_int(&x);
418 gtk_widget_set_sensitive(buttons->priv->bit_panel, enabled);
419 gtk_widget_set_sensitive(buttons->priv->base_label, enabled);
421 if (!enabled)
422 return;
424 for (i = 0; i < MAXBITS; i++) {
425 const gchar *label;
427 if (bits & (1LL << (MAXBITS-i-1)))
428 label = " 1";
429 else
430 label = " 0";
431 gtk_label_set_text(GTK_LABEL(buttons->priv->bit_labels[i]), label);
434 base = math_equation_get_base(buttons->priv->equation);
435 label = g_string_new("");
436 if (base != 8) {
437 if (label->len != 0)
438 g_string_append(label, " = ");
439 g_string_append_printf(label, "%lo", bits);
440 g_string_append(label, "₈");
442 if (base != 10) {
443 if (label->len != 0)
444 g_string_append(label, " = ");
445 g_string_append_printf(label, "%lu", bits);
446 g_string_append(label, "₁₀");
448 if (base != 16) {
449 if (label->len != 0)
450 g_string_append(label, " = ");
451 g_string_append_printf(label, "%lX", bits);
452 g_string_append(label, "₁₆");
455 gtk_label_set_text(GTK_LABEL(buttons->priv->base_label), label->str);
456 g_string_free(label, TRUE);
460 static void
461 update_currency_label(MathButtons *buttons)
463 MPNumber x, value;
464 char *label;
466 if (!buttons->priv->currency_label)
467 return;
469 if (!math_equation_get_number(buttons->priv->equation, &x))
470 return;
472 if (currency_convert(&x,
473 math_equation_get_source_currency(buttons->priv->equation),
474 math_equation_get_target_currency(buttons->priv->equation),
475 &value)) {
476 char *input_text, *output_text;
477 const char *source_symbol, *target_symbol;
479 input_text = mp_serializer_to_string(buttons->priv->currency_serializer, &x);
480 output_text = mp_serializer_to_string(buttons->priv->currency_serializer, &value);
482 source_symbol = currency_get_info(math_equation_get_source_currency(buttons->priv->equation))->symbol;
483 target_symbol = currency_get_info(math_equation_get_target_currency(buttons->priv->equation))->symbol;
485 /* Translators: first and third %s are currency symbols, second and fourth are amounts in these currencies, you may want to change the order of these, example: $100 = €100 */
486 label = g_strdup_printf(_("%s%s = %s%s"),
487 source_symbol, input_text,
488 target_symbol, output_text);
489 g_free(input_text);
490 g_free(output_text);
492 else
493 label = g_strdup("");
495 gtk_label_set_text(GTK_LABEL(buttons->priv->currency_label), label);
496 g_free(label);
500 static void
501 display_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
503 update_currency_label(buttons);
504 update_bit_panel(buttons);
508 static void
509 base_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons)
511 gint value;
512 GtkTreeModel *model;
513 GtkTreeIter iter;
515 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
516 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
517 gtk_tree_model_get(model, &iter, 1, &value, -1);
519 math_equation_set_base(buttons->priv->equation, value);
523 static void
524 base_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
526 GtkTreeModel *model;
527 GtkTreeIter iter;
528 gboolean valid;
530 if (buttons->priv->mode != PROGRAMMING)
531 return;
533 model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->base_combo));
534 valid = gtk_tree_model_get_iter_first(model, &iter);
535 buttons->priv->programming_base = math_equation_get_base(buttons->priv->equation);
537 while (valid) {
538 gint v;
540 gtk_tree_model_get(model, &iter, 1, &v, -1);
541 if (v == buttons->priv->programming_base)
542 break;
543 valid = gtk_tree_model_iter_next(model, &iter);
545 if (!valid)
546 valid = gtk_tree_model_get_iter_first(model, &iter);
548 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->base_combo), &iter);
552 static void
553 source_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons)
555 gchar *value;
556 GtkTreeModel *model;
557 GtkTreeIter iter;
559 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
560 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
561 gtk_tree_model_get(model, &iter, 0, &value, -1);
563 math_equation_set_source_currency(buttons->priv->equation, value);
564 g_free(value);
568 static void
569 source_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
571 GtkTreeModel *model;
572 GtkTreeIter iter;
573 gboolean valid;
575 if (buttons->priv->mode != FINANCIAL)
576 return;
578 model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo));
579 valid = gtk_tree_model_get_iter_first(model, &iter);
581 while (valid) {
582 gchar *v;
583 gboolean matched;
585 gtk_tree_model_get(model, &iter, 0, &v, -1);
586 matched = strcmp(math_equation_get_source_currency(buttons->priv->equation), v) == 0;
587 g_free(v);
588 if (matched)
589 break;
590 valid = gtk_tree_model_iter_next(model, &iter);
592 if (!valid)
593 valid = gtk_tree_model_get_iter_first(model, &iter);
595 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->source_currency_combo), &iter);
596 update_currency_label(buttons);
600 static void
601 target_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons)
603 gchar *value;
604 GtkTreeModel *model;
605 GtkTreeIter iter;
607 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
608 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
609 gtk_tree_model_get(model, &iter, 0, &value, -1);
611 math_equation_set_target_currency(buttons->priv->equation, value);
612 g_free(value);
616 static void
617 target_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
619 GtkTreeModel *model;
620 GtkTreeIter iter;
621 gboolean valid;
623 if (buttons->priv->mode != FINANCIAL)
624 return;
626 model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo));
627 valid = gtk_tree_model_get_iter_first(model, &iter);
629 while (valid) {
630 gchar *v;
631 gboolean matched;
633 gtk_tree_model_get(model, &iter, 0, &v, -1);
634 matched = strcmp(math_equation_get_target_currency(buttons->priv->equation), v) == 0;
635 g_free(v);
636 if (matched)
637 break;
638 valid = gtk_tree_model_iter_next(model, &iter);
640 if (!valid)
641 valid = gtk_tree_model_get_iter_first(model, &iter);
643 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(buttons->priv->target_currency_combo), &iter);
644 update_currency_label(buttons);
648 static GtkWidget *
649 load_mode(MathButtons *buttons, ButtonMode mode)
651 GtkBuilder *builder, **builder_ptr;
652 gint i;
653 gchar *name;
654 const gchar *builder_file;
655 static gchar *objects[] = { "button_panel", "character_code_dialog", "currency_dialog",
656 "ctrm_dialog", "ddb_dialog", "fv_dialog", "gpm_dialog",
657 "pmt_dialog", "pv_dialog", "rate_dialog", "sln_dialog",
658 "syd_dialog", "term_dialog", "adjustment1", "adjustment2", NULL };
659 GtkWidget *widget, **panel;
660 GError *error = NULL;
662 switch (mode) {
663 default:
664 case BASIC:
665 builder_ptr = &buttons->priv->basic_ui;
666 builder_file = UI_BASIC_FILE;
667 panel = &buttons->priv->bas_panel;
668 break;
669 case ADVANCED:
670 builder_ptr = &buttons->priv->advanced_ui;
671 builder_file = UI_ADVANCED_FILE;
672 panel = &buttons->priv->adv_panel;
673 break;
674 case FINANCIAL:
675 builder_ptr = &buttons->priv->financial_ui;
676 builder_file = UI_FINANCIAL_FILE;
677 panel = &buttons->priv->fin_panel;
678 break;
679 case PROGRAMMING:
680 builder_ptr = &buttons->priv->programming_ui;
681 builder_file = UI_PROGRAMMING_FILE;
682 panel = &buttons->priv->prog_panel;
683 break;
686 if (*panel)
687 return *panel;
689 builder = *builder_ptr = gtk_builder_new();
690 // FIXME: Show dialog if failed to load
691 gtk_builder_add_objects_from_file(builder, builder_file, objects, &error);
692 if (error) {
693 g_warning("Error loading button UI: %s", error->message);
694 g_clear_error(&error);
696 *panel = GET_WIDGET(builder, "button_panel");
697 gtk_box_pack_end(GTK_BOX(buttons), *panel, TRUE, TRUE, 0);
699 /* Configure buttons */
700 for (i = 0; button_data[i].widget_name != NULL; i++) {
701 GObject *object;
702 GtkWidget *button;
704 name = g_strdup_printf("calc_%s_button", button_data[i].widget_name);
705 object = gtk_builder_get_object(*builder_ptr, name);
706 g_free(name);
708 if (!object)
709 continue;
710 button = GTK_WIDGET(object);
711 if (button_data[i].data)
712 g_object_set_data(object, "calc_text", (gpointer) button_data[i].data);
714 if (button_data[i].tooltip)
715 gtk_widget_set_tooltip_text(button, _(button_data[i].tooltip));
717 atk_object_set_name(gtk_widget_get_accessible(button), button_data[i].widget_name);
719 switch (button_data[i].class) {
720 case NUMBER:
721 set_tint(button, &buttons->priv->color_numbers, 1);
722 break;
723 case NUMBER_BOLD:
724 set_tint(button, &buttons->priv->color_numbers, 2);
725 break;
726 case OPERATOR:
727 set_tint(button, &buttons->priv->color_operator, 1);
728 break;
729 case FUNCTION:
730 set_tint(button, &buttons->priv->color_function, 1);
731 break;
732 case MEMORY:
733 set_tint(button, &buttons->priv->color_memory, 1);
734 break;
735 case GROUP:
736 set_tint(button, &buttons->priv->color_group, 1);
737 break;
738 case ACTION:
739 set_tint(button, &buttons->priv->color_action, 2);
740 break;
744 /* Set special button data */
745 for (i = 0; i < 16; i++) {
746 GtkWidget *button;
748 name = g_strdup_printf("calc_%d_button", i);
749 button = GET_WIDGET(builder, name);
750 if (button) {
751 gchar buffer[7];
752 gint len;
754 g_object_set_data(G_OBJECT(button), "calc_digit", GINT_TO_POINTER(i));
755 set_tint(button, &buttons->priv->color_numbers, 1);
756 len = g_unichar_to_utf8(math_equation_get_digit_text(buttons->priv->equation, i), buffer);
757 buffer[len] = '\0';
758 gtk_button_set_label(GTK_BUTTON(button), buffer);
760 g_free(name);
762 widget = GET_WIDGET(builder, "calc_numeric_point_button");
763 if (widget) {
764 MpSerializer *serializer = math_equation_get_serializer(buttons->priv->equation);
765 gchar buffer[7];
766 gint len;
767 len = g_unichar_to_utf8(mp_serializer_get_radix(serializer), buffer);
768 buffer[len] = '\0';
769 gtk_button_set_label(GTK_BUTTON(widget), buffer);
772 widget = GET_WIDGET(builder, "calc_superscript_button");
773 if (widget) {
774 buttons->priv->superscript_toggles = g_list_append(buttons->priv->superscript_toggles, widget);
775 if (math_equation_get_number_mode(buttons->priv->equation) == SUPERSCRIPT)
776 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
778 widget = GET_WIDGET(builder, "calc_subscript_button");
779 if (widget) {
780 buttons->priv->subscript_toggles = g_list_append(buttons->priv->subscript_toggles, widget);
781 if (math_equation_get_number_mode(buttons->priv->equation) == SUBSCRIPT)
782 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
785 if (mode == ADVANCED) {
786 GtkWidget *converter;
788 converter = GTK_WIDGET(math_converter_new(buttons->priv->equation));
789 gtk_widget_show(converter);
790 gtk_box_pack_start(GTK_BOX(*panel), converter, FALSE, TRUE, 0);
793 if (mode == PROGRAMMING) {
794 GtkListStore *model;
795 GtkTreeIter iter;
796 GtkCellRenderer *renderer;
798 buttons->priv->base_label = GET_WIDGET(builder, "base_label");
799 buttons->priv->character_code_dialog = GET_WIDGET(builder, "character_code_dialog");
800 buttons->priv->character_code_entry = GET_WIDGET(builder, "character_code_entry");
802 buttons->priv->bit_panel = GET_WIDGET(builder, "bit_table");
803 for (i = 0; i < MAXBITS; i++) {
804 name = g_strdup_printf("bit_label_%d", i);
805 buttons->priv->bit_labels[i] = GET_WIDGET(builder, name);
806 g_free(name);
807 name = g_strdup_printf("bit_eventbox_%d", i);
808 set_int_data(builder, name, "bit_index", i);
811 buttons->priv->base_combo = GET_WIDGET(builder, "base_combo");
812 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
813 gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->base_combo), GTK_TREE_MODEL(model));
814 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
815 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
816 /* Number display mode combo: Binary, e.g. 10011010010₂ */
817 _("Binary"), 1, 2, -1);
818 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
819 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
820 /* Number display mode combo: Octal, e.g. 2322₈ */
821 _("Octal"), 1, 8, -1);
822 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
823 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
824 /* Number display mode combo: Decimal, e.g. 1234 */
825 _("Decimal"), 1, 10, -1);
826 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
827 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
828 /* Number display mode combo: Hexadecimal, e.g. 4D2₁₆ */
829 _("Hexadecimal"), 1, 16, -1);
830 renderer = gtk_cell_renderer_text_new();
831 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->base_combo), renderer, TRUE);
832 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->base_combo), renderer, "text", 0);
834 g_signal_connect(buttons->priv->base_combo, "changed", G_CALLBACK(base_combobox_changed_cb), buttons);
835 g_signal_connect(buttons->priv->equation, "notify::base", G_CALLBACK(base_changed_cb), buttons);
836 base_changed_cb(buttons->priv->equation, NULL, buttons);
839 /* Setup financial functions */
840 if (mode == FINANCIAL) {
841 GtkListStore *model;
842 GtkCellRenderer *renderer;
844 load_finc_dialogs(buttons);
846 buttons->priv->source_currency_combo = GET_WIDGET(builder, "source_currency_combo");
847 buttons->priv->target_currency_combo = GET_WIDGET(builder, "target_currency_combo");
848 buttons->priv->currency_label = GET_WIDGET(builder, "currency_label");
850 buttons->priv->currency_serializer = mp_serializer_new(MP_DISPLAY_FORMAT_AUTOMATIC, 10, 2);
852 model = gtk_list_store_new(1, G_TYPE_STRING);
854 for (i = 0; currency_info[i].short_name != NULL; i++) {
855 GtkTreeIter iter;
857 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
858 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, currency_info[i].short_name, -1);
861 gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo), GTK_TREE_MODEL(model));
862 gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo), GTK_TREE_MODEL(model));
864 renderer = gtk_cell_renderer_text_new();
865 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, TRUE);
866 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, "text", 0);
867 renderer = gtk_cell_renderer_text_new();
868 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, TRUE);
869 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, "text", 0);
871 g_signal_connect(buttons->priv->source_currency_combo, "changed", G_CALLBACK(source_currency_combo_changed_cb), buttons);
872 g_signal_connect(buttons->priv->target_currency_combo, "changed", G_CALLBACK(target_currency_combo_changed_cb), buttons);
873 g_signal_connect(buttons->priv->equation, "notify::source-currency", G_CALLBACK(source_currency_changed_cb), buttons);
874 g_signal_connect(buttons->priv->equation, "notify::target-currency", G_CALLBACK(target_currency_changed_cb), buttons);
875 source_currency_changed_cb(buttons->priv->equation, NULL, buttons);
876 target_currency_changed_cb(buttons->priv->equation, NULL, buttons);
878 set_data(builder, "calc_finc_compounding_term_button", "finc_dialog", "ctrm_dialog");
879 set_data(builder, "calc_finc_double_declining_depreciation_button", "finc_dialog", "ddb_dialog");
880 set_data(builder, "calc_finc_future_value_button", "finc_dialog", "fv_dialog");
881 set_data(builder, "calc_finc_gross_profit_margin_button", "finc_dialog", "gpm_dialog");
882 set_data(builder, "calc_finc_periodic_payment_button", "finc_dialog", "pmt_dialog");
883 set_data(builder, "calc_finc_present_value_button", "finc_dialog", "pv_dialog");
884 set_data(builder, "calc_finc_periodic_interest_rate_button", "finc_dialog", "rate_dialog");
885 set_data(builder, "calc_finc_straight_line_depreciation_button", "finc_dialog", "sln_dialog");
886 set_data(builder, "calc_finc_sum_of_the_years_digits_depreciation_button", "finc_dialog", "syd_dialog");
887 set_data(builder, "calc_finc_term_button", "finc_dialog", "term_dialog");
890 gtk_builder_connect_signals(builder, buttons);
892 display_changed_cb(buttons->priv->equation, NULL, buttons);
894 return *panel;
899 static void
900 load_buttons(MathButtons *buttons)
902 GtkWidget *panel;
904 if (!gtk_widget_get_visible(GTK_WIDGET(buttons)))
905 return;
907 panel = load_mode(buttons, buttons->priv->mode);
908 if (buttons->priv->active_panel == panel)
909 return;
911 /* Hide old buttons */
912 if (buttons->priv->active_panel)
913 gtk_widget_hide(buttons->priv->active_panel);
915 /* Load and display new buttons */
916 buttons->priv->active_panel = panel;
917 if (panel)
918 gtk_widget_show(panel);
922 void
923 math_buttons_set_mode(MathButtons *buttons, ButtonMode mode)
925 ButtonMode old_mode;
927 if (buttons->priv->mode == mode)
928 return;
930 old_mode = buttons->priv->mode;
931 buttons->priv->mode = mode;
933 if (mode == PROGRAMMING)
934 math_equation_set_base(buttons->priv->equation, buttons->priv->programming_base);
935 else
936 math_equation_set_base(buttons->priv->equation, 10);
938 load_buttons(buttons);
940 g_object_notify(G_OBJECT(buttons), "mode");
944 ButtonMode
945 math_buttons_get_mode(MathButtons *buttons)
947 return buttons->priv->mode;
951 void
952 math_buttons_set_programming_base(MathButtons *buttons, gint base)
954 buttons->priv->programming_base = base;
958 gint
959 math_buttons_get_programming_base(MathButtons *buttons)
961 return buttons->priv->programming_base;
965 void exponent_cb(GtkWidget *widget, MathButtons *buttons);
966 G_MODULE_EXPORT
967 void
968 exponent_cb(GtkWidget *widget, MathButtons *buttons)
970 math_equation_insert_exponent(buttons->priv->equation);
974 void subtract_cb(GtkWidget *widget, MathButtons *buttons);
975 G_MODULE_EXPORT
976 void
977 subtract_cb(GtkWidget *widget, MathButtons *buttons)
979 math_equation_insert_subtract(buttons->priv->equation);
983 void button_cb(GtkWidget *widget, MathButtons *buttons);
984 G_MODULE_EXPORT
985 void
986 button_cb(GtkWidget *widget, MathButtons *buttons)
988 math_equation_insert(buttons->priv->equation, g_object_get_data(G_OBJECT(widget), "calc_text"));
992 void solve_cb(GtkWidget *widget, MathButtons *buttons);
993 G_MODULE_EXPORT
994 void
995 solve_cb(GtkWidget *widget, MathButtons *buttons)
997 math_equation_solve(buttons->priv->equation);
1001 void clear_cb(GtkWidget *widget, MathButtons *buttons);
1002 G_MODULE_EXPORT
1003 void
1004 clear_cb(GtkWidget *widget, MathButtons *buttons)
1006 math_equation_clear(buttons->priv->equation);
1010 void delete_cb(GtkWidget *widget, MathButtons *buttons);
1011 G_MODULE_EXPORT
1012 void
1013 delete_cb(GtkWidget *widget, MathButtons *buttons)
1015 math_equation_delete(buttons->priv->equation);
1019 void undo_cb(GtkWidget *widget, MathButtons *buttons);
1020 G_MODULE_EXPORT
1021 void
1022 undo_cb(GtkWidget *widget, MathButtons *buttons)
1024 math_equation_undo(buttons->priv->equation);
1028 static void
1029 shift_cb(GtkWidget *widget, MathButtons *buttons)
1031 math_equation_shift(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "shiftcount")));
1035 static void
1036 button_menu_position_func(GtkMenu *menu, gint *x, gint *y,
1037 gboolean *push_in, gpointer user_data)
1039 GtkWidget *button = user_data;
1040 GtkAllocation allocation;
1041 GdkPoint loc;
1042 gint border;
1044 gdk_window_get_origin(gtk_widget_get_window(button), &loc.x, &loc.y);
1045 border = gtk_container_get_border_width(GTK_CONTAINER(button));
1046 gtk_widget_get_allocation(button, &allocation);
1047 *x = loc.x + allocation.x + border;
1048 *y = loc.y + allocation.y + border;
1052 static void
1053 popup_button_menu(GtkWidget *widget, GtkMenu *menu)
1055 gtk_menu_popup(menu, NULL, NULL,
1056 button_menu_position_func, widget, 1, gtk_get_current_event_time());
1060 static void
1061 save_variable_cb(GtkWidget *widget, MathButtons *buttons)
1063 printf("save\n");
1067 static void
1068 delete_variable_cb(GtkWidget *widget, MathButtons *buttons)
1070 printf("delete\n");
1074 static GtkWidget *
1075 make_register_menu_item(MathButtons *buttons, const gchar *name, const MPNumber *value, gboolean can_modify, GCallback callback)
1077 gchar *text, *mstr;
1078 GtkWidget *item, *label;
1080 if (value) {
1081 MpSerializer *serializer = math_equation_get_serializer(buttons->priv->equation);
1082 text = mp_serializer_to_string(serializer, value);
1083 mstr = g_strdup_printf("<span weight=\"bold\">%s</span> = %s", name, text);
1084 g_free(text);
1086 else
1087 mstr = g_strdup_printf("<span weight=\"bold\">%s</span>", name);
1088 label = gtk_label_new(mstr);
1089 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1090 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1091 g_free(mstr);
1093 item = gtk_menu_item_new();
1095 // FIXME: Buttons don't work inside menus...
1096 if (0) {//can_modify) {
1097 GtkWidget *hbox, *button;
1099 hbox = gtk_hbox_new(FALSE, 6);
1100 gtk_container_add(GTK_CONTAINER(item), hbox);
1102 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
1104 button = gtk_button_new();
1105 gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU));
1106 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1107 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0);
1108 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_variable_cb), buttons);
1110 button = gtk_button_new();
1111 gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU));
1112 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1113 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0);
1114 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(save_variable_cb), buttons);
1116 else
1117 gtk_container_add(GTK_CONTAINER(item), label);
1119 g_object_set_data(G_OBJECT(item), "register_id", g_strdup(name)); // FIXME: Memory leak
1120 g_signal_connect(item, "activate", callback, buttons);
1122 return item;
1126 static void
1127 store_menu_cb(GtkMenuItem *menu, MathButtons *buttons)
1129 math_equation_store(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id"));
1133 void store_cb(GtkWidget *widget, MathButtons *buttons);
1134 G_MODULE_EXPORT
1135 void
1136 store_cb(GtkWidget *widget, MathButtons *buttons)
1138 int i;
1139 GtkWidget *menu;
1140 GtkWidget *item;
1141 gchar **names;
1143 menu = gtk_menu_new();
1144 gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
1145 set_tint(menu, &buttons->priv->color_memory, 1);
1147 names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation));
1148 if (names[0] == NULL) {
1149 item = gtk_menu_item_new_with_label(/* Text shown in store menu when no variables defined */
1150 _("No variables defined"));
1151 gtk_widget_set_sensitive(item, FALSE);
1152 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1154 for (i = 0; names[i]; i++) {
1155 MPNumber *value;
1156 value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]);
1157 item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(store_menu_cb));
1158 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1161 g_strfreev(names);
1163 // FIXME
1164 //item = gtk_menu_item_new_with_label(_("Add variable"));
1165 //gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1167 gtk_widget_show_all(menu);
1168 popup_button_menu(widget, GTK_MENU(menu));
1172 static void
1173 recall_menu_cb(GtkMenuItem *menu, MathButtons *buttons)
1175 math_equation_recall(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id"));
1179 void recall_cb(GtkWidget *widget, MathButtons *buttons);
1180 G_MODULE_EXPORT
1181 void
1182 recall_cb(GtkWidget *widget, MathButtons *buttons)
1184 int i;
1185 GtkWidget *menu;
1186 GtkWidget *item;
1187 gchar **names;
1189 menu = gtk_menu_new();
1190 gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
1191 set_tint(menu, &buttons->priv->color_memory, 1);
1193 names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation));
1194 if (names[0] == NULL) {
1195 item = gtk_menu_item_new_with_label(/* Text shown in recall menu when no variables defined */
1196 _("No variables defined"));
1197 gtk_widget_set_sensitive(item, FALSE);
1198 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1200 for (i = 0; names[i]; i++) {
1201 MPNumber *value;
1202 value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]);
1203 item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(recall_menu_cb));
1204 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1207 g_strfreev(names);
1209 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1210 item = make_register_menu_item(buttons, "ans", math_equation_get_answer(buttons->priv->equation), FALSE, G_CALLBACK(recall_menu_cb));
1211 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1212 item = make_register_menu_item(buttons, "rand", NULL, FALSE, G_CALLBACK(recall_menu_cb));
1213 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1215 gtk_widget_show_all(menu);
1216 popup_button_menu(widget, GTK_MENU(menu));
1220 void shift_left_cb(GtkWidget *widget, MathButtons *buttons);
1221 G_MODULE_EXPORT
1222 void
1223 shift_left_cb(GtkWidget *widget, MathButtons *buttons)
1225 if (!buttons->priv->shift_left_menu) {
1226 gint i;
1227 GtkWidget *menu;
1229 menu = buttons->priv->shift_left_menu = gtk_menu_new();
1230 gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
1231 set_tint(menu, &buttons->priv->color_action, 1);
1233 for (i = 1; i < 16; i++) {
1234 GtkWidget *item, *label;
1235 gchar *format, *text;
1237 if (i < 10) {
1238 /* Left Shift Popup: Menu item to shift left by n places (n < 10) */
1239 format = ngettext("_%d place", "_%d places", i);
1241 else {
1242 /* Left Shift Popup: Menu item to shift left by n places (n >= 10) */
1243 format = ngettext("%d place", "%d places", i);
1245 text = g_strdup_printf(format, i);
1246 label = gtk_label_new_with_mnemonic(text);
1248 item = gtk_menu_item_new();
1249 g_object_set_data(G_OBJECT(item), "shiftcount", GINT_TO_POINTER(i));
1250 gtk_container_add(GTK_CONTAINER(item), label);
1251 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1252 g_signal_connect(item, "activate", G_CALLBACK(shift_cb), buttons);
1254 gtk_widget_show(label);
1255 gtk_widget_show(item);
1256 g_free(text);
1260 popup_button_menu(widget, GTK_MENU(buttons->priv->shift_left_menu));
1264 void shift_right_cb(GtkWidget *widget, MathButtons *buttons);
1265 G_MODULE_EXPORT
1266 void
1267 shift_right_cb(GtkWidget *widget, MathButtons *buttons)
1269 if (!buttons->priv->shift_right_menu) {
1270 gint i;
1271 GtkWidget *menu;
1273 menu = buttons->priv->shift_right_menu = gtk_menu_new();
1274 gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
1275 set_tint(menu, &buttons->priv->color_action, 1);
1277 for (i = 1; i < 16; i++) {
1278 GtkWidget *item, *label;
1279 gchar *format, *text;
1281 if (i < 10) {
1282 /* Right Shift Popup: Menu item to shift right by n places (n < 10) */
1283 format = ngettext("_%d place", "_%d places", i);
1285 else {
1286 /* Right Shift Popup: Menu item to shift right by n places (n >= 10) */
1287 format = ngettext("%d place", "%d places", i);
1289 text = g_strdup_printf(format, i);
1290 label = gtk_label_new_with_mnemonic(text);
1292 item = gtk_menu_item_new();
1293 g_object_set_data(G_OBJECT(item), "shiftcount", GINT_TO_POINTER(-i));
1294 gtk_container_add(GTK_CONTAINER(item), label);
1295 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1296 g_signal_connect(item, "activate", G_CALLBACK(shift_cb), buttons);
1298 gtk_widget_show(label);
1299 gtk_widget_show(item);
1300 g_free(text);
1304 popup_button_menu(widget, GTK_MENU(buttons->priv->shift_right_menu));
1308 static void
1309 insert_function_cb(GtkWidget *widget, MathButtons *buttons)
1311 math_equation_insert(buttons->priv->equation, g_object_get_data(G_OBJECT(widget), "function"));
1315 void function_cb(GtkWidget *widget, MathButtons *buttons);
1316 G_MODULE_EXPORT
1317 void
1318 function_cb(GtkWidget *widget, MathButtons *buttons)
1320 if (!buttons->priv->function_menu) {
1321 gint i;
1322 GtkWidget *menu;
1323 struct
1325 gchar *name, *function;
1326 } functions[] =
1328 { /* Tooltip for the integer component button */
1329 N_("Integer Component"), "int " },
1330 { /* Tooltip for the fractional component button */
1331 N_("Fractional Component"), "frac " },
1332 { /* Tooltip for the round button */
1333 N_("Round"), "round " },
1334 { /* Tooltip for the floor button */
1335 N_("Floor"), "floor " },
1336 { /* Tooltip for the ceiling button */
1337 N_("Ceiling"), "ceil " },
1338 { /* Tooltip for the ceiling button */
1339 N_("Sign"), "sgn " },
1340 { NULL, NULL }
1343 menu = buttons->priv->function_menu = gtk_menu_new();
1344 gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE);
1345 set_tint(menu, &buttons->priv->color_function, 1);
1347 for (i = 0; functions[i].name != NULL; i++) {
1348 GtkWidget *item;
1350 item = gtk_menu_item_new_with_label(_(functions[i].name));
1351 g_object_set_data(G_OBJECT(item), "function", g_strdup(functions[i].function));
1352 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1353 g_signal_connect(item, "activate", G_CALLBACK(insert_function_cb), buttons);
1354 gtk_widget_show(item);
1358 popup_button_menu(widget, GTK_MENU(buttons->priv->function_menu));
1362 void factorize_cb(GtkWidget *widget, MathButtons *buttons);
1363 G_MODULE_EXPORT
1364 void
1365 factorize_cb(GtkWidget *widget, MathButtons *buttons)
1367 math_equation_factorize(buttons->priv->equation);
1371 void digit_cb(GtkWidget *widget, MathButtons *buttons);
1372 G_MODULE_EXPORT
1373 void
1374 digit_cb(GtkWidget *widget, MathButtons *buttons)
1376 math_equation_insert_digit(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "calc_digit")));
1380 void numeric_point_cb(GtkWidget *widget, MathButtons *buttons);
1381 G_MODULE_EXPORT
1382 void
1383 numeric_point_cb(GtkWidget *widget, MathButtons *buttons)
1385 math_equation_insert_numeric_point(buttons->priv->equation);
1390 void finc_cb(GtkWidget *widget, MathButtons *buttons);
1391 G_MODULE_EXPORT
1392 void
1393 finc_cb(GtkWidget *widget, MathButtons *buttons)
1395 gchar *name;
1397 name = g_object_get_data(G_OBJECT(widget), "finc_dialog");
1398 gtk_dialog_run(GTK_DIALOG(GET_WIDGET(buttons->priv->financial_ui, name)));
1399 gtk_widget_hide(GTK_WIDGET(GET_WIDGET(buttons->priv->financial_ui, name)));
1403 void insert_character_code_cb(GtkWidget *widget, MathButtons *buttons);
1404 G_MODULE_EXPORT
1405 void
1406 insert_character_code_cb(GtkWidget *widget, MathButtons *buttons)
1408 gtk_window_present(GTK_WINDOW(buttons->priv->character_code_dialog));
1412 void finc_activate_cb(GtkWidget *widget, MathButtons *buttons);
1413 G_MODULE_EXPORT
1414 void
1415 finc_activate_cb(GtkWidget *widget, MathButtons *buttons)
1417 gint dialog, field;
1419 dialog = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_dialog"));
1420 field = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_field"));
1422 if (finc_dialog_fields[dialog][field+1] == NULL) {
1423 GtkWidget *dialog_widget;
1424 dialog_widget = gtk_widget_get_toplevel(widget);
1425 if (gtk_widget_is_toplevel(dialog_widget)) {
1426 gtk_dialog_response(GTK_DIALOG(dialog_widget),
1427 GTK_RESPONSE_OK);
1428 return;
1431 else {
1432 GtkWidget *next_widget;
1433 next_widget = GET_WIDGET(buttons->priv->financial_ui, finc_dialog_fields[dialog][field+1]);
1434 gtk_widget_grab_focus(next_widget);
1439 void finc_response_cb(GtkWidget *widget, gint response_id, MathButtons *buttons);
1440 G_MODULE_EXPORT
1441 void
1442 finc_response_cb(GtkWidget *widget, gint response_id, MathButtons *buttons)
1444 int dialog;
1445 int i;
1446 MPNumber arg[4];
1447 GtkWidget *entry;
1449 if (response_id != GTK_RESPONSE_OK)
1450 return;
1452 dialog = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_dialog"));
1454 for (i = 0; i < 4; i++) {
1455 if (finc_dialog_fields[dialog][i] == NULL) {
1456 continue;
1458 entry = GET_WIDGET(buttons->priv->financial_ui, finc_dialog_fields[dialog][i]);
1459 mp_set_from_string(gtk_entry_get_text(GTK_ENTRY(entry)), 10, &arg[i]);
1460 gtk_entry_set_text(GTK_ENTRY(entry), "0");
1462 gtk_widget_grab_focus(GET_WIDGET(buttons->priv->financial_ui, finc_dialog_fields[dialog][0]));
1464 do_finc_expression(buttons->priv->equation, dialog, &arg[0], &arg[1], &arg[2], &arg[3]);
1468 void character_code_dialog_response_cb(GtkWidget *dialog, gint response_id, MathButtons *buttons);
1469 G_MODULE_EXPORT
1470 void
1471 character_code_dialog_response_cb(GtkWidget *dialog, gint response_id, MathButtons *buttons)
1473 const gchar *text;
1475 text = gtk_entry_get_text(GTK_ENTRY(buttons->priv->character_code_entry));
1477 if (response_id == GTK_RESPONSE_OK) {
1478 MPNumber x;
1479 int i = 0;
1481 mp_set_from_integer(0, &x);
1482 while (TRUE) {
1483 mp_add_integer(&x, text[i], &x);
1484 if (text[i+1]) {
1485 mp_shift(&x, 8, &x);
1486 i++;
1488 else
1489 break;
1492 math_equation_insert_number(buttons->priv->equation, &x);
1495 gtk_widget_hide(dialog);
1499 void character_code_dialog_activate_cb(GtkWidget *entry, MathButtons *buttons);
1500 G_MODULE_EXPORT
1501 void
1502 character_code_dialog_activate_cb(GtkWidget *entry, MathButtons *buttons)
1504 character_code_dialog_response_cb(buttons->priv->character_code_dialog, GTK_RESPONSE_OK, buttons);
1508 gboolean character_code_dialog_delete_cb(GtkWidget *dialog, GdkEvent *event, MathButtons *buttons);
1509 G_MODULE_EXPORT
1510 gboolean
1511 character_code_dialog_delete_cb(GtkWidget *dialog, GdkEvent *event, MathButtons *buttons)
1513 character_code_dialog_response_cb(dialog, GTK_RESPONSE_CANCEL, buttons);
1514 return TRUE;
1518 gboolean bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, MathButtons *buttons);
1519 G_MODULE_EXPORT
1520 gboolean
1521 bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, MathButtons *buttons)
1523 math_equation_toggle_bit(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(event_box), "bit_index")));
1524 return TRUE;
1528 static void
1529 remove_trailing_spaces(MathButtons *buttons)
1531 GtkTextMark *insert_mark;
1532 GtkTextIter start, end;
1533 insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER(buttons->priv->equation));
1534 gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(buttons->priv->equation), &end, insert_mark);
1535 start = end;
1536 while (gtk_text_iter_backward_char(&start)) {
1537 if (!g_unichar_isspace(gtk_text_iter_get_char(&start)))
1538 break;
1539 gtk_text_buffer_delete(GTK_TEXT_BUFFER(buttons->priv->equation), &start, &end);
1544 void set_superscript_cb(GtkWidget *widget, MathButtons *buttons);
1545 G_MODULE_EXPORT
1546 void
1547 set_superscript_cb(GtkWidget *widget, MathButtons *buttons)
1549 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1550 math_equation_set_number_mode(buttons->priv->equation, SUPERSCRIPT);
1551 if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(buttons->priv->equation))) {
1552 remove_trailing_spaces(buttons);
1555 else if (math_equation_get_number_mode(buttons->priv->equation) == SUPERSCRIPT)
1556 math_equation_set_number_mode(buttons->priv->equation, NORMAL);
1560 void set_subscript_cb(GtkWidget *widget, MathButtons *buttons);
1561 G_MODULE_EXPORT
1562 void
1563 set_subscript_cb(GtkWidget *widget, MathButtons *buttons)
1565 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1566 math_equation_set_number_mode(buttons->priv->equation, SUBSCRIPT);
1567 if (!gtk_text_buffer_get_has_selection(GTK_TEXT_BUFFER(buttons->priv->equation))) {
1568 remove_trailing_spaces(buttons);
1571 else if (math_equation_get_number_mode(buttons->priv->equation) == SUBSCRIPT)
1572 math_equation_set_number_mode(buttons->priv->equation, NORMAL);
1576 static void
1577 number_mode_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons)
1579 GList *i;
1580 NumberMode mode;
1582 mode = math_equation_get_number_mode(equation);
1584 for (i = buttons->priv->superscript_toggles; i; i = i->next) {
1585 GtkWidget *widget = i->data;
1586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUPERSCRIPT);
1588 for (i = buttons->priv->subscript_toggles; i; i = i->next) {
1589 GtkWidget *widget = i->data;
1590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUBSCRIPT);
1595 static void
1596 math_buttons_set_property(GObject *object,
1597 guint prop_id,
1598 const GValue *value,
1599 GParamSpec *pspec)
1601 MathButtons *self;
1603 self = MATH_BUTTONS(object);
1605 switch (prop_id) {
1606 case PROP_EQUATION:
1607 self->priv->equation = g_value_get_object(value);
1608 math_buttons_set_mode(self, self->priv->mode);
1609 g_signal_connect(self->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), self);
1610 g_signal_connect(self->priv->equation, "notify::number-mode", G_CALLBACK(number_mode_changed_cb), self);
1611 g_signal_connect(self->priv->equation, "notify::angle-units", G_CALLBACK(display_changed_cb), self);
1612 g_signal_connect(self->priv->equation, "notify::number-format", G_CALLBACK(display_changed_cb), self);
1613 number_mode_changed_cb(self->priv->equation, NULL, self);
1614 display_changed_cb(self->priv->equation, NULL, self);
1615 break;
1616 case PROP_MODE:
1617 math_buttons_set_mode(self, g_value_get_int(value));
1618 break;
1619 default:
1620 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1621 break;
1626 static void
1627 math_buttons_get_property(GObject *object,
1628 guint prop_id,
1629 GValue *value,
1630 GParamSpec *pspec)
1632 MathButtons *self;
1634 self = MATH_BUTTONS(object);
1636 switch (prop_id) {
1637 case PROP_EQUATION:
1638 g_value_set_object(value, self->priv->equation);
1639 break;
1640 case PROP_MODE:
1641 g_value_set_int(value, self->priv->mode);
1642 break;
1643 default:
1644 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1645 break;
1650 static void
1651 math_buttons_class_init(MathButtonsClass *klass)
1653 static GEnumValue button_mode_values[] =
1655 {BASIC, "basic", "basic"},
1656 {ADVANCED, "advanced", "advanced"},
1657 {FINANCIAL, "financial", "financial"},
1658 {PROGRAMMING, "programming", "programming"},
1659 {0, NULL, NULL}
1661 GObjectClass *object_class = G_OBJECT_CLASS(klass);
1663 object_class->get_property = math_buttons_get_property;
1664 object_class->set_property = math_buttons_set_property;
1666 g_type_class_add_private(klass, sizeof(MathButtonsPrivate));
1668 button_mode_type = g_enum_register_static("ButtonMode", button_mode_values);
1670 g_object_class_install_property(object_class,
1671 PROP_EQUATION,
1672 g_param_spec_object("equation",
1673 "equation",
1674 "Equation being controlled",
1675 math_equation_get_type(),
1676 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1677 g_object_class_install_property(object_class,
1678 PROP_MODE,
1679 g_param_spec_enum("mode",
1680 "mode",
1681 "Button mode",
1682 button_mode_type,
1683 BASIC,
1684 G_PARAM_READWRITE));
1688 static void
1689 math_buttons_init(MathButtons *buttons)
1691 buttons->priv = G_TYPE_INSTANCE_GET_PRIVATE(buttons, math_buttons_get_type(), MathButtonsPrivate);
1692 buttons->priv->programming_base = 10;
1693 gdk_color_parse("#0000FF", &buttons->priv->color_numbers);
1694 gdk_color_parse("#00FF00", &buttons->priv->color_action);
1695 gdk_color_parse("#FF0000", &buttons->priv->color_operator);
1696 gdk_color_parse("#00FFFF", &buttons->priv->color_function);
1697 gdk_color_parse("#FF00FF", &buttons->priv->color_memory);
1698 gdk_color_parse("#FFFFFF", &buttons->priv->color_group);
1699 g_signal_connect(G_OBJECT(buttons), "show", G_CALLBACK(load_buttons), NULL);