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_zero_p (price))
701  continue;
702  }
703 
704  if (g_strcmp0 (memo, xaccSplitGetMemo (split)) == 0)
705  return split;
706  }
707 
708  return NULL;
709 }
710 
711 static Split *
712 gnc_find_split_in_account_by_memo (Account *account, const char *memo,
713  gboolean unit_price)
714 {
715  if (account == nullptr) return nullptr;
716 
717  const auto& splits = xaccAccountGetSplits (account);
718  for (auto it = splits.rbegin(); it != splits.rend(); it++)
719  if (auto split = gnc_find_split_in_trans_by_memo (xaccSplitGetParent (*it),
720  memo, unit_price))
721  return split;
722 
723  return nullptr;
724 }
725 
726 static Split *
727 gnc_find_split_in_reg_by_memo (SplitRegister *reg, const char *memo,
728  gboolean unit_price)
729 {
730  int virt_row, virt_col;
731  int num_rows, num_cols;
732  Transaction *last_trans;
733 
734  if (!reg || !reg->table)
735  return NULL;
736 
737  num_rows = reg->table->num_virt_rows;
738  num_cols = reg->table->num_virt_cols;
739 
740  last_trans = NULL;
741 
742  for (virt_row = num_rows - 1; virt_row >= 0; virt_row--)
743  for (virt_col = num_cols - 1; virt_col >= 0; virt_col--)
744  {
745  Split *split;
746  Transaction *trans;
747  VirtualCellLocation vcell_loc = { virt_row, virt_col };
748 
749  split = gnc_split_register_get_split (reg, vcell_loc);
750  trans = xaccSplitGetParent (split);
751 
752  if (trans == last_trans)
753  continue;
754 
755  split = gnc_find_split_in_trans_by_memo (trans, memo, unit_price);
756  if (split != NULL)
757  return split;
758 
759  last_trans = trans;
760  }
761 
762  return NULL;
763 }
764 
765 static Transaction *
766 gnc_find_trans_in_reg_by_desc (SplitRegister *reg, const char *description)
767 {
768  int virt_row, virt_col;
769  int num_rows, num_cols;
770  Transaction *last_trans;
771 
772  if (!reg || !reg->table)
773  return NULL;
774 
775  num_rows = reg->table->num_virt_rows;
776  num_cols = reg->table->num_virt_cols;
777 
778  last_trans = NULL;
779 
780  for (virt_row = num_rows - 1; virt_row >= 0; virt_row--)
781  for (virt_col = num_cols - 1; virt_col >= 0; virt_col--)
782  {
783  Split *split;
784  Transaction *trans;
785  VirtualCellLocation vcell_loc = { virt_row, virt_col };
786 
787  split = gnc_split_register_get_split (reg, vcell_loc);
788  trans = xaccSplitGetParent (split);
789 
790  if (trans == last_trans)
791  continue;
792 
793  if (g_strcmp0 (description, xaccTransGetDescription (trans)) == 0)
794  return trans;
795 
796  last_trans = trans;
797  }
798 
799  return NULL;
800 }
801 
802 /* This function determines if auto-completion is appropriate and,
803  * if so, performs it. This should only be called by LedgerTraverse. */
804 static gboolean
805 gnc_split_register_auto_completion (SplitRegister *reg,
806  gncTableTraversalDir dir,
807  VirtualLocation *p_new_virt_loc)
808 {
809  SRInfo *info = gnc_split_register_get_info (reg);
810  VirtualLocation new_virt_loc;
811  CursorClass cursor_class;
812  Transaction *pending_trans;
813  Transaction *blank_trans;
814  const char *cell_name;
815  Transaction *trans;
816  Split *blank_split;
817  gnc_numeric amount;
818  BasicCell *cell;
819  Split *split;
820 
821  if (!reg->do_auto_complete)
822  return FALSE;
823 
824  blank_split = xaccSplitLookup (&info->blank_split_guid,
825  gnc_get_current_book ());
826  blank_trans = xaccSplitGetParent (blank_split);
827 
828  pending_trans = xaccTransLookup (&info->pending_trans_guid,
829  gnc_get_current_book ());
830 
831  /* auto-completion is only triggered by a tab out */
832  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
833  return FALSE;
834 
837  if (trans == NULL)
838  return FALSE;
839 
840  cursor_class = gnc_split_register_get_current_cursor_class (reg);
841  cell_name = gnc_table_get_current_cell_name (reg->table);
842 
843  switch (cursor_class)
844  {
845  case CURSOR_CLASS_TRANS:
846  {
847  Transaction *auto_trans;
848  const char *desc;
849 
850  /* there must be a blank transaction * */
851  if (blank_trans == NULL)
852  return FALSE;
853 
854  /* we must be on the blank split */
855  if (trans != blank_trans)
856  return FALSE;
857 
858  /* and leaving the description cell */
859  if (!gnc_cell_name_equal (cell_name, DESC_CELL))
860  return FALSE;
861 
862  /* nothing but the date, num, and description should be changed */
863  /* FIXME, this should be refactored. */
864  if (gnc_table_layout_get_cell_changed (reg->table->layout,
865  XFRM_CELL, TRUE) ||
866  gnc_table_layout_get_cell_changed (reg->table->layout,
867  MXFRM_CELL, TRUE) ||
868  gnc_table_layout_get_cell_changed (reg->table->layout,
869  PRIC_CELL, TRUE) ||
870  gnc_table_layout_get_cell_changed (reg->table->layout,
871  SHRS_CELL, TRUE) ||
872  gnc_table_layout_get_cell_changed (reg->table->layout,
873  DEBT_CELL, TRUE) ||
874  gnc_table_layout_get_cell_changed (reg->table->layout,
875  CRED_CELL, TRUE) ||
876  gnc_table_layout_get_cell_changed (reg->table->layout,
877  NOTES_CELL, TRUE) ||
878  gnc_table_layout_get_cell_changed (reg->table->layout,
879  RECN_CELL, TRUE))
880  return FALSE;
881 
882  /* and the description should be changed */
883  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
884  DESC_CELL, TRUE))
885  return FALSE;
886 
887  /* to a non-empty value */
888  desc = gnc_table_layout_get_cell_value (reg->table->layout, DESC_CELL);
889  if ((desc == NULL) || (*desc == '\0'))
890  return FALSE;
891 
892  /* find a transaction to auto-complete on */
893  if (gnc_split_register_get_default_account (reg) != NULL)
894  {
895  Account *account = gnc_split_register_get_default_account (reg);
896 
897  auto_trans = xaccAccountFindTransByDesc (account, desc);
898  }
899  else
900  auto_trans = gnc_find_trans_in_reg_by_desc (reg, desc);
901 
902  if (auto_trans == NULL)
903  return FALSE;
904 
905  gnc_suspend_gui_refresh ();
906 
907  /* We are guaranteed to be on the blank trans, so we can
908  discount the possibility that the current transaction is
909  being edited in another register. */
910  /* now perform the completion */
911  if (pending_trans != trans)
912  {
913  if (!xaccTransIsOpen (trans))
914  xaccTransBeginEdit (trans);
915  /* This is now the pending transaction */
916  info->pending_trans_guid = *xaccTransGetGUID (trans);
917  if (pending_trans != NULL)
918  {
919  if (xaccTransIsOpen (pending_trans))
920  xaccTransCommitEdit (pending_trans);
921  else g_assert_not_reached ();
922  }
923  }
924  g_assert(xaccTransIsOpen (trans));
925  pending_trans = xaccTransLookup (&info->pending_trans_guid,
926  gnc_get_current_book ());
927  g_assert (pending_trans == trans);
928 
929  gnc_copy_trans_onto_trans (auto_trans, trans, FALSE, FALSE);
930  /* if there is a doclink, let's clear it */
931  if (xaccTransGetDocLink (auto_trans) != NULL)
932  xaccTransSetDocLink (trans, "");
933  blank_split = NULL;
934 
935  if (gnc_split_register_get_default_account (reg) != NULL)
936  {
937  Account *default_account = gnc_split_register_get_default_account (reg);
938  gnc_commodity *trans_cmdty = xaccTransGetCurrency (trans);
939  gnc_commodity *acct_cmdty = xaccAccountGetCommodity (default_account);
940  if (gnc_commodity_is_currency (acct_cmdty) &&
941  !gnc_commodity_equal (trans_cmdty, acct_cmdty))
942  xaccTransSetCurrency (trans, acct_cmdty);
943 
944  for (GList *n = xaccTransGetSplitList (trans); n; n = n->next)
945  {
946  auto s = GNC_SPLIT(n->data);
947  if (default_account == xaccSplitGetAccount (s))
948  {
949  blank_split = s;
950  info->blank_split_guid = *xaccSplitGetGUID (blank_split);
951  break;
952  }
953  }
954  }
955 
956  if (blank_split == NULL)
957  {
958  blank_split = xaccTransGetSplit (trans, 0);
959  info->blank_split_guid = *xaccSplitGetGUID (blank_split);
960  }
961  DEBUG("blank_split=%p", blank_split);
962 
963  info->blank_split_edited = TRUE;
964 
965  {
966  SRSaveData *sd;
967 
968  sd = gnc_split_register_save_data_new (trans, blank_split,
970  gnc_table_save_cells (reg->table, sd);
971  gnc_split_register_save_data_destroy (sd);
972  }
973 
974  gnc_resume_gui_refresh ();
975 
976  /* now move to the non-empty amount column unless config setting says not */
977  if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
978  GNC_PREF_TAB_TRANS_MEMORISED))
979  {
980  amount = xaccSplitGetAmount (blank_split);
981  cell_name = (gnc_numeric_negative_p (amount)) ? CRED_CELL : DEBT_CELL;
982 
983  if (gnc_table_get_current_cell_location (reg->table, cell_name, &new_virt_loc))
984  *p_new_virt_loc = new_virt_loc;
985  }
986  }
987 
988  break;
989 
990  case CURSOR_CLASS_SPLIT:
991  {
992  char *account_name;
993  const char *memo;
994  gboolean unit_price;
995  Split *auto_split;
996 
997  /* we must be on a blank split of a transaction */
998  if (split != NULL)
999  return FALSE;
1000 
1001  /* and leaving the memo cell */
1002  if (!gnc_cell_name_equal (cell_name, MEMO_CELL))
1003  return FALSE;
1004 
1005  /* nothing but the action, memo, and amounts should be changed */
1006  /* FIXME. This should be refactored. */
1007  if (gnc_table_layout_get_cell_changed (reg->table->layout,
1008  XFRM_CELL, TRUE) ||
1009  gnc_table_layout_get_cell_changed (reg->table->layout,
1010  MXFRM_CELL, TRUE) ||
1011  gnc_table_layout_get_cell_changed (reg->table->layout,
1012  PRIC_CELL, TRUE) ||
1013  gnc_table_layout_get_cell_changed (reg->table->layout,
1014  SHRS_CELL, TRUE) ||
1015  gnc_table_layout_get_cell_changed (reg->table->layout,
1016  RECN_CELL, TRUE))
1017  return FALSE;
1018 
1019  /* and the memo should be changed */
1020  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1021  MEMO_CELL, TRUE))
1022  return FALSE;
1023 
1024  /* to a non-empty value */
1025  memo = gnc_table_layout_get_cell_value (reg->table->layout, MEMO_CELL);
1026  if ((memo == NULL) || (*memo == '\0'))
1027  return FALSE;
1028 
1029  /* if there is no price field, only auto-complete from splits with
1030  * a unit share price. */
1031  unit_price = !gnc_table_get_current_cell_location (reg->table,
1032  PRIC_CELL, NULL);
1033 
1034  /* find a split to auto-complete on */
1035  if (gnc_split_register_get_default_account (reg) != NULL)
1036  {
1037  Account *account = gnc_split_register_get_default_account (reg);
1038 
1039  auto_split = gnc_find_split_in_account_by_memo (account, memo,
1040  unit_price);
1041  }
1042  else
1043  auto_split = gnc_find_split_in_reg_by_memo (reg, memo, unit_price);
1044 
1045  if (auto_split == NULL)
1046  return FALSE;
1047 
1048  /* the auto-complete code below is taken from xaccSRGetEntryHandler */
1049 
1050  /* auto-complete the action field if it wasn't changed */
1051  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1052  ACTN_CELL, TRUE))
1053  {
1054  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1055  gnc_combo_cell_set_value ((ComboCell *) cell,
1056  gnc_get_num_action (NULL, auto_split));
1057  }
1058 
1059  /* auto-complete the account name */
1060  cell = gnc_table_layout_get_cell (reg->table->layout, XFRM_CELL);
1061 
1062  account_name = gnc_get_account_name_for_split_register (xaccSplitGetAccount (auto_split),
1063  reg->show_leaf_accounts);
1064  gnc_combo_cell_set_value ((ComboCell *) cell, account_name);
1065  g_free (account_name);
1066 
1067  gnc_basic_cell_set_changed (cell, TRUE);
1068 
1069  if (!gnc_table_layout_get_cell_changed (reg->table->layout,
1070  DEBT_CELL, TRUE) &&
1071  !gnc_table_layout_get_cell_changed (reg->table->layout,
1072  CRED_CELL, TRUE))
1073  {
1074  BasicCell *debit_cell;
1075  BasicCell *credit_cell;
1076 
1077  amount = xaccSplitGetValue (auto_split);
1078 
1079  debit_cell = gnc_table_layout_get_cell (reg->table->layout,
1080  DEBT_CELL);
1081  credit_cell = gnc_table_layout_get_cell (reg->table->layout,
1082  CRED_CELL);
1083 
1085  (PriceCell *) credit_cell,
1086  amount);
1087 
1088  gnc_basic_cell_set_changed (debit_cell, TRUE);
1089  gnc_basic_cell_set_changed (credit_cell, TRUE);
1090  }
1091 
1092  /* and refresh the gui */
1093  gnc_table_refresh_gui (reg->table, TRUE);
1094 
1095  /* now move to the non-empty amount column */
1096  amount = xaccSplitGetAmount (auto_split);
1097  cell_name = (gnc_numeric_negative_p (amount)) ? CRED_CELL : DEBT_CELL;
1098 
1099  if (gnc_table_get_current_cell_location (reg->table, cell_name,
1100  &new_virt_loc))
1101  *p_new_virt_loc = new_virt_loc;
1102  }
1103 
1104  break;
1105 
1106  default:
1107  break;
1108  }
1109 
1110  return TRUE;
1111 }
1112 
1113 static void
1114 gnc_split_register_check_stock_action (SplitRegister *reg,
1115  const char *cell_name)
1116 {
1117  BasicCell *cell;
1118  gnc_numeric shares;
1119  gboolean buy, sell;
1120  const char *name;
1121 
1122  if (!gnc_cell_name_equal (cell_name, ACTN_CELL) ||
1123  !gnc_table_layout_get_cell_changed (reg->table->layout,
1124  ACTN_CELL, FALSE))
1125  return;
1126 
1127  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1128  if (!cell)
1129  return;
1130  name = ((ComboCell *)cell)->cell.value;
1131  if ((name == NULL) || (*name == '\0'))
1132  return;
1133 
1134  buy = g_strcmp0 (name, ACTION_BUY_STR) == 0;
1135  sell = g_strcmp0 (name, ACTION_SELL_STR) == 0;
1136  if (!buy && !sell)
1137  return;
1138 
1139  cell = gnc_table_layout_get_cell (reg->table->layout, SHRS_CELL);
1140  if (!cell)
1141  return;
1142  shares = gnc_price_cell_get_value ((PriceCell *) cell);
1143 
1144  if ((buy && !gnc_numeric_positive_p (shares)) ||
1145  (sell && gnc_numeric_positive_p (shares)))
1146  {
1148  gnc_basic_cell_set_changed (cell, TRUE);
1149  }
1150 }
1151 
1152 static void
1153 gnc_split_register_check_stock_shares (SplitRegister *reg,
1154  const char *cell_name)
1155 {
1156  BasicCell *cell;
1157  gnc_numeric shares;
1158  gboolean buy;
1159  const char *name;
1160 
1161  if (!gnc_cell_name_equal (cell_name, SHRS_CELL) ||
1162  !gnc_table_layout_get_cell_changed (reg->table->layout,
1163  SHRS_CELL, FALSE))
1164  return;
1165 
1166  cell = gnc_table_layout_get_cell (reg->table->layout, SHRS_CELL);
1167  if (!cell)
1168  return;
1169  shares = gnc_price_cell_get_value ((PriceCell *) cell);
1170  if (gnc_numeric_zero_p (shares))
1171  return;
1172  buy = gnc_numeric_positive_p (shares);
1173 
1174  cell = gnc_table_layout_get_cell (reg->table->layout, ACTN_CELL);
1175  if (!cell)
1176  return;
1177  name = ((ComboCell *)cell)->cell.value;
1178 
1179  if (!g_strcmp0 (name, "") ||
1180  !g_strcmp0 (name, buy ? ACTION_SELL_STR : ACTION_BUY_STR))
1181  {
1182  gnc_combo_cell_set_value ((ComboCell *)cell,
1183  buy ? ACTION_BUY_STR : ACTION_SELL_STR);
1184  gnc_basic_cell_set_changed (cell, TRUE);
1185  }
1186 }
1187 
1188 /* This function checks a cell for changes and takes appropriate action if a
1189  * change has occurred. It is recommended to call this function just before
1190  * leaving a cell. Returns FALSE if control should remain in this cell. For
1191  * example, the user may have made a mistake and needs another chance to
1192  * edit the information before moving on. */
1193 gboolean
1194 gnc_split_register_check_cell (SplitRegister *reg, const char *cell_name)
1195 {
1196  ENTER("reg=%p, cell_name=%s", reg, cell_name ? cell_name : "NULL");
1197 
1198  /* See if we are leaving an account field. */
1199  if (!gnc_split_register_check_account (reg, cell_name))
1200  {
1201  LEAVE("account check failed");
1202  return FALSE;
1203  }
1204 
1205  /* See if we are leaving a debit or credit cell */
1206  if (!gnc_split_register_check_debcred (reg, cell_name))
1207  {
1208  LEAVE("debit/credit check failed");
1209  return FALSE;
1210  }
1211 
1212  /* See if we are leaving an action field */
1213  if ((reg->type == STOCK_REGISTER) ||
1214  (reg->type == PORTFOLIO_LEDGER) ||
1215  (reg->type == CURRENCY_REGISTER))
1216  {
1217  gnc_split_register_check_stock_action (reg, cell_name);
1218  gnc_split_register_check_stock_shares (reg, cell_name);
1219  }
1220 
1221  LEAVE(" ");
1222  return TRUE;
1223 }
1224 
1225 static Account *
1226 gnc_split_register_get_account_always (SplitRegister *reg,
1227  const char *cell_name)
1228 {
1229  BasicCell *cell;
1230  const char *name;
1231 
1232  cell = gnc_table_layout_get_cell (reg->table->layout, cell_name);
1233  if (!cell)
1234  return NULL;
1235  name = gnc_basic_cell_get_value (cell);
1236 
1237  /* If 'name' is "-- Split Transaction --" then return NULL or the
1238  register acct */
1239  if (!g_strcmp0 (name, SPLIT_TRANS_STR))
1240  return NULL;
1241 
1242  return gnc_split_register_get_account_by_name (reg, cell, name);
1243 }
1244 
1245 /* Creates a transfer dialog and fills its values from register cells (if
1246  * available) or from the provided transaction and split.
1247  */
1248 static XferDialog *
1249 gnc_split_register_xfer_dialog (SplitRegister *reg, Transaction *txn,
1250  Split *split)
1251 {
1252  XferDialog *xfer;
1253  CellBlock *cur;
1254  BasicCell *cell;
1255 
1256  g_return_val_if_fail (reg, NULL);
1257  g_return_val_if_fail (reg->table, NULL);
1258 
1259  cur = reg->table->current_cursor;
1260 
1261  /* Create the exchange rate dialog. */
1262  xfer = gnc_xfer_dialog (gnc_split_register_get_parent (reg), NULL);
1263  g_return_val_if_fail (xfer, NULL);
1264 
1265  /* Set the description. */
1266  cell = gnc_cellblock_get_cell_by_name (cur, DESC_CELL, NULL, NULL);
1267  if (cell)
1268  gnc_xfer_dialog_set_description (xfer, gnc_basic_cell_get_value (cell));
1269  else
1270  {
1271  const char *str = xaccTransGetDescription (txn);
1272  gnc_xfer_dialog_set_description (xfer, str ? str : "");
1273  }
1274 
1275  /* Set the memo. */
1276  cell = gnc_cellblock_get_cell_by_name (cur, MEMO_CELL, NULL, NULL);
1277  if (cell)
1278  gnc_xfer_dialog_set_memo (xfer, gnc_basic_cell_get_value (cell));
1279  else
1280  {
1281  const char *str = xaccSplitGetMemo (split);
1282  gnc_xfer_dialog_set_memo (xfer, str ? str : "");
1283  }
1284 
1285  /* Set the num. */
1286  cell = gnc_cellblock_get_cell_by_name (cur, NUM_CELL, NULL, NULL);
1287  if (cell)
1288  gnc_xfer_dialog_set_num (xfer, gnc_basic_cell_get_value (cell));
1289  else
1290  {
1291  const char *str = gnc_get_num_action (txn, split);
1292  gnc_xfer_dialog_set_num (xfer, str ? str : "");
1293  }
1294 
1295  /* Set the date. */
1296  cell = gnc_cellblock_get_cell_by_name (cur, DATE_CELL, NULL, NULL);
1297  if (cell)
1298  {
1299  time64 time;
1300  gnc_date_cell_get_date ((DateCell*) cell, &time, TRUE);
1301  gnc_xfer_dialog_set_date (xfer, time);
1302  }
1303  else
1304  gnc_xfer_dialog_set_date (xfer, xaccTransGetDate (txn));
1305 
1306  return xfer;
1307 }
1308 
1317 gboolean
1318 gnc_split_register_handle_exchange (SplitRegister *reg, gboolean force_dialog)
1319 {
1320  SRInfo *info;
1321  Transaction *txn;
1322  Split *split, *osplit;
1323  Account *xfer_acc, *reg_acc;
1324  gnc_commodity *txn_cur, *xfer_com, *reg_com;
1325  gnc_numeric amount, exch_rate;
1326  XferDialog *xfer;
1327  gboolean expanded = FALSE;
1328  PriceCell *rate_cell;
1329  const char *message;
1330  CursorClass cursor_class;
1331 
1332  ENTER("reg=%p, force_dialog=%s", reg, force_dialog ? "TRUE" : "FALSE" );
1333 
1334  /* No point in setting a rate on a template transaction. */
1335  if (reg->is_template)
1336  {
1337  LEAVE("Template transaction, rate makes no sense.");
1338  return FALSE;
1339  }
1340 
1341  /* Make sure we NEED this for this type of register */
1342  if (!gnc_split_reg_has_rate_cell (reg->type))
1343  {
1344  if (force_dialog)
1345  {
1346  message = _("This register does not support editing exchange rates.");
1347  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1348  }
1349  LEAVE("no rate cell");
1350  return FALSE;
1351  }
1352 
1353  rate_cell = (PriceCell*) gnc_table_layout_get_cell (reg->table->layout, RATE_CELL);
1354 
1355  if (!rate_cell)
1356  {
1357  if (force_dialog)
1358  {
1359  message = _("This register does not support editing exchange rates.");
1360  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1361  }
1362  LEAVE("null rate cell");
1363  return FALSE;
1364  }
1365 
1366  /* See if we already have an exchange rate... */
1367  info = gnc_split_register_get_info (reg);
1368  exch_rate = gnc_price_cell_get_value (rate_cell);
1369  if (!gnc_numeric_zero_p (exch_rate) && !force_dialog &&
1370  info->rate_reset != RATE_RESET_REQD)
1371  {
1372  LEAVE("rate already non-zero");
1373  return FALSE;
1374  }
1375 
1376  /* Are we expanded? */
1378  cursor_class = gnc_split_register_get_current_cursor_class (reg);
1379 
1380  /* If we're expanded AND a transaction cursor, there is nothing to do */
1381  if (expanded && cursor_class == CURSOR_CLASS_TRANS)
1382  {
1383  if (force_dialog)
1384  {
1385  message = _("You need to select a split in order to modify its exchange "
1386  "rate.");
1387  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1388  }
1389  LEAVE("expanded with transaction cursor; nothing to do");
1390  return FALSE;
1391  }
1392 
1393  /* Grab the xfer account */
1394  xfer_acc = gnc_split_register_get_account_always (reg,
1395  expanded ? XFRM_CELL : MXFRM_CELL);
1396 
1397  /* If this is an un-expanded, multi-split transaction, then warn the user */
1398  if (force_dialog && !expanded && !xfer_acc)
1399  {
1400  message = _("You need to expand the transaction in order to modify its "
1401  "exchange rates.");
1402  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1403  LEAVE("%s", message);
1404  return TRUE;
1405  }
1406 
1407  /* No account -- don't run the dialog */
1408  if (!xfer_acc)
1409  {
1410  if (force_dialog)
1411  {
1412  message = _("The entered account could not be found.");
1413  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1414  }
1415  LEAVE("no xfer account");
1416  return FALSE;
1417  }
1418 
1419  /* Grab the txn currency and xfer commodity */
1421  txn_cur = xaccTransGetCurrency (txn);
1422  xfer_com = xaccAccountGetCommodity (xfer_acc);
1423 
1424  /* Grab the register account and commodity (may be used later) */
1425  reg_acc = gnc_split_register_get_default_account (reg);
1426  reg_com = xaccAccountGetCommodity (reg_acc);
1427 
1428  /* Grab the split and perhaps the "other" split (if it is a two-split txn) */
1430  osplit = xaccSplitGetOtherSplit (split);
1431 
1432  /* Check if the txn- and xfer- commodities are the same */
1433  if (gnc_commodity_equal (txn_cur, xfer_com))
1434  {
1435  /* If we're not forcing the dialog, then there is no reason to
1436  * go on. We're using the correct accounts.
1437  */
1438  if (!force_dialog)
1439  {
1440  LEAVE("txn and account currencies match, and not forcing");
1441  return FALSE;
1442  }
1443 
1444  /* Only proceed with two-split, basic, non-expanded registers */
1445  if (expanded || osplit == NULL)
1446  {
1447  message = _("The two currencies involved equal each other.");
1448  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1449  LEAVE("register is expanded or osplit == NULL; not forcing dialog");
1450  return FALSE;
1451  }
1452 
1453  /* If we're forcing, then compare the current account
1454  * commodity to the transaction currency.
1455  */
1456  xfer_acc = reg_acc;
1457  xfer_com = reg_com;
1458  if (gnc_commodity_equal (txn_cur, xfer_com))
1459  {
1460  message = _("The two currencies involved equal each other.");
1461  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1462  LEAVE("reg commodity == txn commodity; not forcing");
1463  return FALSE;
1464  }
1465  }
1466 
1467  /* If this is a non-expanded, two-split txn where BOTH splits need
1468  * conversion rates, then require the user to actually expand the
1469  * transaction in order to edit it.
1470  */
1471  if (!expanded && osplit &&
1472  gnc_split_register_split_needs_amount (reg, split) &&
1473  gnc_split_register_split_needs_amount (reg, osplit))
1474  {
1475  message = _("You need to expand the transaction in order to modify its "
1476  "exchange rates.");
1477  if (force_dialog)
1478  {
1479  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1480  }
1481  LEAVE("%s", message);
1482  return TRUE;
1483  }
1484 
1485  /* Strangely, if we're in a two-split, non-expanded txn, we need
1486  * to do something really special with the exchange rate! In
1487  * particular, we have to pick it up from the _other_ split --
1488  * right?
1489  * XXX: perhaps I should pop up an error here? Or maybe require the
1490  * user to go into expanded-mode?
1491  */
1492  if (!expanded && osplit &&
1493  !gnc_commodity_equal (reg_com, txn_cur) &&
1494  !gnc_commodity_equal (reg_com, xfer_com))
1495  {
1496  gnc_numeric amt = xaccSplitGetAmount (osplit);
1497  gnc_numeric val = xaccSplitGetValue (osplit);
1498  exch_rate = gnc_numeric_div (amt, val, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
1499  }
1500 
1501  /* Ok, we need to grab the exchange rate */
1502  amount = gnc_split_register_debcred_cell_value (reg);
1503 
1504  /*
1505  * If "amount" is zero then we don't need an exchange-rate.. Return
1506  * FALSE to let the user continue on.
1507  */
1508  if (gnc_numeric_zero_p (amount))
1509  {
1510  if (force_dialog)
1511  {
1512  message = _("The split's amount is zero, so no exchange rate is needed.");
1513  gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1514  }
1515  LEAVE("amount is zero; no exchange rate needed");
1516  return FALSE;
1517  }
1518 
1519  /* If the exch_rate is zero, we're not forcing the dialog, and this is
1520  * _not_ the blank split, then return FALSE -- this is a "special"
1521  * gain/loss stock transaction.
1522  */
1523  if (gnc_numeric_zero_p (exch_rate) && !force_dialog && split &&
1524  info->rate_reset != RATE_RESET_REQD &&
1525  split != gnc_split_register_get_blank_split (reg))
1526  {
1527  LEAVE("gain/loss split; no exchange rate needed");
1528  return FALSE;
1529  }
1530 
1531  /* Show the exchange-rate dialog */
1532  xfer = gnc_split_register_xfer_dialog (reg, txn, split);
1533  gnc_xfer_dialog_is_exchange_dialog (xfer, &exch_rate);
1534  if (gnc_xfer_dialog_run_exchange_dialog (xfer, &exch_rate, amount,
1535  reg_acc, txn, xfer_com, expanded))
1536  {
1537  /* FIXME: How should the dialog be destroyed? */
1538  LEAVE("leaving rate unchanged");
1539  return TRUE;
1540  }
1541  /* FIXME: How should the dialog be destroyed? */
1542 
1543  /* Set the RATE_CELL on this cursor and mark it changed */
1544  gnc_price_cell_set_value (rate_cell, exch_rate);
1545  gnc_basic_cell_set_changed (&rate_cell->cell, TRUE);
1546  info->rate_account = xfer_acc;
1547  info->rate_reset = RATE_RESET_DONE;
1548  LEAVE("set rate=%s", gnc_num_dbg_to_string (exch_rate));
1549  return FALSE;
1550 }
1551 
1552 /* Returns FALSE if dialog was canceled. */
1553 static gboolean
1554 transaction_changed_confirm (VirtualLocation *p_new_virt_loc,
1555  VirtualLocation *virt_loc,
1556  SplitRegister *reg, Transaction *new_trans,
1557  gboolean exact_traversal)
1558 {
1559  GtkWidget *dialog, *window;
1560  gint response;
1561  const char *title = _("Save the changed transaction?");
1562  const char *message =
1563  _("The current transaction has been changed. Would you like to "
1564  "record the changes before moving to a new transaction, discard the "
1565  "changes, or return to the changed transaction?");
1566 
1567  window = gnc_split_register_get_parent (reg);
1568  dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1569  GTK_DIALOG_DESTROY_WITH_PARENT,
1570  GTK_MESSAGE_QUESTION,
1571  GTK_BUTTONS_NONE,
1572  "%s", title);
1573  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1574  "%s", message);
1575  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1576  _("_Discard Changes"), GTK_RESPONSE_REJECT,
1577  _("_Cancel"), GTK_RESPONSE_CANCEL,
1578  _("_Record Changes"), GTK_RESPONSE_ACCEPT,
1579  NULL);
1580  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_TRANS_MOD);
1581  gtk_widget_destroy (dialog);
1582 
1583  switch (response)
1584  {
1585  case GTK_RESPONSE_ACCEPT:
1586  break;
1587 
1588  case GTK_RESPONSE_REJECT:
1589  {
1590  VirtualCellLocation vcell_loc;
1591  Split *new_split;
1592  Split *trans_split;
1593  CursorClass new_class;
1594 
1595  /* Clear unreconcile split list */
1596  if (reg->unrecn_splits != NULL)
1597  {
1598  g_list_free (reg->unrecn_splits);
1599  reg->unrecn_splits = NULL;
1600  }
1601 
1602  new_split = gnc_split_register_get_split (reg, virt_loc->vcell_loc);
1603  trans_split = gnc_split_register_get_trans_split (reg,
1604  virt_loc->vcell_loc,
1605  NULL);
1606  new_class = gnc_split_register_get_cursor_class (reg,
1607  virt_loc->vcell_loc);
1608 
1610 
1611  if (gnc_split_register_find_split (reg, new_trans, trans_split,
1612  new_split, new_class, &vcell_loc))
1613  virt_loc->vcell_loc = vcell_loc;
1614 
1615  gnc_table_find_close_valid_cell (reg->table, virt_loc,
1616  exact_traversal);
1617 
1618  *p_new_virt_loc = *virt_loc;
1619  }
1620  break;
1621 
1622  case GTK_RESPONSE_CANCEL:
1623  default:
1624  return TRUE;
1625  }
1626 
1627  return FALSE;
1628 }
1629 
1642 static gboolean
1643 gnc_split_register_traverse (VirtualLocation *p_new_virt_loc,
1644  gncTableTraversalDir dir,
1645  gpointer user_data)
1646 {
1647  auto reg = static_cast<SplitRegister*>(user_data);
1648  Transaction *pending_trans;
1649  VirtualLocation virt_loc;
1650  Transaction *trans, *new_trans;
1651  gboolean changed;
1652  SRInfo *info;
1653  Split *split;
1654  const char *cell_name;
1655 
1656  g_return_val_if_fail (p_new_virt_loc, TRUE);
1657 
1658  ENTER("reg=%p, p_new_virt_loc=%p (%d,%d), dir=%d",
1659  reg, p_new_virt_loc,
1660  (*p_new_virt_loc).vcell_loc.virt_row,
1661  (*p_new_virt_loc).vcell_loc.virt_col, dir);
1662 
1663  if (!reg)
1664  {
1665  LEAVE("no register");
1666  return FALSE;
1667  }
1668 
1669  info = gnc_split_register_get_info (reg);
1670 
1671  if (info->first_pass)
1672  {
1673  LEAVE("first pass");
1674  return FALSE;
1675  }
1676 
1677  pending_trans = xaccTransLookup (&info->pending_trans_guid,
1678  gnc_get_current_book ());
1679  virt_loc = *p_new_virt_loc;
1680 
1681  info->exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER);
1682 
1685  if (trans == NULL)
1686  {
1687  LEAVE("no transaction");
1688  return FALSE;
1689  }
1690 
1691  /* no changes, make sure we aren't going off the end */
1692  changed = gnc_table_current_cursor_changed (reg->table, FALSE);
1693  if (!changed && (pending_trans != trans))
1694  {
1695  gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1696  info->exact_traversal);
1697 
1698  *p_new_virt_loc = virt_loc;
1699 
1700  LEAVE("no changes");
1701  return FALSE;
1702  }
1703 
1704  /* Get the current cell-name and check it for changes. */
1705  cell_name = gnc_table_get_current_cell_name (reg->table);
1706  if (!gnc_split_register_check_cell (reg, cell_name))
1707  {
1708  LEAVE("check cell");
1709  return TRUE;
1710  }
1711 
1712  /* See if we are tabbing off the end of the very last line */
1713  do
1714  {
1715  VirtualLocation virt_loc;
1716 
1717  if (!changed && !info->blank_split_edited)
1718  break;
1719 
1720  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1721  break;
1722 
1723  virt_loc = reg->table->current_cursor_loc;
1724  if (gnc_table_move_vertical_position (reg->table, &virt_loc, 1))
1725  break;
1726 
1727  virt_loc = reg->table->current_cursor_loc;
1728  if (gnc_table_move_tab (reg->table, &virt_loc, TRUE))
1729  break;
1730 
1731  /* Deal with the exchange-rate */
1732  if (gnc_split_register_handle_exchange (reg, FALSE))
1733  {
1734  LEAVE("no exchange rate");
1735  return TRUE;
1736  }
1737 
1738  *p_new_virt_loc = reg->table->current_cursor_loc;
1739  (p_new_virt_loc->vcell_loc.virt_row)++;
1740  p_new_virt_loc->phys_row_offset = 0;
1741  p_new_virt_loc->phys_col_offset = 0;
1742 
1743  info->traverse_to_new = TRUE;
1744 
1745  LEAVE("off end of last line");
1746  return FALSE;
1747 
1748  }
1749  while (FALSE);
1750 
1751  /* Now see if we are changing cursors. If not, we may be able to
1752  * auto-complete. */
1753  if (!gnc_table_virtual_cell_out_of_bounds (reg->table, virt_loc.vcell_loc))
1754  {
1755  if (gnc_split_register_auto_completion (reg, dir, p_new_virt_loc))
1756  {
1757  info->auto_complete = TRUE;
1758  LEAVE("auto-complete");
1759  return FALSE;
1760  }
1761  }
1762 
1763  /* See if we are tabbing off the end of a blank split */
1764  do
1765  {
1766  VirtualLocation virt_loc;
1767  int old_virt_row;
1768 
1769  if (!changed)
1770  break;
1771 
1772  if (split)
1773  break;
1774 
1775  if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1776  break;
1777 
1778  virt_loc = reg->table->current_cursor_loc;
1779  old_virt_row = virt_loc.vcell_loc.virt_row;
1780 
1781  if (gnc_table_move_tab (reg->table, &virt_loc, TRUE) &&
1782  old_virt_row == virt_loc.vcell_loc.virt_row)
1783  break;
1784 
1785  /* If we are here, then: (a) the current cursor has been
1786  * edited, and (b) we are on the blank split of a multi-line
1787  * transaction, and (c) we are tabbing out of the last cell
1788  * on the line. Thus, we want to go ahead and add the new
1789  * split and end up on the new blank split of the current
1790  * transaction. */
1791 
1792  /* Deal with the exchange-rate */
1793  if (gnc_split_register_handle_exchange (reg, FALSE))
1794  {
1795  LEAVE("no exchange rate");
1796  return TRUE;
1797  }
1798 
1799  info->cursor_hint_trans = trans;
1800  info->cursor_hint_split = split;
1801  info->cursor_hint_trans_split =
1803  info->cursor_hint_cursor_class = CURSOR_CLASS_SPLIT;
1804  info->hint_set_by_traverse = TRUE;
1805 
1806  LEAVE("off end of blank split");
1807  return FALSE;
1808 
1809  }
1810  while (FALSE);
1811 
1812  {
1813  int old_virt_row = reg->table->current_cursor_loc.vcell_loc.virt_row;
1814 
1815  /* Check for going off the end */
1816  gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1817  info->exact_traversal);
1818 
1819 
1820  /* Did we change vertical position? */
1821  if (virt_loc.vcell_loc.virt_row != old_virt_row)
1822  /* Deal with the exchange-rate */
1823  if (gnc_split_register_handle_exchange (reg, FALSE))
1824  {
1825  LEAVE("no exchange rate");
1826  return TRUE;
1827  }
1828  }
1829 
1830 
1831  /* Same transaction, no problem */
1832  new_trans = gnc_split_register_get_trans (reg, virt_loc.vcell_loc);
1833  if (trans == new_trans)
1834  {
1835  *p_new_virt_loc = virt_loc;
1836  {
1837  LEAVE("staying within txn");
1838  return FALSE;
1839  }
1840  }
1841 
1842  /* Ok, we are changing transactions and the current transaction has
1843  * changed. See what the user wants to do. */
1844  LEAVE("txn change");
1845  return transaction_changed_confirm (p_new_virt_loc, &virt_loc, reg,
1846  new_trans, info->exact_traversal);
1847 }
1848 
1849 TableControl *
1851 {
1852  TableControl *control = gnc_table_control_new ();
1853 
1854  control->move_cursor = gnc_split_register_move_cursor;
1855  control->traverse = gnc_split_register_traverse;
1856 
1857  return control;
1858 }
1859 
1860 gboolean
1861 gnc_split_register_recn_cell_confirm (char old_flag, gpointer data)
1862 {
1863  auto reg = static_cast<SplitRegister*>(data);
1864  GtkWidget *dialog, *window;
1865  gint response;
1866  const gchar *title = _("Mark split as unreconciled?");
1867  const gchar *message =
1868  _("You are about to mark a reconciled split as unreconciled. Doing "
1869  "so might make future reconciliation difficult! Continue "
1870  "with this change?");
1871 
1872  if (old_flag != YREC)
1873  return TRUE;
1874 
1875  /* Does the user want to be warned? */
1876  window = gnc_split_register_get_parent (reg);
1877  dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1878  GTK_DIALOG_DESTROY_WITH_PARENT,
1879  GTK_MESSAGE_WARNING,
1880  GTK_BUTTONS_CANCEL,
1881  "%s", title);
1882  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1883  "%s", message);
1884  gtk_dialog_add_button (GTK_DIALOG(dialog),
1885  _("_Unreconcile"),
1886  GTK_RESPONSE_YES);
1887  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_RECD_SPLIT_UNREC);
1888  gtk_widget_destroy (dialog);
1889  return (response == GTK_RESPONSE_YES);
1890 }
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_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:1471
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:1070
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:4863
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:1928
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:3359
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