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