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 "gnc-accounting-period.h"
35 #include "gnc-amount-edit.h"
36 #include "gnc-commodity-edit.h"
37 #include "gnc-general-select.h"
38 #include "gnc-component-manager.h"
39 #include "gnc-currency-edit.h"
40 #include "gnc-date-edit.h"
41 #include "gnc-engine.h"
42 #include "gnc-gui-query.h"
43 #include "gnc-pricedb.h"
44 #include "gnc-session.h"
45 #include "gnc-tree-view-price.h"
46 #include "gnc-ui.h"
47 #include "gnc-ui-util.h"
48 #include "gnc-warnings.h"
49 #include <gnc-glib-utils.h>
50 
51 
52 #define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
53 #define STATE_SECTION "dialogs/edit_prices"
54 #define GNC_PREFS_GROUP "dialogs.pricedb-editor"
55 
56 /* This static indicates the debugging module that this .o belongs to. */
57 static QofLogModule log_module = GNC_MOD_GUI;
58 
59 
60 extern "C" {
61 void gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data);
62 void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data);
63 void gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data);
64 void gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data);
65 void gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data);
66 void gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data);
67 void gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data);
68 void gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data);
69 static gboolean gnc_prices_dialog_key_press_cb (GtkWidget *widget,
70  GdkEventKey *event,
71  gpointer data);
72 }
73 
74 
76 {
77  GtkWidget * window;
78  QofSession *session;
79  QofBook *book;
80  GNCPriceDB *price_db;
81 
82  GncTreeViewPrice * price_tree;
83 
84  GtkWidget * edit_button;
85  GtkWidget * remove_button;
86  GtkWidget * add_button;
87 
88  GtkWidget *remove_dialog;
89  GtkTreeView *remove_view;
90  int remove_source;
91 };
92 
93 
94 void
95 gnc_prices_dialog_destroy_cb (GtkWidget *object, gpointer data)
96 {
97  auto pdb_dialog = static_cast<PricesDialog *> (data);
98 
99  ENTER(" ");
100  gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
101 
102  if (pdb_dialog->window)
103  {
104  gtk_widget_destroy (pdb_dialog->window);
105  pdb_dialog->window = NULL;
106  }
107 
108  g_free (pdb_dialog);
109  LEAVE(" ");
110 }
111 
112 
113 static gboolean
114 gnc_prices_dialog_delete_event_cb (GtkWidget *widget,
115  GdkEvent *event,
116  gpointer data)
117 {
118  auto pdb_dialog = static_cast<PricesDialog *> (data);
119  // this cb allows the window size to be saved on closing with the X
120  gnc_save_window_size (GNC_PREFS_GROUP,
121  GTK_WINDOW(pdb_dialog->window));
122  return FALSE;
123 }
124 
125 
126 void
127 gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
128 {
129  auto pdb_dialog = static_cast<PricesDialog *> (data);
130 
131  ENTER(" ");
132  gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
133  LEAVE(" ");
134 }
135 
136 
137 void
138 gnc_prices_dialog_help_cb (GtkDialog *dialog, gpointer data)
139 {
140  auto pdb_dialog{static_cast<PricesDialog*>(data)};
141 
142  gnc_gnome_help (GTK_WINDOW (pdb_dialog->window), DF_MANUAL, DL_PRICE_DB);
143 }
144 
145 
146 void
147 gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
148 {
149  auto pdb_dialog = static_cast<PricesDialog *> (data);
150 
151  ENTER(" ");
152  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
153  if (!price_list)
154  {
155  LEAVE("no price selected");
156  return;
157  }
158  if (g_list_next(price_list))
159  {
160  g_list_free(price_list);
161  LEAVE("too many prices selected");
162  return;
163  }
164 
165  auto price = static_cast<GNCPrice *> (price_list->data);
166  gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
167  price, GNC_PRICE_EDIT);
168  g_list_free (price_list);
169  LEAVE(" ");
170 }
171 
172 
173 static void
174 remove_helper(GNCPrice *price, GNCPriceDB *pdb)
175 {
176  gnc_pricedb_remove_price (pdb, price);
177 }
178 
179 
180 void
181 gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
182 {
183  auto pdb_dialog = static_cast<PricesDialog *> (data);
184 
185  ENTER(" ");
186  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
187  if (!price_list)
188  {
189  LEAVE("no price selected");
190  return;
191  }
192 
193  gint response;
194  auto length = g_list_length(price_list);
195  if (length > 0)
196  {
197  gchar *message;
198 
199  message = g_strdup_printf
200  (/* Translators: %d is the number of prices. This is a ngettext(3) message. */
201  ngettext("Are you sure you want to delete the selected price?",
202  "Are you sure you want to delete the %d selected prices?",
203  length),
204  length);
205  auto dialog = gtk_message_dialog_new (GTK_WINDOW(pdb_dialog->window),
206  GTK_DIALOG_DESTROY_WITH_PARENT,
207  GTK_MESSAGE_QUESTION,
208  GTK_BUTTONS_NONE,
209  "%s", _("Delete prices?"));
210  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
211  "%s", message);
212  g_free(message);
213  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
214  _("_Cancel"), GTK_RESPONSE_CANCEL,
215  _("_Delete"), GTK_RESPONSE_YES,
216  (gchar *)NULL);
217  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
218  response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_PRICE_QUOTES_DEL);
219  gtk_widget_destroy(dialog);
220  }
221  else
222  {
223  response = GTK_RESPONSE_YES;
224  }
225 
226  if (response == GTK_RESPONSE_YES)
227  {
228  g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db);
229  }
230  g_list_free(price_list);
231  gnc_gui_refresh_all ();
232  LEAVE(" ");
233 }
234 
235 
237 enum GncPriceColumn {PRICED_FULL_NAME, PRICED_COMM, PRICED_DATE, PRICED_COUNT};
238 
239 static time64
240 gnc_prices_dialog_load_view (GtkTreeView *view, GNCPriceDB *pdb)
241 {
242  auto oldest = gnc_time (nullptr);
243  auto model = gtk_tree_view_get_model (view);
244  const auto commodity_table = gnc_get_current_commodities ();
245  auto namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
246 
247  for (auto node_n = namespace_list; node_n; node_n = g_list_next (node_n))
248  {
249  auto tmp_namespace = static_cast<char*>(node_n->data);
250  DEBUG("Looking at namespace %s", tmp_namespace);
251  auto commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
252  for (auto node_c = commodity_list; node_c; node_c = g_list_next (node_c))
253  {
254  auto tmp_commodity = static_cast<gnc_commodity*>(node_c->data);
255  auto num = gnc_pricedb_num_prices (pdb, tmp_commodity);
256  DEBUG("Looking at commodity %s, Number of prices %d", gnc_commodity_get_fullname (tmp_commodity), num);
257 
258  if (num > 0)
259  {
260  auto list = gnc_pricedb_get_prices (pdb, tmp_commodity, NULL);
261  auto node = g_list_last (list);
262  auto price = static_cast<GNCPrice*> (node->data);
263  auto price_time = gnc_price_get_time64 (price);
264  auto name_str = gnc_commodity_get_printname (tmp_commodity);
265 
266  if (oldest > price_time)
267  oldest = price_time;
268 
269  auto date_str = qof_print_date (price_time);
270  auto num_str = g_strdup_printf ("%d", num);
271 
272  GtkTreeIter iter;
273  gtk_list_store_append (GTK_LIST_STORE(model), &iter);
274  gtk_list_store_set (GTK_LIST_STORE(model), &iter, PRICED_FULL_NAME, name_str,
275  PRICED_COMM, tmp_commodity, PRICED_DATE, date_str, PRICED_COUNT, num_str, -1);
276 
277  g_free (date_str);
278  g_free (num_str);
279  g_list_free_full (list, (GDestroyNotify)gnc_price_unref);
280  }
281  }
282  g_list_free (commodity_list);
283  }
284  g_list_free (namespace_list);
285 
286  return oldest;
287 }
288 
289 static GList *
290 gnc_prices_dialog_get_commodities (GtkTreeView *view)
291 {
292  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
293  auto selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
294  auto list = gtk_tree_selection_get_selected_rows (selection, &model);
295  GList *comm_list = nullptr;
296 
297  // Walk the list
298  for (auto row = g_list_first (list); row; row = g_list_next (row))
299  {
300  auto path = static_cast<GtkTreePath *> (row->data);
301  GtkTreeIter iter;
302  if (gtk_tree_model_get_iter (model, &iter, path))
303  {
304  gnc_commodity *comm;
305  gtk_tree_model_get (model, &iter, PRICED_COMM, &comm, -1);
306  comm_list = g_list_prepend (comm_list, comm);
307  }
308  }
309  g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
310 
311  return g_list_reverse (comm_list);
312 }
313 
314 static void
315 change_source_flag (PriceRemoveSourceFlags source, gboolean set, gpointer data)
316 {
317  auto pdb_dialog = static_cast<PricesDialog *> (data);
318  GtkWidget *w = gtk_dialog_get_widget_for_response (GTK_DIALOG(pdb_dialog->remove_dialog), GTK_RESPONSE_OK);
319  gboolean enable_button;
320 
321  if (set)
322  pdb_dialog->remove_source = pdb_dialog->remove_source | source;
323  else
324  pdb_dialog->remove_source = pdb_dialog->remove_source & (~source);
325 
326  // Check if we have the required options to enable OK button
327  enable_button = (pdb_dialog->remove_source > 8 ? TRUE : FALSE); // commodities flag is 8
328  gtk_widget_set_sensitive (w, enable_button);
329 
330  DEBUG("Source is: %d, remove_source is %d", source, pdb_dialog->remove_source);
331 }
332 
333 static void
334 check_event_fq_cb (GtkWidget *widget, gpointer data)
335 {
336  auto pdb_dialog = static_cast<PricesDialog *> (data);
337  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
338 
339  change_source_flag (PRICE_REMOVE_SOURCE_FQ, active, pdb_dialog);
340 }
341 
342 static void
343 check_event_user_cb (GtkWidget *widget, gpointer data)
344 {
345  auto pdb_dialog = static_cast<PricesDialog *> (data);
346  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
347 
348  change_source_flag (PRICE_REMOVE_SOURCE_USER, active, pdb_dialog);
349 }
350 
351 static void
352 check_event_app_cb (GtkWidget *widget, gpointer data)
353 {
354  auto pdb_dialog = static_cast<PricesDialog *> (data);
355  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
356 
357  change_source_flag (PRICE_REMOVE_SOURCE_APP, active, pdb_dialog);
358 }
359 
360 static void
361 selection_changed_cb (GtkTreeSelection *selection, gpointer data)
362 {
363  auto pdb_dialog = static_cast<PricesDialog *> (data);
364  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->remove_view));
365  auto rows = gtk_tree_selection_get_selected_rows (selection, &model);
366  gboolean have_rows = (gnc_list_length_cmp (rows, 0));
367 
368  change_source_flag (PRICE_REMOVE_SOURCE_COMM, have_rows, pdb_dialog);
369  g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
370 }
371 
372 static GDate
373 get_fiscal_end_date (void)
374 {
375  time64 end;
376  char datebuff[MAX_DATE_LENGTH + 1];
377  memset (datebuff, 0, sizeof(datebuff));
378  end = gnc_accounting_period_fiscal_end();
380  gnc_accounting_period_fiscal_end());
381  PINFO("Fiscal end date is %s", datebuff);
382 
383  return time64_to_gdate (end);
384 }
385 
386 void
387 gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
388 {
389  auto pdb_dialog = static_cast<PricesDialog *> (data);
390 
391  ENTER(" ");
392  auto builder = gtk_builder_new();
393  gnc_builder_add_from_file (builder, "dialog-price.glade", "liststore4");
394  gnc_builder_add_from_file (builder, "dialog-price.glade", "deletion_date_dialog");
395 
396  pdb_dialog->remove_dialog = GTK_WIDGET(gtk_builder_get_object (builder, "deletion_date_dialog"));
397 
398  auto box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
399  auto date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
400 
401  gtk_box_pack_start (GTK_BOX (box), date, FALSE, FALSE, 0);
402  gtk_widget_show (date);
403  gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE);
404  auto label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
405  gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label);
406 
407  // Setup the commodity view
408  pdb_dialog->remove_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "commodty_treeview"));
409  auto selection = gtk_tree_view_get_selection (pdb_dialog->remove_view);
410  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
411 
412  // Add Entries column this way as align does not seem to work from builder
413  auto tree_column = gtk_tree_view_column_new();
414  gtk_tree_view_column_set_title (tree_column, _("Entries"));
415  gtk_tree_view_append_column (GTK_TREE_VIEW(pdb_dialog->remove_view), tree_column);
416  gtk_tree_view_column_set_alignment (tree_column, 0.5);
417  gtk_tree_view_column_set_expand (tree_column, TRUE);
418  auto cr = gtk_cell_renderer_text_new();
419  gtk_tree_view_column_pack_start (tree_column, cr, TRUE);
420  // set 'xalign' property of the cell renderer
421  gtk_tree_view_column_set_attributes (tree_column, cr, "text", PRICED_COUNT, NULL);
422  gtk_cell_renderer_set_alignment (cr, 0.5, 0.5);
423 
424  // Load the view and get the earliest date
425  gnc_prices_dialog_load_view (pdb_dialog->remove_view, pdb_dialog->price_db);
426  gtk_tree_selection_select_all (selection);
427  g_signal_connect (selection, "changed", G_CALLBACK(selection_changed_cb), pdb_dialog);
428 
429  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
430 
431  gtk_window_set_transient_for (GTK_WINDOW (pdb_dialog->remove_dialog), GTK_WINDOW (pdb_dialog->window));
432 
433  pdb_dialog->remove_source = PRICE_REMOVE_SOURCE_FQ + PRICE_REMOVE_SOURCE_COMM; // FQ and Commodities highlighted
434  auto button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_fq"));
435  g_signal_connect (button, "toggled", G_CALLBACK (check_event_fq_cb), pdb_dialog);
436  button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_user"));
437  g_signal_connect (button, "toggled", G_CALLBACK (check_event_user_cb), pdb_dialog);
438  button = GTK_WIDGET(gtk_builder_get_object (builder, "checkbutton_app"));
439  g_signal_connect (button, "toggled", G_CALLBACK (check_event_app_cb), pdb_dialog);
440 
441  auto result = gtk_dialog_run (GTK_DIALOG (pdb_dialog->remove_dialog));
442  if (result == GTK_RESPONSE_OK)
443  {
444  const char *fmt = _("Are you sure you want to delete these prices?");
445  auto comm_list = gnc_prices_dialog_get_commodities (pdb_dialog->remove_view);
446 
447  // Are you sure you want to delete the entries and we have commodities
448  if ((g_list_length (comm_list) != 0) && (gnc_verify_dialog (GTK_WINDOW (pdb_dialog->remove_dialog), FALSE, fmt, NULL)))
449  {
450  time64 last;
451  GDate fiscal_end_date = get_fiscal_end_date ();
452  PriceRemoveKeepOptions keep = PRICE_REMOVE_KEEP_NONE;
453 
454  // disconnect the model to the price treeview
455  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
456  g_object_ref (G_OBJECT(model));
457  gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), NULL);
458 
459  DEBUG("deleting prices");
460  last = gnc_date_edit_get_date (GNC_DATE_EDIT (date));
461 
462  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_week"));
463  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
464  keep = PRICE_REMOVE_KEEP_LAST_WEEKLY;
465  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_month"));
466  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
467  keep = PRICE_REMOVE_KEEP_LAST_MONTHLY;
468  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_quarter"));
469  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
470  keep = PRICE_REMOVE_KEEP_LAST_QUARTERLY;
471  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_last_period"));
472  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
473  keep = PRICE_REMOVE_KEEP_LAST_PERIOD;
474  button = GTK_WIDGET(gtk_builder_get_object (builder, "radiobutton_scaled"));
475  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
476  keep = PRICE_REMOVE_KEEP_SCALED;
477 
478  if (keep != PRICE_REMOVE_KEEP_SCALED)
479  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
480  &fiscal_end_date, last,
481  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
482  keep);
483  else
484  {
485  auto tmp_date = time64_to_gdate (last);
486  g_date_subtract_months (&tmp_date, 6);
487  auto tmp = gdate_to_time64 (tmp_date);
488 
489  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
490  &fiscal_end_date, tmp,
491  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
492  PRICE_REMOVE_KEEP_LAST_WEEKLY);
493 
494  g_date_subtract_months (&tmp_date, 6);
495  tmp = gdate_to_time64 (tmp_date);
496 
497  gnc_pricedb_remove_old_prices (pdb_dialog->price_db, comm_list,
498  &fiscal_end_date, tmp,
499  static_cast<PriceRemoveSourceFlags> (pdb_dialog->remove_source),
500  PRICE_REMOVE_KEEP_LAST_MONTHLY);
501  }
502  // reconnect the model to the price treeview
503  gtk_tree_view_set_model (GTK_TREE_VIEW(pdb_dialog->price_tree), model);
504  g_object_unref(G_OBJECT(model));
505  }
506  g_list_free (comm_list);
507  }
508  gnc_gui_refresh_all ();
509  gtk_widget_destroy (pdb_dialog->remove_dialog);
510  g_object_unref (G_OBJECT (builder));
511  LEAVE(" ");
512 }
513 
514 
515 void
516 gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
517 {
518  auto pdb_dialog = static_cast<PricesDialog *> (data);
519  GNCPrice *price = nullptr;
520  gboolean unref_price = FALSE;
521 
522  ENTER(" ");
523  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
524  auto comm_list = gnc_tree_view_price_get_selected_commodities (pdb_dialog->price_tree);
525 
526  if (price_list) // selected row is on a price
527  {
528  price = static_cast<GNCPrice *> (price_list->data);
529  g_list_free (price_list);
530  }
531  else if (comm_list) // selection contains price parent rows
532  {
533  if (!gnc_list_length_cmp (comm_list, 1)) // make sure it is only one parent
534  {
535  auto comm = GNC_COMMODITY (comm_list->data);
536  auto latest_price = gnc_pricedb_lookup_latest_any_currency (pdb_dialog->price_db, comm);
537 
538  if (latest_price)
539  {
540  price = GNC_PRICE (latest_price->data);
541  gnc_price_ref (price);
542 
543  gnc_price_list_destroy (latest_price);
544  }
545 
546  if (!price)
547  {
548  price = gnc_price_create (pdb_dialog->book);
549  gnc_price_set_commodity (price, comm);
550  }
551 
552  unref_price = TRUE;
553  }
554  g_list_free (comm_list);
555  }
556  gnc_price_edit_dialog (pdb_dialog->window, pdb_dialog->session,
557  price, GNC_PRICE_NEW);
558 
559  if (unref_price)
560  gnc_price_unref (price);
561  LEAVE(" ");
562 }
563 
564 
565 void
566 gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data)
567 {
568  auto pdb_dialog = static_cast<PricesDialog *> (data);
569 
570  ENTER(" ");
571  try {
572  GncQuotes quotes;
573  gnc_set_busy_cursor (NULL, TRUE);
574  quotes.fetch (pdb_dialog->book);
575  gnc_unset_busy_cursor (NULL);
576  if (quotes.had_failures())
577  gnc_warning_dialog(GTK_WINDOW(pdb_dialog->window), "%s",
578  quotes.report_failures().c_str());
579  }
580  catch (const GncQuoteException& err)
581  {
582  gnc_unset_busy_cursor(nullptr);
583  PERR("Price retrieval failed: %s", err.what());
584  gnc_error_dialog(GTK_WINDOW(pdb_dialog->window), _("Price retrieval failed: %s"), err.what());
585  }
586 
587  /* Without this, the summary bar on the accounts tab
588  * won't reflect the new prices (bug #522095). */
589  gnc_gui_refresh_all ();
590 
591  LEAVE(" ");
592 }
593 
594 
595 static void
596 gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection,
597  gpointer data)
598 {
599  auto pdb_dialog = static_cast<PricesDialog *> (data);
600 
601  ENTER(" ");
602  auto price_list = gnc_tree_view_price_get_selected_prices (pdb_dialog->price_tree);
603  auto length = g_list_length (price_list);
604  g_list_free (price_list);
605 
606  auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(pdb_dialog->price_tree));
607  auto rows = gtk_tree_selection_get_selected_rows (treeselection, &model);
608 
609  // if selected rows greater than length, parents must of been selected also
610  if (g_list_length (rows) > length)
611  length = 0;
612 
613  g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
614 
615  gtk_widget_set_sensitive (pdb_dialog->edit_button,
616  length == 1);
617  gtk_widget_set_sensitive (pdb_dialog->remove_button,
618  length >= 1);
619  gtk_widget_set_sensitive (pdb_dialog->add_button,
620  length <= 1);
621  LEAVE("%d prices selected", length);
622 }
623 
624 
625 static gboolean
626 gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
627  gpointer data)
628 {
629  auto pdb_dialog = static_cast<PricesDialog *> (data);
630 
631  /* Never show the template list */
632  auto name = gnc_commodity_namespace_get_name (name_space);
633  if (g_strcmp0 (name, GNC_COMMODITY_NS_TEMPLATE) == 0)
634  return FALSE;
635 
636  /* See if this namespace has commodities */
637  auto cm_list = gnc_commodity_namespace_get_commodity_list (name_space);
638  auto rv = false;
639  for (auto item = cm_list; !rv && item; item = g_list_next (item))
640  {
641  /* For each commodity, see if there are prices */
642  auto comm = static_cast<gnc_commodity *> (item->data);
643  if (gnc_pricedb_has_prices (pdb_dialog->price_db, comm, nullptr))
644  rv = true;
645  }
646 
647  g_list_free (cm_list);
648  return rv;
649 }
650 
651 
652 static gboolean
653 gnc_price_dialog_filter_cm_func (gnc_commodity *commodity,
654  gpointer data)
655 {
656  auto pdb_dialog = static_cast<PricesDialog *> (data);
657 
658  /* Show any commodity that has prices */
659  return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL);
660 }
661 
662 
663 static void
664 row_activated_cb (GtkTreeView *view, GtkTreePath *path,
665  GtkTreeViewColumn *column, gpointer data)
666 {
667  GtkTreeModel *model;
668  GtkTreeIter iter;
669 
670  g_return_if_fail(view);
671 
672  model = gtk_tree_view_get_model(view);
673  if (gtk_tree_model_get_iter(model, &iter, path))
674  {
675  if (gtk_tree_model_iter_has_child(model, &iter))
676  {
677  /* There are children, so it's not a price.
678  * Just expand or collapse the row. */
679  if (gtk_tree_view_row_expanded(view, path))
680  gtk_tree_view_collapse_row(view, path);
681  else
682  gtk_tree_view_expand_row(view, path, FALSE);
683  }
684  else
685  /* It's a price, so click the Edit button. */
686  gnc_prices_dialog_edit_clicked(GTK_WIDGET(view), data);
687  }
688 }
689 
690 
691 static void
692 gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog)
693 {
694  GtkWidget *window, *scrolled_window;
695  GtkBuilder *builder;
696  GtkTreeView *view;
697  GtkTreeSelection *selection;
698 
699  ENTER(" ");
700  builder = gtk_builder_new();
701  gnc_builder_add_from_file (builder, "dialog-price.glade", "prices_window");
702 
703  window = GTK_WIDGET(gtk_builder_get_object (builder, "prices_window"));
704  pdb_dialog->window = window;
705 
706  // Set the name for this dialog so it can be easily manipulated with css
707  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-price-edit");
708  gnc_widget_style_context_add_class (GTK_WIDGET(window), "gnc-class-securities");
709 
710  pdb_dialog->session = gnc_get_current_session();
711  pdb_dialog->book = qof_session_get_book(pdb_dialog->session);
712  pdb_dialog->price_db = gnc_pricedb_get_db(pdb_dialog->book);
713 
714  g_signal_connect (pdb_dialog->window, "delete-event",
715  G_CALLBACK(gnc_prices_dialog_delete_event_cb), pdb_dialog);
716 
717  g_signal_connect (pdb_dialog->window, "key_press_event",
718  G_CALLBACK (gnc_prices_dialog_key_press_cb), pdb_dialog);
719 
720  /* price tree */
721  scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "price_list_window"));
722  view = gnc_tree_view_price_new(pdb_dialog->book,
723  "state-section", STATE_SECTION,
724  "show-column-menu", TRUE,
725  NULL);
726  pdb_dialog->price_tree = GNC_TREE_VIEW_PRICE(view);
727  gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view));
728  gnc_tree_view_price_set_filter (pdb_dialog->price_tree,
729  gnc_price_dialog_filter_ns_func,
730  gnc_price_dialog_filter_cm_func,
731  NULL,
732  pdb_dialog, NULL);
733 
734  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
735  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
736  g_signal_connect (G_OBJECT (selection), "changed",
737  G_CALLBACK (gnc_prices_dialog_selection_changed), pdb_dialog);
738 
739  g_signal_connect (G_OBJECT (view), "row-activated",
740  G_CALLBACK (row_activated_cb), pdb_dialog);
741 
742  /* buttons */
743  {
744  GtkWidget *button;
745 
746  button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button"));
747  pdb_dialog->edit_button = button;
748 
749  button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
750  pdb_dialog->remove_button = button;
751 
752  button = GTK_WIDGET(gtk_builder_get_object (builder, "add_button"));
753  pdb_dialog->add_button = button;
754 
756  {
757  button = GTK_WIDGET(gtk_builder_get_object (builder, "get_quotes_button"));
758  gtk_widget_set_sensitive(button, FALSE);
759  }
760  /* default to 'close' button */
761  button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
762  gtk_widget_grab_default (button);
763  gtk_widget_grab_focus (button);
764 
765  }
766 
767  g_signal_connect (pdb_dialog->window, "destroy",
768  G_CALLBACK(gnc_prices_dialog_destroy_cb), pdb_dialog);
769 
770  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
771  g_object_unref(G_OBJECT(builder));
772 
773  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window), GTK_WINDOW (parent));
774  LEAVE(" ");
775 }
776 
777 
778 static void
779 close_handler (gpointer user_data)
780 {
781  auto pdb_dialog = static_cast<PricesDialog *> (user_data);
782 
783  ENTER(" ");
784  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->window));
785  gtk_widget_destroy (GTK_WIDGET (pdb_dialog->window));
786  LEAVE(" ");
787 }
788 
789 
790 static void
791 refresh_handler (GHashTable *changes, gpointer user_data)
792 {
793  ENTER(" ");
794  LEAVE(" ");
795 }
796 
797 
798 static gboolean
799 show_handler (const char *klass, gint component_id,
800  gpointer user_data, gpointer iter_data)
801 {
802  auto pdb_dialog = static_cast<PricesDialog *> (user_data);
803 
804  ENTER(" ");
805  if (!pdb_dialog)
806  {
807  LEAVE("no data structure");
808  return(FALSE);
809  }
810 
811  gtk_window_present (GTK_WINDOW(pdb_dialog->window));
812  LEAVE(" ");
813  return(TRUE);
814 }
815 
816 
817 gboolean
818 gnc_prices_dialog_key_press_cb (GtkWidget *widget, GdkEventKey *event,
819  gpointer data)
820 {
821  auto pdb_dialog = static_cast<PricesDialog *> (data);
822 
823  if (event->keyval == GDK_KEY_Escape)
824  {
825  close_handler (pdb_dialog);
826  return TRUE;
827  }
828  else
829  return FALSE;
830 }
831 
832 
833 /********************************************************************\
834  * gnc_prices_dialog *
835  * opens up a window showing all price information *
836  * *
837  * Args: parent - the parent of the window to be created *
838  * Return: nothing *
839 \********************************************************************/
840 void
841 gnc_prices_dialog (GtkWidget * parent)
842 {
843  PricesDialog *pdb_dialog;
844  gint component_id;
845 
846  ENTER(" ");
847  if (gnc_forall_gui_components (DIALOG_PRICE_DB_CM_CLASS, show_handler, NULL))
848  {
849  LEAVE("existing dialog raised");
850  return;
851  }
852 
853  pdb_dialog = g_new0 (PricesDialog, 1);
854 
855  gnc_prices_dialog_create (parent, pdb_dialog);
856 
857  component_id = gnc_register_gui_component (DIALOG_PRICE_DB_CM_CLASS,
858  refresh_handler, close_handler,
859  pdb_dialog);
860  gnc_gui_component_set_session (component_id, pdb_dialog->session);
861 
862  gtk_widget_grab_focus (GTK_WIDGET(pdb_dialog->price_tree));
863 
864  gtk_widget_show (pdb_dialog->window);
865  LEAVE(" ");
866 }
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
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
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:1211
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.
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:609
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
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...
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
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:1253
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:261
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...
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:573
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.