1/*
2 * Copyright (C) 2008-2012 Robert Ancell
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free Software
6 * Foundation, either version 3 of the License, or (at your option) any later
7 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
8 * license.
9 */
10
11public enum ButtonMode
12{
13    BASIC,
14    ADVANCED,
15    FINANCIAL,
16    PROGRAMMING,
17    KEYBOARD
18}
19
20public class MathButtons : Gtk.Box
21{
22    private MathEquation equation;
23
24    private ButtonMode _mode;
25    public ButtonMode mode
26    {
27        get { return _mode; }
28        set
29        {
30            if (_mode == value)
31                return;
32            _mode = value;
33
34            if (mode == ButtonMode.PROGRAMMING)
35                equation.number_base = _programming_base;
36            else
37                equation.number_base = 10;
38
39            load_buttons ();
40
41            converter.set_visible (mode == ButtonMode.ADVANCED || mode == ButtonMode.FINANCIAL);
42            if (mode == ButtonMode.ADVANCED)
43            {
44                converter.set_category (null);
45                converter.set_conversion (equation.source_units, equation.target_units);
46            }
47            else if (mode == ButtonMode.FINANCIAL)
48            {
49                converter.set_category ("currency");
50                converter.set_conversion (equation.source_currency, equation.target_currency);
51            }
52
53            update_view_more_visible ();
54            converter.view_more_active = false;
55            if (prog_view_more_button != null)
56                prog_view_more_button.active = false;
57            if (adv_panel != null)
58                (adv_panel as Hdy.Leaflet).visible_child_name = "basic";
59            if (fin_panel != null)
60                (fin_panel as Hdy.Leaflet).visible_child_name = "basic";
61            if (prog_leaflet != null)
62                prog_leaflet.visible_child_name = "basic";
63        }
64    }
65    private int _programming_base = 10;
66
67    private MathConverter converter;
68
69    private Gtk.Builder basic_ui;
70    private Gtk.Builder advanced_ui;
71    private Gtk.Builder financial_ui;
72    private Gtk.Builder programming_ui;
73
74    private Gtk.Widget bas_panel;
75    private Gtk.Widget adv_panel;
76    private Gtk.Widget fin_panel;
77    private Gtk.Widget prog_panel;
78    private Gtk.Widget? active_panel = null;
79
80    private Hdy.Leaflet prog_leaflet;
81    private Gtk.ToggleButton prog_view_more_button;
82
83    private Gtk.ComboBox base_combo;
84    private Gtk.Label base_label;
85    private Gtk.Label word_size_label;
86    private Gtk.Widget bit_panel;
87    private List<Gtk.Button> toggle_bit_buttons;
88
89    private Gtk.Dialog character_code_dialog;
90    private Gtk.Entry character_code_entry;
91
92    /* The names of each field in the dialogs for the financial functions */
93    private const string[] ctrm_entries =  {"ctrm_pint", "ctrm_fv", "ctrm_pv"};
94    private const string[] ddb_entries = {"ddb_cost", "ddb_life", "ddb_period"};
95    private const string[] fv_entries = {"fv_pmt", "fv_pint", "fv_n"};
96    private const string[] gpm_entries = {"gpm_cost", "gpm_margin"};
97    private const string[] pmt_entries = {"pmt_prin", "pmt_pint", "pmt_n"};
98    private const string[] pv_entries = {"pv_pmt", "pv_pint", "pv_n"};
99    private const string[] rate_entries = {"rate_fv", "rate_pv", "rate_n"};
100    private const string[] sln_entries = {"sln_cost", "sln_salvage", "sln_life"};
101    private const string[] syd_entries = {"syd_cost", "syd_salvage", "syd_life", "syd_period" };
102    private const string[] term_entries = {"term_pmt","term_fv", "term_pint"};
103
104    private SimpleActionGroup action_group = new SimpleActionGroup ();
105    private const ActionEntry[] action_entries = {
106        {"insert-general",       on_insert,               "s"                },
107        {"insert-digit",         on_insert_digit,         "i"                },
108        {"subtract",             on_subtract                                 },
109        {"square",               on_square                                   },
110        {"undo",                 on_undo                                     },
111        {"solve",                on_solve                                    },
112        {"clear",                on_clear                                    },
113        {"factorize",            on_factorize                                },
114        {"insert-exponent",      on_insert_exponent                          },
115        {"bitshift",             on_bitshift,             "i"                },
116        {"set-word-size",        on_set_word_size,        "i"                },
117        {"toggle-bit",           on_toggle_bit,           "i"                },
118        {"insert-character",     on_insert_character                         },
119        {"insert-numeric-point", on_insert_numeric_point                     },
120        {"set-number-mode",      on_set_number_mode,      "s", "'normal'"    },
121        {"launch-finc-dialog",   on_launch_finc_dialog,   "s"                }
122    };
123
124    public MathButtons (MathEquation equation)
125    {
126        Object (orientation: Gtk.Orientation.VERTICAL, vexpand_set: true);
127        show.connect (load_buttons);
128        this.equation = equation;
129
130        action_group.add_action_entries (action_entries, this);
131        insert_action_group ("cal", action_group);
132
133        equation.notify["display"].connect ((pspec) => { update_bit_panel (); });
134        equation.notify["number-mode"].connect ((pspec) => { number_mode_changed_cb (); });
135        equation.notify["angle-units"].connect ((pspec) => { update_bit_panel (); });
136        equation.notify["number-format"].connect ((pspec) => { update_bit_panel (); });
137        equation.notify["word-size"].connect ((pspec) => { word_size_changed_cb (); });
138        number_mode_changed_cb ();
139        update_bit_panel ();
140    }
141
142    private void load_finc_dialogs ()
143    {
144        load_finc_dialog ("ctrm_dialog", ctrm_entries, FinancialDialog.CTRM_DIALOG);
145        load_finc_dialog ("ddb_dialog", ddb_entries, FinancialDialog.DDB_DIALOG);
146        load_finc_dialog ("fv_dialog", fv_entries, FinancialDialog.FV_DIALOG);
147        load_finc_dialog ("gpm_dialog", gpm_entries, FinancialDialog.GPM_DIALOG);
148        load_finc_dialog ("pmt_dialog", pmt_entries, FinancialDialog.PMT_DIALOG);
149        load_finc_dialog ("pv_dialog", pv_entries, FinancialDialog.PV_DIALOG);
150        load_finc_dialog ("rate_dialog", rate_entries, FinancialDialog.RATE_DIALOG);
151        load_finc_dialog ("sln_dialog", sln_entries, FinancialDialog.SLN_DIALOG);
152        load_finc_dialog ("syd_dialog", syd_entries, FinancialDialog.SYD_DIALOG);
153        load_finc_dialog ("term_dialog", term_entries, FinancialDialog.TERM_DIALOG);
154    }
155
156    private void load_finc_dialog (string name, string[] entry_names, FinancialDialog function)
157    {
158        var dialog = financial_ui.get_object (name) as Gtk.Dialog;
159        dialog.set_data<int> ("finc-function", function);
160        dialog.response.connect (finc_response_cb);
161        for (var i = 0; i < entry_names.length; i++)
162        {
163            var entry = financial_ui.get_object (entry_names[i]) as Gtk.Entry;
164            if (i != entry_names.length - 1)
165                entry.set_data<Gtk.Entry> ("next-entry", financial_ui.get_object (entry_names[i+1]) as Gtk.Entry);
166            entry.activate.connect (finc_activate_cb);
167        }
168    }
169
170    private void on_insert (SimpleAction action, Variant? param)
171    {
172        equation.insert (param.get_string ());
173    }
174
175    private void on_insert_digit (SimpleAction action, Variant? param)
176    {
177        equation.insert_digit (param.get_int32 ());
178    }
179
180    private void on_subtract (SimpleAction action, Variant? param)
181    {
182        equation.insert_subtract ();
183    }
184
185    private void on_square (SimpleAction action, Variant? param)
186    {
187        equation.insert_square ();
188    }
189
190    private void on_undo (SimpleAction action, Variant? param)
191    {
192        equation.undo ();
193    }
194
195    private void on_solve (SimpleAction action, Variant? param)
196    {
197        equation.solve ();
198    }
199
200    private void on_clear (SimpleAction action, Variant? param)
201    {
202        equation.clear ();
203    }
204
205    private void on_factorize (SimpleAction action, Variant? param)
206    {
207        equation.factorize ();
208    }
209
210    private void on_insert_exponent (SimpleAction action, Variant? param)
211    {
212        equation.insert_exponent ();
213    }
214
215    private void on_bitshift (SimpleAction action, Variant? param)
216    {
217        equation.insert_shift (param.get_int32 ());
218    }
219
220    private void on_set_word_size (SimpleAction action, Variant? param)
221    {
222        equation.word_size = (param.get_int32 ());
223        string format = ngettext("%d-bit", "%d-bit", param.get_int32 ());
224        word_size_label.set_label(format.printf(param.get_int32 ()));
225    }
226
227    private void on_insert_numeric_point (SimpleAction action, Variant? param)
228    {
229        equation.insert_numeric_point ();
230    }
231
232    private void update_bit_panel ()
233    {
234        if (bit_panel == null)
235            return;
236
237        var x = equation.number;
238
239        uint64 bits = 0;
240        var enabled = x != null;
241        if (enabled)
242        {
243            var max = new Number.unsigned_integer (uint64.MAX);
244            var fraction = x.fractional_part ();
245            if (x.is_negative () || x.compare (max) > 0 || !fraction.is_zero ())
246                enabled = false;
247            else
248                bits = x.to_unsigned_integer ();
249        }
250
251        bit_panel.set_sensitive (enabled);
252        base_label.set_sensitive (enabled);
253
254        if (!enabled)
255            return;
256
257        var i = 0;
258        foreach (var button in toggle_bit_buttons)
259        {
260            var text = "0";
261            if ((bits & (1ULL << i)) != 0)
262                text = "1";
263            button.label = text;
264            i++;
265        }
266
267        var number_base = equation.number_base;
268        var label = "";
269        if (number_base != 8)
270            label += "%llo₈".printf (bits);
271        if (number_base != 10)
272        {
273            if (label != "")
274                label += " = ";
275            label += "%llu₁₀".printf (bits);
276        }
277        if (number_base != 16)
278        {
279            if (label != "")
280                label += " = ";
281            label += "%llX₁₆".printf (bits);
282        }
283
284        base_label.set_text (label);
285    }
286
287    private void base_combobox_changed_cb (Gtk.ComboBox combo)
288    {
289        programming_base = int.parse (combo.active_id);
290    }
291
292    private void base_changed_cb ()
293    {
294        if (mode != ButtonMode.PROGRAMMING)
295            return;
296
297        _programming_base = equation.number_base;
298
299        base_combo.active_id = _programming_base.to_string ();
300        update_bit_panel ();
301
302    }
303
304    private void update_view_more_visible ()
305    {
306        bool visible;
307
308        switch (mode)
309        {
310        default:
311        case ButtonMode.BASIC:
312            visible = false;
313            break;
314        case ButtonMode.ADVANCED:
315            visible = adv_panel != null && (adv_panel as Hdy.Leaflet).folded;
316            break;
317        case ButtonMode.FINANCIAL:
318            visible = fin_panel != null && (fin_panel as Hdy.Leaflet).folded;
319            break;
320        case ButtonMode.PROGRAMMING:
321            visible = prog_leaflet != null && prog_leaflet.folded;
322            break;
323        }
324
325        converter.view_more_visible = visible;
326        if (prog_view_more_button != null)
327            prog_view_more_button.visible = visible;
328    }
329
330    private void update_view_more_active ()
331    {
332        switch (mode)
333        {
334        default:
335        case ButtonMode.BASIC:
336            break;
337        case ButtonMode.ADVANCED:
338            if (adv_panel != null)
339                converter.view_more_active = prog_view_more_button.active = (adv_panel as Hdy.Leaflet).visible_child_name == "advanced";
340            break;
341        case ButtonMode.FINANCIAL:
342            if (fin_panel != null)
343                converter.view_more_active = prog_view_more_button.active = (fin_panel as Hdy.Leaflet).visible_child_name == "advanced";
344            break;
345        case ButtonMode.PROGRAMMING:
346            if (prog_panel != null)
347                converter.view_more_active = prog_view_more_button.active = prog_leaflet.visible_child_name == "advanced";
348            break;
349        }
350    }
351
352    private Gtk.Widget load_mode (ButtonMode mode)
353    {
354        Gtk.Builder builder;
355        string builder_resource;
356        switch (mode)
357        {
358        default:
359        case ButtonMode.BASIC:
360            if (bas_panel != null)
361                return bas_panel;
362            builder = basic_ui = new Gtk.Builder ();
363            builder_resource = "buttons-basic.ui";
364            break;
365        case ButtonMode.ADVANCED:
366            if (adv_panel != null)
367                return adv_panel;
368            builder = advanced_ui = new Gtk.Builder ();
369            builder_resource = "buttons-advanced.ui";
370            break;
371        case ButtonMode.FINANCIAL:
372            if (fin_panel != null)
373                return fin_panel;
374            builder = financial_ui = new Gtk.Builder ();
375            builder_resource = "buttons-financial.ui";
376            break;
377        case ButtonMode.PROGRAMMING:
378            if (prog_panel != null)
379                return prog_panel;
380            builder = programming_ui = new Gtk.Builder ();
381            builder_resource = "buttons-programming.ui";
382            break;
383        }
384
385        try
386        {
387            builder.add_from_resource ("/org/gnome/calculator/%s".printf(builder_resource));
388        }
389        catch (Error e)
390        {
391            error ("Error loading button UI: %s", e.message);
392        }
393
394        var panel = builder.get_object ("button_panel") as Gtk.Widget;
395        pack_end (panel, true, true, 0);
396
397        switch (mode)
398        {
399        default:
400        case ButtonMode.BASIC:
401            bas_panel = panel;
402            break;
403        case ButtonMode.ADVANCED:
404            adv_panel = panel;
405
406            converter.bind_property ("view-more-active", panel, "visible-child-name",
407                                     BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL,
408                                     (binding, from, ref to) => { to.set_string (from.get_boolean () ? "advanced" : "basic"); return true; },
409                                     (binding, from, ref to) => { to.set_boolean (from.get_string () == "advanced"); return true; });
410            adv_panel.notify["folded"].connect (update_view_more_visible);
411            break;
412        case ButtonMode.FINANCIAL:
413            fin_panel = panel;
414
415            converter.bind_property ("view-more-active", panel, "visible-child-name",
416                                     BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL,
417                                     (binding, from, ref to) => { to.set_string (from.get_boolean () ? "advanced" : "basic"); return true; },
418                                     (binding, from, ref to) => { to.set_boolean (from.get_string () == "advanced"); return true; });
419            fin_panel.notify["folded"].connect (update_view_more_visible);
420            break;
421        case ButtonMode.PROGRAMMING:
422            prog_panel = panel;
423            prog_leaflet = builder.get_object ("leaflet") as Hdy.Leaflet;
424
425            prog_view_more_button = builder.get_object ("view_more_button") as Gtk.ToggleButton;
426            prog_view_more_button.bind_property ("active", prog_leaflet, "visible-child-name",
427                                                 BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL,
428                                                 (binding, from, ref to) => { to.set_string (from.get_boolean () ? "advanced" : "basic"); return true; },
429                                                 (binding, from, ref to) => { to.set_boolean (from.get_string () == "advanced"); return true; });
430            prog_leaflet.notify["folded"].connect (update_view_more_visible);
431            break;
432        }
433
434        /* Configure buttons */
435        var button = builder.get_object ("calc_numeric_point_button") as Gtk.Button;
436        if (button != null)
437            button.set_label (equation.serializer.get_radix ().to_string ());
438
439        var menu_button = builder.get_object ("calc_shift_left_button") as Gtk.MenuButton;
440        if (menu_button != null)
441            menu_button.menu_model = create_shift_menu (true);
442        menu_button = builder.get_object ("calc_shift_right_button") as Gtk.MenuButton;
443        if (menu_button != null)
444            menu_button.menu_model = create_shift_menu (false);
445        menu_button = builder.get_object ("calc_word_size_button") as Gtk.MenuButton;
446        if (menu_button != null)
447            menu_button.menu_model = create_word_size_menu ();
448        menu_button = builder.get_object ("calc_memory_button") as Gtk.MenuButton;
449        if (menu_button != null)
450            menu_button.popover = new MathVariablePopover (equation);
451        menu_button = builder.get_object ("calc_function_button") as Gtk.MenuButton;
452        if (menu_button != null)
453            menu_button.popover = new MathFunctionPopover (equation);
454
455        if (mode == ButtonMode.PROGRAMMING)
456        {
457            base_label = builder.get_object ("base_label") as Gtk.Label;
458            character_code_dialog = builder.get_object ("character_code_dialog") as Gtk.Dialog;
459            character_code_dialog.response.connect (character_code_dialog_response_cb);
460            character_code_dialog.delete_event.connect (character_code_dialog_delete_cb);
461            character_code_entry = builder.get_object ("character_code_entry") as Gtk.Entry;
462            character_code_entry.activate.connect (character_code_dialog_activate_cb);
463
464            bit_panel = builder.get_object ("bit_table") as Gtk.Widget;
465            toggle_bit_buttons = new List<Gtk.Button> ();
466            var i = 0;
467            while (true)
468            {
469                var name = "toggle_bit_%d_button".printf (i);
470                var toggle_bit_button = builder.get_object (name) as Gtk.Button;
471                if (toggle_bit_button == null)
472                    break;
473                toggle_bit_buttons.append (toggle_bit_button);
474                i++;
475            }
476            toggle_bit_buttons.reverse ();
477            word_size_label = builder.get_object ("word_size_label") as Gtk.Label;
478            base_combo = builder.get_object ("base_combo") as Gtk.ComboBox;
479            base_combo.changed.connect (base_combobox_changed_cb);
480            equation.notify["number-base"].connect ((pspec) => { base_changed_cb (); } );
481            base_changed_cb ();
482            word_size_changed_cb ();
483        }
484
485        /* Setup financial functions */
486        if (mode == ButtonMode.FINANCIAL)
487            load_finc_dialogs ();
488
489        builder.connect_signals (this);
490
491        update_bit_panel ();
492        update_view_more_visible ();
493
494        return panel;
495    }
496
497    private void converter_changed_cb ()
498    {
499        Unit from_unit, to_unit;
500        converter.get_conversion (out from_unit, out to_unit);
501        if (mode == ButtonMode.FINANCIAL)
502        {
503            equation.source_currency = from_unit.name;
504            equation.target_currency = to_unit.name;
505        }
506        else
507        {
508            equation.source_units = from_unit.name;
509            equation.target_units = to_unit.name;
510        }
511    }
512
513    private void load_buttons ()
514    {
515        if (!get_visible ())
516            return;
517
518        if (converter == null)
519        {
520            converter = new MathConverter (equation);
521            converter.changed.connect (converter_changed_cb);
522            pack_start (converter, false, true, 0);
523            converter.notify["view-more-active"].connect (() => {
524                if (adv_panel != null)
525                    (adv_panel as Hdy.Leaflet).visible_child_name = converter.view_more_active ? "advanced" : "basic";
526                if (fin_panel != null)
527                    (fin_panel as Hdy.Leaflet).visible_child_name = converter.view_more_active ? "advanced" : "basic";
528            });
529        }
530
531        var panel = load_mode (mode);
532        if (active_panel == panel)
533            return;
534
535        /* Hide old buttons */
536        if (active_panel != null)
537            active_panel.hide ();
538
539        /* Load and display new buttons */
540        active_panel = panel;
541        if (panel != null)
542            panel.show ();
543    }
544
545    public int programming_base
546    {
547        get { return _programming_base; }
548        set
549        {
550            if (_programming_base == value)
551                return;
552
553            _programming_base = value;
554
555            if (mode == ButtonMode.PROGRAMMING)
556                equation.number_base = value;
557        }
558    }
559
560    private Menu create_shift_menu (bool shift_left)
561    {
562        var shift_menu = new Menu ();
563
564        for (var i = 1; i < 16; i++)
565        {
566            string format = ngettext ("%d place", "%d places", i);
567            if (i < 10) // Provide mnemonic for shifting [0..9] places
568                format = "_" + format;
569
570            var positions = (shift_left) ? i : -i;
571            shift_menu.append (format.printf (i), "cal.bitshift(%d)".printf (positions));
572        }
573
574        return shift_menu;
575    }
576
577    private Menu create_word_size_menu ()
578    {
579        var word_size_menu = new Menu ();
580        var i = 64;
581        string format = ngettext ("%d-bit", "%d-bit", i);
582        word_size_menu.append(format.printf (i), "cal.set-word-size(%d)".printf (i));
583        i = 32;
584        word_size_menu.append(format.printf (i), "cal.set-word-size(%d)".printf (i));
585        i = 16;
586        word_size_menu.append(format.printf (i), "cal.set-word-size(%d)".printf (i));
587        i = 8;
588        word_size_menu.append(format.printf (i), "cal.set-word-size(%d)".printf (i));
589
590        return word_size_menu;
591    }
592
593    private void word_size_changed_cb ()
594    {
595        var size = equation.word_size;
596        string format = ngettext ("%d-bit", "%d-bit", size);
597        word_size_label.set_label (format.printf(size));
598        update_bit_button_sensitivities ();
599    }
600
601    private void update_bit_button_sensitivities ()
602    {
603        var i = 0;
604        foreach (var button in toggle_bit_buttons)
605        {
606        if (i < equation.word_size)
607        {
608            button.set_sensitive (true);
609        }
610        else
611        {
612            if (button.label == "1")
613            {
614            equation.toggle_bit (63-i);
615            }
616            button.set_sensitive (false);
617        }
618            i++;
619        }
620    }
621    private void on_launch_finc_dialog (SimpleAction action, Variant? param)
622    {
623        var name = param.get_string ();
624        var dialog = financial_ui.get_object (name) as Gtk.Dialog;
625        dialog.run ();
626        dialog.hide ();
627    }
628
629    private void on_insert_character (SimpleAction action, Variant? param)
630    {
631        character_code_dialog.present ();
632    }
633
634    private void finc_activate_cb (Gtk.Widget widget)
635    {
636        var next_entry = widget.get_data<Gtk.Entry> ("next-entry");
637        if (next_entry == null)
638        {
639            var dialog = widget.get_toplevel () as Gtk.Dialog;
640            if (dialog.is_toplevel ())
641            {
642                dialog.response (Gtk.ResponseType.OK);
643                return;
644            }
645        }
646        else
647            next_entry.grab_focus ();
648    }
649
650    private void finc_response_cb (Gtk.Widget widget, int response_id)
651    {
652        if (response_id != Gtk.ResponseType.OK)
653            return;
654
655        var function = (FinancialDialog) widget.get_data<int> ("finc-function");
656        var entries = new string[0];
657        switch (function)
658        {
659        case FinancialDialog.CTRM_DIALOG:
660            entries = ctrm_entries;
661            break;
662        case FinancialDialog.DDB_DIALOG:
663            entries = ddb_entries;
664            break;
665        case FinancialDialog.FV_DIALOG:
666            entries = fv_entries;
667            break;
668        case FinancialDialog.GPM_DIALOG:
669            entries = gpm_entries;
670            break;
671        case FinancialDialog.PMT_DIALOG:
672            entries = pmt_entries;
673            break;
674        case FinancialDialog.PV_DIALOG:
675            entries = pv_entries;
676            break;
677        case FinancialDialog.RATE_DIALOG:
678            entries = rate_entries;
679            break;
680        case FinancialDialog.SLN_DIALOG:
681            entries = sln_entries;
682            break;
683        case FinancialDialog.SYD_DIALOG:
684            entries = syd_entries;
685            break;
686        case FinancialDialog.TERM_DIALOG:
687            entries = term_entries;
688            break;
689        }
690
691        Number arg[4] = { new Number.integer (0), new Number.integer (0), new Number.integer (0), new Number.integer (0) };
692        for (var i = 0; i < entries.length; i++)
693        {
694            var entry = financial_ui.get_object (entries[i]) as Gtk.Entry;
695            arg[i] = mp_set_from_string (entry.get_text ());
696            entry.set_text ("0");
697        }
698        var first_entry = financial_ui.get_object (entries[0]) as Gtk.Entry;
699        first_entry.grab_focus ();
700
701        do_finc_expression (equation, function, arg[0], arg[1], arg[2], arg[3]);
702    }
703
704    private void character_code_dialog_response_cb (Gtk.Widget dialog, int response_id)
705    {
706        string text = character_code_entry.get_text ();
707        if (response_id == Gtk.ResponseType.OK)
708        {
709            var x = new Number.integer (0);
710            var decoded = text.data;
711            var len = decoded.length;
712            for (var i = 0; i < len; i++)
713            {
714                x = x.add (new Number.integer (decoded[i]));
715                if(i != (len - 1))
716                {
717                    x = x.shift (8);
718                }
719            }
720            equation.insert_number (x);
721        }
722
723        dialog.hide ();
724    }
725
726    private void character_code_dialog_activate_cb (Gtk.Widget entry)
727    {
728        character_code_dialog_response_cb (character_code_dialog, Gtk.ResponseType.OK);
729    }
730
731    private bool character_code_dialog_delete_cb (Gtk.Widget dialog, Gdk.EventAny event)
732    {
733        character_code_dialog_response_cb (dialog, Gtk.ResponseType.CANCEL);
734        return true;
735    }
736
737    private void on_toggle_bit (SimpleAction action, Variant? param)
738    {
739        equation.toggle_bit (param.get_int32 ());
740    }
741
742    private void on_set_number_mode (SimpleAction action, Variant? param)
743    {
744        if (param.get_string () == action.state.get_string ())
745            equation.number_mode = NumberMode.NORMAL;
746        else if (param.get_string () == "superscript")
747        {
748            equation.number_mode = NumberMode.SUPERSCRIPT;
749            if (!equation.has_selection)
750                equation.remove_trailing_spaces ();
751        }
752        else if (param.get_string () == "subscript")
753        {
754            equation.number_mode = NumberMode.SUBSCRIPT;
755            if (!equation.has_selection)
756                equation.remove_trailing_spaces ();
757        }
758    }
759
760    private void number_mode_changed_cb ()
761    {
762        if (equation.number_mode == NumberMode.SUPERSCRIPT)
763            action_group.change_action_state ("set-number-mode", "superscript");
764        else if (equation.number_mode == NumberMode.SUBSCRIPT)
765            action_group.change_action_state ("set-number-mode", "subscript");
766        else
767            action_group.change_action_state ("set-number-mode", "normal");
768    }
769}
770