GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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
56typedef 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. */
64static 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
133typedef struct
134{
136 gboolean show_inc_exp;
137
139 gboolean show_hidden;
141
142static AccountTreeFilterInfo *from_info = NULL;
143static AccountTreeFilterInfo *to_info = NULL;
144
146{
147 char *acct_full_name;
148 Account *acct;
149};
150typedef struct _acct_list_item acct_list_item;
151
152
154static void gnc_xfer_update_to_amount (XferDialog *xferData);
155static void gnc_xfer_dialog_update_conv_info(XferDialog *xferData);
156
157static Account *gnc_transfer_dialog_get_selected_account (XferDialog *dialog,
158 XferDirection direction);
159static void gnc_transfer_dialog_set_selected_account (XferDialog *dialog,
160 Account *account,
161 XferDirection direction);
162
163extern "C" {
164void 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);
169gboolean gnc_xfer_description_key_press_cb( GtkEntry *entry,
170 GdkEventKey *event,
171 XferDialog *xferData );
172void gnc_xfer_dialog_fetch (GtkButton *button, XferDialog *xferData);
173gboolean gnc_xfer_dialog_inc_exp_filter_func (Account *account,
174 gpointer data);
175void price_amount_radio_toggled_cb(GtkToggleButton *togglebutton, gpointer data);
176
177void gnc_xfer_dialog_response_cb (GtkDialog *dialog, gint response, gpointer data);
178void gnc_xfer_dialog_close_cb(GtkDialog *dialog, gpointer data);
179}
180
183static gnc_numeric
184gnc_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 */
200static gnc_numeric
201round_price(gnc_commodity *from, gnc_commodity *to, gnc_numeric value)
202{
204 value = gnc_numeric_convert(value, CURRENCY_DENOM,
205 GNC_HOW_RND_ROUND_HALF_UP);
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,
210 GNC_HOW_RND_ROUND_HALF_UP);
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,
216 GNC_HOW_RND_ROUND_HALF_UP);
217 }
218 return value;
219}
220
221typedef enum
222{
223 SAME_DAY,
224 NEAREST,
225 LATEST
226} PriceDate;
227
228typedef 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
238static void
239price_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
251static gboolean
252lookup_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",
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)),
293 }
294 else
295 {
296 PINFO("Found price: 1 %s = %f %s",
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. */
306static void
307gnc_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
337static void
338gnc_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
352static gboolean
353gnc_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
369static void
370gnc_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
407static void
408gnc_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
447void
448price_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 */
467static void
468gnc_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
485static void
486gnc_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),
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
517static void
518gnc_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),
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
547gboolean
548gnc_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
567static void
568gnc_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
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
658static void
659gnc_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 */
685static gboolean
686gnc_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
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
790static gboolean
791idle_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 */
809void
810gnc_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
873gboolean
874gnc_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
914static void
915gnc_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
958static gboolean
959gnc_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
974static void
975gnc_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
1019static gboolean
1020gnc_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
1031static gboolean
1032gnc_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
1042static gboolean
1043gnc_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\********************************************************************/
1068void
1069gnc_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\********************************************************************/
1083void
1084gnc_xfer_dialog_select_to_account(XferDialog *xferData, Account *account)
1085{
1086 gnc_transfer_dialog_set_selected_account (xferData, account, XFER_DIALOG_TO);
1087}
1088
1089void
1090gnc_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
1107void
1108gnc_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
1123static void
1124gnc_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\********************************************************************/
1169void
1170gnc_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\********************************************************************/
1183void
1184gnc_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\********************************************************************/
1197void
1198gnc_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\********************************************************************/
1211void
1212gnc_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\********************************************************************/
1228void
1229gnc_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\********************************************************************/
1263void
1264gnc_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}
1278void 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
1285static void
1286gnc_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\********************************************************************/
1307void
1308gnc_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\********************************************************************/
1325void
1326gnc_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\********************************************************************/
1343void
1344gnc_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\********************************************************************/
1361void
1362gnc_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}
1369void 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
1376void
1377gnc_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
1391static gboolean
1392check_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
1444static gboolean
1445check_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
1472static void
1473create_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
1538static gnc_numeric
1539swap_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
1549static void
1550update_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",
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",
1585 gnc_numeric_to_double(gnc_price_get_value(pr->price)),
1587 gnc_price_unref (pr->price);
1588}
1589
1590static void
1591new_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
1622static void
1623create_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
1641void
1642gnc_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
1737void
1738gnc_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
1778void
1779gnc_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
1815static void
1816gnc_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
2011static void
2012close_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\********************************************************************/
2038XferDialog *
2039gnc_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
2089void
2090gnc_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
2099void
2100gnc_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
2108void
2109gnc_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
2121static void
2122gnc_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
2137void
2138gnc_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
2144void
2145gnc_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
2151void
2152gnc_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
2162void
2163gnc_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 */
2174void 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
2191void 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 */
2205static gboolean
2206find_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 */
2215gboolean 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
2278void
2279gnc_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
2291static Account *
2292gnc_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
2315static void
2316gnc_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
2352void 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
2363gboolean 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
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,
2424 GNC_HOW_DENOM_REDUCE);
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}
Account handling public routines.
Account public routines (C++ api)
QuickFill is used to auto-complete typed user entries.
API for Transactions and Splits (journal entries)
void fetch(QofBook *book)
Fetch quotes for all commodities in our db that have a quote source set.
Commodity handling public routines.
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
a simple price database for gnucash
GtkTreeView implementation for gnucash account tree.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
GNCAccountType
The account types are used to determine how the transaction data in the account is displayed.
Definition Account.h:103
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:3305
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
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_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user's computer.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the 'print' name for the specified commodity.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
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.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4119
void xaccTransSetDescription(Transaction *trans, const char *desc)
Sets the transaction Description.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
#define xaccTransAppendSplit(t, s)
Add a split to the transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gboolean xaccAccountIsHidden(const Account *acc)
Should this account be "hidden".
Definition Account.cpp:4202
Split * xaccAccountFindSplitByDesc(const Account *acc, const char *description)
Returns a pointer to the split, not a copy.
Definition Account.cpp:4908
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
This function sets the posted date of the transaction, specified by a time64 (see ctime(3)).
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
#define xaccAccountInsertSplit(acc, s)
The xaccAccountInsertSplit() method will insert the indicated split into the indicated account.
Definition Account.h:1069
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
void xaccTransSetNotes(Transaction *trans, const char *notes)
Sets the transaction Notes.
void gnc_tree_view_account_refilter(GncTreeViewAccount *view)
This function forces the account tree filter to be evaluated.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
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.
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
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.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
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's currency.
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#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
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
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...
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.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
@ GNC_ERROR_ARG
Argument is not a valid number.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
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.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
GNCPrice * gnc_pricedb_lookup_latest(GNCPriceDB *db, const gnc_commodity *commodity, const gnc_commodity *currency)
Find the most recent price between the two commodities.
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.
gboolean gnc_pricedb_add_price(GNCPriceDB *db, GNCPrice *p)
Add a price to the pricedb.
PriceSource
Price source enum.
GNCPrice * gnc_price_create(QofBook *book)
gnc_price_create - returns a newly allocated and initialized price with a reference count of 1.
void gnc_price_unref(GNCPrice *p)
gnc_price_unref - indicate you're finished with a price (i.e.
QuickFill * gnc_quickfill_get_string_match(QuickFill *qf, const char *str)
Return a subnode in the tree whose strings all match the string 'str' as the next substring.
Definition QuickFill.c:179
const char * gnc_quickfill_string(QuickFill *qf)
For the given node 'qf', return the best-guess matching string.
Definition QuickFill.c:123
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
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
Split * xaccMallocSplit(QofBook *book)
Constructor.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
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:1355
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits.
Structure passed to "filter tree accounts" function to provide it information.
gboolean show_inc_exp
Show income/expense accounts in tree.
gboolean show_hidden
Show hidden accounts in tree.
STRUCTS.
QofBook reference.
Definition qofbook-p.hpp:47