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