GnuCash  5.6-150-g038405b370+
split-register-control.cpp
1 /********************************************************************\
2  * split-register-control.cpp -- split register control object *
3  * Copyright (C) 2017 Aaron Laws *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21  * *
22 \********************************************************************/
23 
24 #include <config.h>
25 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 
29 #include "Account.hpp"
30 #include "Scrub.h"
31 #include "combocell.h"
32 #include "gnc-component-manager.h"
33 #include "gnc-prefs.h"
34 #include "gnc-ui.h"
35 #include "gnc-warnings.h"
36 #include "pricecell.h"
37 #include "datecell.h"
38 #include "dialog-transfer.h"
39 #include "dialog-utils.h"
40 #include "split-register-control.h"
42 #include "split-register-p.h"
43 #include "split-register.h"
44 #include "table-allgui.h"
45 #include "engine-helpers.h"
46 #include <gnc-gui-query.h> //for gnc_error_dialog
47 
48 
49 /* This static indicates the debugging module that this .o belongs to. */
50 static QofLogModule log_module = GNC_MOD_LEDGER;
51 
52 static inline bool
53 check_imbalance_fraction (const SplitRegister *reg,
54  const gnc_monetary *imbal_mon,
55  const Transaction *trans)
56 {
57  auto commodity_fraction {gnc_commodity_get_fraction (imbal_mon->commodity)};
58  auto denom_diff = imbal_mon->value.denom > commodity_fraction;
59  if (!denom_diff)
60  {
61  const auto imbal_comm = imbal_mon->commodity;
62  for (auto node = xaccTransGetSplitList (trans); node;
63  node = g_list_next (node))
64  {
65  auto acc = xaccSplitGetAccount (GNC_SPLIT(node->data));
66  if (xaccAccountGetCommodity (acc) == imbal_comm &&
67  imbal_mon->value.denom > xaccAccountGetCommoditySCU (acc))
68  {
69  denom_diff = true;
70  break;
71  }
72  }
73  }
74 
75  if (denom_diff)
76  {
77  gnc_error_dialog (gnc_ui_get_main_window (GTK_WIDGET(reg)),
78  "%s",
79  _("This transaction cannot be balanced: The imbalance is a fraction smaller than the commodity allows."));
80  }
81  return denom_diff;
82 }
83 
84 static gboolean
85 gnc_split_register_balance_trans (SplitRegister *reg, Transaction *trans)
86 {
87  int choice;
88  int default_value;
89  Account *default_account;
90  Account *other_account;
91  Account *root;
92  GList *radio_list = NULL;
93  const char *title = _("Rebalance Transaction");
94  const char *message = _("The current transaction is not balanced.");
95  Split *split;
96  Split *other_split;
97  gboolean two_accounts;
98  gboolean multi_currency;
99 
100  if (xaccTransIsBalanced (trans))
101  return FALSE;
102 
103  if (xaccTransUseTradingAccounts (trans))
104  {
105  MonetaryList *imbal_list;
106  gnc_monetary *imbal_mon;
107  imbal_list = xaccTransGetImbalance (trans);
108 
109  /* See if the imbalance is only in the transaction's currency */
110  if (!imbal_list)
111  /* Value imbalance, but not commodity imbalance. This shouldn't
112  be something that scrubbing can cause to happen. Perhaps someone
113  entered invalid splits. */
114  multi_currency = TRUE;
115  else
116  {
117  imbal_mon = static_cast<gnc_monetary*>(imbal_list->data);
118  if (!imbal_list->next &&
119  gnc_commodity_equiv (gnc_monetary_commodity (*imbal_mon),
120  xaccTransGetCurrency (trans)))
121  multi_currency = FALSE;
122  else
123  multi_currency = TRUE;
124 
125  if (multi_currency && check_imbalance_fraction (reg, imbal_mon, trans))
126  return FALSE;
127  }
128 
129  /* We're done with the imbalance list, the real work will be done
130  by xaccTransScrubImbalance which will get it again. */
131  gnc_monetary_list_free (imbal_list);
132  }
133  else
134  multi_currency = FALSE;
135 
136  split = xaccTransGetSplit (trans, 0);
137  other_split = xaccSplitGetOtherSplit (split);
138 
139  if (other_split == NULL)
140  {
141  /* Attempt to handle the inverted many-to-one mapping */
142  split = xaccTransGetSplit (trans, 1);
143  if (split)
144  other_split = xaccSplitGetOtherSplit (split);
145  else
146  split = xaccTransGetSplit (trans, 0);
147  }
148  if (other_split == NULL || multi_currency)
149  {
150  two_accounts = FALSE;
151  other_account = NULL;
152  }
153  else
154  {
155  two_accounts = TRUE;
156  other_account = xaccSplitGetAccount (other_split);
157  }
158 
159  default_account = gnc_split_register_get_default_account (reg);
160 
161  /* If the two pointers are the same, the account from other_split
162  * is actually the default account. We must make other_account
163  * the account from split instead. */
164 
165  if (default_account == other_account)
166  other_account = xaccSplitGetAccount (split);
167 
168  /* If the two pointers are still the same, we have two splits, but
169  * they both refer to the same account. While non-sensical, we don't
170  * object. */
171 
172  if (default_account == other_account)
173  two_accounts = FALSE;
174 
175  radio_list = g_list_append (radio_list,
176  _("Balance it _manually"));
177  radio_list = g_list_append (radio_list,
178  _("Let GnuCash _add an adjusting split"));
179 
180  if (reg->type < NUM_SINGLE_REGISTER_TYPES && !multi_currency)
181  {
182  radio_list = g_list_append (radio_list,
183  _("Adjust current account _split total"));
184 
185  default_value = 2;
186  if (two_accounts)
187  {
188  radio_list = g_list_append (radio_list,
189  _("Adjust _other account split total"));
190  default_value = 3;
191  }
192  }
193  else
194  default_value = 0;
195 
196  choice = gnc_choose_radio_option_dialog (gnc_split_register_get_parent (reg),
197  title,
198  message,
199  _("_Rebalance"),
200  default_value,
201  radio_list);
202 
203  g_list_free (radio_list);
204 
205  root = default_account ? gnc_account_get_root (default_account) : NULL;
206  switch (choice)
207  {
208  default:
209  case 0:
210  break;
211 
212  case 1:
213  xaccTransScrubImbalance (trans, root, NULL);
214  break;
215 
216  case 2:
217  xaccTransScrubImbalance (trans, root, default_account);
218  break;
219 
220  case 3:
221  xaccTransScrubImbalance (trans, root, other_account);
222  break;
223  }
224 
225  return TRUE;
226 }
227 
228 static gboolean
229 gnc_split_register_old_split_empty_p (SplitRegister *reg, Split *split)
230 {
231  BasicCell *cell;
232  gnc_numeric amount;
233  const char *string;
234 
235  string = gnc_table_layout_get_cell_value (reg->table->layout, MEMO_CELL);
236  if ((string != NULL) && (*string != '\0'))
237  return FALSE;
238 
239  string = gnc_table_layout_get_cell_value (reg->table->layout, XFRM_CELL);
240  if ((string != NULL) && (*string != '\0'))
241  return FALSE;
242 
243  cell = gnc_table_layout_get_cell (reg->table->layout, CRED_CELL);
244  if (cell)
245  {
246  amount = gnc_price_cell_get_value ((PriceCell *) cell);
247  if (!gnc_numeric_zero_p (amount))
248  return FALSE;
249  }
250 
251  cell = gnc_table_layout_get_cell (reg->table->layout, DEBT_CELL);
252  if (cell)
253  {
254  amount = gnc_price_cell_get_value ((PriceCell *) cell);
255  if (!gnc_numeric_zero_p (amount))
256  return FALSE;
257  }
258 
259  return TRUE;
260 }
261 
262 /* Checks a cell for a debit or credit change to see if a new exchange
263  * rate is needed. */
264 
265 static gboolean
266 gnc_split_register_check_debcred (SplitRegister *reg,
267  const char *cell_name)
268 {
269  if ((gnc_cell_name_equal (cell_name, DEBT_CELL) &&
270  gnc_table_layout_get_cell_changed (reg->table->layout, DEBT_CELL, FALSE)) ||
271  (gnc_cell_name_equal (cell_name, CRED_CELL) &&
272  gnc_table_layout_get_cell_changed (reg->table->layout, CRED_CELL, FALSE)))
273  {
274  SRInfo *info = gnc_split_register_get_info (reg);
275  PriceCell *rate_cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
276  RATE_CELL);
277  if (gnc_split_reg_has_rate_cell (reg->type) && info->rate_reset != RATE_RESET_DONE)
278  {
279  /* Debit or credit amount changed, get a new exchange rate */
280  info->rate_reset = RATE_RESET_REQD;
281  if (info->auto_complete)
282  {
283  /* It's auto-filled, start with rate from price DB for the date
284  of the transaction. */
285  gnc_price_cell_set_value (rate_cell, gnc_numeric_zero());
286  }
287  }
288  }
289 
290  return TRUE;
291 }
292 
293 /* Checks a cell for an account change and takes any necessary action if
294  * one has occurred. Returns TRUE if the check passes, FALSE if it fails. */
295 static gboolean
296 gnc_split_register_check_account (SplitRegister *reg,
297  const char *cell_name)
298 {
299  SRInfo *info;
300  ComboCell *cell = NULL;
301  Account* new_acct;
302  Split *split;
303  char *name;
304 
305  g_return_val_if_fail (reg, TRUE);
306 
307  /* See if we are leaving an account field */
308  if (gnc_cell_name_equal (cell_name, XFRM_CELL))
309  {
310  if (gnc_table_layout_get_cell_changed (reg->table->layout,
311  XFRM_CELL, FALSE))
312  cell = (ComboCell *) gnc_table_layout_get_cell (reg->table->layout,
313  XFRM_CELL);
314  }
315  else if (gnc_cell_name_equal (cell_name, MXFRM_CELL))
316  {
317  if (gnc_table_layout_get_cell_changed (reg->table->layout,
318  MXFRM_CELL, FALSE))
319  cell = (ComboCell *) gnc_table_layout_get_cell (reg->table->layout,
320  MXFRM_CELL);
321  }
322 
323  if (!cell)
324  return TRUE;
325 
326  /* The account has been changed. */
327  name = cell->cell.value;
328  DEBUG("Changed to %s", name ? name : "NULL");
329  if (!name || *name == '\0' ||
330  g_strcmp0 (name, SPLIT_TRANS_STR) == 0 ||
331  g_strcmp0 (name, STOCK_SPLIT_STR) == 0)
332  return TRUE;
333 
334  /* Create the account if necessary. Also checks for a placeholder. */
335  info = gnc_split_register_get_info (reg);
336  new_acct = gnc_split_register_get_account_by_name (reg,
337  (BasicCell *) cell,
338  cell->cell.value);
339  if (!new_acct)
340  return FALSE;
341 
343  gnc_split_register_set_cell_fractions (reg, split);
344 
345  /* See if we need to reset the exchange rate. */
346  if (gnc_split_reg_has_rate_cell (reg->type))
347  {
348  PriceCell *rate_cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
349  RATE_CELL);
350  Account *orig_acct = xaccSplitGetAccount (split);
351  gnc_commodity *orig_com = xaccAccountGetCommodity (orig_acct);
352  gnc_commodity *last_com = xaccAccountGetCommodity (info->rate_account);
353  gnc_commodity *new_com = xaccAccountGetCommodity (new_acct);
354 
355  if (gnc_commodity_equal (last_com ? last_com : orig_com, new_com))
356  {
357  DEBUG("Commodity is still %s. Leaving rate unchanged.",
358  new_com ? gnc_commodity_get_mnemonic (new_com) : "NULL");
359  }
360  else if (!gnc_commodity_equal (orig_com, new_com))
361  {
362  /* The commodity has changed but is not the original. Reset the rate. */
363  DEBUG("Commodity now %s (originally %s). Clearing rate.",
364  new_com ? gnc_commodity_get_mnemonic (new_com) : "NULL",
365  orig_com ? gnc_commodity_get_mnemonic (orig_com) : "NULL");
366 
367  gnc_price_cell_set_value (rate_cell, gnc_numeric_zero());
368  info->rate_account = new_acct;
369  info->rate_reset = RATE_RESET_REQD;
370  }
371  else
372  {
373  /* Get the original rate from the split. */
374  gnc_numeric amt = xaccSplitGetAmount (split);
375  gnc_numeric val = xaccSplitGetValue (split);
376  gnc_numeric orig_rate = gnc_numeric_div (amt, val, GNC_DENOM_AUTO,
378 
379  if (!gnc_numeric_check (orig_rate))
380  {
381  DEBUG("Using original rate of %s.", gnc_num_dbg_to_string (orig_rate));
382  gnc_price_cell_set_value (rate_cell, orig_rate);
383  info->rate_account = new_acct;
384  info->rate_reset = RATE_RESET_NOT_REQD;
385  }
386  else
387  {
388  DEBUG("Can't get rate. Using zero.");
389  gnc_price_cell_set_value (rate_cell, gnc_numeric_zero());
390  info->rate_account = new_acct;
391  info->rate_reset = RATE_RESET_REQD;
392  }
393  }
394  }
395 
396  return TRUE;
397 }
398 
399 static inline bool
400 is_trading_split (Split *split)
401 {
402  auto acct{xaccSplitGetAccount (split)};
403  return GNC_IS_ACCOUNT(acct) && xaccAccountGetType (acct) == ACCT_TYPE_TRADING;
404 }
405 
406 static void
407 gnc_split_register_move_cursor (VirtualLocation *p_new_virt_loc,
408  gpointer user_data)
409 {
410  VirtualLocation new_virt_loc = *p_new_virt_loc;
411  VirtualCellLocation old_trans_split_loc;
412  auto reg = static_cast<SplitRegister*>(user_data);
413  Transaction *pending_trans;
414  Transaction *new_trans;
415  Transaction *old_trans;
416  Split *old_trans_split{nullptr};
417  Split *new_trans_split;
418  Split *new_split;
419  Split *old_split{nullptr};
420  CursorClass new_class;
421  CursorClass old_class;
422  gboolean exact_traversal;
423  gboolean do_refresh;
424  gboolean saved;
425  SRInfo *info;
426 
427  ENTER("reg=%p, p_new_virt_loc=%p (%d, %d)",
428  reg, p_new_virt_loc,
429  new_virt_loc.vcell_loc.virt_row,
430  new_virt_loc.vcell_loc.virt_col);
431 
432  if (!reg)
433  {
434  LEAVE("no register");
435  return;
436  }
437 
438  info = gnc_split_register_get_info (reg);
439 
440  /* The transaction we are coming from */
441  if (auto s{gnc_split_register_get_current_split (reg)}; !is_trading_split(s))
442  old_split = s;
443  old_trans = gnc_split_register_get_current_trans (reg);
444  if (auto s{gnc_split_register_get_current_trans_split (reg, &old_trans_split_loc)};
445  !is_trading_split (s))
446  old_trans_split = s;
447 
449 
450  exact_traversal = info->exact_traversal;
451 
452  if (info->traverse_to_new)
453  {
454  if (old_class == CURSOR_CLASS_SPLIT)
455  new_trans = old_trans;
456  else
457  new_trans = NULL;
458 
459  new_split = NULL;
460  new_trans_split = NULL;
461  new_class = CURSOR_CLASS_NONE;
462  }
463  else if (!info->hint_set_by_traverse)
464  {
465  /* The transaction where we are moving to */
466  new_trans = gnc_split_register_get_trans (reg, new_virt_loc.vcell_loc);
467 
468  /* The split we are moving to */
469  new_split = gnc_split_register_get_split (reg, new_virt_loc.vcell_loc);
470 
471  /* The split at the transaction line we are moving to */
472  new_trans_split = gnc_split_register_get_trans_split (reg,
473  new_virt_loc.vcell_loc,
474  NULL);
475 
476  new_class = gnc_split_register_get_cursor_class (reg,
477  new_virt_loc.vcell_loc);
478  }
479  else
480  {
481  new_trans = info->cursor_hint_trans;
482  new_split = info->cursor_hint_split;
483  new_trans_split = info->cursor_hint_trans_split;
484  new_class = info->cursor_hint_cursor_class;
485  }
486 
487  info->hint_set_by_traverse = FALSE;
488  info->reg_loaded = FALSE;
489 
490  gnc_suspend_gui_refresh ();
491 
492  /* commit the contents of the cursor into the database */
493  saved = gnc_split_register_save (reg, old_trans != new_trans);
494  pending_trans = xaccTransLookup (&info->pending_trans_guid,
495  gnc_get_current_book ());
496  Split* blank_split = xaccSplitLookup (&info->blank_split_guid,
497  gnc_get_current_book ());
498  Transaction* blank_trans = xaccSplitGetParent (blank_split);
499 
500  if ((old_class == CURSOR_CLASS_SPLIT) &&
501  old_split &&
502  (old_split != new_split) &&
503  gnc_split_register_old_split_empty_p (reg, old_split))
504  {
505  if (old_split != gnc_split_register_get_blank_split (reg))
506  {
507  int current_row;
508 
509  xaccSplitDestroy (old_split);
510  old_split = NULL;
511 
512  /*
513  * If the user is moving down a row, we've just thrown off the
514  * numbers by deleting a split. Correct for that.
515  */
516  current_row = reg->table->current_cursor_loc.vcell_loc.virt_row;
517  if (new_virt_loc.vcell_loc.virt_row > current_row)
518  new_virt_loc.vcell_loc.virt_row--;
519  }
520  }
521  else if ((pending_trans != NULL) &&
522  (pending_trans == old_trans) &&
523  (pending_trans != blank_trans) &&
524  (old_trans != new_trans))
525  {
526  if (gnc_split_register_balance_trans (reg, pending_trans))
527  {
528  /* Trans was unbalanced. */
529  new_trans = old_trans;
530  new_split = old_split;
531  new_trans_split = old_trans_split;
532  new_class = old_class;
533  new_virt_loc = reg->table->current_cursor_loc;
534  }
535  else
536  {
537  /* Trans was balanced. Let it go. */
538  info->pending_trans_guid = *guid_null ();
539  if (xaccTransIsOpen (pending_trans))
540  xaccTransCommitEdit (pending_trans);
541  else g_assert_not_reached ();
542 
543  pending_trans = NULL;
544  saved = TRUE;
545  }
546  }
547  else if (old_trans &&
548  (old_trans != new_trans) &&
549  !xaccTransHasReconciledSplits (old_trans) &&
550  !info->first_pass &&
551  gnc_split_register_balance_trans (reg, old_trans))
552  {
553  /* no matter what, stay there so the user can see what happened */
554  new_trans = old_trans;
555  new_split = old_split;
556  new_trans_split = old_trans_split;
557  new_class = old_class;
558  new_virt_loc = reg->table->current_cursor_loc;
559  }
560 
561  if (saved)
562  {
563  info->cursor_hint_trans = new_trans;
564  info->cursor_hint_split = new_split;
565  info->cursor_hint_trans_split = new_trans_split;
566  info->cursor_hint_cursor_class = new_class;
567  }
568 
569  /* change from split row to trans row */
570  if (old_class != new_class)
571  info->change_confirmed = FALSE;
572 
573  if (old_split != new_split)
574  {
575  info->change_confirmed = FALSE;
576  info->rate_account = NULL;
577  info->rate_reset = RATE_RESET_NOT_REQD;
578  }
579 
580  gnc_resume_gui_refresh ();
581 
582  /* redrawing the register can muck everything up */
583  if (saved)
584  {
585  VirtualCellLocation vcell_loc;
586 
587  if (!info->reg_loaded)
589 
590  /* if the split we were going to is still in the register,
591  * then it may have moved. Find out where it is now. */
592  if (gnc_split_register_find_split (reg, new_trans, new_trans_split,
593  new_split, new_class, &vcell_loc))
594  {
595  new_virt_loc.vcell_loc = vcell_loc;
596  }
597  else
598  new_virt_loc.vcell_loc = reg->table->current_cursor_loc.vcell_loc;
599 
600  new_trans = gnc_split_register_get_trans (reg, new_virt_loc.vcell_loc);
601  new_split = gnc_split_register_get_split (reg, new_virt_loc.vcell_loc);
602  new_trans_split = gnc_split_register_get_trans_split (reg,
603  new_virt_loc.vcell_loc,
604  NULL);
605  new_class = gnc_split_register_get_cursor_class (reg,
606  new_virt_loc.vcell_loc);
607  }
608  else if (info->traverse_to_new)
609  {
610  new_trans = info->cursor_hint_trans;
611  new_split = info->cursor_hint_split;
612  new_trans_split = info->cursor_hint_trans_split;
613  new_class = info->cursor_hint_cursor_class;
614  }
615 
616  gnc_table_find_close_valid_cell (reg->table, &new_virt_loc, exact_traversal);
617 
618  *p_new_virt_loc = new_virt_loc;
619 
620  PINFO("after move %d %d",
621  new_virt_loc.vcell_loc.virt_row,
622  new_virt_loc.vcell_loc.virt_col);
623 
624  /* if the register was reloaded, then everything should be fine :)
625  * otherwise, we may need to change some visibility settings. */
626  if (saved)
627  {
628  gnc_split_register_set_cell_fractions (reg, new_split);
629 
630  LEAVE("saved");
631  return;
632  }
633 
634  /* in the mult-line and dynamic modes, we need to hide the old
635  * and show the new. */
637  (old_trans_split != new_trans_split))
638  {
639  VirtualCellLocation vc_loc;
640 
641  vc_loc = old_trans_split_loc;
642  gnc_table_set_virt_cell_cursor (reg->table, vc_loc,
643  gnc_split_register_get_passive_cursor (reg));
644  gnc_split_register_set_trans_visible (reg, vc_loc, FALSE,
645  reg->style == REG_STYLE_JOURNAL);
646 
647  if ((REG_STYLE_AUTO_LEDGER == reg->style) ||
648  (REG_STYLE_JOURNAL == reg->style))
649  {
650  gnc_split_register_get_trans_split (reg, new_virt_loc.vcell_loc,
651  &vc_loc);
652  gnc_table_set_virt_cell_cursor (reg->table, vc_loc,
653  gnc_split_register_get_active_cursor (reg));
654  gnc_split_register_set_trans_visible (reg, vc_loc, TRUE,
655  reg->style == REG_STYLE_JOURNAL);
656  }
657 
658  info->trans_expanded = FALSE;
659 
660  do_refresh = TRUE;
661  }
662  else
663  do_refresh = FALSE;
664 
665  info->cursor_hint_trans = new_trans;
666  info->cursor_hint_split = new_split;
667  info->cursor_hint_trans_split = new_trans_split;
668  info->cursor_hint_cursor_class = new_class;
669 
670  gnc_split_register_set_cell_fractions (reg, new_split);
671 
672  gnc_table_find_close_valid_cell (reg->table, p_new_virt_loc,
673  exact_traversal);
674 
675  if (do_refresh)
676  {
677  VirtualCellLocation vc_loc;
678 
679  gnc_table_refresh_gui (reg->table, FALSE);
680  gnc_table_leave_update (reg->table, reg->table->current_cursor_loc);
681 
682  gnc_split_register_get_trans_split (reg, p_new_virt_loc->vcell_loc,
683  &vc_loc);
684  gnc_split_register_show_trans (reg, vc_loc);
685  }
686 
687  LEAVE(" ");
688 }
689 
690 static Split *
691 gnc_find_split_in_trans_by_memo (Transaction *trans, const char *memo,
692  gboolean unit_price)
693 {
694  for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
695  {
696  auto split = GNC_SPLIT(n->data);
697  if (unit_price)
698  {
699  gnc_numeric price = xaccSplitGetSharePrice (split);
700  if (!gnc_numeric_equal (price, gnc_numeric_create (1, 1)) &&
701  !gnc_numeric_zero_p (price))
702  continue;
703  }
704 
705  if (g_strcmp0 (memo, xaccSplitGetMemo (split)) == 0)
706  return split;
707  }
708 
709  return NULL;
710 }
711 
712 static Split *
713 gnc_find_split_in_account_by_memo (Account *account, const char *memo,
714  gboolean unit_price)
715 {
716  if (account == nullptr) return nullptr;
717 
718  const auto& splits = xaccAccountGetSplits (account);
719  for (auto it = splits.rbegin(); it != splits.rend(); it++)
720  if (auto split = gnc_find_split_in_trans_by_memo (xaccSplitGetParent (*it),
721  memo, unit_price))
722  return split;
723 
724  return nullptr;
725 }
726 
727 static Split *
728 gnc_find_split_in_reg_by_memo (SplitRegister *reg, const char *memo,
729  gboolean unit_price)
730 {
731  int virt_row, virt_col;
732  int num_rows, num_cols;
733  Transaction *last_trans;
734 
735  if (!reg || !reg->table)
736  return NULL;
737 
738  num_rows = reg->table->num_virt_rows;
739  num_cols = reg->table->num_virt_cols;
740 
741  last_trans = NULL;
742 
743  for (virt_row = num_rows - 1; virt_row >= 0; virt_row--)
744  for (virt_col = num_cols - 1; virt_col >= 0; virt_col--)
745  {
746  Split *split;
747  Transaction *trans;
748  VirtualCellLocation vcell_loc = { virt_row, virt_col };
749 
750  split = gnc_split_register_get_split (reg, vcell_loc);
751  trans = xaccSplitGetParent (split);
752 
753  if (trans == last_trans)
754  continue;
755 
756  split = gnc_find_split_in_trans_by_memo (trans, memo, unit_price);
757  if (split != NULL)
758  return split;
759 
760  last_trans = trans;
761  }
762 
763  return NULL;
764 }
765 
766 static Transaction *
767 gnc_find_trans_in_reg_by_desc (SplitRegister *reg, const char *description)
768 {
769  int virt_row, virt_col;
770  int num_rows, num_cols;
771  Transaction *last_trans;
772 
773  if (!reg || !reg->table)
774  return NULL;
775 
776  num_rows = reg->table->num_virt_rows;
777  num_cols = reg->table->num_virt_cols;
778 
779  last_trans = NULL;
780 
781  for (virt_row = num_rows - 1; virt_row >= 0; virt_row--)
782  for (virt_col = num_cols - 1; virt_col >= 0; virt_col--)
783  {
784  Split *split;
785  Transaction *trans;
786  VirtualCellLocation vcell_loc = { virt_row, virt_col };
787 
788  split = gnc_split_register_get_split (reg, vcell_loc);
789  trans = xaccSplitGetParent (split);
790 
791  if (trans == last_trans)
792  continue;
793 
794  if (g_strcmp0 (description, xaccTransGetDescription (trans)) == 0)
795  return trans;
796 
797  last_trans = trans;
798  }
799 
800  return NULL;
801 }
802 
803 /* This function determines if auto-completion is appropriate and,
804  * if so, performs it. This should only be called by LedgerTraverse. */
805 static gboolean
806 gnc_split_register_auto_completion (SplitRegister *reg,
807  gncTableTraversalDir dir,
808  VirtualLocation *p_new_virt_loc)
809 {
810  SRInfo *info = gnc_split_register_get_info (reg);
811  VirtualLocation new_virt_loc;
812  CursorClass cursor_class;
813  Transaction *pending_trans;
814  Transaction *blank_trans;
815  const char *cell_name;
816  Transaction *trans;
817  Split *blank_split;
818  gnc_numeric amount;
819  BasicCell *cell;
820  Split *split;
821 
822  if (!reg->do_auto_complete)
823  return FALSE;
824 
825  blank_split = xaccSplitLookup (&info->blank_split_guid,
826  gnc_get_current_book ());
827  blank_trans = xaccSplitGetParent (blank_split);
828 
829  pending_trans = xaccTransLookup (&info->pending_trans_guid,
830  gnc_get_current_book ());
831 
832  /* auto-completion is only triggered by a tab out */
833  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
834  return FALSE;
835 
838  if (trans == NULL)
839  return FALSE;
840 
841  cursor_class = gnc_split_register_get_current_cursor_class (reg);
842  cell_name = gnc_table_get_current_cell_name (reg->table);
843 
844  switch (cursor_class)
845  {
846  case CURSOR_CLASS_TRANS:
847  {
848  Transaction *auto_trans;
849  const char *desc;
850 
851  /* there must be a blank transaction * */
852  if (blank_trans == NULL)
853  return FALSE;
854 
855  /* we must be on the blank split */
856  if (trans != blank_trans)
857  return FALSE;
858 
859  /* and leaving the description cell */
860  if (!gnc_cell_name_equal (cell_name, DESC_CELL))
861  return FALSE;
862 
863  /* nothing but the date, num, and description should be changed */
864  /* FIXME, this should be refactored. */
865  if (gnc_table_layout_get_cell_changed (reg->table->layout,
866  XFRM_CELL, TRUE) ||
867  gnc_table_layout_get_cell_changed (reg->table->layout,
868  MXFRM_CELL, TRUE) ||
869  gnc_table_layout_get_cell_changed (reg->table->layout,
870  PRIC_CELL, TRUE) ||
871  gnc_table_layout_get_cell_changed (reg->table->layout,
872  SHRS_CELL, TRUE) ||
873  gnc_table_layout_get_cell_changed (reg->table->layout,
874  DEBT_CELL, TRUE) ||
875  gnc_table_layout_get_cell_changed (reg->table->layout,
876  CRED_CELL, TRUE) ||
877  gnc_table_layout_get_cell_changed (reg->table->layout,
878  NOTES_CELL, TRUE) ||
879  gnc_table_layout_get_cell_changed (reg->table->layout,
880  RECN_CELL, TRUE))
881  return FALSE;
882 
883  /* and the description should be changed */
884  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
885  DESC_CELL, TRUE))
886  return FALSE;
887 
888  /* to a non-empty value */
889  desc = gnc_table_layout_get_cell_value (reg->table->layout, DESC_CELL);
890  if ((desc == NULL) || (*desc == '\0'))
891  return FALSE;
892 
893  /* find a transaction to auto-complete on */
894  if (gnc_split_register_get_default_account (reg) != NULL)
895  {
896  Account *account = gnc_split_register_get_default_account (reg);
897 
898  auto_trans = xaccAccountFindTransByDesc (account, desc);
899  }
900  else
901  auto_trans = gnc_find_trans_in_reg_by_desc (reg, desc);
902 
903  if (auto_trans == NULL)
904  return FALSE;
905 
906  gnc_suspend_gui_refresh ();
907 
908  /* We are guaranteed to be on the blank trans, so we can
909  discount the possibility that the current transaction is
910  being edited in another register. */
911  /* now perform the completion */
912  if (pending_trans != trans)
913  {
914  if (!xaccTransIsOpen (trans))
915  xaccTransBeginEdit (trans);
916  /* This is now the pending transaction */
917  info->pending_trans_guid = *xaccTransGetGUID (trans);
918  if (pending_trans != NULL)
919  {
920  if (xaccTransIsOpen (pending_trans))
921  xaccTransCommitEdit (pending_trans);
922  else g_assert_not_reached ();
923  }
924  }
925  g_assert(xaccTransIsOpen (trans));
926  pending_trans = xaccTransLookup (&info->pending_trans_guid,
927  gnc_get_current_book ());
928  g_assert (pending_trans == trans);
929 
930  gnc_copy_trans_onto_trans (auto_trans, trans, FALSE, FALSE);
931  /* if there is a doclink, let's clear it */
932  if (xaccTransGetDocLink (auto_trans) != NULL)
933  xaccTransSetDocLink (trans, "");
934  blank_split = NULL;
935 
936  if (gnc_split_register_get_default_account (reg) != NULL)
937  {
938  Account *default_account = gnc_split_register_get_default_account (reg);
939  gnc_commodity *trans_cmdty = xaccTransGetCurrency (trans);
940  gnc_commodity *acct_cmdty = xaccAccountGetCommodity (default_account);
941  if (gnc_commodity_is_currency (acct_cmdty) &&
942  !gnc_commodity_equal (trans_cmdty, acct_cmdty))
943  xaccTransSetCurrency (trans, acct_cmdty);
944 
945  for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
946  {
947  auto s = GNC_SPLIT(n->data);
948  if (default_account == xaccSplitGetAccount (s))
949  {
950  blank_split = s;
951  info->blank_split_guid = *xaccSplitGetGUID (blank_split);
952  break;
953  }
954  }
955  }
956 
957  if (blank_split == NULL)
958  {
959  blank_split = xaccTransGetSplit (trans, 0);
960  info->blank_split_guid = *xaccSplitGetGUID (blank_split);
961  }
962  DEBUG("blank_split=%p", blank_split);
963 
964  info->blank_split_edited = TRUE;
965 
966  {
967  SRSaveData *sd;
968 
969  sd = gnc_split_register_save_data_new (trans, blank_split,
971  gnc_table_save_cells (reg->table, sd);
972  gnc_split_register_save_data_destroy (sd);
973  }
974 
975  gnc_resume_gui_refresh ();
976 
977  /* now move to the non-empty amount column unless config setting says not */
978  if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
979  GNC_PREF_TAB_TRANS_MEMORISED))
980  {
981  amount = xaccSplitGetAmount (blank_split);
982  cell_name = (gnc_numeric_negative_p (amount)) ? CRED_CELL : DEBT_CELL;
983 
984  if (gnc_table_get_current_cell_location (reg->table, cell_name, &new_virt_loc))
985  *p_new_virt_loc = new_virt_loc;
986  }
987  }
988 
989  break;
990 
991  case CURSOR_CLASS_SPLIT:
992  {
993  char *account_name;
994  const char *memo;
995  gboolean unit_price;
996  Split *auto_split;
997 
998  /* we must be on a blank split of a transaction */
999  if (split != NULL)
1000  return FALSE;
1001 
1002  /* and leaving the memo cell */
1003  if (!gnc_cell_name_equal (cell_name, MEMO_CELL))
1004  return FALSE;
1005 
1006  /* nothing but the action, memo, and amounts should be changed */
1007  /* FIXME. This should be refactored. */
1008  if (gnc_table_layout_get_cell_changed (reg->table->layout,
1009  XFRM_CELL, TRUE) ||
1010  gnc_table_layout_get_cell_changed (reg->table->layout,
1011  MXFRM_CELL, TRUE) ||
1012  gnc_table_layout_get_cell_changed (reg->table->layout,
1013  PRIC_CELL, TRUE) ||
1014  gnc_table_layout_get_cell_changed (reg->table->layout,
1015  SHRS_CELL, TRUE) ||
1016  gnc_table_layout_get_cell_changed (reg->table->layout,
1017  RECN_CELL, TRUE))
1018  return FALSE;
1019 
1020  /* and the memo should be changed */
1021  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1022  MEMO_CELL, TRUE))
1023  return FALSE;
1024 
1025  /* to a non-empty value */
1026  memo = gnc_table_layout_get_cell_value (reg->table->layout, MEMO_CELL);
1027  if ((memo == NULL) || (*memo == '\0'))
1028  return FALSE;
1029 
1030  /* if there is no price field, only auto-complete from splits with
1031  * a unit share price. */
1032  unit_price = !gnc_table_get_current_cell_location (reg->table,
1033  PRIC_CELL, NULL);
1034 
1035  /* find a split to auto-complete on */
1036  if (gnc_split_register_get_default_account (reg) != NULL)
1037  {
1038  Account *account = gnc_split_register_get_default_account (reg);
1039 
1040  auto_split = gnc_find_split_in_account_by_memo (account, memo,
1041  unit_price);
1042  }
1043  else
1044  auto_split = gnc_find_split_in_reg_by_memo (reg, memo, unit_price);
1045 
1046  if (auto_split == NULL)
1047  return FALSE;
1048 
1049  /* the auto-complete code below is taken from xaccSRGetEntryHandler */
1050 
1051  /* auto-complete the action field if it wasn't changed */
1052  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1053  ACTN_CELL, TRUE))
1054  {
1055  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1056  gnc_combo_cell_set_value ((ComboCell *) cell,
1057  gnc_get_num_action (NULL, auto_split));
1058  }
1059 
1060  /* auto-complete the account name */
1061  cell = gnc_table_layout_get_cell (reg->table->layout, XFRM_CELL);
1062 
1063  account_name = gnc_get_account_name_for_split_register (xaccSplitGetAccount (auto_split),
1064  reg->show_leaf_accounts);
1065  gnc_combo_cell_set_value ((ComboCell *) cell, account_name);
1066  g_free (account_name);
1067 
1068  gnc_basic_cell_set_changed (cell, TRUE);
1069 
1070  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1071  DEBT_CELL, TRUE) &&
1072  !gnc_table_layout_get_cell_changed (reg->table->layout,
1073  CRED_CELL, TRUE))
1074  {
1075  BasicCell *debit_cell;
1076  BasicCell *credit_cell;
1077 
1078  amount = xaccSplitGetValue (auto_split);
1079 
1080  debit_cell = gnc_table_layout_get_cell (reg->table->layout,
1081  DEBT_CELL);
1082  credit_cell = gnc_table_layout_get_cell (reg->table->layout,
1083  CRED_CELL);
1084 
1086  (PriceCell *) credit_cell,
1087  amount);
1088 
1089  gnc_basic_cell_set_changed (debit_cell, TRUE);
1090  gnc_basic_cell_set_changed (credit_cell, TRUE);
1091  }
1092 
1093  /* and refresh the gui */
1094  gnc_table_refresh_gui (reg->table, TRUE);
1095 
1096  /* now move to the non-empty amount column */
1097  amount = xaccSplitGetAmount (auto_split);
1098  cell_name = (gnc_numeric_negative_p (amount)) ? CRED_CELL : DEBT_CELL;
1099 
1100  if (gnc_table_get_current_cell_location (reg->table, cell_name,
1101  &new_virt_loc))
1102  *p_new_virt_loc = new_virt_loc;
1103  }
1104 
1105  break;
1106 
1107  default:
1108  break;
1109  }
1110 
1111  return TRUE;
1112 }
1113 
1114 static void
1115 gnc_split_register_check_stock_action (SplitRegister *reg,
1116  const char *cell_name)
1117 {
1118  BasicCell *cell;
1119  gnc_numeric shares;
1120  gboolean buy, sell;
1121  const char *name;
1122 
1123  if (!gnc_cell_name_equal (cell_name, ACTN_CELL) ||
1124  !gnc_table_layout_get_cell_changed (reg->table->layout,
1125  ACTN_CELL, FALSE))
1126  return;
1127 
1128  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1129  if (!cell)
1130  return;
1131  name = ((ComboCell *)cell)->cell.value;
1132  if ((name == NULL) || (*name == '\0'))
1133  return;
1134 
1135  buy = g_strcmp0 (name, ACTION_BUY_STR) == 0;
1136  sell = g_strcmp0 (name, ACTION_SELL_STR) == 0;
1137  if (!buy && !sell)
1138  return;
1139 
1140  cell = gnc_table_layout_get_cell (reg->table->layout, SHRS_CELL);
1141  if (!cell)
1142  return;
1143  shares = gnc_price_cell_get_value ((PriceCell *) cell);
1144 
1145  if ((buy && !gnc_numeric_positive_p (shares)) ||
1146  (sell && gnc_numeric_positive_p (shares)))
1147  {
1149  gnc_basic_cell_set_changed (cell, TRUE);
1150  }
1151 }
1152 
1153 static void
1154 gnc_split_register_check_stock_shares (SplitRegister *reg,
1155  const char *cell_name)
1156 {
1157  BasicCell *cell;
1158  gnc_numeric shares;
1159  gboolean buy;
1160  const char *name;
1161 
1162  if (!gnc_cell_name_equal (cell_name, SHRS_CELL) ||
1163  !gnc_table_layout_get_cell_changed (reg->table->layout,
1164  SHRS_CELL, FALSE))
1165  return;
1166 
1167  cell = gnc_table_layout_get_cell (reg->table->layout, SHRS_CELL);
1168  if (!cell)
1169  return;
1170  shares = gnc_price_cell_get_value ((PriceCell *) cell);
1171  if (gnc_numeric_zero_p (shares))
1172  return;
1173  buy = gnc_numeric_positive_p (shares);
1174 
1175  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1176  if (!cell)
1177  return;
1178  name = ((ComboCell *)cell)->cell.value;
1179 
1180  if (!g_strcmp0 (name, "") ||
1181  !g_strcmp0 (name, buy ? ACTION_SELL_STR : ACTION_BUY_STR))
1182  {
1183  gnc_combo_cell_set_value ((ComboCell *)cell,
1184  buy ? ACTION_BUY_STR : ACTION_SELL_STR);
1185  gnc_basic_cell_set_changed (cell, TRUE);
1186  }
1187 }
1188 
1189 /* This function checks a cell for changes and takes appropriate action if a
1190  * change has occurred. It is recommended to call this function just before
1191  * leaving a cell. Returns FALSE if control should remain in this cell. For
1192  * example, the user may have made a mistake and needs another chance to
1193  * edit the information before moving on. */
1194 gboolean
1195 gnc_split_register_check_cell (SplitRegister *reg, const char *cell_name)
1196 {
1197  ENTER("reg=%p, cell_name=%s", reg, cell_name ? cell_name : "NULL");
1198 
1199  /* See if we are leaving an account field. */
1200  if (!gnc_split_register_check_account (reg, cell_name))
1201  {
1202  LEAVE("account check failed");
1203  return FALSE;
1204  }
1205 
1206  /* See if we are leaving a debit or credit cell */
1207  if (!gnc_split_register_check_debcred (reg, cell_name))
1208  {
1209  LEAVE("debit/credit check failed");
1210  return FALSE;
1211  }
1212 
1213  /* See if we are leaving an action field */
1214  if ((reg->type == STOCK_REGISTER) ||
1215  (reg->type == PORTFOLIO_LEDGER) ||
1216  (reg->type == CURRENCY_REGISTER))
1217  {
1218  gnc_split_register_check_stock_action (reg, cell_name);
1219  gnc_split_register_check_stock_shares (reg, cell_name);
1220  }
1221 
1222  LEAVE(" ");
1223  return TRUE;
1224 }
1225 
1226 static Account *
1227 gnc_split_register_get_account_always (SplitRegister *reg,
1228  const char *cell_name)
1229 {
1230  BasicCell *cell;
1231  const char *name;
1232 
1233  cell = gnc_table_layout_get_cell (reg->table->layout, cell_name);
1234  if (!cell)
1235  return NULL;
1236  name = gnc_basic_cell_get_value (cell);
1237 
1238  /* If 'name' is "-- Split Transaction --" then return NULL or the
1239  register acct */
1240  if (!g_strcmp0 (name, SPLIT_TRANS_STR))
1241  return NULL;
1242 
1243  return gnc_split_register_get_account_by_name (reg, cell, name);
1244 }
1245 
1246 /* Creates a transfer dialog and fills its values from register cells (if
1247  * available) or from the provided transaction and split.
1248  */
1249 static XferDialog *
1250 gnc_split_register_xfer_dialog (SplitRegister *reg, Transaction *txn,
1251  Split *split)
1252 {
1253  XferDialog *xfer;
1254  CellBlock *cur;
1255  BasicCell *cell;
1256 
1257  g_return_val_if_fail (reg, NULL);
1258  g_return_val_if_fail (reg->table, NULL);
1259 
1260  cur = reg->table->current_cursor;
1261 
1262  /* Create the exchange rate dialog. */
1263  xfer = gnc_xfer_dialog (gnc_split_register_get_parent (reg), NULL);
1264  g_return_val_if_fail (xfer, NULL);
1265 
1266  /* Set the description. */
1267  cell = gnc_cellblock_get_cell_by_name (cur, DESC_CELL, NULL, NULL);
1268  if (cell)
1269  gnc_xfer_dialog_set_description (xfer, gnc_basic_cell_get_value (cell));
1270  else
1271  {
1272  const char *str = xaccTransGetDescription (txn);
1273  gnc_xfer_dialog_set_description (xfer, str ? str : "");
1274  }
1275 
1276  /* Set the memo. */
1277  cell = gnc_cellblock_get_cell_by_name (cur, MEMO_CELL, NULL, NULL);
1278  if (cell)
1279  gnc_xfer_dialog_set_memo (xfer, gnc_basic_cell_get_value (cell));
1280  else
1281  {
1282  const char *str = xaccSplitGetMemo (split);
1283  gnc_xfer_dialog_set_memo (xfer, str ? str : "");
1284  }
1285 
1286  /* Set the num. */
1287  cell = gnc_cellblock_get_cell_by_name (cur, NUM_CELL, NULL, NULL);
1288  if (cell)
1289  gnc_xfer_dialog_set_num (xfer, gnc_basic_cell_get_value (cell));
1290  else
1291  {
1292  const char *str = gnc_get_num_action (txn, split);
1293  gnc_xfer_dialog_set_num (xfer, str ? str : "");
1294  }
1295 
1296  /* Set the date. */
1297  cell = gnc_cellblock_get_cell_by_name (cur, DATE_CELL, NULL, NULL);
1298  if (cell)
1299  {
1300  time64 time;
1301  gnc_date_cell_get_date ((DateCell*) cell, &time, TRUE);
1302  gnc_xfer_dialog_set_date (xfer, time);
1303  }
1304  else
1305  gnc_xfer_dialog_set_date (xfer, xaccTransGetDate (txn));
1306 
1307  return xfer;
1308 }
1309 
1318 gboolean
1319 gnc_split_register_handle_exchange (SplitRegister *reg, gboolean force_dialog)
1320 {
1321  SRInfo *info;
1322  Transaction *txn;
1323  Split *split, *osplit;
1324  Account *xfer_acc, *reg_acc;
1325  gnc_commodity *txn_cur, *xfer_com, *reg_com;
1326  gnc_numeric amount, exch_rate;
1327  XferDialog *xfer;
1328  gboolean expanded = FALSE;
1329  PriceCell *rate_cell;
1330  const char *message;
1331  CursorClass cursor_class;
1332 
1333  ENTER("reg=%p, force_dialog=%s", reg, force_dialog ? "TRUE" : "FALSE" );
1334 
1335  /* No point in setting a rate on a template transaction. */
1336  if (reg->is_template)
1337  {
1338  LEAVE("Template transaction, rate makes no sense.");
1339  return FALSE;
1340  }
1341 
1342  /* Make sure we NEED this for this type of register */
1343  if (!gnc_split_reg_has_rate_cell (reg->type))
1344  {
1345  if (force_dialog)
1346  {
1347  message = _("This register does not support editing exchange rates.");
1348  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1349  }
1350  LEAVE("no rate cell");
1351  return FALSE;
1352  }
1353 
1354  rate_cell = (PriceCell*) gnc_table_layout_get_cell (reg->table->layout, RATE_CELL);
1355 
1356  if (!rate_cell)
1357  {
1358  if (force_dialog)
1359  {
1360  message = _("This register does not support editing exchange rates.");
1361  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1362  }
1363  LEAVE("null rate cell");
1364  return FALSE;
1365  }
1366 
1367  /* See if we already have an exchange rate... */
1368  info = gnc_split_register_get_info (reg);
1369  exch_rate = gnc_price_cell_get_value (rate_cell);
1370  if (!gnc_numeric_zero_p (exch_rate) && !force_dialog &&
1371  info->rate_reset != RATE_RESET_REQD)
1372  {
1373  LEAVE("rate already non-zero");
1374  return FALSE;
1375  }
1376 
1377  /* Are we expanded? */
1379  cursor_class = gnc_split_register_get_current_cursor_class (reg);
1380 
1381  /* If we're expanded AND a transaction cursor, there is nothing to do */
1382  if (expanded && cursor_class == CURSOR_CLASS_TRANS)
1383  {
1384  if (force_dialog)
1385  {
1386  message = _("You need to select a split in order to modify its exchange "
1387  "rate.");
1388  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1389  }
1390  LEAVE("expanded with transaction cursor; nothing to do");
1391  return FALSE;
1392  }
1393 
1394  /* Grab the xfer account */
1395  xfer_acc = gnc_split_register_get_account_always (reg,
1396  expanded ? XFRM_CELL : MXFRM_CELL);
1397 
1398  /* If this is an un-expanded, multi-split transaction, then warn the user */
1399  if (force_dialog && !expanded && !xfer_acc)
1400  {
1401  message = _("You need to expand the transaction in order to modify its "
1402  "exchange rates.");
1403  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1404  LEAVE("%s", message);
1405  return TRUE;
1406  }
1407 
1408  /* No account -- don't run the dialog */
1409  if (!xfer_acc)
1410  {
1411  if (force_dialog)
1412  {
1413  message = _("The entered account could not be found.");
1414  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1415  }
1416  LEAVE("no xfer account");
1417  return FALSE;
1418  }
1419 
1420  /* Grab the txn currency and xfer commodity */
1422  txn_cur = xaccTransGetCurrency (txn);
1423  xfer_com = xaccAccountGetCommodity (xfer_acc);
1424 
1425  /* Grab the register account and commodity (may be used later) */
1426  reg_acc = gnc_split_register_get_default_account (reg);
1427  reg_com = xaccAccountGetCommodity (reg_acc);
1428 
1429  /* Grab the split and perhaps the "other" split (if it is a two-split txn) */
1431  osplit = xaccSplitGetOtherSplit (split);
1432 
1433  /* Check if the txn- and xfer- commodities are the same */
1434  if (gnc_commodity_equal (txn_cur, xfer_com))
1435  {
1436  /* If we're not forcing the dialog, then there is no reason to
1437  * go on. We're using the correct accounts.
1438  */
1439  if (!force_dialog)
1440  {
1441  LEAVE("txn and account currencies match, and not forcing");
1442  return FALSE;
1443  }
1444 
1445  /* Only proceed with two-split, basic, non-expanded registers */
1446  if (expanded || osplit == NULL)
1447  {
1448  message = _("The two currencies involved equal each other.");
1449  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1450  LEAVE("register is expanded or osplit == NULL; not forcing dialog");
1451  return FALSE;
1452  }
1453 
1454  /* If we're forcing, then compare the current account
1455  * commodity to the transaction currency.
1456  */
1457  xfer_acc = reg_acc;
1458  xfer_com = reg_com;
1459  if (gnc_commodity_equal (txn_cur, xfer_com))
1460  {
1461  message = _("The two currencies involved equal each other.");
1462  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1463  LEAVE("reg commodity == txn commodity; not forcing");
1464  return FALSE;
1465  }
1466  }
1467 
1468  /* If this is a non-expanded, two-split txn where BOTH splits need
1469  * conversion rates, then require the user to actually expand the
1470  * transaction in order to edit it.
1471  */
1472  if (!expanded && osplit &&
1473  gnc_split_register_split_needs_amount (reg, split) &&
1474  gnc_split_register_split_needs_amount (reg, osplit))
1475  {
1476  message = _("You need to expand the transaction in order to modify its "
1477  "exchange rates.");
1478  if (force_dialog)
1479  {
1480  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1481  }
1482  LEAVE("%s", message);
1483  return TRUE;
1484  }
1485 
1486  /* Strangely, if we're in a two-split, non-expanded txn, we need
1487  * to do something really special with the exchange rate! In
1488  * particular, we have to pick it up from the _other_ split --
1489  * right?
1490  * XXX: perhaps I should pop up an error here? Or maybe require the
1491  * user to go into expanded-mode?
1492  */
1493  if (!expanded && osplit &&
1494  !gnc_commodity_equal (reg_com, txn_cur) &&
1495  !gnc_commodity_equal (reg_com, xfer_com))
1496  {
1497  gnc_numeric amt = xaccSplitGetAmount (osplit);
1498  gnc_numeric val = xaccSplitGetValue (osplit);
1499  exch_rate = gnc_numeric_div (amt, val, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
1500  }
1501 
1502  /* Ok, we need to grab the exchange rate */
1503  amount = gnc_split_register_debcred_cell_value (reg);
1504 
1505  /*
1506  * If "amount" is zero then we don't need an exchange-rate.. Return
1507  * FALSE to let the user continue on.
1508  */
1509  if (gnc_numeric_zero_p (amount))
1510  {
1511  if (force_dialog)
1512  {
1513  message = _("The split's amount is zero, so no exchange rate is needed.");
1514  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1515  }
1516  LEAVE("amount is zero; no exchange rate needed");
1517  return FALSE;
1518  }
1519 
1520  /* If the exch_rate is zero, we're not forcing the dialog, and this is
1521  * _not_ the blank split, then return FALSE -- this is a "special"
1522  * gain/loss stock transaction.
1523  */
1524  if (gnc_numeric_zero_p (exch_rate) && !force_dialog && split &&
1525  info->rate_reset != RATE_RESET_REQD &&
1526  split != gnc_split_register_get_blank_split (reg))
1527  {
1528  LEAVE("gain/loss split; no exchange rate needed");
1529  return FALSE;
1530  }
1531 
1532  /* Show the exchange-rate dialog */
1533  xfer = gnc_split_register_xfer_dialog (reg, txn, split);
1534  gnc_xfer_dialog_is_exchange_dialog (xfer, &exch_rate);
1535  if (gnc_xfer_dialog_run_exchange_dialog (xfer, &exch_rate, amount,
1536  reg_acc, txn, xfer_com, expanded))
1537  {
1538  /* FIXME: How should the dialog be destroyed? */
1539  LEAVE("leaving rate unchanged");
1540  return TRUE;
1541  }
1542  /* FIXME: How should the dialog be destroyed? */
1543 
1544  /* Set the RATE_CELL on this cursor and mark it changed */
1545  gnc_price_cell_set_value (rate_cell, exch_rate);
1546  gnc_basic_cell_set_changed (&rate_cell->cell, TRUE);
1547  info->rate_account = xfer_acc;
1548  info->rate_reset = RATE_RESET_DONE;
1549  LEAVE("set rate=%s", gnc_num_dbg_to_string (exch_rate));
1550  return FALSE;
1551 }
1552 
1553 /* Returns FALSE if dialog was canceled. */
1554 static gboolean
1555 transaction_changed_confirm (VirtualLocation *p_new_virt_loc,
1556  VirtualLocation *virt_loc,
1557  SplitRegister *reg, Transaction *new_trans,
1558  gboolean exact_traversal)
1559 {
1560  GtkWidget *dialog, *window;
1561  gint response;
1562  const char *title = _("Save the changed transaction?");
1563  const char *message =
1564  _("The current transaction has been changed. Would you like to "
1565  "record the changes before moving to a new transaction, discard the "
1566  "changes, or return to the changed transaction?");
1567 
1568  window = gnc_split_register_get_parent (reg);
1569  dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1570  GTK_DIALOG_DESTROY_WITH_PARENT,
1571  GTK_MESSAGE_QUESTION,
1572  GTK_BUTTONS_NONE,
1573  "%s", title);
1574  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1575  "%s", message);
1576  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1577  _("_Discard Changes"), GTK_RESPONSE_REJECT,
1578  _("_Cancel"), GTK_RESPONSE_CANCEL,
1579  _("_Record Changes"), GTK_RESPONSE_ACCEPT,
1580  NULL);
1581  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_TRANS_MOD);
1582  gtk_widget_destroy (dialog);
1583 
1584  switch (response)
1585  {
1586  case GTK_RESPONSE_ACCEPT:
1587  break;
1588 
1589  case GTK_RESPONSE_REJECT:
1590  {
1591  VirtualCellLocation vcell_loc;
1592  Split *new_split;
1593  Split *trans_split;
1594  CursorClass new_class;
1595 
1596  /* Clear unreconcile split list */
1597  if (reg->unrecn_splits != NULL)
1598  {
1599  g_list_free (reg->unrecn_splits);
1600  reg->unrecn_splits = NULL;
1601  }
1602 
1603  new_split = gnc_split_register_get_split (reg, virt_loc->vcell_loc);
1604  trans_split = gnc_split_register_get_trans_split (reg,
1605  virt_loc->vcell_loc,
1606  NULL);
1607  new_class = gnc_split_register_get_cursor_class (reg,
1608  virt_loc->vcell_loc);
1609 
1611 
1612  if (gnc_split_register_find_split (reg, new_trans, trans_split,
1613  new_split, new_class, &vcell_loc))
1614  virt_loc->vcell_loc = vcell_loc;
1615 
1616  gnc_table_find_close_valid_cell (reg->table, virt_loc,
1617  exact_traversal);
1618 
1619  *p_new_virt_loc = *virt_loc;
1620  }
1621  break;
1622 
1623  case GTK_RESPONSE_CANCEL:
1624  default:
1625  return TRUE;
1626  }
1627 
1628  return FALSE;
1629 }
1630 
1643 static gboolean
1644 gnc_split_register_traverse (VirtualLocation *p_new_virt_loc,
1645  gncTableTraversalDir dir,
1646  gpointer user_data)
1647 {
1648  auto reg = static_cast<SplitRegister*>(user_data);
1649  Transaction *pending_trans;
1650  VirtualLocation virt_loc;
1651  Transaction *trans, *new_trans;
1652  gboolean changed;
1653  SRInfo *info;
1654  Split *split;
1655  const char *cell_name;
1656 
1657  g_return_val_if_fail (p_new_virt_loc, TRUE);
1658 
1659  ENTER("reg=%p, p_new_virt_loc=%p (%d,%d), dir=%d",
1660  reg, p_new_virt_loc,
1661  (*p_new_virt_loc).vcell_loc.virt_row,
1662  (*p_new_virt_loc).vcell_loc.virt_col, dir);
1663 
1664  if (!reg)
1665  {
1666  LEAVE("no register");
1667  return FALSE;
1668  }
1669 
1670  info = gnc_split_register_get_info (reg);
1671 
1672  if (info->first_pass)
1673  {
1674  LEAVE("first pass");
1675  return FALSE;
1676  }
1677 
1678  pending_trans = xaccTransLookup (&info->pending_trans_guid,
1679  gnc_get_current_book ());
1680  virt_loc = *p_new_virt_loc;
1681 
1682  info->exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER);
1683 
1686  if (trans == NULL)
1687  {
1688  LEAVE("no transaction");
1689  return FALSE;
1690  }
1691 
1692  /* no changes, make sure we aren't going off the end */
1693  changed = gnc_table_current_cursor_changed (reg->table, FALSE);
1694  if (!changed && (pending_trans != trans))
1695  {
1696  gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1697  info->exact_traversal);
1698 
1699  *p_new_virt_loc = virt_loc;
1700 
1701  LEAVE("no changes");
1702  return FALSE;
1703  }
1704 
1705  /* Get the current cell-name and check it for changes. */
1706  cell_name = gnc_table_get_current_cell_name (reg->table);
1707  if (!gnc_split_register_check_cell (reg, cell_name))
1708  {
1709  LEAVE("check cell");
1710  return TRUE;
1711  }
1712 
1713  /* See if we are tabbing off the end of the very last line */
1714  do
1715  {
1716  VirtualLocation virt_loc;
1717 
1718  if (!changed && !info->blank_split_edited)
1719  break;
1720 
1721  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1722  break;
1723 
1724  virt_loc = reg->table->current_cursor_loc;
1725  if (gnc_table_move_vertical_position (reg->table, &virt_loc, 1))
1726  break;
1727 
1728  virt_loc = reg->table->current_cursor_loc;
1729  if (gnc_table_move_tab (reg->table, &virt_loc, TRUE))
1730  break;
1731 
1732  /* Deal with the exchange-rate */
1733  if (gnc_split_register_handle_exchange (reg, FALSE))
1734  {
1735  LEAVE("no exchange rate");
1736  return TRUE;
1737  }
1738 
1739  *p_new_virt_loc = reg->table->current_cursor_loc;
1740  (p_new_virt_loc->vcell_loc.virt_row)++;
1741  p_new_virt_loc->phys_row_offset = 0;
1742  p_new_virt_loc->phys_col_offset = 0;
1743 
1744  info->traverse_to_new = TRUE;
1745 
1746  LEAVE("off end of last line");
1747  return FALSE;
1748 
1749  }
1750  while (FALSE);
1751 
1752  /* Now see if we are changing cursors. If not, we may be able to
1753  * auto-complete. */
1754  if (!gnc_table_virtual_cell_out_of_bounds (reg->table, virt_loc.vcell_loc))
1755  {
1756  if (gnc_split_register_auto_completion (reg, dir, p_new_virt_loc))
1757  {
1758  info->auto_complete = TRUE;
1759  LEAVE("auto-complete");
1760  return FALSE;
1761  }
1762  }
1763 
1764  /* See if we are tabbing off the end of a blank split */
1765  do
1766  {
1767  VirtualLocation virt_loc;
1768  int old_virt_row;
1769 
1770  if (!changed)
1771  break;
1772 
1773  if (split)
1774  break;
1775 
1776  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1777  break;
1778 
1779  virt_loc = reg->table->current_cursor_loc;
1780  old_virt_row = virt_loc.vcell_loc.virt_row;
1781 
1782  if (gnc_table_move_tab (reg->table, &virt_loc, TRUE) &&
1783  old_virt_row == virt_loc.vcell_loc.virt_row)
1784  break;
1785 
1786  /* If we are here, then: (a) the current cursor has been
1787  * edited, and (b) we are on the blank split of a multi-line
1788  * transaction, and (c) we are tabbing out of the last cell
1789  * on the line. Thus, we want to go ahead and add the new
1790  * split and end up on the new blank split of the current
1791  * transaction. */
1792 
1793  /* Deal with the exchange-rate */
1794  if (gnc_split_register_handle_exchange (reg, FALSE))
1795  {
1796  LEAVE("no exchange rate");
1797  return TRUE;
1798  }
1799 
1800  info->cursor_hint_trans = trans;
1801  info->cursor_hint_split = split;
1802  info->cursor_hint_trans_split =
1804  info->cursor_hint_cursor_class = CURSOR_CLASS_SPLIT;
1805  info->hint_set_by_traverse = TRUE;
1806 
1807  LEAVE("off end of blank split");
1808  return FALSE;
1809 
1810  }
1811  while (FALSE);
1812 
1813  {
1814  int old_virt_row = reg->table->current_cursor_loc.vcell_loc.virt_row;
1815 
1816  /* Check for going off the end */
1817  gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1818  info->exact_traversal);
1819 
1820 
1821  /* Did we change vertical position? */
1822  if (virt_loc.vcell_loc.virt_row != old_virt_row)
1823  /* Deal with the exchange-rate */
1824  if (gnc_split_register_handle_exchange (reg, FALSE))
1825  {
1826  LEAVE("no exchange rate");
1827  return TRUE;
1828  }
1829  }
1830 
1831 
1832  /* Same transaction, no problem */
1833  new_trans = gnc_split_register_get_trans (reg, virt_loc.vcell_loc);
1834  if (trans == new_trans)
1835  {
1836  *p_new_virt_loc = virt_loc;
1837  {
1838  LEAVE("staying within txn");
1839  return FALSE;
1840  }
1841  }
1842 
1843  /* Ok, we are changing transactions and the current transaction has
1844  * changed. See what the user wants to do. */
1845  LEAVE("txn change");
1846  return transaction_changed_confirm (p_new_virt_loc, &virt_loc, reg,
1847  new_trans, info->exact_traversal);
1848 }
1849 
1850 TableControl *
1852 {
1853  TableControl *control = gnc_table_control_new ();
1854 
1855  control->move_cursor = gnc_split_register_move_cursor;
1856  control->traverse = gnc_split_register_traverse;
1857 
1858  return control;
1859 }
1860 
1861 gboolean
1862 gnc_split_register_recn_cell_confirm (char old_flag, gpointer data)
1863 {
1864  auto reg = static_cast<SplitRegister*>(data);
1865  GtkWidget *dialog, *window;
1866  gint response;
1867  const gchar *title = _("Mark split as unreconciled?");
1868  const gchar *message =
1869  _("You are about to mark a reconciled split as unreconciled. Doing "
1870  "so might make future reconciliation difficult! Continue "
1871  "with this change?");
1872 
1873  if (old_flag != YREC)
1874  return TRUE;
1875 
1876  /* Does the user want to be warned? */
1877  window = gnc_split_register_get_parent (reg);
1878  dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1879  GTK_DIALOG_DESTROY_WITH_PARENT,
1880  GTK_MESSAGE_WARNING,
1881  GTK_BUTTONS_CANCEL,
1882  "%s", title);
1883  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1884  "%s", message);
1885  gtk_dialog_add_button (GTK_DIALOG(dialog),
1886  _("_Unreconcile"),
1887  GTK_RESPONSE_YES);
1888  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_RECD_SPLIT_UNREC);
1889  gtk_widget_destroy (dialog);
1890  return (response == GTK_RESPONSE_YES);
1891 }
CursorClass gnc_split_register_get_current_cursor_class(SplitRegister *reg)
Returns the class of a register&#39;s current cursor.
Split * gnc_split_register_get_current_trans_split(SplitRegister *reg, VirtualCellLocation *trans_split_loc)
Gets the anchoring split of the transaction at the current cursor location, which may be on the trans...
void gnc_copy_trans_onto_trans(Transaction *from, Transaction *to, gboolean use_cut_semantics, gboolean do_commit)
Private function – outsiders must not use this.
gboolean xaccTransHasReconciledSplits(const Transaction *trans)
FIXME: document me.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
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...
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
gboolean gnc_split_register_save(SplitRegister *reg, gboolean do_commit)
Copy the contents of the current cursor to a split.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing. ...
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void gnc_split_register_set_trans_visible(SplitRegister *reg, VirtualCellLocation vcell_loc, gboolean visible, gboolean only_blank_split)
Set the visibility of the split rows belonging to a transaction located at vcell_loc.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3217
Transaction * gnc_split_register_get_current_trans(SplitRegister *reg)
Gets the transaction at the current cursor location, which may be on the transaction itself or on any...
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition: Split.cpp:1472
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2696
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
TableControl specialized for the SplitRegister.
gboolean gnc_table_find_close_valid_cell(Table *table, VirtualLocation *virt_loc, gboolean exact_pointer)
Find a close valid cell.
CursorClass gnc_split_register_get_cursor_class(SplitRegister *reg, VirtualCellLocation vcell_loc)
Returns the class of the cursor at the given virtual cell location.
Save handlers for the SplitRegister Model and Template SplitRegister model.
gboolean gnc_split_reg_has_rate_cell(SplitRegisterType type)
Determine if we need to perform any conversion on the splits in this transaction, and if so...
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gboolean xaccTransIsBalanced(const Transaction *trans)
Returns true if the transaction is balanced according to the rules currently in effect.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gboolean gnc_split_register_current_trans_expanded(SplitRegister *reg)
Return TRUE if current trans is expanded and style is REG_STYLE_LEDGER.
const char * xaccTransGetDocLink(const Transaction *trans)
Gets the transaction Document Link.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
Account used to record multiple commodity transactions.
Definition: Account.h:155
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id...
convert single-entry accounts to clean double-entry
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id, or NULL if there is no such split.
Definition: Split.cpp:1071
void gnc_table_refresh_gui(Table *table, gboolean do_scroll)
Refresh the whole GUI from the table.
Definition: table-gnome.c:165
void gnc_split_register_redraw(SplitRegister *reg)
Causes a redraw of the register window associated with reg.
The ComboCell object implements a cell handler with a "combination-box" pull-down menu in it...
Definition: combocell.h:52
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
Definition: gnc-numeric.h:195
Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description)
Returns a pointer to the transaction, not a copy.
Definition: Account.cpp:4859
CursorClass
Types of cursors.
Account public routines (C++ api)
#define YREC
The Split has been reconciled.
Definition: Split.h:74
char * gnc_get_account_name_for_split_register(const Account *account, gboolean show_leaf_accounts)
Get either the full name of the account or the simple name, depending on the show_leaf_accounts.
The PriceCell object implements a cell handler that stores a single double-precision value...
Definition: pricecell.h:54
Split * gnc_split_register_get_blank_split(SplitRegister *reg)
Gets the blank split for a register.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
void gnc_date_cell_get_date(DateCell *cell, time64 *time, gboolean warn)
Set a time64 to the value in the DateCell.
void xaccTransScrubImbalance(Transaction *trans, Account *root, Account *account)
Correct transaction imbalances.
Definition: Scrub.cpp:829
private declarations for SplitRegister
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
TableControl * gnc_split_register_control_new(void)
Create a new TableControl specialized for the SplitRegister.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
#define xaccSplitGetGUID(X)
Definition: Split.h:552
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
gnc_numeric xaccSplitGetSharePrice(const Split *split)
Returns the price of the split, that is, the value divided by the amount.
Definition: Split.cpp:1932
gboolean gnc_table_move_vertical_position(Table *table, VirtualLocation *virt_loc, int phys_row_offset)
Moves away from virtual location virt_loc by phys_row_offset physical rows.
API for checkbook register display area.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
#define xaccTransGetGUID(X)
Definition: Transaction.h:788
Generic api to store and retrieve preferences.
void gnc_split_register_cancel_cursor_trans_changes(SplitRegister *reg)
Cancels any changes made to the current pending transaction, reloads the table from the engine...
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3351
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:130
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
void gnc_table_set_virt_cell_cursor(Table *table, VirtualCellLocation vcell_loc, CellBlock *cursor)
Set the cellblock handler for a virtual cell.
Definition: table-allgui.c:737
MonetaryList * xaccTransGetImbalance(const Transaction *trans)
The xaccTransGetImbalance method returns a list giving the value of the transaction in each currency ...
gboolean gnc_table_virtual_cell_out_of_bounds(Table *table, VirtualCellLocation vcell_loc)
checks the given location and returns true if it is out of bounds of the table.
Definition: table-allgui.c:207
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void xaccTransSetDocLink(Transaction *trans, const char *doclink)
Sets the transaction Document Link.
Declarations for the Table object.
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gboolean gnc_price_cell_set_value(PriceCell *cell, gnc_numeric amount)
updates amount, returns TRUE if string representation actually changed
Definition: pricecell.c:219
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
The DateCell object implements a date handling cell.
Definition: datecell.h:91
gboolean gnc_split_register_handle_exchange(SplitRegister *reg, gboolean force_dialog)
If needed display the transfer dialog to get a price/exchange rate and adjust the price cell accordin...
Account * gnc_account_get_root(Account *acc)
This routine returns the root account of the account tree that the specified account belongs to...
Definition: Account.cpp:2893
void gnc_price_cell_set_debt_credit_value(PriceCell *debit, PriceCell *credit, gnc_numeric amount)
updates two cells; the deb cell if amt is negative, the credit cell if amount is positive, and makes the other cell blank.
Definition: pricecell.c:281
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
gnc_numeric gnc_price_cell_get_value(PriceCell *cell)
return the value of a price cell
Definition: pricecell.c:208
Split * gnc_split_register_get_current_split(SplitRegister *reg)
Returns the split at which the cursor is currently located.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
BasicCell * gnc_cellblock_get_cell_by_name(CellBlock *cellblock, const char *cell_name, int *row, int *col)
Searches by name for a particular cell in a CellBlock.
Definition: cellblock.c:124
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69