|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * dialog-price-editor.c -- price selector dialog * 00003 * Copyright (C) 2001 Gnumatic, Inc. * 00004 * Author: Dave Peticolas <dave@krondo.com> * 00005 * Copyright (C) 2003,2005 David Hampton * 00006 * Copyright (C) 2011 Robert Fewell * 00007 * * 00008 * This program is free software; you can redistribute it and/or * 00009 * modify it under the terms of the GNU General Public License as * 00010 * published by the Free Software Foundation; either version 2 of * 00011 * the License, or (at your option) any later version. * 00012 * * 00013 * This program is distributed in the hope that it will be useful, * 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00016 * GNU General Public License for more details. * 00017 * * 00018 * You should have received a copy of the GNU General Public License* 00019 * along with this program; if not, contact: * 00020 * * 00021 * Free Software Foundation Voice: +1-617-542-5942 * 00022 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00023 * Boston, MA 02110-1301, USA gnu@gnu.org * 00024 \********************************************************************/ 00025 00026 #include "config.h" 00027 00028 #include <gtk/gtk.h> 00029 #include <glib/gi18n.h> 00030 #include <libguile.h> 00031 #include "guile-mappings.h" 00032 #include <time.h> 00033 00034 #include "dialog-utils.h" 00035 #include "gnc-amount-edit.h" 00036 #include "gnc-commodity-edit.h" 00037 #include "gnc-general-select.h" 00038 #include "gnc-component-manager.h" 00039 #include "gnc-currency-edit.h" 00040 #include "gnc-date-edit.h" 00041 #include "gnc-engine.h" 00042 #include "gnc-gui-query.h" 00043 #include "gnc-pricedb.h" 00044 #include "gnc-session.h" 00045 #include "gnc-tree-view-price.h" 00046 #include "gnc-ui.h" 00047 #include "gnc-ui-util.h" 00048 #include "guile-util.h" 00049 #include "engine-helpers.h" 00050 #include "swig-runtime.h" 00051 00052 00053 #define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db" 00054 #define GCONF_SECTION "dialogs/edit_prices" 00055 00056 /* This static indicates the debugging module that this .o belongs to. */ 00057 static QofLogModule log_module = GNC_MOD_GUI; 00058 00059 00060 void gnc_prices_dialog_window_destroy_cb (GtkObject *object, gpointer data); 00061 void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data); 00062 void gnc_prices_dialog_response (GtkDialog *dialog, gint response_id, gpointer data); 00063 void gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data); 00064 void gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data); 00065 void gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data); 00066 void gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data); 00067 void gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data); 00068 00069 00070 typedef struct 00071 { 00072 GtkWidget * dialog; 00073 QofSession *session; 00074 QofBook *book; 00075 GNCPriceDB *price_db; 00076 00077 GncTreeViewPrice * price_tree; 00078 00079 GtkWidget * edit_button; 00080 GtkWidget * remove_button; 00081 } PricesDialog; 00082 00083 00084 void 00085 gnc_prices_dialog_window_destroy_cb (GtkObject *object, gpointer data) 00086 { 00087 PricesDialog *pdb_dialog = data; 00088 00089 ENTER(" "); 00090 gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog); 00091 00092 if (pdb_dialog->dialog) 00093 { 00094 gtk_widget_destroy(pdb_dialog->dialog); 00095 pdb_dialog->dialog = NULL; 00096 } 00097 00098 g_free (pdb_dialog); 00099 LEAVE(" "); 00100 } 00101 00102 00103 void 00104 gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data) 00105 { 00106 PricesDialog *pdb_dialog = data; 00107 00108 ENTER(" "); 00109 gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog); 00110 LEAVE(" "); 00111 } 00112 00113 00114 void 00115 gnc_prices_dialog_response (GtkDialog *dialog, gint response_id, gpointer data) 00116 { 00117 PricesDialog *pdb_dialog = data; 00118 00119 ENTER(" "); 00120 gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog); 00121 LEAVE(" "); 00122 } 00123 00124 00125 void 00126 gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data) 00127 { 00128 PricesDialog *pdb_dialog = data; 00129 GList *price_list; 00130 00131 ENTER(" "); 00132 price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree); 00133 if (!price_list) 00134 { 00135 LEAVE("no price selected"); 00136 return; 00137 } 00138 if (g_list_next(price_list)) 00139 { 00140 g_list_free(price_list); 00141 LEAVE("too many prices selected"); 00142 return; 00143 } 00144 00145 gnc_price_edit_dialog (pdb_dialog->dialog, pdb_dialog->session, 00146 price_list->data, GNC_PRICE_EDIT); 00147 g_list_free(price_list); 00148 LEAVE(" "); 00149 } 00150 00151 00152 static void 00153 remove_helper(GNCPrice *price, GNCPriceDB *pdb) 00154 { 00155 gnc_pricedb_remove_price (pdb, price); 00156 } 00157 00158 00159 void 00160 gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data) 00161 { 00162 PricesDialog *pdb_dialog = data; 00163 GList *price_list; 00164 gint length, response; 00165 GtkWidget *dialog; 00166 00167 ENTER(" "); 00168 price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree); 00169 if (!price_list) 00170 { 00171 LEAVE("no price selected"); 00172 return; 00173 } 00174 00175 length = g_list_length(price_list); 00176 if (length > 1) 00177 { 00178 gchar *message; 00179 00180 message = g_strdup_printf 00181 (/* Translators: %d is the number of prices. This 00182 is a ngettext(3) message. */ 00183 ngettext("Are you sure you want to delete the %d selected price?", 00184 "Are you sure you want to delete the %d selected prices?", 00185 length), 00186 length); 00187 dialog = gtk_message_dialog_new(GTK_WINDOW(pdb_dialog->dialog), 00188 GTK_DIALOG_DESTROY_WITH_PARENT, 00189 GTK_MESSAGE_QUESTION, 00190 GTK_BUTTONS_NONE, 00191 "%s", _("Delete prices?")); 00192 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), 00193 "%s", message); 00194 g_free(message); 00195 gtk_dialog_add_buttons(GTK_DIALOG(dialog), 00196 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 00197 GTK_STOCK_DELETE, GTK_RESPONSE_YES, 00198 (gchar *)NULL); 00199 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); 00200 response = gnc_dialog_run(GTK_DIALOG(dialog), "pricedb_remove_multiple"); 00201 gtk_widget_destroy(dialog); 00202 } 00203 else 00204 { 00205 response = GTK_RESPONSE_YES; 00206 } 00207 00208 if (response == GTK_RESPONSE_YES) 00209 { 00210 g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db); 00211 } 00212 g_list_free(price_list); 00213 LEAVE(" "); 00214 } 00215 00216 00217 void 00218 gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data) 00219 { 00220 PricesDialog *pdb_dialog = data; 00221 GtkBuilder *builder; 00222 GtkWidget *dialog, *button, *date, *label, *box; 00223 gint result; 00224 gboolean delete_user, delete_last; 00225 00226 ENTER(" "); 00227 builder = gtk_builder_new(); 00228 gnc_builder_add_from_file (builder, "dialog-price.glade", "Deletion Date"); 00229 00230 dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Deletion Date")); 00231 00232 box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox")); 00233 date = gnc_date_edit_new (time (NULL), FALSE, FALSE); 00234 00235 gtk_box_pack_start (GTK_BOX (box), date, TRUE, TRUE, 0); 00236 gtk_widget_show (date); 00237 gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE); 00238 label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label")); 00239 gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label); 00240 00241 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog); 00242 00243 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (pdb_dialog->dialog)); 00244 00245 result = gtk_dialog_run (GTK_DIALOG (dialog)); 00246 if (result == GTK_RESPONSE_OK) 00247 { 00248 Timespec ts; 00249 00250 DEBUG("deleting prices"); 00251 ts.tv_sec = gnc_date_edit_get_date (GNC_DATE_EDIT (date)); 00252 ts.tv_nsec = 0; 00253 00254 button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_manual")); 00255 delete_user = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); 00256 button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_last")); 00257 delete_last = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); 00258 00259 gnc_pricedb_remove_old_prices(pdb_dialog->price_db, ts, 00260 delete_user, delete_last); 00261 } 00262 00263 gtk_widget_destroy(dialog); 00264 LEAVE(" "); 00265 } 00266 00267 00268 void 00269 gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data) 00270 { 00271 PricesDialog *pdb_dialog = data; 00272 GNCPrice *price = NULL; 00273 GList *price_list; 00274 00275 ENTER(" "); 00276 price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree); 00277 if (price_list) 00278 { 00279 price = price_list->data; 00280 g_list_free(price_list); 00281 } 00282 gnc_price_edit_dialog (pdb_dialog->dialog, pdb_dialog->session, 00283 price, GNC_PRICE_NEW); 00284 LEAVE(" "); 00285 } 00286 00287 00288 void 00289 gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data) 00290 { 00291 PricesDialog *pdb_dialog = data; 00292 SCM quotes_func; 00293 SCM book_scm; 00294 SCM scm_window; 00295 00296 ENTER(" "); 00297 quotes_func = scm_c_eval_string ("gnc:book-add-quotes"); 00298 if (!scm_is_procedure (quotes_func)) 00299 { 00300 LEAVE(" no procedure"); 00301 return; 00302 } 00303 00304 book_scm = gnc_book_to_scm (pdb_dialog->book); 00305 if (scm_is_true (scm_not (book_scm))) 00306 { 00307 LEAVE("no book"); 00308 return; 00309 } 00310 00311 scm_window = SWIG_NewPointerObj(pdb_dialog->dialog, 00312 SWIG_TypeQuery("_p_GtkWidget"), 0); 00313 00314 gnc_set_busy_cursor (NULL, TRUE); 00315 scm_call_2 (quotes_func, scm_window, book_scm); 00316 gnc_unset_busy_cursor (NULL); 00317 00318 /* Without this, the summary bar on the accounts tab 00319 * won't reflect the new prices (bug #522095). */ 00320 gnc_gui_refresh_all (); 00321 00322 LEAVE(" "); 00323 } 00324 00325 00326 static void 00327 gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection, 00328 gpointer data) 00329 { 00330 PricesDialog *pdb_dialog = data; 00331 GList *price_list; 00332 gint length; 00333 00334 ENTER(" "); 00335 price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree); 00336 length = g_list_length(price_list); 00337 00338 gtk_widget_set_sensitive (pdb_dialog->edit_button, 00339 length == 1); 00340 gtk_widget_set_sensitive (pdb_dialog->remove_button, 00341 length >= 1); 00342 LEAVE("%d prices selected", length); 00343 } 00344 00345 00346 static gboolean 00347 gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *namespace, 00348 gpointer data) 00349 { 00350 PricesDialog *pdb_dialog = data; 00351 const gchar *name; 00352 static GList *cm_list; 00353 GList *item; 00354 00355 /* Never show the template list */ 00356 name = gnc_commodity_namespace_get_name (namespace); 00357 if (safe_strcmp (name, "template") == 0) 00358 return FALSE; 00359 00360 /* See if this namespace has commodities */ 00361 cm_list = gnc_commodity_namespace_get_commodity_list(namespace); 00362 for (item = cm_list; item; item = g_list_next(item)) 00363 { 00364 00365 /* For each commodity, see if there are prices */ 00366 if (gnc_pricedb_has_prices(pdb_dialog->price_db, item->data, NULL)) 00367 { 00368 return TRUE; 00369 } 00370 } 00371 00372 // printf("Namespace %s not visible\n", name); 00373 return FALSE; 00374 } 00375 00376 00377 static gboolean 00378 gnc_price_dialog_filter_cm_func (gnc_commodity *commodity, 00379 gpointer data) 00380 { 00381 PricesDialog *pdb_dialog = data; 00382 00383 /* Show any commodity that has prices */ 00384 return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL); 00385 } 00386 00387 00388 static void 00389 row_activated_cb (GtkTreeView *view, GtkTreePath *path, 00390 GtkTreeViewColumn *column, gpointer data) 00391 { 00392 GtkTreeModel *model; 00393 GtkTreeIter iter; 00394 00395 g_return_if_fail(view); 00396 00397 model = gtk_tree_view_get_model(view); 00398 if (gtk_tree_model_get_iter(model, &iter, path)) 00399 { 00400 if (gtk_tree_model_iter_has_child(model, &iter)) 00401 { 00402 /* There are children, so it's not a price. 00403 * Just expand or collapse the row. */ 00404 if (gtk_tree_view_row_expanded(view, path)) 00405 gtk_tree_view_collapse_row(view, path); 00406 else 00407 gtk_tree_view_expand_row(view, path, FALSE); 00408 } 00409 else 00410 /* It's a price, so click the Edit button. */ 00411 gnc_prices_dialog_edit_clicked(GTK_WIDGET(view), data); 00412 } 00413 } 00414 00415 00416 static void 00417 gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog) 00418 { 00419 GtkWidget *dialog, *scrolled_window; 00420 GtkBuilder *builder; 00421 GtkTreeView *view; 00422 GtkTreeSelection *selection; 00423 00424 ENTER(" "); 00425 builder = gtk_builder_new(); 00426 gnc_builder_add_from_file (builder, "dialog-price.glade", "Prices Dialog"); 00427 00428 dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Prices Dialog")); 00429 pdb_dialog->dialog = dialog; 00430 00431 pdb_dialog->session = gnc_get_current_session(); 00432 pdb_dialog->book = qof_session_get_book(pdb_dialog->session); 00433 pdb_dialog->price_db = gnc_pricedb_get_db(pdb_dialog->book); 00434 00435 /* parent */ 00436 if (parent != NULL) 00437 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent)); 00438 00439 /* default to 'close' button */ 00440 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); 00441 00442 /* price tree */ 00443 scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "price_list_window")); 00444 view = gnc_tree_view_price_new(pdb_dialog->book, 00445 "gconf-section", GCONF_SECTION, 00446 "show-column-menu", TRUE, 00447 NULL); 00448 pdb_dialog->price_tree = GNC_TREE_VIEW_PRICE(view); 00449 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view)); 00450 gnc_tree_view_price_set_filter (pdb_dialog->price_tree, 00451 gnc_price_dialog_filter_ns_func, 00452 gnc_price_dialog_filter_cm_func, 00453 NULL, 00454 pdb_dialog, NULL); 00455 00456 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); 00457 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); 00458 g_signal_connect (G_OBJECT (selection), "changed", 00459 G_CALLBACK (gnc_prices_dialog_selection_changed), pdb_dialog); 00460 00461 g_signal_connect (G_OBJECT (view), "row-activated", 00462 G_CALLBACK (row_activated_cb), pdb_dialog); 00463 00464 /* buttons */ 00465 { 00466 GtkWidget *button; 00467 00468 button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button")); 00469 pdb_dialog->edit_button = button; 00470 00471 button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button")); 00472 pdb_dialog->remove_button = button; 00473 00474 if (!gnc_quote_source_fq_installed()) 00475 { 00476 button = GTK_WIDGET(gtk_builder_get_object (builder, "get_quotes_button")); 00477 gtk_widget_set_sensitive(button, FALSE); 00478 } 00479 } 00480 00481 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog); 00482 00483 g_object_unref(G_OBJECT(builder)); 00484 00485 gnc_restore_window_size(GCONF_SECTION, GTK_WINDOW(pdb_dialog->dialog)); 00486 LEAVE(" "); 00487 } 00488 00489 00490 static void 00491 close_handler (gpointer user_data) 00492 { 00493 PricesDialog *pdb_dialog = user_data; 00494 00495 ENTER(" "); 00496 gnc_save_window_size(GCONF_SECTION, GTK_WINDOW(pdb_dialog->dialog)); 00497 00498 gtk_widget_destroy (GTK_WIDGET (pdb_dialog->dialog)); 00499 LEAVE(" "); 00500 } 00501 00502 00503 static void 00504 refresh_handler (GHashTable *changes, gpointer user_data) 00505 { 00506 ENTER(" "); 00507 LEAVE(" "); 00508 } 00509 00510 00511 static gboolean 00512 show_handler (const char *class, gint component_id, 00513 gpointer user_data, gpointer iter_data) 00514 { 00515 PricesDialog *pdb_dialog = user_data; 00516 00517 ENTER(" "); 00518 if (!pdb_dialog) 00519 { 00520 LEAVE("no data strucure"); 00521 return(FALSE); 00522 } 00523 00524 gtk_window_present (GTK_WINDOW(pdb_dialog->dialog)); 00525 LEAVE(" "); 00526 return(TRUE); 00527 } 00528 00529 00530 /********************************************************************\ 00531 * gnc_prices_dialog * 00532 * opens up a window showing all price information * 00533 * * 00534 * Args: parent - the parent of the window to be created * 00535 * Return: nothing * 00536 \********************************************************************/ 00537 void 00538 gnc_prices_dialog (GtkWidget * parent) 00539 { 00540 PricesDialog *pdb_dialog; 00541 gint component_id; 00542 00543 ENTER(" "); 00544 if (gnc_forall_gui_components (DIALOG_PRICE_DB_CM_CLASS, show_handler, NULL)) 00545 { 00546 LEAVE("existing dialog raised"); 00547 return; 00548 } 00549 00550 pdb_dialog = g_new0 (PricesDialog, 1); 00551 00552 gnc_prices_dialog_create (parent, pdb_dialog); 00553 00554 component_id = gnc_register_gui_component (DIALOG_PRICE_DB_CM_CLASS, 00555 refresh_handler, close_handler, 00556 pdb_dialog); 00557 gnc_gui_component_set_session (component_id, pdb_dialog->session); 00558 00559 gtk_widget_grab_focus (GTK_WIDGET(pdb_dialog->price_tree)); 00560 00561 gtk_widget_show (pdb_dialog->dialog); 00562 LEAVE(" "); 00563 }
1.7.4