GnuCash  5.6-150-g038405b370+
dialog-price-edit-db.cpp
1 /********************************************************************\
2  * dialog-price-editor.c -- price selector dialog *
3  * Copyright (C) 2001 Gnumatic, Inc. *
4  * Author: Dave Peticolas <dave@krondo.com> *
5  * Copyright (C) 2003,2005 David Hampton *
6  * Copyright (C) 2011 Robert Fewell *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24 \********************************************************************/
25 
26 #include <config.h>
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <time.h>
31 #include <gnc-quotes.hpp>
32 
33 #include "dialog-utils.h"
34 #include "dialog-commodity.h"
35 #include "gnc-accounting-period.h"
36 #include "gnc-amount-edit.h"
37 #include "gnc-commodity-edit.h"
38 #include "gnc-general-select.h"
39 #include "gnc-component-manager.h"
40 #include "gnc-currency-edit.h"
41 #include "gnc-date-edit.h"
42 #include "gnc-engine.h"
43 #include "gnc-gtk-utils.h"
44 #include "gnc-gui-query.h"
45 #include "gnc-pricedb.h"
46 #include "gnc-session.h"
47 #include "gnc-tree-view-price.h"
48 #include "gnc-ui.h"
49 #include "gnc-ui-util.h"
50 #include "gnc-warnings.h"
51 #include <gnc-glib-utils.h>
52 
53 
54 #define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
55 #define STATE_SECTION "dialogs/edit_prices"
56 #define GNC_PREFS_GROUP "dialogs.pricedb-editor"
57 
58 /* This static indicates the debugging module that this .o belongs to. */
59 static QofLogModule log_module = GNC_MOD_GUI;
60 
61 
62 extern "C" {
63 void gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data);
64 void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data);
65 void gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data);
66 void gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data);
67 void gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data);
68 void gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data);
69 void gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data);
70 void gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data);
71 static gboolean gnc_prices_dialog_key_press_cb (GtkWidget *widget,
72  GdkEventKey *event,
73  gpointer data);
74 }
75 
76 
78 {
79  GtkWidget * window;
80  QofSession *session;
81  QofBook *book;
82  GNCPriceDB *price_db;
83 
84  GncTreeViewPrice * price_tree;
85 
86  GtkWidget * edit_button;
87  GtkWidget * remove_button;
88  GtkWidget * add_button;
89 
90  GtkWidget *remove_dialog;
91  GtkTreeView *remove_view;
92  GtkWidget *namespace_cbwe;
93  gchar *target_namespace_name;
94  int remove_source;
95 };
96 
97 
98 void
99 gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data)
100 {
101  auto pdb_dialog = static_cast<PricesDialog *> (data);
102 
103  ENTER(" ");
104  gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
105 
106  if (pdb_dialog->window)
107  {
108  gtk_widget_destroy (pdb_dialog->window);
109  pdb_dialog->window = NULL;
110  }
111 
112  g_free (pdb_dialog);
113  LEAVE(" ");
114 }
115 
116 
117 static gboolean
118 gnc_prices_dialog_delete_event_cb (GtkWidget *widget,
119  GdkEvent *event,
120  gpointer data)
121 {
122  auto pdb_dialog = static_cast<PricesDialog *> (data);
123  // this cb allows the window size to be saved on closing with the X
124  gnc_save_window_size (GNC_PREFS_GROUP,
125  GTK_WINDOW(pdb_dialog->window));
126  return FALSE;
127 }
128 
129 
130 void
131 gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
132 {
133  auto pdb_dialog = static_cast<PricesDialog *> (data);
134 
135  ENTER(" ");
136  gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
137  LEAVE(" ");
138 }
139 
140 
141 void
142 gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data)
143 {
144  auto pdb_dialog{static_cast<PricesDialog*>(data)};
145 
146  gnc_gnome_help (GTK_WINDOW (pdb_dialog->window), DF_MANUAL, DL_PRICE_DB);
147 }
148 
149 
150 void
151 gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
152 {
153  auto pdb_dialog = static_cast<PricesDialog *> (data);
154 
155  ENTER(" ");
156  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
157  if (!price_list)
158  {
159  LEAVE("no price selected");
160  return;
161  }
162  if (g_list_next(price_list))
163  {
164  g_list_free(price_list);
165  LEAVE("too many prices selected");
166  return;
167  }
168 
169  auto price = static_cast<GNCPrice *> (price_list->data);
170  gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
171  price, GNC_PRICE_EDIT);
172  g_list_free (price_list);
173  LEAVE(" ");
174 }
175 
176 
177 static void
178 remove_helper(GNCPrice *price, GNCPriceDB *pdb)
179 {
180  gnc_pricedb_remove_price (pdb, price);
181 }
182 
183 
184 void
185 gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
186 {
187  auto pdb_dialog = static_cast<PricesDialog *> (data);
188 
189  ENTER(" ");
190  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
191  if (!price_list)
192  {
193  LEAVE("no price selected");
194  return;
195  }
196 
197  gint response;
198  auto length = g_list_length(price_list);
199  if (length > 0)
200  {
201  gchar *message;
202 
203  message = g_strdup_printf
204  (/* Translators: %d is the number of prices. This is a ngettext(3) message. */
205  ngettext("Are you sure you want to delete the selected price?",
206  "Are you sure you want to delete the %d selected prices?",
207  length),
208  length);
209  auto dialog = gtk_message_dialog_new (GTK_WINDOW(pdb_dialog->window),
210  GTK_DIALOG_DESTROY_WITH_PARENT,
211  GTK_MESSAGE_QUESTION,
212  GTK_BUTTONS_NONE,
213  "%s", _("Delete prices?"));
214  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
215  "%s", message);
216  g_free(message);
217  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
218  _("_Cancel"), GTK_RESPONSE_CANCEL,
219  _("_Delete"), GTK_RESPONSE_YES,
220  (gchar *)NULL);
221  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
222  response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_PRICE_QUOTES_DEL);
223  gtk_widget_destroy(dialog);
224  }
225  else
226  {
227  response = GTK_RESPONSE_YES;
228  }
229 
230  if (response == GTK_RESPONSE_YES)
231  {
232  g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db);
233  }
234  g_list_free(price_list);
235  gnc_gui_refresh_all ();
236  LEAVE(" ");
237 }
238 
239 
241 enum GncPriceColumn {PRICED_NAMESPACE_NAME, PRICED_FULL_NAME, PRICED_COMM, PRICED_DATE, PRICED_COUNT};
242 
243 static bool
244 continue_namespace_check (const gchar *target_namespace_name, const gchar *namespace_name)
245 {
246  if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_CURRENCY) == 0) &&
247  (g_strcmp0 (namespace_name, GNC_COMMODITY_NS_CURRENCY) != 0))
248  return true;
249 
250  if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_NONISO_GUI) == 0) &&
251  (g_strcmp0 (namespace_name, GNC_COMMODITY_NS_CURRENCY) == 0))
252  return true;
253 
254  if ((g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_NONISO_GUI) != 0) &&
255  (g_strcmp0 (target_namespace_name, GNC_COMMODITY_NS_CURRENCY) != 0) &&
256  (g_strcmp0 (target_namespace_name, namespace_name) != 0))
257  return true;
258 
259  return false;
260 }
261 
262 static time64
263 gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb, const gchar *target_namespace_name)
264 {
265  auto oldest = gnc_time (nullptr);
266  auto model = gtk_tree_view_get_model (view);
267  const auto commodity_table = gnc_get_current_commodities ();
268  auto namespace_list = gnc_commodity_table_get_namespaces_list (commodity_table);
269 
270  // disconnect the model to the price treeview
271  g_object_ref (G_OBJECT(model));
272  gtk_tree_view_set_model (GTK_TREE_VIEW(view), nullptr);
273 
274  gtk_list_store_clear (GTK_LIST_STORE(model));
275 
276  for (auto node_n = namespace_list; node_n; node_n = g_list_next (node_n))
277  {
278  auto tmp_namespace = static_cast<gnc_commodity_namespace*>(node_n->data);
279  auto tmp_namespace_name_str = gnc_commodity_namespace_get_name (tmp_namespace);
280 
281  DEBUG("Restricted to %s, looking at namespace %s", target_namespace_name, tmp_namespace_name_str);
282  if (continue_namespace_check (target_namespace_name, tmp_namespace_name_str))
283  continue;
284 
285  auto commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace_name_str);
286  for (auto node_c = commodity_list; node_c; node_c = g_list_next (node_c))
287  {
288  auto tmp_commodity = static_cast<gnc_commodity*>(node_c->data);
289  auto num = gnc_pricedb_num_prices (pdb, tmp_commodity);
290  DEBUG("Looking at commodity %s, Number of prices %d", gnc_commodity_get_fullname (tmp_commodity), num);
291 
292  if (num > 0)
293  {
294  auto list = gnc_pricedb_get_prices (pdb, tmp_commodity, NULL);
295  auto node = g_list_last (list);
296  auto price = static_cast<GNCPrice*> (node->data);
297  auto price_time = gnc_price_get_time64 (price);
298  auto name_str = gnc_commodity_get_printname (tmp_commodity);
299  auto tmp_namespace_gui_str = gnc_commodity_namespace_get_gui_name (tmp_namespace);
300 
301  if (oldest > price_time)
302  oldest = price_time;
303 
304  auto date_str = qof_print_date (price_time);
305  auto num_str = g_strdup_printf ("%d", num);
306 
307  GtkTreeIter iter;
308  gtk_list_store_append (GTK_LIST_STORE(model), &iter);
309  gtk_list_store_set (GTK_LIST_STORE(model), &iter,
310  PRICED_NAMESPACE_NAME, tmp_namespace_gui_str,
311  PRICED_FULL_NAME, name_str,
312  PRICED_COMM, tmp_commodity,
313  PRICED_DATE, date_str,
314  PRICED_COUNT, num_str,
315  -1);
316 
317  g_free (date_str);
318  g_free (num_str);
319  g_list_free_full (list, (GDestroyNotify)gnc_price_unref);
320  }
321  }
322  g_list_free (commodity_list);
323  }
324  g_list_free (namespace_list);
325 
326  // reconnect the model to the price treeview
327  gtk_tree_view_set_model (GTK_TREE_VIEW(view), model);
328  g_object_unref (G_OBJECT(model));
329 
330  return oldest;
331 }
332 
333 static GList *
334 gnc_prices_dialog_get_commodities (GtkTreeView *view)
335 {
336  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
337  auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
338  auto list = gtk_tree_selection_get_selected_rows (selection, &model);
339  GList *comm_list = nullptr;
340 
341  // Walk the list
342  for (auto row = g_list_first (list); row; row = g_list_next (row))
343  {
344  auto path = static_cast<GtkTreePath *> (row->data);
345  GtkTreeIter iter;
346  if (gtk_tree_model_get_iter (model, &iter, path))
347  {
348  gnc_commodity *comm;
349  gtk_tree_model_get (model, &iter, PRICED_COMM, &comm, -1);
350  comm_list = g_list_prepend (comm_list, comm);
351  }
352  }
353  g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
354 
355  return g_list_reverse (comm_list);
356 }
357 
358 static void
359 change_source_flag (PriceRemoveSourceFlags source, gboolean set, gpointer data)
360 {
361  auto pdb_dialog = static_cast<PricesDialog *> (data);
362  auto widget_ok = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog),
363  GTK_RESPONSE_OK);
364  auto widget_apply = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog),
365  GTK_RESPONSE_APPLY);
366 
367  if (set)
368  pdb_dialog->remove_source = pdb_dialog->remove_source | source;
369  else
370  pdb_dialog->remove_source = pdb_dialog->remove_source & (~source);
371 
372  // Check if we have the required options to enable OK and Apply buttons
373  gboolean enable_button = (pdb_dialog->remove_source > 8 ? TRUE : FALSE); // commodities flag is 8
374  gtk_widget_set_sensitive (widget_ok, enable_button);
375  gtk_widget_set_sensitive (widget_apply, enable_button);
376 
377  DEBUG("Source is: %d, remove_source is %d", source, pdb_dialog->remove_source);
378 }
379 
380 static void
381 check_event_fq_cb (GtkWidget *widget, gpointer data)
382 {
383  auto pdb_dialog = static_cast<PricesDialog *> (data);
384  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
385 
386  change_source_flag (PRICE_REMOVE_SOURCE_FQ, active, pdb_dialog);
387 }
388 
389 static void
390 check_event_user_cb (GtkWidget *widget, gpointer data)
391 {
392  auto pdb_dialog = static_cast<PricesDialog *> (data);
393  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
394 
395  change_source_flag (PRICE_REMOVE_SOURCE_USER, active, pdb_dialog);
396 }
397 
398 static void
399 check_event_app_cb (GtkWidget *widget, gpointer data)
400 {
401  auto pdb_dialog = static_cast<PricesDialog *> (data);
402  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
403 
404  change_source_flag (PRICE_REMOVE_SOURCE_APP, active, pdb_dialog);
405 }
406 
407 static void
408 selection_changed_cb (GtkTreeSelection *selection, gpointer data)
409 {
410  auto pdb_dialog = static_cast<PricesDialog *> (data);
411  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->remove_view));
412  auto rows = gtk_tree_selection_get_selected_rows (selection, &model);
413  gboolean have_rows = (gnc_list_length_cmp (rows, 0));
414 
415  change_source_flag (PRICE_REMOVE_SOURCE_COMM, have_rows, pdb_dialog);
416  g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
417 }
418 
419 static GDate
420 get_fiscal_end_date (void)
421 {
422  time64 end;
423  char datebuff[MAX_DATE_LENGTH + 1];
424  memset (datebuff, 0, sizeof(datebuff));
425  end = gnc_accounting_period_fiscal_end();
427  gnc_accounting_period_fiscal_end());
428  PINFO("Fiscal end date is %s", datebuff);
429 
430  return time64_to_gdate (end);
431 }
432 
433 static void
434 namespace_changed_cb (GtkComboBox *cbwe, gpointer data)
435 {
436  auto pdb_dialog = static_cast<PricesDialog *>(data);
437 
438  if (pdb_dialog->target_namespace_name)
439  g_free (pdb_dialog->target_namespace_name);
440  pdb_dialog->target_namespace_name = gnc_ui_namespace_picker_ns (GTK_WIDGET(cbwe));
441 
442  gnc_prices_dialog_load_view (pdb_dialog->remove_view,
443  pdb_dialog->price_db,
444  pdb_dialog->target_namespace_name);
445 }
446 
447 static PriceRemoveKeepOptions
448 get_keep_options_value (GtkBuilder *builder)
449 {
450  if (!builder)
451  return PRICE_REMOVE_KEEP_LAST_WEEKLY;
452 
453  auto button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_none"));
454  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
455  return PRICE_REMOVE_KEEP_NONE;
456  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_month"));
457  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
458  return PRICE_REMOVE_KEEP_LAST_MONTHLY;
459  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_quarter"));
460  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
461  return PRICE_REMOVE_KEEP_LAST_QUARTERLY;
462  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_period"));
463  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
464  return PRICE_REMOVE_KEEP_LAST_PERIOD;
465  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_scaled"));
466  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)))
467  return PRICE_REMOVE_KEEP_SCALED;
468 
469  // default which is also "radiobutton_last_week"
470  return PRICE_REMOVE_KEEP_LAST_WEEKLY;
471 }
472 
473 void
474 gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
475 {
476  auto pdb_dialog = static_cast<PricesDialog *> (data);
477 
478  ENTER(" ");
479  auto builder = gtk_builder_new();
480  gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore3");
481  gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore4");
482  gnc_builder_add_from_file (builder, "dialog-price.glade", "deletion_date_dialog");
483 
484  pdb_dialog->remove_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "deletion_date_dialog"));
485 
486  auto box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
487  auto date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
488 
489  gtk_box_pack_start (GTK_BOX (box), date, FALSE, FALSE, 0);
490  gtk_widget_show (date);
491  gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE);
492  auto label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
493  gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label);
494 
495  // Setup namespace
496  pdb_dialog->namespace_cbwe = GTK_WIDGET(gtk_builder_get_object (builder, "namespace_combo_we"));
497  gnc_ui_update_namespace_picker (pdb_dialog->namespace_cbwe, nullptr, DIAG_COMM_ALL);
498  gnc_cbwe_require_list_item (GTK_COMBO_BOX(pdb_dialog->namespace_cbwe));
499  gtk_combo_box_set_active (GTK_COMBO_BOX(pdb_dialog->namespace_cbwe), 1);
500  g_signal_connect (G_OBJECT(pdb_dialog->namespace_cbwe), "changed",
501  G_CALLBACK(namespace_changed_cb), pdb_dialog);
502 
503  // Setup the commodity view
504  pdb_dialog->remove_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "commodty_treeview"));
505  auto selection = gtk_tree_view_get_selection (pdb_dialog->remove_view);
506  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
507 
508  // Add Entries column this way as align does not seem to work from builder
509  auto tree_column = gtk_tree_view_column_new();
510  gtk_tree_view_column_set_title (tree_column, _("Entries"));
511  gtk_tree_view_append_column (GTK_TREE_VIEW(pdb_dialog->remove_view), tree_column);
512  gtk_tree_view_column_set_alignment (tree_column, 0.5);
513  gtk_tree_view_column_set_expand (tree_column, TRUE);
514  auto cr = gtk_cell_renderer_text_new();
515  gtk_tree_view_column_pack_start (tree_column, cr, TRUE);
516  // set 'xalign' property of the cell renderer
517  gtk_tree_view_column_set_attributes (tree_column, cr, "text", PRICED_COUNT, NULL);
518  gtk_cell_renderer_set_alignment (cr, 0.5, 0.5);
519 
520  // Load the view and get the earliest date
521  pdb_dialog->target_namespace_name = g_strdup (GNC_COMMODITY_NS_NONISO_GUI);
522  gnc_prices_dialog_load_view (pdb_dialog->remove_view,
523  pdb_dialog->price_db,
524  pdb_dialog->target_namespace_name);
525 
526  g_signal_connect (selection, "changed", G_CALLBACK(selection_changed_cb), pdb_dialog);
527 
528  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
529 
530  gtk_window_set_transient_for (GTK_WINDOW (pdb_dialog->remove_dialog), GTK_WINDOW (pdb_dialog->window));
531 
532  pdb_dialog->remove_source = PRICE_REMOVE_SOURCE_FQ;
533  change_source_flag (PRICE_REMOVE_SOURCE_FQ, TRUE, pdb_dialog);
534 
535  auto button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_fq"));
536  g_signal_connect (button, "toggled", G_CALLBACK (check_event_fq_cb), pdb_dialog);
537  button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_user"));
538  g_signal_connect (button, "toggled", G_CALLBACK (check_event_user_cb), pdb_dialog);
539  button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_app"));
540  g_signal_connect (button, "toggled", G_CALLBACK (check_event_app_cb), pdb_dialog);
541 
542  bool leave = false;
543  int response = 0;
544  while (!leave && (response = gtk_dialog_run (GTK_DIALOG(pdb_dialog->remove_dialog))))
545  {
546  if ((response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_DELETE_EVENT))
547  leave = true;
548 
549  if ((response == GTK_RESPONSE_OK) || (response == GTK_RESPONSE_APPLY))
550  {
551  const char *fmt = _("Are you sure you want to delete these prices?");
552  auto comm_list = gnc_prices_dialog_get_commodities (pdb_dialog->remove_view);
553  bool delete_entries = false;
554 
555  // Are you sure you want to delete the entries and we have commodities
556  if ((g_list_length (comm_list) != 0) &&
557  (gnc_verify_dialog (GTK_WINDOW(pdb_dialog->remove_dialog), FALSE, fmt, NULL)))
558  {
559  time64 last;
560  GDate fiscal_end_date = get_fiscal_end_date ();
561  PriceRemoveKeepOptions keep = get_keep_options_value (builder);
562  delete_entries = true;
563 
564  // disconnect the model to the price treeview
565  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
566  g_object_ref (G_OBJECT(model));
567  gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), nullptr);
568 
569  DEBUG("deleting prices for keep option %d", keep);
570  last = gnc_date_edit_get_date (GNC_DATE_EDIT (date));
571 
572  if (keep != PRICE_REMOVE_KEEP_SCALED)
573  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
574  &fiscal_end_date, last,
575  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
576  keep);
577  else
578  {
579  auto tmp_date = time64_to_gdate (last);
580  g_date_subtract_months (&tmp_date, 6);
581  auto tmp = gdate_to_time64 (tmp_date);
582 
583  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
584  &fiscal_end_date, tmp,
585  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
586  PRICE_REMOVE_KEEP_LAST_WEEKLY);
587 
588  g_date_subtract_months (&tmp_date, 6);
589  tmp = gdate_to_time64 (tmp_date);
590 
591  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
592  &fiscal_end_date, tmp,
593  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
594  PRICE_REMOVE_KEEP_LAST_MONTHLY);
595  }
596  // reconnect the model to the price treeview
597  gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), model);
598  g_object_unref (G_OBJECT(model));
599  }
600  g_list_free (comm_list);
601 
602  if (response == GTK_RESPONSE_OK)
603  {
604  if (delete_entries)
605  leave = true;
606  }
607  else
608  {
609  if (delete_entries)
610  gnc_prices_dialog_load_view (pdb_dialog->remove_view,
611  pdb_dialog->price_db,
612  pdb_dialog->target_namespace_name);
613  }
614  }
615  }
616  gnc_gui_refresh_all ();
617 
618  if (pdb_dialog->target_namespace_name)
619  g_free (pdb_dialog->target_namespace_name);
620 
621  gtk_widget_destroy (pdb_dialog->remove_dialog);
622  g_object_unref (G_OBJECT(builder));
623  LEAVE(" ");
624 }
625 
626 
627 void
628 gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
629 {
630  auto pdb_dialog = static_cast<PricesDialog *> (data);
631  GNCPrice *price = nullptr;
632  gboolean unref_price = FALSE;
633 
634  ENTER(" ");
635  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
636  auto comm_list = gnc_tree_view_price_get_selected_commodities (pdb_dialog->price_tree);
637 
638  if (price_list) // selected row is on a price
639  {
640  price = static_cast<GNCPrice *> (price_list->data);
641  g_list_free (price_list);
642  }
643  else if (comm_list) // selection contains price parent rows
644  {
645  if (!gnc_list_length_cmp (comm_list, 1)) // make sure it is only one parent
646  {
647  auto comm = GNC_COMMODITY (comm_list->data);
648  auto latest_price = gnc_pricedb_lookup_latest_any_currency (pdb_dialog->price_db, comm);
649 
650  if (latest_price)
651  {
652  price = GNC_PRICE (latest_price->data);
653  gnc_price_ref (price);
654 
655  gnc_price_list_destroy (latest_price);
656  }
657 
658  if (!price)
659  {
660  price = gnc_price_create (pdb_dialog->book);
661  gnc_price_set_commodity (price, comm);
662  }
663 
664  unref_price = TRUE;
665  }
666  g_list_free (comm_list);
667  }
668  gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
669  price, GNC_PRICE_NEW);
670 
671  if (unref_price)
672  gnc_price_unref (price);
673  LEAVE(" ");
674 }
675 
676 
677 void
678 gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data)
679 {
680  auto pdb_dialog = static_cast<PricesDialog *> (data);
681 
682  ENTER(" ");
683  try {
684  GncQuotes quotes;
685  gnc_set_busy_cursor (NULL, TRUE);
686  quotes.fetch (pdb_dialog->book);
687  gnc_unset_busy_cursor (NULL);
688  if (quotes.had_failures())
689  gnc_warning_dialog(GTK_WINDOW(pdb_dialog->window), "%s",
690  quotes.report_failures().c_str());
691  }
692  catch (const GncQuoteException& err)
693  {
694  gnc_unset_busy_cursor(nullptr);
695  PERR("Price retrieval failed: %s", err.what());
696  gnc_error_dialog(GTK_WINDOW(pdb_dialog->window), _("Price retrieval failed: %s"), err.what());
697  }
698 
699  /* Without this, the summary bar on the accounts tab
700  * won't reflect the new prices (bug #522095). */
701  gnc_gui_refresh_all ();
702 
703  LEAVE(" ");
704 }
705 
706 
707 static void
708 gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection,
709  gpointer data)
710 {
711  auto pdb_dialog = static_cast<PricesDialog *> (data);
712 
713  ENTER(" ");
714  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
715  auto length = g_list_length (price_list);
716  g_list_free (price_list);
717 
718  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
719  auto rows = gtk_tree_selection_get_selected_rows (treeselection, &model);
720 
721  // if selected rows greater than length, parents must of been selected also
722  if (g_list_length (rows) > length)
723  length = 0;
724 
725  g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
726 
727  gtk_widget_set_sensitive (pdb_dialog->edit_button,
728  length == 1);
729  gtk_widget_set_sensitive (pdb_dialog->remove_button,
730  length >= 1);
731  gtk_widget_set_sensitive (pdb_dialog->add_button,
732  length <= 1);
733  LEAVE("%d prices selected", length);
734 }
735 
736 
737 static gboolean
738 gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
739  gpointer data)
740 {
741  auto pdb_dialog = static_cast<PricesDialog *> (data);
742 
743  /* Never show the template list */
744  auto name = gnc_commodity_namespace_get_name (name_space);
745  if (g_strcmp0 (name, GNC_COMMODITY_NS_TEMPLATE) == 0)
746  return FALSE;
747 
748  /* See if this namespace has commodities */
749  auto cm_list = gnc_commodity_namespace_get_commodity_list (name_space);
750  auto rv = false;
751  for (auto item = cm_list; !rv && item; item = g_list_next (item))
752  {
753  /* For each commodity, see if there are prices */
754  auto comm = static_cast<gnc_commodity *> (item->data);
755  if (gnc_pricedb_has_prices (pdb_dialog->price_db, comm, nullptr))
756  rv = true;
757  }
758 
759  g_list_free (cm_list);
760  return rv;
761 }
762 
763 
764 static gboolean
765 gnc_price_dialog_filter_cm_func (gnc_commodity *commodity,
766  gpointer data)
767 {
768  auto pdb_dialog = static_cast<PricesDialog *> (data);
769 
770  /* Show any commodity that has prices */
771  return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL);
772 }
773 
774 
775 static void
776 row_activated_cb (GtkTreeView *view, GtkTreePath *path,
777  GtkTreeViewColumn *column, gpointer data)
778 {
779  GtkTreeModel *model;
780  GtkTreeIter iter;
781 
782  g_return_if_fail(view);
783 
784  model = gtk_tree_view_get_model(view);
785  if (gtk_tree_model_get_iter(model, &iter, path))
786  {
787  if (gtk_tree_model_iter_has_child(model, &iter))
788  {
789  /* There are children, so it's not a price.
790  * Just expand or collapse the row. */
791  if (gtk_tree_view_row_expanded(view, path))
792  gtk_tree_view_collapse_row(view, path);
793  else
794  gtk_tree_view_expand_row(view, path, FALSE);
795  }
796  else
797  /* It's a price, so click the Edit button. */
798  gnc_prices_dialog_edit_clicked(GTK_WIDGET(view), data);
799  }
800 }
801 
802 
803 static void
804 gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog)
805 {
806  GtkWidget *window, *scrolled_window;
807  GtkBuilder *builder;
808  GtkTreeView *view;
809  GtkTreeSelection *selection;
810 
811  ENTER(" ");
812  builder = gtk_builder_new();
813  gnc_builder_add_from_file (builder, "dialog-price.glade", "prices_window");
814 
815  window = GTK_WIDGET(gtk_builder_get_object (builder, "prices_window"));
816  pdb_dialog->window = window;
817 
818  // Set the name for this dialog so it can be easily manipulated with css
819  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-price-edit");
820  gnc_widget_style_context_add_class (GTK_WIDGET(window), "gnc-class-securities");
821 
822  pdb_dialog->session = gnc_get_current_session();
823  pdb_dialog->book = qof_session_get_book(pdb_dialog->session);
824  pdb_dialog->price_db = gnc_pricedb_get_db(pdb_dialog->book);
825 
826  g_signal_connect (pdb_dialog->window, "delete-event",
827  G_CALLBACK(gnc_prices_dialog_delete_event_cb), pdb_dialog);
828 
829  g_signal_connect (pdb_dialog->window, "key_press_event",
830  G_CALLBACK (gnc_prices_dialog_key_press_cb), pdb_dialog);
831 
832  /* price tree */
833  scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "price_list_window"));
834  view = gnc_tree_view_price_new(pdb_dialog->book,
835  "state-section", STATE_SECTION,
836  "show-column-menu", TRUE,
837  NULL);
838  pdb_dialog->price_tree = GNC_TREE_VIEW_PRICE(view);
839  gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view));
840  gnc_tree_view_price_set_filter (pdb_dialog->price_tree,
841  gnc_price_dialog_filter_ns_func,
842  gnc_price_dialog_filter_cm_func,
843  NULL,
844  pdb_dialog, NULL);
845 
846  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
847  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
848  g_signal_connect (G_OBJECT (selection), "changed",
849  G_CALLBACK (gnc_prices_dialog_selection_changed), pdb_dialog);
850 
851  g_signal_connect (G_OBJECT (view), "row-activated",
852  G_CALLBACK (row_activated_cb), pdb_dialog);
853 
854  /* buttons */
855  {
856  GtkWidget *button;
857 
858  button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button"));
859  pdb_dialog->edit_button = button;
860 
861  button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
862  pdb_dialog->remove_button = button;
863 
864  button = GTK_WIDGET(gtk_builder_get_object (builder, "add_button"));
865  pdb_dialog->add_button = button;
866 
868  {
869  button = GTK_WIDGET(gtk_builder_get_object (builder, "get_quotes_button"));
870  gtk_widget_set_sensitive(button, FALSE);
871  }
872  /* default to 'close' button */
873  button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
874  gtk_widget_grab_default (button);
875  gtk_widget_grab_focus (button);
876 
877  }
878 
879  g_signal_connect (pdb_dialog->window, "destroy",
880  G_CALLBACK(gnc_prices_dialog_destroy_cb), pdb_dialog);
881 
882  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
883  g_object_unref(G_OBJECT(builder));
884 
885  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window), GTK_WINDOW (parent));
886  LEAVE(" ");
887 }
888 
889 
890 static void
891 close_handler (gpointer user_data)
892 {
893  auto pdb_dialog = static_cast<PricesDialog *> (user_data);
894 
895  ENTER(" ");
896  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window));
897  gtk_widget_destroy (GTK_WIDGET (pdb_dialog->window));
898  LEAVE(" ");
899 }
900 
901 
902 static void
903 refresh_handler (GHashTable *changes, gpointer user_data)
904 {
905  ENTER(" ");
906  LEAVE(" ");
907 }
908 
909 
910 static gboolean
911 show_handler (const char *klass, gint component_id,
912  gpointer user_data, gpointer iter_data)
913 {
914  auto pdb_dialog = static_cast<PricesDialog *> (user_data);
915 
916  ENTER(" ");
917  if (!pdb_dialog)
918  {
919  LEAVE("no data structure");
920  return(FALSE);
921  }
922 
923  gtk_window_present (GTK_WINDOW(pdb_dialog->window));
924  LEAVE(" ");
925  return(TRUE);
926 }
927 
928 
929 gboolean
930 gnc_prices_dialog_key_press_cb (GtkWidget *widget, GdkEventKey *event,
931  gpointer data)
932 {
933  auto pdb_dialog = static_cast<PricesDialog *> (data);
934 
935  if (event->keyval == GDK_KEY_Escape)
936  {
937  close_handler (pdb_dialog);
938  return TRUE;
939  }
940  else
941  return FALSE;
942 }
943 
944 
945 /********************************************************************\
946  * gnc_prices_dialog *
947  * opens up a window showing all price information *
948  * *
949  * Args: parent - the parent of the window to be created *
950  * Return: nothing *
951 \********************************************************************/
952 void
953 gnc_prices_dialog (GtkWidget * parent)
954 {
955  PricesDialog *pdb_dialog;
956  gint component_id;
957 
958  ENTER(" ");
959  if (gnc_forall_gui_components (DIALOG_PRICE_DB_CM_CLASS, show_handler, NULL))
960  {
961  LEAVE("existing dialog raised");
962  return;
963  }
964 
965  pdb_dialog = g_new0 (PricesDialog, 1);
966 
967  gnc_prices_dialog_create (parent, pdb_dialog);
968 
969  component_id = gnc_register_gui_component (DIALOG_PRICE_DB_CM_CLASS,
970  refresh_handler, close_handler,
971  pdb_dialog);
972  gnc_gui_component_set_session (component_id, pdb_dialog->session);
973 
974  gtk_widget_grab_focus (GTK_WIDGET(pdb_dialog->price_tree));
975 
976  gtk_widget_show (pdb_dialog->window);
977  LEAVE(" ");
978 }
void gnc_price_list_destroy(PriceList *prices)
gnc_price_list_destroy - destroy the given price list, calling gnc_price_unref on all the prices incl...
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
a simple price database for gnucash
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user...
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gtk helper routines.
gchar * gnc_ui_namespace_picker_ns(GtkWidget *cbwe)
Given a combo box, return the currently selected namespaces.
int gnc_pricedb_num_prices(GNCPriceDB *db, const gnc_commodity *c)
Get the number of prices, in any currency, for a given commodity.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you&#39;re finished with a price (i.e.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
bool had_failures() noexcept
Report if there were quotes requested but not retrieved.
gboolean gnc_pricedb_remove_old_prices(GNCPriceDB *db, GList *comm_list, GDate *fiscal_end_date, time64 cutoff, PriceRemoveSourceFlags source, PriceRemoveKeepOptions keep)
Remove and unref prices older than a certain time.
GDate time64_to_gdate(time64 t)
Returns the GDate in which the time64 occurs.
Definition: gnc-date.cpp:1284
GList * gnc_tree_view_price_get_selected_commodities(GncTreeViewPrice *view)
This function returns a list of commodities associated with the selected rows that are not prices but...
GList * gnc_tree_view_price_get_selected_prices(GncTreeViewPrice *view)
This function returns a list of the prices associated with the selected items in the price tree view...
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
Dialog box should allow selection of anything.
GtkTreeView implementation for gnucash price tree.
Currency selection widget.
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:610
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:575
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
General utilities for dealing with accounting periods.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
Definition: gnc-date.cpp:1323
PriceList * gnc_pricedb_lookup_latest_any_currency(GNCPriceDB *db, const gnc_commodity *commodity)
Find the most recent price between a commodity and all other commodities.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
All type declarations for the whole Gnucash engine.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
GLib helper routines.
gboolean gnc_pricedb_has_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Report whether the pricedb contains prices for one commodity in another.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:262
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
GtkTreeView * gnc_tree_view_price_new(QofBook *book, const gchar *first_property_name,...)
Create a new price tree view.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
void gnc_ui_update_namespace_picker(GtkWidget *cbwe, const gchar *sel, dialog_commodity_mode mode)
Given a combo box, fill in the known commodity namespaces and then select one.
"select" and "new" commodity windows
void gnc_price_ref(GNCPrice *p)
gnc_price_ref - indicate your need for a given price to stick around (i.e.
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:574
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user&#39;s computer...
PriceList * gnc_pricedb_get_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Return all the prices for a given commodity in another.