[gcalctool] Split the UI into different files and stop storing it in a global variable



commit 7b44ee71bb0c5a78132fb0f23a709f5b87f00d93
Author: Robert Ancell <robert ancell gmail com>
Date:   Sun Apr 4 14:46:18 2010 +1000

    Split the UI into different files and stop storing it in a global variable

 data/Makefile.am     |    2 +-
 data/gcalctool.ui    |    8 +-
 data/gcalctoolrc     |  132 ---
 src/Makefile.am      |   10 +-
 src/calctool.c       |    6 +-
 src/calctool.h       |    2 +
 src/display.c        |   53 +-
 src/gtk.c            | 2270 --------------------------------------------------
 src/ui-buttons.c     |  657 +++++++++++++++
 src/ui-buttons.h     |   24 +
 src/ui-display.c     |  495 +++++++++++
 src/ui-display.h     |   31 +
 src/ui-financial.c   |  359 ++++++++
 src/ui-financial.h   |   24 +
 src/ui-internal.h    |   30 +
 src/ui-preferences.c |  321 +++++++
 src/ui-preferences.h |   26 +
 src/ui.c             |  393 +++++++++
 src/ui.h             |   83 ++-
 19 files changed, 2471 insertions(+), 2455 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 92a47c1..0995645 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -20,7 +20,7 @@ Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop)
 
 man1_MANS = gcalctool.1
 
-EXTRA_DIST = $(ui_DATA) $(schema_in_files) gcalctool.desktop.in $(man1_MANS) gcalctoolrc
+EXTRA_DIST = $(ui_DATA) $(schema_in_files) gcalctool.desktop.in $(man1_MANS)
 
 DISTCLEANFILES = \
 	Makefile.in \
diff --git a/data/gcalctool.ui b/data/gcalctool.ui
index d90de31..e7d0f50 100644
--- a/data/gcalctool.ui
+++ b/data/gcalctool.ui
@@ -94,7 +94,7 @@
                         <property name="label" translatable="yes" comments="View|Basic menu item">_Basic</property>
                         <property name="use_underline">True</property>
                         <property name="active">True</property>
-                        <signal name="activate" handler="mode_radio_cb"/>
+                        <signal name="activate" handler="mode_changed_cb"/>
                       </object>
                     </child>
                     <child>
@@ -103,7 +103,7 @@
                         <property name="label" translatable="yes" comments="View|Advanced menu item">_Advanced</property>
                         <property name="use_underline">True</property>
                         <property name="group">view_basic_menu</property>
-                        <signal name="activate" handler="mode_radio_cb"/>
+                        <signal name="activate" handler="mode_changed_cb"/>
                       </object>
                     </child>
                     <child>
@@ -112,7 +112,7 @@
                         <property name="label" translatable="yes" comments="View|Financial menu item">_Financial</property>
                         <property name="use_underline">True</property>
                         <property name="group">view_basic_menu</property>
-                        <signal name="activate" handler="mode_radio_cb"/>
+                        <signal name="activate" handler="mode_changed_cb"/>
                       </object>
                     </child>
                     <child>
@@ -121,7 +121,7 @@
                         <property name="label" translatable="yes" comments="View|Programming menu item">_Programming</property>
                         <property name="use_underline">True</property>
                         <property name="group">view_basic_menu</property>
-                        <signal name="activate" handler="mode_radio_cb"/>
+                        <signal name="activate" handler="mode_changed_cb"/>
                       </object>
                     </child>
                   </object>
diff --git a/src/Makefile.am b/src/Makefile.am
index 6b22fb7..d552067 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,8 +32,16 @@ gcalctool_SOURCES = \
 	financial.h \
 	register.c \
 	register.h \
-	gtk.c \
+	ui.c \
 	ui.h \
+	ui-buttons.c \
+	ui-buttons.h \
+	ui-display.c \
+	ui-display.h \
+	ui-financial.c \
+	ui-financial.h \
+	ui-preferences.c \
+	ui-preferences.h \
 	unittest.c \
 	unittest.h
 
diff --git a/src/calctool.c b/src/calctool.c
index 46b1070..eb71c2a 100644
--- a/src/calctool.c
+++ b/src/calctool.c
@@ -36,6 +36,8 @@
 static CalculatorVariables calc_state;
 CalculatorVariables *v;
 
+GCalctoolUI *X;
+
 static void
 version()
 {
@@ -221,8 +223,8 @@ main(int argc, char **argv)
 
     get_options(argc, argv);
 
-    ui_load();
-    ui_start();
+    X = ui_new();
+    ui_start(X);
 
     currency_free_resources();
 
diff --git a/src/calctool.h b/src/calctool.h
index f945057..0f1f7bf 100644
--- a/src/calctool.h
+++ b/src/calctool.h
@@ -25,6 +25,7 @@
 #include "config.h"
 #include "mp.h"
 #include "display.h"
+#include "ui.h"
 
 /* To make lint happy. */
 #define SNPRINTF     (void) snprintf
@@ -59,5 +60,6 @@ typedef struct {
 } CalculatorVariables;
 
 extern CalculatorVariables *v; /* Calctool variables and options. */
+extern GCalctoolUI *X;
 
 #endif /* CALCTOOL_H */
diff --git a/src/display.c b/src/display.c
index b3ed337..650047e 100644
--- a/src/display.c
+++ b/src/display.c
@@ -32,6 +32,7 @@
 #include "mp-equation.h"
 #include "register.h"
 #include "currency.h"
+#include "calctool.h"
 
 static GCDisplayState *
 get_state(GCDisplay *display)
@@ -203,7 +204,7 @@ display_set_number(GCDisplay *display, const MPNumber *x)
     display_set_string(display, text, -1);
 
     enabled = display_get_unsigned_integer(display, &bit_value);
-    ui_set_bitfield(enabled, bit_value);
+    ui_set_bitfield(X, enabled, bit_value);
 }
 
 
@@ -251,7 +252,7 @@ display_refresh(GCDisplay *display)
 
     cursor = display_get_cursor(display);
     display_make_text(display, localized, MAX_LOCALIZED, &cursor);
-    ui_set_display(localized, cursor);
+    ui_set_display(X, localized, cursor);
 }
 
 
@@ -286,7 +287,7 @@ display_set_cursor(GCDisplay *display, int cursor)
 void
 display_set_error(GCDisplay *display, const char *message)
 {
-    ui_set_statusbar(message);
+    ui_set_statusbar(X, message);
 }
 
 
@@ -314,24 +315,6 @@ copy_state(GCDisplayState *dst, GCDisplayState *src)
 }
 
 
-static void
-update_undo_redo_button_sensitivity(GCDisplay *display)
-{
-    int undo = 0;
-    int redo = 0;
-
-    if (display->h.current != display->h.end) {
-        redo = 1;
-    }
-
-    if (display->h.current != display->h.begin) {
-        undo = 1;
-    }
-
-    ui_set_undo_enabled(undo, redo);
-}
-
-
 void
 display_clear_stack(GCDisplay *display)
 {
@@ -344,7 +327,6 @@ display_clear_stack(GCDisplay *display)
         i = ((i + 1) % UNDO_HISTORY_LENGTH);
     }
     display->h.begin = display->h.end = display->h.current;
-    update_undo_redo_button_sensitivity(display);
 }
 
 
@@ -376,7 +358,6 @@ display_push(GCDisplay *display)
     }
 
     copy_state(&(display->h.e[display->h.current]), &(display->h.e[c]));
-    update_undo_redo_button_sensitivity(display);
 }
 
 
@@ -385,11 +366,10 @@ display_pop(GCDisplay *display)
 {
     if (display->h.current != display->h.begin) {
         display->h.current = ((display->h.current - 1) % UNDO_HISTORY_LENGTH);
-        ui_set_statusbar("");
+        ui_set_statusbar(X, "");
     } else {
-        ui_set_statusbar(_("No undo history"));
+        ui_set_statusbar(X, _("No undo history"));
     }
-    update_undo_redo_button_sensitivity(display);
 
     display_refresh(display);
 }
@@ -400,11 +380,10 @@ display_unpop(GCDisplay *display)
 {
     if (display->h.current != display->h.end) {
         display->h.current = ((display->h.current + 1) % UNDO_HISTORY_LENGTH);
-        ui_set_statusbar("");
+        ui_set_statusbar(X, "");
     } else {
-        ui_set_statusbar(_("No redo steps"));
+        ui_set_statusbar(X, _("No redo steps"));
     }
-    update_undo_redo_button_sensitivity(display);
     get_state(display)->cursor = -1;
     display_refresh(display);
 }
@@ -448,7 +427,7 @@ display_insert(GCDisplay *display, int cursor_start, int cursor_end, const char
         }
 
         cursor = 0;
