GnuCash  5.6-150-g038405b370+
split-register-util.c
1 /********************************************************************\
2  * split-register-util.c -- split register utilities *
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 gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 #include <config.h>
24 
25 #include <glib.h>
26 
27 #include "gnc-date.h"
28 #include "pricecell.h"
29 #include "split-register-p.h"
30 
31 
32 static QofLogModule log_module = GNC_MOD_LEDGER;
33 
34 
35 /* The routines below create, access, and destroy the SRInfo structure
36  * used by SplitLedger routines to store data for a particular register.
37  * This is the only code that should access the user_data member of a
38  * SplitRegister directly. If additional user data is needed, just add
39  * it to the SRInfo structure above. */
40 static void
41 gnc_split_register_init_info (SplitRegister *reg)
42 {
43  SRInfo *info;
44 
45  if (reg == NULL)
46  return;
47 
48  info = g_new0 (SRInfo, 1);
49 
50  info->blank_split_guid = *guid_null ();
51  info->pending_trans_guid = *guid_null ();
52  info->default_account = *guid_null ();
53  info->template_account = *guid_null ();
54 
55  info->last_date_entered = gnc_time64_get_today_start ();
56 
57  info->first_pass = TRUE;
58  info->quickfill_setup = FALSE;
59  info->full_refresh = TRUE;
60  info->separator_changed = TRUE;
61 
62  reg->sr_info = info;
63 }
64 
65 SRInfo *
66 gnc_split_register_get_info (SplitRegister *reg)
67 {
68  if (!reg)
69  return NULL;
70 
71  if (reg->sr_info == NULL)
72  gnc_split_register_init_info (reg);
73 
74  return reg->sr_info;
75 }
76 
77 GtkWidget *
78 gnc_split_register_get_parent (SplitRegister *reg)
79 {
80  SRInfo *info = gnc_split_register_get_info (reg);
81 
82  if (reg == NULL)
83  return NULL;
84 
85  if (info->get_parent == NULL)
86  return NULL;
87 
88  return info->get_parent (info->user_data);
89 }
90 
91 Split *
92 gnc_split_register_get_split (SplitRegister *reg,
93  VirtualCellLocation vcell_loc)
94 {
95  GncGUID *guid;
96 
97  if (reg == NULL)
98  return NULL;
99 
100  guid = gnc_table_get_vcell_data (reg->table, vcell_loc);
101  if (guid == NULL)
102  return NULL;
103 
104  return xaccSplitLookup (guid, gnc_get_current_book ());
105 }
106 
107 Account *
108 gnc_split_register_get_default_account (SplitRegister *reg)
109 {
110  SRInfo *info = gnc_split_register_get_info (reg);
111 
112  return xaccAccountLookup (&info->default_account,
113  gnc_get_current_book ());
114 }
115 
116 void
118  Account *template_account)
119 {
120  SRInfo *info = gnc_split_register_get_info (reg);
121 
122  g_return_if_fail (reg != NULL);
123 
124  info->template_account = *xaccAccountGetGUID (template_account);
125 }
126 
127 Transaction *
128 gnc_split_register_get_trans (SplitRegister *reg,
129  VirtualCellLocation vcell_loc)
130 {
131  Split *split;
132 
133  if (!reg || !reg->table)
134  return NULL;
135 
136  split = gnc_split_register_get_split (reg, vcell_loc);
137 
138  if (split != NULL)
139  return xaccSplitGetParent(split);
140 
141  /* Split is blank. Assume it is the blank split of a multi-line
142  * transaction. Go back one row to find a split in the transaction. */
143  vcell_loc.virt_row--;
144 
145  split = gnc_split_register_get_split (reg, vcell_loc);
146 
147  /* This split could be NULL during register initialization. */
148  if (split == NULL)
149  return NULL;
150 
151  return xaccSplitGetParent(split);
152 }
153 
154 Split *
155 gnc_split_register_get_trans_split (SplitRegister *reg,
156  VirtualCellLocation vcell_loc,
157  VirtualCellLocation *trans_split_loc)
158 {
159  CursorClass cursor_class;
160 
161  if (reg == NULL)
162  return NULL;
163 
164  while (TRUE)
165  {
166  if ((0 > vcell_loc.virt_row) || (0 > vcell_loc.virt_col))
167  {
168  PERR ("bad row\n");
169  return NULL;
170  }
171 
172  cursor_class = gnc_split_register_get_cursor_class (reg, vcell_loc);
173 
174  if (cursor_class == CURSOR_CLASS_TRANS)
175  {
176  if (trans_split_loc)
177  *trans_split_loc = vcell_loc;
178 
179  return gnc_split_register_get_split (reg, vcell_loc);
180  }
181 
182  vcell_loc.virt_row--;
183  }
184 }
185 
186 Split *
188  SplitRegister *reg, VirtualCellLocation *trans_split_loc)
189 {
190  VirtualCellLocation vcell_loc;
191 
192  if (reg == NULL)
193  return NULL;
194 
195  vcell_loc = reg->table->current_cursor_loc.vcell_loc;
196 
197  return gnc_split_register_get_trans_split (reg, vcell_loc, trans_split_loc);
198 }
199 
200 gboolean
201 gnc_split_register_find_split (SplitRegister *reg,
202  Transaction *trans, Split *trans_split,
203  Split *split, CursorClass find_class,
204  VirtualCellLocation *vcell_loc)
205 {
206  Table *table = reg->table;
207  gboolean found_trans = FALSE;
208  gboolean found_trans_split = FALSE;
209  gboolean found_something = FALSE;
210  CursorClass cursor_class;
211  int v_row, v_col;
212  Transaction *t;
213  Split *s;
214 
215  for (v_row = 1; v_row < table->num_virt_rows; v_row++)
216  for (v_col = 0; v_col < table->num_virt_cols; v_col++)
217  {
218  VirtualCellLocation vc_loc = { v_row, v_col };
219 
220  s = gnc_split_register_get_split (reg, vc_loc);
221  t = xaccSplitGetParent(s);
222 
223  cursor_class = gnc_split_register_get_cursor_class (reg, vc_loc);
224 
225  if (t == trans)
226  {
227  /* A register entry for the correct transaction. */
228  found_trans = TRUE;
229 
230  if (cursor_class == CURSOR_CLASS_TRANS)
231  {
232  /* This row is the transaction split for this transaction. */
233  if (s == trans_split)
234  /* It's the copy of the transaction that we want. */
235  found_trans_split = TRUE;
236  else
237  found_trans_split = FALSE;
238 
239  if (find_class == CURSOR_CLASS_TRANS &&
240  (s == split || reg->style == REG_STYLE_JOURNAL))
241  {
242  /* We're looking for a transaction split and this is the split we're looking for
243  or there is only one entry for this transaction in this register (since it's
244  a journal style register) so we must return the only transaction split there is. */
245  if (vcell_loc != NULL)
246  *vcell_loc = vc_loc;
247  return TRUE;
248  }
249  }
250  }
251  else
252  {
253  /* Not the correct transaction. We shouldn't get here if these are true, but just
254  to be safe reset them. */
255  found_trans = FALSE;
256  found_trans_split = FALSE;
257  }
258 
259  if (found_trans && (s == split) && s)
260  {
261  /* We're on the right transaction, but perhaps not the copy of it we want, and
262  this is the correct split, return it if we don't find anything better. */
263  if (vcell_loc != NULL)
264  *vcell_loc = vc_loc;
265 
266  found_something = TRUE;
267  }
268 
269  if (found_trans_split && (s == split))
270  {
271  /* We're on the right copy of the right transaction, and this is the split we
272  want, return it (it should be the right class since if we wanted a transaction
273  split we would have returned it above. */
274  if (vcell_loc != NULL)
275  *vcell_loc = vc_loc;
276 
277  if (cursor_class == find_class)
278  return TRUE;
279  }
280  }
281 
282  return found_something;
283 }
284 
285 void
286 gnc_split_register_show_trans (SplitRegister *reg,
287  VirtualCellLocation start_loc)
288 {
289  VirtualCellLocation end_loc;
290  int v_row;
291 
292  end_loc = start_loc;
293 
294  for (v_row = end_loc.virt_row + 1;
295  v_row < reg->table->num_virt_rows; v_row++)
296  {
297  VirtualCellLocation vc_loc = { v_row, 0 };
298  CursorClass cursor_class;
299 
300  cursor_class = gnc_split_register_get_cursor_class (reg, vc_loc);
301  if (cursor_class == CURSOR_CLASS_TRANS)
302  break;
303 
304  if (cursor_class != CURSOR_CLASS_SPLIT)
305  {
306  v_row--;
307  break;
308  }
309  }
310 
311  end_loc.virt_row = MIN (v_row, reg->table->num_virt_rows - 1);
312 
313  gnc_table_show_range (reg->table, start_loc, end_loc);
314 }
315 
316 void
318  VirtualCellLocation vcell_loc,
319  gboolean visible,
320  gboolean only_blank_split)
321 {
322  CursorClass cursor_class;
323 
324  while (TRUE)
325  {
326  vcell_loc.virt_row++;
327 
328  cursor_class = gnc_split_register_get_cursor_class (reg, vcell_loc);
329  if (cursor_class != CURSOR_CLASS_SPLIT)
330  return;
331 
332  if (only_blank_split && gnc_split_register_get_split (reg, vcell_loc))
333  continue;
334 
335  gnc_table_set_virt_cell_visible (reg->table, vcell_loc, visible);
336  }
337 }
338 
339 void
340 gnc_split_register_set_cell_fractions (SplitRegister *reg, Split *split)
341 {
342  Account *split_account;
343  Account *reg_account;
344  Transaction *trans;
345  gnc_commodity *trans_currency; /* or default currency if no transaction */
346  PriceCell *cell;
347  int fraction;
348  gboolean trading_accts;
349  gnc_commodity *commodity;
350 
351  /* This function must use the same algorithm as gnc_split_register_get_shares_entry
352  and gnc_split_register_get_debcred_entry. Changes here may require changes in
353  one of them or vice versa. */
354 
355  /* If the split has a new account use that, otherwise use the one it
356  had before we started editing it. */
357  split_account = gnc_split_register_get_account (reg, XFRM_CELL);
358  if (!split_account)
359  split_account = xaccSplitGetAccount (split);
360 
361  reg_account = gnc_split_register_get_default_account (reg);
362 
363  trans = xaccSplitGetParent (split);
364  if (trans)
365  {
366  trading_accts = xaccTransUseTradingAccounts (trans);
367  trans_currency = xaccTransGetCurrency (trans);
368  }
369  else
370  {
371  /* It should be ok to use the current book since that's what
372  gnc_split_register_get_account uses to find the account. */
373  trading_accts = qof_book_use_trading_accounts (gnc_get_current_book());
374  trans_currency = gnc_default_currency();
375  }
376 
377  /* What follows is similar to the tests in gnc_split_register_get_debcred_entry */
378  if (trading_accts)
379  {
380  if (reg->type == STOCK_REGISTER ||
381  reg->type == CURRENCY_REGISTER ||
382  reg->type == PORTFOLIO_LEDGER)
383  {
384  /* These tests are similar to gnc_split_register_use_security_cells */
385  if (!split_account)
386  commodity = trans_currency;
387  else if (trading_accts &&
389  commodity = trans_currency;
390  else if (xaccAccountIsPriced (split_account))
391  commodity = trans_currency;
392  else
393  commodity = xaccAccountGetCommodity (split_account);
394  }
395  else
396  {
397  commodity = xaccAccountGetCommodity (split_account);
398  }
399  }
400  else
401  {
402  /* Not trading accounts */
403  if (reg->type == STOCK_REGISTER ||
404  reg->type == CURRENCY_REGISTER ||
405  reg->type == PORTFOLIO_LEDGER)
406  commodity = trans_currency;
407  else
408  commodity = xaccAccountGetCommodity (reg_account);
409  }
410  if (!commodity)
411  commodity = gnc_default_currency ();
412 
413  fraction = gnc_commodity_get_fraction (commodity);
414 
415  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
416  DEBT_CELL);
417  gnc_price_cell_set_fraction (cell, fraction);
418 
419  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
420  CRED_CELL);
421  gnc_price_cell_set_fraction (cell, fraction);
422 
423  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
424  SHRS_CELL);
425 
426  /* gnc_split_register_get_shares_entry always uses the split's commodity */
427  if (split_account)
429  else
431 }
432 
433 CellBlock *
434 gnc_split_register_get_passive_cursor (SplitRegister *reg)
435 {
436  const char *cursor_name = NULL;
437 
438  switch (reg->style)
439  {
440  case REG_STYLE_LEDGER:
441  case REG_STYLE_AUTO_LEDGER:
442  cursor_name = reg->use_double_line ?
443  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_LEDGER
444  : CURSOR_DOUBLE_LEDGER_NUM_ACTN)
445  : CURSOR_SINGLE_LEDGER;
446  break;
447 
448  case REG_STYLE_JOURNAL:
449  cursor_name = reg->use_double_line ?
450  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_JOURNAL
451  : CURSOR_DOUBLE_JOURNAL_NUM_ACTN)
452  : CURSOR_SINGLE_JOURNAL;
453  break;
454  }
455 
456  if (!cursor_name)
457  {
458  PWARN ("bad register style");
459  return NULL;
460  }
461 
462  return gnc_table_layout_get_cursor (reg->table->layout, cursor_name);
463 }
464 
465 CellBlock *
466 gnc_split_register_get_active_cursor (SplitRegister *reg)
467 {
468  SRInfo *info = gnc_split_register_get_info (reg);
469  const char *cursor_name = NULL;
470 
471  switch (reg->style)
472  {
473  case REG_STYLE_LEDGER:
474  if (!info->trans_expanded)
475  {
476  cursor_name = reg->use_double_line ?
477  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_LEDGER
478  : CURSOR_DOUBLE_LEDGER_NUM_ACTN)
479  : CURSOR_SINGLE_LEDGER;
480  break;
481  }
482 
483  /* fall through */
484  case REG_STYLE_AUTO_LEDGER:
485  case REG_STYLE_JOURNAL:
486  cursor_name = reg->use_double_line ?
487  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_JOURNAL
488  : CURSOR_DOUBLE_JOURNAL_NUM_ACTN)
489  : CURSOR_SINGLE_JOURNAL;
490  break;
491  }
492 
493  if (!cursor_name)
494  {
495  PWARN ("bad register style");
496  return NULL;
497  }
498 
499  return gnc_table_layout_get_cursor (reg->table->layout, cursor_name);
500 }
501 
502 void
503 gnc_split_register_set_last_num (SplitRegister *reg, const char *num)
504 {
505  Account *account;
506 
507  account = gnc_split_register_get_default_account (reg);
508  if (!account)
509  return;
510 
511  xaccAccountSetLastNum (account, num);
512 }
513 
514 static CursorClass
515 gnc_split_register_cursor_class (SplitRegister *reg,
516  CellBlock *cursor)
517 {
518  if (cursor == NULL)
519  return CURSOR_CLASS_NONE;
520 
521  return gnc_split_register_cursor_name_to_class (cursor->cursor_name);
522 }
523 
526  VirtualCellLocation vcell_loc)
527 {
528  VirtualCell *vcell;
529  Table *table;
530 
531  if (reg == NULL)
532  return CURSOR_CLASS_NONE;
533 
534  table = reg->table;
535  if (table == NULL)
536  return CURSOR_CLASS_NONE;
537 
538  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
539  if (vcell == NULL)
540  return CURSOR_CLASS_NONE;
541 
542  return gnc_split_register_cursor_class (reg, vcell->cellblock);
543 }
544 
547 {
548  Table *table;
549 
550  if (reg == NULL)
551  return CURSOR_CLASS_NONE;
552 
553  table = reg->table;
554  if (table == NULL)
555  return CURSOR_CLASS_NONE;
556 
557  return gnc_split_register_cursor_class (reg, table->current_cursor);
558 }
559 
561 gnc_split_register_cursor_name_to_class (const char *cursor_name)
562 {
563  if (cursor_name == NULL)
564  return CURSOR_CLASS_NONE;
565 
566  if (strcmp (cursor_name, CURSOR_SINGLE_LEDGER) == 0 ||
567  strcmp (cursor_name, CURSOR_DOUBLE_LEDGER) == 0 ||
568  strcmp (cursor_name, CURSOR_DOUBLE_LEDGER_NUM_ACTN) == 0 ||
569  strcmp (cursor_name, CURSOR_SINGLE_JOURNAL) == 0 ||
570  strcmp (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
571  strcmp (cursor_name, CURSOR_DOUBLE_JOURNAL_NUM_ACTN) == 0)
572  return CURSOR_CLASS_TRANS;
573 
574  if (strcmp (cursor_name, CURSOR_SPLIT) == 0)
575  return CURSOR_CLASS_SPLIT;
576 
577  return CURSOR_CLASS_NONE;
578 }
CursorClass gnc_split_register_get_current_cursor_class(SplitRegister *reg)
Returns the class of a register&#39;s current cursor.
Split * gnc_split_register_get_current_trans_split(SplitRegister *reg, VirtualCellLocation *trans_split_loc)
Gets the anchoring split of the transaction at the current cursor location, which may be on the trans...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
Date and Time handling routines.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false. ...
Definition: Account.cpp:4640
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.
gpointer gnc_table_get_vcell_data(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell data associated with a cursor located at the given virtual coords...
Definition: table-allgui.c:932
void gnc_table_set_virt_cell_visible(Table *table, VirtualCellLocation vcell_loc, gboolean visible)
Set the visibility flag for a particular location.
Definition: table-allgui.c:720
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2685
STRUCTS.
holds information about each virtual cell.
Definition: table-allgui.h:132
CursorClass gnc_split_register_get_cursor_class(SplitRegister *reg, VirtualCellLocation vcell_loc)
Returns the class of the cursor at the given virtual cell location.
void gnc_split_register_set_template_account(SplitRegister *reg, Account *template_account)
Set the template account for use in a template register.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void xaccAccountSetLastNum(Account *acc, const char *num)
Set the last num field of an Account.
Definition: Account.cpp:4873
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
#define xaccAccountGetGUID(X)
Definition: Account.h:248
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id, or NULL if there is no such split.
Definition: Split.cpp:1070
CursorClass
Types of cursors.
The PriceCell object implements a cell handler that stores a single double-precision value...
Definition: pricecell.h:54
void gnc_table_show_range(Table *table, VirtualCellLocation start_loc, VirtualCellLocation end_loc)
Try to show the whole range in the register.
Definition: table-gnome.c:215
VirtualCell * gnc_table_get_virtual_cell(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell associated with a particular virtual location.
Definition: table-allgui.c:227
private declarations for SplitRegister
time64 gnc_time64_get_today_start(void)
The gnc_time64_get_today_start() routine returns a time64 value corresponding to the first second of ...
Definition: gnc-date.cpp:1346
void gnc_price_cell_set_fraction(PriceCell *cell, int fraction)
Sets the fraction used for rounding.
Definition: pricecell.c:242
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3413
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:130
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
The type used to store guids in C.
Definition: guid.h:75
gboolean qof_book_use_trading_accounts(const QofBook *book)
Returns flag indicating whether this book uses trading accounts.
Definition: qofbook.cpp:909
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2048