1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2007, 2010, 2011, 2015, 2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include <gdk/gdkkeysyms.h>
20 #include "psppire-keypad.h"
23 #define _(msgid) gettext (msgid)
32 static guint keypad_signals [n_SIGNALS] = { 0 };
34 G_DEFINE_TYPE (PsppireKeypad, psppire_keypad, GTK_TYPE_EVENT_BOX)
36 static GObjectClass * parent_class = NULL;
39 psppire_keypad_dispose (GObject *obj)
41 PsppireKeypad *kp = PSPPIRE_KEYPAD (obj);
43 if (kp->dispose_has_run)
46 /* Make sure dispose does not run twice. */
47 kp->dispose_has_run = TRUE;
49 g_hash_table_unref (kp->frag_table);
51 /* Chain up to the parent class */
52 G_OBJECT_CLASS (parent_class)->dispose (obj);
56 psppire_keypad_finalize (GObject *obj)
58 /* Chain up to the parent class */
59 G_OBJECT_CLASS (parent_class)->finalize (obj);
63 psppire_keypad_class_init (PsppireKeypadClass *klass)
65 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
67 parent_class = g_type_class_peek_parent (klass);
69 gobject_class->dispose = psppire_keypad_dispose;
70 gobject_class->finalize = psppire_keypad_finalize;
72 keypad_signals[INSERT_SYNTAX] = g_signal_new ("insert-syntax",
73 G_TYPE_FROM_CLASS (klass),
74 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
75 G_STRUCT_OFFSET (PsppireKeypadClass,
79 g_cclosure_marshal_VOID__STRING,
83 keypad_signals[ERASE] = g_signal_new ("erase",
84 G_TYPE_FROM_CLASS (klass),
85 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
86 G_STRUCT_OFFSET (PsppireKeypadClass,
90 g_cclosure_marshal_VOID__VOID,
96 These are the strings that will be arguments to
98 The order of these must correspond
99 to the order of the button declarations
101 static const char * const keypad_insert_text[] =
103 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
104 ".", "+", "-", "*", "**", "/", "=", "<>", "<", "<=",
105 ">", ">=", "&", "|", "~", "()", NULL
109 /* Callback for any button click.
110 Emits the "insert-syntax" signal for the keypad,
111 with the string corresponding to the clicked button.
114 button_click (GtkButton *b, PsppireKeypad *kp)
116 const gchar *s = g_hash_table_lookup (kp->frag_table, b);
120 g_signal_emit (kp, keypad_signals [INSERT_SYNTAX], 0, s);
122 g_signal_emit (kp, keypad_signals [ERASE], 0);
125 static const gint cols = 6;
126 static const gint rows = 5;
130 /* Add BUTTON to KP. The top-left corner at X1,Y1, the
131 botton-right corner at X2,Y2 */
133 add_button (PsppireKeypad *kp, GtkWidget **button,
137 g_object_set (G_OBJECT (*button), "focus-on-click", FALSE, NULL);
139 gtk_grid_attach (GTK_GRID(kp->table), *button, x1, y1, x2 - x1, y2 - y1);
141 gtk_widget_set_size_request (*button,
142 30 * rows / (float) cols,
143 30 * cols / (float) rows);
145 g_hash_table_insert (kp->frag_table, *button,
146 (void *) keypad_insert_text[(button - &kp->digit[0])]);
148 g_signal_connect (*button, "clicked",
149 G_CALLBACK (button_click), kp);
151 gtk_widget_show (*button);
155 /* Return a new button with CODE as the unicode character for its label */
156 static inline GtkWidget *
157 button_new_from_unicode (gunichar code)
159 char s[6] = {0,0,0,0,0,0};
161 g_unichar_to_utf8 (code, s);
163 return gtk_button_new_with_label (s);
167 /* Callback which occurs when the mouse enters the widget.
168 It sets or unsets the focus.
171 enter_leave_notify (GtkWidget *widget,
172 GdkEventCrossing *event,
175 /* Do nothing if we're just moving between the widget and
177 if (event->detail == GDK_NOTIFY_INFERIOR)
180 if (event->type == GDK_ENTER_NOTIFY)
181 gtk_widget_grab_focus (widget);
187 key_release_callback (GtkWidget *widget,
191 if (! gtk_widget_has_focus (widget))
194 switch (event->keyval)
197 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "(");
200 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, ")");
203 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, ">");
206 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "<");
208 case GDK_KEY_KP_Equal :
210 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "=");
212 case GDK_KEY_KP_Multiply :
214 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "*");
216 case GDK_KEY_KP_Add :
218 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "+");
220 case GDK_KEY_KP_Subtract :
222 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "-");
224 case GDK_KEY_KP_Decimal :
226 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, ".");
228 case GDK_KEY_KP_Divide :
230 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "/");
234 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "0");
238 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "1");
242 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "2");
246 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "3");
250 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "4");
254 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "5");
258 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "6");
262 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "7");
266 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "8");
270 g_signal_emit (widget, keypad_signals [INSERT_SYNTAX], 0, "9");
281 psppire_keypad_init (PsppireKeypad *kp)
284 const int digit_voffset = 0;
285 const int digit_hoffset = 3;
287 gtk_widget_set_can_focus (GTK_WIDGET (kp), TRUE);
289 kp->dispose_has_run = FALSE;
291 g_signal_connect (kp, "enter-notify-event", G_CALLBACK (enter_leave_notify),
294 g_signal_connect (kp, "leave-notify-event", G_CALLBACK (enter_leave_notify),
297 g_signal_connect (kp, "key-release-event", G_CALLBACK (key_release_callback),
300 kp->frag_table = g_hash_table_new (g_direct_hash, g_direct_equal);
302 kp->table = gtk_grid_new ();
304 /* Buttons for the digits */
305 for (i = 0; i < 10; i++)
309 g_snprintf (buf, 5, "%d", i);
310 kp->digit[i] = gtk_button_new_with_label (buf);
313 add_button (kp, &kp->digit[i],
314 digit_hoffset + 0, digit_hoffset + 2,
315 digit_voffset + 3, digit_voffset + 4);
317 add_button (kp, &kp->digit[i],
318 digit_hoffset + j % 3, digit_hoffset + j % 3 + 1,
319 digit_voffset + 2 - (j / 3),
320 digit_voffset + 2 - (j / 3) + 1);
323 /* ... all the other buttons */
325 kp->dot = button_new_from_unicode (0xB7); /* MIDDLE DOT */
326 add_button (kp, &kp->dot, digit_hoffset + 2,
331 kp->plus = gtk_button_new_with_label ("+");
332 gtk_widget_set_tooltip_text (kp->plus, _("add"));
333 add_button (kp, &kp->plus, 0, 1, 0, 1);
335 kp->minus = button_new_from_unicode (0x2212); /* MINUS SIGN */
336 gtk_widget_set_tooltip_text (kp->minus, _("subtract"));
337 add_button (kp, &kp->minus, 0, 1, 1, 2);
339 kp->star = button_new_from_unicode (0xD7); /* MULTIPLICATION SIGN */
340 gtk_widget_set_tooltip_text (kp->star, _("multiply"));
341 add_button (kp, &kp->star, 0, 1, 2, 3);
343 kp->slash = button_new_from_unicode (0xF7); /* DIVISION SIGN */
344 gtk_widget_set_tooltip_text (kp->slash, _("divide"));
345 add_button (kp, &kp->slash, 0, 1, 3, 4);
351 g_markup_printf_escaped ("<span style=\"italic\">x<sup>y</sup></span>");
353 label = gtk_label_new ("**");
355 gtk_label_set_markup (GTK_LABEL (label), markup);
358 kp->star_star = gtk_button_new ();
359 gtk_widget_set_tooltip_text (kp->star_star, _("raise x to the power of y"));
361 gtk_container_add (GTK_CONTAINER (kp->star_star), label);
363 gtk_widget_show (label);
365 add_button (kp, &kp->star_star, 0, 1, 4, 5);
369 kp->gt = button_new_from_unicode (0x3E); /* GREATER-THAN SIGN*/
370 gtk_widget_set_tooltip_text (kp->gt, _("is greater than"));
371 add_button (kp, &kp->gt, 2, 3, 0, 1);
373 kp->lt = button_new_from_unicode (0x3C); /* LESS-THAN SIGN*/
374 gtk_widget_set_tooltip_text (kp->lt, _("is less than"));
375 add_button (kp, &kp->lt, 1, 2, 0, 1);
377 kp->ge = button_new_from_unicode (0x2265); /* GREATER-THAN OR EQUAL */
378 gtk_widget_set_tooltip_text (kp->ge, _("is no less than"));
379 add_button (kp, &kp->ge, 2, 3, 1, 2);
381 kp->le = button_new_from_unicode (0x2264); /* LESS-THAN OR EQUAL */
382 gtk_widget_set_tooltip_text (kp->le, _("is no greater than"));
383 add_button (kp, &kp->le, 1, 2, 1, 2);
385 kp->neq = button_new_from_unicode (0x2260); /* NOT EQUAL */
386 gtk_widget_set_tooltip_text (kp->neq, _("is not equal to"));
387 add_button (kp, &kp->neq, 2, 3, 2, 3);
389 kp->eq = gtk_button_new_with_label ("=");
390 gtk_widget_set_tooltip_text (kp->eq, _("is equal to"));
391 add_button (kp, &kp->eq, 1, 2, 2, 3);
393 kp->parentheses = gtk_button_new_with_label ("()");
394 add_button (kp, &kp->parentheses, 2, 3, 4, 5);
396 kp->delete = gtk_button_new_with_label ("Delete");
397 add_button (kp, &kp->delete, 3, 6, 4, 5);
399 kp->and = button_new_from_unicode (0x2227); /* LOGICAL AND */
400 gtk_widget_set_tooltip_text (kp->and, _("and"));
401 add_button (kp, &kp->and, 1, 2, 3, 4);
403 kp->or = button_new_from_unicode (0x2228); /* LOGICAL OR */
404 gtk_widget_set_tooltip_text (kp->or, _("or"));
405 add_button (kp, &kp->or, 2, 3, 3, 4);
407 kp->not = button_new_from_unicode (0xAC); /* NOT SIGN */
408 gtk_widget_set_tooltip_text (kp->not, _("not"));
409 add_button (kp, &kp->not, 1, 2, 4, 5);
411 g_object_set (G_OBJECT (kp->table), "row-spacing", 5, NULL);
412 g_object_set (G_OBJECT (kp->table), "column-spacing", 5, NULL);
414 gtk_container_add (GTK_CONTAINER (kp), kp->table);
415 gtk_widget_show (kp->table);
417 gtk_widget_add_events (GTK_WIDGET (kp),
418 GDK_KEY_RELEASE_MASK |
419 GDK_LEAVE_NOTIFY_MASK |
420 GDK_ENTER_NOTIFY_MASK |
421 GDK_FOCUS_CHANGE_MASK);
426 psppire_keypad_new (void)
428 return GTK_WIDGET (g_object_new (psppire_keypad_get_type (), NULL));