diff options
Diffstat (limited to 'src/math-buttons.c')
-rw-r--r-- | src/math-buttons.c | 1790 |
1 files changed, 1790 insertions, 0 deletions
diff --git a/src/math-buttons.c b/src/math-buttons.c new file mode 100644 index 0000000..01abb1e --- /dev/null +++ b/src/math-buttons.c @@ -0,0 +1,1790 @@ +/* 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 <glib/gi18n.h> + +#include "math-buttons.h" +#include "financial.h" +#include "currency.h" + +enum { + PROP_0, + PROP_EQUATION, + PROP_MODE +}; + +static GType button_mode_type; + +#define MAXBITS 64 /* Bit panel: number of bit fields. */ + +struct MathButtonsPrivate +{ + MathEquation *equation; + + ButtonMode mode; + gint programming_base; + + GtkBuilder *basic_ui, *advanced_ui, *financial_ui, *programming_ui; + + GdkColor color_numbers, color_action, color_operator, color_function, color_memory, color_group; + + GtkWidget *bas_panel, *adv_panel, *fin_panel, *prog_panel; + GtkWidget *active_panel; + + GtkWidget *shift_left_menu, *shift_right_menu; + + GtkWidget *function_menu; + + GList *superscript_toggles; + GList *subscript_toggles; + + GtkWidget *angle_combo; + GtkWidget *angle_label; + + GtkWidget *base_combo; + GtkWidget *base_label; + GtkWidget *bit_panel; + GtkWidget *bit_labels[MAXBITS]; + + GtkWidget *source_currency_combo; + GtkWidget *target_currency_combo; + GtkWidget *currency_label; + + GtkWidget *character_code_dialog; + GtkWidget *character_code_entry; +}; + +G_DEFINE_TYPE (MathButtons, math_buttons, GTK_TYPE_VBOX); + +#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_WIDGET(ui, name) \ + GTK_WIDGET(gtk_builder_get_object((ui), (name))) + +#define WM_WIDTH_FACTOR 10 +#define WM_HEIGHT_FACTOR 30 + +typedef enum +{ + NUMBER, + NUMBER_BOLD, + OPERATOR, + FUNCTION, + MEMORY, + GROUP, + ACTION +} ButtonClass; + +typedef struct { + const char *widget_name; + const char *data; + ButtonClass class; + const char *tooltip; +} ButtonData; + +static ButtonData button_data[] = { + {"pi", "π", NUMBER, + /* Tooltip for the Pi button */ + N_("Pi [Ctrl+P]")}, + {"eulers_number", "e", NUMBER, + /* Tooltip for the Euler's Number button */ + N_("Euler’s Number")}, + {"imaginary", "i", NUMBER, NULL}, + {"numeric_point", NULL, NUMBER, NULL}, + {"subscript", NULL, NUMBER_BOLD, + /* Tooltip for the subscript button */ + N_("Subscript mode [Alt]")}, + {"superscript", NULL, NUMBER_BOLD, + /* Tooltip for the superscript button */ + N_("Superscript mode [Ctrl]")}, + {"exponential", NULL, NUMBER_BOLD, + /* Tooltip for the scientific exponent button */ + N_("Scientific exponent [Ctrl+E]")}, + {"add", "+", OPERATOR, + /* Tooltip for the add button */ + N_("Add [+]")}, + {"subtract", "−", OPERATOR, + /* Tooltip for the subtract button */ + N_("Subtract [-]")}, + {"multiply", "×", OPERATOR, + /* Tooltip for the multiply button */ + N_("Multiply [*]")}, + {"divide", "÷", OPERATOR, + /* Tooltip for the divide button */ + N_("Divide [/]")}, + {"modulus_divide", " mod ", OPERATOR, + /* Tooltip for the modulus divide button */ + N_("Modulus divide")}, + {"function", NULL, FUNCTION, + /* Tooltip for the additional functions button */ + N_("Additional Functions")}, + {"x_pow_y", "^", FUNCTION, + /* Tooltip for the exponent button */ + N_("Exponent [^ or **]")}, + {"x_squared", "²", FUNCTION, + /* Tooltip for the square button */ + N_("Square [Ctrl+2]")}, + {"percentage", "%", NUMBER, + /* Tooltip for the percentage button */ + N_("Percentage [%]")}, + {"factorial", "!", FUNCTION, + /* Tooltip for the factorial button */ + N_("Factorial [!]")}, + {"abs", "|", FUNCTION, + /* Tooltip for the absolute value button */ + N_("Absolute value [|]")}, + {"arg", "Arg ", FUNCTION, + /* Tooltip for the complex argument component button */ + N_("Complex argument")}, + {"conjugate", "conj ", FUNCTION, + /* Tooltip for the complex conjugate button */ + N_("Complex conjugate")}, + {"root", "√", FUNCTION, + /* Tooltip for the root button */ + N_("Root [Ctrl+R]")}, + {"square_root", "√", FUNCTION, + /* Tooltip for the square root button */ + N_("Square root [Ctrl+R]")}, + {"logarithm", "log ", FUNCTION, + /* Tooltip for the logarithm button */ + N_("Logarithm")}, + {"natural_logarithm", "ln ", FUNCTION, + /* Tooltip for the natural logarithm button */ + N_("Natural Logarithm")}, + {"sine", "sin ", FUNCTION, + /* Tooltip for the sine button */ + N_("Sine")}, + {"cosine", "cos ", FUNCTION, + /* Tooltip for the cosine button */ + N_("Cosine")}, + {"tangent", "tan ", FUNCTION, + /* Tooltip for the tangent button */ + N_("Tangent")}, + {"hyperbolic_sine", "sinh ", FUNCTION, + /* Tooltip for the hyperbolic sine button */ + N_("Hyperbolic Sine")}, + {"hyperbolic_cosine", "cosh ", FUNCTION, + /* Tooltip for the hyperbolic cosine button */ + N_("Hyperbolic Cosine")}, + {"hyperbolic_tangent", "tanh ", FUNCTION, + /* Tooltip for the hyperbolic tangent button */ + N_("Hyperbolic Tangent")}, + {"inverse", "⁻¹", FUNCTION, + /* Tooltip for the inverse button */ + N_("Inverse [Ctrl+I]")}, + {"and", "∧", OPERATOR, + /* Tooltip for the boolean AND button */ + N_("Boolean AND")}, + {"or", "∨", OPERATOR, + /* Tooltip for the boolean OR button */ + N_("Boolean OR")}, + {"xor", "⊻", OPERATOR, + /* Tooltip for the exclusive OR button */ + N_("Boolean Exclusive OR")}, + {"not", "¬", FUNCTION, + /* Tooltip for the boolean NOT button */ + N_("Boolean NOT")}, + {"integer_portion", "int ", FUNCTION, + /* Tooltip for the integer component button */ + N_("Integer Component")}, + {"fractional_portion", "frac ", FUNCTION, + /* Tooltip for the fractional component button */ + N_("Fractional Component")}, + {"real_portion", "Re ", FUNCTION, + /* Tooltip for the real component button */ + N_("Real Component")}, + {"imaginary_portion", "Im ", FUNCTION, + /* Tooltip for the imaginary component button */ + N_("Imaginary Component")}, + {"ones_complement", "ones ", FUNCTION, + /* Tooltip for the ones complement button */ + N_("Ones Complement")}, + {"twos_complement", "twos ", FUNCTION, + /* Tooltip for the twos complement button */ + N_("Twos Complement")}, + {"trunc", "trunc ", FUNCTION, + /* Tooltip for the truncate button */ + N_("Truncate")}, + {"start_group", "(", GROUP, + /* Tooltip for the start group button */ + N_("Start Group [(]")}, + {"end_group", ")", GROUP, + /* Tooltip for the end group button */ + N_("End Group [)]")}, + {"store", NULL, MEMORY, + /* Tooltip for the assign variable button */ + N_("Assign Variable")}, + {"recall", NULL, MEMORY, + /* Tooltip for the insert variable button */ + N_("Insert Variable")}, + {"character", NULL, MEMORY, + /* Tooltip for the insert character code button */ + N_("Insert Character Code")}, + {"result", NULL, ACTION, + /* Tooltip for the solve button */ + N_("Calculate Result")}, + {"factor", NULL, ACTION, + /* Tooltip for the factor button */ + N_("Factorize [Ctrl+F]")}, + {"clear", NULL, GROUP, + /* Tooltip for the clear button */ + N_("Clear Display [Escape]")}, + {"undo", NULL, GROUP, + /* Tooltip for the undo button */ + N_("Undo [Ctrl+Z]")}, + {"shift_left", NULL, ACTION, + /* Tooltip for the shift left button */ + N_("Shift Left")}, + {"shift_right", NULL, ACTION, + /* Tooltip for the shift right button */ + N_("Shift Right")}, + {"finc_compounding_term", NULL, FUNCTION, + /* Tooltip for the compounding term button */ + N_("Compounding Term")}, + {"finc_double_declining_depreciation", NULL, FUNCTION, + /* Tooltip for the double declining depreciation button */ + N_("Double Declining Depreciation")}, + {"finc_future_value", NULL, FUNCTION, + /* Tooltip for the future value button */ + N_("Future Value")}, + {"finc_term", NULL, FUNCTION, + /* Tooltip for the financial term button */ + N_("Financial Term")}, + {"finc_sum_of_the_years_digits_depreciation", NULL, FUNCTION, + /* Tooltip for the sum of the years digits depreciation button */ + N_("Sum of the Years Digits Depreciation")}, + {"finc_straight_line_depreciation", NULL, FUNCTION, + /* Tooltip for the straight line depreciation button */ + N_("Straight Line Depreciation")}, + {"finc_periodic_interest_rate", NULL, FUNCTION, + /* Tooltip for the periodic interest rate button */ + N_("Periodic Interest Rate")}, + {"finc_present_value", NULL, FUNCTION, + /* Tooltip for the present value button */ + N_("Present Value")}, + {"finc_periodic_payment", NULL, FUNCTION, + /* Tooltip for the periodic payment button */ + N_("Periodic Payment")}, + {"finc_gross_profit_margin", NULL, FUNCTION, + /* Tooltip for the gross profit margin button */ + N_("Gross Profit Margin")}, + {NULL, NULL, 0, NULL} +}; + +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} +}; + + +MathButtons * +math_buttons_new(MathEquation *equation) +{ + return g_object_new (math_buttons_get_type(), "equation", equation, NULL); +} + + +static void +set_tint (GtkWidget *widget, GdkColor *tint, gint alpha) +{ + GtkStyle *style; + int j; + + if (!widget) + return; + + gtk_widget_ensure_style(widget); + 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 char *value) +{ + GObject *object; + object = gtk_builder_get_object(ui, object_name); + if (object) + g_object_set_data(object, name, GINT_TO_POINTER(value)); +} + + +static void +set_int_data(GtkBuilder *ui, const gchar *object_name, const gchar *name, gint value) +{ + GObject *object; + object = gtk_builder_get_object(ui, object_name); + if (object) + g_object_set_data(object, name, GINT_TO_POINTER(value)); +} + + +static void +load_finc_dialogs(MathButtons *buttons) +{ + int i, j; + + set_int_data(buttons->priv->financial_ui, "ctrm_dialog", "finc_dialog", FINC_CTRM_DIALOG); + set_int_data(buttons->priv->financial_ui, "ddb_dialog", "finc_dialog", FINC_DDB_DIALOG); + set_int_data(buttons->priv->financial_ui, "fv_dialog", "finc_dialog", FINC_FV_DIALOG); + set_int_data(buttons->priv->financial_ui, "gpm_dialog", "finc_dialog", FINC_GPM_DIALOG); + set_int_data(buttons->priv->financial_ui, "pmt_dialog", "finc_dialog", FINC_PMT_DIALOG); + set_int_data(buttons->priv->financial_ui, "pv_dialog", "finc_dialog", FINC_PV_DIALOG); + set_int_data(buttons->priv->financial_ui, "rate_dialog", "finc_dialog", FINC_RATE_DIALOG); + set_int_data(buttons->priv->financial_ui, "sln_dialog", "finc_dialog", FINC_SLN_DIALOG); + set_int_data(buttons->priv->financial_ui, "syd_dialog", "finc_dialog", FINC_SYD_DIALOG); + set_int_data(buttons->priv->financial_ui, "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(buttons->priv->financial_ui, finc_dialog_fields[i][j]); + if(!o) + printf("missing '%s'\n", 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)); + } + } +} + + +static void +update_angle_label (MathButtons *buttons) +{ + MPNumber x; + MPNumber pi, max_value, min_value, fraction, input, output; + char *label, input_text[1024], output_text[1024]; + + if (!buttons->priv->angle_label) + return; + + if (!math_equation_get_number(buttons->priv->equation, &x)) + return; + + mp_get_pi(&pi); + switch (math_equation_get_angle_units(buttons->priv->equation)) { + default: + case MP_DEGREES: + label = g_strdup(""); + break; + case MP_RADIANS: + /* Clip to the range ±2π */ + mp_multiply_integer(&pi, 2, &max_value); + mp_invert_sign(&max_value, &min_value); + if (!mp_is_equal(&x, &max_value) && !mp_is_equal(&x, &min_value)) { + mp_divide(&x, &max_value, &fraction); + mp_fractional_component(&fraction, &fraction); + mp_multiply(&fraction, &max_value, &input); + } + else { + mp_set_from_mp(&x, &input); + mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction); + } + mp_cast_to_string(&input, 10, 10, 2, false, input_text, 1024); + + mp_multiply_integer(&fraction, 360, &output); + mp_cast_to_string(&output, 10, 10, 2, false, output_text, 1024); + label = g_strdup_printf("%s radians = %s degrees", input_text, output_text); + break; + case MP_GRADIANS: + /* Clip to the range ±400 */ + mp_set_from_integer(400, &max_value); + mp_invert_sign(&max_value, &min_value); + if (!mp_is_equal(&x, &max_value) && !mp_is_equal(&x, &min_value)) { + mp_divide(&x, &max_value, &fraction); + mp_fractional_component(&fraction, &fraction); + mp_multiply(&fraction, &max_value, &input); + } + else { + mp_set_from_mp(&x, &input); + mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction); + } + + mp_cast_to_string(&input, 10, 10, 2, false, input_text, 1024); + + mp_multiply_integer(&fraction, 360, &output); + mp_cast_to_string(&output, 10, 10, 2, false, output_text, 1024); + label = g_strdup_printf("%s gradians = %s degrees", input_text, output_text); + break; + } + + gtk_label_set_text(GTK_LABEL(buttons->priv->angle_label), label); + g_free(label); +} + + +static void +update_bit_panel(MathButtons *buttons) +{ + MPNumber x; + gboolean enabled; + guint64 bits; + int i; + GString *label; + gint base; + + if (!buttons->priv->bit_panel) + return; + + enabled = math_equation_get_number(buttons->priv->equation, &x); + + if (enabled) { + MPNumber max, fraction; + + mp_set_from_unsigned_integer(G_MAXUINT64, &max); + mp_fractional_part(&x, &fraction); + if (mp_is_negative(&x) || mp_is_greater_than(&x, &max) || !mp_is_zero(&fraction)) + enabled = FALSE; + else + bits = mp_cast_to_unsigned_int(&x); + } + + gtk_widget_set_sensitive(buttons->priv->bit_panel, enabled); + gtk_widget_set_sensitive(buttons->priv->base_label, enabled); + + if (!enabled) + return; + + for (i = 0; i < MAXBITS; i++) { + const gchar *label; + + if (bits & (1LL << (MAXBITS-i-1))) + label = " 1"; + else + label = " 0"; + gtk_label_set_text(GTK_LABEL(buttons->priv->bit_labels[i]), label); + } + + base = math_equation_get_base(buttons->priv->equation); + label = g_string_new(""); + if (base != 8) { + if (label->len != 0) + g_string_append(label, " = "); + g_string_append_printf(label, "%lo", bits); + g_string_append(label, "₈"); + } + if (base != 10) { + if (label->len != 0) + g_string_append(label, " = "); + g_string_append_printf(label, "%lu", bits); + g_string_append(label, "₁₀"); + } + if (base != 16) { + if (label->len != 0) + g_string_append(label, " = "); + g_string_append_printf(label, "%lX", bits); + g_string_append(label, "₁₆"); + } + + gtk_label_set_text(GTK_LABEL(buttons->priv->base_label), label->str); + g_string_free(label, TRUE); +} + + +static void +update_currency_label(MathButtons *buttons) +{ + MPNumber x, value; + char *label; + + if (!buttons->priv->currency_label) + return; + + if (!math_equation_get_number(buttons->priv->equation, &x)) + return; + + if (currency_convert(&x, + math_equation_get_source_currency(buttons->priv->equation), + math_equation_get_target_currency(buttons->priv->equation), + &value)) { + char input_text[1024], output_text[1024]; + const char *source_symbol, *target_symbol; + int i; + + mp_cast_to_string(&x, 10, 10, 2, false, input_text, 1024); + mp_cast_to_string(&value, 10, 10, 2, false, output_text, 1024); + + for (i = 0; strcmp(math_equation_get_source_currency(buttons->priv->equation), currency_names[i].short_name) != 0; i++); + source_symbol = currency_names[i].symbol; + for (i = 0; strcmp(math_equation_get_target_currency(buttons->priv->equation), currency_names[i].short_name) != 0; i++); + target_symbol = currency_names[i].symbol; + + label = g_strdup_printf("%s%s = %s%s", + source_symbol, input_text, + target_symbol, output_text); + } + else + label = g_strdup(""); + + gtk_label_set_text(GTK_LABEL(buttons->priv->currency_label), label); + g_free(label); +} + + +static void +display_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + update_angle_label(buttons); + update_currency_label(buttons); + update_bit_panel(buttons); +} + + +static void +angle_unit_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons) +{ + MPAngleUnit 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); + math_equation_set_angle_units(buttons->priv->equation, value); +} + + +static void +angle_unit_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->angle_combo)); + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gint v; + + gtk_tree_model_get(model, &iter, 1, &v, -1); + if (v == math_equation_get_angle_units(buttons->priv->equation)) + 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(buttons->priv->angle_combo), &iter); +} + + +static void +base_combobox_changed_cb(GtkWidget *combo, MathButtons *buttons) +{ + 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); + + math_equation_set_base(buttons->priv->equation, value); +} + + +static void +base_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + if (buttons->priv->mode != PROGRAMMING) + return; + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->base_combo)); + valid = gtk_tree_model_get_iter_first(model, &iter); + buttons->priv->programming_base = math_equation_get_base(buttons->priv->equation); + + while (valid) { + gint v; + + gtk_tree_model_get(model, &iter, 1, &v, -1); + if (v == buttons->priv->programming_base) + 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(buttons->priv->base_combo), &iter); +} + + +static void +source_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons) +{ + gchar *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, 0, &value, -1); + + math_equation_set_source_currency(buttons->priv->equation, value); + g_free (value); +} + + +static void +source_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + if (buttons->priv->mode != FINANCIAL) + return; + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo)); + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gchar *v; + gboolean matched; + + gtk_tree_model_get(model, &iter, 0, &v, -1); + matched = strcmp (math_equation_get_source_currency(buttons->priv->equation), v) == 0; + g_free (v); + 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(buttons->priv->source_currency_combo), &iter); + update_currency_label(buttons); +} + + +static void +target_currency_combo_changed_cb(GtkWidget *combo, MathButtons *buttons) +{ + gchar *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, 0, &value, -1); + + math_equation_set_target_currency(buttons->priv->equation, value); + g_free (value); +} + + +static void +target_currency_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + if (buttons->priv->mode != FINANCIAL) + return; + + model = gtk_combo_box_get_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo)); + valid = gtk_tree_model_get_iter_first(model, &iter); + + while (valid) { + gchar *v; + gboolean matched; + + gtk_tree_model_get(model, &iter, 0, &v, -1); + matched = strcmp (math_equation_get_target_currency(buttons->priv->equation), v) == 0; + g_free (v); + 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(buttons->priv->target_currency_combo), &iter); + update_currency_label(buttons); +} + + +static GtkWidget * +load_mode(MathButtons *buttons, ButtonMode mode) +{ + GtkBuilder *builder, **builder_ptr; + gint i; + gchar *name; + const gchar *builder_file; + static gchar *objects[] = { "button_panel", "character_code_dialog", "currency_dialog", + "ctrm_dialog", "ddb_dialog", "fv_dialog", "gpm_dialog", + "pmt_dialog", "pv_dialog", "rate_dialog", "sln_dialog", + "syd_dialog", "term_dialog", "adjustment1", "adjustment2", NULL }; + GtkWidget *widget, **panel; + GError *error = NULL; + + switch (mode) { + case BASIC: + builder_ptr = &buttons->priv->basic_ui; + builder_file = UI_BASIC_FILE; + panel = &buttons->priv->bas_panel; + break; + case ADVANCED: + builder_ptr = &buttons->priv->advanced_ui; + builder_file = UI_ADVANCED_FILE; + panel = &buttons->priv->adv_panel; + break; + case FINANCIAL: + builder_ptr = &buttons->priv->financial_ui; + builder_file = UI_FINANCIAL_FILE; + panel = &buttons->priv->fin_panel; + break; + case PROGRAMMING: + builder_ptr = &buttons->priv->programming_ui; + builder_file = UI_PROGRAMMING_FILE; + panel = &buttons->priv->prog_panel; + break; + } + + if (*panel) + return *panel; + + builder = *builder_ptr = gtk_builder_new(); + // FIXME: Show dialog if failed to load + gtk_builder_add_objects_from_file(builder, builder_file, objects, &error); + if (error) { + g_warning("Error loading button UI: %s", error->message); + g_clear_error(&error); + } + *panel = GET_WIDGET(builder, "button_panel"); + gtk_box_pack_end(GTK_BOX(buttons), *panel, FALSE, TRUE, 0); + + /* Configure buttons */ + for (i = 0; button_data[i].widget_name != NULL; i++) { + GObject *object; + GtkWidget *button; + + name = g_strdup_printf("calc_%s_button", button_data[i].widget_name); + object = gtk_builder_get_object(*builder_ptr, name); + g_free(name); + + if (!object) + continue; + button = GTK_WIDGET(object); + if (button_data[i].data) + g_object_set_data(object, "calc_text", (gpointer) button_data[i].data); + + if (button_data[i].tooltip) + gtk_widget_set_tooltip_text(button, _(button_data[i].tooltip)); + + atk_object_set_name (gtk_widget_get_accessible (button), button_data[i].widget_name); + + switch (button_data[i].class) { + case NUMBER: + set_tint(button, &buttons->priv->color_numbers, 1); + break; + case NUMBER_BOLD: + set_tint(button, &buttons->priv->color_numbers, 2); + break; + case OPERATOR: + set_tint(button, &buttons->priv->color_operator, 1); + break; + case FUNCTION: + set_tint(button, &buttons->priv->color_function, 1); + break; + case MEMORY: + set_tint(button, &buttons->priv->color_memory, 1); + break; + case GROUP: + set_tint(button, &buttons->priv->color_group, 1); + break; + case ACTION: + set_tint(button, &buttons->priv->color_action, 2); + break; + } + } + + /* Set special button data */ + for (i = 0; i < 16; i++) { + GtkWidget *button; + + name = g_strdup_printf("calc_%d_button", i); + button = GET_WIDGET(builder, name); + if (button) { + g_object_set_data(G_OBJECT(button), "calc_digit", GINT_TO_POINTER(i)); + set_tint(button, &buttons->priv->color_numbers, 1); + gtk_button_set_label(GTK_BUTTON(button), math_equation_get_digit_text(buttons->priv->equation, i)); + } + g_free(name); + } + widget = GET_WIDGET(builder, "calc_numeric_point_button"); + if (widget) + gtk_button_set_label(GTK_BUTTON(widget), math_equation_get_numeric_point_text(buttons->priv->equation)); + + widget = GET_WIDGET(builder, "calc_superscript_button"); + if (widget) { + buttons->priv->superscript_toggles = g_list_append(buttons->priv->superscript_toggles, widget); + if (math_equation_get_number_mode(buttons->priv->equation) == SUPERSCRIPT) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); + } + widget = GET_WIDGET(builder, "calc_subscript_button"); + if (widget) { + buttons->priv->subscript_toggles = g_list_append(buttons->priv->subscript_toggles, widget); + if (math_equation_get_number_mode(buttons->priv->equation) == SUBSCRIPT) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); + } + + if (mode == ADVANCED) { + GtkListStore *model; + GtkTreeIter iter; + GtkCellRenderer *renderer; + + buttons->priv->angle_label = GET_WIDGET(builder, "angle_label"); + + buttons->priv->angle_combo = GET_WIDGET(builder, "angle_units_combo"); + model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); + gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->angle_combo), GTK_TREE_MODEL(model)); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Advanced buttons: Angle unit combo box: Use degrees for trigonometric calculations */ + _("Degrees"), 1, MP_DEGREES, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Advanced buttons: Angle unit combo box: Use radians for trigonometric calculations */ + _("Radians"), 1, MP_RADIANS, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Advanced buttons: Angle unit combo box: Use gradians for trigonometric calculations */ + _("Gradians"), 1, MP_GRADIANS, -1); + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->angle_combo), renderer, TRUE); + gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->angle_combo), renderer, "text", 0); + + g_signal_connect(buttons->priv->angle_combo, "changed", G_CALLBACK(angle_unit_combobox_changed_cb), buttons); + g_signal_connect(buttons->priv->equation, "notify::angle-units", G_CALLBACK(angle_unit_cb), buttons); + angle_unit_cb(buttons->priv->equation, NULL, buttons); + } + + if (mode == PROGRAMMING) { + GtkListStore *model; + GtkTreeIter iter; + GtkCellRenderer *renderer; + + buttons->priv->base_label = GET_WIDGET(builder, "base_label"); + buttons->priv->character_code_dialog = GET_WIDGET(builder, "character_code_dialog"); + buttons->priv->character_code_entry = GET_WIDGET(builder, "character_code_entry"); + + buttons->priv->bit_panel = GET_WIDGET(builder, "bit_table"); + for (i = 0; i < MAXBITS; i++) { + name = g_strdup_printf("bit_label_%d", i); + buttons->priv->bit_labels[i] = GET_WIDGET(builder, name); + g_free(name); + name = g_strdup_printf("bit_eventbox_%d", i); + set_int_data(builder, name, "bit_index", i); + } + + buttons->priv->base_combo = GET_WIDGET(builder, "base_combo"); + model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); + gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->base_combo), GTK_TREE_MODEL(model)); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Number display mode combo: Binary, e.g. 10011010010₂ */ + _("Binary"), 1, 2, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Number display mode combo: Octal, e.g. 2322₈ */ + _("Octal"), 1, 8, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Number display mode combo: Decimal, e.g. 1234 */ + _("Decimal"), 1, 10, -1); + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, + /* Number display mode combo: Hexadecimal, e.g. 4D2₁₆ */ + _("Hexadecimal"), 1, 16, -1); + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->base_combo), renderer, TRUE); + gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->base_combo), renderer, "text", 0); + + g_signal_connect(buttons->priv->base_combo, "changed", G_CALLBACK(base_combobox_changed_cb), buttons); + g_signal_connect(buttons->priv->equation, "notify::base", G_CALLBACK(base_changed_cb), buttons); + base_changed_cb(buttons->priv->equation, NULL, buttons); + } + + /* Setup financial functions */ + if (mode == FINANCIAL) { + GtkListStore *model; + GtkCellRenderer *renderer; + + load_finc_dialogs(buttons); + + buttons->priv->source_currency_combo = GET_WIDGET(builder, "source_currency_combo"); + buttons->priv->target_currency_combo = GET_WIDGET(builder, "target_currency_combo"); + buttons->priv->currency_label = GET_WIDGET(builder, "currency_label"); + + model = gtk_list_store_new(1, G_TYPE_STRING); + + for (i = 0; currency_names[i].short_name != NULL; i++) { + GtkTreeIter iter; + + gtk_list_store_append(GTK_LIST_STORE(model), &iter); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, currency_names[i].short_name, -1); + } + + gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->source_currency_combo), GTK_TREE_MODEL(model)); + gtk_combo_box_set_model(GTK_COMBO_BOX(buttons->priv->target_currency_combo), GTK_TREE_MODEL(model)); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, TRUE); + gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->source_currency_combo), renderer, "text", 0); + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, TRUE); + gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(buttons->priv->target_currency_combo), renderer, "text", 0); + + g_signal_connect(buttons->priv->source_currency_combo, "changed", G_CALLBACK(source_currency_combo_changed_cb), buttons); + g_signal_connect(buttons->priv->target_currency_combo, "changed", G_CALLBACK(target_currency_combo_changed_cb), buttons); + g_signal_connect(buttons->priv->equation, "notify::source-currency", G_CALLBACK(source_currency_changed_cb), buttons); + g_signal_connect(buttons->priv->equation, "notify::target-currency", G_CALLBACK(target_currency_changed_cb), buttons); + source_currency_changed_cb(buttons->priv->equation, NULL, buttons); + target_currency_changed_cb(buttons->priv->equation, NULL, buttons); + + 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, buttons); + + display_changed_cb(buttons->priv->equation, NULL, buttons); + + return *panel; +} + + + +static void +load_buttons(MathButtons *buttons) +{ + GtkWidget *panel; + + if (!gtk_widget_get_visible(GTK_WIDGET(buttons))) + return; + + panel = load_mode(buttons, buttons->priv->mode); + if (buttons->priv->active_panel == panel) + return; + + /* Hide old buttons */ + if (buttons->priv->active_panel) + gtk_widget_hide(buttons->priv->active_panel); + + /* Load and display new buttons */ + buttons->priv->active_panel = panel; + if (panel) + gtk_widget_show(panel); +} + + +void +math_buttons_set_mode(MathButtons *buttons, ButtonMode mode) +{ + ButtonMode old_mode; + + if (buttons->priv->mode == mode) + return; + + old_mode = buttons->priv->mode; + buttons->priv->mode = mode; + + if (mode == PROGRAMMING) + math_equation_set_base(buttons->priv->equation, buttons->priv->programming_base); + else + math_equation_set_base(buttons->priv->equation, 10); + + load_buttons(buttons); + + g_object_notify(G_OBJECT(buttons), "mode"); +} + + +ButtonMode +math_buttons_get_mode(MathButtons *buttons) +{ + return buttons->priv->mode; +} + + +void +math_buttons_set_programming_base(MathButtons *buttons, gint base) +{ + buttons->priv->programming_base = base; +} + + +gint +math_buttons_get_programming_base(MathButtons *buttons) +{ + return buttons->priv->programming_base; +} + + +void exponent_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +exponent_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert_exponent(buttons->priv->equation); +} + + +void subtract_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +subtract_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert_subtract(buttons->priv->equation); +} + + +void button_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +button_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert(buttons->priv->equation, g_object_get_data(G_OBJECT(widget), "calc_text")); +} + + +void solve_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +solve_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_solve(buttons->priv->equation); +} + + +void clear_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +clear_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_clear(buttons->priv->equation); +} + + +void delete_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +delete_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_delete(buttons->priv->equation); +} + + +void undo_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +undo_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_undo(buttons->priv->equation); +} + + +static void +shift_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_shift(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "shiftcount"))); +} + + +static void +button_menu_position_func(GtkMenu *menu, gint *x, gint *y, + gboolean *push_in, gpointer user_data) +{ + GtkWidget *button = user_data; + GtkAllocation allocation; + GdkPoint loc; + gint border; + + gdk_window_get_origin(gtk_widget_get_window(button), &loc.x, &loc.y); + border = gtk_container_get_border_width(GTK_CONTAINER(button)); + gtk_widget_get_allocation(button, &allocation); + *x = loc.x + allocation.x + border; + *y = loc.y + allocation.y + border; +} + + +static void +popup_button_menu(GtkWidget *widget, GtkMenu *menu) +{ + gtk_menu_popup(menu, NULL, NULL, + button_menu_position_func, widget, 1, gtk_get_current_event_time()); +} + + +static void +save_variable_cb(GtkWidget *widget, MathButtons *buttons) +{ + printf("save\n"); +} + + +static void +delete_variable_cb(GtkWidget *widget, MathButtons *buttons) +{ + printf("delete\n"); +} + + +static GtkWidget * +make_register_menu_item(MathButtons *buttons, const gchar *name, const MPNumber *value, gboolean can_modify, GCallback callback) +{ + gchar text[1024] = "", *mstr; + GtkWidget *item, *label; + + if (value) { + display_make_number(buttons->priv->equation, text, 1024, value); + mstr = g_strdup_printf("<span weight=\"bold\">%s</span> = %s", name, text); + } + else + mstr = g_strdup_printf("<span weight=\"bold\">%s</span>", name); + label = gtk_label_new(mstr); + gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + g_free(mstr); + + item = gtk_menu_item_new(); + + // FIXME: Buttons don't work inside menus... + if (0) {//can_modify) { + GtkWidget *hbox, *button; + + hbox = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(item), hbox); + + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + + button = gtk_button_new(); + gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU)); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_variable_cb), buttons); + + button = gtk_button_new(); + gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU)); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(save_variable_cb), buttons); + } + else + gtk_container_add(GTK_CONTAINER(item), label); + + g_object_set_data(G_OBJECT(item), "register_id", g_strdup(name)); // FIXME: Memory leak + g_signal_connect(item, "activate", callback, buttons); + + return item; +} + + +static void +store_menu_cb(GtkMenuItem *menu, MathButtons *buttons) +{ + math_equation_store(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id")); +} + + +void store_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +store_cb(GtkWidget *widget, MathButtons *buttons) +{ + int i; + GtkWidget *menu; + GtkWidget *item; + gchar **names; + + menu = gtk_menu_new(); + gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); + set_tint(menu, &buttons->priv->color_memory, 1); + + names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation)); + if (names[0] == NULL) { + item = gtk_menu_item_new_with_label(/* Text shown in store menu when no variables defined */ + _("No variables defined")); + gtk_widget_set_sensitive(item, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + for (i = 0; names[i]; i++) { + MPNumber *value; + value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]); + item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(store_menu_cb)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + + g_strfreev(names); + + // FIXME + //item = gtk_menu_item_new_with_label(_("Add variable")); + //gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + gtk_widget_show_all(menu); + popup_button_menu(widget, GTK_MENU(menu)); +} + + +static void +recall_menu_cb(GtkMenuItem *menu, MathButtons *buttons) +{ + math_equation_recall(buttons->priv->equation, g_object_get_data(G_OBJECT(menu), "register_id")); +} + + +void recall_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +recall_cb(GtkWidget *widget, MathButtons *buttons) +{ + int i; + GtkWidget *menu; + GtkWidget *item; + gchar **names; + + menu = gtk_menu_new(); + gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); + set_tint(menu, &buttons->priv->color_memory, 1); + + names = math_variables_get_names(math_equation_get_variables(buttons->priv->equation)); + if (names[0] == NULL) { + item = gtk_menu_item_new_with_label(/* Text shown in recall menu when no variables defined */ + _("No variables defined")); + gtk_widget_set_sensitive(item, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + for (i = 0; names[i]; i++) { + MPNumber *value; + value = math_variables_get_value(math_equation_get_variables(buttons->priv->equation), names[i]); + item = make_register_menu_item(buttons, names[i], value, TRUE, G_CALLBACK(recall_menu_cb)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + + g_strfreev(names); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); + item = make_register_menu_item(buttons, "ans", math_equation_get_answer(buttons->priv->equation), FALSE, G_CALLBACK(recall_menu_cb)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + item = make_register_menu_item(buttons, "rand", NULL, FALSE, G_CALLBACK(recall_menu_cb)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + gtk_widget_show_all(menu); + popup_button_menu(widget, GTK_MENU(menu)); +} + + +void shift_left_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +shift_left_cb(GtkWidget *widget, MathButtons *buttons) +{ + if (!buttons->priv->shift_left_menu) { + gint i; + GtkWidget *menu; + + menu = buttons->priv->shift_left_menu = gtk_menu_new(); + gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); + set_tint(menu, &buttons->priv->color_action, 1); + + for (i = 1; i < 16; i++) { + GtkWidget *item, *label; + gchar *format, *text; + + if (i < 10) { + /* Left Shift Popup: Menu item to shift left by n places (n < 10) */ + format = ngettext("_%d place", "_%d places", i); + } + else { + /* Left Shift Popup: Menu item to shift left by n places (n >= 10) */ + format = ngettext("%d place", "%d places", i); + } + text = g_strdup_printf(format, i); + label = gtk_label_new_with_mnemonic(text); + + item = gtk_menu_item_new(); + g_object_set_data(G_OBJECT(item), "shiftcount", GINT_TO_POINTER(i)); + gtk_container_add(GTK_CONTAINER(item), label); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(shift_cb), buttons); + + gtk_widget_show(label); + gtk_widget_show(item); + g_free(text); + } + } + + popup_button_menu(widget, GTK_MENU(buttons->priv->shift_left_menu)); +} + + +void shift_right_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +shift_right_cb(GtkWidget *widget, MathButtons *buttons) +{ + if (!buttons->priv->shift_right_menu) { + gint i; + GtkWidget *menu; + + menu = buttons->priv->shift_right_menu = gtk_menu_new(); + gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); + set_tint(menu, &buttons->priv->color_action, 1); + + for (i = 1; i < 16; i++) { + GtkWidget *item, *label; + gchar *format, *text; + + if (i < 10) { + /* Right Shift Popup: Menu item to shift right by n places (n < 10) */ + format = ngettext("_%d place", "_%d places", i); + } + else { + /* Right Shift Popup: Menu item to shift right by n places (n >= 10) */ + format = ngettext("%d place", "%d places", i); + } + text = g_strdup_printf(format, i); + label = gtk_label_new_with_mnemonic(text); + + item = gtk_menu_item_new(); + g_object_set_data(G_OBJECT(item), "shiftcount", GINT_TO_POINTER(-i)); + gtk_container_add(GTK_CONTAINER(item), label); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(shift_cb), buttons); + + gtk_widget_show(label); + gtk_widget_show(item); + g_free(text); + } + } + + popup_button_menu(widget, GTK_MENU(buttons->priv->shift_right_menu)); +} + + +static void +insert_function_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert(buttons->priv->equation, g_object_get_data(G_OBJECT(widget), "function")); +} + + +void function_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +function_cb(GtkWidget *widget, MathButtons *buttons) +{ + if (!buttons->priv->function_menu) { + gint i; + GtkWidget *menu; + struct + { + gchar *name, *function; + } functions[] = + { + { /* Tooltip for the integer component button */ + N_("Integer Component"), "int " }, + { /* Tooltip for the fractional component button */ + N_("Fractional Component"), "frac " }, + { /* Tooltip for the round button */ + N_("Round"), "round " }, + { /* Tooltip for the floor button */ + N_("Floor"), "floor " }, + { /* Tooltip for the ceiling button */ + N_("Ceiling"), "ceil " }, + { /* Tooltip for the ceiling button */ + N_("Sign"), "sgn " }, + { NULL, NULL } + }; + + menu = buttons->priv->function_menu = gtk_menu_new(); + gtk_menu_set_reserve_toggle_size(GTK_MENU(menu), FALSE); + set_tint(menu, &buttons->priv->color_function, 1); + + for (i = 0; functions[i].name != NULL; i++) { + GtkWidget *item; + + item = gtk_menu_item_new_with_label(_(functions[i].name)); + g_object_set_data(G_OBJECT(item), "function", g_strdup (functions[i].function)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(insert_function_cb), buttons); + gtk_widget_show(item); + } + } + + popup_button_menu(widget, GTK_MENU(buttons->priv->function_menu)); +} + + +void factorize_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +factorize_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_factorize (buttons->priv->equation); +} + + +void digit_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +digit_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert_digit(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "calc_digit"))); +} + + +void numeric_point_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +numeric_point_cb(GtkWidget *widget, MathButtons *buttons) +{ + math_equation_insert_numeric_point(buttons->priv->equation); +} + + + +void finc_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +finc_cb(GtkWidget *widget, MathButtons *buttons) +{ + gchar *name; + + name = g_object_get_data(G_OBJECT(widget), "finc_dialog"); + gtk_dialog_run(GTK_DIALOG(GET_WIDGET(buttons->priv->financial_ui, name))); + gtk_widget_hide(GTK_WIDGET(GET_WIDGET(buttons->priv->financial_ui, name))); +} + + +void insert_character_code_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +insert_character_code_cb(GtkWidget *widget, MathButtons *buttons) +{ + gtk_window_present(GTK_WINDOW(buttons->priv->character_code_dialog)); +} + + +void finc_activate_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +finc_activate_cb(GtkWidget *widget, MathButtons *buttons) +{ + 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(buttons->priv->financial_ui, finc_dialog_fields[dialog][field+1]); + gtk_widget_grab_focus(next_widget); + } +} + + +void finc_response_cb(GtkWidget *widget, gint response_id, MathButtons *buttons); +G_MODULE_EXPORT +void +finc_response_cb(GtkWidget *widget, gint response_id, MathButtons *buttons) +{ + 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(buttons->priv->financial_ui, finc_dialog_fields[dialog][i]); + mp_set_from_string(gtk_entry_get_text(GTK_ENTRY(entry)), 10, &arg[i]); + gtk_entry_set_text(GTK_ENTRY(entry), "0"); + } + gtk_widget_grab_focus(GET_WIDGET(buttons->priv->financial_ui, finc_dialog_fields[dialog][0])); + + do_finc_expression(buttons->priv->equation, dialog, &arg[0], &arg[1], &arg[2], &arg[3]); +} + + +void character_code_dialog_response_cb(GtkWidget *dialog, gint response_id, MathButtons *buttons); +G_MODULE_EXPORT +void +character_code_dialog_response_cb(GtkWidget *dialog, gint response_id, MathButtons *buttons) +{ + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(buttons->priv->character_code_entry)); + + if (response_id == GTK_RESPONSE_OK) { + MPNumber x; + int i = 0; + + mp_set_from_integer(0, &x); + while (TRUE) { + mp_add_integer(&x, text[i], &x); + if (text[i+1]) { + mp_shift(&x, 8, &x); + i++; + } + else + break; + } + + math_equation_insert_number(buttons->priv->equation, &x); + } + + gtk_widget_hide(dialog); +} + + +void character_code_dialog_activate_cb(GtkWidget *entry, MathButtons *buttons); +G_MODULE_EXPORT +void +character_code_dialog_activate_cb(GtkWidget *entry, MathButtons *buttons) +{ + character_code_dialog_response_cb(buttons->priv->character_code_dialog, GTK_RESPONSE_OK, buttons); +} + + +gboolean character_code_dialog_delete_cb(GtkWidget *dialog, GdkEvent *event, MathButtons *buttons); +G_MODULE_EXPORT +gboolean +character_code_dialog_delete_cb(GtkWidget *dialog, GdkEvent *event, MathButtons *buttons) +{ + character_code_dialog_response_cb(dialog, GTK_RESPONSE_CANCEL, buttons); + return TRUE; +} + + +gboolean bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, MathButtons *buttons); +G_MODULE_EXPORT +gboolean +bit_toggle_cb(GtkWidget *event_box, GdkEventButton *event, MathButtons *buttons) +{ + math_equation_toggle_bit(buttons->priv->equation, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(event_box), "bit_index"))); + return TRUE; +} + + + +void set_superscript_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +set_superscript_cb(GtkWidget *widget, MathButtons *buttons) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + math_equation_set_number_mode(buttons->priv->equation, SUPERSCRIPT); + else if (math_equation_get_number_mode(buttons->priv->equation) == SUPERSCRIPT) + math_equation_set_number_mode(buttons->priv->equation, NORMAL); +} + + +void set_subscript_cb(GtkWidget *widget, MathButtons *buttons); +G_MODULE_EXPORT +void +set_subscript_cb(GtkWidget *widget, MathButtons *buttons) +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + math_equation_set_number_mode(buttons->priv->equation, SUBSCRIPT); + else if (math_equation_get_number_mode(buttons->priv->equation) == SUBSCRIPT) + math_equation_set_number_mode(buttons->priv->equation, NORMAL); +} + + +static void +number_mode_changed_cb(MathEquation *equation, GParamSpec *spec, MathButtons *buttons) +{ + GList *i; + NumberMode mode; + + mode = math_equation_get_number_mode(equation); + + for (i = buttons->priv->superscript_toggles; i; i = i->next) { + GtkWidget *widget = i->data; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUPERSCRIPT); + } + for (i = buttons->priv->subscript_toggles; i; i = i->next) { + GtkWidget *widget = i->data; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mode == SUBSCRIPT); + } +} + + +static void +math_buttons_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MathButtons *self; + + self = MATH_BUTTONS (object); + + switch (prop_id) { + case PROP_EQUATION: + self->priv->equation = g_value_get_object (value); + math_buttons_set_mode(self, self->priv->mode); + g_signal_connect(self->priv->equation, "notify::display", G_CALLBACK(display_changed_cb), self); + g_signal_connect(self->priv->equation, "notify::number-mode", G_CALLBACK(number_mode_changed_cb), self); + g_signal_connect(self->priv->equation, "notify::angle-units", G_CALLBACK(display_changed_cb), self); + g_signal_connect(self->priv->equation, "notify::number-format", G_CALLBACK(display_changed_cb), self); + number_mode_changed_cb(self->priv->equation, NULL, self); + display_changed_cb(self->priv->equation, NULL, self); + break; + case PROP_MODE: + math_buttons_set_mode(self, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +math_buttons_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MathButtons *self; + + self = MATH_BUTTONS (object); + + switch (prop_id) { + case PROP_EQUATION: + g_value_set_object (value, self->priv->equation); + break; + case PROP_MODE: + g_value_set_int (value, self->priv->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +math_buttons_class_init (MathButtonsClass *klass) +{ + static GEnumValue button_mode_values[] = + { + {BASIC, "basic", "basic"}, + {ADVANCED, "advanced", "advanced"}, + {FINANCIAL, "financial", "financial"}, + {PROGRAMMING, "programming", "programming"}, + {0, NULL, NULL} + }; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = math_buttons_get_property; + object_class->set_property = math_buttons_set_property; + + g_type_class_add_private (klass, sizeof (MathButtonsPrivate)); + + button_mode_type = g_enum_register_static("ButtonMode", button_mode_values); + + g_object_class_install_property (object_class, + PROP_EQUATION, + g_param_spec_object ("equation", + "equation", + "Equation being controlled", + math_equation_get_type(), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_MODE, + g_param_spec_enum ("mode", + "mode", + "Button mode", + button_mode_type, + BASIC, + G_PARAM_READWRITE)); +} + + +static void +math_buttons_init (MathButtons *buttons) +{ + buttons->priv = G_TYPE_INSTANCE_GET_PRIVATE (buttons, math_buttons_get_type(), MathButtonsPrivate); + buttons->priv->programming_base = 10; + gdk_color_parse("#0000FF", &buttons->priv->color_numbers); + gdk_color_parse("#00FF00", &buttons->priv->color_action); + gdk_color_parse("#FF0000", &buttons->priv->color_operator); + gdk_color_parse("#00FFFF", &buttons->priv->color_function); + gdk_color_parse("#FF00FF", &buttons->priv->color_memory); + gdk_color_parse("#FFFFFF", &buttons->priv->color_group); + g_signal_connect(G_OBJECT(buttons), "show", G_CALLBACK(load_buttons), NULL); +} |