-        for (c = ui_get_display(); *c; c = g_utf8_next_char(c), cursor++) {
+        for (c = ui_get_display(X); *c; c = g_utf8_next_char(c), cursor++) {
             gboolean use = TRUE;
 
             /* Ignore selected part */
@@ -502,7 +481,7 @@ display_backspace(GCDisplay *display, int cursor_start, int cursor_end)
     /* If cursor is at end of the line then delete the last character preserving accuracy */
     if (cursor_start < 0) {
         int len;
-        len = g_utf8_strlen(ui_get_display(), -1);
+        len = g_utf8_strlen(ui_get_display(X), -1);
         display_insert(display, len - 1, len, "");
     } else if (cursor_start != cursor_end) {
         display_insert(display, cursor_start, cursor_end, "");
@@ -933,7 +912,7 @@ do_shift(GCDisplay *display, int count)
     if (!display_is_usable_number(display, &z)) {
         /* Translators: This message is displayed in the status bar when a bit
            shift operation is performed and the display does not contain a number */
-        ui_set_statusbar(_("No sane value to bitwise shift"));
+        ui_set_statusbar(X, _("No sane value to bitwise shift"));
     }
     else {
         mp_shift(&z, count, display_get_answer(display));
@@ -949,7 +928,7 @@ do_factorize()
 
     if (!display_is_usable_number(&v->display, &value)) {
         /* Translators: Error displayed when trying to factorize a non-integer value */
-        ui_set_statusbar(_("Need an integer to factorize"));
+        ui_set_statusbar(X, _("Need an integer to factorize"));
         return;
     }
     display_clear(&v->display);
@@ -975,7 +954,7 @@ do_sto(GCDisplay *display, const char *name)
     MPNumber t;
 
     if (!display_is_usable_number(display, &t))
-        ui_set_statusbar(_("No sane value to store"));
+        ui_set_statusbar(X, _("No sane value to store"));
     else
         register_set_value(name, &t);
 }
@@ -1006,7 +985,7 @@ display_do_function(GCDisplay *display, int function, gpointer arg, int cursor_s
     display_set_cursor(display, cursor_start);
     ans = display_get_answer(display);
 
-    ui_set_statusbar("");
+    ui_set_statusbar(X, "");
 
     switch (function) {
         case FN_CLEAR:
@@ -1126,7 +1105,7 @@ display_do_function(GCDisplay *display, int function, gpointer arg, int cursor_s
                         break;
                 }
                 if (message)
-                    ui_set_statusbar(message);
+                    ui_set_statusbar(X, message);
             }
             break;
 
@@ -1140,5 +1119,5 @@ display_do_function(GCDisplay *display, int function, gpointer arg, int cursor_s
     }
 
     enabled = display_get_unsigned_integer(display, &bit_value);
-    ui_set_bitfield(enabled, bit_value);
+    ui_set_bitfield(X, enabled, bit_value);
 }
diff --git a/src/ui-buttons.c b/src/ui-buttons.c
new file mode 100644
index 0000000..9326a4e
--- /dev/null
+++ b/src/ui-buttons.c
@@ -0,0 +1,657 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include "ui-buttons.h"
+#include "ui-internal.h"
+#include "register.h"
+
+#define UI_BASIC_FILE       UI_DIR "/buttons-basic.ui"
+#define UI_ADVANCED_FILE    UI_DIR "/buttons-advanced.ui"
+#define UI_FINANCIAL_FILE   UI_DIR "/buttons-financial.ui"
+#define UI_PROGRAMMING_FILE UI_DIR "/buttons-programming.ui"
+
+#define GET_OBJECT(ui, name) \
+          gtk_builder_get_object((ui), (name))
+#define GET_WIDGET(ui, name) \
+          GTK_WIDGET(GET_OBJECT(ui, name))
+
+static char *registers[] = {"a", "b", "c", "x", "y", "z", NULL};
+
+typedef enum {
+    POPUP_RIGHT,     /* Place popup to right of baseframe */
+    POPUP_LEFT,      /* Place popup to left of baseframe */
+    POPUP_ABOVE,     /* Place popup above baseframe */
+    POPUP_BELOW,     /* Place popup below baseframe */
+    POPUP_LOR,       /* Place popup to right or left of baseframe */
+    POPUP_AOB,       /* Place popup above or below baseframe */
+    POPUP_CENTERED   /* Center popup within baseframe */
+} PopupLocation;
+
+#define WM_WIDTH_FACTOR  10
+#define WM_HEIGHT_FACTOR 30
+
+typedef struct {
+    const char *widget_name;
+    const char *data;
+/*    const char *colour;
+    int alpha;*/
+} ButtonData;
+
+static ButtonData button_data[] = {
+    {"pi",                 "Ï?"},
+    {"eulers_number",      "e"},
+    {"random",             "rand"},
+    {"ans",                "ans"},
+    {"numeric_point",      "."},
+    {"add",                "+"},
+    {"multiply",           "Ã?"},
+    {"divide",             "÷"},
+    {"modulus_divide",     " mod "},
+    {"x_pow_y",            "^"},
+    {"percentage",         "%"},
+    {"factorial",          "!"},
+    {"abs",                "|"},
+    {"root",               "â??"},
+    {"logarithm",          "log"},
+    {"natural_logarithm",  "ln"},
+    {"sine",               "sin"},
+    {"cosine",             "cos"},
+    {"tangent",            "tan"},
+    {"hyperbolic_sine",    "sinh"},
+    {"hyperbolic_cosine",  "cosh"},
+    {"hyperbolic_tangent", "tanh"},
+    {"inverse",            "�¹"},
+    {"and",                "â?§"},
+    {"or",                 "â?¨"},
+    {"xor",                "â?»"},
+    {"not",                "¬"},
+    {"integer_portion",    "int"},
+    {"fractional_portion", "frac"},
+    {"ones_complement",    "ones"},
+    {"twos_complement",    "twos"},
+    {"trunc",              "trunc"},
+    {"start_group",        "("},
+    {"end_group",          ")"},
+    {NULL, NULL}
+};
+
+static const char *subscript_digits[] = {"â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", NULL};
+static const char *superscript_digits[] = {"�", "¹", "²", "³", "�", "�", "�", "�", "�", "�", NULL};
+
+
+static void
+set_tint (GtkWidget *widget, GdkColor *tint, gint alpha)
+{
+    GtkStyle *style;
+    int j;
+  
+    if (!widget)
+      return;
+
+    style = gtk_widget_get_style(widget);
+  
+    for (j = 0; j < 5; j++) {
+        GdkColor color;
+
+        color.red = (style->bg[j].red * (10 - alpha) + tint->red * alpha) / 10;
+        color.green = (style->bg[j].green * (10 - alpha) + tint->green * alpha) / 10;
+        color.blue = (style->bg[j].blue * (10 - alpha) + tint->blue * alpha) / 10;
+        gdk_colormap_alloc_color(gdk_colormap_get_system(), &color, FALSE, TRUE);
+        gtk_widget_modify_bg(widget, j, &color);
+    }
+}
+
+
+static void set_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const gpointer value)
+{
+    GObject *object;  
+    object = gtk_builder_get_object(ui, object_name);
+    if (object)
+        g_object_set_data(object, name, value);
+}
+
+static void set_string_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const char *value)
+{
+    GObject *object;
+    object = gtk_builder_get_object(ui, object_name);
+    if (object)
+        g_object_set_data(object, name, (gpointer)value); // FIXME: Copy?
+}
+
+static void set_int_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, gint value)
+{
+    set_data(ui, object_name, name, GINT_TO_POINTER(value));
+}
+
+
+void
+ui_load_mode(GCalctoolUI *ui, ModeType mode)
+{
+    GtkBuilder *builder, **builder_ptr;
+    gint i;
+    gchar name[MAXLINE];
+    GdkColor colour_numbers, colour_action, colour_operator, colour_function, colour_memory, colour_trig, colour_group;
+    const gchar *builder_file;
+    gchar *objects[] = { "button_panel", NULL };
+    GtkWidget *widget, **panel;
+
+    colour_numbers.red = 0;
+    colour_numbers.green = 0;
+    colour_numbers.blue = 65535;
+    colour_action.red = 0;
+    colour_action.green = 65535;
+    colour_action.blue = 0;
+    colour_operator.red = 65535;
+    colour_operator.green = 0;
+    colour_operator.blue = 0;
+    colour_function.red = 0;
+    colour_function.green = 65535;
+    colour_function.blue = 65535;
+    colour_memory.red = 65535;
+    colour_memory.green = 0;
+    colour_memory.blue = 65535;
+    colour_trig.red = 65535;
+    colour_trig.green = 65535;
+    colour_trig.blue = 0;
+    colour_group.red = 65535;
+    colour_group.green = 65535;
+    colour_group.blue = 65535;
+
+    // FIXME
+    /* Get labels from popup menus */
+    for (i = 0; registers[i]; i++) {
+        SNPRINTF(name, MAXLINE, "store_menu_item%d", i);
+        widget = GET_WIDGET(ui->ui, name);
+        g_object_set_data(G_OBJECT(widget), "register_id", registers[i]);
+        ui->memory_store_labels[i] = gtk_bin_get_child(GTK_BIN(widget));
+
+        SNPRINTF(name, MAXLINE, "recall_menu_item%d", i);
+        widget = GET_WIDGET(ui->ui, name);
+        g_object_set_data(G_OBJECT(widget), "register_id", registers[i]);
+        ui->memory_recall_labels[i] = gtk_bin_get_child(GTK_BIN(widget));
+    }
+  
+    switch (mode) {
+    case BASIC:
+        builder_ptr = &ui->basic_ui;
+        builder_file = UI_BASIC_FILE;
+        panel = &ui->bas_panel;
+        break;
+    case ADVANCED:
+        builder_ptr = &ui->advanced_ui;
+        builder_file = UI_ADVANCED_FILE;
+        panel = &ui->adv_panel;
+        break;
+    case FINANCIAL:
+        builder_ptr = &ui->financial_ui;
+        builder_file = UI_FINANCIAL_FILE;
+        panel = &ui->fin_panel;
+        break;
+    case PROGRAMMING:
+        builder_ptr = &ui->programming_ui;
+        builder_file = UI_PROGRAMMING_FILE;
+        panel = &ui->prog_panel;
+        break;
+    }
+
+    builder = *builder_ptr = gtk_builder_new();
+    // FIXME: Show dialog if failed to load
+    gtk_builder_add_objects_from_file(builder, builder_file, objects, NULL);
+    *panel = GET_WIDGET(builder, "button_panel");
+    gtk_box_pack_end(GTK_BOX(ui->button_vbox), *panel, FALSE, TRUE, 0);
+    gtk_widget_realize(*panel);
+
+    /* Connect text to buttons */
+    for (i = 0; button_data[i].widget_name != NULL; i++) {
+        SNPRINTF(name, MAXLINE, "calc_%s_button", button_data[i].widget_name);
+        set_string_data(builder, name, "calc_text", button_data[i].data);
+    }
+
+    /* Localize buttons */
+    for (i = 0; i < 16; i++) {
+        GtkWidget *button;
+
+        SNPRINTF(name, MAXLINE, "calc_%d_button", i);
+        button = GET_WIDGET(builder, name);
+      
+        if (button) {
+            gtk_button_set_label(GTK_BUTTON(button), v->digits[i]);
+            set_string_data(builder, name, "calc_text", v->digits[i]);
+        }
+    }
+    widget = GET_WIDGET(builder, "calc_numeric_point_button");
+    if (widget)
+        gtk_button_set_label(GTK_BUTTON(widget), v->radix);
+
+    /* Connect super and subscript */
+    for (i = 0; i < 10; i++) {
+        SNPRINTF(name, MAXLINE, "calc_%d_button", i);
+        set_string_data(builder, name, "calc_subscript_text", subscript_digits[i]);
+        set_string_data(builder, name, "calc_superscript_text", superscript_digits[i]);
+        set_tint(GET_WIDGET(builder, name), &colour_numbers, 1);
+    }
+  
+    widget = GET_WIDGET(builder, "superscript_togglebutton");
+    if (widget) {
+        ui->superscript_toggles = g_list_append(ui->superscript_toggles, widget);
+        if (ui->number_mode == SUPERSCRIPT)
+            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
+    }
+    widget = GET_WIDGET(builder, "subscript_togglebutton");
+    if (widget) {
+        ui->subscript_toggles = g_list_append(ui->subscript_toggles, widget);
+        if (ui->number_mode == SUBSCRIPT)
+            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
+    }
+
+    set_tint(GET_WIDGET(builder, "calc_10_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_11_button"), &colour_numbers, 1);  
+    set_tint(GET_WIDGET(builder, "calc_12_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_13_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_14_button"), &colour_numbers, 1);  
+    set_tint(GET_WIDGET(builder, "calc_15_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_imaginary_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_pi_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_eulers_number_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_numeric_point_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_percentage_button"), &colour_numbers, 2);
+    set_tint(GET_WIDGET(builder, "subscript_togglebutton"), &colour_numbers, 2);  
+    set_tint(GET_WIDGET(builder, "superscript_togglebutton"), &colour_numbers, 2);
+    set_tint(GET_WIDGET(builder, "calc_exponential_button"), &colour_numbers, 2);
+    set_tint(GET_WIDGET(builder, "calc_base_2_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_base_8_button"), &colour_numbers, 1);
+    set_tint(GET_WIDGET(builder, "calc_base_16_button"), &colour_numbers, 1);
+
+    set_tint(GET_WIDGET(builder, "calc_result_button"), &colour_action, 2);
+    set_tint(GET_WIDGET(builder, "calc_factor_button"), &colour_action, 2);
+    set_tint(GET_WIDGET(builder, "calc_clear_button"), &colour_action, 1); // Different colour?
+    set_tint(GET_WIDGET(builder, "calc_trunc_button"), &colour_action, 1);
+    set_tint(GET_WIDGET(builder, "calc_shift_left_button"), &colour_action, 1);
+    set_tint(GET_WIDGET(builder, "calc_shift_right_button"), &colour_action, 1);
+  
+    set_tint(GET_WIDGET(builder, "calc_add_button"), &colour_operator, 1);
+    set_tint(GET_WIDGET(builder, "calc_subtract_button"), &colour_operator, 1);  
+    set_tint(GET_WIDGET(builder, "calc_multiply_button"), &colour_operator, 1);
+    set_tint(GET_WIDGET(builder, "calc_divide_button"), &colour_operator, 1);
+    set_tint(GET_WIDGET(builder, "calc_modulus_divide_button"), &colour_operator, 1);
+    set_tint(GET_WIDGET(builder, "calc_and_button"), &colour_operator, 1);  
+    set_tint(GET_WIDGET(builder, "calc_or_button"), &colour_operator, 1);  
+    set_tint(GET_WIDGET(builder, "calc_xor_button"), &colour_operator, 1);  
+
+    set_tint(GET_WIDGET(builder, "calc_cosine_button"), &colour_trig, 1);
+    set_tint(GET_WIDGET(builder, "calc_sine_button"), &colour_trig, 1);
+    set_tint(GET_WIDGET(builder, "calc_tangent_button"), &colour_trig, 1);
+    set_tint(GET_WIDGET(builder, "calc_hyperbolic_cosine_button"), &colour_trig, 1);
+    set_tint(GET_WIDGET(builder, "calc_hyperbolic_sine_button"), &colour_trig, 1);
+    set_tint(GET_WIDGET(builder, "calc_hyperbolic_tangent_button"), &colour_trig, 1);
+
+    set_tint(GET_WIDGET(builder, "calc_start_group_button"), &colour_group, 1);
+    set_tint(GET_WIDGET(builder, "calc_end_group_button"), &colour_group, 1);
+    set_tint(GET_WIDGET(builder, "calc_store_button"), &colour_memory, 1);
+    set_tint(GET_WIDGET(builder, "calc_recall_button"), &colour_memory, 1);
+    set_tint(GET_WIDGET(builder, "calc_ans_button"), &colour_memory, 1);
+    set_tint(GET_WIDGET(builder, "calc_random_button"), &colour_memory, 1);
+    set_tint(GET_WIDGET(builder, "calc_character_button"), &colour_memory, 1);
+
+    set_tint(GET_WIDGET(builder, "calc_integer_portion_button"), &colour_function, 1);
+    set_tint(GET_WIDGET(builder, "calc_fractional_portion_button"), &colour_function, 1);
+    set_tint(GET_WIDGET(builder, "calc_x_pow_y_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_factorial_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_root_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_abs_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_inverse_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_logarithm_button"), &colour_function, 1);  
+    set_tint(GET_WIDGET(builder, "calc_natural_logarithm_button"), &colour_function, 1);
+    set_tint(GET_WIDGET(builder, "calc_ones_complement_button"), &colour_function, 1);
+    set_tint(GET_WIDGET(builder, "calc_twos_complement_button"), &colour_function, 1);
+    set_tint(GET_WIDGET(builder, "calc_not_button"), &colour_function, 1);  
+//    set_tint(GET_WIDGET(builder, "calc__button"), &colour_function, 1);
+  
+    /* Set base button data */
+    set_int_data(builder, "calc_base_2_button", "base", 2);
+    set_int_data(builder, "calc_base_8_button", "base", 8);
+    set_int_data(builder, "calc_base_16_button", "base", 16);
+
+    /* Connect menus to popup buttons */
+    set_data(builder, "calc_shift_left_button", "calc_menu", GET_WIDGET(builder, "left_shift_popup"));
+    set_data(builder, "calc_shift_right_button", "calc_menu", GET_WIDGET(builder, "right_shift_popup"));
+    set_data(builder, "calc_store_button", "calc_menu", GET_WIDGET(builder, "memory_store_popup"));
+    set_data(builder, "calc_recall_button", "calc_menu", GET_WIDGET(builder, "memory_recall_popup"));
+
+    /* Load bit panel */
+    if (mode == PROGRAMMING) {
+        for (i = 0; i < MAXBITS; i++) {
+            SNPRINTF(name, MAXLINE, "bit_label_%d", i);
+            ui->bit_labels[i] = GET_WIDGET(builder, name);
+            SNPRINTF(name, MAXLINE, "bit_eventbox_%d", i);
+            set_int_data(builder, name, "bit_index", i);
+        }
+    }
+
+    /* Setup financial functions */
+    if (mode == FINANCIAL) {
+        set_data(builder, "calc_finc_compounding_term_button", "finc_dialog", "ctrm_dialog");
+        set_data(builder, "calc_finc_double_declining_depreciation_button", "finc_dialog", "ddb_dialog");
+        set_data(builder, "calc_finc_future_value_button", "finc_dialog", "fv_dialog");
+        set_data(builder, "calc_finc_gross_profit_margin_button", "finc_dialog", "gpm_dialog");
+        set_data(builder, "calc_finc_periodic_payment_button", "finc_dialog", "pmt_dialog");
+        set_data(builder, "calc_finc_present_value_button", "finc_dialog", "pv_dialog");
+        set_data(builder, "calc_finc_periodic_interest_rate_button", "finc_dialog", "rate_dialog");
+        set_data(builder, "calc_finc_straight_line_depreciation_button", "finc_dialog", "sln_dialog");
+        set_data(builder, "calc_finc_sum_of_the_years_digits_depreciation_button", "finc_dialog", "syd_dialog");
+        set_data(builder, "calc_finc_term_button", "finc_dialog", "term_dialog");
+    }
+
+    gtk_builder_connect_signals(builder, ui);
+}
+
+
+G_MODULE_EXPORT
+void
+base_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_set_base(ui, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "base")));
+}
+
+
+G_MODULE_EXPORT
+void
+exponent_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_exponent(ui);
+}
+
+
+G_MODULE_EXPORT
+void
+subtract_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_subtract(ui);
+}
+
+
+G_MODULE_EXPORT
+void
+button_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_insert_text(ui, g_object_get_data(G_OBJECT(widget), "calc_text"));
+}
+
+
+G_MODULE_EXPORT
+void
+store_menu_cb(GtkMenuItem *menu, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_STORE, g_object_get_data(G_OBJECT(menu), "register_id"));
+}
+
+
+G_MODULE_EXPORT
+void
+recall_menu_cb(GtkMenuItem *menu, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_RECALL, g_object_get_data(G_OBJECT(menu), "register_id"));
+}
+
+
+G_MODULE_EXPORT
+void
+solve_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_CALCULATE, NULL);
+}
+
+
+G_MODULE_EXPORT
+void
+clear_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_CLEAR, NULL);
+}
+
+
+G_MODULE_EXPORT
+void
+finc_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    gchar *name;
+
+    if (ui->financial == NULL)
+        ui_setup_finc_dialogs(ui);
+    name = g_object_get_data(G_OBJECT(widget), "finc_dialog");
+    gtk_dialog_run(GTK_DIALOG(GET_WIDGET(ui->financial, name)));
+    gtk_widget_hide(GTK_WIDGET(GET_WIDGET(ui->financial, name)));
+}
+
+
+static void
+position_popup(GtkWidget *base, GtkWidget *popup,
+               PopupLocation location_op)
+{
+    int base_x, base_y, base_width, base_height;
+    int popup_x, popup_y, popup_width, popup_height;
+    int screen_width, screen_height;
+    int n;
+
+    gtk_window_get_position(GTK_WINDOW(base), &base_x, &base_y);
+    gtk_window_get_size(GTK_WINDOW(base), &base_width, &base_height);
+    gtk_window_get_position(GTK_WINDOW(popup), &popup_x, &popup_y);
+    gtk_window_get_size(GTK_WINDOW(popup), &popup_width, &popup_height);
+    screen_width = gdk_screen_width();
+    screen_height = gdk_screen_height();
+
+    if (location_op == POPUP_LOR) {
+        if (base_x >= screen_width - base_width - base_x)
+            location_op = POPUP_LEFT;
+        else
+            location_op = POPUP_RIGHT;
+    } else if (location_op == POPUP_AOB) {
+        if (base_y > screen_height - base_height - base_y)
+            location_op = POPUP_ABOVE;
+        else
+            location_op = POPUP_BELOW;
+    }
+
+    switch (location_op) {
+        case POPUP_RIGHT:
+            popup_x = base_x + base_width + WM_WIDTH_FACTOR;
+            popup_y = base_y;
+            break;
+
+        case POPUP_LEFT:
+            popup_x = base_x - popup_width - WM_WIDTH_FACTOR;
+            popup_y = base_y;
+            break;
+
+        case POPUP_ABOVE:
+            popup_x = base_x;
+            popup_y = base_y - popup_height - WM_HEIGHT_FACTOR;
+            break;
+
+        case POPUP_BELOW:
+            popup_x = base_x;
+            popup_y = base_y + base_height + WM_HEIGHT_FACTOR;
+            break;
+
+        case POPUP_CENTERED:
+        default:
+            popup_x = base_x + (base_width - popup_width) / 2;
+            popup_y = base_y + (base_height - popup_height) / 2;
+            break;
+    }
+
+    /* Make sure frame doesn't go off side of screen */
+    n = popup_x + popup_width;
+    if (n > screen_width)
+        popup_x -= (n - screen_width);
+    else if (popup_x < 0)
+        popup_x = 0;
+
+    /* Make sure frame doesn't go off top or bottom */
+    n = popup_y + popup_height;
+    if (n > screen_height)
+        popup_y -= n - screen_height;
+    else if (popup_y < 0)
+        popup_y = 0;
+
+    gtk_window_move(GTK_WINDOW(popup), popup_x, popup_y);
+}
+
+
+G_MODULE_EXPORT
+void
+insert_ascii_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_create_dialogs(ui);
+  
+    if (!gtk_widget_get_visible(ui->ascii_dialog))
+        position_popup(ui->main_window, ui->ascii_dialog, POPUP_LEFT);
+    gtk_widget_grab_focus(GTK_WIDGET(ui->ascii_entry));
+    gtk_widget_show(ui->ascii_dialog);
+}
+
+
+G_MODULE_EXPORT
+void
+shift_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_SHIFT, g_object_get_data(G_OBJECT(widget), "shiftcount"));
+}
+
+G_MODULE_EXPORT
+void
+factorize_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_FACTORIZE, NULL);
+}
+
+
+G_MODULE_EXPORT
+void
+digit_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    if (ui->number_mode == SUPERSCRIPT)
+        ui_insert_text(ui, g_object_get_data(G_OBJECT(widget), "calc_superscript_text"));
+    else if (ui->number_mode == SUBSCRIPT)
+        ui_insert_text(ui, g_object_get_data(G_OBJECT(widget), "calc_subscript_text"));
+    else
+        ui_insert_text(ui, g_object_get_data(G_OBJECT(widget), "calc_text"));
+}
+
+
+static void
+update_memory_menus(GCalctoolUI *ui)
+{
+    int i;
+
+    for (i = 0; registers[i] != NULL; i++) {
+        char value[MAXLINE] = "", mstr[MAXLINE];
+        MPNumber *t;
+
+        t = register_get_value(registers[i]);
+        if (t)
+            display_make_number(&v->display, value, MAXLINE, t);
+        SNPRINTF(mstr, MAXLINE, "<span weight=\"bold\">%s</span> = %s", registers[i], value);
+        gtk_label_set_markup_with_mnemonic(GTK_LABEL(ui->memory_store_labels[i]), mstr);
+        gtk_label_set_markup_with_mnemonic(GTK_LABEL(ui->memory_recall_labels[i]), mstr);
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+popup_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    GtkWidget *menu;
+    GdkPoint loc;
+
+    update_memory_menus(ui);
+
+    menu = (GtkWidget *)g_object_get_data(G_OBJECT(widget), "calc_menu");
+    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+                   1, 0);
+}
+
+G_MODULE_EXPORT
+void
+ascii_dialog_response_cb(GtkWidget *dialog, gint response_id, GCalctoolUI *ui)
+{
+    const gchar *text;
+
+    text = gtk_entry_get_text(GTK_ENTRY(ui->ascii_entry));
+
+    if (response_id == GTK_RESPONSE_OK)
+        ui_do_button(ui, FN_INSERT_CHARACTER, (gpointer) text);
+
+    gtk_widget_hide(dialog);
+}
+
+
+G_MODULE_EXPORT
+void
+ascii_dialog_activate_cb(GtkWidget *entry, GCalctoolUI *ui)
+{
+    ascii_dialog_response_cb(ui->ascii_dialog, GTK_RESPONSE_OK, ui);
+}
+
+
+G_MODULE_EXPORT
+gboolean
+ascii_dialog_delete_cb(GtkWidget *dialog, GCalctoolUI *ui)
+{
+    ascii_dialog_response_cb(dialog, GTK_RESPONSE_CANCEL, ui);
+    return TRUE;
+}
+
+
+G_MODULE_EXPORT
+gboolean
+bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, GCalctoolUI *ui)
+{
+    ui_do_button(ui, FN_TOGGLE_BIT,
+              g_object_get_data(G_OBJECT(event_box), "bit_index"));
+    return TRUE;
+}
+
+
+
+G_MODULE_EXPORT
+void
+set_superscript_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+       ui->can_super_minus = TRUE;
+       ui_set_number_mode(ui, SUPERSCRIPT);
+    }
+    else {
+       ui->can_super_minus = FALSE;
+       if (ui->number_mode == SUPERSCRIPT)
+           ui_set_number_mode(ui, NORMAL);
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+set_subscript_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+       ui_set_number_mode(ui, SUBSCRIPT);
+    else if (ui->number_mode == SUBSCRIPT)
+       ui_set_number_mode(ui, NORMAL);
+}
diff --git a/src/ui-buttons.h b/src/ui-buttons.h
new file mode 100644
index 0000000..c846400
--- /dev/null
+++ b/src/ui-buttons.h
@@ -0,0 +1,24 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef UI_BUTTONS_H
+#define UI_BUTTONS_H
+
+#include "ui.h"
+
+#endif /* UI_BUTTONS_H */
diff --git a/src/ui-display.c b/src/ui-display.c
new file mode 100644
index 0000000..86de091
--- /dev/null
+++ b/src/ui-display.c
@@ -0,0 +1,495 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include <gdk/gdkkeysyms.h>
+
+#include "ui-display.h"
+#include "ui-internal.h"
+
+gchar *
+ui_get_display(GCalctoolUI *ui)
+{
+    GtkTextIter start, end;
+    gtk_text_buffer_get_bounds(ui->display_buffer, &start, &end);
+    return gtk_text_buffer_get_text(ui->display_buffer, &start, &end, FALSE);
+}
+
+
+void
+ui_insert_text(GCalctoolUI *ui, const char *text)
+{
+    ui_do_button(ui, FN_TEXT, (gpointer) text);
+}
+
+
+static gboolean
+redo_display(GCalctoolUI *ui)
+{
+    gchar *text;
+    GtkTextIter start, end, cursor;
+    gint cursor_position;
+
+    gtk_text_buffer_get_start_iter(ui->display_buffer, &start);
+    gtk_text_buffer_get_end_iter(ui->display_buffer, &end);
+    text = gtk_text_buffer_get_text(ui->display_buffer, &start, &end, FALSE);
+
+    g_object_get(G_OBJECT(ui->display_buffer), "cursor-position", &cursor_position, NULL);
+
+    gtk_text_buffer_set_text(ui->display_buffer, text, -1);
+    gtk_text_buffer_get_iter_at_offset(ui->display_buffer, &cursor, cursor_position);
+    gtk_text_buffer_place_cursor(ui->display_buffer, &cursor);
+
+    g_free(text);
+
+    return FALSE;
+}
+
+
+void
+ui_set_display(GCalctoolUI *ui, char *str, int cursor)
+{
+    GtkTextIter iter;
+    GtkAdjustment *adj;
+
+    gtk_text_buffer_set_text(ui->display_buffer, str, -1);
+
+    if (cursor < 0)
+        gtk_text_buffer_get_end_iter(ui->display_buffer, &iter);
+    else
+        gtk_text_buffer_get_iter_at_offset(ui->display_buffer, &iter, cursor);
+    gtk_text_buffer_place_cursor(ui->display_buffer, &iter);
+    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui->display_item), &iter, 0.0, TRUE, 1.0, 0.0);
+
+    /* This is a workaround for bug #524602.
+     * Basically the above code can cause the display to disappear when going from
+     * a display that is wider than the widget to one that is thinner. The following
+     * causes the display to be set twice which seems to work the second time.
+     */
+    g_idle_add((GSourceFunc)redo_display, ui);
+
+    /* Align to the right */
+    if (cursor < 0) {
+        adj = gtk_scrolled_window_get_hadjustment(
+                 GTK_SCROLLED_WINDOW(ui->scrolledwindow));
+        gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj));
+    }
+}
+
+
+void
+ui_do_button(GCalctoolUI *ui, int function, gpointer arg)
+{
+    GtkTextIter start, end;
+    gint cursor_start, cursor_end;
+  
+    /* Can't enter superscript minus after entering digits */
+    if (function == FN_TEXT && strstr("�¹²³������", (char *)arg) != NULL)
+        ui->can_super_minus = FALSE;
+
+    /* Disable super/subscript mode when finished entering */
+    if (function == FN_CALCULATE ||
+        function == FN_CLEAR ||
+        (function == FN_TEXT && strstr("â?»â?°Â¹Â²Â³â?´â?µâ?¶â?·â?¸â?¹â??â??â??â??â??â??â??â??â??â??", (char *)arg) == NULL)) {
+        ui_set_number_mode(ui, NORMAL);
+    }
+
+    if (gtk_text_buffer_get_selection_bounds(ui->display_buffer, &start, &end)) {
+        cursor_start = gtk_text_iter_get_offset(&start);
+        cursor_end = gtk_text_iter_get_offset(&end);
+    }
+    else {
+        g_object_get(G_OBJECT(ui->display_buffer), "cursor-position", &cursor_start, NULL);
+        if (cursor_start == gtk_text_buffer_get_char_count(ui->display_buffer))
+            cursor_start = -1;
+        cursor_end = cursor_start;
+    }
+
+    /* Some keyboards don't have a '^' button so convert two multiplies to one '^' */
+    if (cursor_start == cursor_end &&
+        function == FN_TEXT && ui->last_text != NULL &&
+        strcmp((char *)arg, "Ã?") == 0 && strcmp(ui->last_text, "Ã?") == 0) {
+        ui_do_button(ui, FN_BACKSPACE, NULL);
+        ui_do_button(ui, FN_TEXT, "^");
+    }
+    else {
+        display_do_function(&v->display, function, arg, cursor_start, cursor_end);
+        if (function == FN_TEXT)
+            ui->last_text = (char *)arg;
+        else
+            ui->last_text = NULL;
+    }
+}
+
+
+void
+ui_display_copy(GCalctoolUI *ui)
+{
+    gchar *string = NULL;
+    GtkTextIter start, end;
+
+    if (gtk_text_buffer_get_selection_bounds(ui->display_buffer, &start, &end) == TRUE)
+        string = gtk_text_buffer_get_text(ui->display_buffer, &start, &end, FALSE);
+    else
+        string = ui_get_display(ui);
+
+    if (ui->shelf != NULL)
+        g_free(ui->shelf);
+    ui->shelf = g_locale_from_utf8(string, strlen(string), NULL, NULL, NULL);
+    g_free(string);
+
+    gtk_clipboard_set_text(gtk_clipboard_get(ui->clipboard_atom), ui->shelf, -1);
+}
+
+
+void
+ui_do_exponent(GCalctoolUI *ui)
+{
+    ui_insert_text(ui, "Ã?10");
+    ui_set_number_mode(ui, SUPERSCRIPT);
+}
+
+
+void
+ui_do_subtract(GCalctoolUI *ui)
+{
+    if (ui->can_super_minus) {
+        ui_insert_text(ui, "â?»");
+        ui->can_super_minus = FALSE;
+    }
+    else {
+        ui_insert_text(ui, "â??");
+        ui_set_number_mode(ui, NORMAL);
+    }
+}
+
+
+static gboolean
+check_for_localized_numeric_point(int keyval)
+{
+    gchar outbuf[10]; /* Minumum size 6. */
+    gunichar ch;
+
+    ch = gdk_keyval_to_unicode(keyval);
+    g_return_val_if_fail(g_unichar_validate(ch), FALSE);
+
+    outbuf[g_unichar_to_utf8(ch, outbuf)] = '\0';
+
+    return (strcmp(outbuf, v->radix) == 0);
+}
+
+
+// FIXME: Should be able to replace by making display can_default
+G_MODULE_EXPORT
+gboolean
+main_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, GCalctoolUI *ui)
+{
+    int i, state;
+    const char *conversions[]       = {"*", "/", NULL};
+    const char *conversion_values[] = {"�", "÷", NULL };
+
+    /* Only look at the modifiers we use */
+    state = event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK);
+
+    // FIXME: Convert event to character
+    // FIXME: Or safer to intercept characters as they enter the text input (handles input methods)
+
+    if (check_for_localized_numeric_point(event->keyval) == TRUE) {
+        event->state = 0;
+        event->keyval = GDK_KP_Decimal;
+    }
+
+    /* Shortcuts */
+    if (state == GDK_CONTROL_MASK) {
+        switch(event->keyval)
+        {
+        case GDK_b:
+            ui_set_base(ui, 2);
+            return TRUE;
+        case GDK_d:
+            ui_set_base(ui, 10);
+            return TRUE;
+        case GDK_e:
+            ui_do_exponent(ui);
+            return TRUE;
+        case GDK_f:
+            ui_do_button(ui, FN_FACTORIZE, NULL);
+            return TRUE;
+        case GDK_h:
+            ui_set_base(ui, 16);
+            return TRUE;
+        case GDK_i:
+            ui_insert_text(ui, "�¹");
+            return TRUE;
+        case GDK_o:
+            ui_set_base(ui, 8);
+            return TRUE;
+        case GDK_p:
+            ui_insert_text(ui, "Ï?");
+            return TRUE;
+        case GDK_r:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_u:
+            ui_insert_text(ui, "µ");
+            return TRUE;
+        }
+    }
+    if (state == GDK_CONTROL_MASK || ui->number_mode == SUPERSCRIPT) {
+        switch(event->keyval)
+        {
+        case GDK_0:
+            ui_insert_text(ui, "â?°");
+            return TRUE;
+        case GDK_1:
+            ui_insert_text(ui, "¹");
+            return TRUE;
+        case GDK_2:
+            ui_insert_text(ui, "²");
+            return TRUE;
+        case GDK_3:
+            ui_insert_text(ui, "³");
+            return TRUE;
+        case GDK_4:
+            ui_insert_text(ui, "â?´");
+            return TRUE;
+        case GDK_5:
+            ui_insert_text(ui, "â?µ");
+            return TRUE;
+        case GDK_6:
+            ui_insert_text(ui, "â?¶");
+            return TRUE;
+        case GDK_7:
+            ui_insert_text(ui, "â?·");
+            return TRUE;
+        case GDK_8:
+            ui_insert_text(ui, "â?¸");
+            return TRUE;
+        case GDK_9:
+            ui_insert_text(ui, "â?¹");
+            return TRUE;
+        }
+    }
+    else if (state == GDK_MOD1_MASK || ui->number_mode == SUBSCRIPT) {
+        switch(event->keyval)
+        {
+        case GDK_0:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_1:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_2:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_3:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_4:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_5:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_6:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_7:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_8:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        case GDK_9:
+            ui_insert_text(ui, "â??");
+            return TRUE;
+        }
+    }
+
+    /* Delete in display */
+    if (event->keyval == GDK_Delete && state == 0 && (event->state & GDK_SHIFT_MASK) == 0) {
+        ui_do_button(ui, FN_DELETE, NULL);
+        return TRUE;
+    }
+    if (event->keyval == GDK_BackSpace && state == 0 && (event->state & GDK_SHIFT_MASK) == 0) {
+        ui_do_button(ui, FN_BACKSPACE, NULL);
+        return TRUE;
+    }
+
+    /* Clear display */
+    if ((event->keyval == GDK_Escape && state == 0) ||
+        (event->keyval == GDK_BackSpace && state == GDK_CONTROL_MASK) ||
+        (event->keyval == GDK_Delete && state == GDK_SHIFT_MASK)) {
+        ui_do_button(ui, FN_CLEAR, NULL);
+        return TRUE;
+    }
+
+    /* Solve */
+    if ((event->keyval == GDK_Return && state == 0) ||
+        (event->keyval == GDK_KP_Enter && state == 0)) {
+        if (gtk_widget_has_focus(ui->display_item)) {
+            ui_do_button(ui, FN_CALCULATE, NULL);
+            return TRUE;
+        }
+        else {
+            return FALSE;
+        }
+    }
+
+    if (state == GDK_CONTROL_MASK && event->keyval == GDK_minus) 
+    {
+        ui_insert_text(ui, "â?»");
+        ui->can_super_minus = FALSE;
+        return TRUE;
+    }
+
+    if (state != 0)
+        return FALSE;
+
+    // FIXME: event->string deprecated
+
+    if (strcmp(event->string, "-") == 0 || strcmp(event->string, "â??") == 0) {
+        ui_do_subtract(ui);
+        return TRUE;
+    }
+
+    for (i = 0; conversions[i]; i++) {
+        if (strcmp(event->string, conversions[i]) == 0) {
+            ui_insert_text(ui, conversion_values[i]);
+            return TRUE;
+        }
+    }
+    if (strcmp(event->string, ".") == 0) {
+        ui_insert_text(ui, v->radix);
+        return TRUE;
+    }
+
+    /* Some keyboards use this keyval for '^' (e.g. German) */
+    if (event->keyval == GDK_dead_circumflex) {
+        ui_insert_text(ui, "^");
+        return TRUE;
+    }
+
+    switch(*event->string)
+    {
+    case '\n':
+        ui_do_button(ui, FN_CALCULATE, NULL);
+        return TRUE;
+    }
+  
+    /* Don't override space - it is used in UI */
+    if (event->string[0] == ' ' && !gtk_widget_has_focus(ui->display_item))
+        return FALSE;
+
+    if (event->string[0] != '\0') {
+        ui_insert_text(ui, event->string);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+static void
+popup_paste_cb(GtkWidget *menu, GCalctoolUI *ui)
+{
+    ui_display_paste(ui);
+}
+
+
+// FIXME: Kill this
+static void
+for_each_menu(GtkWidget *widget, GCalctoolUI *ui)
+{
+    /* Find the "Paste" entry and activate it (see bug #317786).
+     * It is disabled because the GtkEntry is not marked as editable.
+     */
+    if (strcmp(G_OBJECT_TYPE_NAME(widget), "GtkImageMenuItem") == 0) {
+        GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget));
+
+         if (strcmp(gtk_label_get_text(GTK_LABEL(label)), _("Paste")) == 0) {
+            if (gtk_clipboard_wait_is_text_available(gtk_clipboard_get(ui->clipboard_atom))) {
+                gtk_widget_set_sensitive(GTK_WIDGET(widget), TRUE);
+                g_signal_connect(GTK_OBJECT(widget), "activate",
+                                 G_CALLBACK(popup_paste_cb), ui);
+            }
+        }
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+buffer_populate_popup_cb(GtkTextView *textview, GtkMenu *menu, GCalctoolUI *ui)
+{
+    gtk_container_foreach(GTK_CONTAINER(menu), (GtkCallback)for_each_menu, ui);
+}
+
+
+static void
+on_paste(GtkClipboard *clipboard, const gchar *text, GCalctoolUI *ui)
+{
+    if (text != NULL)
+        ui_do_button(ui, FN_PASTE, (gpointer) text);
+}
+
+
+void
+ui_display_paste(GCalctoolUI *ui)
+{
+    gtk_clipboard_request_text(gtk_clipboard_get(ui->clipboard_atom),
+                               (GtkClipboardTextReceivedFunc)on_paste, ui);
+}
+
+
+G_MODULE_EXPORT
+gboolean
+middle_click_paste_cb(GtkWidget *widget, GdkEventButton *event, GCalctoolUI *ui)
+{
+    if (event->button == 2)
+        ui_display_paste(ui);
+
+    return FALSE;
+}
+
+
+void
+ui_set_base(GCalctoolUI *ui, gint base)
+{
+    /* If has a number already in a base, then solve and convert it */
+    if (!display_is_result(&v->display) && display_is_number_with_base(&v->display))
+        ui_do_button(ui, FN_CALCULATE, NULL);
+
+    if (display_is_result(&v->display)) {
+        if (base == 2)
+            display_convert (&v->display, BIN);
+        else if (base == 8)
+            display_convert (&v->display, OCT);
+        else if (base == 16)
+            display_convert (&v->display, HEX);
+        else
+            display_convert (&v->display, DEC);
+    }
+    else {
+        if (base == 2)
+            ui_insert_text(ui, "â??");
+        else if (base == 8)
+            ui_insert_text(ui, "â??");
+        else if (base == 16)
+            ui_insert_text(ui, "â??â??");
+    }
+}
diff --git a/src/ui-display.h b/src/ui-display.h
new file mode 100644
index 0000000..2222184
--- /dev/null
+++ b/src/ui-display.h
@@ -0,0 +1,31 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef UI_DISPLAY_H
+#define UI_DISPLAY_H
+
+#include "ui.h"
+
+void ui_display_copy(GCalctoolUI *ui);
+void ui_display_paste(GCalctoolUI *ui);
+void ui_insert_text(GCalctoolUI *ui, const char *text);
+void ui_set_base(GCalctoolUI *ui, gint base);
+void ui_do_subtract(GCalctoolUI *ui);
+void ui_do_exponent(GCalctoolUI *ui);
+
+#endif /* UI_DISPLAY_H */
diff --git a/src/ui-financial.c b/src/ui-financial.c
new file mode 100644
index 0000000..d7e766a
--- /dev/null
+++ b/src/ui-financial.c
@@ -0,0 +1,359 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include "ui-financial.h"
+#include "financial.h"
+#include "currency.h"
+
+#define GET_OBJECT(ui, name) \
+          gtk_builder_get_object((ui), (name))
+#define GET_WIDGET(ui, name) \
+          GTK_WIDGET(GET_OBJECT(ui, name))
+
+typedef enum {
+    CURRENCY_TARGET_UPPER,
+    CURRENCY_TARGET_LOWER
+} CurrencyTargetRow;
+
+/* The names of each field in the dialogs for the financial functions */
+static char *finc_dialog_fields[][5] = {
+    {"ctrm_pint", "ctrm_fv",     "ctrm_pv",    NULL,         NULL},
+    {"ddb_cost",  "ddb_life",    "ddb_period", NULL,         NULL},
+    {"fv_pmt",    "fv_pint",     "fv_n",       NULL,         NULL},
+    {"gpm_cost",  "gpm_margin",  NULL,         NULL,         NULL},
+    {"pmt_prin",  "pmt_pint",    "pmt_n",      NULL,         NULL},
+    {"pv_pmt",    "pv_pint",     "pv_n",       NULL,         NULL},
+    {"rate_fv",   "rate_pv",     "rate_n",     NULL,         NULL},
+    {"sln_cost",  "sln_salvage", "sln_life",   NULL,         NULL},
+    {"syd_cost",  "syd_salvage", "syd_life",   "syd_period", NULL},
+    {"term_pmt",  "term_fv",     "term_pint",  NULL,         NULL},
+    {NULL,        NULL,          NULL,         NULL,         NULL}
+};
+
+#define UI_FINC_FILE        UI_DIR "/financial.ui"
+
+G_MODULE_EXPORT
+void
+finc_activate_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    gint dialog, field;
+
+    dialog = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_dialog"));
+    field = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "finc_field"));
+
+    if (finc_dialog_fields[dialog][field+1] == NULL) {
+        GtkWidget *dialog_widget;
+        dialog_widget = gtk_widget_get_toplevel(widget);
+        if (gtk_widget_is_toplevel (dialog_widget)) {
+            gtk_dialog_response(GTK_DIALOG(dialog_widget),
+                                GTK_RESPONSE_OK);
+            return;
+        }
+    }
+    else {
+        GtkWidget *next_widget;
+        next_widget = GET_WIDGET(ui->financial, finc_dialog_fields[dialog][field+1]);
+        gtk_widget_grab_focus(next_widget);
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+finc_response_cb(GtkWidget *widget, gint response_id, GCalctoolUI *ui)
+{
+    int dialog;
+    int i;
+    MPNumber arg[4];
+    GtkWidget *entry;
+
+    if (response_id != GTK_RESPONSE_OK)
+        return;
+
+    dialog = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(widget), "finc_dialog"));
+
+    for (i = 0; i < 4; i++) {
+        if (finc_dialog_fields[dialog][i] == NULL) {
+            continue;
+        }
+        entry = GET_WIDGET(ui->financial, finc_dialog_fields[dialog][i]);
+        // FIXME: Have to delocalize the input
+        mp_set_from_string(gtk_entry_get_text(GTK_ENTRY(entry)), &arg[i]);
+        gtk_entry_set_text(GTK_ENTRY(entry), "0");
+    }
+    gtk_widget_grab_focus(GET_WIDGET(ui->financial, finc_dialog_fields[dialog][0]));
+
+    do_finc_expression(dialog, &arg[0], &arg[1], &arg[2], &arg[3]);
+}
+
+
+static void set_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, const gpointer value)
+{
+    GObject *object;  
+    object = gtk_builder_get_object(ui, object_name);
+    if (object)
+        g_object_set_data(object, name, value);
+}
+
+
+static void set_int_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, gint value)
+{
+    set_data(ui, object_name, name, GINT_TO_POINTER(value));
+}
+
+
+void
+ui_setup_finc_dialogs(GCalctoolUI *ui)
+{
+    int i, j;
+    GtkListStore *currency_store;
+    GtkCellRenderer *render;
+    GtkSpinButton *currency_amount_upper;
+    GtkSpinButton *currency_amount_lower;
+    GtkComboBox   *currency_type_upper;
+    GtkComboBox   *currency_type_lower;
+
+    // FIXME: Handle errors
+    ui->financial = gtk_builder_new();
+    gtk_builder_add_from_file(ui->financial, UI_FINC_FILE, NULL);
+
+    set_int_data(ui->financial, "ctrm_dialog", "finc_dialog", FINC_CTRM_DIALOG);
+    set_int_data(ui->financial, "ddb_dialog", "finc_dialog", FINC_DDB_DIALOG);
+    set_int_data(ui->financial, "fv_dialog", "finc_dialog", FINC_FV_DIALOG);
+    set_int_data(ui->financial, "gpm_dialog", "finc_dialog", FINC_GPM_DIALOG);
+    set_int_data(ui->financial, "pmt_dialog", "finc_dialog", FINC_PMT_DIALOG);
+    set_int_data(ui->financial, "pv_dialog", "finc_dialog", FINC_PV_DIALOG);
+    set_int_data(ui->financial, "rate_dialog", "finc_dialog", FINC_RATE_DIALOG);
+    set_int_data(ui->financial, "sln_dialog", "finc_dialog", FINC_SLN_DIALOG);
+    set_int_data(ui->financial, "syd_dialog", "finc_dialog", FINC_SYD_DIALOG);
+    set_int_data(ui->financial, "term_dialog", "finc_dialog", FINC_TERM_DIALOG);
+
+    for (i = 0; finc_dialog_fields[i][0] != NULL; i++) {
+        for (j = 0; finc_dialog_fields[i][j]; j++) {
+            GObject *o;
+            o = gtk_builder_get_object(ui->financial, finc_dialog_fields[i][j]);
+            g_object_set_data(o, "finc_field", GINT_TO_POINTER(j));
+            g_object_set_data(o, "finc_dialog", GINT_TO_POINTER(i));
+        }
+    }
+
+    currency_amount_upper = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_upper"));
+    currency_amount_lower = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_lower"));
+    currency_type_upper = GTK_COMBO_BOX(gtk_builder_get_object(
+        ui->financial,
+        "currency_type_upper"));
+    currency_type_lower = GTK_COMBO_BOX(gtk_builder_get_object(
+        ui->financial,
+        "currency_type_lower"));
+
+    currency_store = gtk_list_store_new(2,
+                                        G_TYPE_INT,
+                                        G_TYPE_STRING);
+
+    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(currency_store),
+                                         1,
+                                         GTK_SORT_ASCENDING);
+
+    gtk_combo_box_set_model(currency_type_upper,
+                            GTK_TREE_MODEL(currency_store));
+    gtk_combo_box_set_model(currency_type_lower,
+                            GTK_TREE_MODEL(currency_store));
+
+    render = gtk_cell_renderer_text_new();
+
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(currency_type_upper),
+                               render,
+                               TRUE);
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(currency_type_lower),
+                               render,
+                               TRUE);
+
+    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(currency_type_upper),
+                                  render,
+                                  "text",
+                                  1);
+    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(currency_type_lower),
+                                  render,
+                                  "text",
+                                  1);
+
+    set_int_data(ui->financial, "currency_amount_upper", "target", CURRENCY_TARGET_LOWER);
+    set_int_data(ui->financial, "currency_amount_lower", "target", CURRENCY_TARGET_UPPER);
+
+    gtk_builder_connect_signals(ui->financial, ui);
+}
+
+
+static void
+recalculate_currency(GCalctoolUI *ui, CurrencyTargetRow target)
+{
+    int upper_index, lower_index;
+
+    GtkComboBox *combo_upper = GTK_COMBO_BOX(gtk_builder_get_object(
+        ui->financial,
+        "currency_type_upper"));
+    GtkComboBox *combo_lower = GTK_COMBO_BOX(gtk_builder_get_object(
+        ui->financial,
+        "currency_type_lower"));
+    GtkSpinButton *spin_upper = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_upper"));
+    GtkSpinButton *spin_lower = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_lower"));
+
+    GtkTreeModel *model = gtk_combo_box_get_model(combo_upper);
+    GtkTreeIter iter;
+
+    if (!gtk_combo_box_get_active_iter(combo_upper, &iter))
+        return;
+    gtk_tree_model_get(model, &iter, 0, &upper_index, -1);
+
+    if (!gtk_combo_box_get_active_iter(combo_lower, &iter))
+        return;
+    gtk_tree_model_get(model, &iter, 0, &lower_index, -1);
+
+    if (target == CURRENCY_TARGET_LOWER) {
+        MPNumber input, output;
+        mp_set_from_double (gtk_spin_button_get_value(spin_upper), &input);
+        currency_convert(&input, upper_index, lower_index, &output);
+        if (!mp_is_zero(&output))
+            gtk_spin_button_set_value(spin_lower, mp_cast_to_double(&output));
+    } else {
+        MPNumber input, output;
+        mp_set_from_double (gtk_spin_button_get_value(spin_lower), &input);
+        currency_convert(&input, lower_index, upper_index, &output);
+        if (!mp_is_zero(&output))
+            gtk_spin_button_set_value(spin_upper, mp_cast_to_double(&output));
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+currency_type_cb(GtkComboBox *combo, gpointer user_data, GCalctoolUI *ui)
+{
+    recalculate_currency(ui, CURRENCY_TARGET_LOWER);
+}
+
+
+G_MODULE_EXPORT
+void
+currency_amount_cb (GtkSpinButton *spinbutton, gpointer user_data, GCalctoolUI *ui)
+{
+    recalculate_currency(ui, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(spinbutton),
+                                                               "target")));
+}
+
+static void
+setup_currency_rates(GCalctoolUI *ui)
+{
+    static int has_run = 0;
+    int i;
+    GtkListStore *currency_store;
+    GObject *currency_type;
+
+    if (has_run)
+        return;
+
+    if (currency_rates_needs_update()) {
+        GtkWidget *dialog = gtk_message_dialog_new(NULL, 0,
+                                        GTK_MESSAGE_INFO,
+                                        GTK_BUTTONS_YES_NO,
+                                        /* Translators: Title of the error dialog when prompting to download currency rates */
+                                        N_("You don't have any recent currency rates. Should some be downloaded now?"));
+        int response = gtk_dialog_run(GTK_DIALOG(dialog));
+        gtk_widget_destroy(dialog);
+
+        if (response == GTK_RESPONSE_YES) {
+            if (!currency_download_rates()) {
+                dialog = gtk_message_dialog_new(NULL, 0,
+                                                GTK_MESSAGE_ERROR,
+                                                GTK_BUTTONS_OK,
+                                                /* Translators: Title of the error dialog when unable to download currency rates */
+                                                N_("Currency rates could not be downloaded. You may receive inaccurate results, or you may not receive any results at all."));
+            }
+        }
+    }
+    currency_load_rates();
+
+    currency_type = gtk_builder_get_object(ui->financial, "currency_type_upper");
+    currency_store = GTK_LIST_STORE(gtk_combo_box_get_model(
+        GTK_COMBO_BOX(currency_type)));
+
+    for (i = 0; currency_names[i].short_name; i++) {
+        GtkTreeIter iter;
+        int index;
+
+        if ((index = currency_get_index(currency_names[i].short_name)) < 0) {
+            continue;
+        }
+        gtk_list_store_append(currency_store, &iter);
+        gtk_list_store_set(currency_store, &iter,
+                           0, index,
+                           1, gettext(currency_names[i].long_name),
+                           -1);
+    }
+
+    has_run = 1;
+}
+
+
+G_MODULE_EXPORT
+void
+currency_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    GtkDialog *win;
+    GtkSpinButton *c_amount_upper, *c_amount_lower;
+    MPNumber display_val;
+
+    if (ui->financial == NULL)
+        ui_setup_finc_dialogs(ui);
+
+    setup_currency_rates(ui);
+
+    win = GTK_DIALOG(gtk_builder_get_object(ui->financial, "currency_dialog"));
+    c_amount_upper = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_upper"));
+    c_amount_lower = GTK_SPIN_BUTTON(gtk_builder_get_object(
+        ui->financial,
+        "currency_amount_lower"));
+    if (display_is_usable_number(&v->display, &display_val)) {
+        double start_val = mp_cast_to_double(&display_val);
+        gtk_spin_button_set_value(c_amount_upper, start_val);
+    }
+    gtk_widget_grab_focus(GTK_WIDGET(c_amount_upper));
+
+    if (gtk_dialog_run(win) == GTK_RESPONSE_OK) {
+        gchar *result;
+        
+        result = g_strdup_printf("%.2f",
+                                 gtk_spin_button_get_value(c_amount_lower));
+        mp_set_from_string(result, &display_val);
+        g_free(result);
+
+        display_set_number(&v->display, &display_val);
+    }
+
+    gtk_widget_hide(GTK_WIDGET(win));
+}
diff --git a/src/ui-financial.h b/src/ui-financial.h
new file mode 100644
index 0000000..8df0c7a
--- /dev/null
+++ b/src/ui-financial.h
@@ -0,0 +1,24 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef UI_FINANCIAL_H
+#define UI_FINANCIAL_H
+
+#include "ui.h"
+
+#endif /* UI_FINANCIAL_H */
diff --git a/src/ui-internal.h b/src/ui-internal.h
new file mode 100644
index 0000000..f3c14d2
--- /dev/null
+++ b/src/ui-internal.h
@@ -0,0 +1,30 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef UI_INTERNAL_H
+#define UI_INTERNAL_H
+
+#include "ui.h"
+
+void ui_load_mode(GCalctoolUI *ui, ModeType mode);
+void ui_do_button(GCalctoolUI *ui, int function, gpointer arg);
+void ui_setup_finc_dialogs(GCalctoolUI *ui);
+void ui_create_dialogs(GCalctoolUI *ui);
+void ui_set_number_mode(GCalctoolUI *ui, NumberMode mode);
+
+#endif /* UI_INTERNAL_H */
diff --git a/src/ui-preferences.c b/src/ui-preferences.c
new file mode 100644
index 0000000..c6fcd1d
--- /dev/null
+++ b/src/ui-preferences.c
@@ -0,0 +1,321 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "ui-preferences.h"
+#include "ui-internal.h"
+#include "get.h"
+
+
+#define UI_DIALOGS_FILE     UI_DIR "/dialogs.ui"
+
+#define GET_OBJECT(ui, name) \
+          gtk_builder_get_object((ui), (name))
+#define GET_WIDGET(ui, name) \
+          GTK_WIDGET(GET_OBJECT(ui, name))
+
+
+G_MODULE_EXPORT
+void
+preferences_response_cb(GtkWidget *widget, gint id, GCalctoolUI *ui)
+{
+    gtk_widget_hide(ui->preferences_dialog);
+}
+
+
+G_MODULE_EXPORT
+gboolean
+preferences_dialog_delete_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    preferences_response_cb(widget, 0, ui);
+    return TRUE;
+}
+
+
+G_MODULE_EXPORT
+void
+angle_unit_combobox_changed_cb(GtkWidget *combo)
+{
+    int i;
+    const gchar *value;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    struct
+    {
+        const gchar *value;
+        MPAngleUnit units;
+    } unit_map[] =
+    {
+        {"degrees",     MP_DEGREES},
+        {"radians" ,    MP_RADIANS},
+        {"gradians",    MP_GRADIANS},
+        {NULL,          MP_DEGREES}
+    };
+
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
+    gtk_tree_model_get(model, &iter, 1, &value, -1);
+    for (i = 0; unit_map[i].value != NULL && strcmp(unit_map[i].value, value) != 0; i++);
+    display_set_angle_unit(&v->display, unit_map[i].units);
+
+    set_resource(R_TRIG, value);
+}
+
+
+G_MODULE_EXPORT
+void
+display_format_combobox_changed_cb(GtkWidget *combo)
+{
+    int i;
+    const gchar *value;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    struct
+    {
+        const gchar *value;
+        DisplayFormat format;
+    } mode_map[] =
+    {
+        {"decimal",     DEC},
+        {"binary" ,     BIN},
+        {"octal",       OCT},
+        {"hexadecimal", HEX},
+        {"scientific",  SCI},
+        {"engineering", ENG},
+        {NULL,          DEC}
+    };
+
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
+    gtk_tree_model_get(model, &iter, 1, &value, -1);
+    for (i = 0; mode_map[i].value != NULL && strcmp(mode_map[i].value, value) != 0; i++);
+    display_set_format(&v->display, mode_map[i].format);
+
+    set_resource(R_DISPLAY, value);
+}
+
+
+G_MODULE_EXPORT
+void
+word_size_combobox_changed_cb(GtkWidget *combo)
+{
+    gint value;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
+    gtk_tree_model_get(model, &iter, 1, &value, -1);
+    display_set_word_size(&v->display, value);
+
+    set_int_resource(R_WORDLEN, value);
+}
+
+
+G_MODULE_EXPORT
+void
+decimal_places_spin_change_value_cb(GtkWidget *spin)
+{
+    gint value = 0;
+
+    value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+    display_set_accuracy(&v->display, value);
+
+    set_int_resource(R_ACCURACY, value);
+}
+
+
+G_MODULE_EXPORT
+void
+thousands_separator_check_toggled_cb(GtkWidget *check)
+{
+    gboolean value;
+
+    value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
+    display_set_show_thousands_separator(&v->display, value);
+    set_boolean_resource(R_TSEP, value);
+}
+
+
+G_MODULE_EXPORT
+void
+trailing_zeroes_check_toggled_cb(GtkWidget *check)
+{
+    gboolean value;
+
+    value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
+    display_set_show_trailing_zeroes(&v->display, value);
+    set_boolean_resource(R_ZEROES, value);
+}
+
+
+void
+ui_show_preferences(GCalctoolUI *ui)
+{  
+    ui_create_dialogs(ui);
+    gtk_window_present(GTK_WINDOW(ui->preferences_dialog));
+}
+
+
+static void
+set_combo_box_from_config(GCalctoolUI *ui, const gchar *name, const gchar *key_name, GType key_type)
+{
+    GtkWidget *combo;
+    GtkTreeModel *model;
+    gchar *str_key_value = NULL;
+    int int_key_value;
+    GtkTreeIter iter;
+    gboolean valid;
+
+    combo = GET_WIDGET(ui->dialog_ui, name);
+    model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
+    valid = gtk_tree_model_get_iter_first(model, &iter);
+
+    switch (key_type)
+    {
+    case G_TYPE_STRING:
+        str_key_value = get_resource(key_name);
+        if (!str_key_value)
+            valid = FALSE;
+        break;
+    case G_TYPE_INT:
+        if (!get_int_resource(key_name, &int_key_value))
+            valid = FALSE;
+        break;
+    default:
+        break;
+    }
+
+    while (valid) {
+        gchar *str_value;
+        gint int_value;
+        gboolean matched = FALSE;
+
+        switch (key_type)
+        {
+        case G_TYPE_STRING:
+            gtk_tree_model_get(model, &iter, 1, &str_value, -1);
+            matched = strcmp(str_value, str_key_value) == 0;
+            break;
+        case G_TYPE_INT:
+            gtk_tree_model_get(model, &iter, 1, &int_value, -1);
+            matched = int_value == int_key_value;
+            break;
+        default:
+            break;
+        }
+
+        if (matched)
+            break;
+
+        valid = gtk_tree_model_iter_next(model, &iter);
+    }
+    if (!valid)
+        valid = gtk_tree_model_get_iter_first(model, &iter);
+
+    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter);
+
+    g_free(str_key_value);
+}
+
+
+// FIXME: Take out ascii dialog
+void
+ui_create_dialogs(GCalctoolUI *ui)
+{
+    GtkWidget *widget;
+    GtkCellRenderer *renderer;
+    gchar *string, **tokens;
+    int value;
+  
+    if (ui->dialog_ui)
+        return;
+
+    // FIXME: Handle errors
+    ui->dialog_ui = gtk_builder_new();
+    gtk_builder_add_from_file(ui->dialog_ui, UI_DIALOGS_FILE, NULL);
+
+    ui->ascii_dialog = GET_WIDGET(ui->dialog_ui, "ascii_dialog");
+    ui->ascii_entry = GET_WIDGET(ui->dialog_ui, "ascii_entry");
+
+    /* Make dialogs transient of the main window */
+    gtk_window_set_transient_for(GTK_WINDOW(ui->ascii_dialog), GTK_WINDOW(ui->main_window));
+
+    ui->preferences_dialog = GET_WIDGET(ui->dialog_ui, "preferences_dialog");
+
+    /* Configuration dialog */
+
+    widget = GET_WIDGET(ui->dialog_ui, "angle_unit_combobox");
+    renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);
+
+    widget = GET_WIDGET(ui->dialog_ui, "display_format_combobox");
+    renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);
+
+    widget = GET_WIDGET(ui->dialog_ui, "word_size_combobox");
+    renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE);
+    gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(widget), renderer, "text", 0);
+
+    /* Label used in preferences dialog.  The %d is replaced by a spinbutton */
+    string = _("Show %d decimal _places");
+    tokens = g_strsplit(string, "%d", 2);
+    widget = GET_WIDGET(ui->dialog_ui, "decimal_places_label1");
+    if (tokens[0])
+        string = g_strstrip(tokens[0]);
+    else
+        string = "";
+    if (string[0] != '\0')
+        gtk_label_set_text_with_mnemonic(GTK_LABEL(widget), string);
+    else
+        gtk_widget_hide(widget);
+
+    widget = GET_WIDGET(ui->dialog_ui, "decimal_places_label2");
+    if (tokens[0] && tokens[1])
+        string = g_strstrip(tokens[1]);
+    else
+        string = "";
+    if (string[0] != '\0')
+        gtk_label_set_text_with_mnemonic(GTK_LABEL(widget), string);
+    else
+        gtk_widget_hide(widget);
+
+    g_strfreev(tokens);
+
+    set_combo_box_from_config(ui, "angle_unit_combobox", R_TRIG, G_TYPE_STRING);
+    set_combo_box_from_config(ui, "display_format_combobox", R_DISPLAY, G_TYPE_STRING);
+    set_combo_box_from_config(ui, "word_size_combobox", R_WORDLEN, G_TYPE_INT);
+
+    if (!get_int_resource(R_ACCURACY, &value))
+        value = 9;
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(GET_OBJECT(ui->dialog_ui, "decimal_places_spin")), value);
+
+    if (!get_boolean_resource(R_TSEP, &value))
+        value = FALSE;
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GET_OBJECT(ui->dialog_ui, "thousands_separator_check")), value);
+
+    if (!get_boolean_resource(R_ZEROES, &value))
+        value = FALSE;
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GET_OBJECT(ui->dialog_ui, "trailing_zeroes_check")), value);
+
+    gtk_builder_connect_signals(ui->dialog_ui, ui);
+}
diff --git a/src/ui-preferences.h b/src/ui-preferences.h
new file mode 100644
index 0000000..724254c
--- /dev/null
+++ b/src/ui-preferences.h
@@ -0,0 +1,26 @@
+/*  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef UI_PREFERENCES_H
+#define UI_PREFERENCES_H
+
+#include "ui.h"
+
+void ui_show_preferences(GCalctoolUI *ui);
+
+#endif /* UI_PREFERENCES_H */
diff --git a/src/ui.c b/src/ui.c
new file mode 100644
index 0000000..8e2be59
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,393 @@
+/*  Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ *  Copyright (c) 2008-2009 Robert Ancell
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <gtk/gtk.h>
+
+#include <limits.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "ui.h"
+#include "ui-internal.h"
+#include "ui-display.h"
+#include "ui-buttons.h"
+#include "ui-preferences.h"
+#include "config.h"
+#include "get.h"
+
+static const char *mode_names[] = { "BASIC", "ADVANCED", "FINANCIAL", "PROGRAMMING", NULL };
+
+#define UI_FILE UI_DIR "/gcalctool.ui"
+#define GET_WIDGET(ui, name)  GTK_WIDGET(gtk_builder_get_object(ui, name))
+
+void
+ui_init(int *argc, char ***argv)
+{
+    gtk_init(argc, argv);
+    gtk_window_set_default_icon_name("accessories-calculator");
+}
+
+
+static void
+create_main_window(GCalctoolUI *ui)
+{
+    int i;
+    char name[MAXLINE];
+    GtkWidget *widget;
+    PangoFontDescription *font_desc;
+    GtkCellRenderer *renderer;
+    GError *error = NULL;
+
+    ui->ui = gtk_builder_new();
+    gtk_builder_add_from_file(ui->ui, UI_FILE, &error);
+    if (error) {
+       gchar *contents;
+       contents = g_strdup_printf(/* Description in UI error dialog when unable to load the UI files. %s is replaced with the error message provided by GTK+ */
+                                  _("A required file is missing or damaged. Please check your installation.\n\n%s"),
+                                  error->message);
+       ui_critical_error(ui,
+                         /* Title of the error dialog when unable to load the UI files */
+                         _("Error loading user interface"),
+                         contents);
+    }
+    gtk_builder_connect_signals(ui->ui, ui);
+
+    ui->clipboard_atom  = gdk_atom_intern("CLIPBOARD", FALSE);
+    ui->primary_atom    = gdk_atom_intern("PRIMARY", FALSE);
+
+    ui->main_window     = GET_WIDGET(ui->ui, "calc_window");
+    ui->scrolledwindow  = GET_WIDGET(ui->ui, "display_scroll"),
+    ui->display_item    = GET_WIDGET(ui->ui, "displayitem"),
+    ui->info_buffer     = GTK_TEXT_BUFFER(gtk_builder_get_object(ui->ui, "info_buffer"));
+    ui->button_vbox     = GET_WIDGET(ui->ui, "button_vbox");
+
+    ui->display_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->display_item));
+    gtk_widget_ensure_style(ui->display_item);
+    font_desc = pango_font_description_copy(gtk_widget_get_style(ui->display_item)->font_desc);
+    pango_font_description_set_size(font_desc, 16 * PANGO_SCALE);
+    gtk_widget_modify_font(ui->display_item, font_desc);
+    pango_font_description_free(font_desc);
+    gtk_widget_set_name(ui->display_item, "displayitem");
+    atk_object_set_role(gtk_widget_get_accessible(ui->display_item),
+                                                  ATK_ROLE_EDITBAR);
+
+    gtk_widget_realize(ui->main_window);
+
+    /* Set modes for menu items */
+    for (i = 1; i < 16; i++) {
+        SNPRINTF(name, MAXLINE, "shift_left%d_menu", i);
+        g_object_set_data(gtk_builder_get_object(ui->ui, name), "shiftcount", GINT_TO_POINTER(i));
+        SNPRINTF(name, MAXLINE, "shift_right%d_menu", i);
+        g_object_set_data(gtk_builder_get_object(ui->ui, name), "shiftcount", GINT_TO_POINTER(-i));
+    }
+    g_object_set_data(gtk_builder_get_object(ui->ui, "view_basic_menu"), "calcmode", GINT_TO_POINTER(BASIC));
+    g_object_set_data(gtk_builder_get_object(ui->ui, "view_advanced_menu"), "calcmode", GINT_TO_POINTER(ADVANCED));
+    g_object_set_data(gtk_builder_get_object(ui->ui, "view_financial_menu"), "calcmode", GINT_TO_POINTER(FINANCIAL));
+    g_object_set_data(gtk_builder_get_object(ui->ui, "view_programming_menu"), "calcmode", GINT_TO_POINTER(PROGRAMMING));
+}
+
+
+GCalctoolUI *
+ui_new()
+{
+    gchar *path;
+    int value;
+    GCalctoolUI *ui;
+
+    ui = g_malloc0(sizeof(GCalctoolUI));
+
+    if (get_enumerated_resource(R_MODE, mode_names, &value))
+        ui->mode = (ModeType) value;
+    else
+        ui->mode = BASIC;
+
+    /* Create main gcalctool window. */
+    create_main_window(ui);
+
+    return ui;
+}
+
+
+void
+ui_start(GCalctoolUI *ui)
+{
+    gtk_widget_show(ui->main_window);
+    gtk_main();
+}
+
+
+void
+ui_critical_error(GCalctoolUI *ui, const gchar *title, const gchar *contents)
+{
+    GtkWidget *dialog;
+
+    dialog = gtk_message_dialog_new(NULL, 0,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_NONE,
+                                    "%s", title);
+    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+                                             "%s", contents);
+    gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT, NULL);
+
+    gtk_dialog_run(GTK_DIALOG(dialog));
+  
+    gtk_main_quit();
+}
+
+
+// FIXME: Watch for changes in programming mode
+void
+ui_set_bitfield(GCalctoolUI *ui, int enabled, guint64 bits)
+{
+    int i;
+    const gchar *label;
+  
+    if (!ui->bit_panel)
+       return;
+
+    gtk_widget_set_sensitive(ui->bit_panel, enabled);
+
+    for (i = 0; i < MAXBITS; i++) {
+        if (bits & (1LL << (MAXBITS-i-1)))
+            label = " 1";
+        else
+            label = " 0";
+        gtk_label_set_text(GTK_LABEL(ui->bit_labels[i]), label);
+    }
+}
+
+
+void
+ui_set_number_mode(GCalctoolUI *ui, NumberMode mode)
+{
+    GList *i;
+
+    ui->number_mode = mode;
+    for (i = ui->superscript_toggles; i; i = i->next) {
+        GtkWidget *widget = i->data;
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUPERSCRIPT);
+    }
+    for (i = ui->subscript_toggles; i; i = i->next) {
+        GtkWidget *widget = i->data;
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUBSCRIPT);
+    }
+}
+
+
+static GtkWidget *
+get_buttons(GCalctoolUI *ui, ModeType mode)
+{
+    switch (mode) {
+    case BASIC:
+        return ui->bas_panel;
+    case ADVANCED:
+        return ui->adv_panel;
+    case FINANCIAL:
+        return ui->fin_panel;
+    case PROGRAMMING:
+        return ui->prog_panel;
+    }
+}
+
+
+static void
+ui_set_mode(GCalctoolUI *ui, ModeType mode)
+{
+    GtkWidget *menu;
+    ModeType old_mode;
+
+    old_mode = ui->mode;
+    ui->mode = mode;
+
+    /* Save mode */
+    set_enumerated_resource(R_MODE, mode_names, (int)mode);
+
+    /* Hide the existing mode */
+    if (get_buttons(ui, old_mode))
+        gtk_widget_hide(get_buttons(ui, old_mode));
+  
+    /* Create the new mode if necessary */
+    if (!get_buttons(ui, mode))
+        ui_load_mode(ui, mode);
+    gtk_widget_show(get_buttons(ui, mode));
+
+    /* Update the menu */
+    switch (mode) {
+        case BASIC:
+            menu = GET_WIDGET(ui->ui, "view_basic_menu");
+            break;
+
+        case ADVANCED:
+            menu = GET_WIDGET(ui->ui, "view_advanced_menu");
+            break;
+
+        case FINANCIAL:
+            menu = GET_WIDGET(ui->ui, "view_financial_menu");
+            break;
+
+        case PROGRAMMING:
+            menu = GET_WIDGET(ui->ui, "view_programming_menu");
+            break;
+
+        default:
+            assert(FALSE);
+            return;
+    }
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
+}
+
+
+void
+ui_set_statusbar(GCalctoolUI *ui, const gchar *text)
+{
+    gtk_text_buffer_set_text(ui->info_buffer, text, -1);
+}
+
+
+G_MODULE_EXPORT
+void
+copy_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_display_copy(ui);
+}
+
+
+G_MODULE_EXPORT
+void
+paste_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    ui_display_paste(ui);
+}
+
+
+G_MODULE_EXPORT
+void
+mode_changed_cb(GtkWidget *menu, GCalctoolUI *ui)
+{
+    int mode;
+
+    if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)))
+        return;
+
+    mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu), "calcmode"));
+    ui_set_mode(ui, mode);
+}
+
+
+G_MODULE_EXPORT
+void
+show_preferences_cb(GtkMenuItem *menu, GCalctoolUI *ui)
+{
+    ui_show_preferences(ui);
+}
+
+
+G_MODULE_EXPORT
+void
+help_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    GdkScreen *screen;
+    GError *error = NULL;
+
+    screen = gtk_widget_get_screen (GTK_WIDGET (ui->main_window));
+    gtk_show_uri (screen, "ghelp:gcalctool", gtk_get_current_event_time (), &error);
+
+    if (error != NULL)
+    {
+        GtkWidget *d;
+        /* Translators: Error message displayed when unable to launch help browser */
+        const char *message = _("Unable to open help file");
+
+        d = gtk_message_dialog_new (GTK_WINDOW (ui->main_window),
+                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+                                    "%s", message);
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (d),
+                                                  "%s", error->message);
+        g_signal_connect (d, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+        gtk_window_present (GTK_WINDOW (d));
+
+        g_error_free (error);
+    }
+}
+
+
+G_MODULE_EXPORT
+void
+about_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    const gchar *authors[] = {
+        "Rich Burridge <rich burridge sun com>",
+        "Robert Ancell <robert ancell gmail com>",
+        "Klaus Niederkrüger <kniederk umpa ens-lyon fr>",
+        NULL
+    };
+    const gchar *documenters[] = {
+        "Sun Microsystems",
+        NULL
+    };
+
+    /* Translators: The translator credits. Please translate this with your name(s). */
+    const gchar *translator_credits = _("translator-credits");
+
+    /* Translators: The license this software is under (GPL2+) */
+    char *license = _("Gcalctool is free software; you can redistribute it and/or modify\n"
+          "it under the terms of the GNU General Public License as published by\n"
+          "the Free Software Foundation; either version 2 of the License, or\n"
+          "(at your option) any later version.\n"
+          "\n"
+          "Gcalctool is distributed in the hope that it will be useful,\n"
+          "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+          "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+          "GNU General Public License for more details.\n"
+          "\n"
+          "You should have received a copy of the GNU General Public License\n"
+          "along with Gcalctool; if not, write to the Free Software Foundation, Inc.,\n"
+          "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA");
+
+    gtk_show_about_dialog(GTK_WINDOW(ui->main_window),
+                          /* Translators: Program name in the about dialog */
+                          "name", _("Gcalctool"),
+                          "version", VERSION,
+                          /* Translators: Copyright notice in the about dialog */
+                          "copyright", _("\xc2\xa9 1986â??2008 The Gcalctool authors"),
+                          "license", license,
+                          /* Translators: Short description in the about dialog */
+                          "comments", _("Calculator with financial and scientific modes."),
+                          "authors", authors,
+                          "documenters", documenters,
+                          "translator_credits", translator_credits,
+                          "logo-icon-name", "accessories-calculator",
+                          NULL);
+}
+
+
+G_MODULE_EXPORT
+void
+quit_cb(GtkWidget *widget, GCalctoolUI *ui)
+{
+    gtk_main_quit();
+}
diff --git a/src/ui.h b/src/ui.h
index 68f8ead..57f6c5d 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -21,19 +21,86 @@
 #define UI_H
 
 #include <stdint.h>
