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