summaryrefslogtreecommitdiff
path: root/src/math-variable-popup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/math-variable-popup.c')
-rw-r--r--src/math-variable-popup.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/math-variable-popup.c b/src/math-variable-popup.c
new file mode 100644
index 0000000..d033571
--- /dev/null
+++ b/src/math-variable-popup.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2008-2011 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 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "math-variable-popup.h"
+
+enum {
+ PROP_0,
+ PROP_EQUATION
+};
+
+struct MathVariablePopupPrivate
+{
+ MathEquation *equation;
+
+ GtkWidget *vbox;
+ GtkWidget *variable_name_entry;
+ GtkWidget *add_variable_button;
+};
+
+G_DEFINE_TYPE (MathVariablePopup, math_variable_popup, GTK_TYPE_WINDOW);
+
+MathVariablePopup *
+math_variable_popup_new(MathEquation *equation)
+{
+ return g_object_new(math_variable_popup_get_type(), "equation", equation, NULL);
+}
+
+
+static void
+variable_focus_out_event_cb(GtkWidget *widget, GdkEventFocus *event, MathVariablePopup *popup)
+{
+ gtk_widget_destroy(widget);
+}
+
+
+static void
+insert_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ math_equation_insert(popup->priv->equation, name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static gboolean
+variable_name_key_press_cb(GtkWidget *widget, GdkEventKey *event, MathVariablePopup *popup)
+{
+ /* Can't have whitespace in names, so replace with underscores */
+ if (event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
+ event->keyval = GDK_KEY_underscore;
+
+ return FALSE;
+}
+
+
+static void
+variable_name_changed_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *text = gtk_entry_get_text(GTK_ENTRY(popup->priv->variable_name_entry));
+ gtk_widget_set_sensitive(popup->priv->add_variable_button, text[0] != '\0');
+}
+
+
+static void
+add_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+ MPNumber z;
+
+ name = gtk_entry_get_text(GTK_ENTRY(popup->priv->variable_name_entry));
+ if (name[0] == '\0')
+ return;
+
+ if (math_equation_get_number(popup->priv->equation, &z))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, &z);
+ else if (math_equation_is_result(popup->priv->equation))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, math_equation_get_answer(popup->priv->equation));
+ else
+ g_warning("Can't add variable %s, the display is not a number", name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static void
+save_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+ MPNumber z;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ if (math_equation_get_number(popup->priv->equation, &z))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, &z);
+ else if (math_equation_is_result(popup->priv->equation))
+ math_variables_set(math_equation_get_variables(popup->priv->equation), name, math_equation_get_answer(popup->priv->equation));
+ else
+ g_warning("Can't save variable %s, the display is not a number", name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static void
+delete_variable_cb(GtkWidget *widget, MathVariablePopup *popup)
+{
+ const gchar *name;
+
+ name = g_object_get_data(G_OBJECT(widget), "variable_name");
+ math_variables_delete(math_equation_get_variables(popup->priv->equation), name);
+
+ gtk_widget_destroy(gtk_widget_get_toplevel(widget));
+}
+
+
+static GtkWidget *
+make_variable_entry(MathVariablePopup *popup, const gchar *name, const MPNumber *value, gboolean writable)
+{
+ GtkWidget *hbox, *button, *label;
+ gchar *text;
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+
+ if (value)
+ {
+ gchar *value_text = mp_serializer_to_string(math_equation_get_serializer(popup->priv->equation), value);
+ text = g_strdup_printf("<b>%s</b> = %s", name, value_text);
+ g_free (value_text);
+ }
+ else
+ text = g_strdup_printf("<b>%s</b>", name);
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name)); // FIXME: These will all leak memory
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_variable_cb), popup);
+ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ label = gtk_label_new(text);
+ g_free(text);
+ gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_container_add(GTK_CONTAINER(button), label);
+ gtk_widget_show(label);
+
+ if (writable)
+ {
+ GtkWidget *image;
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name));
+ image = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(save_variable_cb), popup);
+ gtk_widget_show(image);
+ gtk_widget_show(button);
+
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "variable_name", g_strdup(name));
+ image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_variable_cb), popup);
+ gtk_widget_show(image);
+ gtk_widget_show(button);
+ }
+
+ return hbox;
+}
+
+
+static void
+math_variable_popup_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MathVariablePopup *self;
+ gchar **names;
+ int i;
+ GtkWidget *entry, *image;
+
+ self = MATH_VARIABLE_POPUP(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ self->priv->equation = g_value_get_object(value);
+
+ names = math_variables_get_names(math_equation_get_variables(self->priv->equation));
+ for (i = 0; names[i]; i++) {
+ MPNumber *value;
+
+ value = math_variables_get(math_equation_get_variables(self->priv->equation), names[i]);
+ entry = make_variable_entry(self, names[i], value, TRUE);
+ gtk_widget_show(entry);
+ gtk_box_pack_start(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+ }
+ g_strfreev(names);
+
+ entry = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_widget_show(entry);
+
+ // TODO: Show greyed "variable name" text to give user a hint how to use
+ self->priv->variable_name_entry = gtk_entry_new();
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "key-press-event", G_CALLBACK(variable_name_key_press_cb), self);
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "changed", G_CALLBACK(variable_name_changed_cb), self);
+ g_signal_connect(G_OBJECT(self->priv->variable_name_entry), "activate", G_CALLBACK(add_variable_cb), self);
+ gtk_box_pack_start(GTK_BOX(entry), self->priv->variable_name_entry, TRUE, TRUE, 0);
+ gtk_widget_show(self->priv->variable_name_entry);
+
+ self->priv->add_variable_button = gtk_button_new();
+ gtk_widget_set_sensitive(self->priv->add_variable_button, FALSE);
+ g_signal_connect(G_OBJECT(self->priv->add_variable_button), "clicked", G_CALLBACK(add_variable_cb), self);
+ image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
+ gtk_container_add(GTK_CONTAINER(self->priv->add_variable_button), image);
+ gtk_box_pack_start(GTK_BOX(entry), self->priv->add_variable_button, FALSE, TRUE, 0);
+ gtk_widget_show(image);
+ gtk_widget_show(self->priv->add_variable_button);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+
+ entry = make_variable_entry(self, "rand", NULL, FALSE);
+ gtk_widget_show(entry);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+
+ entry = make_variable_entry(self, "ans", math_equation_get_answer(self->priv->equation), FALSE);
+ gtk_widget_show(entry);
+ gtk_box_pack_end(GTK_BOX(self->priv->vbox), entry, FALSE, TRUE, 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+math_variable_popup_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MathVariablePopup *self;
+
+ self = MATH_VARIABLE_POPUP(object);
+
+ switch (prop_id) {
+ case PROP_EQUATION:
+ g_value_set_object(value, self->priv->equation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+math_variable_popup_class_init(MathVariablePopupClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = math_variable_popup_get_property;
+ object_class->set_property = math_variable_popup_set_property;
+
+ g_type_class_add_private(klass, sizeof(MathVariablePopupPrivate));
+
+ 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));
+}
+
+
+static void
+math_variable_popup_init(MathVariablePopup *popup)
+{
+ popup->priv = G_TYPE_INSTANCE_GET_PRIVATE(popup, math_variable_popup_get_type(), MathVariablePopupPrivate);
+
+ gtk_window_set_decorated(GTK_WINDOW(popup), FALSE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(popup), TRUE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(popup), 6);
+
+ /* Destroy this window when it loses focus */
+ g_signal_connect(G_OBJECT(popup), "focus-out-event", G_CALLBACK(variable_focus_out_event_cb), popup);
+
+ popup->priv->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_set_homogeneous(GTK_BOX(popup->priv->vbox), TRUE);
+ gtk_container_add(GTK_CONTAINER(popup), popup->priv->vbox);
+ gtk_widget_show(popup->priv->vbox);
+}