GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-price-edit-db.c
1 /********************************************************************\
2  * dialog-price-editor.c -- price selector dialog *
3  * Copyright (C) 2001 Gnumatic, Inc. *
4  * Author: Dave Peticolas <[email protected]> *
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 [email protected] *
24 \********************************************************************/
25 
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <libguile.h>
31 #include "guile-mappings.h"
32 #include <time.h>
33 
34 #include "dialog-utils.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 "gnome-utils/gnc-warnings.h"
49 #include "guile-util.h"
50 #include "engine-helpers-guile.h"
51 #include "swig-runtime.h"
52 
53 
54 #define DIALOG_PRICE_DB_CM_CLASS "dialog-price-edit-db"
55 #define STATE_SECTION "dialogs/edit_prices"
56 #define GNC_PREFS_GROUP "dialogs.pricedb-editor"
57 
58 /* This static indicates the debugging module that this .o belongs to. */
59 static QofLogModule log_module = GNC_MOD_GUI;
60 
61 
62 void gnc_prices_dialog_window_destroy_cb (GtkObject *object, gpointer data);
63 void gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data);
64 void gnc_prices_dialog_response (GtkDialog *dialog, gint response_id, 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 
71 
72 typedef struct
73 {
74  GtkWidget * dialog;
75  QofSession *session;
76  QofBook *book;
77  GNCPriceDB *price_db;
78 
79  GncTreeViewPrice * price_tree;
80 
81  GtkWidget * edit_button;
82  GtkWidget * remove_button;
83 } PricesDialog;
84 
85 
86 void
87 gnc_prices_dialog_window_destroy_cb (GtkObject *object, gpointer data)
88 {
89  PricesDialog *pdb_dialog = data;
90 
91  ENTER(" ");
92  gnc_unregister_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
93 
94  if (pdb_dialog->dialog)
95  {
96  gtk_widget_destroy(pdb_dialog->dialog);
97  pdb_dialog->dialog = NULL;
98  }
99 
100  g_free (pdb_dialog);
101  LEAVE(" ");
102 }
103 
104 
105 void
106 gnc_prices_dialog_close_cb (GtkDialog *dialog, gpointer data)
107 {
108  PricesDialog *pdb_dialog = data;
109 
110  ENTER(" ");
111  gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
112  LEAVE(" ");
113 }
114 
115 
116 void
117 gnc_prices_dialog_response (GtkDialog *dialog, gint response_id, gpointer data)
118 {
119  PricesDialog *pdb_dialog = data;
120 
121  ENTER(" ");
122  gnc_close_gui_component_by_data (DIALOG_PRICE_DB_CM_CLASS, pdb_dialog);
123  LEAVE(" ");
124 }
125 
126 
127 void
128 gnc_prices_dialog_edit_clicked (GtkWidget *widget, gpointer data)
129 {
130  PricesDialog *pdb_dialog = data;
131  GList *price_list;
132 
133  ENTER(" ");
134  price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
135  if (!price_list)
136  {
137  LEAVE("no price selected");
138  return;
139  }
140  if (g_list_next(price_list))
141  {
142  g_list_free(price_list);
143  LEAVE("too many prices selected");
144  return;
145  }
146 
147  gnc_price_edit_dialog (pdb_dialog->dialog, pdb_dialog->session,
148  price_list->data, GNC_PRICE_EDIT);
149  g_list_free(price_list);
150  LEAVE(" ");
151 }
152 
153 
154 static void
155 remove_helper(GNCPrice *price, GNCPriceDB *pdb)
156 {
157  gnc_pricedb_remove_price (pdb, price);
158 }
159 
160 
161 void
162 gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
163 {
164  PricesDialog *pdb_dialog = data;
165  GList *price_list;
166  gint length, response;
167  GtkWidget *dialog;
168 
169  ENTER(" ");
170  price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
171  if (!price_list)
172  {
173  LEAVE("no price selected");
174  return;
175  }
176 
177  length = g_list_length(price_list);
178  if (length > 1)
179  {
180  gchar *message;
181 
182  message = g_strdup_printf
183  (/* Translators: %d is the number of prices. This
184  is a ngettext(3) message. */
185  ngettext("Are you sure you want to delete the selected price?",
186  "Are you sure you want to delete the %d selected prices?",
187  length),
188  length);
189  dialog = gtk_message_dialog_new(GTK_WINDOW(pdb_dialog->dialog),
190  GTK_DIALOG_DESTROY_WITH_PARENT,
191  GTK_MESSAGE_QUESTION,
192  GTK_BUTTONS_NONE,
193  "%s", _("Delete prices?"));
194  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
195  "%s", message);
196  g_free(message);
197  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
198  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
199  GTK_STOCK_DELETE, GTK_RESPONSE_YES,
200  (gchar *)NULL);
201  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
202  response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_PRICE_QUOTES_DEL);
203  gtk_widget_destroy(dialog);
204  }
205  else
206  {
207  response = GTK_RESPONSE_YES;
208  }
209 
210  if (response == GTK_RESPONSE_YES)
211  {
212  g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db);
213  }
214  g_list_free(price_list);
215  LEAVE(" ");
216 }
217 
218 
219 void
220 gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
221 {
222  PricesDialog *pdb_dialog = data;
223  GtkBuilder *builder;
224  GtkWidget *dialog, *button, *date, *label, *box;
225  gint result;
226  gboolean delete_user, delete_last;
227 
228  ENTER(" ");
229  builder = gtk_builder_new();
230  gnc_builder_add_from_file (builder, "dialog-price.glade", "Deletion Date");
231 
232  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Deletion Date"));
233 
234  box = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
235  date = gnc_date_edit_new (time (NULL), FALSE, FALSE);
236 
237  gtk_box_pack_start (GTK_BOX (box), date, TRUE, TRUE, 0);
238  gtk_widget_show (date);
239  gtk_entry_set_activates_default(GTK_ENTRY(GNC_DATE_EDIT(date)->date_entry), TRUE);
240  label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
241  gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date), label);
242 
243  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
244 
245  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (pdb_dialog->dialog));
246 
247  result = gtk_dialog_run (GTK_DIALOG (dialog));
248  if (result == GTK_RESPONSE_OK)
249  {
250  Timespec ts;
251 
252  DEBUG("deleting prices");
253  ts.tv_sec = gnc_date_edit_get_date (GNC_DATE_EDIT (date));
254  ts.tv_nsec = 0;
255 
256  button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_manual"));
257  delete_user = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
258  button = GTK_WIDGET(gtk_builder_get_object (builder, "delete_last"));
259  delete_last = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
260 
261  gnc_pricedb_remove_old_prices(pdb_dialog->price_db, ts,
262  delete_user, delete_last);
263  }
264 
265  gtk_widget_destroy(dialog);
266  LEAVE(" ");
267 }
268 
269 
270 void
271 gnc_prices_dialog_add_clicked (GtkWidget *widget, gpointer data)
272 {
273  PricesDialog *pdb_dialog = data;
274  GNCPrice *price = NULL;
275  GList *price_list;
276 
277  ENTER(" ");
278  price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
279  if (price_list)
280  {
281  price = price_list->data;
282  g_list_free(price_list);
283  }
284  gnc_price_edit_dialog (pdb_dialog->dialog, pdb_dialog->session,
285  price, GNC_PRICE_NEW);
286  LEAVE(" ");
287 }
288 
289 
290 void
291 gnc_prices_dialog_get_quotes_clicked (GtkWidget *widget, gpointer data)
292 {
293  PricesDialog *pdb_dialog = data;
294  SCM quotes_func;
295  SCM book_scm;
296  SCM scm_window;
297 
298  ENTER(" ");
299  quotes_func = scm_c_eval_string ("gnc:book-add-quotes");
300  if (!scm_is_procedure (quotes_func))
301  {
302  LEAVE(" no procedure");
303  return;
304  }
305 
306  book_scm = gnc_book_to_scm (pdb_dialog->book);
307  if (scm_is_true (scm_not (book_scm)))
308  {
309  LEAVE("no book");
310  return;
311  }
312 
313  scm_window = SWIG_NewPointerObj(pdb_dialog->dialog,
314  SWIG_TypeQuery("_p_GtkWidget"), 0);
315 
316  gnc_set_busy_cursor (NULL, TRUE);
317  scm_call_2 (quotes_func, scm_window, book_scm);
318  gnc_unset_busy_cursor (NULL);
319 
320  /* Without this, the summary bar on the accounts tab
321  * won't reflect the new prices (bug #522095). */
322  gnc_gui_refresh_all ();
323 
324  LEAVE(" ");
325 }
326 
327 
328 static void
329 gnc_prices_dialog_selection_changed (GtkTreeSelection *treeselection,
330  gpointer data)
331 {
332  PricesDialog *pdb_dialog = data;
333  GList *price_list;
334  gint length;
335 
336  ENTER(" ");
337  price_list = gnc_tree_view_price_get_selected_prices(pdb_dialog->price_tree);
338  length = g_list_length(price_list);
339 
340  gtk_widget_set_sensitive (pdb_dialog->edit_button,
341  length == 1);
342  gtk_widget_set_sensitive (pdb_dialog->remove_button,
343  length >= 1);
344  LEAVE("%d prices selected", length);
345 }
346 
347 
348 static gboolean
349 gnc_price_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
350  gpointer data)
351 {
352  PricesDialog *pdb_dialog = data;
353  const gchar *name;
354  static GList *cm_list;
355  GList *item;
356 
357  /* Never show the template list */
358  name = gnc_commodity_namespace_get_name (name_space);
359  if (g_strcmp0 (name, "template") == 0)
360  return FALSE;
361 
362  /* See if this namespace has commodities */
363  cm_list = gnc_commodity_namespace_get_commodity_list(name_space);
364  for (item = cm_list; item; item = g_list_next(item))
365  {
366 
367  /* For each commodity, see if there are prices */
368  if (gnc_pricedb_has_prices(pdb_dialog->price_db, item->data, NULL))
369  {
370  return TRUE;
371  }
372  }
373 
374  // printf("Namespace %s not visible\n", name);
375  return FALSE;
376 }
377 
378 
379 static gboolean
380 gnc_price_dialog_filter_cm_func (gnc_commodity *commodity,
381  gpointer data)
382 {
383  PricesDialog *pdb_dialog = data;
384 
385  /* Show any commodity that has prices */
386  return gnc_pricedb_has_prices(pdb_dialog->price_db, commodity, NULL);
387 }
388 
389 
390 static void
391 row_activated_cb (GtkTreeView *view, GtkTreePath *path,
392  GtkTreeViewColumn *column, gpointer data)
393 {
394  GtkTreeModel *model;
395  GtkTreeIter iter;
396 
397  g_return_if_fail(view);
398 
399  model = gtk_tree_view_get_model(view);
400  if (gtk_tree_model_get_iter(model, &iter, path))
401  {
402  if (gtk_tree_model_iter_has_child(model, &iter))
403  {
404  /* There are children, so it's not a price.
405  * Just expand or collapse the row. */
406  if (gtk_tree_view_row_expanded(view, path))
407  gtk_tree_view_collapse_row(view, path);
408  else
409  gtk_tree_view_expand_row(view, path, FALSE);
410  }
411  else
412  /* It's a price, so click the Edit button. */
413  gnc_prices_dialog_edit_clicked(GTK_WIDGET(view), data);
414  }
415 }
416 
417 
418 static void
419 gnc_prices_dialog_create (GtkWidget * parent, PricesDialog *pdb_dialog)
420 {
421  GtkWidget *dialog, *scrolled_window;
422  GtkBuilder *builder;
423  GtkTreeView *view;
424  GtkTreeSelection *selection;
425 
426  ENTER(" ");
427  builder = gtk_builder_new();
428  gnc_builder_add_from_file (builder, "dialog-price.glade", "Prices Dialog");
429 
430  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Prices Dialog"));
431  pdb_dialog->dialog = dialog;
432 
433  pdb_dialog->session = gnc_get_current_session();
434  pdb_dialog->book = qof_session_get_book(pdb_dialog->session);
435  pdb_dialog->price_db = gnc_pricedb_get_db(pdb_dialog->book);
436 
437  /* parent */
438  if (parent != NULL)
439  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
440 
441  /* default to 'close' button */
442  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
443 
444  /* price tree */
445  scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "price_list_window"));
446  view = gnc_tree_view_price_new(pdb_dialog->book,
447  "state-section", STATE_SECTION,
448  "show-column-menu", TRUE,
449  NULL);
450  pdb_dialog->price_tree = GNC_TREE_VIEW_PRICE(view);
451  gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view));
452  gnc_tree_view_price_set_filter (pdb_dialog->price_tree,
453  gnc_price_dialog_filter_ns_func,
454  gnc_price_dialog_filter_cm_func,
455  NULL,
456  pdb_dialog, NULL);
457 
458  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
459  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
460  g_signal_connect (G_OBJECT (selection), "changed",
461  G_CALLBACK (gnc_prices_dialog_selection_changed), pdb_dialog);
462 
463  g_signal_connect (G_OBJECT (view), "row-activated",
464  G_CALLBACK (row_activated_cb), pdb_dialog);
465 
466  /* buttons */
467  {
468  GtkWidget *button;
469 
470  button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button"));
471  pdb_dialog->edit_button = button;
472 
473  button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
474  pdb_dialog->remove_button = button;
475 
477  {
478  button = GTK_WIDGET(gtk_builder_get_object (builder, "get_quotes_button"));
479  gtk_widget_set_sensitive(button, FALSE);
480  }
481  }
482 
483  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, pdb_dialog);
484 
485  g_object_unref(G_OBJECT(builder));
486 
487  gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->dialog));
488  LEAVE(" ");
489 }
490 
491 
492 static void
493 close_handler (gpointer user_data)
494 {
495  PricesDialog *pdb_dialog = user_data;
496 
497  ENTER(" ");
498  gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(pdb_dialog->dialog));
499 
500  gtk_widget_destroy (GTK_WIDGET (pdb_dialog->dialog));
501  LEAVE(" ");
502 }
503 
504 
505 static void
506 refresh_handler (GHashTable *changes, gpointer user_data)
507 {
508  ENTER(" ");
509  LEAVE(" ");
510 }
511 
512 
513 static gboolean
514 show_handler (const char *klass, gint component_id,
515  gpointer user_data, gpointer iter_data)
516 {
517  PricesDialog *pdb_dialog = user_data;
518 
519  ENTER(" ");
520  if (!pdb_dialog)
521  {
522  LEAVE("no data strucure");
523  return(FALSE);
524  }
525 
526  gtk_window_present (GTK_WINDOW(pdb_dialog->dialog));
527  LEAVE(" ");
528  return(TRUE);
529 }
530 
531 
532 /********************************************************************\
533  * gnc_prices_dialog *
534  * opens up a window showing all price information *
535  * *
536  * Args: parent - the parent of the window to be created *
537  * Return: nothing *
538 \********************************************************************/
539 void
540 gnc_prices_dialog (GtkWidget * parent)
541 {
542  PricesDialog *pdb_dialog;
543  gint component_id;
544 
545  ENTER(" ");
546  if (gnc_forall_gui_components (DIALOG_PRICE_DB_CM_CLASS, show_handler, NULL))
547  {
548  LEAVE("existing dialog raised");
549  return;
550  }
551 
552  pdb_dialog = g_new0 (PricesDialog, 1);
553 
554  gnc_prices_dialog_create (parent, pdb_dialog);
555 
556  component_id = gnc_register_gui_component (DIALOG_PRICE_DB_CM_CLASS,
557  refresh_handler, close_handler,
558  pdb_dialog);
559  gnc_gui_component_set_session (component_id, pdb_dialog->session);
560 
561  gtk_widget_grab_focus (GTK_WIDGET(pdb_dialog->price_tree));
562 
563  gtk_widget_show (pdb_dialog->dialog);
564  LEAVE(" ");
565 }
a simple price database for gnucash
utility functions for the GnuCash UI
#define DEBUG(format, args...)
Definition: qoflog.h:255
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
GList * gnc_tree_view_price_get_selected_prices(GncTreeViewPrice *view)
#define ENTER(format, args...)
Definition: qoflog.h:261
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Definition: gnc-pricedb.c:872
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
GtkTreeView implementation for gnucash price tree.
Currency selection widget.
QofBook * qof_session_get_book(const QofSession *session)
All type declarations for the whole Gnucash engine.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Definition: gnc-pricedb.c:1160
gboolean gnc_pricedb_has_prices(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Definition: gnc-pricedb.c:1428
#define LEAVE(format, args...)
Definition: qoflog.h:271
GtkTreeView * gnc_tree_view_price_new(QofBook *book, const gchar *first_property_name,...)
gboolean gnc_quote_source_fq_installed(void)
const gchar * QofLogModule
Definition: qofid.h:89