+#include <gtk/gtk.h>
+
+typedef struct GCalctoolUI GCalctoolUI;
 
 #include "calctool.h"
 
-void ui_init(int *argc, char ***argv);
-void ui_load(void);
-void ui_start(void);
+/* Calculator modes. */
+typedef enum {
+    BASIC,
+    ADVANCED,
+    FINANCIAL,
+    PROGRAMMING
+} ModeType;
+
+typedef enum {
+    NORMAL,
+    SUPERSCRIPT,
+    SUBSCRIPT
+} NumberMode;
+
+#define MAXBITS 64      /* Bit panel: number of bit fields. */
+#define MAX_REGISTERS 6
+
+// FIXME: Make opaque
+struct GCalctoolUI {
+    ModeType mode;  /* Current calculator mode. */
+    NumberMode number_mode;
+
+    GtkBuilder *ui;
+    GtkBuilder *dialog_ui;
+    GtkBuilder *financial;
+    GtkBuilder *basic_ui, *advanced_ui, *financial_ui, *programming_ui;
+
+    GtkWidget *main_window;
+
+    GtkWidget *bit_panel;
+    GtkWidget *bit_labels[MAXBITS];
+
+    GtkWidget *ascii_dialog;
+    GtkWidget *ascii_entry;
 
-void ui_set_undo_enabled(gboolean, gboolean);
+    GtkWidget *display_item;           /* Calculator display. */
+    GtkTextBuffer *display_buffer;     /* Buffer used in display */
+    GtkTextBuffer *info_buffer;        /* Buffer used in info messages */
+    GtkWidget *scrolledwindow;         /* Scrolled window for display_item. */
+
+    GtkWidget *button_vbox;
+    GtkWidget *bas_panel;      /* Panel containing basic mode widgets. */
+    GtkWidget *adv_panel;      /* Panel containing advanced mode widgets. */
+    GtkWidget *fin_panel;      /* Panel containing financial mode widgets. */
+    GtkWidget *prog_panel;     /* Panel containing programming mode widgets. */
+
+    GList *superscript_toggles;
+    GList *subscript_toggles;
+
+    gboolean can_super_minus;
+
+    /* Labels for popup menus */
+    GtkWidget *memory_store_labels[MAX_REGISTERS];
+    GtkWidget *memory_recall_labels[MAX_REGISTERS];
+
+    GtkWidget *preferences_dialog;
+
+    GdkAtom clipboard_atom;
+    GdkAtom primary_atom;
+    char *shelf;                       /* PUT selection shelf contents. */
+
+    /* Last text entered */
+    char *last_text;
+};
+
+void ui_init(int *argc, char ***argv);
+GCalctoolUI *ui_new(void);
+void ui_critical_error(GCalctoolUI *ui, const gchar *title, const gchar *contents);
+void ui_start(GCalctoolUI *ui);
 
-void ui_set_display(char *, int);
-void ui_set_bitfield(int enabled, guint64 bits);
-void ui_set_statusbar(const gchar *);
+void ui_set_display(GCalctoolUI *ui, char *, int);
+void ui_set_bitfield(GCalctoolUI *ui, int enabled, guint64 bits);
+void ui_set_statusbar(GCalctoolUI *ui, const gchar *);
 
-gchar *ui_get_display(void);
+gchar *ui_get_display(GCalctoolUI *ui);
 
 #endif /* UI_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]