GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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. */
50static QofLogModule log_module = GNC_MOD_LEDGER;
51
52static inline bool
53check_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
89static gboolean
90gnc_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
233static gboolean
234gnc_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
270static gboolean
271gnc_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. */
300static gboolean
301gnc_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,
385 GNC_HOW_DENOM_REDUCE);
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
407static inline bool
408is_trading_split (Split *split)
409{
410 auto acct{xaccSplitGetAccount (split)};
411 return GNC_IS_ACCOUNT(acct) && xaccAccountGetType (acct) == ACCT_TYPE_TRADING;
412}
413
414static void
415gnc_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
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);
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
698static Split *
699gnc_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
724static Split *
725gnc_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
739static Split *
740gnc_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
778static Transaction *
779gnc_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. */
817static gboolean
818gnc_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
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
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
1130static void
1131gnc_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
1169static void
1170gnc_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. */
1210gboolean
1211gnc_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
1242static Account *
1243gnc_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 */
1265static XferDialog *
1266gnc_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
1334gboolean
1335gnc_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 /* Registers have either a visible PRIC_CELL (price cell) or a
1359 * hidden RATE_CELL. We need launch the Transfer Dialog to collect
1360 * an exchange rate only when the RATE_CELL is provided. The first
1361 * check looks at the register type to see which kind it's
1362 * supposed to use, the second looks at what's actually in the
1363 * register layout but doesn't always look at the right
1364 * register.
1365 */
1366 if (!gnc_split_reg_has_rate_cell (reg->type))
1367 {
1368 if (force_dialog)
1369 {
1370 message = _("Edit prices/exchange rates directly in the split line.");
1371 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1372 }
1373 LEAVE("no rate cell");
1374 return FALSE;
1375 }
1376
1377 rate_cell = (PriceCell*) gnc_table_layout_get_cell (reg->table->layout, RATE_CELL);
1378
1379 if (!rate_cell)
1380 {
1381 if (force_dialog)
1382 {
1383 message = _("Edit prices/exchange rates directly in the split line.");
1384 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1385 }
1386 LEAVE("null rate cell");
1387 return FALSE;
1388 }
1389
1390 /* See if we already have an exchange rate... */
1391 info = gnc_split_register_get_info (reg);
1392 exch_rate = gnc_price_cell_get_value (rate_cell);
1393 if (!gnc_numeric_zero_p (exch_rate) && !force_dialog &&
1394 info->rate_reset != RATE_RESET_REQD)
1395 {
1396 LEAVE("rate already non-zero");
1397 return FALSE;
1398 }
1399
1400 /* Are we expanded? */
1403
1404 /* If we're expanded AND a transaction cursor, there is nothing to do */
1405 if (expanded && cursor_class == CURSOR_CLASS_TRANS)
1406 {
1407 if (force_dialog)
1408 {
1409 message = _("You need to select a split in order to modify its exchange "
1410 "rate.");
1411 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1412 }
1413 LEAVE("expanded with transaction cursor; nothing to do");
1414 return FALSE;
1415 }
1416
1417 /* Grab the xfer account */
1418 xfer_acc = gnc_split_register_get_account_always (reg,
1419 expanded ? XFRM_CELL : MXFRM_CELL);
1420
1421 /* If this is an un-expanded, multi-split transaction, then warn the user */
1422 if (force_dialog && !expanded && !xfer_acc)
1423 {
1424 message = _("You need to expand the transaction in order to modify its "
1425 "exchange rates.");
1426 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1427 LEAVE("%s", message);
1428 return TRUE;
1429 }
1430
1431 /* No account -- don't run the dialog */
1432 if (!xfer_acc)
1433 {
1434 if (force_dialog)
1435 {
1436 message = _("The entered account could not be found.");
1437 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1438 }
1439 LEAVE("no xfer account");
1440 return FALSE;
1441 }
1442
1443 /* Grab the txn currency and xfer commodity */
1445 txn_cur = xaccTransGetCurrency (txn);
1446 xfer_com = xaccAccountGetCommodity (xfer_acc);
1447
1448 /* Grab the register account and commodity (may be used later) */
1449 reg_acc = gnc_split_register_get_default_account (reg);
1450 reg_com = xaccAccountGetCommodity (reg_acc);
1451
1452 /* Grab the split and perhaps the "other" split (if it is a two-split txn) */
1454 osplit = xaccSplitGetOtherSplit (split);
1455
1456 /* Check if the txn- and xfer- commodities are the same */
1457 if (gnc_commodity_equal (txn_cur, xfer_com))
1458 {
1459 /* If we're not forcing the dialog, then there is no reason to
1460 * go on. We're using the correct accounts.
1461 */
1462 if (!force_dialog)
1463 {
1464 LEAVE("txn and account currencies match, and not forcing");
1465 return FALSE;
1466 }
1467
1468 /* Only proceed with two-split, basic, non-expanded registers */
1469 if (expanded || osplit == NULL)
1470 {
1471 message = _("The two currencies involved equal each other.");
1472 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1473 LEAVE("register is expanded or osplit == NULL; not forcing dialog");
1474 return FALSE;
1475 }
1476
1477 /* If we're forcing, then compare the current account
1478 * commodity to the transaction currency.
1479 */
1480 xfer_acc = reg_acc;
1481 xfer_com = reg_com;
1482 if (gnc_commodity_equal (txn_cur, xfer_com))
1483 {
1484 message = _("The two currencies involved equal each other.");
1485 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1486 LEAVE("reg commodity == txn commodity; not forcing");
1487 return FALSE;
1488 }
1489 }
1490
1491 /* If this is a non-expanded, two-split txn where BOTH splits need
1492 * conversion rates, then require the user to actually expand the
1493 * transaction in order to edit it.
1494 */
1495 if (!expanded && osplit &&
1496 gnc_split_register_split_needs_amount (reg, split) &&
1497 gnc_split_register_split_needs_amount (reg, osplit))
1498 {
1499 message = _("You need to expand the transaction in order to modify its "
1500 "exchange rates.");
1501 if (force_dialog)
1502 {
1503 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1504 }
1505 LEAVE("%s", message);
1506 return TRUE;
1507 }
1508
1509 /* Strangely, if we're in a two-split, non-expanded txn, we need
1510 * to do something really special with the exchange rate! In
1511 * particular, we have to pick it up from the _other_ split --
1512 * right?
1513 * XXX: perhaps I should pop up an error here? Or maybe require the
1514 * user to go into expanded-mode?
1515 */
1516 if (!expanded && osplit &&
1517 !gnc_commodity_equal (reg_com, txn_cur) &&
1518 !gnc_commodity_equal (reg_com, xfer_com))
1519 {
1520 gnc_numeric amt = xaccSplitGetAmount (osplit);
1521 gnc_numeric val = xaccSplitGetValue (osplit);
1522 exch_rate = gnc_numeric_div (amt, val, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
1523 }
1524
1525 /* Ok, we need to grab the exchange rate */
1526 amount = gnc_split_register_debcred_cell_value (reg);
1527
1528 /*
1529 * If "amount" is zero then we don't need an exchange-rate.. Return
1530 * FALSE to let the user continue on.
1531 */
1532 if (gnc_numeric_zero_p (amount))
1533 {
1534 if (force_dialog)
1535 {
1536 message = _("The split's amount is zero, so no exchange rate is needed.");
1537 gnc_error_dialog (GTK_WINDOW(gnc_split_register_get_parent (reg)), "%s", message);
1538 }
1539 LEAVE("amount is zero; no exchange rate needed");
1540 return FALSE;
1541 }
1542
1543 /* If the exch_rate is zero, we're not forcing the dialog, and this is
1544 * _not_ the blank split, then return FALSE -- this is a "special"
1545 * gain/loss stock transaction.
1546 */
1547 if (gnc_numeric_zero_p (exch_rate) && !force_dialog && split &&
1548 info->rate_reset != RATE_RESET_REQD &&
1550 {
1551 LEAVE("gain/loss split; no exchange rate needed");
1552 return FALSE;
1553 }
1554
1555 /* Show the exchange-rate dialog */
1556 xfer = gnc_split_register_xfer_dialog (reg, txn, split);
1557 gnc_xfer_dialog_is_exchange_dialog (xfer, &exch_rate);
1558 if (gnc_xfer_dialog_run_exchange_dialog (xfer, &exch_rate, amount,
1559 reg_acc, txn, xfer_com, expanded))
1560 {
1561 /* FIXME: How should the dialog be destroyed? */
1562 LEAVE("leaving rate unchanged");
1563 return TRUE;
1564 }
1565 /* FIXME: How should the dialog be destroyed? */
1566
1567 /* Set the RATE_CELL on this cursor and mark it changed */
1568 gnc_price_cell_set_value (rate_cell, exch_rate);
1569 gnc_basic_cell_set_changed (&rate_cell->cell, TRUE);
1570 info->rate_account = xfer_acc;
1571 info->rate_reset = RATE_RESET_DONE;
1572 LEAVE("set rate=%s", gnc_num_dbg_to_string (exch_rate));
1573 return FALSE;
1574}
1575
1576/* Returns FALSE if dialog was canceled. */
1577static gboolean
1578transaction_changed_confirm (VirtualLocation *p_new_virt_loc,
1579 VirtualLocation *virt_loc,
1580 SplitRegister *reg, Transaction *new_trans,
1581 gboolean exact_traversal)
1582{
1583 GtkWidget *dialog, *window;
1584 gint response;
1585 const char *title = _("Save the changed transaction?");
1586 const char *message =
1587 _("The current transaction has been changed. Would you like to "
1588 "record the changes before moving to a new transaction, discard the "
1589 "changes, or return to the changed transaction?");
1590
1591 window = gnc_split_register_get_parent (reg);
1592 dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1593 GTK_DIALOG_DESTROY_WITH_PARENT,
1594 GTK_MESSAGE_QUESTION,
1595 GTK_BUTTONS_NONE,
1596 "%s", title);
1597 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1598 "%s", message);
1599 gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1600 _("_Discard Changes"), GTK_RESPONSE_REJECT,
1601 _("_Cancel"), GTK_RESPONSE_CANCEL,
1602 _("_Record Changes"), GTK_RESPONSE_ACCEPT,
1603 NULL);
1604 response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_TRANS_MOD);
1605 gtk_widget_destroy (dialog);
1606
1607 switch (response)
1608 {
1609 case GTK_RESPONSE_ACCEPT:
1610 break;
1611
1612 case GTK_RESPONSE_REJECT:
1613 {
1614 VirtualCellLocation vcell_loc;
1615 Split *new_split;
1616 Split *trans_split;
1617 CursorClass new_class;
1618
1619 /* Clear unreconcile split list */
1620 if (reg->unrecn_splits != NULL)
1621 {
1622 g_list_free (reg->unrecn_splits);
1623 reg->unrecn_splits = NULL;
1624 }
1625
1626 new_split = gnc_split_register_get_split (reg, virt_loc->vcell_loc);
1627 trans_split = gnc_split_register_get_trans_split (reg,
1628 virt_loc->vcell_loc,
1629 NULL);
1630 new_class = gnc_split_register_get_cursor_class (reg,
1631 virt_loc->vcell_loc);
1632
1634
1635 if (gnc_split_register_find_split (reg, new_trans, trans_split,
1636 new_split, new_class, &vcell_loc))
1637 virt_loc->vcell_loc = vcell_loc;
1638
1639 gnc_table_find_close_valid_cell (reg->table, virt_loc,
1640 exact_traversal);
1641
1642 *p_new_virt_loc = *virt_loc;
1643 }
1644 break;
1645
1646 case GTK_RESPONSE_CANCEL:
1647 default:
1648 return TRUE;
1649 }
1650
1651 return FALSE;
1652}
1653
1666static gboolean
1667gnc_split_register_traverse (VirtualLocation *p_new_virt_loc,
1668 gncTableTraversalDir dir,
1669 gpointer user_data)
1670{
1671 auto reg = static_cast<SplitRegister*>(user_data);
1672 Transaction *pending_trans;
1673 VirtualLocation virt_loc;
1674 Transaction *trans, *new_trans;
1675 gboolean changed;
1676 SRInfo *info;
1677 Split *split;
1678 const char *cell_name;
1679
1680 g_return_val_if_fail (p_new_virt_loc, TRUE);
1681
1682 ENTER("reg=%p, p_new_virt_loc=%p (%d,%d), dir=%d",
1683 reg, p_new_virt_loc,
1684 (*p_new_virt_loc).vcell_loc.virt_row,
1685 (*p_new_virt_loc).vcell_loc.virt_col, dir);
1686
1687 if (!reg)
1688 {
1689 LEAVE("no register");
1690 return FALSE;
1691 }
1692
1693 info = gnc_split_register_get_info (reg);
1694
1695 if (info->first_pass)
1696 {
1697 LEAVE("first pass");
1698 return FALSE;
1699 }
1700
1701 pending_trans = xaccTransLookup (&info->pending_trans_guid,
1702 gnc_get_current_book ());
1703 virt_loc = *p_new_virt_loc;
1704
1705 info->exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER);
1706
1709 if (trans == NULL)
1710 {
1711 LEAVE("no transaction");
1712 return FALSE;
1713 }
1714
1715 /* no changes, make sure we aren't going off the end */
1716 changed = gnc_table_current_cursor_changed (reg->table, FALSE);
1717 if (!changed && (pending_trans != trans))
1718 {
1719 gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1720 info->exact_traversal);
1721
1722 *p_new_virt_loc = virt_loc;
1723
1724 LEAVE("no changes");
1725 return FALSE;
1726 }
1727
1728 /* Get the current cell-name and check it for changes. */
1729 cell_name = gnc_table_get_current_cell_name (reg->table);
1730 if (!gnc_split_register_check_cell (reg, cell_name))
1731 {
1732 LEAVE("check cell");
1733 return TRUE;
1734 }
1735
1736 /* See if we are tabbing off the end of the very last line */
1737 do
1738 {
1739 VirtualLocation virt_loc;
1740
1741 if (!changed && !info->blank_split_edited)
1742 break;
1743
1744 if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1745 break;
1746
1747 virt_loc = reg->table->current_cursor_loc;
1748 if (gnc_table_move_vertical_position (reg->table, &virt_loc, 1))
1749 break;
1750
1751 virt_loc = reg->table->current_cursor_loc;
1752 if (gnc_table_move_tab (reg->table, &virt_loc, TRUE))
1753 break;
1754
1755 /* Deal with the exchange-rate */
1756 if (gnc_split_register_handle_exchange (reg, FALSE))
1757 {
1758 LEAVE("no exchange rate");
1759 return TRUE;
1760 }
1761
1762 *p_new_virt_loc = reg->table->current_cursor_loc;
1763 (p_new_virt_loc->vcell_loc.virt_row)++;
1764 p_new_virt_loc->phys_row_offset = 0;
1765 p_new_virt_loc->phys_col_offset = 0;
1766
1767 info->traverse_to_new = TRUE;
1768
1769 LEAVE("off end of last line");
1770 return FALSE;
1771
1772 }
1773 while (FALSE);
1774
1775 /* Now see if we are changing cursors. If not, we may be able to
1776 * auto-complete. */
1777 if (!gnc_table_virtual_cell_out_of_bounds (reg->table, virt_loc.vcell_loc))
1778 {
1779 if (gnc_split_register_auto_completion (reg, dir, p_new_virt_loc))
1780 {
1781 info->auto_complete = TRUE;
1782 LEAVE("auto-complete");
1783 return FALSE;
1784 }
1785 }
1786
1787 /* See if we are tabbing off the end of a blank split */
1788 do
1789 {
1790 VirtualLocation virt_loc;
1791 int old_virt_row;
1792
1793 if (!changed)
1794 break;
1795
1796 if (split)
1797 break;
1798
1799 if (dir != GNC_TABLE_TRAVERSE_RIGHT)
1800 break;
1801
1802 virt_loc = reg->table->current_cursor_loc;
1803 old_virt_row = virt_loc.vcell_loc.virt_row;
1804
1805 if (gnc_table_move_tab (reg->table, &virt_loc, TRUE) &&
1806 old_virt_row == virt_loc.vcell_loc.virt_row)
1807 break;
1808
1809 /* If we are here, then: (a) the current cursor has been
1810 * edited, and (b) we are on the blank split of a multi-line
1811 * transaction, and (c) we are tabbing out of the last cell
1812 * on the line. Thus, we want to go ahead and add the new
1813 * split and end up on the new blank split of the current
1814 * transaction. */
1815
1816 /* Deal with the exchange-rate */
1817 if (gnc_split_register_handle_exchange (reg, FALSE))
1818 {
1819 LEAVE("no exchange rate");
1820 return TRUE;
1821 }
1822
1823 info->cursor_hint_trans = trans;
1824 info->cursor_hint_split = split;
1825 info->cursor_hint_trans_split =
1827 info->cursor_hint_cursor_class = CURSOR_CLASS_SPLIT;
1828 info->hint_set_by_traverse = TRUE;
1829
1830 LEAVE("off end of blank split");
1831 return FALSE;
1832
1833 }
1834 while (FALSE);
1835
1836 {
1837 int old_virt_row = reg->table->current_cursor_loc.vcell_loc.virt_row;
1838
1839 /* Check for going off the end */
1840 gnc_table_find_close_valid_cell (reg->table, &virt_loc,
1841 info->exact_traversal);
1842
1843
1844 /* Did we change vertical position? */
1845 if (virt_loc.vcell_loc.virt_row != old_virt_row)
1846 /* Deal with the exchange-rate */
1847 if (gnc_split_register_handle_exchange (reg, FALSE))
1848 {
1849 LEAVE("no exchange rate");
1850 return TRUE;
1851 }
1852 }
1853
1854
1855 /* Same transaction, no problem */
1856 new_trans = gnc_split_register_get_trans (reg, virt_loc.vcell_loc);
1857 if (trans == new_trans)
1858 {
1859 *p_new_virt_loc = virt_loc;
1860 {
1861 LEAVE("staying within txn");
1862 return FALSE;
1863 }
1864 }
1865
1866 /* Ok, we are changing transactions and the current transaction has
1867 * changed. See what the user wants to do. */
1868 LEAVE("txn change");
1869 return transaction_changed_confirm (p_new_virt_loc, &virt_loc, reg,
1870 new_trans, info->exact_traversal);
1871}
1872
1875{
1876 TableControl *control = gnc_table_control_new ();
1877
1878 control->move_cursor = gnc_split_register_move_cursor;
1879 control->traverse = gnc_split_register_traverse;
1880
1881 return control;
1882}
1883
1884gboolean
1885gnc_split_register_recn_cell_confirm (char old_flag, gpointer data)
1886{
1887 auto reg = static_cast<SplitRegister*>(data);
1888 GtkWidget *dialog, *window;
1889 gint response;
1890 const gchar *title = _("Mark split as unreconciled?");
1891 const gchar *message =
1892 _("You are about to mark a reconciled split as unreconciled. Doing "
1893 "so might make future reconciliation difficult! Continue "
1894 "with this change?");
1895
1896 if (old_flag != YREC)
1897 return TRUE;
1898
1899 /* Does the user want to be warned? */
1900 window = gnc_split_register_get_parent (reg);
1901 dialog = gtk_message_dialog_new (GTK_WINDOW(window),
1902 GTK_DIALOG_DESTROY_WITH_PARENT,
1903 GTK_MESSAGE_WARNING,
1904 GTK_BUTTONS_CANCEL,
1905 "%s", title);
1906 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1907 "%s", message);
1908 gtk_dialog_add_button (GTK_DIALOG(dialog),
1909 _("_Unreconcile"),
1910 GTK_RESPONSE_YES);
1911 response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_REG_RECD_SPLIT_UNREC);
1912 gtk_widget_destroy (dialog);
1913 return (response == GTK_RESPONSE_YES);
1914}
Account public routines (C++ api)
convert single-entry accounts to clean double-entry
Generic api to store and retrieve preferences.
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:2942
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:3305
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition Account.cpp:2745
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id,...
Definition Account.cpp:2050
@ ACCT_TYPE_TRADING
Account used to record multiple commodity transactions.
Definition Account.h:155
gnc_numeric gnc_price_cell_get_value(PriceCell *cell)
return the value of a price cell
Definition pricecell.c:208
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,...
Definition pricecell.c:281
gboolean gnc_price_cell_set_value(PriceCell *cell, gnc_numeric amount)
updates amount, returns TRUE if string representation actually changed
Definition pricecell.c:219
void gnc_date_cell_get_date(DateCell *cell, time64 *time, gboolean warn)
Set a time64 to the value in the DateCell.
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
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
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.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
const char * xaccTransGetDocLink(const Transaction *trans)
Gets the transaction Document Link.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
MonetaryList * xaccTransGetImbalance(const Transaction *trans)
The xaccTransGetImbalance method returns a list giving the value of the transaction in each currency ...
gboolean xaccTransIsOpen(const Transaction *trans)
The xaccTransIsOpen() method returns TRUE if the transaction is open for editing.
gboolean xaccTransIsBalanced(const Transaction *trans)
Returns true if the transaction is balanced according to the rules currently in effect.
#define xaccTransGetGUID(X)
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id,...
gboolean xaccTransHasReconciledSplits(const Transaction *trans)
FIXME: document me.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction's split list.
void xaccTransSetDocLink(Transaction *trans, const char *doclink)
Sets the transaction Document Link.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
Transaction * xaccAccountFindTransByDesc(const Account *acc, const char *description)
Returns a pointer to the transaction, not a copy.
Definition Account.cpp:4921
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
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.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
@ GNC_HOW_DENOM_REDUCE
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void xaccTransScrubImbalance(Transaction *trans, Account *root, Account *account)
Correct transaction imbalances.
Definition Scrub.cpp:845
gboolean gnc_split_register_current_trans_expanded(SplitRegister *reg)
Return TRUE if current trans is expanded and style is REG_STYLE_LEDGER.
CursorClass gnc_split_register_get_cursor_class(SplitRegister *reg, VirtualCellLocation vcell_loc)
Returns the class of the cursor at the given virtual cell location.
gboolean gnc_split_register_save(SplitRegister *reg, gboolean do_commit)
Copy the contents of the current cursor to a split.
Split * gnc_split_register_get_current_split(SplitRegister *reg)
Returns the split at which the cursor is currently located.
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...
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...
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,...
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.
void gnc_split_register_redraw(SplitRegister *reg)
Causes a redraw of the register window associated with reg.
TableControl * gnc_split_register_control_new(void)
Create a new TableControl specialized for the SplitRegister.
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...
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,...
Split * gnc_split_register_get_blank_split(SplitRegister *reg)
Gets the blank split for a register.
CursorClass
Types of cursors.
CursorClass gnc_split_register_get_current_cursor_class(SplitRegister *reg)
Returns the class of a register's current cursor.
void gnc_table_refresh_gui(Table *table, gboolean do_scroll)
Refresh the whole GUI from the table.
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.
void gnc_table_set_virt_cell_cursor(Table *table, VirtualCellLocation vcell_loc, CellBlock *cursor)
Set the cellblock handler for a virtual cell.
gboolean gnc_table_find_close_valid_cell(Table *table, VirtualLocation *virt_loc, gboolean exact_pointer)
Find a close valid cell.
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.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id,...
Definition Split.cpp:1091
#define xaccSplitGetGUID(X)
Definition Split.h:579
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gnc_numeric xaccSplitGetSharePrice(const Split *split)
Returns the price of the split, that is, the value divided by the amount.
Definition Split.cpp:1995
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits.
#define YREC
The Split has been reconciled.
Definition Split.h:74
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
TableControl specialized for the SplitRegister.
Save handlers for the SplitRegister Model and Template SplitRegister model.
private declarations for SplitRegister
API for checkbook register display area.
STRUCTS.
The ComboCell object implements a cell handler with a "combination-box" pull-down menu in it.
Definition combocell.h:53
The DateCell object implements a date handling cell.
Definition datecell.h:92
The PriceCell object implements a cell handler that stores a single double-precision value,...
Definition pricecell.h:55
Declarations for the Table object.