1/* 2 * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. 3 * Copyright (C) 2008-2012 Robert Ancell 4 * 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 3 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. 10 */ 11 12public class Calculator : Gtk.Application 13{ 14 private Settings settings; 15 private MathWindow last_opened_window; 16 int n_math_windows = 0; 17 private MathPreferencesDialog preferences_dialog; 18 private Gtk.ShortcutsWindow shortcuts_window; 19 private static string program_name = null; 20 private static string equation_string = null; 21 private static string mode_string = null; 22 23 private const OptionEntry[] option_entries = { 24 /* Translators: Do not translate possible mode names basic, advanced, financial, programming and keyboard */ 25 { "mode", 'm', 0, OptionArg.STRING, ref mode_string, N_("Start in given mode (basic, advanced, financial, programming, keyboard)"), "mode" }, 26 { "solve", 's', 0, OptionArg.STRING, null, N_("Solve given equation"), "equation" }, 27 { "equation", 'e', 0, OptionArg.STRING, ref equation_string, N_("Start with given equation"), "equation"}, 28 { "version", 'v', 0, OptionArg.NONE, null, N_("Show release version"), null }, 29 { null } 30 }; 31 32 private const ActionEntry[] app_entries = 33 { 34 { "new-window", new_window_cb, null, null, null }, 35 { "preferences", show_preferences_cb, null, null, null }, 36 { "shortcuts", keyboard_shortcuts_cb, null, null, null }, 37 { "help", help_cb, null, null, null }, 38 { "about", about_cb, null, null, null }, 39 { "quit", quit_cb, null, null, null }, 40 }; 41 42 public Calculator () 43 { 44 Object (flags : ApplicationFlags.NON_UNIQUE, application_id : "org.gnome.Calculator"); 45 46 add_main_option_entries (option_entries); 47 } 48 49 private MathWindow create_new_window (Settings settings) 50 { 51 var accuracy = settings.get_int ("accuracy"); 52 var word_size = settings.get_int ("word-size"); 53 var number_base = settings.get_int ("base"); 54 var show_tsep = settings.get_boolean ("show-thousands"); 55 var show_zeroes = settings.get_boolean ("show-zeroes"); 56 var number_format = (DisplayFormat) settings.get_enum ("number-format"); 57 var angle_units = (AngleUnit) settings.get_enum ("angle-units"); 58 var button_mode = (ButtonMode) settings.get_enum ("button-mode"); 59 var source_currency = settings.get_string ("source-currency"); 60 var target_currency = settings.get_string ("target-currency"); 61 var source_units = settings.get_string ("source-units"); 62 var target_units = settings.get_string ("target-units"); 63 var precision = settings.get_int ("precision"); 64 65 var equation = new MathEquation (); 66 equation.accuracy = accuracy; 67 equation.word_size = word_size; 68 equation.show_thousands_separators = show_tsep; 69 equation.show_trailing_zeroes = show_zeroes; 70 equation.number_format = number_format; 71 equation.angle_units = angle_units; 72 equation.source_currency = source_currency; 73 equation.target_currency = target_currency; 74 equation.source_units = source_units; 75 equation.target_units = target_units; 76 Number.precision = precision; 77 78 add_action_entries (app_entries, this); 79 80 var current_window = new MathWindow (this, equation); 81 current_window.set_title (_("Calculator")); 82 // when closing the last window save its position to the settings 83 current_window.delete_event.connect((sender, event) => { 84 if (n_math_windows == 1) { 85 save_window_position ((sender as MathWindow)); 86 } 87 n_math_windows -= 1; 88 return false; 89 }); 90 n_math_windows += 1; 91 92 var buttons = current_window.buttons; 93 buttons.programming_base = number_base; 94 buttons.mode = button_mode; // FIXME: We load the basic buttons even if we immediately switch to the next type 95 96 set_accels_for_action ("win.mode::basic", {"<control><alt>B"}); 97 set_accels_for_action ("win.mode::advanced", {"<control><alt>A"}); 98 set_accels_for_action ("win.mode::financial", {"<control><alt>F"}); 99 set_accels_for_action ("win.mode::programming", {"<control><alt>P"}); 100 set_accels_for_action ("win.mode::keyboard", {"<control><alt>K", "<control><alt>T"}); 101 set_accels_for_action ("win.copy", {"<control>C"}); 102 set_accels_for_action ("win.paste", {"<control>V"}); 103 set_accels_for_action ("win.undo", {"<control>Z"}); 104 set_accels_for_action ("win.close", {"<control>W"}); 105 set_accels_for_action ("win.redo", {"<control><shift>Z"}); 106 set_accels_for_action ("win.clear", {"<Primary>Escape"}); 107 108 set_accels_for_action ("app.quit", {"<control>Q"}); 109 set_accels_for_action ("app.new-window", {"<control>N"}); 110 set_accels_for_action ("app.help", {"F1"}); 111 set_accels_for_action ("app.shortcuts", {"<control>question"}); 112 return current_window; 113 } 114 115 protected override void startup () 116 { 117 base.startup (); 118 119 Hdy.init (); 120 121 settings = new Settings ("org.gnome.calculator"); 122 settings.delay (); 123 last_opened_window = create_new_window (settings); 124 // restore the first window position from the settings 125 load_window_position (last_opened_window); 126 CurrencyManager.get_default ().refresh_interval = settings.get_int ("refresh-interval"); 127 CurrencyManager.get_default ().refresh_async (); 128 129 settings.changed["refresh-interval"].connect(() => { 130 CurrencyManager.get_default ().refresh_interval = settings.get_int ("refresh-interval"); 131 CurrencyManager.get_default ().refresh_async (); 132 }); 133 } 134 135 private MathWindow get_active_math_window () 136 { 137 return (MathWindow) get_active_window (); 138 } 139 140 protected override void activate () 141 { 142 base.activate (); 143 144 last_opened_window.present (); 145 if (equation_string != "" && equation_string != null) 146 { 147 var equations = (equation_string.compress ()).split ("\n",0); 148 for (var i = 0; i < equations.length; i++) 149 { 150 if ((equations [i].strip ()).length > 0) 151 last_opened_window.equation.set (equations [i]); 152 else 153 last_opened_window.equation.solve (); 154 } 155 } 156 if (mode_string != "" && mode_string != null) 157 { 158 var mode = ButtonMode.BASIC; 159 160 switch (mode_string) 161 { 162 case "basic": 163 mode = ButtonMode.BASIC; 164 break; 165 case "advanced": 166 mode = ButtonMode.ADVANCED; 167 break; 168 case "financial": 169 mode = ButtonMode.FINANCIAL; 170 break; 171 case "programming": 172 mode = ButtonMode.PROGRAMMING; 173 break; 174 case "keyboard": 175 mode = ButtonMode.KEYBOARD; 176 break; 177 } 178 last_opened_window.buttons.mode = mode; 179 } 180 } 181 182 protected override void shutdown () 183 { 184 base.shutdown (); 185 186 var window = last_opened_window; 187 var equation = window.equation; 188 var buttons = window.buttons; 189 190 settings.set_enum ("button-mode", buttons.mode); 191 settings.set_int ("accuracy", equation.accuracy); 192 settings.set_int ("word-size", equation.word_size); 193 settings.set_boolean ("show-thousands", equation.show_thousands_separators); 194 settings.set_boolean ("show-zeroes", equation.show_trailing_zeroes); 195 settings.set_enum ("number-format", equation.number_format); 196 settings.set_enum ("angle-units", equation.angle_units); 197 settings.set_string ("source-currency", equation.source_currency); 198 settings.set_string ("target-currency", equation.target_currency); 199 settings.set_string ("source-units", equation.source_units); 200 settings.set_string ("target-units", equation.target_units); 201 settings.set_int ("base", buttons.programming_base); 202 settings.apply (); 203 } 204 205 protected override int handle_local_options (GLib.VariantDict options) 206 { 207 if (options.contains ("version")) 208 { 209 /* NOTE: Is not translated so can be easily parsed */ 210 stderr.printf ("%1$s %2$s\n", program_name, VERSION); 211 return Posix.EXIT_SUCCESS; 212 } 213 214 if (options.contains ("solve")) 215 { 216 var solve_equation = (string) options.lookup_value ("solve", VariantType.STRING); 217 var tsep_string = Posix.nl_langinfo (Posix.NLItem.THOUSEP); 218 if (tsep_string == null || tsep_string == "") 219 tsep_string = " "; 220 221 var decimal = Posix.nl_langinfo (Posix.NLItem.RADIXCHAR); 222 if (decimal == null) 223 decimal = ""; 224 225 settings = new Settings ("org.gnome.calculator"); 226 var angle_units = (AngleUnit) settings.get_enum ("angle-units"); 227 var e = new ConvertEquation (solve_equation.replace (tsep_string, "").replace (decimal, ".")); 228 e.base = 10; 229 e.wordlen = 32; 230 e.angle_units = angle_units; 231 232 ErrorCode error; 233 string? error_token = null; 234 uint representation_base; 235 var result = e.parse (out representation_base, out error, out error_token); 236 237 // if unknown conversion, try force reloading conversion rates and retry conversion 238 if (error == ErrorCode.UNKNOWN_CONVERSION) { 239 CurrencyManager.get_default ().refresh_interval = settings.get_int ("refresh-interval"); 240 CurrencyManager.get_default ().refresh_sync (); 241 result = e.parse (out representation_base, out error, out error_token); 242 } 243 if (result != null) 244 { 245 var serializer = new Serializer (DisplayFormat.AUTOMATIC, 10, 9); 246 serializer.set_representation_base (representation_base); 247 var eq_result = serializer.to_string (result); 248 if (serializer.error != null) { 249 stderr.printf (serializer.error); 250 return Posix.EXIT_FAILURE; 251 } 252 253 stdout.printf ("%s\n", eq_result); 254 return Posix.EXIT_SUCCESS; 255 } 256 else if (error == ErrorCode.MP) 257 { 258 stderr.printf ("Error: %s\n", (Number.error != null) ? Number.error : error_token); 259 return Posix.EXIT_FAILURE; 260 } 261 else 262 { 263 stderr.printf ("Error: %s\n", mp_error_code_to_string (error)); 264 return Posix.EXIT_FAILURE; 265 } 266 } 267 268 return -1; 269 } 270 271 private void show_preferences_cb () 272 { 273 if (preferences_dialog == null) 274 { 275 preferences_dialog = new MathPreferencesDialog (get_active_math_window ().equation); 276 preferences_dialog.set_transient_for (get_active_window ()); 277 } 278 preferences_dialog.present (); 279 } 280 281 private void keyboard_shortcuts_cb () 282 { 283 if (shortcuts_window == null) 284 { 285 var builder = new Gtk.Builder (); 286 try 287 { 288 builder.add_from_resource ("/org/gnome/calculator/math-shortcuts.ui"); 289 } 290 catch (Error e) 291 { 292 error ("Error loading shortcuts window UI: %s", e.message); 293 } 294 295 shortcuts_window = builder.get_object ("shortcuts-calculator") as Gtk.ShortcutsWindow; 296 shortcuts_window.destroy.connect ( (event) => { shortcuts_window = null; }); 297 } 298 299 if (get_active_window () != shortcuts_window.get_transient_for ()) 300 shortcuts_window.set_transient_for (get_active_window ()); 301 shortcuts_window.show_all (); 302 shortcuts_window.present (); 303 } 304 305 private void help_cb () 306 { 307 try 308 { 309 Gtk.show_uri_on_window (get_active_window (), "help:gnome-calculator", Gtk.get_current_event_time ()); 310 } 311 catch (Error e) 312 { 313 /* Translators: Error message displayed when unable to launch help browser */ 314 var message = _("Unable to open help file"); 315 316 var d = new Gtk.MessageDialog (get_active_window (), 317 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, 318 Gtk.MessageType.ERROR, 319 Gtk.ButtonsType.CLOSE, 320 "%s", message); 321 d.format_secondary_text ("%s", e.message); 322 d.run (); 323 d.destroy (); 324 } 325 } 326 327 private void about_cb () 328 { 329 string[] authors = 330 { 331 "Robert Roth <robert.roth.off@gmail.com>", 332 "Robert Ancell", 333 "Klaus Niederkrüger", 334 "Robin Sonefors", 335 "Rich Burridge", 336 null 337 }; 338 string[] documenters = 339 { 340 "Sun Microsystems", 341 null 342 }; 343 344 /* The translator credits. Please translate this with your name (s). */ 345 var translator_credits = _("translator-credits"); 346 347 Gtk.show_about_dialog (get_active_window (), 348 "program-name", 349 /* Program name in the about dialog */ 350 _("Calculator"), 351 "title", _("About Calculator"), 352 "version", VERSION, 353 "website", "https://wiki.gnome.org/Apps/Calculator", 354 "copyright", 355 "\xc2\xa9 1986–2016 The Calculator authors", 356 /* We link to MPFR and MPC which are LGPLv3+, so Calculator cannot be conveyed as GPLv2+ */ 357 "license-type", Gtk.License.GPL_3_0, 358 "comments", 359 /* Short description in the about dialog */ 360 _("Calculator with financial and scientific modes."), 361 "authors", authors, 362 "documenters", documenters, 363 "translator_credits", translator_credits, 364 "logo-icon-name", "org.gnome.Calculator"); 365 } 366 367 private void quit_cb () 368 { 369 save_window_position (get_active_math_window ()); 370 371 if (get_windows ().length () > 1) 372 { 373 var dialog = new Gtk.MessageDialog.with_markup (get_active_math_window (), Gtk.DialogFlags.MODAL, 374 Gtk.MessageType.QUESTION, Gtk.ButtonsType.CANCEL, 375 _("Are you sure you want to close all open windows?")); 376 dialog.add_buttons (_("Close _All"), Gtk.ResponseType.CLOSE); 377 378 int result = dialog.run (); 379 if (result == Gtk.ResponseType.CLOSE) 380 this.quit (); 381 dialog.destroy (); 382 } else { 383 this.quit (); 384 } 385 } 386 387 private void new_window_cb () 388 { 389 var window = create_new_window (settings); 390 window.show (); 391 } 392 393 /** 394 * Load `window-position` from the settings and move the window to that 395 * position 396 */ 397 private void load_window_position (MathWindow window) { 398 int32 x, y; 399 settings.get("window-position", "(ii)", out x, out y); 400 // (-1, -1) is the default value 401 if (x != -1 && y != -1) { 402 window.move (x, y); 403 } 404 } 405 406 /** 407 * Save window position to the settings 408 */ 409 private void save_window_position (MathWindow window) { 410 int32 x, y; 411 window.get_position (out x, out y); 412 settings.set_value("window-position", new Variant("(ii)", x, y)); 413 } 414 415 public static int main (string[] args) 416 { 417 Intl.setlocale (LocaleCategory.ALL, ""); 418 Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); 419 Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); 420 Intl.textdomain (GETTEXT_PACKAGE); 421 422 /* Seed random number generator. */ 423 var now = new DateTime.now_utc (); 424 Random.set_seed (now.get_microsecond ()); 425 426 program_name = Path.get_basename (args [0]); 427 428 Gtk.Window.set_default_icon_name ("org.gnome.Calculator-symbolic"); 429 430 var app = new Calculator (); 431 432 return app.run (args); 433 } 434} 435