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