GnuCash  5.6-150-g038405b370+
dialog-transfer.cpp
1 /********************************************************************\
2  * dialog-transfer.c -- transfer dialog for GnuCash *
3  * Copyright (C) 1999 Linas Vepstas *
4  * Copyright (C) 2000 Dave Peticolas *
5  * Copyright (C) 2000 Herbert Thoma *
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 <gdk/gdkkeysyms.h>
29 #include <glib/gi18n.h>
30 #include <gnc-quotes.hpp>
31 
32 #include "dialog-transfer.h"
33 #include "dialog-utils.h"
34 #include "gnc-amount-edit.h"
35 #include "gnc-component-manager.h"
36 #include "gnc-date-edit.h"
37 #include "gnc-engine.h"
38 #include "gnc-euro.h"
39 #include "gnc-exp-parser.h"
40 #include "gnc-prefs.h"
41 #include "gnc-gui-query.h"
42 #include "gnc-pricedb.h"
43 #include "gnc-tree-view-account.h"
44 #include "gnc-ui.h"
45 #include "Transaction.h"
46 #include "Account.h"
47 #include "Account.hpp"
48 #include "engine-helpers.h"
49 #include "QuickFill.h"
50 #include <gnc-commodity.h>
51 
52 
53 #define DIALOG_TRANSFER_CM_CLASS "dialog-transfer"
54 #define GNC_PREFS_GROUP "dialogs.transfer"
55 
56 typedef enum
57 {
58  XFER_DIALOG_FROM,
59  XFER_DIALOG_TO
60 } XferDirection;
61 
62 
63 /* This static indicates the debugging module that this .o belongs to. */
64 static QofLogModule log_module = GNC_MOD_GUI;
65 
67 {
68  GtkWidget *dialog;
69  GtkWidget *amount_edit;
70  GtkWidget *date_entry;
71  GtkWidget *num_entry;
72  GtkWidget *description_entry;
73  GtkWidget *notes_entry;
74  GtkWidget *memo_entry;
75  GtkWidget *conv_forward;
76  GtkWidget *conv_reverse;
77 
78  GtkWidget *from_window;
79  GtkTreeView * from_tree_view;
80  gnc_commodity *from_commodity;
81  GtkWidget *to_window;
82  GtkTreeView *to_tree_view;
83  gnc_commodity *to_commodity;
84 
85  QuickFill *qf; /* Quickfill on transfer descriptions,
86  defaults to matching on the "From" account. */
87 
88  XferDirection quickfill; /* direction match on the account instead. */
89 
90  /* stored data for the description quickfill selection function */
91  gint desc_start_selection;
92  gint desc_end_selection;
93  guint desc_selection_source_id;
94 
95  GtkWidget *transferinfo_label;
96 
97  GtkWidget *from_transfer_label;
98  GtkWidget *to_transfer_label;
99 
100  GtkWidget *from_currency_label;
101  GtkWidget *to_currency_label;
102 
103  GtkWidget *from_show_button;
104  GtkWidget *to_show_button;
105 
106  GtkWidget *curr_xfer_table;
107 
108  GtkWidget *price_edit;
109  GtkWidget *to_amount_edit;
110 
111  GtkWidget *price_radio;
112  GtkWidget *amount_radio;
113 
114  GtkWidget *fetch_button;
115 
116  QofBook *book;
117  GNCPriceDB *pricedb;
118 
119  /* Where to store the "exchange_rate" at exit (in lieu of
120  * creating a transaction)
121  */
122  gnc_numeric *exch_rate;
123  PriceSource price_source;
124  const char *price_type;
125 
126  /* Callback function to notify of the newly created Transaction */
127  gnc_xfer_dialog_cb transaction_cb;
128  /* , and its user_data */
129  gpointer transaction_user_data;
130 };
131 
133 typedef struct
134 {
136  gboolean show_inc_exp;
137 
139  gboolean show_hidden;
141 
142 static AccountTreeFilterInfo *from_info = NULL;
143 static AccountTreeFilterInfo *to_info = NULL;
144 
146 {
147  char *acct_full_name;
148  Account *acct;
149 };
150 typedef struct _acct_list_item acct_list_item;
151 
152 
154 static void gnc_xfer_update_to_amount (XferDialog *xferData);
155 static void gnc_xfer_dialog_update_conv_info(XferDialog *xferData);
156 
157 static Account *gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
158  XferDirection direction);
159 static void gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
160  Account *account,
161  XferDirection direction);
162 
163 extern "C" {
164 void gnc_xfer_description_insert_cb(GtkEditable *editable,
165  const gchar *insert_text,
166  const gint insert_text_len,
167  gint *start_pos,
168  XferDialog *xferData);
169 gboolean gnc_xfer_description_key_press_cb( GtkEntry *entry,
170  GdkEventKey *event,
171  XferDialog *xferData );
172 void gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData);
173 gboolean gnc_xfer_dialog_inc_exp_filter_func (Account *account,
174  gpointer data);
175 void price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data);
176 
177 void gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
178 void gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data);
179 }
180 
183 static gnc_numeric
184 gnc_xfer_dialog_compute_price_value (XferDialog *xferData)
185 {
186  gnc_numeric from_amt, to_amt;
187  g_return_val_if_fail (xferData != NULL, gnc_numeric_error (GNC_ERROR_ARG));
188 
189  from_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
190  to_amt = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
191 
192  return(gnc_numeric_div(to_amt, from_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE));
193 }
194 
195 /* Round a price value according to this policy:
196  * If both commodities are currencies, round to a fixed denominator.
197  * If only one is a currency, round to the currency's scu * a fixed factor.
198  * The fixed values are defined in gnc-pricedb.h
199  */
200 static gnc_numeric
201 round_price(gnc_commodity *from, gnc_commodity *to, gnc_numeric value)
202 {
204  value = gnc_numeric_convert(value, CURRENCY_DENOM,
206  else if (gnc_commodity_is_currency(to))
207  {
208  int scu = gnc_commodity_get_fraction (to);
209  value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
211  }
212  else if (gnc_commodity_is_currency(from))
213  {
214  int scu = gnc_commodity_get_fraction (from);
215  value = gnc_numeric_convert(value, scu * COMMODITY_DENOM_MULT,
217  }
218  return value;
219 }
220 
221 typedef enum
222 {
223  SAME_DAY,
224  NEAREST,
225  LATEST
226 } PriceDate;
227 
228 typedef struct
229 {
230  GNCPrice *price;
231  GNCPriceDB *pricedb;
232  gnc_commodity *from;
233  gnc_commodity *to;
234  time64 time;
235  gboolean reverse;
236 } PriceReq;
237 
238 static void
239 price_request_from_xferData(PriceReq *pr, XferDialog *xd)
240 {
241  g_return_if_fail (pr != NULL);
242  g_return_if_fail (xd != NULL);
243  pr->price = NULL;
244  pr->pricedb = xd->pricedb;
245  pr->from = xd->from_commodity;
246  pr->to = xd->to_commodity;
247  pr->time = gnc_date_edit_get_date (GNC_DATE_EDIT (xd->date_entry));
248  pr->reverse = FALSE;
249 }
250 
251 static gboolean
252 lookup_price(PriceReq *pr, PriceDate pd)
253 {
254  GNCPrice *prc = NULL;
255  g_return_val_if_fail (pr != NULL, FALSE);
256  g_return_val_if_fail (pr->pricedb != NULL, FALSE);
257  g_return_val_if_fail (pr->from != NULL, FALSE);
258  g_return_val_if_fail (pr->to != NULL, FALSE);
259 
260  pr->reverse = FALSE;
261  switch (pd)
262  {
263  default:
264  case SAME_DAY:
265  prc = gnc_pricedb_lookup_day_t64 (pr->pricedb, pr->from,
266  pr->to, pr->time);
267  break;
268  case NEAREST:
269  prc = gnc_pricedb_lookup_nearest_in_time64 (pr->pricedb, pr->from,
270  pr->to, pr->time);
271  break;
272  case LATEST:
273  prc = gnc_pricedb_lookup_latest (pr->pricedb, pr->from, pr->to);
274  break;
275  }
276 
277  if (!prc) //no price found
278  {
279  PINFO("No price Found for %s, %s",
280  gnc_commodity_get_mnemonic(pr->from),
282  pr->price = NULL;
283  return FALSE;
284  }
285 
286  if (gnc_commodity_equiv(gnc_price_get_currency(prc), pr->from))
287  {
288  pr->reverse = TRUE;
289  PINFO("Found reverse price: 1 %s = %f %s",
291  gnc_numeric_to_double(gnc_price_get_value(prc)),
292  gnc_commodity_get_mnemonic(pr->from));
293  }
294  else
295  {
296  PINFO("Found price: 1 %s = %f %s",
297  gnc_commodity_get_mnemonic(pr->from),
298  gnc_numeric_to_double(gnc_price_get_value(prc)),
300  }
301  pr->price = prc;
302  return TRUE;
303 }
304 
305 /* (maybe) update the price from the pricedb. */
306 static void
307 gnc_xfer_dialog_update_price (XferDialog *xferData)
308 {
309  PriceReq pr;
310  gnc_numeric price_value;
311 
312  if (!xferData) return;
313  if (!GNC_IS_COMMODITY (xferData->from_commodity) ||
314  !GNC_IS_COMMODITY (xferData->to_commodity)) return;
315  if (gnc_commodity_equal (xferData->from_commodity, xferData->to_commodity))
316  return;
317  if (!xferData->pricedb) return;
318 
319  price_request_from_xferData(&pr, xferData);
320  if (!lookup_price(&pr, SAME_DAY))
321  if (!lookup_price(&pr, NEAREST))
322  return;
323 
324  /* grab the price from the pricedb */
325  price_value = gnc_price_get_value (pr.price);
326  if (pr.reverse)
327  price_value = gnc_numeric_invert (price_value);
328  gnc_price_unref(pr.price);
329 
330  /* and set the price entry */
331  gnc_xfer_dialog_set_price_edit(xferData, price_value);
332 
333  /* And then update the to_amount */
334  gnc_xfer_update_to_amount (xferData);
335 }
336 
337 static void
338 gnc_xfer_dialog_toggle_cb(GtkToggleButton *button, gpointer data)
339 {
340  GncTreeViewAccount* treeview = GNC_TREE_VIEW_ACCOUNT (data);
341 
342  auto info = static_cast<AccountTreeFilterInfo*> (g_object_get_data (G_OBJECT(treeview), "filter-info"));
343  if (info)
344  {
345  info->show_inc_exp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
346  info->show_hidden = FALSE;
347 
349  }
350 }
351 
352 static gboolean
353 gnc_xfer_dialog_key_press_cb (GtkWidget *widget,
354  GdkEventKey *event,
355  gpointer unused)
356 {
357  if ((event->keyval == GDK_KEY_Return) || (event->keyval == GDK_KEY_KP_Enter))
358  {
359  auto toplevel = gtk_widget_get_toplevel (widget);
360  if (gtk_widget_is_toplevel(toplevel) && GTK_IS_WINDOW(toplevel))
361  {
362  gtk_window_activate_default(GTK_WINDOW(toplevel));
363  return TRUE;
364  }
365  }
366  return FALSE;
367 }
368 
369 static void
370 gnc_xfer_dialog_set_price_auto (XferDialog *xferData,
371  gboolean currency_active,
372  const gnc_commodity *from_currency,
373  const gnc_commodity *to_currency)
374 {
375  if (!currency_active)
376  {
377  gnc_xfer_dialog_set_price_edit(xferData, gnc_numeric_zero());
378  auto entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
379  (GNC_AMOUNT_EDIT(xferData->price_edit)));
380  gtk_entry_set_text(entry, "");
381 
382  gnc_xfer_update_to_amount (xferData);
383 
384  return;
385  }
386 
387  if (!gnc_is_euro_currency (from_currency) ||
388  !gnc_is_euro_currency (to_currency))
389  {
390  gnc_xfer_dialog_update_price (xferData);
391  return;
392  }
393 
394  auto from_rate = gnc_euro_currency_get_rate (from_currency);
395  auto to_rate = gnc_euro_currency_get_rate (to_currency);
396 
397  if (gnc_numeric_zero_p (from_rate) || gnc_numeric_zero_p (to_rate))
398  gnc_xfer_dialog_update_price (xferData);
399 
400  auto price_value = gnc_numeric_div (to_rate, from_rate, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
401 
402  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(xferData->price_edit), price_value);
403 
404  gnc_xfer_update_to_amount (xferData);
405 }
406 
407 static void
408 gnc_xfer_dialog_curr_acct_activate(XferDialog *xferData)
409 {
410  g_return_if_fail (xferData != NULL);
411  auto from_account =
412  gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
413 
414  auto to_account =
415  gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
416 
417  gboolean curr_active =
418  (xferData->exch_rate ||
419  ((from_account != NULL) && (to_account != NULL))) &&
420  !gnc_commodity_equiv(xferData->from_commodity, xferData->to_commodity);
421 
422  gtk_widget_set_sensitive(xferData->curr_xfer_table, curr_active);
423  gtk_widget_set_sensitive(xferData->price_edit,
424  curr_active && gtk_toggle_button_get_active
425  (GTK_TOGGLE_BUTTON(xferData->price_radio)));
426  gtk_widget_set_sensitive(xferData->to_amount_edit,
427  curr_active && gtk_toggle_button_get_active
428  (GTK_TOGGLE_BUTTON(xferData->amount_radio)));
429  gtk_widget_set_sensitive(xferData->price_radio, curr_active);
430  gtk_widget_set_sensitive(xferData->amount_radio, curr_active);
431 
432  gnc_xfer_dialog_set_price_auto (xferData, curr_active,
433  xferData->from_commodity, xferData->to_commodity);
434  gnc_xfer_dialog_update_conv_info(xferData);
435 
436  if (!curr_active)
437  {
438  gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->to_amount_edit),
439  gnc_numeric_zero ());
440  auto entry = GTK_ENTRY(gnc_amount_edit_gtk_entry
441  (GNC_AMOUNT_EDIT(xferData->to_amount_edit)));
442  gtk_entry_set_text(entry, "");
443  }
444 }
445 
446 
447 void
448 price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data)
449 {
450  g_return_if_fail (data);
451 
452  auto xferData = static_cast<XferDialog *> (data);
453  gtk_widget_set_sensitive(xferData->price_edit, gtk_toggle_button_get_active
454  (GTK_TOGGLE_BUTTON(xferData->price_radio)));
455  gtk_widget_set_sensitive(xferData->to_amount_edit,
456  gtk_toggle_button_get_active
457  (GTK_TOGGLE_BUTTON(xferData->amount_radio)));
458 }
459 
460 
461 /* Reload the xferDialog quickfill with the descriptions
462  * from the currently selected from account. Note that this
463  * doesn't use the initial account passed into gnc_xfer_dialog,
464  * because that's NULL if no account is selected in the main
465  * account window tree view.
466  */
467 static void
468 gnc_xfer_dialog_reload_quickfill( XferDialog *xferData )
469 {
470  auto account = gnc_transfer_dialog_get_selected_account (xferData, xferData->quickfill);
471 
472  /* get a new QuickFill to use */
473  gnc_quickfill_destroy( xferData->qf );
474  xferData->qf = gnc_quickfill_new();
475 
476  for (auto split : xaccAccountGetSplits (account))
477  {
478  auto trans = xaccSplitGetParent (split);
479  gnc_quickfill_insert( xferData->qf,
480  xaccTransGetDescription (trans), QUICKFILL_LIFO);
481  }
482 }
483 
484 
485 static void
486 gnc_xfer_dialog_from_tree_selection_changed_cb (GtkTreeSelection *selection,
487  gpointer data)
488 {
489  auto xferData = static_cast<XferDialog *> (data);
490 
491  auto account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
492  if (!account)
493  return;
494 
495  auto commodity = gnc_account_or_default_currency(account, NULL);
496  gtk_label_set_text(GTK_LABEL(xferData->from_currency_label),
497  gnc_commodity_get_printname(commodity));
498 
499  xferData->from_commodity = commodity;
500 
501  auto print_info = gnc_account_print_info (account, FALSE);
502  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (xferData->amount_edit),
503  print_info);
504  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->amount_edit),
505  xaccAccountGetCommoditySCU (account));
506 
507  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL);
508 
509  gnc_xfer_dialog_curr_acct_activate(xferData);
510 
511  /* Reload the xferDialog quickfill if it is based on the from account */
512  if (xferData->quickfill == XFER_DIALOG_FROM)
513  gnc_xfer_dialog_reload_quickfill(xferData);
514 }
515 
516 
517 static void
518 gnc_xfer_dialog_to_tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
519 {
520  auto xferData = static_cast<XferDialog *> (data);
521 
522  auto account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
523  if (!account)
524  return;
525 
526  auto commodity = xaccAccountGetCommodity(account);
527  gtk_label_set_text(GTK_LABEL(xferData->to_currency_label),
528  gnc_commodity_get_printname(commodity));
529 
530  xferData->to_commodity = commodity;
531 
532  auto print_info = gnc_account_print_info (account, FALSE);
533  gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (xferData->to_amount_edit),
534  print_info);
535  gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (xferData->to_amount_edit),
536  xaccAccountGetCommoditySCU (account));
537 
538  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL);
539 
540  gnc_xfer_dialog_curr_acct_activate(xferData);
541 
542  /* Reload the xferDialog quickfill if it is based on the to account */
543  if (xferData->quickfill == XFER_DIALOG_TO)
544  gnc_xfer_dialog_reload_quickfill(xferData);
545 }
546 
547 gboolean
548 gnc_xfer_dialog_inc_exp_filter_func (Account *account,
549  gpointer data)
550 {
551  auto info = static_cast<AccountTreeFilterInfo *> (data);
552 
553  if (!info->show_hidden && xaccAccountIsHidden(account))
554  {
555  return FALSE;
556  }
557 
558  if (info->show_inc_exp)
559  {
560  return TRUE;
561  }
562 
563  auto type = xaccAccountGetType(account);
564  return ((type != ACCT_TYPE_INCOME) && (type != ACCT_TYPE_EXPENSE));
565 }
566 
567 static void
568 gnc_xfer_dialog_fill_tree_view(XferDialog *xferData,
569  XferDirection direction)
570 {
571  const char *show_inc_exp_message = _("Show the income and expense accounts");
572  GtkWidget *button;
573  GtkWidget *scroll_win;
574  auto builder = static_cast<GtkBuilder *> (g_object_get_data (G_OBJECT (xferData->dialog), "builder"));
575 
576  g_return_if_fail (xferData != NULL);
577  auto use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
578  GNC_PREF_ACCOUNTING_LABELS);
579 
580  /* In "normal" mode (non accounting terms) the account where the
581  * money comes from is displayed on the left side and the account
582  * where the money gets transferred to is displayed on the right
583  * side. In accounting terms the "from" account is called the
584  * "credit" account ("Haben" in german) and the "to" account is
585  * called "debit" account ("Soll" in german). Accountants told me
586  * that they always want the credit account on the right side
587  * and the debit on the left side (like the debit and credit
588  * columns in the register window). So reverse from and to account
589  * trees when in "accountant" mode. -- Herbert Thoma, 2004-01-18
590  */
591  if (use_accounting_labels)
592  {
593  button = GTK_WIDGET(gtk_builder_get_object (builder,
594  (direction == XFER_DIALOG_TO) ?
595  "left_show_button" : "right_show_button"));
596  scroll_win = GTK_WIDGET(gtk_builder_get_object (builder,
597  (direction == XFER_DIALOG_TO) ?
598  "left_trans_window" : "right_trans_window"));
599  }
600  else
601  {
602  button = GTK_WIDGET(gtk_builder_get_object (builder,
603  (direction == XFER_DIALOG_TO) ?
604  "right_show_button" : "left_show_button"));
605  scroll_win = GTK_WIDGET(gtk_builder_get_object (builder,
606  (direction == XFER_DIALOG_TO) ?
607  "right_trans_window" : "left_trans_window"));
608  }
609 
610 
611  AccountTreeFilterInfo *info;
612  if (direction == XFER_DIALOG_TO)
613  info = to_info;
614  else
615  info = from_info;
616 
617  auto tree_view = GTK_TREE_VIEW(gnc_tree_view_account_new(FALSE));
618  gtk_container_add(GTK_CONTAINER(scroll_win), GTK_WIDGET(tree_view));
619  info->show_inc_exp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
620  info->show_hidden = FALSE;
621  gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT (tree_view),
622  gnc_xfer_dialog_inc_exp_filter_func,
623  info, /* user data */
624  NULL /* destroy callback */);
625  g_object_set_data (G_OBJECT(tree_view), "filter-info", info);
626 
627  gtk_widget_show(GTK_WIDGET(tree_view));
628  g_signal_connect (G_OBJECT (tree_view), "key-press-event",
629  G_CALLBACK (gnc_xfer_dialog_key_press_cb), NULL);
630 
631  auto selection = gtk_tree_view_get_selection (tree_view);
632  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
633 
634  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
635  gtk_widget_set_tooltip_text (button, show_inc_exp_message);
636 
637  if (direction == XFER_DIALOG_TO)
638  {
639  xferData->to_tree_view = tree_view;
640  xferData->to_window = scroll_win;
641  xferData->to_show_button = GTK_WIDGET (button);
642  g_signal_connect (G_OBJECT (selection), "changed",
643  G_CALLBACK (gnc_xfer_dialog_to_tree_selection_changed_cb), xferData);
644  }
645  else
646  {
647  xferData->from_tree_view = tree_view;
648  xferData->from_window = scroll_win;
649  xferData->from_show_button = GTK_WIDGET (button);
650  g_signal_connect (G_OBJECT (selection), "changed",
651  G_CALLBACK (gnc_xfer_dialog_from_tree_selection_changed_cb), xferData);
652  }
653  g_signal_connect (G_OBJECT (button), "toggled",
654  G_CALLBACK (gnc_xfer_dialog_toggle_cb), tree_view);
655 }
656 
657 
658 static void
659 gnc_parse_error_dialog (XferDialog *xferData, const char *error_string)
660 {
661  const char * parse_error_string;
662  g_return_if_fail (xferData != NULL);
663 
664  parse_error_string = gnc_exp_parser_error_string ();
665  if (parse_error_string == NULL)
666  parse_error_string = "";
667 
668  if (error_string == NULL)
669  error_string = "";
670 
671  gnc_error_dialog (GTK_WINDOW (xferData->dialog),
672  "%s\n\n%s: %s.",
673  error_string, _("Error"),
674  parse_error_string);
675 }
676 
677 /*** Callbacks for description quickfill. ***/
678 
679 /* gnc_xfer_dialog_quickfill will update the fields of the dialog
680  * based on the contents of the Description entry. Returns TRUE
681  * if the fields were updated, or FALSE if the fields were already
682  * updated or if the Description couldn't be matched and no updates
683  * were made.
684  */
685 static gboolean
686 gnc_xfer_dialog_quickfill( XferDialog *xferData )
687 {
688  const char *desc;
689  Account *match_account; /* the matched text was from this account */
690  Split *split; /* the split to autocomplete from */
691  Split *other = NULL; /* the other split of the transaction */
692  Account *other_acct = NULL; /* the Account of the other split */
693  gboolean changed = FALSE;
694 
695  ENTER("xferData=%p", xferData);
696  if ( !xferData )
697  {
698  LEAVE("bad args");
699  return( FALSE );
700  }
701 
702  match_account = gnc_transfer_dialog_get_selected_account (xferData, xferData->quickfill);
703 
704  desc = gtk_entry_get_text( GTK_ENTRY(xferData->description_entry) );
705 
706  if ( !desc || desc[0] == '\0' ) /* no description to match */
707  return( FALSE );
708 
709  split = xaccAccountFindSplitByDesc( match_account, desc );
710 
711  if ( !split )
712  {
713  LEAVE("split not found");
714  return( FALSE );
715  }
716  DEBUG("split=%p", split);
717 
718  /* Now update any blank fields of the transfer dialog with
719  * the memo and amount from the split, and the description
720  * we were passed (assumed to match the split's transaction).
721  */
722 
723  if ( gnc_numeric_zero_p(
724  gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit))))
725  {
726  gnc_numeric amt;
727  DEBUG("updating amount");
728  amt = xaccSplitGetValue( split );
729 
730  /* If we've matched a previous transfer, it will appear
731  * to be negative in the from account.
732  * Need to swap the sign in order for this value
733  * to be posted as a withdrawal from the "from" account.
734  */
735  if ( gnc_numeric_negative_p( amt ) )
736  amt = gnc_numeric_neg( amt );
737 
738  gnc_amount_edit_set_amount( GNC_AMOUNT_EDIT(xferData->amount_edit), amt );
739  changed = TRUE;
740  }
741 
742  if ( !g_strcmp0(gtk_entry_get_text(GTK_ENTRY(xferData->memo_entry)), "" ))
743  {
744  DEBUG("updating memo");
745  gtk_entry_set_text( GTK_ENTRY(xferData->memo_entry),
746  xaccSplitGetMemo( split ) );
747  changed = TRUE;
748  }
749 
750  /* Since we're quickfilling off of one account (either from or to)
751  * that account must be the account of the matched split.
752  * Find the other account from the other split,
753  * and select that account in the appropriate account tree.
754  */
755  if ( ( other = xaccSplitGetOtherSplit( split ) ) &&
756  ( other_acct = xaccSplitGetAccount( other ) ) )
757  {
758  GNCAccountType other_type;
759  GtkWidget *other_button;
760  XferDirection other_direction;
761 
762  DEBUG("updating other split");
763  if (xferData->quickfill == XFER_DIALOG_FROM)
764  {
765  other_button = xferData->to_show_button;
766  other_direction = XFER_DIALOG_TO;
767  }
768  else
769  {
770  other_button = xferData->from_show_button;
771  other_direction = XFER_DIALOG_FROM;
772  }
773 
774  other_type = xaccAccountGetType(other_acct);
775 
776  /* Don't want to deactivate the button just because this
777  * isn't an income or expense account
778  */
779  if ( (other_type == ACCT_TYPE_EXPENSE) || (other_type == ACCT_TYPE_INCOME) )
780  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(other_button), TRUE);
781 
782  gnc_transfer_dialog_set_selected_account (xferData, other_acct, other_direction);
783 
784  changed = TRUE;
785  }
786 
787  return( changed );
788 }
789 
790 static gboolean
791 idle_select_region(gpointer data)
792 {
793  g_return_val_if_fail(data, FALSE);
794 
795  auto xferData = static_cast<XferDialog *> (data);
796 
797  gtk_editable_select_region(GTK_EDITABLE(xferData->description_entry),
798  xferData->desc_start_selection,
799  xferData->desc_end_selection);
800 
801  xferData->desc_selection_source_id = 0;
802  return FALSE;
803 }
804 
805 /* The insert_cb will do the insert and quickfill if possible and set the
806  * cursor position accordingly. It will not set the selection but will register
807  * idle_select_region to do that once the program returns to its main loop.
808  */
809 void
810 gnc_xfer_description_insert_cb(GtkEditable *editable,
811  const gchar *insert_text,
812  const gint insert_text_len,
813  gint *start_pos,
814  XferDialog *xferData)
815 {
816  gchar *prefix, *suffix, *new_text;
817  QuickFill *match;
818  const gchar *match_str;
819  gint prefix_len, new_text_len, match_str_len;
820 
821  g_return_if_fail (xferData != NULL);
822 
823  if (insert_text_len <= 0)
824  return;
825 
826  suffix = gtk_editable_get_chars(editable, *start_pos, -1);
827 
828  /* If we are inserting in the middle, do nothing */
829  if (*suffix)
830  {
831  g_free(suffix);
832  return;
833  }
834  g_free(suffix);
835 
836  prefix = gtk_editable_get_chars(editable, 0, *start_pos);
837  new_text = g_strconcat(prefix, insert_text, (gchar*) NULL);
838  prefix_len = strlen(prefix);
839  new_text_len = prefix_len + insert_text_len;
840  g_free(prefix);
841 
842  if ((match = gnc_quickfill_get_string_match(xferData->qf, new_text))
843  && (match_str = gnc_quickfill_string(match))
844  && ((match_str_len = strlen(match_str)) > new_text_len))
845  {
846  g_signal_handlers_block_matched (G_OBJECT (editable),
847  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
848 
849  gtk_editable_insert_text(editable,
850  match_str + prefix_len,
851  match_str_len - prefix_len,
852  start_pos);
853 
854  g_signal_handlers_unblock_matched (G_OBJECT (editable),
855  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xferData);
856 
857  /* stop the current insert */
858  g_signal_stop_emission_by_name (G_OBJECT (editable), "insert_text");
859 
860  /* set the position */
861  *start_pos = g_utf8_strlen(new_text, -1);
862 
863  /* select region on idle, because it would be reset once this function
864  finishes */
865  xferData->desc_start_selection = *start_pos;
866  xferData->desc_end_selection = -1;
867  xferData->desc_selection_source_id = g_idle_add(idle_select_region,
868  xferData);
869  }
870  g_free(new_text);
871 }
872 
873 gboolean
874 gnc_xfer_description_key_press_cb( GtkEntry *entry,
875  GdkEventKey *event,
876  XferDialog *xferData )
877 {
878  gboolean done_with_input = FALSE;
879 
880  /* Most "special" keys are allowed to be handled directly by
881  * the entry's key press handler, but in some cases that doesn't
882  * seem to work right, so handle them here.
883  */
884  ENTER(" ");
885  switch ( event->keyval )
886  {
887  case GDK_KEY_Return:
888  case GDK_KEY_KP_Enter:
889  gnc_xfer_dialog_quickfill( xferData );
890  /* NOT done with input, activate the default button of the dialog. */
891  break;
892 
893  case GDK_KEY_Tab:
894  case GDK_KEY_ISO_Left_Tab:
895  if ( !( event->state & GDK_SHIFT_MASK) ) /* Complete on Tab,
896  * but not Shift-Tab */
897  {
898  gnc_xfer_dialog_quickfill( xferData );
899  /* NOT done with input, though, since we need to focus to the next
900  * field. Unselect the current field, though.
901  */
902  gtk_editable_select_region( GTK_EDITABLE(xferData->description_entry),
903  0, 0 );
904  }
905  break;
906  }
907 
908  LEAVE("done=%d", done_with_input);
909  return( done_with_input );
910 }
911 
912 /*** End of quickfill-specific callbacks ***/
913 
914 static void
915 gnc_xfer_dialog_update_conv_info (XferDialog *xferData)
916 {
917  const gchar *to_mnemonic, *from_mnemonic;
918  gchar *string;
919  gnc_numeric rate;
920 
921  from_mnemonic = gnc_commodity_get_mnemonic(xferData->from_commodity);
922  to_mnemonic = gnc_commodity_get_mnemonic(xferData->to_commodity);
923 
924  /* On the theory that if we don't have a mnemonic then we don't
925  * have a commodity... On Solaris this crashes without a string.
926  * So, just leave now and wait for the second initialization to
927  * occur.
928  */
929  if (!from_mnemonic || !to_mnemonic)
930  return;
931 
932  rate = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->price_edit));
933  if (gnc_numeric_zero_p(rate))
934  {
935  string = g_strdup_printf("1 %s = x %s", from_mnemonic, to_mnemonic);
936  gtk_label_set_text(GTK_LABEL(xferData->conv_forward), string);
937  g_free(string);
938 
939  string = g_strdup_printf("1 %s = x %s", to_mnemonic, from_mnemonic);
940  gtk_label_set_text(GTK_LABEL(xferData->conv_reverse), string);
941  g_free(string);
942  }
943  else
944  {
945  string = g_strdup_printf("1 %s = %f %s", from_mnemonic,
946  gnc_numeric_to_double(rate), to_mnemonic);
947  gtk_label_set_text(GTK_LABEL(xferData->conv_forward), string);
948  g_free(string);
949 
950  rate = gnc_numeric_invert(rate);
951  string = g_strdup_printf("1 %s = %f %s", to_mnemonic,
952  gnc_numeric_to_double(rate), from_mnemonic);
953  gtk_label_set_text(GTK_LABEL(xferData->conv_reverse), string);
954  g_free(string);
955  }
956 }
957 
958 static gboolean
959 gnc_xfer_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
960  gpointer data)
961 {
962  g_return_val_if_fail (data, FALSE);
963 
964  auto xferData = static_cast<XferDialog *> (data);
965 
966  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL);
967 
968  gnc_xfer_update_to_amount (xferData);
969 
970  return FALSE;
971 }
972 
973 
974 static void
975 gnc_xfer_update_to_amount (XferDialog *xferData)
976 {
977  GNCAmountEdit *amount_edit, *price_edit, *to_amount_edit;
978  gnc_numeric price_value, to_amount;
979  Account *account;
980  int scu = 0;
981 
982  g_return_if_fail(xferData);
983 
984  xferData->price_source = PRICE_SOURCE_USER_PRICE;
985 
986  /* Get the amount editing controls of the dialog. */
987  amount_edit = GNC_AMOUNT_EDIT(xferData->amount_edit);
988  price_edit = GNC_AMOUNT_EDIT(xferData->price_edit);
989  to_amount_edit = GNC_AMOUNT_EDIT(xferData->to_amount_edit);
990 
991  /* Determine the SCU (smallest commodity unit) of the "to" amount. */
992  account = gnc_transfer_dialog_get_selected_account(xferData, XFER_DIALOG_TO);
993  if (account == NULL)
994  account = gnc_transfer_dialog_get_selected_account(xferData,
995  XFER_DIALOG_FROM);
996  if (account != NULL)
997  scu = xaccAccountGetCommoditySCU(account);
998  else if (xferData->to_commodity != NULL)
999  scu = gnc_commodity_get_fraction(xferData->to_commodity);
1000 
1001  /* Determine the amount to transfer. */
1002  if (!gnc_amount_edit_evaluate(price_edit, NULL) ||
1003  gnc_numeric_zero_p(price_value = gnc_amount_edit_get_amount(price_edit)))
1004  to_amount = gnc_numeric_zero();
1005  else
1006  to_amount = gnc_numeric_mul(gnc_amount_edit_get_amount(amount_edit),
1007  price_value, scu, GNC_HOW_RND_ROUND_HALF_UP);
1008 
1009  /* Update the dialog. */
1010  gnc_amount_edit_set_amount(to_amount_edit, to_amount);
1011  if (gnc_numeric_zero_p(to_amount))
1012  gtk_entry_set_text(GTK_ENTRY(gnc_amount_edit_gtk_entry(to_amount_edit)),
1013  "");
1014 
1015  gnc_xfer_dialog_update_conv_info(xferData);
1016 }
1017 
1018 
1019 static gboolean
1020 gnc_xfer_price_update_cb(GtkWidget *widget, GdkEventFocus *event,
1021  gpointer data)
1022 {
1023  auto xferData = static_cast<XferDialog *> (data);
1024 
1025  gnc_xfer_update_to_amount (xferData);
1026  xferData->price_type = PRICE_TYPE_TRN;
1027 
1028  return FALSE;
1029 }
1030 
1031 static gboolean
1032 gnc_xfer_date_changed_cb(GtkWidget *widget, gpointer data)
1033 {
1034  auto xferData = static_cast<XferDialog *> (data);
1035 
1036  if (xferData)
1037  gnc_xfer_dialog_update_price (xferData);
1038 
1039  return FALSE;
1040 }
1041 
1042 static gboolean
1043 gnc_xfer_to_amount_update_cb(GtkWidget *widget, GdkEventFocus *event,
1044  gpointer data)
1045 {
1046  auto xferData = static_cast<XferDialog *> (data);
1047 
1048  gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL);
1049  auto price_value = gnc_xfer_dialog_compute_price_value (xferData);
1050  gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
1051  price_value);
1052  xferData->price_source = PRICE_SOURCE_XFER_DLG_VAL;
1053  xferData->price_type = PRICE_TYPE_TRN;
1054  gnc_xfer_dialog_update_conv_info(xferData);
1055 
1056  return FALSE;
1057 }
1058 
1059 
1060 /********************************************************************\
1061  * gnc_xfer_dialog_select_from_account *
1062  * select the from account in a xfer dialog *
1063  * *
1064  * Args: xferData - xfer dialog structure *
1065  * account - account to select *
1066  * Return: none *
1067 \********************************************************************/
1068 void
1069 gnc_xfer_dialog_select_from_account(XferDialog *xferData, Account *account)
1070 {
1071  gnc_transfer_dialog_set_selected_account (xferData, account, XFER_DIALOG_FROM);
1072 }
1073 
1074 
1075 /********************************************************************\
1076  * gnc_xfer_dialog_select_to_account *
1077  * select the to account in a xfer dialog *
1078  * *
1079  * Args: xferData - xfer dialog structure *
1080  * account - account to select *
1081  * Return: none *
1082 \********************************************************************/
1083 void
1084 gnc_xfer_dialog_select_to_account(XferDialog *xferData, Account *account)
1085 {
1086  gnc_transfer_dialog_set_selected_account (xferData, account, XFER_DIALOG_TO);
1087 }
1088 
1089 void
1090 gnc_xfer_dialog_select_from_currency(XferDialog *xferData, gnc_commodity *cur)
1091 {
1092  if (!xferData) return;
1093  if (!cur) return;
1094 
1095  gtk_label_set_text(GTK_LABEL(xferData->from_currency_label),
1097 
1098  gnc_amount_edit_set_print_info(GNC_AMOUNT_EDIT(xferData->amount_edit),
1099  gnc_commodity_print_info(cur, FALSE));
1100  gnc_amount_edit_set_fraction(GNC_AMOUNT_EDIT(xferData->amount_edit),
1102 
1103  xferData->from_commodity = cur;
1104  gnc_xfer_dialog_curr_acct_activate(xferData);
1105 }
1106 
1107 void
1108 gnc_xfer_dialog_select_to_currency(XferDialog *xferData, gnc_commodity *cur)
1109 {
1110  g_return_if_fail (cur && GNC_IS_COMMODITY (cur));
1111  gtk_label_set_text(GTK_LABEL(xferData->to_currency_label),
1113 
1114  gnc_amount_edit_set_print_info(GNC_AMOUNT_EDIT(xferData->to_amount_edit),
1115  gnc_commodity_print_info(cur, FALSE));
1116  gnc_amount_edit_set_fraction(GNC_AMOUNT_EDIT(xferData->to_amount_edit),
1118 
1119  xferData->to_commodity = cur;
1120  gnc_xfer_dialog_curr_acct_activate(xferData);
1121 }
1122 
1123 static void
1124 gnc_xfer_dialog_lock_account_tree(XferDialog *xferData,
1125  XferDirection direction,
1126  gboolean hide)
1127 {
1128  GtkTreeView *tree_view;
1129  GtkWidget *show_button;
1130  GtkWidget *scroll_win;
1131 
1132  if (xferData == NULL)
1133  return;
1134 
1135  switch (direction)
1136  {
1137  case XFER_DIALOG_FROM:
1138  tree_view = xferData->from_tree_view;
1139  scroll_win = xferData->from_window;
1140  show_button = xferData->from_show_button;
1141  break;
1142  case XFER_DIALOG_TO:
1143  tree_view = xferData->to_tree_view;
1144  scroll_win = xferData->to_window;
1145  show_button = xferData->to_show_button;
1146  break;
1147  default:
1148  return;
1149  }
1150 
1151  gtk_widget_set_sensitive( GTK_WIDGET(tree_view), FALSE );
1152  gtk_widget_set_sensitive( GTK_WIDGET(show_button), FALSE );
1153 
1154  if (hide)
1155  {
1156  gtk_widget_hide( scroll_win );
1157  gtk_widget_hide( GTK_WIDGET(show_button) );
1158  }
1159 }
1160 
1161 
1162 /********************************************************************\
1163  * gnc_xfer_dialog_lock_from_account_tree *
1164  * prevent changes to the from account tree in an xfer dialog *
1165  * *
1166  * Args: xferData - xfer dialog structure *
1167  * Return: none *
1168 \********************************************************************/
1169 void
1170 gnc_xfer_dialog_lock_from_account_tree(XferDialog *xferData)
1171 {
1172  gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_FROM, FALSE);
1173 }
1174 
1175 
1176 /********************************************************************\
1177  * gnc_xfer_dialog_lock_to_account_tree *
1178  * prevent changes to the to account tree in an xfer dialog *
1179  * *
1180  * Args: xferData - xfer dialog structure *
1181  * Return: none *
1182 \********************************************************************/
1183 void
1184 gnc_xfer_dialog_lock_to_account_tree(XferDialog *xferData)
1185 {
1186  gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_TO, FALSE);
1187 }
1188 
1189 
1190 /********************************************************************\
1191  * gnc_xfer_dialog_hide_from_account_tree *
1192  * prevent changes to the from account tree in an xfer dialog *
1193  * *
1194  * Args: xferData - xfer dialog structure *
1195  * Return: none *
1196 \********************************************************************/
1197 void
1198 gnc_xfer_dialog_hide_from_account_tree(XferDialog *xferData)
1199 {
1200  gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_FROM, TRUE);
1201 }
1202 
1203 
1204 /********************************************************************\
1205  * gnc_xfer_dialog_hide_to_account_tree *
1206  * prevent changes to the to account tree in an xfer dialog *
1207  * *
1208  * Args: xferData - xfer dialog structure *
1209  * Return: none *
1210 \********************************************************************/
1211 void
1212 gnc_xfer_dialog_hide_to_account_tree(XferDialog *xferData)
1213 {
1214  gnc_xfer_dialog_lock_account_tree(xferData, XFER_DIALOG_TO, TRUE);
1215 }
1216 
1217 
1218 /********************************************************************\
1219  * gnc_xfer_dialog_is_exchange_dialog *
1220  * set the dialog as an "exchange-dialog", which means that the *
1221  * Transfer Information table is read-only (and the dialog *
1222  * will NOT create a transaction when it is closed) *
1223  * *
1224  * Args: xferData - xfer dialog structure *
1225  * exch_rate - place to store the exchange rate at exit *
1226  * Return: none *
1227 \********************************************************************/
1228 void
1229 gnc_xfer_dialog_is_exchange_dialog (XferDialog *xferData,
1230  gnc_numeric *exch_rate)
1231 {
1232  GNCAmountEdit *gae;
1233 
1234  g_return_if_fail(xferData);
1235  ENTER("xferData=%p, exch_rate=%p (%s)", xferData, exch_rate,
1236  exch_rate == NULL ? "NULL" : xaccPrintAmount(*exch_rate,
1237  gnc_default_print_info(FALSE)));
1238 
1239  gtk_widget_set_sensitive (xferData->amount_edit, FALSE);
1240  gtk_widget_set_sensitive (xferData->date_entry, FALSE);
1241  gtk_widget_set_sensitive (xferData->num_entry, FALSE);
1242  gtk_widget_set_sensitive (xferData->description_entry, FALSE);
1243  gtk_widget_set_sensitive (xferData->notes_entry, FALSE);
1244  gtk_widget_set_sensitive (xferData->memo_entry, FALSE);
1245 
1246 
1247  gae = GNC_AMOUNT_EDIT (xferData->price_edit);
1248  gtk_widget_grab_focus (gnc_amount_edit_gtk_entry (gae));
1249 
1250  xferData->exch_rate = exch_rate;
1251 
1252  LEAVE(" ");
1253 }
1254 
1255 /********************************************************************\
1256  * gnc_xfer_dialog_set_amount *
1257  * set the amount in the given xfer dialog *
1258  * *
1259  * Args: xferData - xfer dialog structure *
1260  * amount - the amount to set *
1261  * Return: none *
1262 \********************************************************************/
1263 void
1264 gnc_xfer_dialog_set_amount(XferDialog *xferData, gnc_numeric amount)
1265 {
1266  Account * account;
1267 
1268  if (xferData == NULL)
1269  return;
1270 
1271  account = gnc_transfer_dialog_get_selected_account (xferData,
1272  XFER_DIALOG_FROM);
1273  if (account == NULL)
1274  gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
1275 
1276  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->amount_edit), amount);
1277 }
1278 void gnc_xfer_dialog_set_amount_sensitive(XferDialog *xferData,
1279  gboolean is_sensitive)
1280 {
1281  g_assert(xferData);
1282  gtk_widget_set_sensitive(gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT (xferData->amount_edit)), is_sensitive);
1283 }
1284 
1285 static void
1286 gnc_xfer_dialog_set_fetch_sensitive (GtkWidget *fetch)
1287 {
1289  {
1290  gtk_widget_set_sensitive (fetch, TRUE);
1291  gtk_widget_set_tooltip_text (fetch, _("Retrieve the current online quote. This will fail if there is a manually-created price for today."));
1292  return;
1293  }
1294  gtk_widget_set_sensitive (fetch, FALSE);
1295  gtk_widget_set_tooltip_text (fetch, _("Finance::Quote must be installed to enable this button."));
1296  return;
1297 }
1298 
1299 /********************************************************************\
1300  * gnc_xfer_dialog_set_description *
1301  * set the description in the given xfer dialog *
1302  * *
1303  * Args: xferData - xfer dialog structure *
1304  * description - the description to set *
1305  * Return: none *
1306 \********************************************************************/
1307 void
1308 gnc_xfer_dialog_set_description(XferDialog *xferData, const char *description)
1309 {
1310  if (xferData == NULL)
1311  return;
1312 
1313  gtk_entry_set_text(GTK_ENTRY(xferData->description_entry), description);
1314  gnc_quickfill_insert( xferData->qf, description, QUICKFILL_LIFO );
1315 }
1316 
1317 /********************************************************************\
1318  * gnc_xfer_dialog_set_memo *
1319  * set the memo in the given xfer dialog *
1320  * *
1321  * Args: xferData - xfer dialog structure *
1322  * memo - the memo to set *
1323  * Return: none *
1324 \********************************************************************/
1325 void
1326 gnc_xfer_dialog_set_memo(XferDialog *xferData, const char *memo)
1327 {
1328  if (xferData == NULL)
1329  return;
1330 
1331  gtk_entry_set_text(GTK_ENTRY(xferData->memo_entry), memo);
1332  /* gnc_quickfill_insert( xferData->qf, memo, QUICKFILL_LIFO ); */
1333 }
1334 
1335 /********************************************************************\
1336  * gnc_xfer_dialog_set_num *
1337  * set the num in the given xfer dialog *
1338  * *
1339  * Args: xferData - xfer dialog structure *
1340  * num - the num to set *
1341  * Return: none *
1342 \********************************************************************/
1343 void
1344 gnc_xfer_dialog_set_num(XferDialog *xferData, const char *num)
1345 {
1346  if (xferData == NULL)
1347  return;
1348 
1349  gtk_entry_set_text(GTK_ENTRY(xferData->num_entry), num);
1350  /* gnc_quickfill_insert( xferData->qf, num, QUICKFILL_LIFO ); */
1351 }
1352 
1353 /********************************************************************\
1354  * gnc_xfer_dialog_set_date *
1355  * set the date in the given xfer dialog *
1356  * *
1357  * Args: xferData - xfer dialog structure *
1358  * set_date - the date to set *
1359  * Return: none *
1360 \********************************************************************/
1361 void
1362 gnc_xfer_dialog_set_date(XferDialog *xferData, time64 set_date)
1363 {
1364  if (xferData == NULL)
1365  return;
1366 
1367  gnc_date_edit_set_time( GNC_DATE_EDIT(xferData->date_entry), set_date );
1368 }
1369 void gnc_xfer_dialog_set_date_sensitive(XferDialog *xferData,
1370  gboolean is_sensitive)
1371 {
1372  g_assert(xferData);
1373  gtk_widget_set_sensitive (xferData->date_entry, is_sensitive);
1374 }
1375 
1376 void
1377 gnc_xfer_dialog_set_price_edit(XferDialog *xferData, gnc_numeric price_value)
1378 {
1379  if (xferData == NULL)
1380  return;
1381 
1382  if (gnc_numeric_zero_p (price_value))
1383  return;
1384 
1385  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (xferData->price_edit),
1386  price_value);
1387 
1388  gnc_xfer_update_to_amount (xferData);
1389 }
1390 
1391 static gboolean
1392 check_accounts (XferDialog* xferData, Account* from_account,
1393  Account* to_account)
1394 {
1395  if ((from_account == NULL) || (to_account == NULL))
1396  {
1397  const char *message = _("You must specify an account to transfer from, "
1398  "or to, or both, for this transaction. "
1399  "Otherwise, it will not be recorded.");
1400  gnc_error_dialog (GTK_WINDOW (xferData->dialog), "%s", message);
1401  LEAVE("bad account");
1402  return FALSE;
1403  }
1404 
1405  if (from_account == to_account)
1406  {
1407  const char *message = _("You can't transfer from and to the same "
1408  "account!");
1409  gnc_error_dialog (GTK_WINDOW (xferData->dialog), "%s", message);
1410  LEAVE("same account");
1411  return FALSE;
1412  }
1413 
1414  if (xaccAccountGetPlaceholder(from_account) ||
1415  xaccAccountGetPlaceholder(to_account))
1416  {
1417  const char *placeholder_format =
1418  _("The account %s does not allow transactions.");
1419  char *name;
1420 
1421  if (xaccAccountGetPlaceholder(from_account))
1422  name = gnc_account_get_full_name(from_account);
1423  else
1424  name = gnc_account_get_full_name(to_account);
1425  gnc_error_dialog (GTK_WINDOW (xferData->dialog), placeholder_format, name);
1426  g_free(name);
1427  LEAVE("placeholder");
1428  return FALSE;
1429  }
1430 
1431  if (!gnc_commodity_is_iso (xferData->from_commodity))
1432  {
1433  const char *message =
1434  _("You can't transfer from a non-currency account. "
1435  "Try reversing the \"from\" and \"to\" accounts "
1436  "and making the \"amount\" negative.");
1437  gnc_error_dialog (GTK_WINDOW (xferData->dialog), "%s", message);
1438  LEAVE("non-currency");
1439  return FALSE;
1440  }
1441  return TRUE;
1442 }
1443 
1444 static gboolean
1445 check_edit(XferDialog *xferData)
1446 {
1447  if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->price_edit), NULL))
1448  {
1449  if (gtk_toggle_button_get_active
1450  (GTK_TOGGLE_BUTTON(xferData->price_radio)))
1451  {
1452  gnc_parse_error_dialog (xferData, _("You must enter a valid price."));
1453  LEAVE("invalid price");
1454  return FALSE;
1455  }
1456  }
1457 
1458  if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->to_amount_edit), NULL))
1459  {
1460  if (gtk_toggle_button_get_active
1461  (GTK_TOGGLE_BUTTON(xferData->amount_radio)))
1462  {
1463  gnc_parse_error_dialog (xferData,
1464  _("You must enter a valid 'to' amount."));
1465  LEAVE("invalid to amount");
1466  return FALSE;
1467  }
1468  }
1469  return TRUE;
1470 }
1471 
1472 static void
1473 create_transaction(XferDialog *xferData, time64 time,
1474  Account *from_account, Account* to_account,
1475  gnc_numeric amount, gnc_numeric to_amount)
1476 {
1477  Transaction *trans;
1478  Split *from_split;
1479  Split *to_split;
1480  const char *string;
1481  /* Create the transaction */
1482  trans = xaccMallocTransaction(xferData->book);
1483 
1484  xaccTransBeginEdit(trans);
1485 
1486  xaccTransSetCurrency(trans, xferData->from_commodity);
1488 
1489  /* Trans-Num or Split-Action set with gnc_set_num_action below per book
1490  * option */
1491 
1492  string = gtk_entry_get_text(GTK_ENTRY(xferData->description_entry));
1493  xaccTransSetDescription(trans, string);
1494 
1495  /* create from split */
1496  from_split = xaccMallocSplit(xferData->book);
1497  xaccTransAppendSplit(trans, from_split);
1498 
1499  /* create to split */
1500  to_split = xaccMallocSplit(xferData->book);
1501  xaccTransAppendSplit(trans, to_split);
1502 
1503  xaccAccountBeginEdit(from_account);
1504  xaccAccountInsertSplit(from_account, from_split);
1505 
1506  xaccAccountBeginEdit(to_account);
1507  xaccAccountInsertSplit(to_account, to_split);
1508 
1509  xaccSplitSetBaseValue(from_split, gnc_numeric_neg (amount),
1510  xferData->from_commodity);
1511  xaccSplitSetBaseValue(to_split, amount, xferData->from_commodity);
1512  xaccSplitSetBaseValue(to_split, to_amount, xferData->to_commodity);
1513 
1514  /* Set the transaction number or split action field based on book option*/
1515  string = gtk_entry_get_text(GTK_ENTRY(xferData->num_entry));
1516  gnc_set_num_action (trans, from_split, string, NULL);
1517 
1518  /* Set the transaction notes */
1519  string = gtk_entry_get_text(GTK_ENTRY(xferData->notes_entry));
1520  xaccTransSetNotes(trans, string);
1521 
1522  /* Set the memo fields */
1523  string = gtk_entry_get_text(GTK_ENTRY(xferData->memo_entry));
1524  xaccSplitSetMemo(from_split, string);
1525  xaccSplitSetMemo(to_split, string);
1526 
1527  /* finish transaction */
1528  xaccTransCommitEdit(trans);
1529  xaccAccountCommitEdit(from_account);
1530  xaccAccountCommitEdit(to_account);
1531 
1532  /* If there is a registered callback handler that should be
1533  notified of the newly created Transaction, call it now. */
1534  if (xferData->transaction_cb)
1535  xferData->transaction_cb(trans, xferData->transaction_user_data);
1536 }
1537 
1538 static gnc_numeric
1539 swap_commodities(gnc_commodity **from, gnc_commodity **to, gnc_numeric value)
1540 {
1541  gnc_commodity *tmp = *to;
1542 
1543  *to = *from;
1544  *from = tmp;
1545  value = gnc_numeric_invert(value);
1546  return value;
1547 }
1548 
1549 static void
1550 update_price(XferDialog *xferData, PriceReq *pr)
1551 {
1552  gnc_commodity *from = xferData->from_commodity;
1553  gnc_commodity *to = xferData->to_commodity;
1554  gnc_numeric value = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->price_edit));
1555  gnc_numeric price_value = gnc_price_get_value(pr->price);
1556  gnc_numeric rounded_pr_value = round_price(pr->from, pr->to, price_value);
1557  gnc_numeric rounded_value;
1558 
1559  if (gnc_price_get_source(pr->price) < xferData->price_source)
1560  {
1561  PINFO("Existing price is preferred, so won't supersede.");
1562  gnc_price_unref (pr->price);
1563  return;
1564  }
1565 
1566  if (pr->reverse)
1567  value = swap_commodities(&from, &to, value);
1568  /* Test the rounded values for equality to minimize price-dithering. */
1569  rounded_value = round_price(from, to, value);
1570  if (gnc_numeric_equal(rounded_value, rounded_pr_value))
1571  {
1572  PINFO("Same price for %s in %s",
1573  gnc_commodity_get_mnemonic(pr->from),
1574  gnc_commodity_get_mnemonic(pr->to));
1575  gnc_price_unref (pr->price);
1576  return;
1577  }
1578  gnc_price_begin_edit (pr->price);
1579  gnc_price_set_time64 (pr->price, pr->time);
1580  gnc_price_set_typestr(pr->price, xferData->price_type);
1581  gnc_price_set_value (pr->price, value);
1582  gnc_price_commit_edit (pr->price);
1583  PINFO("Updated price: 1 %s = %f %s",
1584  gnc_commodity_get_mnemonic(pr->from),
1585  gnc_numeric_to_double(gnc_price_get_value(pr->price)),
1586  gnc_commodity_get_mnemonic(pr->to));
1587  gnc_price_unref (pr->price);
1588 }
1589 
1590 static void
1591 new_price(XferDialog *xferData, time64 time)
1592 {
1593  GNCPrice *price = NULL;
1594  gnc_commodity *from = xferData->from_commodity;
1595  gnc_commodity *to = xferData->to_commodity;
1596  gnc_numeric value = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->price_edit));
1597 
1598  value = gnc_numeric_abs(value);
1599 
1600  /* store price against the non currency commodity */
1602  value = swap_commodities (&from, &to, value);
1603  /* store rate against default currency if present */
1604  else if (from == gnc_default_currency() && to != gnc_default_currency())
1605  value = swap_commodities (&from, &to, value);
1606 
1607  price = gnc_price_create (xferData->book);
1608  gnc_price_begin_edit (price);
1609  gnc_price_set_commodity (price, from);
1610  gnc_price_set_currency (price, to);
1611  gnc_price_set_time64 (price, time);
1612  gnc_price_set_source (price, xferData->price_source);
1613  gnc_price_set_typestr (price, xferData->price_type);
1614  gnc_price_set_value (price, value);
1615  gnc_pricedb_add_price (xferData->pricedb, price);
1616  gnc_price_commit_edit (price);
1617  PINFO("Created price: 1 %s = %f %s", gnc_commodity_get_mnemonic(from),
1619  gnc_price_unref (price);
1620 }
1621 
1622 static void
1623 create_price(XferDialog *xferData, time64 time)
1624 {
1625  PriceReq pr;
1626 
1627 /* Bail in the unlikely event that both currencies have joined the Euro. */
1628  if (gnc_is_euro_currency (xferData->from_commodity) &&
1629  gnc_is_euro_currency (xferData->to_commodity))
1630  return;
1631 
1632  price_request_from_xferData(&pr, xferData);
1633  if (lookup_price(&pr, SAME_DAY))
1634  {
1635  update_price(xferData, &pr);
1636  return;
1637  }
1638  new_price (xferData, time);
1639 }
1640 
1641 void
1642 gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data)
1643 {
1644  g_return_if_fail (data);
1645  auto xferData = static_cast<XferDialog *> (data);
1646 
1647  ENTER(" ");
1648 
1649  if (response == GTK_RESPONSE_APPLY)
1650  {
1651  LEAVE("fetching exchange rate");
1652  return;
1653  }
1654 
1655  /* We're closing, either by cancel, esc or ok
1656  * Remove date changed handler to prevent it from triggering
1657  * on a focus-out event while we're already destroying the widget */
1658  g_signal_handlers_disconnect_by_func (G_OBJECT (xferData->date_entry),
1659  (gpointer)gnc_xfer_date_changed_cb,
1660  xferData);
1661 
1662  if (response != GTK_RESPONSE_OK)
1663  {
1664  gnc_close_gui_component_by_data (DIALOG_TRANSFER_CM_CLASS, xferData);
1665  LEAVE("cancel, etc.");
1666  return;
1667  }
1668 
1669  auto from_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_FROM);
1670  auto to_account = gnc_transfer_dialog_get_selected_account (xferData, XFER_DIALOG_TO);
1671 
1672  if (xferData->exch_rate == NULL &&
1673  !check_accounts(xferData, from_account, to_account))
1674  return;
1675 
1676  if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (xferData->amount_edit), NULL))
1677  {
1678  gnc_parse_error_dialog (xferData, _("You must enter a valid amount."));
1679  LEAVE("no amount");
1680  return;
1681  }
1682 
1683  auto amount = gnc_amount_edit_get_amount(GNC_AMOUNT_EDIT(xferData->amount_edit));
1684 
1685  if (gnc_numeric_zero_p (amount))
1686  {
1687  const char *message = _("You must enter an amount to transfer.");
1688  gnc_error_dialog (GTK_WINDOW (xferData->dialog), "%s", message);
1689  LEAVE("invalid from amount");
1690  return;
1691  }
1692 
1693  GDate date;
1694  g_date_clear (&date, 1);
1695  gnc_date_edit_get_gdate (GNC_DATE_EDIT (xferData->date_entry), &date);
1696  auto time = gdate_to_time64 (date);
1697 
1698  auto to_amount = amount;
1699  if (!gnc_commodity_equiv(xferData->from_commodity, xferData->to_commodity))
1700  {
1701  if (!check_edit(xferData))
1702  return;
1703  to_amount = gnc_amount_edit_get_amount
1704  (GNC_AMOUNT_EDIT(xferData->to_amount_edit));
1705  }
1706 
1707  gnc_suspend_gui_refresh ();
1708 
1709  if (xferData->exch_rate)
1710  {
1711  /* If we've got the price-button set, then make sure we update the
1712  * to-amount before we use it.
1713  */
1714  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(xferData->price_radio)))
1715  gnc_xfer_update_to_amount(xferData);
1716 
1717  auto price_value = gnc_xfer_dialog_compute_price_value(xferData);
1718  gnc_amount_edit_set_amount(GNC_AMOUNT_EDIT(xferData->price_edit),
1719  price_value);
1720  *(xferData->exch_rate) = gnc_numeric_abs(price_value);
1721  }
1722  else
1723  create_transaction (xferData, time, from_account, to_account,
1724  amount, to_amount);
1725  /* try to save this to the pricedb */
1726  if (xferData->pricedb && !gnc_commodity_equal (xferData->from_commodity,
1727  xferData->to_commodity))
1728  create_price(xferData, time);
1729  /* Refresh everything */
1730  gnc_resume_gui_refresh ();
1731 
1732  DEBUG("close component");
1733  gnc_close_gui_component_by_data (DIALOG_TRANSFER_CM_CLASS, xferData);
1734  LEAVE("ok");
1735 }
1736 
1737 void
1738 gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data)
1739 {
1740  auto xferData = static_cast<XferDialog *> (data);
1741 
1742  /* Notify transaction callback to unregister here */
1743  if (xferData->transaction_cb)
1744  xferData->transaction_cb(NULL, xferData->transaction_user_data);
1745 
1746  auto entry = gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(xferData->amount_edit));
1747  g_signal_handlers_disconnect_matched (G_OBJECT (entry), G_SIGNAL_MATCH_DATA,
1748  0, 0, NULL, NULL, xferData);
1749 
1750  entry = gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(xferData->price_edit));
1751  g_signal_handlers_disconnect_matched (G_OBJECT (entry), G_SIGNAL_MATCH_DATA,
1752  0, 0, NULL, NULL, xferData);
1753 
1754  entry = gnc_amount_edit_gtk_entry(GNC_AMOUNT_EDIT(xferData->to_amount_edit));
1755  g_signal_handlers_disconnect_matched (G_OBJECT (entry), G_SIGNAL_MATCH_DATA,
1756  0, 0, NULL, NULL, xferData);
1757 
1758  entry = xferData->description_entry;
1759  g_signal_handlers_disconnect_matched (G_OBJECT (entry), G_SIGNAL_MATCH_DATA,
1760  0, 0, NULL, NULL, xferData);
1761 
1762  DEBUG("unregister component");
1763  gnc_unregister_gui_component_by_data (DIALOG_TRANSFER_CM_CLASS, xferData);
1764 
1765  gnc_quickfill_destroy (xferData->qf);
1766  xferData->qf = NULL;
1767 
1768  if (xferData->desc_selection_source_id)
1769  g_source_remove (xferData->desc_selection_source_id);
1770 
1771  g_free(xferData);
1772  xferData = NULL;
1773 
1774  DEBUG("xfer dialog destroyed");
1775 }
1776 
1777 
1778 void
1779 gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData)
1780 {
1781  g_return_if_fail (xferData);
1782 
1783  ENTER(" ");
1784 
1785  try
1786  {
1787  GncQuotes quotes;
1788  gnc_set_busy_cursor(nullptr, TRUE);
1789  quotes.fetch(xferData->book);
1790  gnc_unset_busy_cursor(nullptr);
1791  }
1792  catch (const GncQuoteException& err)
1793  {
1794  gnc_unset_busy_cursor(nullptr);
1795  PERR("Price retrieval failed: %s", err.what());
1796  gnc_error_dialog(GTK_WINDOW(xferData->dialog), _("Price retrieval failed: %s"), err.what());
1797  }
1798 
1799  /*the results should be in the price db now, but don't crash if not. */
1800  PriceReq pr;
1801  price_request_from_xferData(&pr, xferData);
1802  if (lookup_price(&pr, LATEST))
1803  {
1804  gnc_numeric price_value = gnc_price_get_value(pr.price);
1805  if (pr.reverse)
1806  price_value = gnc_numeric_invert(price_value);
1807  gnc_xfer_dialog_set_price_edit(xferData, price_value);
1808  gnc_price_unref (pr.price);
1809  }
1810 
1811  LEAVE("quote retrieved");
1812 
1813 }
1814 
1815 static void
1816 gnc_xfer_dialog_create(GtkWidget *parent, XferDialog *xferData)
1817 {
1818  GtkBuilder *builder;
1819  gboolean use_accounting_labels;
1820  g_return_if_fail(to_info == NULL && from_info == NULL);
1821 
1822  use_accounting_labels = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
1823  GNC_PREF_ACCOUNTING_LABELS);
1824 
1825  ENTER(" ");
1826  builder = gtk_builder_new();
1827  gnc_builder_add_from_file (builder, "dialog-transfer.glade", "transfer_dialog");
1828 
1829  xferData->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "transfer_dialog"));
1830  g_object_set_data_full (G_OBJECT (xferData->dialog), "builder", builder, g_object_unref);
1831 
1832  // Set the name for this dialog so it can be easily manipulated with css
1833  gtk_widget_set_name (GTK_WIDGET(xferData->dialog), "gnc-id-transfer");
1834  gnc_widget_style_context_add_class (GTK_WIDGET(xferData->dialog), "gnc-class-securities");
1835 
1836  /* parent */
1837  if (parent != NULL)
1838  gtk_window_set_transient_for (GTK_WINDOW (xferData->dialog), GTK_WINDOW (parent));
1839 
1840  /* default to quickfilling off of the "From" account. */
1841  xferData->quickfill = XFER_DIALOG_FROM;
1842 
1843  xferData->transferinfo_label = GTK_WIDGET(gtk_builder_get_object (builder, "transferinfo-label"));
1844 
1845  xferData->fetch_button = GTK_WIDGET(gtk_builder_get_object (builder, "fetch"));
1846  gnc_xfer_dialog_set_fetch_sensitive (xferData->fetch_button);
1847 
1848  /* amount & date widgets */
1849  {
1850  GtkWidget *amount;
1851  GtkWidget *entry;
1852  GtkWidget *date;
1853  GtkWidget *hbox;
1854 
1855  amount = gnc_amount_edit_new();
1856  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "amount_hbox"));
1857  gtk_box_pack_end(GTK_BOX(hbox), amount, TRUE, TRUE, 0);
1858  gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (amount), TRUE);
1859  xferData->amount_edit = amount;
1860 
1861  entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (amount));
1862  gtk_entry_set_activates_default (GTK_ENTRY(entry), TRUE);
1863  g_signal_connect (G_OBJECT (entry), "focus-out-event",
1864  G_CALLBACK (gnc_xfer_amount_update_cb), xferData);
1865 
1866  date = gnc_date_edit_new(time (NULL), FALSE, FALSE);
1867  gnc_date_activates_default (GNC_DATE_EDIT(date), TRUE);
1868  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
1869 
1870  gtk_box_pack_end(GTK_BOX(hbox), date, TRUE, TRUE, 0);
1871  xferData->date_entry = date;
1872  g_signal_connect (G_OBJECT (date), "date_changed",
1873  G_CALLBACK (gnc_xfer_date_changed_cb), xferData);
1874  }
1875 
1876  {
1877  GtkWidget *entry;
1878 
1879  entry = GTK_WIDGET(gtk_builder_get_object (builder, "num_entry"));
1880  xferData->num_entry = entry;
1881 
1882  entry = GTK_WIDGET(gtk_builder_get_object (builder, "description_entry"));
1883  xferData->description_entry = entry;
1884 
1885  entry = GTK_WIDGET(gtk_builder_get_object (builder, "notes_entry"));
1886  xferData->notes_entry = entry;
1887 
1888  entry = GTK_WIDGET(gtk_builder_get_object (builder, "memo_entry"));
1889  xferData->memo_entry = entry;
1890  }
1891 
1892  /* from and to */
1893  {
1894  GtkWidget *label;
1895  gchar *text;
1896 
1897  to_info = g_new0(AccountTreeFilterInfo, 1);
1898  from_info = g_new0(AccountTreeFilterInfo, 1);
1899 
1900  gnc_xfer_dialog_fill_tree_view (xferData, XFER_DIALOG_TO);
1901  gnc_xfer_dialog_fill_tree_view (xferData, XFER_DIALOG_FROM);
1902 
1903  /* Reverse from and to account trees when in "accountant" mode,
1904  see comment in function gnc_xfer_dialog_fill_tree_table */
1905  if (use_accounting_labels)
1906  {
1907  label = GTK_WIDGET(gtk_builder_get_object (builder, "right_trans_label"));
1908  xferData->from_transfer_label = label;
1909 
1910  label = GTK_WIDGET(gtk_builder_get_object (builder, "left_trans_label"));
1911  xferData->to_transfer_label = label;
1912 
1913  text = g_strconcat ("<b>", _("Credit Account"), "</b>", NULL);
1914  gtk_label_set_markup (GTK_LABEL (xferData->from_transfer_label), text);
1915  g_free (text);
1916 
1917  text = g_strconcat ("<b>", _("Debit Account"), "</b>", NULL);
1918  gtk_label_set_markup (GTK_LABEL (xferData->to_transfer_label), text);
1919  g_free (text);
1920 
1921  label = GTK_WIDGET(gtk_builder_get_object (builder, "right_currency_label"));
1922  xferData->from_currency_label = label;
1923 
1924  label = GTK_WIDGET(gtk_builder_get_object (builder, "left_currency_label"));
1925  xferData->to_currency_label = label;
1926  }
1927  else
1928  {
1929  label = GTK_WIDGET(gtk_builder_get_object (builder, "left_trans_label"));
1930  xferData->from_transfer_label = label;
1931 
1932  label = GTK_WIDGET(gtk_builder_get_object (builder, "right_trans_label"));
1933  xferData->to_transfer_label = label;
1934 
1935  text = g_strconcat ("<b>", _("Transfer From"), "</b>", NULL);
1936  gtk_label_set_markup (GTK_LABEL (xferData->from_transfer_label), text);
1937  g_free (text);
1938 
1939  text = g_strconcat ("<b>", _("Transfer To"), "</b>", NULL);
1940  gtk_label_set_markup (GTK_LABEL (xferData->to_transfer_label), text);
1941 
1942  label = GTK_WIDGET(gtk_builder_get_object (builder, "left_currency_label"));
1943  xferData->from_currency_label = label;
1944 
1945  label = GTK_WIDGET(gtk_builder_get_object (builder, "right_currency_label"));
1946  xferData->to_currency_label = label;
1947  }
1948 
1949  label = GTK_WIDGET(gtk_builder_get_object (builder, "conv_forward"));
1950  xferData->conv_forward = label;
1951 
1952  label = GTK_WIDGET(gtk_builder_get_object (builder, "conv_reverse"));
1953  xferData->conv_reverse = label;
1954  }
1955 
1956  /* optional intermediate currency account */
1957  {
1958  GtkWidget *table;
1959  GtkWidget *entry;
1960  GtkWidget *edit;
1961  GtkWidget *hbox;
1962  GtkWidget *button;
1963 
1964  table = GTK_WIDGET(gtk_builder_get_object (builder, "curr_transfer_table"));
1965  xferData->curr_xfer_table = table;
1966 
1967  edit = gnc_amount_edit_new();
1968  gnc_amount_edit_set_print_info(GNC_AMOUNT_EDIT(edit),
1969  gnc_default_print_info (FALSE));
1970  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "price_hbox"));
1971  gtk_box_pack_start(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
1972  xferData->price_edit = edit;
1973  entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (edit));
1974  g_signal_connect (G_OBJECT (entry), "focus-out-event",
1975  G_CALLBACK (gnc_xfer_price_update_cb), xferData);
1976  gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE);
1977 
1978  edit = gnc_amount_edit_new();
1979  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "right_amount_hbox"));
1980  gtk_box_pack_start(GTK_BOX(hbox), edit, TRUE, TRUE, 0);
1981  xferData->to_amount_edit = edit;
1982  entry = gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (edit));
1983  g_signal_connect (G_OBJECT (entry), "focus-out-event",
1984  G_CALLBACK (gnc_xfer_to_amount_update_cb), xferData);
1985  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
1986 
1987  button = GTK_WIDGET(gtk_builder_get_object (builder, "price_radio"));
1988  xferData->price_radio = button;
1989 
1990  button = GTK_WIDGET(gtk_builder_get_object (builder, "amount_radio"));
1991  xferData->amount_radio = button;
1992 
1993  if (use_accounting_labels)
1994  {
1995  gtk_label_set_text(GTK_LABEL(gtk_bin_get_child (GTK_BIN(xferData->amount_radio))),
1996  _("Debit Amount"));
1997  }
1998  else
1999  {
2000  gtk_label_set_text(GTK_LABEL(gtk_bin_get_child (GTK_BIN(xferData->amount_radio))),
2001  _("To Amount"));
2002  }
2003  }
2004 
2005  gtk_builder_connect_signals(builder, xferData);
2006  gnc_restore_window_size (GNC_PREFS_GROUP,
2007  GTK_WINDOW (xferData->dialog), GTK_WINDOW (parent));
2008  LEAVE(" ");
2009 }
2010 
2011 static void
2012 close_handler (gpointer user_data)
2013 {
2014  auto xferData = static_cast<XferDialog *> (user_data);
2015 
2016  ENTER(" ");
2017  auto dialog = GTK_WIDGET (xferData->dialog);
2018 
2019  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog));
2020  gtk_widget_hide (dialog);
2021  gnc_xfer_dialog_close_cb(GTK_DIALOG(dialog), xferData);
2022  gtk_widget_destroy (dialog);
2023  g_free (to_info);
2024  to_info = NULL;
2025  g_free (from_info);
2026  from_info = NULL;
2027  LEAVE(" ");
2028 }
2029 
2030 /********************************************************************\
2031  * gnc_xfer_dialog *
2032  * opens up a window to do an automatic transfer between accounts *
2033  * *
2034  * Args: parent - the parent of the window to be created *
2035  * initial - the initial account in the from/to fields *
2036  * Return: XferDialog structure *
2037 \********************************************************************/
2038 XferDialog *
2039 gnc_xfer_dialog (GtkWidget * parent, Account * initial)
2040 {
2041  XferDialog *xferData;
2042  GNCAmountEdit *gae;
2043  GtkWidget *amount_entry;
2044  QofBook *book = NULL;
2045 
2046  xferData = g_new0 (XferDialog, 1);
2047 
2048  xferData->desc_start_selection = 0;
2049  xferData->desc_end_selection = 0;
2050  xferData->desc_selection_source_id = 0;
2051  xferData->quickfill = XFER_DIALOG_FROM;
2052  xferData->transaction_cb = NULL;
2053 
2054  if (initial)
2055  {
2056  book = gnc_account_get_book (initial);
2057  }
2058  else
2059  {
2060  book = gnc_get_current_book ();
2061  }
2062 
2063  xferData->book = book;
2064  xferData->pricedb = gnc_pricedb_get_db (book);
2065 
2066  gnc_xfer_dialog_create(parent, xferData);
2067 
2068  DEBUG("register component");
2069  gnc_register_gui_component (DIALOG_TRANSFER_CM_CLASS,
2070  NULL, close_handler, xferData);
2071 
2072  gae = GNC_AMOUNT_EDIT(xferData->amount_edit);
2073  amount_entry = gnc_amount_edit_gtk_entry (gae);
2074 
2075  gtk_widget_grab_focus(amount_entry);
2076 
2077  gnc_xfer_dialog_select_from_account(xferData, initial);
2078  gnc_xfer_dialog_select_to_account(xferData, initial);
2079 
2080  gnc_xfer_dialog_curr_acct_activate(xferData);
2081 
2082  gtk_widget_show_all(xferData->dialog);
2083 
2084  gnc_window_adjust_for_screen(GTK_WINDOW(xferData->dialog));
2085 
2086  return xferData;
2087 }
2088 
2089 void
2090 gnc_xfer_dialog_close( XferDialog *xferData )
2091 {
2092  if ( xferData )
2093  {
2094  DEBUG("close component");
2095  gtk_dialog_response( GTK_DIALOG(xferData->dialog), GTK_RESPONSE_NONE );
2096  }
2097 }
2098 
2099 void
2100 gnc_xfer_dialog_set_title( XferDialog *xferData, const gchar *title )
2101 {
2102  if ( xferData && title )
2103  {
2104  gtk_window_set_title (GTK_WINDOW (xferData->dialog), title);
2105  }
2106 }
2107 
2108 void
2109 gnc_xfer_dialog_set_information_label( XferDialog *xferData,
2110  const gchar *text )
2111 {
2112  if (xferData && text)
2113  {
2114  gchar *markup_text = g_strdup_printf ("<b>%s</b>", text);
2115  gtk_label_set_markup (GTK_LABEL (xferData->transferinfo_label), markup_text);
2116  g_free (markup_text);
2117  }
2118 }
2119 
2120 
2121 static void
2122 gnc_xfer_dialog_set_account_label( XferDialog *xferData,
2123  const gchar *text,
2124  XferDirection direction )
2125 {
2126  if (xferData && text)
2127  {
2128  gchar *markup_text = g_strdup_printf ("<b>%s</b>", text);
2129  gtk_label_set_markup (GTK_LABEL ((direction == XFER_DIALOG_FROM ?
2130  xferData->from_transfer_label :
2131  xferData->to_transfer_label)),
2132  markup_text);
2133  g_free (markup_text);
2134  }
2135 }
2136 
2137 void
2138 gnc_xfer_dialog_set_from_account_label( XferDialog *xferData,
2139  const gchar *label )
2140 {
2141  gnc_xfer_dialog_set_account_label (xferData, label, XFER_DIALOG_FROM);
2142 }
2143 
2144 void
2145 gnc_xfer_dialog_set_to_account_label( XferDialog *xferData,
2146  const gchar *label )
2147 {
2148  gnc_xfer_dialog_set_account_label (xferData, label, XFER_DIALOG_TO);
2149 }
2150 
2151 void
2152 gnc_xfer_dialog_set_from_show_button_active( XferDialog *xferData,
2153  gboolean set_value )
2154 {
2155  if ( xferData && xferData->from_show_button )
2156  {
2157  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(xferData->from_show_button),
2158  set_value );
2159  }
2160 }
2161 
2162 void
2163 gnc_xfer_dialog_set_to_show_button_active( XferDialog *xferData,
2164  gboolean set_value )
2165 {
2166  if ( xferData && xferData->to_show_button )
2167  {
2168  gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(xferData->to_show_button),
2169  set_value );
2170  }
2171 }
2172 
2173 /* Add a button with a user-specified label and "clicked" callback */
2174 void gnc_xfer_dialog_add_user_specified_button( XferDialog *xferData,
2175  const gchar *label,
2176  GCallback callback,
2177  gpointer user_data )
2178 {
2179  if ( xferData && label && callback )
2180  {
2181  auto builder = static_cast<GtkBuilder *> (g_object_get_data (G_OBJECT (xferData->dialog), "builder"));
2182  auto button = gtk_button_new_with_label( label );
2183  auto box = GTK_WIDGET (gtk_builder_get_object (builder,
2184  "transfermain-vbox" ));
2185  gtk_box_pack_end( GTK_BOX(box), button, FALSE, FALSE, 0 );
2186  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (callback), user_data);
2187  gtk_widget_show( button );
2188  }
2189 }
2190 
2191 void gnc_xfer_dialog_toggle_currency_table( XferDialog *xferData,
2192  gboolean show_table )
2193 {
2194  if (xferData && xferData->curr_xfer_table)
2195  {
2196  if (show_table)
2197  gtk_widget_show(xferData->curr_xfer_table);
2198  else
2199  gtk_widget_hide(xferData->curr_xfer_table);
2200  }
2201 }
2202 
2203 
2204 /* helper function */
2205 static gboolean
2206 find_xfer (gpointer find_data, gpointer user_data)
2207 {
2208  return( find_data == user_data );
2209 }
2210 
2211 /* Run the dialog until the user has either successfully completed the
2212  * transaction (just clicking OK doesn't always count) or clicked Cancel.
2213  * Return TRUE if the transaction was a success, FALSE otherwise.
2214  */
2215 gboolean gnc_xfer_dialog_run_until_done( XferDialog *xferData )
2216 {
2217  GtkDialog *dialog;
2218  gint count, response;
2219 
2220  ENTER("xferData=%p", xferData);
2221  if ( xferData == NULL )
2222  {
2223  LEAVE("bad args");
2224  return( FALSE );
2225  }
2226 
2227  dialog = GTK_DIALOG (xferData->dialog);
2228 
2229  /*
2230  * We need to call the response_cb function by hand. Calling it
2231  * automatically on a button click can destroy the window, and
2232  * that's bad mojo whole gtk_dialog_run is still in control.
2233  */
2234  count = g_signal_handlers_disconnect_by_func(dialog,
2235  (gpointer) gnc_xfer_dialog_response_cb,
2236  xferData);
2237  g_assert(count == 1);
2238 
2239  while ( TRUE )
2240  {
2241  DEBUG("calling gtk_dialog_run");
2242  response = gtk_dialog_run (dialog);
2243  DEBUG("gtk_dialog_run returned %d", response);
2244  gnc_xfer_dialog_response_cb (dialog, response, xferData);
2245 
2246  if ((response != GTK_RESPONSE_OK) && (response != GTK_RESPONSE_APPLY))
2247  {
2248  LEAVE("not ok");
2249  return FALSE;
2250  }
2251 
2252  /* See if the dialog is still there. For various reasons, the
2253  * user could have hit OK but remained in the dialog. We don't
2254  * want to return processing back to anyone else until we clear
2255  * off this dialog, so if the dialog is still there we'll just
2256  * run it again.
2257  */
2258  if ( !gnc_find_first_gui_component( DIALOG_TRANSFER_CM_CLASS,
2259  find_xfer, xferData ) )
2260  {
2261  /* no more dialog, and OK was clicked, so assume it's all good */
2262  LEAVE("ok");
2263  return TRUE;
2264  }
2265 
2266  /* else run the dialog again */
2267  }
2268 
2269  g_assert_not_reached();
2270  return FALSE; /* to satisfy static code analysis */
2271 }
2272 
2273 
2274 /* Indicate that the dialog should quickfill based on the "To" account,
2275  * rather than the default which is the "From" account.
2276  */
2277 
2278 void
2279 gnc_xfer_dialog_quickfill_to_account(XferDialog *xferData,
2280  gboolean qf_to_account )
2281 {
2282  XferDirection old = xferData->quickfill;
2283 
2284  xferData->quickfill = qf_to_account ? XFER_DIALOG_TO : XFER_DIALOG_FROM;
2285 
2286  /* reload the quickfill if necessary */
2287  if ( old != xferData->quickfill )
2288  gnc_xfer_dialog_reload_quickfill( xferData );
2289 }
2290 
2291 static Account *
2292 gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
2293  XferDirection direction)
2294 {
2295  GtkTreeView *tree_view;
2296  Account *account;
2297 
2298  switch (direction)
2299  {
2300  case XFER_DIALOG_FROM:
2301  tree_view = dialog->from_tree_view;
2302  break;
2303  case XFER_DIALOG_TO:
2304  tree_view = dialog->to_tree_view;
2305  break;
2306  default:
2307  g_assert_not_reached ();
2308  return NULL;
2309  }
2310 
2311  account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (tree_view));
2312  return account;
2313 }
2314 
2315 static void
2316 gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
2317  Account *account,
2318  XferDirection direction)
2319 {
2320  GtkTreeView *tree_view;
2321  GtkCheckButton *show_button;
2322  GNCAccountType type;
2323 
2324  if (account == NULL)
2325  return;
2326 
2327  switch (direction)
2328  {
2329  case XFER_DIALOG_FROM:
2330  tree_view = dialog->from_tree_view;
2331  show_button = GTK_CHECK_BUTTON (dialog->from_show_button);
2332  break;
2333  case XFER_DIALOG_TO:
2334  tree_view = dialog->to_tree_view;
2335  show_button = GTK_CHECK_BUTTON (dialog->to_show_button);
2336  break;
2337  default:
2338  g_assert_not_reached ();
2339  return;
2340  }
2341 
2342  type = xaccAccountGetType (account);
2343  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_button),
2344  (type == ACCT_TYPE_EXPENSE) ||
2345  (type == ACCT_TYPE_INCOME));
2346 
2347  gnc_tree_view_account_set_selected_account (GNC_TREE_VIEW_ACCOUNT (tree_view),
2348  account);
2349 }
2350 
2351 
2352 void gnc_xfer_dialog_set_txn_cb(XferDialog *xferData,
2353  gnc_xfer_dialog_cb handler,
2354  gpointer user_data)
2355 {
2356  g_assert(xferData);
2357  xferData->transaction_cb = handler;
2358  xferData->transaction_user_data = user_data;
2359 }
2360 
2361 
2362 
2363 gboolean gnc_xfer_dialog_run_exchange_dialog(
2364  XferDialog *xfer, gnc_numeric *exch_rate, gnc_numeric amount,
2365  Account *reg_acc, Transaction *txn, gnc_commodity *xfer_com,
2366  gboolean expanded)
2367 {
2368  gboolean swap_amounts = FALSE;
2369  gnc_commodity *txn_cur = xaccTransGetCurrency(txn);
2370  gnc_commodity *reg_com = xaccAccountGetCommodity(reg_acc);
2371 
2372  g_return_val_if_fail(txn_cur && GNC_IS_COMMODITY (txn_cur), TRUE);
2373  g_return_val_if_fail(xfer_com && GNC_IS_COMMODITY (xfer_com), TRUE);
2374 
2375  if (xaccTransUseTradingAccounts (txn))
2376  {
2377  /* If we're using commodity trading accounts then "amount" is
2378  really the split's amount and it's in xfer_com commodity.
2379  We need an exchange rate that will convert this amount
2380  into a value in the transaction currency. */
2381  if (gnc_commodity_equal(xfer_com, txn_cur))
2382  {
2383  /* Transaction is in the same currency as the split, exchange
2384  rate is 1. */
2385  *exch_rate = gnc_numeric_create(1, 1);
2386  return FALSE;
2387  }
2388  swap_amounts = expanded;
2389  }
2390 
2391  /* We know that "amount" is always in the reg_com currency.
2392  * Unfortunately it is possible that neither xfer_com or txn_cur are
2393  * the same as reg_com, in which case we need to convert to the txn
2394  * currency... Or, if the register commodity is the xfer_com, then we
2395  * need to flip-flop the commodities and the exchange rates.
2396  */
2397 
2398  else if (gnc_commodity_equal(reg_com, txn_cur))
2399  {
2400  /* we're working in the txn currency. Great. Nothing to do! */
2401  swap_amounts = FALSE;
2402 
2403  }
2404  else if (gnc_commodity_equal(reg_com, xfer_com))
2405  {
2406  /* We're working in the xfer commodity. Great. Just swap the
2407  amounts. */
2408  swap_amounts = TRUE;
2409 
2410  /* XXX: Do we need to check for expanded v. non-expanded
2411  accounts here? */
2412 
2413  }
2414  else
2415  {
2416  /* UGGH -- we're not in either. That means we need to convert
2417  * 'amount' from the register commodity to the txn currency.
2418  */
2419  gnc_numeric rate = xaccTransGetAccountConvRate(txn, reg_acc);
2420 
2421  /* XXX: should we tell the user we've done the conversion? */
2422  amount = gnc_numeric_div(amount, rate,
2423  gnc_commodity_get_fraction(txn_cur),
2425  }
2426 
2427  /* enter the accounts */
2428  if (swap_amounts)
2429  {
2430  gnc_xfer_dialog_select_to_currency(xfer, txn_cur);
2431  gnc_xfer_dialog_select_from_currency(xfer, xfer_com);
2432  if (!gnc_numeric_zero_p(*exch_rate))
2433  *exch_rate = gnc_numeric_invert(*exch_rate);
2434  amount = gnc_numeric_neg(amount);
2435  }
2436  else
2437  {
2438  gnc_xfer_dialog_select_to_currency(xfer, xfer_com);
2439  gnc_xfer_dialog_select_from_currency(xfer, txn_cur);
2440  if (xaccTransUseTradingAccounts ( txn ))
2441  amount = gnc_numeric_neg(amount);
2442  }
2443  gnc_xfer_dialog_hide_to_account_tree(xfer);
2444  gnc_xfer_dialog_hide_from_account_tree(xfer);
2445 
2446  gnc_xfer_dialog_set_amount(xfer, amount);
2447  /* Now that from amount is set, set the to amount. */
2448  gnc_xfer_update_to_amount(xfer);
2449 
2450  /*
2451  * When we flip, we should tell the dialog so it can deal with the
2452  * pricedb properly.
2453  */
2454 
2455  /* Set the exchange rate */
2456  gnc_xfer_dialog_set_price_edit(xfer, *exch_rate);
2457 
2458  /* and run it... */
2459  if (gnc_xfer_dialog_run_until_done(xfer) == FALSE)
2460  return TRUE;
2461  /* If we inverted the rate for the dialog, invert it back. */
2462  if (swap_amounts)
2463  *exch_rate = gnc_numeric_invert(*exch_rate);
2464 
2465  return FALSE;
2466 }
GNCPrice * gnc_pricedb_lookup_day_t64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commodities on the indicated day.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1...
void gnc_quickfill_insert(QuickFill *qf, const char *text, QuickFillSort sort)
Add the string "text" to the collection of searchable strings.
Definition: QuickFill.c:229
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
Definition: Transaction.h:381
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
void xaccSplitSetBaseValue(Split *s, gnc_numeric value, const gnc_commodity *base_currency)
Depending on the base_currency, set either the value or the amount of this split or both: If the base...
Definition: Split.cpp:1321
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3))...
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
a simple price database for gnucash
Expense accounts are used to denote expenses.
Definition: Account.h:143
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3237
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2716
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
STRUCTS.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you&#39;re finished with a price (i.e.
Structure passed to "filter tree accounts" function to provide it information.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gboolean show_inc_exp
Show income/expense accounts in tree.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
gboolean xaccAccountIsHidden(const Account *acc)
Should this account be "hidden".
Definition: Account.cpp:4157
Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description)
Returns a pointer to the split, not a copy.
Definition: Account.cpp:4866
gdouble gnc_numeric_to_double(gnc_numeric n)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
void gnc_tree_view_account_set_filter(GncTreeViewAccount *view, gnc_tree_view_account_filter_func func, gpointer data, GSourceFunc destroy)
This function attaches a filter function to the given account tree.
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:3275
Account handling public routines.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments &#39;...
void gnc_tree_view_account_refilter(GncTreeViewAccount *view)
This function forces the account tree filter to be evaluated.
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
Definition: gnc-numeric.h:195
GtkTreeView implementation for gnucash account tree.
Income accounts are used to denote income.
Definition: Account.h:140
Account public routines (C++ api)
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Argument is not a valid number.
Definition: gnc-numeric.h:224
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
Definition: gnc-date.cpp:1253
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Return a subnode in the tree whose strings all match the string &#39;str&#39; as the next substring...
Definition: QuickFill.c:179
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
Division.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed...
Definition: Account.h:101
Split * xaccMallocSplit(QofBook *book)
Constructor.
Definition: gmock-Split.cpp:37
GNCPrice * gnc_pricedb_lookup_nearest_in_time64(GNCPriceDB *db, const gnc_commodity *c, const gnc_commodity *currency, time64 t)
Return the price between the two commoditiesz nearest to the given time.
Generic api to store and retrieve preferences.
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
Returns a gnc_commodity that is a currency, suitable for being a Transaction&#39;s currency.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
const char * gnc_quickfill_string(QuickFill *qf)
For the given node &#39;qf&#39;, return the best-guess matching string.
Definition: QuickFill.c:123
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1477
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3371
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account...
Definition: Account.h:1052
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition: Account.cpp:4074
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
PriceSource
Price source enum.
Definition: gnc-pricedb.h:169
Account * gnc_tree_view_account_get_selected_account(GncTreeViewAccount *view)
This function returns the account associated with the selected item in the account tree view...
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
QuickFill is used to auto-complete typed user entries.
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
gboolean show_hidden
Show hidden accounts in tree.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
GNCPrice * gnc_pricedb_lookup_latest(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Find the most recent price between the two commodities.
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1518
Commodity handling public routines.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
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...