Change 'undo' and 'clear' buttons to use symbolic icons
[gcalctool.git] / src / math-window.c
blob803acd0bd3a3cf3811f901192458c11e850adbc7
1 /*
2 * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (C) 2008-2011 Robert Ancell.
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 2 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9 * license.
12 #include <glib/gi18n.h>
13 #include <gtk/gtk.h>
14 #include <gdk/gdkkeysyms.h>
16 #include "math-window.h"
17 #include "math-preferences.h"
19 enum {
20 PROP_0,
21 PROP_EQUATION
24 struct MathWindowPrivate
26 GtkWidget *menu_bar;
27 MathEquation *equation;
28 MathDisplay *display;
29 MathButtons *buttons;
30 MathPreferencesDialog *preferences_dialog;
31 gboolean right_aligned;
32 GtkWidget *mode_basic_menu_item, *mode_advanced_menu_item, *mode_financial_menu_item, *mode_programming_menu_item;
35 G_DEFINE_TYPE (MathWindow, math_window, GTK_TYPE_WINDOW);
37 enum
39 QUIT,
40 LAST_SIGNAL
42 static guint signals[LAST_SIGNAL] = { 0, };
45 MathWindow *
46 math_window_new(MathEquation *equation)
48 return g_object_new(math_window_get_type(), "equation", equation, NULL);
52 GtkWidget *
53 math_window_get_menu_bar(MathWindow *window)
55 g_return_val_if_fail(window != NULL, NULL);
56 return window->priv->menu_bar;
60 MathEquation *
61 math_window_get_equation(MathWindow *window)
63 g_return_val_if_fail(window != NULL, NULL);
64 return window->priv->equation;
68 MathDisplay *
69 math_window_get_display(MathWindow *window)
71 g_return_val_if_fail(window != NULL, NULL);
72 return window->priv->display;
76 MathButtons *
77 math_window_get_buttons(MathWindow *window)
79 g_return_val_if_fail(window != NULL, NULL);
80 return window->priv->buttons;
84 void
85 math_window_critical_error(MathWindow *window, const gchar *title, const gchar *contents)
87 GtkWidget *dialog;
89 g_return_if_fail(window != NULL);
90 g_return_if_fail(title != NULL);
91 g_return_if_fail(contents != NULL);
93 dialog = gtk_message_dialog_new(NULL, 0,
94 GTK_MESSAGE_ERROR,
95 GTK_BUTTONS_NONE,
96 "%s", title);
97 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
98 "%s", contents);
99 gtk_dialog_add_buttons(GTK_DIALOG(dialog), GTK_STOCK_QUIT, GTK_RESPONSE_ACCEPT, NULL);
101 gtk_dialog_run(GTK_DIALOG(dialog));
103 g_signal_emit(window, signals[QUIT], 0);
107 static void
108 copy_cb(GtkWidget *widget, MathWindow *window)
110 math_equation_copy(window->priv->equation);
114 static void
115 paste_cb(GtkWidget *widget, MathWindow *window)
117 math_equation_paste(window->priv->equation);
121 static void
122 undo_cb(GtkWidget *widget, MathWindow *window)
124 math_equation_undo(window->priv->equation);
128 static void
129 redo_cb(GtkWidget *widget, MathWindow *window)
131 math_equation_redo(window->priv->equation);
135 static void
136 mode_changed_cb(GtkWidget *menu, MathWindow *window)
138 int mode;
140 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)))
141 return;
143 mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu), "calcmode"));
144 math_buttons_set_mode(window->priv->buttons, mode);
148 static void
149 show_preferences_cb(GtkMenuItem *menu, MathWindow *window)
151 if (!window->priv->preferences_dialog) {
152 window->priv->preferences_dialog = math_preferences_dialog_new(window->priv->equation);
153 gtk_window_set_transient_for(GTK_WINDOW(window->priv->preferences_dialog), GTK_WINDOW(window));
155 gtk_window_present(GTK_WINDOW(window->priv->preferences_dialog));
159 static void
160 help_cb(GtkWidget *widget, MathWindow *window)
162 GdkScreen *screen;
163 GError *error = NULL;
165 screen = gtk_widget_get_screen(GTK_WIDGET(window));
166 gtk_show_uri(screen, "help:gcalctool", gtk_get_current_event_time(), &error);
168 if (error != NULL)
170 GtkWidget *d;
171 /* Translators: Error message displayed when unable to launch help browser */
172 const char *message = _("Unable to open help file");
174 d = gtk_message_dialog_new(GTK_WINDOW (window),
175 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
176 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
177 "%s", message);
178 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG (d),
179 "%s", error->message);
180 g_signal_connect(d, "response", G_CALLBACK(gtk_widget_destroy), NULL);
181 gtk_window_present(GTK_WINDOW(d));
183 g_error_free(error);
188 static void
189 about_cb(GtkWidget *widget, MathWindow *window)
191 const gchar *authors[] = {
192 "Rich Burridge <rich.burridge@gmail.com>",
193 "Robert Ancell <robert.ancell@gmail.com>",
194 "Klaus Niederkrüger <kniederk@umpa.ens-lyon.fr>",
195 "Robin Sonefors <ozamosi@flukkost.nu>",
196 NULL
198 const gchar *documenters[] = {
199 "Sun Microsystems",
200 NULL
203 /* The translator credits. Please translate this with your name(s). */
204 const gchar *translator_credits = _("translator-credits");
206 /* The license this software is under (GPL2+) */
207 char *license = _("Gcalctool is free software; you can redistribute it and/or modify\n"
208 "it under the terms of the GNU General Public License as published by\n"
209 "the Free Software Foundation; either version 2 of the License, or\n"
210 "(at your option) any later version.\n"
211 "\n"
212 "Gcalctool is distributed in the hope that it will be useful,\n"
213 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
214 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
215 "GNU General Public License for more details.\n"
216 "\n"
217 "You should have received a copy of the GNU General Public License\n"
218 "along with Gcalctool; if not, write to the Free Software Foundation, Inc.,\n"
219 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA");
221 gtk_show_about_dialog(GTK_WINDOW(window),
222 "name",
223 /* Program name in the about dialog */
224 _("Gcalctool"),
225 "version", VERSION,
226 "copyright",
227 /* Copyright notice in the about dialog */
228 _("\xc2\xa9 1986–2010 The Gcalctool authors"),
229 "license", license,
230 "comments",
231 /* Short description in the about dialog */
232 _("Calculator with financial and scientific modes."),
233 "authors", authors,
234 "documenters", documenters,
235 "translator_credits", translator_credits,
236 "logo-icon-name", "accessories-calculator",
237 NULL);
241 static void
242 quit_cb(GtkWidget *widget, MathWindow *window)
244 g_signal_emit(window, signals[QUIT], 0);
248 static gboolean
249 key_press_cb(MathWindow *window, GdkEventKey *event)
251 gboolean result;
252 g_signal_emit_by_name(window->priv->display, "key-press-event", event, &result);
254 if (math_buttons_get_mode (window->priv->buttons) == PROGRAMMING && (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) {
255 switch(event->keyval)
257 /* Binary */
258 case GDK_KEY_b:
259 math_equation_set_base (window->priv->equation, 2);
260 return TRUE;
261 /* Octal */
262 case GDK_KEY_o:
263 math_equation_set_base (window->priv->equation, 8);
264 return TRUE;
265 /* Decimal */
266 case GDK_KEY_d:
267 math_equation_set_base (window->priv->equation, 10);
268 return TRUE;
269 /* Hexdecimal */
270 case GDK_KEY_h:
271 math_equation_set_base (window->priv->equation, 16);
272 return TRUE;
276 return result;
280 static void
281 delete_cb(MathWindow *window, GdkEvent *event)
283 g_signal_emit(window, signals[QUIT], 0);
287 static void
288 scroll_changed_cb(GtkAdjustment *adjustment, MathWindow *window)
290 if (window->priv->right_aligned)
291 gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
295 static void
296 scroll_value_changed_cb(GtkAdjustment *adjustment, MathWindow *window)
298 if (gtk_adjustment_get_value(adjustment) == gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment))
299 window->priv->right_aligned = TRUE;
300 else
301 window->priv->right_aligned = FALSE;
305 static void
306 button_mode_changed_cb(MathButtons *buttons, GParamSpec *spec, MathWindow *window)
308 GtkWidget *menu;
310 switch(math_buttons_get_mode(buttons))
312 default:
313 case BASIC:
314 menu = window->priv->mode_basic_menu_item;
315 //FIXME: Should it revert to decimal mode? math_equation_set_number_format(window->priv->equation, DEC);
316 break;
318 case ADVANCED:
319 menu = window->priv->mode_advanced_menu_item;
320 break;
322 case FINANCIAL:
323 menu = window->priv->mode_financial_menu_item;
324 break;
326 case PROGRAMMING:
327 menu = window->priv->mode_programming_menu_item;
328 break;
330 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
334 static GtkWidget *
335 add_menu(GtkWidget *menu_bar, const gchar *name)
337 GtkWidget *menu_item, *menu;
339 menu_item = gtk_menu_item_new_with_mnemonic(name);
340 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_item);
341 gtk_widget_show(menu_item);
342 menu = gtk_menu_new();
343 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
345 return menu;
349 static GtkWidget *
350 add_menu_item(GtkWidget *menu, GtkWidget *menu_item, GCallback callback, gpointer callback_data)
352 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
353 gtk_widget_show(menu_item);
354 if (callback)
355 g_signal_connect(G_OBJECT(menu_item), "activate", callback, callback_data);
356 return menu_item;
360 static GtkWidget *
361 radio_menu_item_new(GSList **group, const gchar *name)
363 GtkWidget *menu_item;
364 menu_item = gtk_radio_menu_item_new_with_mnemonic(*group, name);
365 *group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menu_item));
366 return menu_item;
370 static void
371 create_menu(MathWindow *window)
373 GtkAccelGroup *accel_group;
374 GtkWidget *menu, *menu_item;
375 GSList *group = NULL;
377 accel_group = gtk_accel_group_new();
378 gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
380 /* Calculator menu */
381 #define CALCULATOR_MENU_LABEL _("_Calculator")
382 /* Mode menu */
383 #define MODE_MENU_LABEL _("_Mode")
384 /* Help menu label */
385 #define HELP_MENU_LABEL _("_Help")
386 /* Basic menu label */
387 #define MODE_BASIC_LABEL _("_Basic")
388 /* Advanced menu label */
389 #define MODE_ADVANCED_LABEL _("_Advanced")
390 /* Financial menu label */
391 #define MODE_FINANCIAL_LABEL _("_Financial")
392 /* Programming menu label */
393 #define MODE_PROGRAMMING_LABEL _("_Programming")
394 /* Help>Contents menu label */
395 #define HELP_CONTENTS_LABEL _("_Contents")
397 menu = add_menu(window->priv->menu_bar, CALCULATOR_MENU_LABEL);
398 add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_COPY, accel_group), G_CALLBACK(copy_cb), window);
399 add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, accel_group), G_CALLBACK(paste_cb), window);
400 menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_UNDO, accel_group), G_CALLBACK(undo_cb), window);
401 gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_KEY_z, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
402 menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_REDO, accel_group), G_CALLBACK(redo_cb), window);
403 gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
404 add_menu_item(menu, gtk_separator_menu_item_new(), NULL, NULL);
405 add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, accel_group), G_CALLBACK(show_preferences_cb), window);
406 add_menu_item(menu, gtk_separator_menu_item_new(), NULL, NULL);
407 menu_item = add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group), G_CALLBACK(quit_cb), window);
408 gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_KEY_w, GDK_CONTROL_MASK, 0);
410 menu = add_menu(window->priv->menu_bar, MODE_MENU_LABEL);
411 window->priv->mode_basic_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_BASIC_LABEL), G_CALLBACK(mode_changed_cb), window);
412 g_object_set_data(G_OBJECT(window->priv->mode_basic_menu_item), "calcmode", GINT_TO_POINTER(BASIC));
413 window->priv->mode_advanced_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_ADVANCED_LABEL), G_CALLBACK(mode_changed_cb), window);
414 g_object_set_data(G_OBJECT(window->priv->mode_advanced_menu_item), "calcmode", GINT_TO_POINTER(ADVANCED));
415 window->priv->mode_financial_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_FINANCIAL_LABEL), G_CALLBACK(mode_changed_cb), window);
416 g_object_set_data(G_OBJECT(window->priv->mode_financial_menu_item), "calcmode", GINT_TO_POINTER(FINANCIAL));
417 window->priv->mode_programming_menu_item = add_menu_item(menu, radio_menu_item_new(&group, MODE_PROGRAMMING_LABEL), G_CALLBACK(mode_changed_cb), window);
418 g_object_set_data(G_OBJECT(window->priv->mode_programming_menu_item), "calcmode", GINT_TO_POINTER(PROGRAMMING));
420 menu = add_menu(window->priv->menu_bar, HELP_MENU_LABEL);
421 menu_item = add_menu_item(menu, gtk_menu_item_new_with_mnemonic(HELP_CONTENTS_LABEL), G_CALLBACK(help_cb), window);
422 gtk_widget_add_accelerator(menu_item, "activate", accel_group, GDK_KEY_F1, 0, GTK_ACCEL_VISIBLE);
423 add_menu_item(menu, gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, accel_group), G_CALLBACK(about_cb), window);
427 static void
428 create_gui(MathWindow *window)
430 GtkWidget *main_vbox, *vbox;
431 GtkWidget *scrolled_window;
433 main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
434 gtk_container_add(GTK_CONTAINER(window), main_vbox);
435 gtk_widget_show(main_vbox);
437 window->priv->menu_bar = gtk_menu_bar_new();
438 gtk_box_pack_start(GTK_BOX(main_vbox), window->priv->menu_bar, FALSE, FALSE, 0);
439 gtk_widget_show(window->priv->menu_bar);
441 create_menu(window);
443 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
444 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
445 gtk_box_pack_start(GTK_BOX(main_vbox), vbox, TRUE, TRUE, 0);
446 gtk_widget_show(vbox);
448 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
449 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
450 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolled_window), FALSE, FALSE, 0);
451 g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window)), "changed", G_CALLBACK(scroll_changed_cb), window);
452 g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolled_window)), "value-changed", G_CALLBACK(scroll_value_changed_cb), window);
453 window->priv->right_aligned = TRUE;
454 gtk_widget_show(scrolled_window);
456 window->priv->display = math_display_new_with_equation(window->priv->equation);
457 gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(window->priv->display));
458 gtk_widget_show(GTK_WIDGET(window->priv->display));
460 window->priv->buttons = math_buttons_new(window->priv->equation);
461 g_signal_connect(window->priv->buttons, "notify::mode", G_CALLBACK(button_mode_changed_cb), window);
462 button_mode_changed_cb(window->priv->buttons, NULL, window);
463 gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(window->priv->buttons), TRUE, TRUE, 0);
464 gtk_widget_show(GTK_WIDGET(window->priv->buttons));
468 static void
469 math_window_set_property(GObject *object,
470 guint prop_id,
471 const GValue *value,
472 GParamSpec *pspec)
474 MathWindow *self;
476 self = MATH_WINDOW(object);
478 switch (prop_id) {
479 case PROP_EQUATION:
480 self->priv->equation = g_value_get_object(value);
481 create_gui(self);
482 break;
483 default:
484 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
485 break;
490 static void
491 math_window_get_property(GObject *object,
492 guint prop_id,
493 GValue *value,
494 GParamSpec *pspec)
496 MathWindow *self;
498 self = MATH_WINDOW(object);
500 switch (prop_id) {
501 case PROP_EQUATION:
502 g_value_set_object(value, self->priv->equation);
503 break;
504 default:
505 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
506 break;
511 static void
512 math_window_class_init(MathWindowClass *klass)
514 GObjectClass *object_class = G_OBJECT_CLASS(klass);
516 object_class->get_property = math_window_get_property;
517 object_class->set_property = math_window_set_property;
519 g_type_class_add_private(klass, sizeof(MathWindowPrivate));
521 g_object_class_install_property(object_class,
522 PROP_EQUATION,
523 g_param_spec_object("equation",
524 "equation",
525 "Equation being calculated",
526 math_equation_get_type(),
527 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
529 signals[QUIT] =
530 g_signal_new("quit",
531 G_TYPE_FROM_CLASS (klass),
532 G_SIGNAL_RUN_LAST,
533 G_STRUCT_OFFSET (MathWindowClass, quit),
534 NULL, NULL,
535 g_cclosure_marshal_VOID__VOID,
536 G_TYPE_NONE, 0);
540 static void
541 math_window_init(MathWindow *window)
543 window->priv = G_TYPE_INSTANCE_GET_PRIVATE(window, math_window_get_type(), MathWindowPrivate);
544 gtk_window_set_title(GTK_WINDOW(window),
545 /* Title of main window */
546 _("Calculator"));
547 gtk_window_set_icon_name(GTK_WINDOW(window), "accessories-calculator");
548 gtk_window_set_role(GTK_WINDOW(window), "gcalctool");
549 gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
550 g_signal_connect_after(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_cb), NULL);
551 g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(delete_cb), NULL);