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