/********************************************************************\ * dialog-price-editor.c -- price editor dialog * * Copyright (C) 2001 Gnumatic, Inc. * * Author: Dave Peticolas * * Copyright (c) 2006 David Hampton * * Copyright (c) 2009 Herbert Thoma * * Copyright (c) 2011 Robert Fewell * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ #include #include #include #include #include "dialog-utils.h" #include "gnc-gtk-utils.h" #include "gnc-amount-edit.h" #include "gnc-commodity-edit.h" #include "dialog-commodity.h" #include "gnc-general-select.h" #include "gnc-component-manager.h" #include "gnc-currency-edit.h" #include "gnc-date-edit.h" #include "qof.h" #include "gnc-pricedb.h" #include "gnc-session.h" #include "gnc-ui.h" #include "gnc-ui-util.h" #include "gnc-warnings.h" #include "engine-helpers.h" #define DIALOG_PRICE_EDIT_CM_CLASS "dialog-price-edit" #define GNC_PREFS_GROUP "dialogs.price-editor" /* This static indicates the debugging module that this .o belongs to. */ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI; typedef struct { GtkWidget * dialog; QofSession *session; QofBook *book; GNCPriceDB *price_db; GNCPriceEditType type; GtkWidget * namespace_cbwe; GtkWidget * commodity_cbwe; GtkWidget * currency_edit; GtkWidget * date_edit; GtkWidget * source_entry; GtkWidget * type_combobox; GtkWidget * price_edit; GtkWidget * cancel_button; GtkWidget * apply_button; GtkWidget * ok_button; GNCPrice *price; gboolean changed; gboolean is_new; } PriceEditDialog; void pedit_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data); void pedit_data_changed_cb (GtkWidget *w, gpointer data); void pedit_commodity_ns_changed_cb (GtkComboBox *cbwe, gpointer data); void pedit_commodity_changed_cb (GtkComboBox *cbwe, gpointer data); static void gnc_prices_set_changed (PriceEditDialog *pedit_dialog, gboolean changed) { pedit_dialog->changed = changed; gtk_widget_set_sensitive (pedit_dialog->apply_button, changed); gtk_widget_set_sensitive (pedit_dialog->ok_button, changed); } static int type_string_to_index (const char *type) { if (g_strcmp0 (type, "bid") == 0) return 0; if (g_strcmp0 (type, "ask") == 0) return 1; if (g_strcmp0 (type, "last") == 0) return 2; if (g_strcmp0 (type, "nav") == 0) return 3; return 4; } static const char * type_index_to_string (int index) { switch (index) { case 0: return "bid"; case 1: return "ask"; case 2: return "last"; case 3: return "nav"; default: return "unknown"; } } static void price_to_gui (PriceEditDialog *pedit_dialog) { GNCPrintAmountInfo print_info; gnc_commodity *commodity = NULL; gnc_commodity *currency = NULL; const gchar *name_space, *fullname; const char *source; const char *type; gnc_numeric value; time64 date; if (pedit_dialog->price) { commodity = gnc_price_get_commodity (pedit_dialog->price); } if (commodity) { name_space = gnc_commodity_get_namespace(commodity); fullname = gnc_commodity_get_printname(commodity); gnc_ui_update_namespace_picker(pedit_dialog->namespace_cbwe, name_space, DIAG_COMM_ALL); gnc_ui_update_commodity_picker(pedit_dialog->commodity_cbwe, name_space, fullname); currency = gnc_price_get_currency (pedit_dialog->price); date = gnc_price_get_time64 (pedit_dialog->price); source = gnc_price_get_source_string (pedit_dialog->price); type = gnc_price_get_typestr (pedit_dialog->price); value = gnc_price_get_value (pedit_dialog->price); } else { currency = gnc_default_currency (); date = gnc_time (NULL); source = "user:price-editor"; //Sync with source_names in gnc-pricedb.c type = ""; value = gnc_numeric_zero (); } if (currency) { gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit), currency); } gnc_date_edit_set_time (GNC_DATE_EDIT (pedit_dialog->date_edit), date); gtk_entry_set_text (GTK_ENTRY (pedit_dialog->source_entry), source); gtk_combo_box_set_active (GTK_COMBO_BOX(pedit_dialog->type_combobox), type_string_to_index (type)); print_info = gnc_commodity_print_info (currency, FALSE); gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), print_info); gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), 0); gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), value); } static gboolean pedit_dialog_replace_found_price (PriceEditDialog *pedit_dialog, const gnc_commodity *commodity, const gnc_commodity *currency, time64 t) { gboolean price_found = FALSE; GNCPrice *test_price = gnc_pricedb_lookup_day_t64 (pedit_dialog->price_db, commodity, currency, t); if (test_price) { if (pedit_dialog->is_new) // new price price_found = TRUE; else // edit price { if (!gnc_price_equal (test_price, pedit_dialog->price)) price_found = TRUE; } gnc_price_unref (test_price); } if (price_found) { gint response; GtkWidget *dialog; gchar *message = _("Are you sure you want to replace the existing price?"); dialog = gtk_message_dialog_new (GTK_WINDOW (pedit_dialog->dialog), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", _("Replace price?")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog), "%s", message); gtk_dialog_add_buttons (GTK_DIALOG(dialog), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Replace"), GTK_RESPONSE_YES, (gchar *)NULL); gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES); response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_PRICE_QUOTES_REPLACE); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_CANCEL) return FALSE; } return TRUE; } static const char * gui_to_price (PriceEditDialog *pedit_dialog) { GNCPrintAmountInfo print_info; gnc_commodity *commodity; gnc_commodity *currency; gchar *name_space; const gchar *fullname; const char *source; const char *type; gnc_numeric value; time64 date; name_space = gnc_ui_namespace_picker_ns (pedit_dialog->namespace_cbwe); fullname = gtk_entry_get_text( GTK_ENTRY( gtk_bin_get_child( GTK_BIN( GTK_COMBO_BOX(pedit_dialog->commodity_cbwe))))); commodity = gnc_commodity_table_find_full(gnc_get_current_commodities(), name_space, fullname); if (!commodity) return _("You must select a Security."); currency = gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit)); if (!currency) return _("You must select a Currency."); date = gnc_date_edit_get_date (GNC_DATE_EDIT (pedit_dialog->date_edit)); source = gtk_entry_get_text (GTK_ENTRY (pedit_dialog->source_entry)); type = type_index_to_string (gtk_combo_box_get_active (GTK_COMBO_BOX (pedit_dialog->type_combobox))); print_info = gnc_commodity_print_info (currency, FALSE); gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), print_info); gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), 0); if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (pedit_dialog->price_edit), NULL)) return _("You must enter a valid amount."); value = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pedit_dialog->price_edit)); // test for existing price on same day if (pedit_dialog_replace_found_price (pedit_dialog, commodity, currency, date)) { if (!pedit_dialog->price) pedit_dialog->price = gnc_price_create (pedit_dialog->book); gnc_price_begin_edit (pedit_dialog->price); gnc_price_set_commodity (pedit_dialog->price, commodity); gnc_price_set_currency (pedit_dialog->price, currency); gnc_price_set_time64 (pedit_dialog->price, date); gnc_price_set_source_string (pedit_dialog->price, source); gnc_price_set_typestr (pedit_dialog->price, type); gnc_price_set_value (pedit_dialog->price, value); gnc_price_commit_edit (pedit_dialog->price); g_free (name_space); return NULL; } else { g_free (name_space); return "CANCEL"; } } static void pedit_dialog_destroy_cb (GtkWidget *widget, gpointer data) { PriceEditDialog *pedit_dialog = data; gnc_unregister_gui_component_by_data (DIALOG_PRICE_EDIT_CM_CLASS, pedit_dialog); if (pedit_dialog->price) { gnc_price_unref (pedit_dialog->price); pedit_dialog->price = NULL; pedit_dialog->is_new = FALSE; } g_free (pedit_dialog); } void pedit_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data) { PriceEditDialog *pedit_dialog = data; GNCPrice *new_price = NULL; const char *error_str; if ((response == GTK_RESPONSE_OK) || (response == GTK_RESPONSE_APPLY)) { error_str = gui_to_price (pedit_dialog); if (g_strcmp0 (error_str, "CANCEL") == 0) // cancel from replace price dialog { // set the ok and cancel buttons sensitivity gnc_prices_set_changed (pedit_dialog, FALSE); return; } else if (error_str) // error string from gui { gnc_warning_dialog (GTK_WINDOW (pedit_dialog->dialog), "%s", error_str); return; } // set the ok and cancel buttons sensitivity gnc_prices_set_changed (pedit_dialog, FALSE); if (pedit_dialog->is_new) gnc_pricedb_add_price (pedit_dialog->price_db, pedit_dialog->price); gnc_gui_refresh_all (); } if (response == GTK_RESPONSE_APPLY) { new_price = gnc_price_clone (pedit_dialog->price, pedit_dialog->book); pedit_dialog->is_new = TRUE; gnc_price_unref (pedit_dialog->price); pedit_dialog->price = new_price; } else { gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pedit_dialog->dialog)); gtk_widget_destroy (GTK_WIDGET (pedit_dialog->dialog)); pedit_dialog_destroy_cb (NULL, pedit_dialog); } } void pedit_commodity_ns_changed_cb (GtkComboBox *cbwe, gpointer data) { PriceEditDialog *pedit_dialog = data; gchar *name_space; gnc_prices_set_changed (pedit_dialog, TRUE); name_space = gnc_ui_namespace_picker_ns (pedit_dialog->namespace_cbwe); gnc_ui_update_commodity_picker (pedit_dialog->commodity_cbwe, name_space, NULL); g_free(name_space); } void pedit_commodity_changed_cb (GtkComboBox *cbwe, gpointer data) { gnc_commodity *commodity = NULL; gnc_commodity *currency = NULL; gchar *name_space; const gchar *fullname; GList *price_list; PriceEditDialog *pedit_dialog = data; gnc_prices_set_changed (pedit_dialog, TRUE); name_space = gnc_ui_namespace_picker_ns (pedit_dialog->namespace_cbwe); fullname = gtk_entry_get_text( GTK_ENTRY( gtk_bin_get_child( GTK_BIN( GTK_COMBO_BOX(pedit_dialog->commodity_cbwe))))); commodity = gnc_commodity_table_find_full(gnc_get_current_commodities(), name_space, fullname); if (commodity) { price_list = gnc_pricedb_lookup_latest_any_currency (pedit_dialog->price_db, commodity); if (price_list) { GNCPrice * price = (GNCPrice*)price_list->data; if (gnc_commodity_equiv(commodity, gnc_price_get_currency(price))) currency = gnc_price_get_commodity((GNCPrice *)price); else currency = gnc_price_get_currency((GNCPrice *)price); if (currency) gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit), currency); gnc_price_list_destroy(price_list); } else { gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit), gnc_default_currency()); } } g_free(name_space); } void pedit_data_changed_cb (GtkWidget *w, gpointer data) { PriceEditDialog *pedit_dialog = data; gnc_prices_set_changed (pedit_dialog, TRUE); } static void gnc_price_pedit_dialog_create (GtkWidget *parent, PriceEditDialog *pedit_dialog, QofSession *session) { GtkBuilder *builder; GNCPrintAmountInfo print_info; GtkWidget *dialog; GtkWidget *entry; GtkWidget *box; GtkWidget *w; GtkWidget *label; gchar *name_space; builder = gtk_builder_new(); gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore1"); gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore2"); gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore3"); gnc_builder_add_from_file (builder, "dialog-price.glade", "price_dialog"); pedit_dialog->session = session; pedit_dialog->book = qof_session_get_book(pedit_dialog->session); pedit_dialog->price_db = gnc_pricedb_get_db(pedit_dialog->book); dialog = GTK_WIDGET(gtk_builder_get_object (builder, "price_dialog")); pedit_dialog->dialog = dialog; /* parent */ if (parent != NULL) gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent)); w = GTK_WIDGET(gtk_builder_get_object (builder, "namespace_cbwe")); pedit_dialog->namespace_cbwe = w; gnc_ui_update_namespace_picker(w, NULL, DIAG_COMM_ALL); gnc_cbwe_require_list_item(GTK_COMBO_BOX(pedit_dialog->namespace_cbwe)); gtk_combo_box_set_active(GTK_COMBO_BOX(pedit_dialog->namespace_cbwe), 1); w = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_cbwe")); pedit_dialog->commodity_cbwe = w; gnc_cbwe_require_list_item(GTK_COMBO_BOX(pedit_dialog->commodity_cbwe)); name_space = gnc_ui_namespace_picker_ns(pedit_dialog->namespace_cbwe); gnc_ui_update_commodity_picker(pedit_dialog->commodity_cbwe, name_space, NULL); g_free(name_space); box = GTK_WIDGET(gtk_builder_get_object (builder, "currency_box")); w = gnc_currency_edit_new (); gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT (w), gnc_default_currency ()); pedit_dialog->currency_edit = w; gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0); gtk_widget_show (w); g_signal_connect (G_OBJECT (GTK_COMBO_BOX(w)), "changed", G_CALLBACK (pedit_data_changed_cb), pedit_dialog); label = GTK_WIDGET(gtk_builder_get_object (builder, "currency_label")); gtk_label_set_mnemonic_widget (GTK_LABEL(label), w); box = GTK_WIDGET(gtk_builder_get_object (builder, "date_box")); w = gnc_date_edit_new (time (NULL), FALSE, FALSE); pedit_dialog->date_edit = w; gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0); gtk_widget_show (w); g_signal_connect (G_OBJECT (w), "date_changed", G_CALLBACK (pedit_data_changed_cb), pedit_dialog); g_signal_connect (G_OBJECT (GNC_DATE_EDIT (w)->date_entry), "changed", G_CALLBACK (pedit_data_changed_cb), pedit_dialog); gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(w)->date_entry), TRUE); label = GTK_WIDGET(gtk_builder_get_object (builder, "date__label")); gnc_date_make_mnemonic_target (GNC_DATE_EDIT(w), label); w = GTK_WIDGET(gtk_builder_get_object (builder, "source_entry")); pedit_dialog->source_entry = w; w = GTK_WIDGET(gtk_builder_get_object (builder, "type_combobox")); pedit_dialog->type_combobox = w; box = GTK_WIDGET(gtk_builder_get_object (builder, "price_box")); w = gnc_amount_edit_new (); pedit_dialog->price_edit = w; gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0); entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (w)); gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (w), TRUE); print_info = gnc_default_price_print_info (gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT (pedit_dialog->currency_edit))); gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (w), print_info); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); gtk_widget_show (w); label = GTK_WIDGET(gtk_builder_get_object (builder, "price_label")); gnc_amount_edit_make_mnemonic_target (GNC_AMOUNT_EDIT(w), label); g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (pedit_data_changed_cb), pedit_dialog); w = GTK_WIDGET(gtk_builder_get_object (builder, "pd_cancel_button")); pedit_dialog->cancel_button = w; w = GTK_WIDGET(gtk_builder_get_object (builder, "pd_apply_button")); pedit_dialog->apply_button = w; w = GTK_WIDGET(gtk_builder_get_object (builder, "pd_ok_button")); pedit_dialog->ok_button = w; gnc_prices_set_changed (pedit_dialog, FALSE); gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pedit_dialog); g_object_unref(G_OBJECT(builder)); } static void close_handler (gpointer user_data) { PriceEditDialog *pedit_dialog = user_data; gtk_dialog_response(GTK_DIALOG(pedit_dialog->dialog), GTK_RESPONSE_CANCEL); } static void refresh_handler (GHashTable *changes, gpointer user_data) { // PriceEditDialog *pedit_dialog = user_data; // gnc_prices_load_prices (pedit_dialog); } static gboolean show_handler (const char *klass, gint component_id, gpointer user_data, gpointer iter_data) { PriceEditDialog *pedit_dialog = user_data; GNCPrice * price = iter_data; if (!pedit_dialog || (pedit_dialog->price != price)) return(FALSE); gtk_window_present (GTK_WINDOW(pedit_dialog->dialog)); return(TRUE); } /********************************************************************\ * gnc_price_edit_dialog * * opens up a window to edit price information * * * * Args: parent - the parent of the window to be created * * Return: nothing * \********************************************************************/ void gnc_price_edit_dialog (GtkWidget * parent, QofSession *session, GNCPrice * price, GNCPriceEditType type) { PriceEditDialog *pedit_dialog; gint component_id; if ((type == GNC_PRICE_EDIT) && (gnc_forall_gui_components (DIALOG_PRICE_EDIT_CM_CLASS, show_handler, price))) return; pedit_dialog = g_new0 (PriceEditDialog, 1); gnc_price_pedit_dialog_create (parent, pedit_dialog, session); gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pedit_dialog->dialog), GTK_WINDOW(parent)); pedit_dialog->type = type; switch (type) { case GNC_PRICE_NEW: if (price) { price = gnc_price_clone(price, pedit_dialog->book); gnc_price_set_source (price, PRICE_SOURCE_EDIT_DLG); gnc_price_set_time64 (price, gnc_time (NULL)); gnc_price_set_value (price, gnc_numeric_zero ()); } pedit_dialog->is_new = TRUE; /* New price will only have one ref, this dialog. */ break; case GNC_PRICE_EDIT: gnc_price_ref(price); /* Add ref from this dialog */ pedit_dialog->is_new = FALSE; break; } pedit_dialog->price = price; price_to_gui(pedit_dialog); gnc_prices_set_changed (pedit_dialog, FALSE); component_id = gnc_register_gui_component (DIALOG_PRICE_EDIT_CM_CLASS, refresh_handler, close_handler, pedit_dialog); gnc_gui_component_set_session (component_id, pedit_dialog->session); gtk_widget_grab_focus (pedit_dialog->commodity_cbwe); gtk_widget_show (pedit_dialog->dialog); } /********************************************************************\ * gnc_price_edit_by_guid * * opens up a window to edit price information * * * * Args: parent - the parent of the window to be created * * Return: nothing * \********************************************************************/ GNCPrice * gnc_price_edit_by_guid (GtkWidget * parent, const GncGUID * guid) { GNCPrice *price; QofSession *session = gnc_get_current_session(); QofBook* book = qof_session_get_book (session); if (!book) return (NULL); price = gnc_price_lookup (guid, book); if (price == NULL) return(NULL); gnc_price_edit_dialog(parent, session, price, GNC_PRICE_EDIT); return price; }