2 * Copyright (C) 2008-2011 Robert Ancell
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free Software
6 * Foundation, either version 2 of the License, or (at your option) any later
7 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
12 #include <glib/gi18n.h>
13 #include <gdk/gdkkeysyms.h>
15 #include "math-display.h"
22 struct MathDisplayPrivate
24 /* Equation being displayed */
25 MathEquation
*equation
;
30 /* Buffer that shows errors etc */
31 GtkTextBuffer
*info_buffer
;
33 /* Spinner widget that shows if we're calculating a response */
37 G_DEFINE_TYPE (MathDisplay
, math_display
, GTK_TYPE_VIEWPORT
);
39 #define GET_WIDGET(ui, name) GTK_WIDGET(gtk_builder_get_object(ui, name))
44 return g_object_new(math_display_get_type(), "equation", math_equation_new(), NULL
);
49 math_display_new_with_equation(MathEquation
*equation
)
51 return g_object_new(math_display_get_type(), "equation", equation
, NULL
);
56 math_display_get_equation(MathDisplay
*display
)
58 return display
->priv
->equation
;
63 display_key_press_cb(GtkWidget
*widget
, GdkEventKey
*event
, MathDisplay
*display
)
69 /* Treat keypad keys as numbers even when numlock is off */
72 case GDK_KEY_KP_Insert
:
73 new_keyval
= GDK_KEY_0
;
76 new_keyval
= GDK_KEY_1
;
79 new_keyval
= GDK_KEY_2
;
81 case GDK_KEY_KP_Page_Down
:
82 new_keyval
= GDK_KEY_3
;
85 new_keyval
= GDK_KEY_4
;
87 case GDK_KEY_KP_Begin
: /* This is apparently what "5" does when numlock is off. */
88 new_keyval
= GDK_KEY_5
;
90 case GDK_KEY_KP_Right
:
91 new_keyval
= GDK_KEY_6
;
94 new_keyval
= GDK_KEY_7
;
97 new_keyval
= GDK_KEY_8
;
99 case GDK_KEY_KP_Page_Up
:
100 new_keyval
= GDK_KEY_9
;
108 new_event
= gdk_event_copy((GdkEvent
*)event
);
109 ((GdkEventKey
*)new_event
)->keyval
= new_keyval
;
110 g_signal_emit_by_name(widget
, "key-press-event", new_event
, &result
);
111 gdk_event_free(new_event
);
115 state
= event
->state
& (GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
116 c
= gdk_keyval_to_unicode(event
->keyval
);
119 if (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_KP_Enter
) {
120 math_equation_solve(display
->priv
->equation
);
124 /* Clear on escape */
125 if ((event
->keyval
== GDK_KEY_Escape
&& state
== 0) ||
126 (event
->keyval
== GDK_KEY_BackSpace
&& state
== GDK_CONTROL_MASK
) ||
127 (event
->keyval
== GDK_KEY_Delete
&& state
== GDK_SHIFT_MASK
)) {
128 math_equation_clear(display
->priv
->equation
);
132 /* Numeric keypad will often insert '.' regardless of locale */
133 if (event
->keyval
== GDK_KEY_KP_Decimal
) {
134 math_equation_insert_numeric_point(display
->priv
->equation
);
141 math_equation_insert(display
->priv
->equation
, "×");
145 math_equation_insert(display
->priv
->equation
, "÷");
149 math_equation_insert_subtract(display
->priv
->equation
);
155 if (state
== GDK_CONTROL_MASK
) {
156 switch(event
->keyval
)
158 case GDK_KEY_bracketleft
:
159 math_equation_insert(display
->priv
->equation
, "⌈");
161 case GDK_KEY_bracketright
:
162 math_equation_insert(display
->priv
->equation
, "⌉");
165 math_equation_insert_exponent(display
->priv
->equation
);
168 math_equation_factorize(display
->priv
->equation
);
171 math_equation_insert(display
->priv
->equation
, "⁻¹");
174 math_equation_insert(display
->priv
->equation
, "π");
177 math_equation_insert(display
->priv
->equation
, "√");
180 math_equation_insert(display
->priv
->equation
, "µ");
183 math_equation_insert(display
->priv
->equation
, "⁻");
185 case GDK_KEY_apostrophe
:
186 math_equation_insert(display
->priv
->equation
, "°");
190 if (state
== GDK_MOD1_MASK
) {
191 switch(event
->keyval
)
193 case GDK_KEY_bracketleft
:
194 math_equation_insert(display
->priv
->equation
, "⌊");
196 case GDK_KEY_bracketright
:
197 math_equation_insert(display
->priv
->equation
, "⌋");
202 if (state
== GDK_CONTROL_MASK
|| math_equation_get_number_mode(display
->priv
->equation
) == SUPERSCRIPT
) {
203 switch(event
->keyval
)
207 math_equation_insert(display
->priv
->equation
, "⁰");
211 math_equation_insert(display
->priv
->equation
, "¹");
215 math_equation_insert(display
->priv
->equation
, "²");
219 math_equation_insert(display
->priv
->equation
, "³");
223 math_equation_insert(display
->priv
->equation
, "⁴");
227 math_equation_insert(display
->priv
->equation
, "⁵");
231 math_equation_insert(display
->priv
->equation
, "⁶");
235 math_equation_insert(display
->priv
->equation
, "⁷");
239 math_equation_insert(display
->priv
->equation
, "⁸");
243 math_equation_insert(display
->priv
->equation
, "⁹");
247 else if (state
== GDK_MOD1_MASK
|| math_equation_get_number_mode(display
->priv
->equation
) == SUBSCRIPT
) {
248 switch(event
->keyval
)
252 math_equation_insert(display
->priv
->equation
, "₀");
256 math_equation_insert(display
->priv
->equation
, "₁");
260 math_equation_insert(display
->priv
->equation
, "₂");
264 math_equation_insert(display
->priv
->equation
, "₃");
268 math_equation_insert(display
->priv
->equation
, "₄");
272 math_equation_insert(display
->priv
->equation
, "₅");
276 math_equation_insert(display
->priv
->equation
, "₆");
280 math_equation_insert(display
->priv
->equation
, "₇");
284 math_equation_insert(display
->priv
->equation
, "₈");
288 math_equation_insert(display
->priv
->equation
, "₉");
298 key_press_cb(MathDisplay
*display
, GdkEventKey
*event
)
301 g_signal_emit_by_name(display
->priv
->text_view
, "key-press-event", event
, &result
);
307 status_changed_cb(MathEquation
*equation
, GParamSpec
*spec
, MathDisplay
*display
)
309 gtk_text_buffer_set_text(display
->priv
->info_buffer
, math_equation_get_status(equation
), -1);
310 if (math_equation_in_solve(equation
) && !gtk_widget_get_visible(display
->priv
->spinner
)) {
311 gtk_widget_show(display
->priv
->spinner
);
312 gtk_spinner_start(GTK_SPINNER(display
->priv
->spinner
));
314 else if (!math_equation_in_solve(equation
) && gtk_widget_get_visible(display
->priv
->spinner
)) {
315 gtk_widget_hide(display
->priv
->spinner
);
316 gtk_spinner_stop(GTK_SPINNER(display
->priv
->spinner
));
322 create_gui(MathDisplay
*display
)
324 GtkWidget
*info_view
, *info_box
, *main_box
;
325 PangoFontDescription
*font_desc
;
329 main_box
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
330 gtk_container_add(GTK_CONTAINER(display
), main_box
);
332 g_signal_connect(display
, "key-press-event", G_CALLBACK(key_press_cb
), display
);
334 display
->priv
->text_view
= gtk_text_view_new_with_buffer(GTK_TEXT_BUFFER(display
->priv
->equation
));
335 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(display
->priv
->text_view
), GTK_WRAP_WORD
);
336 gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(display
->priv
->text_view
), FALSE
);
337 gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(display
->priv
->text_view
), 8);
338 gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(display
->priv
->text_view
), 2);
339 /* TEMP: Disabled for now as GTK+ doesn't properly render a right aligned right margin, see bug #482688 */
340 /*gtk_text_view_set_right_margin(GTK_TEXT_VIEW(display->priv->text_view), 6);*/
341 gtk_text_view_set_justification(GTK_TEXT_VIEW(display
->priv
->text_view
), GTK_JUSTIFY_RIGHT
);
342 gtk_widget_ensure_style(display
->priv
->text_view
);
343 font_desc
= pango_font_description_copy(gtk_widget_get_style(display
->priv
->text_view
)->font_desc
);
344 pango_font_description_set_size(font_desc
, 16 * PANGO_SCALE
);
345 gtk_widget_modify_font(display
->priv
->text_view
, font_desc
);
346 pango_font_description_free(font_desc
);
347 gtk_widget_set_name(display
->priv
->text_view
, "displayitem");
348 atk_object_set_role(gtk_widget_get_accessible(display
->priv
->text_view
), ATK_ROLE_EDITBAR
);
349 //FIXME:<property name="AtkObject::accessible-description" translatable="yes" comments="Accessible description for the area in which results are displayed">Result Region</property>
350 g_signal_connect(display
->priv
->text_view
, "key-press-event", G_CALLBACK(display_key_press_cb
), display
);
351 gtk_box_pack_start(GTK_BOX(main_box
), display
->priv
->text_view
, TRUE
, TRUE
, 0);
353 info_box
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
354 gtk_box_pack_start(GTK_BOX(main_box
), info_box
, FALSE
, TRUE
, 0);
356 info_view
= gtk_text_view_new();
357 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(info_view
), GTK_WRAP_WORD
);
358 gtk_widget_set_can_focus(info_view
, TRUE
); // FIXME: This should be FALSE but it locks the cursor inside the main view for some reason
359 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(info_view
), FALSE
); // FIXME: Just here so when incorrectly gets focus doesn't look editable
360 gtk_text_view_set_editable(GTK_TEXT_VIEW(info_view
), FALSE
);
361 gtk_text_view_set_justification(GTK_TEXT_VIEW(info_view
), GTK_JUSTIFY_RIGHT
);
362 /* TEMP: Disabled for now as GTK+ doesn't properly render a right aligned right margin, see bug #482688 */
363 /*gtk_text_view_set_right_margin(GTK_TEXT_VIEW(info_view), 6);*/
364 gtk_box_pack_start(GTK_BOX(info_box
), info_view
, TRUE
, TRUE
, 0);
365 display
->priv
->info_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(info_view
));
367 display
->priv
->spinner
= gtk_spinner_new();
368 gtk_box_pack_end(GTK_BOX(info_box
), display
->priv
->spinner
, FALSE
, FALSE
, 0);
369 style
= gtk_widget_get_style(info_view
);
370 for (i
= 0; i
< 5; i
++) {
371 gtk_widget_modify_bg(GTK_WIDGET(display
), i
, &style
->base
[i
]);
374 gtk_widget_show(info_box
);
375 gtk_widget_show(info_view
);
376 gtk_widget_show(display
->priv
->text_view
);
377 gtk_widget_show(main_box
);
379 g_signal_connect(display
->priv
->equation
, "notify::status", G_CALLBACK(status_changed_cb
), display
);
380 status_changed_cb(display
->priv
->equation
, NULL
, display
);
385 math_display_set_property(GObject
*object
,
392 self
= MATH_DISPLAY(object
);
396 self
->priv
->equation
= g_value_get_object(value
);
400 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
407 math_display_get_property(GObject
*object
,
414 self
= MATH_DISPLAY(object
);
418 g_value_set_object(value
, self
->priv
->equation
);
421 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
428 math_display_class_init(MathDisplayClass
*klass
)
430 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
432 object_class
->get_property
= math_display_get_property
;
433 object_class
->set_property
= math_display_set_property
;
435 g_type_class_add_private(klass
, sizeof(MathDisplayPrivate
));
437 g_object_class_install_property(object_class
,
439 g_param_spec_object("equation",
441 "Equation being displayed",
442 math_equation_get_type(),
443 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
448 math_display_init(MathDisplay
*display
)
450 display
->priv
= G_TYPE_INSTANCE_GET_PRIVATE(display
, math_display_get_type(), MathDisplayPrivate
);