GnuCash  4.12-558-g06612b8434
qofbook.cpp
1 /********************************************************************\
2  * qofbook.c -- dataset access (set of books of entities) *
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  * FILE:
24  * qofbook.cpp
25  *
26  * FUNCTION:
27  * Encapsulate all the information about a QOF dataset.
28  *
29  * HISTORY:
30  * Created by Linas Vepstas December 1998
31  * Copyright (c) 1998-2001,2003 Linas Vepstas <linas@linas.org>
32  * Copyright (c) 2000 Dave Peticolas
33  * Copyright (c) 2007 David Hampton <hampton@employees.org>
34  */
35 #include <glib.h>
36 
37 extern "C"
38 {
39 
40 #include <config.h>
41 
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #ifdef GNC_PLATFORM_WINDOWS
46  /* Mingw disables the standard type macros for C++ without this override. */
47 #define __STDC_FORMAT_MACROS = 1
48 #endif
49 #include <inttypes.h>
50 
51 }
52 
53 #include "qof.h"
54 #include "qofevent-p.h"
55 #include "qofbackend.h"
56 #include "qofbook-p.h"
57 #include "qofid-p.h"
58 #include "qofobject-p.h"
59 #include "qofbookslots.h"
60 #include "kvp-frame.hpp"
61 // For GNC_ID_ROOT_ACCOUNT:
62 #include "AccountP.h"
63 
64 #include "qofbook.hpp"
65 
66 static QofLogModule log_module = QOF_MOD_ENGINE;
67 #define AB_KEY "hbci"
68 #define AB_TEMPLATES "template-list"
69 
70 enum
71 {
72  PROP_0,
73 // PROP_ROOT_ACCOUNT, /* Table */
74 // PROP_ROOT_TEMPLATE, /* Table */
75  PROP_OPT_TRADING_ACCOUNTS, /* KVP */
76  PROP_OPT_AUTO_READONLY_DAYS, /* KVP */
77  PROP_OPT_NUM_FIELD_SOURCE, /* KVP */
78  PROP_OPT_DEFAULT_BUDGET, /* KVP */
79  PROP_OPT_FY_END, /* KVP */
80  PROP_AB_TEMPLATES, /* KVP */
81  N_PROPERTIES /* Just a counter */
82 };
83 
84 static void
85 qof_book_option_num_field_source_changed_cb (GObject *gobject,
86  GParamSpec *pspec,
87  gpointer user_data);
88 static void
89 qof_book_option_num_autoreadonly_changed_cb (GObject *gobject,
90  GParamSpec *pspec,
91  gpointer user_data);
92 
93 // Use a #define for the GParam name to avoid typos
94 #define PARAM_NAME_NUM_FIELD_SOURCE "split-action-num-field"
95 #define PARAM_NAME_NUM_AUTOREAD_ONLY "autoreadonly-days"
96 
97 G_DEFINE_TYPE(QofBook, qof_book, QOF_TYPE_INSTANCE);
98 QOF_GOBJECT_DISPOSE(qof_book);
99 QOF_GOBJECT_FINALIZE(qof_book);
100 
101 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
102 #undef G_PARAM_READWRITE
103 #define G_PARAM_READWRITE static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_WRITABLE)
104 /* ====================================================================== */
105 /* constructor / destructor */
106 
107 static void coll_destroy(gpointer col)
108 {
109  qof_collection_destroy((QofCollection *) col);
110 }
111 
112 static void
113 qof_book_init (QofBook *book)
114 {
115  if (!book) return;
116 
117  book->hash_of_collections = g_hash_table_new_full(
118  g_str_hash, g_str_equal,
119  (GDestroyNotify)qof_string_cache_remove, /* key_destroy_func */
120  coll_destroy); /* value_destroy_func */
121 
122  qof_instance_init_data (&book->inst, QOF_ID_BOOK, book);
123 
124  book->data_tables = g_hash_table_new (g_str_hash, g_str_equal);
125  book->data_table_finalizers = g_hash_table_new (g_str_hash, g_str_equal);
126 
127  book->book_open = 'y';
128  book->read_only = FALSE;
129  book->session_dirty = FALSE;
130  book->version = 0;
131  book->cached_num_field_source_isvalid = FALSE;
132  book->cached_num_days_autoreadonly_isvalid = FALSE;
133 
134  // Register a callback on this NUM_FIELD_SOURCE property of that object
135  // because it gets called quite a lot, so that its value must be stored in
136  // a bool member variable instead of a KVP lookup on each getter call.
137  g_signal_connect (G_OBJECT(book),
138  "notify::" PARAM_NAME_NUM_FIELD_SOURCE,
139  G_CALLBACK (qof_book_option_num_field_source_changed_cb),
140  book);
141 
142  // Register a callback on this NUM_AUTOREAD_ONLY property of that object
143  // because it gets called quite a lot, so that its value must be stored in
144  // a bool member variable instead of a KVP lookup on each getter call.
145  g_signal_connect (G_OBJECT(book),
146  "notify::" PARAM_NAME_NUM_AUTOREAD_ONLY,
147  G_CALLBACK (qof_book_option_num_autoreadonly_changed_cb),
148  book);
149 }
150 
151 static const std::string str_KVP_OPTION_PATH(KVP_OPTION_PATH);
152 static const std::string str_OPTION_SECTION_ACCOUNTS(OPTION_SECTION_ACCOUNTS);
153 static const std::string str_OPTION_SECTION_BUDGETING(OPTION_SECTION_BUDGETING);
154 static const std::string str_OPTION_NAME_DEFAULT_BUDGET(OPTION_NAME_DEFAULT_BUDGET);
155 static const std::string str_OPTION_NAME_TRADING_ACCOUNTS(OPTION_NAME_TRADING_ACCOUNTS);
156 static const std::string str_OPTION_NAME_AUTO_READONLY_DAYS(OPTION_NAME_AUTO_READONLY_DAYS);
157 static const std::string str_OPTION_NAME_NUM_FIELD_SOURCE(OPTION_NAME_NUM_FIELD_SOURCE);
158 
159 static void
160 qof_book_get_property (GObject* object,
161  guint prop_id,
162  GValue* value,
163  GParamSpec* pspec)
164 {
165  QofBook *book;
166  gchar *key;
167 
168  g_return_if_fail (QOF_IS_BOOK (object));
169  book = QOF_BOOK (object);
170  switch (prop_id)
171  {
172  case PROP_OPT_TRADING_ACCOUNTS:
173  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
174  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
175  break;
176  case PROP_OPT_AUTO_READONLY_DAYS:
177  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
178  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
179  break;
180  case PROP_OPT_NUM_FIELD_SOURCE:
181  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
182  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
183  break;
184  case PROP_OPT_DEFAULT_BUDGET:
185  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
186  str_OPTION_SECTION_BUDGETING, str_OPTION_NAME_DEFAULT_BUDGET});
187  break;
188  case PROP_OPT_FY_END:
189  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
190  break;
191  case PROP_AB_TEMPLATES:
192  qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"AB_KEY", "AB_TEMPLATES"});
193  break;
194  default:
195  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
196  break;
197  }
198 }
199 
200 static void
201 qof_book_set_property (GObject *object,
202  guint prop_id,
203  const GValue *value,
204  GParamSpec *pspec)
205 {
206  QofBook *book;
207  gchar *key;
208 
209  g_return_if_fail (QOF_IS_BOOK (object));
210  book = QOF_BOOK (object);
211  g_assert (qof_instance_get_editlevel(book));
212 
213  switch (prop_id)
214  {
215  case PROP_OPT_TRADING_ACCOUNTS:
216  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
217  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
218  break;
219  case PROP_OPT_AUTO_READONLY_DAYS:
220  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
221  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
222  break;
223  case PROP_OPT_NUM_FIELD_SOURCE:
224  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
225  str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_NUM_FIELD_SOURCE});
226  break;
227  case PROP_OPT_DEFAULT_BUDGET:
228  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
229  str_OPTION_SECTION_BUDGETING, OPTION_NAME_DEFAULT_BUDGET});
230  break;
231  case PROP_OPT_FY_END:
232  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
233  break;
234  case PROP_AB_TEMPLATES:
235  qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {AB_KEY, AB_TEMPLATES});
236  break;
237  default:
238  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
239  break;
240  }
241 }
242 
243 static void
244 qof_book_class_init (QofBookClass *klass)
245 {
246  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
247  gobject_class->dispose = qof_book_dispose;
248  gobject_class->finalize = qof_book_finalize;
249  gobject_class->get_property = qof_book_get_property;
250  gobject_class->set_property = qof_book_set_property;
251 
252  g_object_class_install_property
253  (gobject_class,
254  PROP_OPT_TRADING_ACCOUNTS,
255  g_param_spec_string("trading-accts",
256  "Use Trading Accounts",
257  "Scheme true ('t') or NULL. If 't', then the book "
258  "uses trading accounts for managing multiple-currency "
259  "transactions.",
260  NULL,
261  G_PARAM_READWRITE));
262 
263  g_object_class_install_property
264  (gobject_class,
265  PROP_OPT_NUM_FIELD_SOURCE,
266  g_param_spec_string(PARAM_NAME_NUM_FIELD_SOURCE,
267  "Use Split-Action in the Num Field",
268  "Scheme true ('t') or NULL. If 't', then the book "
269  "will put the split action value in the Num field.",
270  NULL,
271  G_PARAM_READWRITE));
272 
273  g_object_class_install_property
274  (gobject_class,
275  PROP_OPT_AUTO_READONLY_DAYS,
276  g_param_spec_double("autoreadonly-days",
277  "Transaction Auto-read-only Days",
278  "Prevent editing of transactions posted more than "
279  "this many days ago.",
280  0,
281  G_MAXDOUBLE,
282  0,
283  G_PARAM_READWRITE));
284 
285  g_object_class_install_property
286  (gobject_class,
287  PROP_OPT_DEFAULT_BUDGET,
288  g_param_spec_boxed("default-budget",
289  "Book Default Budget",
290  "The default Budget for this book.",
291  GNC_TYPE_GUID,
292  G_PARAM_READWRITE));
293  g_object_class_install_property
294  (gobject_class,
295  PROP_OPT_FY_END,
296  g_param_spec_boxed("fy-end",
297  "Book Fiscal Year End",
298  "A GDate with a bogus year having the last Month and "
299  "Day of the Fiscal year for the book.",
300  G_TYPE_DATE,
301  G_PARAM_READWRITE));
302  g_object_class_install_property
303  (gobject_class,
304  PROP_AB_TEMPLATES,
305  g_param_spec_boxed("ab-templates",
306  "AQBanking Template List",
307  "A GList of AQBanking Templates",
308  GNC_TYPE_VALUE_LIST,
309  G_PARAM_READWRITE));
310 }
311 
312 QofBook *
314 {
315  QofBook *book;
316 
317  ENTER (" ");
318  book = static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, NULL));
319  qof_object_book_begin (book);
320 
321  qof_event_gen (&book->inst, QOF_EVENT_CREATE, NULL);
322  LEAVE ("book=%p", book);
323  return book;
324 }
325 
326 static void
327 book_final (gpointer key, gpointer value, gpointer booq)
328 {
329  QofBookFinalCB cb = reinterpret_cast<QofBookFinalCB>(value);
330  QofBook *book = static_cast<QofBook*>(booq);
331 
332  gpointer user_data = g_hash_table_lookup (book->data_tables, key);
333  (*cb) (book, key, user_data);
334 }
335 
336 static void
337 qof_book_dispose_real (G_GNUC_UNUSED GObject *bookp)
338 {
339 }
340 
341 static void
342 qof_book_finalize_real (G_GNUC_UNUSED GObject *bookp)
343 {
344 }
345 
346 void
347 qof_book_destroy (QofBook *book)
348 {
349  GHashTable* cols;
350 
351  if (!book) return;
352  ENTER ("book=%p", book);
353 
354  book->shutting_down = TRUE;
355  qof_event_force (&book->inst, QOF_EVENT_DESTROY, NULL);
356 
357  /* Call the list of finalizers, let them do their thing.
358  * Do this before tearing into the rest of the book.
359  */
360  g_hash_table_foreach (book->data_table_finalizers, book_final, book);
361 
362  qof_object_book_end (book);
363 
364  g_hash_table_destroy (book->data_table_finalizers);
365  book->data_table_finalizers = NULL;
366  g_hash_table_destroy (book->data_tables);
367  book->data_tables = NULL;
368 
369  /* qof_instance_release (&book->inst); */
370 
371  /* Note: we need to save this hashtable until after we remove ourself
372  * from it, otherwise we'll crash in our dispose() function when we
373  * DO remove ourself from the collection but the collection had already
374  * been destroyed.
375  */
376  cols = book->hash_of_collections;
377  g_object_unref (book);
378  g_hash_table_destroy (cols);
379  /*book->hash_of_collections = NULL;*/
380 
381  LEAVE ("book=%p", book);
382 }
383 
384 /* ====================================================================== */
385 
386 gboolean
387 qof_book_session_not_saved (const QofBook *book)
388 {
389  if (!book) return FALSE;
390  return !qof_book_empty(book) && book->session_dirty;
391 
392 }
393 
394 void
396 {
397  if (!book) return;
398 
399  book->dirty_time = 0;
400  if (book->session_dirty)
401  {
402  /* Set the session clean upfront, because the callback will check. */
403  book->session_dirty = FALSE;
404  if (book->dirty_cb)
405  book->dirty_cb(book, FALSE, book->dirty_data);
406  }
407 }
408 
409 void qof_book_mark_session_dirty (QofBook *book)
410 {
411  if (!book) return;
412  if (!book->session_dirty)
413  {
414  /* Set the session dirty upfront, because the callback will check. */
415  book->session_dirty = TRUE;
416  book->dirty_time = gnc_time (NULL);
417  if (book->dirty_cb)
418  book->dirty_cb(book, TRUE, book->dirty_data);
419  }
420 }
421 
422 void
423 qof_book_print_dirty (const QofBook *book)
424 {
425  if (qof_book_session_not_saved(book))
426  PINFO("book is dirty.");
427  qof_book_foreach_collection
428  (book, (QofCollectionForeachCB)qof_collection_print_dirty, NULL);
429 }
430 
431 time64
432 qof_book_get_session_dirty_time (const QofBook *book)
433 {
434  return book->dirty_time;
435 }
436 
437 void
438 qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
439 {
440  g_return_if_fail(book);
441  if (book->dirty_cb)
442  PWARN("Already existing callback %p, will be overwritten by %p\n",
443  book->dirty_cb, cb);
444  book->dirty_data = user_data;
445  book->dirty_cb = cb;
446 }
447 
448 /* ====================================================================== */
449 /* getters */
450 
451 QofBackend *
452 qof_book_get_backend (const QofBook *book)
453 {
454  if (!book) return NULL;
455  return book->backend;
456 }
457 
458 gboolean
459 qof_book_shutting_down (const QofBook *book)
460 {
461  if (!book) return FALSE;
462  return book->shutting_down;
463 }
464 
465 /* ====================================================================== */
466 /* setters */
467 
468 void
469 qof_book_set_backend (QofBook *book, QofBackend *be)
470 {
471  if (!book) return;
472  ENTER ("book=%p be=%p", book, be);
473  book->backend = be;
474  LEAVE (" ");
475 }
476 
477 /* ====================================================================== */
478 /* Store arbitrary pointers in the QofBook for data storage extensibility */
479 /* XXX if data is NULL, we should remove the key from the hash table!
480  */
481 void
482 qof_book_set_data (QofBook *book, const char *key, gpointer data)
483 {
484  if (!book || !key) return;
485  g_hash_table_insert (book->data_tables, (gpointer)key, data);
486 }
487 
488 void
489 qof_book_set_data_fin (QofBook *book, const char *key, gpointer data, QofBookFinalCB cb)
490 {
491  if (!book || !key) return;
492  g_hash_table_insert (book->data_tables, (gpointer)key, data);
493 
494  if (!cb) return;
495  g_hash_table_insert (book->data_table_finalizers, (gpointer)key,
496  reinterpret_cast<void*>(cb));
497 }
498 
499 gpointer
500 qof_book_get_data (const QofBook *book, const char *key)
501 {
502  if (!book || !key) return NULL;
503  return g_hash_table_lookup (book->data_tables, (gpointer)key);
504 }
505 
506 /* ====================================================================== */
507 gboolean
508 qof_book_is_readonly(const QofBook *book)
509 {
510  g_return_val_if_fail( book != NULL, TRUE );
511  return book->read_only;
512 }
513 
514 void
516 {
517  g_return_if_fail( book != NULL );
518  book->read_only = TRUE;
519 }
520 
521 gboolean
522 qof_book_empty(const QofBook *book)
523 {
524  if (!book) return TRUE;
525  auto root_acct_col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
526  return qof_collection_get_data(root_acct_col) == nullptr;
527 }
528 
529 /* ====================================================================== */
530 
531 QofCollection *
532 qof_book_get_collection (const QofBook *book, QofIdType entity_type)
533 {
534  QofCollection *col;
535 
536  if (!book || !entity_type) return NULL;
537 
538  col = static_cast<QofCollection*>(g_hash_table_lookup (book->hash_of_collections, entity_type));
539  if (!col)
540  {
541  col = qof_collection_new (entity_type);
542  g_hash_table_insert(
543  book->hash_of_collections,
544  (gpointer)qof_string_cache_insert(entity_type), col);
545  }
546  return col;
547 }
548 
549 struct _iterate
550 {
552  gpointer data;
553 };
554 
555 static void
556 foreach_cb (G_GNUC_UNUSED gpointer key, gpointer item, gpointer arg)
557 {
558  struct _iterate *iter = static_cast<_iterate*>(arg);
559  QofCollection *col = static_cast<QofCollection*>(item);
560 
561  iter->fn (col, iter->data);
562 }
563 
564 void
565 qof_book_foreach_collection (const QofBook *book,
566  QofCollectionForeachCB cb, gpointer user_data)
567 {
568  struct _iterate iter;
569 
570  g_return_if_fail (book);
571  g_return_if_fail (cb);
572 
573  iter.fn = cb;
574  iter.data = user_data;
575 
576  g_hash_table_foreach (book->hash_of_collections, foreach_cb, &iter);
577 }
578 
579 /* ====================================================================== */
580 
581 void qof_book_mark_closed (QofBook *book)
582 {
583  if (!book)
584  {
585  return;
586  }
587  book->book_open = 'n';
588 }
589 
590 gint64
591 qof_book_get_counter (QofBook *book, const char *counter_name)
592 {
593  KvpFrame *kvp;
594  KvpValue *value;
595 
596  if (!book)
597  {
598  PWARN ("No book!!!");
599  return -1;
600  }
601 
602  if (!counter_name || *counter_name == '\0')
603  {
604  PWARN ("Invalid counter name.");
605  return -1;
606  }
607 
608  /* Use the KVP in the book */
609  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
610 
611  if (!kvp)
612  {
613  PWARN ("Book has no KVP_Frame");
614  return -1;
615  }
616 
617  value = kvp->get_slot({"counters", counter_name});
618  if (value)
619  {
620  /* found it */
621  return value->get<int64_t>();
622  }
623  else
624  {
625  /* New counter */
626  return 0;
627  }
628 }
629 
630 gchar *
631 qof_book_increment_and_format_counter (QofBook *book, const char *counter_name)
632 {
633  KvpFrame *kvp;
634  KvpValue *value;
635  gint64 counter;
636  gchar* format;
637  gchar* result;
638 
639  if (!book)
640  {
641  PWARN ("No book!!!");
642  return NULL;
643  }
644 
645  if (!counter_name || *counter_name == '\0')
646  {
647  PWARN ("Invalid counter name.");
648  return NULL;
649  }
650 
651  /* Get the current counter value from the KVP in the book. */
652  counter = qof_book_get_counter(book, counter_name);
653 
654  /* Check if an error occurred */
655  if (counter < 0)
656  return NULL;
657 
658  /* Increment the counter */
659  counter++;
660 
661  /* Get the KVP from the current book */
662  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
663 
664  if (!kvp)
665  {
666  PWARN ("Book has no KVP_Frame");
667  return NULL;
668  }
669 
670  /* Save off the new counter */
671  qof_book_begin_edit(book);
672  value = new KvpValue(counter);
673  delete kvp->set_path({"counters", counter_name}, value);
674  qof_instance_set_dirty (QOF_INSTANCE (book));
675  qof_book_commit_edit(book);
676 
677  format = qof_book_get_counter_format(book, counter_name);
678 
679  if (!format)
680  {
681  PWARN("Cannot get format for counter");
682  return NULL;
683  }
684 
685  /* Generate a string version of the counter */
686  result = g_strdup_printf(format, counter);
687  g_free (format);
688  return result;
689 }
690 
691 char *
692 qof_book_get_counter_format(const QofBook *book, const char *counter_name)
693 {
694  KvpFrame *kvp;
695  const char *user_format = NULL;
696  gchar *norm_format = NULL;
697  KvpValue *value;
698  gchar *error = NULL;
699 
700  if (!book)
701  {
702  PWARN ("No book!!!");
703  return NULL;
704  }
705 
706  if (!counter_name || *counter_name == '\0')
707  {
708  PWARN ("Invalid counter name.");
709  return NULL;
710  }
711 
712  /* Get the KVP from the current book */
713  kvp = qof_instance_get_slots (QOF_INSTANCE (book));
714 
715  if (!kvp)
716  {
717  PWARN ("Book has no KVP_Frame");
718  return NULL;
719  }
720 
721  /* Get the format string */
722  value = kvp->get_slot({"counter_formats", counter_name});
723  if (value)
724  {
725  user_format = value->get<const char*>();
726  norm_format = qof_book_normalize_counter_format(user_format, &error);
727  if (!norm_format)
728  {
729  PWARN("Invalid counter format string. Format string: '%s' Counter: '%s' Error: '%s')", user_format, counter_name, error);
730  /* Invalid format string */
731  user_format = NULL;
732  g_free(error);
733  }
734  }
735 
736  /* If no (valid) format string was found, use the default format
737  * string */
738  if (!norm_format)
739  {
740  /* Use the default format */
741  norm_format = g_strdup ("%.6" PRIi64);
742  }
743  return norm_format;
744 }
745 
746 gchar *
747 qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
748 {
749  const gchar *valid_formats [] = {
750  G_GINT64_FORMAT,
751  "lli",
752  "I64i",
753  PRIi64,
754  "li",
755  NULL,
756  };
757  int i = 0;
758  gchar *normalized_spec = NULL;
759 
760  while (valid_formats[i])
761  {
762 
763  if (err_msg && *err_msg)
764  {
765  g_free (*err_msg);
766  *err_msg = NULL;
767  }
768 
769  normalized_spec = qof_book_normalize_counter_format_internal(p, valid_formats[i], err_msg);
770  if (normalized_spec)
771  return normalized_spec; /* Found a valid format specifier, return */
772  i++;
773  }
774 
775  return NULL;
776 }
777 
778 gchar *
780  const gchar *gint64_format, gchar **err_msg)
781 {
782  const gchar *conv_start, *base, *tmp = NULL;
783  gchar *normalized_str = NULL, *aux_str = NULL;
784 
785  /* Validate a counter format. This is a very simple "parser" that
786  * simply checks for a single gint64 conversion specification,
787  * allowing all modifiers and flags that printf(3) specifies (except
788  * for the * width and precision, which need an extra argument). */
789  base = p;
790 
791  /* Skip a prefix of any character except % */
792  while (*p)
793  {
794  /* Skip two adjacent percent marks, which are literal percent
795  * marks */
796  if (p[0] == '%' && p[1] == '%')
797  {
798  p += 2;
799  continue;
800  }
801  /* Break on a single percent mark, which is the start of the
802  * conversion specification */
803  if (*p == '%')
804  break;
805  /* Skip all other characters */
806  p++;
807  }
808 
809  if (!*p)
810  {
811  if (err_msg)
812  *err_msg = g_strdup("Format string ended without any conversion specification");
813  return NULL;
814  }
815 
816  /* Store the start of the conversion for error messages */
817  conv_start = p;
818 
819  /* Skip the % */
820  p++;
821 
822  /* See whether we have already reached the correct format
823  * specification (e.g. "li" on Unix, "I64i" on Windows). */
824  tmp = strstr(p, gint64_format);
825 
826  if (!tmp)
827  {
828  if (err_msg)
829  *err_msg = g_strdup_printf("Format string doesn't contain requested format specifier: %s", gint64_format);
830  return NULL;
831  }
832 
833  /* Skip any number of flag characters */
834  while (*p && (tmp != p) && strchr("#0- +'I", *p))
835  {
836  p++;
837  tmp = strstr(p, gint64_format);
838  }
839 
840  /* Skip any number of field width digits,
841  * and precision specifier digits (including the leading dot) */
842  while (*p && (tmp != p) && strchr("0123456789.", *p))
843  {
844  p++;
845  tmp = strstr(p, gint64_format);
846  }
847 
848  if (!*p)
849  {
850  if (err_msg)
851  *err_msg = g_strdup_printf("Format string ended during the conversion specification. Conversion seen so far: %s", conv_start);
852  return NULL;
853  }
854 
855  /* See if the format string starts with the correct format
856  * specification. */
857  tmp = strstr(p, gint64_format);
858  if (tmp == NULL)
859  {
860  if (err_msg)
861  *err_msg = g_strdup_printf("Invalid length modifier and/or conversion specifier ('%.4s'), it should be: %s", p, gint64_format);
862  return NULL;
863  }
864  else if (tmp != p)
865  {
866  if (err_msg)
867  *err_msg = g_strdup_printf("Garbage before length modifier and/or conversion specifier: '%*s'", (int)(tmp - p), p);
868  return NULL;
869  }
870 
871  /* Copy the string we have so far and add normalized format specifier for long int */
872  aux_str = g_strndup (base, p - base);
873  normalized_str = g_strconcat (aux_str, PRIi64, nullptr);
874  g_free (aux_str);
875 
876  /* Skip length modifier / conversion specifier */
877  p += strlen(gint64_format);
878  tmp = p;
879 
880  /* Skip a suffix of any character except % */
881  while (*p)
882  {
883  /* Skip two adjacent percent marks, which are literal percent
884  * marks */
885  if (p[0] == '%' && p[1] == '%')
886  {
887  p += 2;
888  continue;
889  }
890  /* Break on a single percent mark, which is the start of the
891  * conversion specification */
892  if (*p == '%')
893  {
894  if (err_msg)
895  *err_msg = g_strdup_printf("Format string contains unescaped %% signs (or multiple conversion specifications) at '%s'", p);
896  g_free (normalized_str);
897  return NULL;
898  }
899  /* Skip all other characters */
900  p++;
901  }
902 
903  /* Add the suffix to our normalized string */
904  aux_str = normalized_str;
905  normalized_str = g_strconcat (aux_str, tmp, nullptr);
906  g_free (aux_str);
907 
908  /* If we end up here, the string was valid, so return no error
909  * message */
910  return normalized_str;
911 }
912 
913 /* Determine whether this book uses trading accounts */
914 gboolean
915 qof_book_use_trading_accounts (const QofBook *book)
916 {
917  char *opt = nullptr;
918  qof_instance_get (QOF_INSTANCE (book), "trading-accts", &opt, nullptr);
919  auto retval = (opt && opt[0] == 't' && opt[1] == 0);
920  g_free (opt);
921  return retval;
922 }
923 
924 /* Returns TRUE if this book uses split action field as the 'Num' field, FALSE
925  * if it uses transaction number field */
926 gboolean
928 {
929  g_return_val_if_fail (book, FALSE);
930  if (!book->cached_num_field_source_isvalid)
931  {
932  // No cached value? Then do the expensive KVP lookup
933  gboolean result;
934  char *opt = NULL;
935  qof_instance_get (QOF_INSTANCE (book),
936  PARAM_NAME_NUM_FIELD_SOURCE, &opt,
937  NULL);
938 
939  if (opt && opt[0] == 't' && opt[1] == 0)
940  result = TRUE;
941  else
942  result = FALSE;
943  g_free (opt);
944 
945  // We need to const_cast the "book" argument into a non-const pointer,
946  // but as we are dealing only with cache variables, I think this is
947  // understandable enough.
948  const_cast<QofBook*>(book)->cached_num_field_source = result;
949  const_cast<QofBook*>(book)->cached_num_field_source_isvalid = TRUE;
950  }
951  // Value is cached now. Use the cheap variable returning.
952  return book->cached_num_field_source;
953 }
954 
955 // The callback that is called when the KVP option value of
956 // "split-action-num-field" changes, so that we mark the cached value as
957 // invalid.
958 static void
959 qof_book_option_num_field_source_changed_cb (GObject *gobject,
960  GParamSpec *pspec,
961  gpointer user_data)
962 {
963  QofBook *book = reinterpret_cast<QofBook*>(user_data);
964  g_return_if_fail(QOF_IS_BOOK(book));
965  book->cached_num_field_source_isvalid = FALSE;
966 }
967 
968 gboolean qof_book_uses_autoreadonly (const QofBook *book)
969 {
970  g_assert(book);
971  return (qof_book_get_num_days_autoreadonly(book) != 0);
972 }
973 
974 gint qof_book_get_num_days_autoreadonly (const QofBook *book)
975 {
976  g_assert(book);
977 
978  if (!book->cached_num_days_autoreadonly_isvalid)
979  {
980  double tmp;
981 
982  // No cached value? Then do the expensive KVP lookup
983  qof_instance_get (QOF_INSTANCE (book),
984  PARAM_NAME_NUM_AUTOREAD_ONLY, &tmp,
985  NULL);
986 
987  const_cast<QofBook*>(book)->cached_num_days_autoreadonly = tmp;
988  const_cast<QofBook*>(book)->cached_num_days_autoreadonly_isvalid = TRUE;
989  }
990  // Value is cached now. Use the cheap variable returning.
991  return (gint) book->cached_num_days_autoreadonly;
992 }
993 
994 GDate* qof_book_get_autoreadonly_gdate (const QofBook *book)
995 {
996  gint num_days;
997  GDate* result = NULL;
998 
999  g_assert(book);
1000  num_days = qof_book_get_num_days_autoreadonly(book);
1001  if (num_days > 0)
1002  {
1003  result = gnc_g_date_new_today();
1004  g_date_subtract_days(result, num_days);
1005  }
1006  return result;
1007 }
1008 
1009 // The callback that is called when the KVP option value of
1010 // "autoreadonly-days" changes, so that we mark the cached value as
1011 // invalid.
1012 static void
1013 qof_book_option_num_autoreadonly_changed_cb (GObject *gobject,
1014  GParamSpec *pspec,
1015  gpointer user_data)
1016 {
1017  QofBook *book = reinterpret_cast<QofBook*>(user_data);
1018  g_return_if_fail(QOF_IS_BOOK(book));
1019  book->cached_num_days_autoreadonly_isvalid = FALSE;
1020 }
1021 
1022 /* Note: this will fail if the book slots we're looking for here are flattened at some point !
1023  * When that happens, this function can be removed. */
1024 static Path opt_name_to_path (const char* opt_name)
1025 {
1026  Path result;
1027  g_return_val_if_fail (opt_name, result);
1028  auto opt_name_list = g_strsplit(opt_name, "/", -1);
1029  for (int i=0; opt_name_list[i]; i++)
1030  result.push_back (opt_name_list[i]);
1031  g_strfreev (opt_name_list);
1032  return result;
1033 }
1034 
1035 const char*
1036 qof_book_get_string_option(const QofBook* book, const char* opt_name)
1037 {
1038  auto slot = qof_instance_get_slots(QOF_INSTANCE (book))->get_slot(opt_name_to_path(opt_name));
1039  if (slot == nullptr)
1040  return nullptr;
1041  return slot->get<const char*>();
1042 }
1043 
1044 void
1045 qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val)
1046 {
1047  qof_book_begin_edit(book);
1048  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1049  auto opt_path = opt_name_to_path(opt_name);
1050  if (opt_val && (*opt_val != '\0'))
1051  delete frame->set_path(opt_path, new KvpValue(g_strdup(opt_val)));
1052  else
1053  delete frame->set_path(opt_path, nullptr);
1054  qof_instance_set_dirty (QOF_INSTANCE (book));
1055  qof_book_commit_edit(book);
1056 }
1057 
1058 const GncGUID*
1059 qof_book_get_guid_option(QofBook* book, GSList* path)
1060 {
1061  g_return_val_if_fail(book != nullptr, nullptr);
1062  g_return_val_if_fail(path != nullptr, nullptr);
1063 
1064  auto table_value = qof_book_get_option(book, path);
1065  if (!table_value)
1066  return nullptr;
1067  return table_value->get<GncGUID*>();
1068 }
1069 
1070 void
1071 qof_book_option_frame_delete (QofBook *book, const char* opt_name)
1072 {
1073  if (opt_name && (*opt_name != '\0'))
1074  {
1075  qof_book_begin_edit(book);
1076  auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
1077  auto opt_path = opt_name_to_path(opt_name);
1078  delete frame->set_path(opt_path, nullptr);
1079  qof_instance_set_dirty (QOF_INSTANCE (book));
1080  qof_book_commit_edit(book);
1081  }
1082 }
1083 
1084 void
1085 qof_book_begin_edit (QofBook *book)
1086 {
1087  qof_begin_edit(&book->inst);
1088 }
1089 
1090 static void commit_err (G_GNUC_UNUSED QofInstance *inst, QofBackendError errcode)
1091 {
1092  PERR ("Failed to commit: %d", errcode);
1093 // gnc_engine_signal_commit_error( errcode );
1094 }
1095 
1096 #define GNC_FEATURES "features"
1097 static void
1098 add_feature_to_hash (const gchar *key, KvpValue *value, GHashTable * user_data)
1099 {
1100  gchar *descr = g_strdup(value->get<const char*>());
1101  g_hash_table_insert (user_data, (gchar*)key, descr);
1102 }
1103 
1104 GHashTable *
1105 qof_book_get_features (QofBook *book)
1106 {
1107  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1108  GHashTable *features = g_hash_table_new_full (g_str_hash, g_str_equal,
1109  NULL, g_free);
1110 
1111  PWARN ("qof_book_get_features is now deprecated.");
1112 
1113  auto slot = frame->get_slot({GNC_FEATURES});
1114  if (slot != nullptr)
1115  {
1116  frame = slot->get<KvpFrame*>();
1117  frame->for_each_slot_temp(&add_feature_to_hash, features);
1118  }
1119  return features;
1120 }
1121 
1122 void
1123 qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr)
1124 {
1125  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1126  KvpValue* feature = nullptr;
1127  auto feature_slot = frame->get_slot({GNC_FEATURES});
1128  if (feature_slot)
1129  {
1130  auto feature_frame = feature_slot->get<KvpFrame*>();
1131  feature = feature_frame->get_slot({key});
1132  }
1133  if (feature == nullptr || g_strcmp0 (feature->get<const char*>(), descr))
1134  {
1135  qof_book_begin_edit (book);
1136  delete frame->set_path({GNC_FEATURES, key}, new KvpValue(g_strdup (descr)));
1137  qof_instance_set_dirty (QOF_INSTANCE (book));
1138  qof_book_commit_edit (book);
1139  }
1140 }
1141 
1142 std::vector<std::string>
1143 qof_book_get_unknown_features (QofBook *book, const FeaturesTable& features)
1144 {
1145  std::vector<std::string> rv;
1146  auto test_feature = [&](const KvpFrameImpl::map_type::value_type& feature)
1147  {
1148  if (features.find (feature.first) == features.end ())
1149  rv.push_back (feature.second->get<const char*>());
1150  };
1151  auto frame = qof_instance_get_slots (QOF_INSTANCE (book));
1152  auto slot = frame->get_slot({GNC_FEATURES});
1153  if (slot != nullptr)
1154  {
1155  frame = slot->get<KvpFrame*>();
1156  std::for_each (frame->begin (), frame->end (), test_feature);
1157  }
1158  return rv;
1159 }
1160 
1161 bool
1162 qof_book_test_feature (QofBook *book, const char *feature)
1163 {
1164  auto frame = qof_instance_get_slots (QOF_INSTANCE (book));
1165  return (frame->get_slot({GNC_FEATURES, feature}) != nullptr);
1166 }
1167 
1168 void
1169 qof_book_unset_feature (QofBook *book, const gchar *key)
1170 {
1171  KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
1172  auto feature_slot = frame->get_slot({GNC_FEATURES, key});
1173  if (!feature_slot)
1174  {
1175  PWARN ("no feature %s. bail out.", key);
1176  return;
1177  }
1178  qof_book_begin_edit (book);
1179  delete frame->set_path({GNC_FEATURES, key}, nullptr);
1180  qof_instance_set_dirty (QOF_INSTANCE (book));
1181  qof_book_commit_edit (book);
1182 }
1183 
1184 void
1185 qof_book_load_options (QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
1186 {
1187  load_cb (odb, book);
1188 }
1189 
1190 void
1191 qof_book_save_options (QofBook *book, GncOptionSave save_cb,
1192  GncOptionDB* odb, gboolean clear)
1193 {
1194  /* Wrap this in begin/commit so that it commits only once instead of doing
1195  * so for every option. Qof_book_set_option will take care of dirtying the
1196  * book.
1197  */
1198  qof_book_begin_edit (book);
1199  save_cb (odb, book, clear);
1200  qof_book_commit_edit (book);
1201 }
1202 
1203 static void noop (QofInstance *inst) {}
1204 
1205 void
1206 qof_book_commit_edit(QofBook *book)
1207 {
1208  if (!qof_commit_edit (QOF_INSTANCE(book))) return;
1209  qof_commit_edit_part2 (&book->inst, commit_err, noop, noop/*lot_free*/);
1210 }
1211 
1212 /* Deal with the fact that some options are not in the "options" tree but rather
1213  * in the "counters" or "counter_formats" tree */
1214 static Path gslist_to_option_path (GSList *gspath)
1215 {
1216  Path tmp_path;
1217  if (!gspath) return tmp_path;
1218 
1219  Path path_v {str_KVP_OPTION_PATH};
1220  for (auto item = gspath; item != nullptr; item = g_slist_next(item))
1221  tmp_path.push_back(static_cast<const char*>(item->data));
1222  if ((tmp_path.front() == "counters") || (tmp_path.front() == "counter_formats"))
1223  return tmp_path;
1224 
1225  path_v.insert(path_v.end(), tmp_path.begin(), tmp_path.end());
1226  return path_v;
1227 }
1228 
1229 void
1230 qof_book_set_option (QofBook *book, KvpValue *value, GSList *path)
1231 {
1232  KvpFrame *root = qof_instance_get_slots (QOF_INSTANCE (book));
1233  qof_book_begin_edit (book);
1234  delete root->set_path(gslist_to_option_path(path), value);
1235  qof_instance_set_dirty (QOF_INSTANCE (book));
1236  qof_book_commit_edit (book);
1237 
1238  // Also, mark any cached value as invalid
1239  book->cached_num_field_source_isvalid = FALSE;
1240 }
1241 
1242 KvpValue*
1243 qof_book_get_option (QofBook *book, GSList *path)
1244 {
1245  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1246  return root->get_slot(gslist_to_option_path(path));
1247 }
1248 
1249 void
1250 qof_book_options_delete (QofBook *book, GSList *path)
1251 {
1252  KvpFrame *root = qof_instance_get_slots(QOF_INSTANCE (book));
1253  if (path != nullptr)
1254  {
1255  Path path_v {str_KVP_OPTION_PATH};
1256  Path tmp_path;
1257  for (auto item = path; item != nullptr; item = g_slist_next(item))
1258  tmp_path.push_back(static_cast<const char*>(item->data));
1259  delete root->set_path(gslist_to_option_path(path), nullptr);
1260  }
1261  else
1262  delete root->set_path({str_KVP_OPTION_PATH}, nullptr);
1263 }
1264 
1265 /* QofObject function implementation and registration */
1266 gboolean qof_book_register (void)
1267 {
1268  static QofParam params[] =
1269  {
1270  { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_entity_get_guid, NULL },
1271  { QOF_PARAM_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL },
1272  { NULL },
1273  };
1274 
1275  qof_class_register (QOF_ID_BOOK, NULL, params);
1276 
1277  return TRUE;
1278 }
1279 
1280 /* ========================== END OF FILE =============================== */
Holds all of the options for a book, report, or stylesheet, organized by GncOptionSections.
API for data storage Backend.
void qof_book_load_options(QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
Load a GncOptionsDB from KVP data.
Definition: qofbook.cpp:1185
void qof_instance_get(const QofInstance *inst, const gchar *first_prop,...)
Wrapper for g_object_get.
void qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
Set the function to call when a book transitions from clean to dirty, or vice versa.
Definition: qofbook.cpp:438
KvpValue * qof_book_get_option(QofBook *book, GSList *path)
Read a single option value.
Definition: qofbook.cpp:1243
void(* QofCollectionForeachCB)(QofCollection *, gpointer user_data)
Invoke the indicated callback on each collection in the book.
Definition: qofbook.h:238
gboolean qof_book_register(void)
Register the book object with the QOF object system.
Definition: qofbook.cpp:1266
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void qof_object_book_begin(QofBook *book)
To be called from within the book.
Definition: qofobject.cpp:92
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
time64 qof_book_get_session_dirty_time(const QofBook *book)
Retrieve the earliest modification time on the book.
Definition: qofbook.cpp:432
gchar * qof_book_increment_and_format_counter(QofBook *book, const char *counter_name)
This will increment the named counter for this book and format it.
Definition: qofbook.cpp:631
gint qof_book_get_num_days_autoreadonly(const QofBook *book)
Returns the number of days for auto-read-only transactions.
Definition: qofbook.cpp:974
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
Returns TRUE if this book uses split action field as the &#39;Num&#39; field, FALSE if it uses transaction nu...
Definition: qofbook.cpp:927
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_function, const QofParam *params)
This function registers a new object class with the Qof subsystem.
Definition: qofclass.cpp:86
QofBook * qof_book_new(void)
Allocate, initialise and return a new QofBook.
Definition: qofbook.cpp:313
void qof_book_mark_closed(QofBook *book)
Close a book to editing.
Definition: qofbook.cpp:581
char * qof_book_get_counter_format(const QofBook *book, const char *counter_name)
Get the format string to use for the named counter.
Definition: qofbook.cpp:692
void qof_book_mark_readonly(QofBook *book)
Mark the book as read only.
Definition: qofbook.cpp:515
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GHashTable * qof_book_get_features(QofBook *book)
Access functions for reading and setting the used-features on this book.
Definition: qofbook.cpp:1105
const char * qof_string_cache_insert(const char *key)
You can use this function with g_hash_table_insert(), for the key (or value), as long as you use the ...
the Core Object Registration/Lookup Private Interface
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gint64 qof_book_get_counter(QofBook *book, const char *counter_name)
This will get the named counter for this book.
Definition: qofbook.cpp:591
gboolean qof_book_empty(const QofBook *book)
Check if the book has had anything loaded into it.
Definition: qofbook.cpp:522
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:85
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
void qof_book_save_options(QofBook *book, GncOptionSave save_cb, GncOptionDB *odb, gboolean clear)
Save a GncOptionsDB back to the book&#39;s KVP.
Definition: qofbook.cpp:1191
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
Returns the GDate that is the threshold for auto-read-only.
Definition: qofbook.cpp:994
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database)...
Definition: qofbook.cpp:395
void qof_book_set_data_fin(QofBook *book, const gchar *key, gpointer data, QofBookFinalCB)
Same as qof_book_set_data(), except that the callback will be called when the book is destroyed...
void qof_book_set_data(QofBook *book, const gchar *key, gpointer data)
The qof_book_set_data() allows arbitrary pointers to structs to be stored in QofBook.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:177
gchar * qof_book_normalize_counter_format(const gchar *p, gchar **err_msg)
Validate a counter format string.
Definition: qofbook.cpp:747
gchar * qof_book_normalize_counter_format_internal(const gchar *p, const gchar *gint64_format, gchar **err_msg)
Validate a counter format string with a given format specifier.
Definition: qofbook.cpp:779
gboolean qof_book_session_not_saved(const QofBook *book)
qof_book_not_saved() returns the value of the session_dirty flag, set when changes to any object in t...
Definition: qofbook.cpp:387
const GncGUID * qof_entity_get_guid(gconstpointer ent)
#define QOF_PARAM_KVP
"Known" Object Parameters – some objects might support these
Definition: qofquery.h:113
void qof_collection_destroy(QofCollection *col)
destroy the collection
Definition: qofid.cpp:62
void qof_book_mark_session_dirty(QofBook *book)
The qof_book_mark_dirty() routine marks the book as having been modified.
Definition: qofbook.cpp:409
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:508
This is the private header for the account structure.
void qof_book_print_dirty(const QofBook *book)
This debugging function can be used to traverse the book structure and all subsidiary structures...
Definition: qofbook.cpp:423
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.cpp:273
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:532
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
gpointer qof_collection_get_data(const QofCollection *col)
Store and retrieve arbitrary object-defined data.
Definition: qofid.cpp:291
void qof_book_set_option(QofBook *book, KvpValue *value, GSList *path)
Save a single option value.
Definition: qofbook.cpp:1230
gboolean qof_book_uses_autoreadonly(const QofBook *book)
Returns TRUE if the auto-read-only feature should be used, otherwise FALSE.
Definition: qofbook.cpp:968
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
Definition: qofbook.cpp:452
gboolean qof_book_shutting_down(const QofBook *book)
Is the book shutting down?
Definition: qofbook.cpp:459
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231
void qof_book_options_delete(QofBook *book, GSList *path)
Delete the options.
Definition: qofbook.cpp:1250
The type used to store guids in C.
Definition: guid.h:75
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
gboolean qof_book_use_trading_accounts(const QofBook *book)
Returns flag indicating whether this book uses trading accounts.
Definition: qofbook.cpp:915
void qof_string_cache_remove(const char *key)
You can use this function as a destroy notifier for a GHashTable that uses common strings as keys (or...
void qof_book_destroy(QofBook *book)
End any editing sessions associated with book, and free all memory associated with it...
Definition: qofbook.cpp:347
QofCollection * qof_collection_new(QofIdType type)
create a new collection of entities of type
Definition: qofid.cpp:51
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).
Definition: gnc-date.cpp:1229