GnuCash  5.6-150-g038405b370+
gnc-currency-edit.cpp
1 /*
2  * gnc-currency-edit.c -- Currency editor widget
3  *
4  * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
5  * All rights reserved.
6  *
7  * Gnucash is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public License
9  * as published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * Gnucash 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 GNU
15  * Library 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 
49 #include <config.h>
50 
51 #include <gtk/gtk.h>
52 #include <glib/gi18n.h>
53 #include <string.h>
54 #include <ctype.h>
55 #include <stdio.h>
56 
57 #include "gnc-currency-edit.h"
58 #include "gnc-commodity.h"
59 #include "gnc-gtk-utils.h"
60 #include "gnc-ui-util.h"
61 #include "gnc-engine.h"
62 #include "dialog-utils.h"
63 
65 static QofLogModule log_module = GNC_MOD_GUI;
66 
67 static void gnc_currency_edit_finalize (GObject *object);
68 static void gnc_currency_edit_mnemonic_changed (GObject *gobject,
69  GParamSpec *pspec,
70  gpointer user_data);
71 static void gnc_currency_edit_active_changed (GtkComboBox *gobject,
72  gpointer user_data);
73 
75 {
76  GtkComboBox combobox;
77 
78  gchar *mnemonic;
79 };
80 
81 G_DEFINE_TYPE(GNCCurrencyEdit, gnc_currency_edit, GTK_TYPE_COMBO_BOX)
82 
83 
86 enum
87 {
88  PROP_0,
89 
90  PROP_GCE_MNEMONIC,
91 
92  N_PROPERTIES
93 };
94 
95 static GParamSpec *obj_properties[N_PROPERTIES] = { nullptr, };
96 
97 static void
98 gnc_currency_edit_set_property (GObject *object,
99  guint property_id,
100  const GValue *value,
101  GParamSpec *pspec)
102 {
103  GNCCurrencyEdit *self = GNC_CURRENCY_EDIT (object);
104 
105  switch (property_id)
106  {
107  case PROP_GCE_MNEMONIC:
108  g_free (self->mnemonic);
109  self->mnemonic = g_value_dup_string (value);
110  DEBUG ("mnemonic: %s\n", self->mnemonic);
111  break;
112 
113  default:
114  /* We don't have any other property... */
115  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
116  break;
117  }
118 }
119 
120 static void
121 gnc_currency_edit_get_property (GObject *object,
122  guint property_id,
123  GValue *value,
124  GParamSpec *pspec)
125 {
126  GNCCurrencyEdit *self = GNC_CURRENCY_EDIT (object);
127 
128  switch (property_id)
129  {
130  case PROP_GCE_MNEMONIC:
131  g_value_set_string (value, self->mnemonic);
132  break;
133 
134  default:
135  /* We don't have any other property... */
136  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137  break;
138  }
139 }
140 
147 static void
148 gnc_currency_edit_class_init (GNCCurrencyEditClass *klass)
149 {
150  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
151 
152  gobject_class->set_property = gnc_currency_edit_set_property;
153  gobject_class->get_property = gnc_currency_edit_get_property;
154  gobject_class->finalize = gnc_currency_edit_finalize;
155 
156  obj_properties[PROP_GCE_MNEMONIC] =
157  g_param_spec_string ("mnemonic",
158  "Active currency's mnemonic",
159  "Active currency's mnemonic",
160  "USD" /* default value */,
161  G_PARAM_READWRITE);
162 
163  g_object_class_install_properties (gobject_class,
164  N_PROPERTIES,
165  obj_properties);
166 }
167 
168 
175 static void
176 gnc_currency_edit_init (GNCCurrencyEdit *gce)
177 {
178  // Set the name for this widget so it can be easily manipulated with css
179  gtk_widget_set_name (GTK_WIDGET(gce), "gnc-id-currency-edit");
180 
181  g_signal_connect (gce, "notify::mnemonic",
182  G_CALLBACK (gnc_currency_edit_mnemonic_changed), gce);
183  g_signal_connect (gce, "changed",
184  G_CALLBACK (gnc_currency_edit_active_changed), gce);
185 }
186 
187 
198 static void
199 gnc_currency_edit_finalize (GObject *object)
200 {
201  g_return_if_fail (object != nullptr);
202  g_return_if_fail (GNC_IS_CURRENCY_EDIT (object));
203 
204  GNCCurrencyEdit *self = GNC_CURRENCY_EDIT(object);
205 
206  g_free (self->mnemonic);
207 
208  G_OBJECT_CLASS(gnc_currency_edit_parent_class)->finalize (object);
209 }
210 
211 
212 static void
213 gnc_currency_edit_mnemonic_changed (GObject *gobject,
214  GParamSpec *pspec,
215  gpointer user_data)
216 {
217 
218  GNCCurrencyEdit *self = GNC_CURRENCY_EDIT (gobject);
219 
220  gnc_commodity *currency = gnc_commodity_table_lookup (gnc_get_current_commodities (),
221  GNC_COMMODITY_NS_CURRENCY,
222  self->mnemonic);
223 
224  /* If there isn't any such commodity, get the default */
225  if (!currency)
226  {
227  currency = gnc_locale_default_currency();
228  DEBUG("gce %p, default currency mnemonic %s",
229  self, gnc_commodity_get_mnemonic(currency));
230  }
231 
232  g_signal_handlers_block_by_func(G_OBJECT(self),
233  (gpointer)gnc_currency_edit_mnemonic_changed,
234  user_data);
235  gnc_currency_edit_set_currency(self, currency);
236  g_signal_handlers_unblock_by_func(G_OBJECT(self),
237  (gpointer)gnc_currency_edit_mnemonic_changed,
238  user_data);
239 }
240 
241 
242 static void gnc_currency_edit_active_changed (GtkComboBox *gobject,
243  gpointer user_data)
244 {
245  GNCCurrencyEdit *self = GNC_CURRENCY_EDIT (gobject);
246 
247  /* Check that there is a proper selection before proceeding. Doing so allows
248  * GTK entry completion to proceed. */
249  if (gtk_combo_box_get_active(GTK_COMBO_BOX(self)) != -1)
250  {
251  gnc_commodity *currency = gnc_currency_edit_get_currency (self);
252  const gchar *mnemonic = gnc_commodity_get_mnemonic (currency);
253 
254  g_signal_handlers_block_by_func(G_OBJECT(self),
255  (gpointer)gnc_currency_edit_active_changed, user_data);
256  g_object_set (G_OBJECT (self), "mnemonic", mnemonic, nullptr);
257  g_signal_handlers_unblock_by_func(G_OBJECT(self),
258  (gpointer)gnc_currency_edit_active_changed, user_data);
259  }
260 }
261 
262 enum
263 {
264  CURRENCY_COL_NAME,
265  CURRENCY_COL_NORMALIZED_FOLDED,
266  NUM_CURRENCY_COLS
267 };
268 
279 static void
280 add_item(gnc_commodity *commodity, GNCCurrencyEdit *gce)
281 {
282  GtkTreeModel *model;
283  GtkTreeIter iter;
284  const char *string;
285  gchar *normalized, *normalized_folded;
286 
287  model = gtk_combo_box_get_model(GTK_COMBO_BOX(gce));
288 
289  string = gnc_commodity_get_printname(commodity);
290  normalized = g_utf8_normalize (string, -1, G_NORMALIZE_NFC);
291  normalized_folded = normalized ? g_utf8_casefold (normalized, -1) : nullptr;
292 
293  gtk_list_store_append(GTK_LIST_STORE(model), &iter);
294  gtk_list_store_set (GTK_LIST_STORE(model), &iter,
295  CURRENCY_COL_NAME, string,
296  CURRENCY_COL_NORMALIZED_FOLDED, normalized_folded,
297  -1);
298  g_free (normalized_folded);
299  g_free (normalized);
300 }
301 
302 
311 static void
312 fill_currencies(GNCCurrencyEdit *gce)
313 {
314  GList *currencies;
315 
317  (gnc_get_current_commodities (), GNC_COMMODITY_NS_CURRENCY);
318  g_list_foreach(currencies, (GFunc)add_item, gce);
319  g_list_free(currencies);
320 }
321 
322 
323 static gboolean
324 match_func (GtkEntryCompletion *completion, const char *entry_str,
325  GtkTreeIter *iter, GtkTreeModel *model)
326 {
327  gchar *currency_name;
328  gboolean ret = FALSE;
329  gtk_tree_model_get (model, iter,
330  CURRENCY_COL_NORMALIZED_FOLDED, &currency_name,
331  -1);
332  if (currency_name && *currency_name && strstr (currency_name, entry_str))
333  ret = TRUE;
334  g_free (currency_name);
335  return ret;
336 }
337 
338 /* Create a new GNCCurrencyEdit widget which can be used to provide
339  * an easy way to enter ISO currency codes.
340  *
341  * @return A GNCCurrencyEdit widget.
342  */
343 GtkWidget *
345 {
346  GNCCurrencyEdit *gce;
347  GtkListStore *store;
348  GtkEntryCompletion* completion;
349 
350  store = gtk_list_store_new (NUM_CURRENCY_COLS, G_TYPE_STRING, G_TYPE_STRING);
351  gce = GNC_CURRENCY_EDIT(g_object_new (GNC_TYPE_CURRENCY_EDIT,
352  "model", store,
353  "has-entry", true,
354  nullptr));
355  g_object_unref (store);
356 
357  /* Set the column for the text */
358  gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(gce), CURRENCY_COL_NAME);
359 
360  /* Now the signals to make sure the user can't leave the
361  widget without a valid currency. */
362  gnc_cbwe_require_list_item(GTK_COMBO_BOX(gce));
363 
364  /* Fill in all the data. */
365  fill_currencies (gce);
366  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
367  CURRENCY_COL_NAME,
368  GTK_SORT_ASCENDING);
369 
370  completion = gtk_entry_completion_new ();
371  gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
372  gtk_entry_completion_set_text_column (completion, CURRENCY_COL_NAME);
373  gtk_entry_completion_set_match_func (completion,
374  (GtkEntryCompletionMatchFunc)match_func,
375  GTK_TREE_MODEL(store), nullptr);
376  gtk_entry_set_completion (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (gce))),
377  completion);
378 
379  return GTK_WIDGET (gce);
380 }
381 
387 /* Set the widget to display a certain currency name.
388  *
389  * @param gce The currency editor widget to set.
390  *
391  * @param currency The currency to set as the displayed/selected
392  * value of the widget.
393  */
394 void
395 gnc_currency_edit_set_currency (GNCCurrencyEdit *gce,
396  const gnc_commodity *currency)
397 {
398  const gchar *printname;
399 
400  g_return_if_fail(gce != nullptr);
401  g_return_if_fail(GNC_IS_CURRENCY_EDIT(gce));
402  g_return_if_fail(currency != nullptr);
403 
404  printname = gnc_commodity_get_printname(currency);
405  gnc_cbwe_set_by_string(GTK_COMBO_BOX(gce), printname);
406 }
407 
408 
409 /* Retrieve the displayed currency of the widget.
410  *
411  * @param gce The currency editor widget whose values should be retrieved.
412  *
413  * @return A pointer to the selected currency (a gnc_commodity
414  * structure).
415  */
416 gnc_commodity *
417 gnc_currency_edit_get_currency (GNCCurrencyEdit *gce)
418 {
419  gnc_commodity *commodity;
420  char *mnemonic, *name;
421  GtkTreeModel *model;
422  GtkTreeIter iter;
423 
424  g_return_val_if_fail(gce != nullptr, nullptr);
425  g_return_val_if_fail(GNC_IS_CURRENCY_EDIT(gce), nullptr);
426 
427  if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(gce), &iter))
428  {
429  model = gtk_combo_box_get_model(GTK_COMBO_BOX(gce));
430  gtk_tree_model_get (model, &iter, 0, &mnemonic, -1);
431 
432  name = strchr(mnemonic, ' ');
433  if (name != nullptr)
434  *name = '\0';
435  commodity = gnc_commodity_table_lookup (gnc_get_current_commodities (),
436  GNC_COMMODITY_NS_CURRENCY,
437  mnemonic);
438  g_free(mnemonic);
439  }
440  else
441  {
442  g_warning("Combo box returned 'inactive'. Using locale default currency.");
443  commodity = gnc_locale_default_currency();
444  }
445 
446 
447  return commodity;
448 }
449 
457 void
458 gnc_currency_edit_clear_display (GNCCurrencyEdit *gce)
459 {
460  GtkTreeModel *model;
461  GtkWidget *entry;
462 
463  g_return_if_fail(gce != nullptr);
464  g_return_if_fail(GNC_IS_CURRENCY_EDIT(gce));
465 
466  model = gtk_combo_box_get_model (GTK_COMBO_BOX(gce));
467 
468  entry = gtk_bin_get_child (GTK_BIN(gce));
469 
470  g_object_ref (model);
471 
472  g_signal_handlers_block_by_func (G_OBJECT(gce),
473  (gpointer)gnc_currency_edit_active_changed, gce);
474 
475  gtk_combo_box_set_model (GTK_COMBO_BOX(gce), nullptr);
476  gtk_entry_set_text (GTK_ENTRY(entry),"");
477  gtk_combo_box_set_active (GTK_COMBO_BOX(gce), -1);
478  gtk_combo_box_set_model (GTK_COMBO_BOX(gce), model);
479 
480  g_signal_handlers_block_by_func (G_OBJECT(gce),
481  (gpointer)gnc_currency_edit_active_changed, gce);
482 
483  g_object_unref (model);
484 }
485 
void gnc_cbwe_set_by_string(GtkComboBox *cbwe, const gchar *text)
Find an entry in the GtkComboBox by its text value, and set the widget to that value.
Definition: gnc-gtk-utils.c:41
void gnc_currency_edit_set_currency(GNCCurrencyEdit *gce, const gnc_commodity *currency)
Set the widget to display a certain currency name.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
utility functions for the GnuCash UI
gtk helper routines.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_currency_edit_clear_display(GNCCurrencyEdit *gce)
Clear the displayed currency of the widget.
Currency selection widget.
gnc_commodity * gnc_currency_edit_get_currency(GNCCurrencyEdit *gce)
Retrieve the displayed currency of the widget.
All type declarations for the whole Gnucash engine.
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.
GtkWidget * gnc_currency_edit_new(void)
Create a new GNCCurrencyEdit widget which can be used to provide an easy way to enter ISO currency co...
gnc_commodity * gnc_locale_default_currency(void)
Returns the default currency of the current locale.
Commodity handling public routines.