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