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