00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "config.h"
00025
00026 #include <gtk/gtk.h>
00027 #include <glib/gi18n.h>
00028 #include <locale.h>
00029 #include <time.h>
00030
00031 #include "dialog-fincalc.h"
00032 #include "dialog-utils.h"
00033 #include "finproto.h"
00034 #include "finvar.h"
00035 #include "gnc-amount-edit.h"
00036 #include "gnc-commodity.h"
00037 #include "gnc-component-manager.h"
00038 #include "gnc-date-edit.h"
00039 #include "gnc-engine.h"
00040 #include "gnc-gui-query.h"
00041
00042
00043 #define DIALOG_FINCALC_CM_CLASS "dialog-fincalc"
00044 #define GCONF_SECTION "dialogs/fincalc"
00045
00046 typedef enum
00047 {
00048 PAYMENT_PERIODS = 0,
00049 INTEREST_RATE,
00050 PRESENT_VALUE,
00051 PERIODIC_PAYMENT,
00052 FUTURE_VALUE,
00053 NUM_FIN_CALC_VALUES
00054 } FinCalcValue;
00055
00056
00058 struct _FinCalcDialog
00059 {
00060 GladeXML *xml;
00061 GtkWidget *dialog;
00062
00063 GtkWidget *amounts[NUM_FIN_CALC_VALUES];
00064
00065 GtkWidget *calc_button;
00066
00067 GtkWidget *compounding_combo;
00068 GtkWidget *payment_combo;
00069
00070 GtkWidget *end_of_period_radio;
00071 GtkWidget *discrete_compounding_radio;
00072
00073 GtkWidget *payment_total_label;
00074
00075 financial_info financial_info;
00076 };
00077
00078 static unsigned int periods[] =
00079 {
00080 1,
00081 2,
00082 3,
00083 4,
00084 6,
00085 12,
00086 24,
00087 26,
00088 52,
00089 360,
00090 365,
00091 };
00092
00093
00094 static QofLogModule log_module = GNC_MOD_GUI;
00095
00096
00098 void fincalc_update_calc_button_cb(GtkWidget *unused, FinCalcDialog *fcd);
00099 void fincalc_calc_clicked_cb(GtkButton *button, FinCalcDialog *fcd);
00100 void fincalc_compounding_radio_toggled(GtkToggleButton *togglebutton, gpointer data);
00101 void fincalc_amount_clear_clicked_cb(GtkButton *button, GNCAmountEdit *edit);
00102 void fincalc_response_cb (GtkDialog *dialog, gint response, FinCalcDialog *fcd);
00103
00106
00107
00108 static int
00109 normalize_period(unsigned int *period)
00110 {
00111 int i;
00112
00113 g_return_val_if_fail (period, 0);
00114
00115 for (i = (sizeof(periods) / sizeof(unsigned int)) - 1; i >= 0; i--)
00116 if (*period >= periods[i])
00117 {
00118 *period = periods[i];
00119 return i;
00120 }
00121
00122 *period = periods[0];
00123
00124 return 0;
00125 }
00126
00127
00128 static void
00129 fi_to_gui(FinCalcDialog *fcd)
00130 {
00131 const gnc_commodity *commodity;
00132 static char string[64];
00133 gnc_numeric total;
00134 gnc_numeric npp;
00135 gnc_numeric pmt;
00136 int i;
00137
00138 if (fcd == NULL)
00139 return;
00140
00141 npp = gnc_numeric_create (fcd->financial_info.npp, 1);
00142
00143 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]),
00144 npp);
00145 gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[INTEREST_RATE]),
00146 fcd->financial_info.ir);
00147 gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE]),
00148 fcd->financial_info.pv);
00149 gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT]),
00150 fcd->financial_info.pmt);
00151 gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE]),
00152 -fcd->financial_info.fv);
00153
00154 pmt = double_to_gnc_numeric (fcd->financial_info.pmt, 100000, GNC_RND_ROUND);
00155
00156 commodity = gnc_default_currency ();
00157
00158 total = gnc_numeric_mul (npp, pmt, gnc_commodity_get_fraction (commodity),
00159 GNC_RND_ROUND);
00160
00161 xaccSPrintAmount (string, total, gnc_default_print_info (FALSE));
00162 gtk_label_set_text (GTK_LABEL(fcd->payment_total_label), string);
00163
00164 i = normalize_period(&fcd->financial_info.CF);
00165 gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->compounding_combo), i);
00166
00167 i = normalize_period(&fcd->financial_info.PF);
00168 gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->payment_combo), i);
00169
00170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fcd->end_of_period_radio),
00171 !fcd->financial_info.bep);
00172
00173 gtk_toggle_button_set_active
00174 (GTK_TOGGLE_BUTTON(fcd->discrete_compounding_radio),
00175 fcd->financial_info.disc);
00176 }
00177
00178
00179 static void
00180 gui_to_fi(FinCalcDialog *fcd)
00181 {
00182 GtkToggleButton *toggle;
00183 gnc_numeric npp;
00184 int i;
00185
00186 if (fcd == NULL)
00187 return;
00188
00189 npp =
00190 gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]));
00191 fcd->financial_info.npp = npp.num;
00192
00193 fcd->financial_info.ir =
00194 gnc_amount_edit_get_damount(GNC_AMOUNT_EDIT(fcd->amounts[INTEREST_RATE]));
00195
00196 fcd->financial_info.pv =
00197 gnc_amount_edit_get_damount(GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE]));
00198
00199 fcd->financial_info.pmt =
00200 gnc_amount_edit_get_damount(GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT]));
00201
00202 fcd->financial_info.fv =
00203 gnc_amount_edit_get_damount(GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE]));
00204 fcd->financial_info.fv = -fcd->financial_info.fv;
00205
00206 i = gtk_combo_box_get_active(GTK_COMBO_BOX(fcd->compounding_combo));
00207 fcd->financial_info.CF = periods[i];
00208
00209 i = gtk_combo_box_get_active(GTK_COMBO_BOX(fcd->payment_combo));
00210 fcd->financial_info.PF = periods[i];
00211
00212 toggle = GTK_TOGGLE_BUTTON(fcd->end_of_period_radio);
00213 fcd->financial_info.bep = !gtk_toggle_button_get_active(toggle);
00214
00215 toggle = GTK_TOGGLE_BUTTON(fcd->discrete_compounding_radio);
00216 fcd->financial_info.disc = gtk_toggle_button_get_active(toggle);
00217
00218 fcd->financial_info.prec = gnc_locale_decimal_places ();
00219 }
00220
00221
00222 void
00223 fincalc_update_calc_button_cb(GtkWidget *unused, FinCalcDialog *fcd)
00224 {
00225 const gchar *text;
00226 gint i;
00227
00228 if (fcd == NULL)
00229 return;
00230
00231 for (i = 0; i < NUM_FIN_CALC_VALUES; i++) {
00232 text = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
00233 if ((text == NULL) || (*text == '\0')) {
00234 gtk_widget_set_sensitive(GTK_WIDGET(fcd->calc_button), TRUE);
00235 return;
00236 }
00237 }
00238
00239 gtk_widget_set_sensitive(GTK_WIDGET(fcd->calc_button), FALSE);
00240 }
00241
00242
00243 static void
00244 fincalc_dialog_destroy(GtkObject *object, gpointer data)
00245 {
00246 FinCalcDialog *fcd = data;
00247
00248 if (fcd == NULL)
00249 return;
00250
00251 gnc_unregister_gui_component_by_data (DIALOG_FINCALC_CM_CLASS, fcd);
00252
00253 g_object_unref(fcd->xml);
00254 g_free(fcd);
00255 }
00256
00257 void
00258 fincalc_compounding_radio_toggled(GtkToggleButton *togglebutton, gpointer data)
00259 {
00260 FinCalcDialog *fcd = data;
00261 gboolean sensitive;
00262
00263 if (fcd == NULL)
00264 return;
00265
00266 fincalc_update_calc_button_cb(GTK_WIDGET(togglebutton), fcd);
00267
00268 sensitive = gtk_toggle_button_get_active (togglebutton);
00269
00270 gtk_widget_set_sensitive (fcd->compounding_combo, sensitive);
00271 }
00272
00273 void
00274 fincalc_amount_clear_clicked_cb(GtkButton *button, GNCAmountEdit *edit)
00275 {
00276 gtk_entry_set_text(GTK_ENTRY (edit), "");
00277 }
00278
00279 static void
00280 init_fi(FinCalcDialog *fcd)
00281 {
00282 struct lconv *lc;
00283
00284 if (fcd == NULL)
00285 return;
00286
00287 lc = gnc_localeconv();
00288
00289 fcd->financial_info.npp = 12;
00290 fcd->financial_info.ir = 8.5;
00291 fcd->financial_info.pv = 15000.0;
00292 fcd->financial_info.pmt = -400.0;
00293 fcd->financial_info.CF = 12;
00294 fcd->financial_info.PF = 12;
00295 fcd->financial_info.bep = FALSE;
00296 fcd->financial_info.disc = TRUE;
00297 fcd->financial_info.prec = lc->frac_digits;
00298
00299 fi_calc_future_value(&fcd->financial_info);
00300 }
00301
00302
00303
00304
00305 static const char *
00306 can_calc_value(FinCalcDialog *fcd, FinCalcValue value, int *error_item)
00307 {
00308 const char *missing = _("This program can only calculate one value at a time. "
00309 "You must enter values for all but one quantity.");
00310 const char *bad_exp = _("GnuCash cannot determine the value in one of the fields. "
00311 "You must enter a valid expression.");
00312 const char *string;
00313 gnc_numeric nvalue;
00314 unsigned int i;
00315
00316 if (fcd == NULL)
00317 return NULL;
00318
00319
00320 for (i = 0; i < NUM_FIN_CALC_VALUES; i++)
00321 if (i != value)
00322 {
00323 string = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
00324 if ((string == NULL) || (*string == '\0'))
00325 {
00326 *error_item = i;
00327 return missing;
00328 }
00329
00330 if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (fcd->amounts[i])))
00331 {
00332 *error_item = i;
00333 return bad_exp;
00334 }
00335 }
00336
00337
00338 switch (value)
00339 {
00340 case PAYMENT_PERIODS:
00341 case PRESENT_VALUE:
00342 case PERIODIC_PAYMENT:
00343 case FUTURE_VALUE:
00344 nvalue = gnc_amount_edit_get_amount
00345 (GNC_AMOUNT_EDIT (fcd->amounts[INTEREST_RATE]));
00346 if (gnc_numeric_zero_p (nvalue))
00347 {
00348 *error_item = INTEREST_RATE;
00349 return _("The interest rate cannot be zero.");
00350 }
00351 break;
00352 default:
00353 break;
00354 }
00355
00356
00357 switch (value)
00358 {
00359 case INTEREST_RATE:
00360 case PRESENT_VALUE:
00361 case PERIODIC_PAYMENT:
00362 case FUTURE_VALUE:
00363 nvalue = gnc_amount_edit_get_amount
00364 (GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]));
00365 if (gnc_numeric_zero_p (nvalue))
00366 {
00367 *error_item = PAYMENT_PERIODS;
00368 return _("The number of payments cannot be zero.");
00369 }
00370 if (gnc_numeric_negative_p (nvalue))
00371 {
00372 *error_item = PAYMENT_PERIODS;
00373 return _("The number of payments cannot be negative.");
00374 }
00375 break;
00376 default:
00377 break;
00378 }
00379
00380 return NULL;
00381 }
00382
00383 static void
00384 calc_value(FinCalcDialog *fcd, FinCalcValue value)
00385 {
00386 const char *string;
00387 int error_item = 0;
00388
00389 if (fcd == NULL)
00390 return;
00391
00392 string = can_calc_value(fcd, value, &error_item);
00393 if (string != NULL)
00394 {
00395 GtkWidget *entry;
00396
00397 gnc_error_dialog(fcd->dialog, "%s", string);
00398 if (error_item == 0)
00399 entry = fcd->amounts[0];
00400 else
00401 entry = fcd->amounts[error_item];
00402 gtk_widget_grab_focus (entry);
00403 return;
00404 }
00405
00406 gui_to_fi(fcd);
00407
00408 switch (value)
00409 {
00410 case PAYMENT_PERIODS:
00411 fi_calc_num_payments(&fcd->financial_info);
00412 break;
00413 case INTEREST_RATE:
00414 fi_calc_interest(&fcd->financial_info);
00415 break;
00416 case PRESENT_VALUE:
00417 fi_calc_present_value(&fcd->financial_info);
00418 break;
00419 case PERIODIC_PAYMENT:
00420 fi_calc_payment(&fcd->financial_info);
00421 break;
00422 case FUTURE_VALUE:
00423 fi_calc_future_value(&fcd->financial_info);
00424 break;
00425 default:
00426 PERR("Unknown financial variable");
00427 break;
00428 }
00429
00430 fi_to_gui(fcd);
00431
00432 gtk_widget_set_sensitive(GTK_WIDGET(fcd->calc_button), FALSE);
00433 }
00434
00435 void
00436 fincalc_calc_clicked_cb(GtkButton *button, FinCalcDialog *fcd)
00437 {
00438 const gchar *text;
00439 gint i;
00440
00441 for (i = 0; i < NUM_FIN_CALC_VALUES; i++) {
00442 text = gtk_entry_get_text(GTK_ENTRY(fcd->amounts[i]));
00443 if ((text != NULL) && (*text != '\0'))
00444 continue;
00445 calc_value(fcd, i);
00446 return;
00447 }
00448 }
00449
00450 void fincalc_response_cb (GtkDialog *dialog,
00451 gint response,
00452 FinCalcDialog *fcd)
00453 {
00454 switch (response) {
00455 case GTK_RESPONSE_OK:
00456
00457
00458
00459 case GTK_RESPONSE_CLOSE:
00460 gnc_save_window_size(GCONF_SECTION, GTK_WINDOW(dialog));
00461 break;
00462
00463 default:
00464
00465 break;
00466 }
00467
00468 gnc_close_gui_component_by_data (DIALOG_FINCALC_CM_CLASS, fcd);
00469 }
00470
00471
00472 static void
00473 close_handler (gpointer user_data)
00474 {
00475 FinCalcDialog *fcd = user_data;
00476
00477 gtk_widget_destroy (fcd->dialog);
00478 }
00479
00480 static gboolean
00481 show_handler (const char *class, gint component_id,
00482 gpointer user_data, gpointer iter_data)
00483 {
00484 FinCalcDialog *fcd = user_data;
00485
00486 if (!fcd)
00487 return(FALSE);
00488 gtk_window_present (GTK_WINDOW(fcd->dialog));
00489 return(TRUE);
00490 }
00491
00492
00505 static void
00506 fincalc_init_gae (GNCAmountEdit *edit,
00507 gint min_places,
00508 gint max_places,
00509 gint fraction)
00510 {
00511 GNCPrintAmountInfo print_info;
00512
00513 print_info = gnc_integral_print_info ();
00514 print_info.min_decimal_places = min_places;
00515 print_info.max_decimal_places = max_places;
00516
00517 gnc_amount_edit_set_print_info (edit, print_info);
00518 gnc_amount_edit_set_fraction (edit, fraction);
00519 gnc_amount_edit_set_evaluate_on_enter (edit, TRUE);
00520 gtk_entry_set_alignment (GTK_ENTRY(edit), 1.0);
00521 }
00522
00528 static void
00529 fincalc_init_commodity_gae (GNCAmountEdit *edit)
00530 {
00531 GNCPrintAmountInfo print_info;
00532 gnc_commodity *commodity;
00533 gint fraction;
00534
00535 commodity = gnc_default_currency();
00536 fraction = gnc_commodity_get_fraction(commodity);
00537 print_info = gnc_commodity_print_info (commodity, FALSE);
00538
00539 gnc_amount_edit_set_print_info (edit, print_info);
00540 gnc_amount_edit_set_fraction (edit, fraction);
00541 gnc_amount_edit_set_evaluate_on_enter (edit, TRUE);
00542 gtk_entry_set_alignment (GTK_ENTRY(edit), 1.0);
00543 }
00544
00545 void
00546 gnc_ui_fincalc_dialog_create(void)
00547 {
00548 FinCalcDialog *fcd;
00549 GtkWidget *button;
00550 GtkWidget *combo;
00551 GtkWidget *edit;
00552 GladeXML *xml;
00553
00554 if (gnc_forall_gui_components (DIALOG_FINCALC_CM_CLASS,
00555 show_handler, NULL))
00556 return;
00557
00558
00559 fcd = g_new0(FinCalcDialog, 1);
00560
00561 xml = gnc_glade_xml_new ("fincalc.glade", "Financial Calculator Dialog");
00562
00563 fcd->xml = xml;
00564 fcd->dialog = glade_xml_get_widget (xml, "Financial Calculator Dialog");
00565
00566 gnc_register_gui_component (DIALOG_FINCALC_CM_CLASS,
00567 NULL, close_handler, fcd);
00568
00569 g_signal_connect (G_OBJECT (fcd->dialog), "destroy",
00570 G_CALLBACK (fincalc_dialog_destroy), fcd);
00571
00572
00573 edit = glade_xml_get_widget (xml, "payment_periods_edit");
00574 fincalc_init_gae (GNC_AMOUNT_EDIT (edit), 0, 0, 1);
00575 fcd->amounts[PAYMENT_PERIODS] = edit;
00576
00577 edit = glade_xml_get_widget (xml, "interest_rate_edit");
00578 fincalc_init_gae (GNC_AMOUNT_EDIT (edit), 2, 5, 100000);
00579 fcd->amounts[INTEREST_RATE] = edit;
00580
00581 edit = glade_xml_get_widget (xml, "present_value_edit");
00582 fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
00583 fcd->amounts[PRESENT_VALUE] = edit;
00584
00585 edit = glade_xml_get_widget (xml, "period_payment_edit");
00586 fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
00587 fcd->amounts[PERIODIC_PAYMENT] = edit;
00588
00589 edit = glade_xml_get_widget (xml, "future_value_edit");
00590 fincalc_init_commodity_gae (GNC_AMOUNT_EDIT (edit));
00591 fcd->amounts[FUTURE_VALUE] = edit;
00592
00593
00594 fcd->calc_button = glade_xml_get_widget (xml, "calc_button");
00595
00596
00597 combo = glade_xml_get_widget (xml, "compounding_combo");
00598 fcd->compounding_combo = combo;
00599 g_signal_connect(fcd->compounding_combo, "changed",
00600 G_CALLBACK (fincalc_update_calc_button_cb), fcd);
00601
00602 combo = glade_xml_get_widget (xml, "payment_combo");
00603 fcd->payment_combo = combo;
00604 g_signal_connect(fcd->compounding_combo, "changed",
00605 G_CALLBACK (fincalc_update_calc_button_cb), fcd);
00606
00607 button = glade_xml_get_widget (xml, "period_payment_radio");
00608 fcd->end_of_period_radio = button;
00609
00610 button = glade_xml_get_widget (xml, "discrete_compounding_radio");
00611 fcd->discrete_compounding_radio = button;
00612
00613 fcd->payment_total_label = glade_xml_get_widget (xml, "payment_total_label");
00614
00615 button = glade_xml_get_widget (xml, "schedule_button");
00616 gtk_widget_hide (button);
00617
00618 init_fi(fcd);
00619
00620 fi_to_gui(fcd);
00621
00622 gtk_widget_grab_focus(fcd->amounts[PAYMENT_PERIODS]);
00623
00624
00625 glade_xml_signal_autoconnect_full( xml,
00626 gnc_glade_autoconnect_full_func,
00627 fcd);
00628
00629 gnc_restore_window_size(GCONF_SECTION, GTK_WINDOW(fcd->dialog));
00630 gtk_widget_show(fcd->dialog);
00631 }
00632
00633 void
00634 gnc_ui_fincalc_dialog_destroy(FinCalcDialog *fcd)
00635 {
00636 if (fcd == NULL)
00637 return;
00638
00639 gnc_close_gui_component_by_data (DIALOG_FINCALC_CM_CLASS, fcd);
00640 }