GnuCash  5.6-150-g038405b370+
dialog-commodities.cpp
1 /********************************************************************\
2  * dialog-commodities.c -- commodities dialog *
3  * Copyright (C) 2001 Gnumatic, Inc. *
4  * Author: Dave Peticolas <dave@krondo.com> *
5  * Copyright (C) 2003,2005 David Hampton *
6  * *
7  * This program is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU General Public License as *
9  * published by the Free Software Foundation; either version 2 of *
10  * the License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact: *
19  * *
20  * Free Software Foundation Voice: +1-617-542-5942 *
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22  * Boston, MA 02110-1301, USA gnu@gnu.org *
23 \********************************************************************/
24 
25 #include <config.h>
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 
30 #include "dialog-commodity.h"
31 #include "dialog-utils.h"
32 #include "gnc-commodity.h"
33 #include "gnc-component-manager.h"
34 #include "qof.h"
36 #include "gnc-prefs.h"
37 #include "gnc-ui.h"
38 #include "gnc-ui-util.h"
39 #include "gnc-gnome-utils.h"
40 #include "gnc-session.h"
41 #include "gnc-warnings.h"
42 #include "Account.hpp"
43 
44 #include <vector>
45 #include <string>
46 
47 #define DIALOG_COMMODITIES_CM_CLASS "dialog-commodities"
48 #define STATE_SECTION "dialogs/edit_commodities"
49 #define GNC_PREFS_GROUP "dialogs.commodities"
50 #define GNC_PREF_INCL_ISO "include-iso"
51 
52 /* This static indicates the debugging module that this .o belongs to. */
53 /* static short module = MOD_GUI; */
54 
55 typedef struct
56 {
57  GtkWidget * window;
58  QofSession *session;
59  QofBook *book;
60 
61  GncTreeViewCommodity * commodity_tree;
62  GtkWidget * edit_button;
63  GtkWidget * remove_button;
64  gboolean show_currencies;
65 
66  gboolean is_new;
68 
69 
70 void gnc_commodities_window_destroy_cb (GtkWidget *object, CommoditiesDialog *cd);
71 
72 extern "C" {
73 void gnc_commodities_dialog_add_clicked (GtkWidget *widget, gpointer data);
74 void gnc_commodities_dialog_edit_clicked (GtkWidget *widget, gpointer data);
75 void gnc_commodities_dialog_remove_clicked (GtkWidget *widget, gpointer data);
76 void gnc_commodities_dialog_close_clicked (GtkWidget *widget, gpointer data);
77 void gnc_commodities_show_currencies_toggled (GtkToggleButton *toggle, CommoditiesDialog *cd);
78 }
79 
80 gboolean gnc_commodities_window_key_press_cb (GtkWidget *widget,
81  GdkEventKey *event,
82  gpointer data);
83 
84 
85 void
86 gnc_commodities_window_destroy_cb (GtkWidget *object, CommoditiesDialog *cd)
87 {
88  gnc_unregister_gui_component_by_data (DIALOG_COMMODITIES_CM_CLASS, cd);
89 
90  if (cd->window)
91  {
92  gtk_widget_destroy (cd->window);
93  cd->window = NULL;
94  }
95  g_free (cd);
96 }
97 
98 static gboolean
99 gnc_commodities_window_delete_event_cb (GtkWidget *widget,
100  GdkEvent *event,
101  gpointer data)
102 {
103  auto cd = static_cast<CommoditiesDialog*>(data);
104  // this cb allows the window size to be saved on closing with the X
105  gnc_save_window_size (GNC_PREFS_GROUP,
106  GTK_WINDOW(cd->window));
107  return FALSE;
108 }
109 
110 void
111 gnc_commodities_dialog_edit_clicked (GtkWidget *widget, gpointer data)
112 {
113  auto cd = static_cast<CommoditiesDialog*>(data);
114  gnc_commodity *commodity;
115 
116  commodity = gnc_tree_view_commodity_get_selected_commodity (cd->commodity_tree);
117  if (commodity == NULL)
118  return;
119 
120  if (gnc_ui_edit_commodity_modal (commodity, cd->window))
121  {
122  gnc_tree_view_commodity_select_commodity (cd->commodity_tree, commodity);
123  gnc_gui_refresh_all ();
124  }
125 }
126 
127 static void
128 row_activated_cb (GtkTreeView *view, GtkTreePath *path,
129  GtkTreeViewColumn *column, CommoditiesDialog *cd)
130 {
131  GtkTreeModel *model;
132  GtkTreeIter iter;
133 
134  g_return_if_fail(view);
135 
136  model = gtk_tree_view_get_model(view);
137  if (gtk_tree_model_get_iter(model, &iter, path))
138  {
139  if (gtk_tree_model_iter_has_child(model, &iter))
140  {
141  /* There are children, so it's not a commodity.
142  * Just expand or collapse the row. */
143  if (gtk_tree_view_row_expanded(view, path))
144  gtk_tree_view_collapse_row(view, path);
145  else
146  gtk_tree_view_expand_row(view, path, FALSE);
147  }
148  else
149  /* It's a commodity, so click the Edit button. */
150  gnc_commodities_dialog_edit_clicked (NULL, cd);
151  }
152 }
153 
154 void
155 gnc_commodities_dialog_remove_clicked (GtkWidget *widget, gpointer data)
156 {
157  auto cd = static_cast<CommoditiesDialog*>(data);
158  GNCPriceDB *pdb;
159  GList *node;
160  GList *prices;
161  gnc_commodity *commodity;
162  GtkWidget *dialog;
163  const gchar *message, *warning;
164  gint response;
165 
166  commodity = gnc_tree_view_commodity_get_selected_commodity (cd->commodity_tree);
167  if (commodity == NULL)
168  return;
169 
170  std::vector<Account*> commodity_accounts;
171 
172  gnc_account_foreach_descendant (gnc_book_get_root_account(cd->book),
173  [commodity, &commodity_accounts](auto acct)
174  {
175  if (commodity == xaccAccountGetCommodity (acct))
176  commodity_accounts.push_back (acct);
177  });
178 
179  /* FIXME check for transaction references */
180 
181  if (!commodity_accounts.empty())
182  {
183  std::string msg{_("This commodity is currently used by the following accounts. You may "
184  "not delete it.\n")};
185 
186  for (const auto acct : commodity_accounts)
187  {
188  auto full_name = gnc_account_get_full_name (acct);
189  msg.append ("\n* ").append (full_name);
190  g_free (full_name);
191  }
192 
193  gnc_warning_dialog (GTK_WINDOW (cd->window), "%s", msg.c_str());
194  return;
195  }
196 
197  pdb = gnc_pricedb_get_db (cd->book);
198  prices = gnc_pricedb_get_prices (pdb, commodity, NULL);
199  if (prices)
200  {
201  message = _("This commodity has price quotes. Are "
202  "you sure you want to delete the selected "
203  "commodity and its price quotes?");
204  warning = GNC_PREF_WARN_PRICE_COMM_DEL_QUOTES;
205  }
206  else
207  {
208  message = _("Are you sure you want to delete the "
209  "selected commodity?");
210  warning = GNC_PREF_WARN_PRICE_COMM_DEL;
211  }
212 
213  dialog = gtk_message_dialog_new (GTK_WINDOW(cd->window),
214  GTK_DIALOG_DESTROY_WITH_PARENT,
215  GTK_MESSAGE_QUESTION,
216  GTK_BUTTONS_NONE,
217  "%s", _("Delete commodity?"));
218  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
219  "%s", message);
220  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
221  _("_Cancel"), GTK_RESPONSE_CANCEL,
222  _("_Delete"), GTK_RESPONSE_OK,
223  (gchar *)NULL);
224  response = gnc_dialog_run (GTK_DIALOG(dialog), warning);
225  gtk_widget_destroy (dialog);
226 
227  if (response == GTK_RESPONSE_OK)
228  {
229  gnc_commodity_table *ct;
230 
231  ct = gnc_commodity_table_get_table (cd->book);
232  for (node = prices; node; node = node->next)
233  gnc_pricedb_remove_price(pdb, GNC_PRICE(node->data));
234 
235  gnc_commodity_table_remove (ct, commodity);
236  gnc_commodity_destroy (commodity);
237  commodity = NULL;
238 
239  // to be consistent, unselect all after remove
240  gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW(cd->commodity_tree)));
241  }
242 
243  gnc_price_list_destroy(prices);
244  gnc_gui_refresh_all ();
245 }
246 
247 void
248 gnc_commodities_dialog_add_clicked (GtkWidget *widget, gpointer data)
249 {
250  auto cd = static_cast<CommoditiesDialog*>(data);
251  gnc_commodity *commodity;
252  gnc_commodity *ret_commodity;
253  const char *name_space;
254 
255  commodity = gnc_tree_view_commodity_get_selected_commodity (cd->commodity_tree);
256  if (commodity)
257  name_space = gnc_commodity_get_namespace (commodity);
258  else
259  name_space = NULL;
260 
261  ret_commodity = gnc_ui_new_commodity_modal (name_space, cd->window);
262  gnc_tree_view_commodity_select_commodity (cd->commodity_tree, ret_commodity);
263 }
264 
265 void
266 gnc_commodities_dialog_close_clicked (GtkWidget *widget, gpointer data)
267 {
268  auto cd = static_cast<CommoditiesDialog*>(data);
269 
270  gnc_close_gui_component_by_data (DIALOG_COMMODITIES_CM_CLASS, cd);
271 }
272 
273 static void
274 gnc_commodities_dialog_selection_changed (GtkTreeSelection *selection,
275  CommoditiesDialog *cd)
276 {
277  gboolean remove_ok;
278  gnc_commodity *commodity;
279 
280  commodity = gnc_tree_view_commodity_get_selected_commodity (cd->commodity_tree);
281  remove_ok = commodity && !gnc_commodity_is_iso(commodity);
282  gtk_widget_set_sensitive (cd->edit_button, commodity != NULL);
283  gtk_widget_set_sensitive (cd->remove_button, remove_ok);
284 }
285 
286 void
287 gnc_commodities_show_currencies_toggled (GtkToggleButton *toggle,
288  CommoditiesDialog *cd)
289 {
290  cd->show_currencies = gtk_toggle_button_get_active (toggle);
291  gnc_tree_view_commodity_refilter (cd->commodity_tree);
292 }
293 
294 static gboolean
295 gnc_commodities_dialog_filter_ns_func (gnc_commodity_namespace *name_space,
296  gpointer data)
297 {
298  auto cd = static_cast<CommoditiesDialog*>(data);
299  const gchar *name;
300  GList *list;
301 
302  /* Never show the template list */
303  name = gnc_commodity_namespace_get_name (name_space);
304  if (g_strcmp0 (name, GNC_COMMODITY_NS_TEMPLATE) == 0)
305  return FALSE;
306 
307  /* Check whether or not to show commodities */
308  if (!cd->show_currencies && gnc_commodity_namespace_is_iso(name))
309  return FALSE;
310 
311  /* Show any other namespace that has commodities */
313  gboolean rv = (list != NULL);
314  g_list_free (list);
315  return rv;
316 }
317 
318 static gboolean
319 gnc_commodities_dialog_filter_cm_func (gnc_commodity *commodity,
320  gpointer data)
321 {
322  auto cd = static_cast<CommoditiesDialog*>(data);
323 
324  if (cd->show_currencies)
325  return TRUE;
326  return !gnc_commodity_is_iso(commodity);
327 }
328 
329 static void
330 gnc_commodities_dialog_create (GtkWidget * parent, CommoditiesDialog *cd)
331 {
332  GtkWidget *button;
333  GtkWidget *scrolled_window;
334  GtkBuilder *builder;
335  GtkTreeView *view;
336  GtkTreeSelection *selection;
337 
338  builder = gtk_builder_new();
339  gnc_builder_add_from_file (builder, "dialog-commodities.glade", "securities_window");
340 
341  cd->window = GTK_WIDGET(gtk_builder_get_object (builder, "securities_window"));
342  cd->session = gnc_get_current_session();
343  cd->book = qof_session_get_book(cd->session);
344  cd->show_currencies = gnc_prefs_get_bool(GNC_PREFS_GROUP, GNC_PREF_INCL_ISO);
345 
346  // Set the name for this dialog so it can be easily manipulated with css
347  gtk_widget_set_name (GTK_WIDGET(cd->window), "gnc-id-commodity");
348  gnc_widget_style_context_add_class (GTK_WIDGET(cd->window), "gnc-class-securities");
349 
350  /* buttons */
351  cd->remove_button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button"));
352  cd->edit_button = GTK_WIDGET(gtk_builder_get_object (builder, "edit_button"));
353 
354  /* commodity tree */
355  scrolled_window = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_list_window"));
356  view = gnc_tree_view_commodity_new(cd->book,
357  "state-section", STATE_SECTION,
358  "show-column-menu", TRUE,
359  NULL);
360  cd->commodity_tree = GNC_TREE_VIEW_COMMODITY(view);
361  gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(view));
362  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cd->commodity_tree), TRUE);
363  gnc_tree_view_commodity_set_filter (cd->commodity_tree,
364  gnc_commodities_dialog_filter_ns_func,
365  gnc_commodities_dialog_filter_cm_func,
366  cd, NULL);
367  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
368  g_signal_connect (G_OBJECT (selection), "changed",
369  G_CALLBACK (gnc_commodities_dialog_selection_changed), cd);
370 
371  g_signal_connect (G_OBJECT (cd->commodity_tree), "row-activated",
372  G_CALLBACK (row_activated_cb), cd);
373 
374  /* Show currency button */
375  button = GTK_WIDGET(gtk_builder_get_object (builder, "show_currencies_button"));
376  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), cd->show_currencies);
377 
378  /* default to 'close' button */
379  button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
380  gtk_widget_grab_default (button);
381  gtk_widget_grab_focus (button);
382 
383  g_signal_connect (cd->window, "destroy",
384  G_CALLBACK(gnc_commodities_window_destroy_cb), cd);
385 
386  g_signal_connect (cd->window, "delete-event",
387  G_CALLBACK(gnc_commodities_window_delete_event_cb), cd);
388 
389  g_signal_connect (cd->window, "key_press_event",
390  G_CALLBACK (gnc_commodities_window_key_press_cb), cd);
391 
392  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, cd);
393  g_object_unref (G_OBJECT(builder));
394 
395  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(cd->window), GTK_WINDOW(parent));
396 }
397 
398 static void
399 close_handler (gpointer user_data)
400 {
401  auto cd = static_cast<CommoditiesDialog*>(user_data);
402 
403  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(cd->window));
404 
405  gnc_prefs_set_bool (GNC_PREFS_GROUP, GNC_PREF_INCL_ISO, cd->show_currencies);
406 
407  gtk_widget_destroy (cd->window);
408 }
409 
410 static void
411 refresh_handler (GHashTable *changes, gpointer user_data)
412 {
413  auto cd = static_cast<CommoditiesDialog*>(user_data);
414 
415  g_return_if_fail(cd != NULL);
416 
417  gnc_tree_view_commodity_refilter (cd->commodity_tree);
418 }
419 
420 static gboolean
421 show_handler (const char *klass, gint component_id,
422  gpointer user_data, gpointer iter_data)
423 {
424  auto cd = static_cast<CommoditiesDialog*>(user_data);
425 
426  if (!cd)
427  return(FALSE);
428  gtk_window_present (GTK_WINDOW(cd->window));
429  return(TRUE);
430 }
431 
432 gboolean
433 gnc_commodities_window_key_press_cb (GtkWidget *widget, GdkEventKey *event,
434  gpointer data)
435 {
436  auto cd = static_cast<CommoditiesDialog*>(data);
437 
438  if (event->keyval == GDK_KEY_Escape)
439  {
440  close_handler (cd);
441  return TRUE;
442  }
443  else
444  return FALSE;
445 }
446 
447 /********************************************************************\
448  * gnc_commodities_dialog *
449  * opens up a window to edit price information *
450  * *
451  * Args: parent - the parent of the window to be created *
452  * Return: nothing *
453 \********************************************************************/
454 void
455 gnc_commodities_dialog (GtkWidget * parent)
456 {
457  gint component_id;
458 
459  if (gnc_forall_gui_components (DIALOG_COMMODITIES_CM_CLASS,
460  show_handler, NULL))
461  return;
462 
463  auto cd = static_cast<CommoditiesDialog*>(g_new0 (CommoditiesDialog, 1));
464 
465  gnc_commodities_dialog_create (parent, cd);
466 
467  component_id = gnc_register_gui_component (DIALOG_COMMODITIES_CM_CLASS,
468  refresh_handler, close_handler,
469  cd);
470  gnc_gui_component_set_session (component_id, cd->session);
471 
472  gtk_widget_grab_focus (GTK_WIDGET(cd->commodity_tree));
473 
474  gtk_widget_show (cd->window);
475 }
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...
GtkTreeView * gnc_tree_view_commodity_new(QofBook *book, const gchar *first_property_name,...)
Create a new commodity tree view.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void gnc_tree_view_commodity_set_filter(GncTreeViewCommodity *view, gnc_tree_view_commodity_ns_filter_func ns_func, gnc_tree_view_commodity_cm_filter_func cm_func, gpointer data, GDestroyNotify destroy)
This function attaches a filter function to the given commodity tree.
utility functions for the GnuCash UI
gnc_commodity * gnc_ui_new_commodity_modal(const char *default_namespace, GtkWidget *parent)
Ask the user to provide the information necessary to create a new commodity.
gboolean gnc_ui_edit_commodity_modal(gnc_commodity *commodity, GtkWidget *parent)
Given an existing commodity, uses the gnc_ui_build_commodity_dialog() routine to build a basic edit d...
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
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.
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3255
void gnc_tree_view_commodity_select_commodity(GncTreeViewCommodity *view, gnc_commodity *commodity)
Select the commodity in the associated commodity tree view.
Account public routines (C++ api)
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Store a boolean value into the preferences backend.
Definition: gnc-prefs.c:277
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
Gnome specific utility functions.
gboolean gnc_pricedb_remove_price(GNCPriceDB *db, GNCPrice *p)
Remove a price from the pricedb and unref the price.
Generic api to store and retrieve preferences.
gnc_commodity * gnc_tree_view_commodity_get_selected_commodity(GncTreeViewCommodity *view)
This function returns the commodity associated with the selected item in the commodity tree view...
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
GtkTreeView implementation for gnucash commodity tree.
void gnc_commodity_table_remove(gnc_commodity_table *table, gnc_commodity *comm)
Remove a commodity from the commodity table.
void gnc_tree_view_commodity_refilter(GncTreeViewCommodity *view)
This function forces the commodity tree filter to be evaluated.
"select" and "new" commodity windows
Commodity handling public routines.
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
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.