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