1 /********************************************************************\
2  * qofinstance.c -- handler for fields common to all objects        *
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 /*
24  * Object instance holds many common fields that most
25  * gnucash objects use.
26  *
27  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
28  * Copyright (c) 2007 David Hampton <hampton@employees.org>
29  * Copyright 2017 Aaron Laws <dartme18@gmail.com>
30  */
31 
32 #include "guid.hpp"
33 extern "C"
34 {
35 #include <config.h>
36 #include <glib.h>
37 }
38 
39 #include <utility>
40 #include "qof.h"
41 #include "qofbook-p.h"
42 #include "qofid-p.h"
43 #include "kvp-frame.hpp"
44 #include "qofinstance-p.h"
45 #include "qof-backend.hpp"
46 
47 static QofLogModule log_module = QOF_MOD_ENGINE;
48 
49 /* ========================================================== */
50 
51 enum
52 {
53     LAST_SIGNAL
54 };
55 
56 enum
57 {
58     PROP_0,
59     PROP_TYPE,
60     PROP_GUID,
61     PROP_COLLECTION,
62     PROP_BOOK,
63     PROP_LAST_UPDATE,
64     PROP_EDITLEVEL,
65     PROP_DESTROYING,
66     PROP_DIRTY,
67     PROP_INFANT,
68 
69     PROP_VERSION,
70     PROP_VERSION_CHECK,
71     PROP_IDATA,
72 };
73 
74 typedef struct QofInstancePrivate
75 {
76 //    QofIdType        e_type;    /**<	Entity type */
77     GncGUID guid;                  /**< GncGUID for the entity */
78     QofCollection  *collection; /**< Entity collection */
79 
80     /* The entity_table in which this instance is stored */
81     QofBook * book;
82 
83     /*  Timestamp used to track the last modification to this
84      *  instance.  Typically used to compare two versions of the
85      *  same object, to see which is newer.  When used with the
86      *  SQL backend, this field is reserved for SQL use, to compare
87      *  the version in local memory to the remote, server version.
88      */
89     time64 last_update;
90 
91     /*  Keep track of nesting level of begin/end edit calls */
92     int editlevel;
93 
94     /*  In process of being destroyed */
95     gboolean do_free;
96 
97     /*  dirty/clean flag. If dirty, then this instance has been modified,
98      *  but has not yet been written out to storage (file/database)
99      */
100     gboolean dirty;
101 
102     /* True iff this instance has never been committed. */
103     gboolean infant;
104 
105     /* version number, used for tracking multiuser updates */
106     gint32 version;
107     guint32 version_check;  /* data aging timestamp */
108 
109     /* -------------------------------------------------------------- */
110     /* Backend private expansion data */
111     guint32  idata;   /* used by the sql backend for kvp management */
112 }  QofInstancePrivate;
113 
114 #define GET_PRIVATE(o)  \
115     ((QofInstancePrivate*)g_type_instance_get_private((GTypeInstance*)o, QOF_TYPE_INSTANCE))
116 
117 G_DEFINE_TYPE_WITH_PRIVATE(QofInstance, qof_instance, G_TYPE_OBJECT);
118 QOF_GOBJECT_FINALIZE(qof_instance);
119 #undef G_PARAM_READWRITE
120 #define G_PARAM_READWRITE static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_WRITABLE)
121 
122 static void qof_instance_get_property (GObject         *object,
123                                        guint            prop_id,
124                                        GValue          *value,
125                                        GParamSpec      *pspec);
126 static void qof_instance_set_property (GObject         *object,
127                                        guint            prop_id,
128                                        const GValue    *value,
129                                        GParamSpec      *pspec);
130 static void qof_instance_dispose(GObject*);
qof_instance_class_init(QofInstanceClass * klass)131 static void qof_instance_class_init(QofInstanceClass *klass)
132 {
133     GObjectClass *object_class = G_OBJECT_CLASS(klass);
134     object_class->finalize = qof_instance_finalize;
135     object_class->dispose = qof_instance_dispose;
136     object_class->set_property = qof_instance_set_property;
137     object_class->get_property = qof_instance_get_property;
138 
139     klass->get_display_name = NULL;
140     klass->refers_to_object = NULL;
141     klass->get_typed_referring_object_list = NULL;
142 
143     g_object_class_install_property
144     (object_class,
145      PROP_GUID,
146      g_param_spec_boxed ("guid",
147                          "Object GncGUID",
148                          "The object Globally Unique ID.",
149                          GNC_TYPE_GUID,
150                          G_PARAM_READWRITE));
151 
152     g_object_class_install_property
153     (object_class,
154      PROP_COLLECTION,
155      g_param_spec_pointer ("collection",
156                            "Object Collection",
157                            "A collection of like objects of which this "
158                            "particular object is amember.  E.g.. A "
159                            "collection of accounts, or a collection of "
160                            "splits.",
161                            G_PARAM_READWRITE));
162 
163     g_object_class_install_property
164     (object_class,
165      PROP_BOOK,
166      g_param_spec_object ("book",
167                           "Object Book",
168                           "The book that contains this object.",
169                           QOF_TYPE_BOOK,
170                           G_PARAM_READWRITE));
171 
172     g_object_class_install_property
173     (object_class,
174      PROP_LAST_UPDATE,
175      g_param_spec_pointer ("last-update",
176                            "Object Last Update",
177                            "A pointer to the last time this object was "
178                            "updated.  This value is present for use by "
179                            "backends and shouldnot be written by other "
180                            "code.",
181                            G_PARAM_READWRITE));
182 
183     g_object_class_install_property
184     (object_class,
185      PROP_EDITLEVEL,
186      g_param_spec_int ("editlevel",
187                        "Object Edit Level",
188                        "The object edit level.",
189                        0, G_MAXINT32, 0,
190                        G_PARAM_READABLE));
191 
192     g_object_class_install_property
193     (object_class,
194      PROP_DESTROYING,
195      g_param_spec_boolean ("destroying",
196                            "Object Destroying",
197                            "This flag is set to TRUE if the object is "
198                            "about to be destroyed.",
199                            FALSE,
200                            G_PARAM_READWRITE));
201 
202     g_object_class_install_property
203     (object_class,
204      PROP_DIRTY,
205      g_param_spec_boolean ("dirty",
206                            "Object Dirty",
207                            "This flag is set to TRUE if the object has "
208                            "unsaved changes.",
209                            FALSE,
210                            G_PARAM_READWRITE));
211 
212     g_object_class_install_property
213     (object_class,
214      PROP_INFANT,
215      g_param_spec_boolean ("infant",
216                            "Object Infant",
217                            "This flag is set to TRUE if the object has "
218                            "never been added to a book.  This implies "
219                            "that its destruction does not affect the "
220                            "state of the book, and therefore the saved "
221                            "state of the data file.",
222                            FALSE,
223                            G_PARAM_READABLE));
224 
225     g_object_class_install_property
226     (object_class,
227      PROP_VERSION,
228      g_param_spec_int ("version",
229                        "Version",
230                        "The version number of the current instance state.",
231                        0,
232                        G_MAXINT32,
233                        0,
234                        G_PARAM_READWRITE));
235 
236     g_object_class_install_property
237     (object_class,
238      PROP_VERSION_CHECK,
239      g_param_spec_uint ("version-check",
240                         "Version Check",
241                         "The version check number of the current instance state.",
242                         0,
243                         G_MAXUINT32,
244                         0,
245                         G_PARAM_READWRITE));
246 
247     g_object_class_install_property
248     (object_class,
249      PROP_EDITLEVEL,
250      g_param_spec_uint ("idata",
251                         "Object IData",
252                         "Per instance backend private data.",
253                         0, G_MAXUINT32, 0,
254                         G_PARAM_READWRITE));
255 }
256 
257 static void
qof_instance_init(QofInstance * inst)258 qof_instance_init (QofInstance *inst)
259 {
260     QofInstancePrivate *priv;
261 
262     priv = GET_PRIVATE(inst);
263     priv->book = NULL;
264     inst->kvp_data = new KvpFrame;
265     priv->last_update = 0;
266     priv->editlevel = 0;
267     priv->do_free = FALSE;
268     priv->dirty = FALSE;
269     priv->infant = TRUE;
270 }
271 
272 void
qof_instance_init_data(QofInstance * inst,QofIdType type,QofBook * book)273 qof_instance_init_data (QofInstance *inst, QofIdType type, QofBook *book)
274 {
275     QofInstancePrivate *priv;
276     QofCollection *col;
277     QofIdType col_type;
278 
279     g_return_if_fail(QOF_IS_INSTANCE(inst));
280     priv = GET_PRIVATE(inst);
281     g_return_if_fail(!priv->book);
282 
283     priv->book = book;
284     col = qof_book_get_collection (book, type);
285     g_return_if_fail(col != NULL);
286 
287     /* XXX We passed redundant info to this routine ... but I think that's
288      * OK, it might eliminate programming errors. */
289 
290     col_type = qof_collection_get_type(col);
291     if (g_strcmp0(col_type, type))
292     {
293         PERR ("attempt to insert \"%s\" into \"%s\"", type, col_type);
294         return;
295     }
296     priv = GET_PRIVATE(inst);
297     inst->e_type = static_cast<QofIdType>(CACHE_INSERT (type));
298 
299     do
300     {
301         guid_replace(&priv->guid);
302 
303         if (NULL == qof_collection_lookup_entity (col, &priv->guid))
304             break;
305 
306         PWARN("duplicate id created, trying again");
307     }
308     while (1);
309 
310     priv->collection = col;
311 
312     qof_collection_insert_entity (col, inst);
313 }
314 
315 static void
qof_instance_dispose(GObject * instp)316 qof_instance_dispose (GObject *instp)
317 {
318     QofInstancePrivate *priv;
319     QofInstance* inst = QOF_INSTANCE(instp);
320 
321     priv = GET_PRIVATE(instp);
322     if (priv->collection)
323         qof_collection_remove_entity(inst);
324 
325     CACHE_REMOVE(inst->e_type);
326     inst->e_type = NULL;
327 
328     G_OBJECT_CLASS(qof_instance_parent_class)->dispose(instp);
329 }
330 
331 static void
qof_instance_finalize_real(GObject * instp)332 qof_instance_finalize_real (GObject *instp)
333 {
334     QofInstancePrivate *priv;
335     QofInstance* inst = QOF_INSTANCE(instp);
336 
337     delete inst->kvp_data;
338     inst->kvp_data = nullptr;
339 
340     priv = GET_PRIVATE(inst);
341     priv->editlevel = 0;
342     priv->do_free = FALSE;
343     priv->dirty = FALSE;
344 }
345 
346 /* Note that g_value_set_object() refs the object, as does
347  * g_object_get(). But g_object_get() only unrefs once when it disgorges
348  * the object, leaving an unbalanced ref, which leaks. So instead of
349  * using g_value_set_object(), use g_value_take_object() which doesn't
350  * ref the object when used in get_property().
351  */
352 static void
qof_instance_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)353 qof_instance_get_property (GObject         *object,
354                            guint            prop_id,
355                            GValue          *value,
356                            GParamSpec      *pspec)
357 {
358     QofInstance *inst;
359     QofInstancePrivate *priv;
360 
361     g_return_if_fail(QOF_IS_INSTANCE(object));
362 
363     inst = QOF_INSTANCE(object);
364     priv = GET_PRIVATE(inst);
365 
366     switch (prop_id)
367     {
368     case PROP_GUID:
369         g_value_set_boxed(value, &priv->guid);
370         break;
371     case PROP_COLLECTION:
372         g_value_set_pointer(value, priv->collection);
373         break;
374     case PROP_BOOK:
375         g_value_take_object(value, priv->book);
376         break;
377     case PROP_LAST_UPDATE:
378         g_value_set_pointer(value, &priv->last_update);
379         break;
380     case PROP_EDITLEVEL:
381         g_value_set_int(value, priv->editlevel);
382         break;
383     case PROP_DESTROYING:
384         g_value_set_boolean(value, priv->do_free);
385         break;
386     case PROP_DIRTY:
387         g_value_set_boolean(value, qof_instance_get_dirty(inst));
388         break;
389     case PROP_INFANT:
390         g_value_set_boolean(value, priv->infant);
391         break;
392     case PROP_VERSION:
393         g_value_set_int(value, priv->version);
394         break;
395     case PROP_VERSION_CHECK:
396         g_value_set_uint(value, priv->version_check);
397         break;
398     case PROP_IDATA:
399         g_value_set_uint(value, priv->idata);
400         break;
401     default:
402         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
403         break;
404     }
405 }
406 
407 static void
qof_instance_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)408 qof_instance_set_property (GObject         *object,
409                            guint            prop_id,
410                            const GValue    *value,
411                            GParamSpec      *pspec)
412 {
413     QofInstance *inst;
414     Time64 t;
415 
416     g_return_if_fail(QOF_IS_INSTANCE(object));
417 
418     inst = QOF_INSTANCE(object);
419 
420     switch (prop_id)
421     {
422     case PROP_GUID:
423         qof_instance_set_guid(inst,
424 			      static_cast<GncGUID*>(g_value_get_boxed(value)));
425         break;
426     case PROP_COLLECTION:
427         qof_instance_set_collection(inst, static_cast<QofCollection*>(g_value_get_pointer(value)));
428         break;
429     case PROP_BOOK:
430         qof_instance_set_book(inst,
431 			      static_cast<QofBook*>(g_value_get_object(value)));
432         break;
433     case PROP_LAST_UPDATE:
434         t = *(static_cast<Time64*>(g_value_get_pointer(value)));
435         qof_instance_set_last_update(inst, t.t);
436         break;
437     case PROP_DESTROYING:
438         qof_instance_set_destroying(inst, g_value_get_boolean(value));
439         break;
440     case PROP_DIRTY:
441         qof_instance_set_dirty(inst);
442         break;
443     case PROP_VERSION:
444         qof_instance_set_version(inst, g_value_get_int(value));
445         break;
446     case PROP_VERSION_CHECK:
447         qof_instance_set_version_check(inst, g_value_get_uint(value));
448         break;
449     case PROP_IDATA:
450         qof_instance_set_idata(inst, g_value_get_uint(value));
451         break;
452     default:
453         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
454         break;
455     }
456 }
457 
458 const GncGUID *
qof_instance_get_guid(gconstpointer inst)459 qof_instance_get_guid (gconstpointer inst)
460 {
461     QofInstancePrivate *priv;
462 
463     if (!inst) return NULL;
464     g_return_val_if_fail(QOF_IS_INSTANCE(inst), guid_null());
465     priv = GET_PRIVATE(inst);
466     return &(priv->guid);
467 }
468 
469 const GncGUID *
qof_entity_get_guid(gconstpointer ent)470 qof_entity_get_guid (gconstpointer ent)
471 {
472     return ent ? qof_instance_get_guid(ent) : guid_null();
473 }
474 
475 void
qof_instance_set_guid(gpointer ptr,const GncGUID * guid)476 qof_instance_set_guid (gpointer ptr, const GncGUID *guid)
477 {
478     QofInstancePrivate *priv;
479     QofInstance *inst;
480     QofCollection *col;
481 
482     g_return_if_fail(QOF_IS_INSTANCE(ptr));
483 
484     inst = QOF_INSTANCE(ptr);
485     priv = GET_PRIVATE(inst);
486     if (guid_equal (guid, &priv->guid))
487         return;
488 
489     col = priv->collection;
490     qof_collection_remove_entity(inst);
491     priv->guid = *guid;
492     qof_collection_insert_entity(col, inst);
493 }
494 
495 void
qof_instance_copy_guid(gpointer to,gconstpointer from)496 qof_instance_copy_guid (gpointer to, gconstpointer from)
497 {
498     g_return_if_fail(QOF_IS_INSTANCE(to));
499     g_return_if_fail(QOF_IS_INSTANCE(from));
500 
501     GET_PRIVATE(to)->guid = GET_PRIVATE(from)->guid;
502 }
503 
504 gint
qof_instance_guid_compare(gconstpointer ptr1,gconstpointer ptr2)505 qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
506 {
507     const QofInstancePrivate *priv1, *priv2;
508 
509     g_return_val_if_fail(QOF_IS_INSTANCE(ptr1), -1);
510     g_return_val_if_fail(QOF_IS_INSTANCE(ptr2),  1);
511 
512     priv1 = GET_PRIVATE(ptr1);
513     priv2 = GET_PRIVATE(ptr2);
514 
515     return guid_compare(&priv1->guid, &priv2->guid);
516 }
517 
518 QofCollection *
qof_instance_get_collection(gconstpointer ptr)519 qof_instance_get_collection (gconstpointer ptr)
520 {
521 
522     g_return_val_if_fail(QOF_IS_INSTANCE(ptr), NULL);
523     return GET_PRIVATE(ptr)->collection;
524 }
525 
526 void
qof_instance_set_collection(gconstpointer ptr,QofCollection * col)527 qof_instance_set_collection (gconstpointer ptr, QofCollection *col)
528 {
529     g_return_if_fail(QOF_IS_INSTANCE(ptr));
530     GET_PRIVATE(ptr)->collection = col;
531 }
532 
533 QofBook *
qof_instance_get_book(gconstpointer inst)534 qof_instance_get_book (gconstpointer inst)
535 {
536     if (!inst) return NULL;
537     g_return_val_if_fail(QOF_IS_INSTANCE(inst), NULL);
538     return GET_PRIVATE(inst)->book;
539 }
540 
541 void
qof_instance_set_book(gconstpointer inst,QofBook * book)542 qof_instance_set_book (gconstpointer inst, QofBook *book)
543 {
544     g_return_if_fail(QOF_IS_INSTANCE(inst));
545     GET_PRIVATE(inst)->book = book;
546 }
547 
548 void
qof_instance_copy_book(gpointer ptr1,gconstpointer ptr2)549 qof_instance_copy_book (gpointer ptr1, gconstpointer ptr2)
550 {
551     g_return_if_fail(QOF_IS_INSTANCE(ptr1));
552     g_return_if_fail(QOF_IS_INSTANCE(ptr2));
553 
554     GET_PRIVATE(ptr1)->book = GET_PRIVATE(ptr2)->book;
555 }
556 
557 gboolean
qof_instance_books_equal(gconstpointer ptr1,gconstpointer ptr2)558 qof_instance_books_equal (gconstpointer ptr1, gconstpointer ptr2)
559 {
560     const QofInstancePrivate *priv1, *priv2;
561 
562     g_return_val_if_fail(QOF_IS_INSTANCE(ptr1), FALSE);
563     g_return_val_if_fail(QOF_IS_INSTANCE(ptr2), FALSE);
564 
565     priv1 = GET_PRIVATE(ptr1);
566     priv2 = GET_PRIVATE(ptr2);
567 
568     return (priv1->book == priv2->book);
569 }
570 
571 /* Watch out: This function is still used (as a "friend") in src/import-export/aqb/gnc-ab-kvp.c */
572 KvpFrame*
qof_instance_get_slots(const QofInstance * inst)573 qof_instance_get_slots (const QofInstance *inst)
574 {
575     if (!inst) return NULL;
576     return inst->kvp_data;
577 }
578 
579 void
qof_instance_set_slots(QofInstance * inst,KvpFrame * frm)580 qof_instance_set_slots (QofInstance *inst, KvpFrame *frm)
581 {
582     QofInstancePrivate *priv;
583 
584     if (!inst) return;
585 
586     priv = GET_PRIVATE(inst);
587     if (inst->kvp_data && (inst->kvp_data != frm))
588     {
589         delete inst->kvp_data;
590     }
591 
592     priv->dirty = TRUE;
593     inst->kvp_data = frm;
594 }
595 
596 void
qof_instance_set_last_update(QofInstance * inst,time64 t)597 qof_instance_set_last_update (QofInstance *inst, time64 t)
598 {
599     if (!inst) return;
600     GET_PRIVATE(inst)->last_update = t;
601 }
602 
603 gint
qof_instance_get_editlevel(gconstpointer ptr)604 qof_instance_get_editlevel (gconstpointer ptr)
605 {
606     g_return_val_if_fail(QOF_IS_INSTANCE(ptr), 0);
607     return GET_PRIVATE(ptr)->editlevel;
608 }
609 
qof_instance_increase_editlevel(gpointer ptr)610 void qof_instance_increase_editlevel (gpointer ptr)
611 {
612     g_return_if_fail(QOF_IS_INSTANCE(ptr));
613     GET_PRIVATE(ptr)->editlevel++;
614 }
615 
qof_instance_decrease_editlevel(gpointer ptr)616 void qof_instance_decrease_editlevel (gpointer ptr)
617 {
618     g_return_if_fail(QOF_IS_INSTANCE(ptr));
619     GET_PRIVATE(ptr)->editlevel--;
620 }
621 
qof_instance_reset_editlevel(gpointer ptr)622 void qof_instance_reset_editlevel (gpointer ptr)
623 {
624     g_return_if_fail(QOF_IS_INSTANCE(ptr));
625     GET_PRIVATE(ptr)->editlevel = 0;
626 }
627 
628 int
qof_instance_version_cmp(const QofInstance * left,const QofInstance * right)629 qof_instance_version_cmp (const QofInstance *left, const QofInstance *right)
630 {
631     QofInstancePrivate *lpriv, *rpriv;
632 
633     if (!left && !right) return 0;
634     if (!left) return -1;
635     if (!right) return +1;
636 
637     lpriv = GET_PRIVATE(left);
638     rpriv = GET_PRIVATE(right);
639     return lpriv->last_update < rpriv->last_update ? -1 :
640         lpriv->last_update > rpriv->last_update ? 1 : 0;
641 }
642 
643 gboolean
qof_instance_get_destroying(gconstpointer ptr)644 qof_instance_get_destroying (gconstpointer ptr)
645 {
646     g_return_val_if_fail(QOF_IS_INSTANCE(ptr), FALSE);
647     return GET_PRIVATE(ptr)->do_free;
648 }
649 
650 void
qof_instance_set_destroying(gpointer ptr,gboolean value)651 qof_instance_set_destroying (gpointer ptr, gboolean value)
652 {
653     g_return_if_fail(QOF_IS_INSTANCE(ptr));
654     GET_PRIVATE(ptr)->do_free = value;
655 }
656 
657 gboolean
qof_instance_get_dirty_flag(gconstpointer ptr)658 qof_instance_get_dirty_flag (gconstpointer ptr)
659 {
660     g_return_val_if_fail(QOF_IS_INSTANCE(ptr), FALSE);
661     return GET_PRIVATE(ptr)->dirty;
662 }
663 
664 void
qof_instance_set_dirty_flag(gconstpointer inst,gboolean flag)665 qof_instance_set_dirty_flag (gconstpointer inst, gboolean flag)
666 {
667     g_return_if_fail(QOF_IS_INSTANCE(inst));
668     GET_PRIVATE(inst)->dirty = flag;
669 }
670 
671 void
qof_instance_mark_clean(QofInstance * inst)672 qof_instance_mark_clean (QofInstance *inst)
673 {
674     if (!inst) return;
675     GET_PRIVATE(inst)->dirty = FALSE;
676 }
677 
678 void
qof_instance_print_dirty(const QofInstance * inst,gpointer dummy)679 qof_instance_print_dirty (const QofInstance *inst, gpointer dummy)
680 {
681     QofInstancePrivate *priv;
682 
683     priv = GET_PRIVATE(inst);
684     if (priv->dirty)
685     {
686         gchar guidstr[GUID_ENCODING_LENGTH+1];
687         guid_to_string_buff(&priv->guid, guidstr);
688         printf("%s instance %s is dirty.\n", inst->e_type, guidstr);
689     }
690 }
691 
692 gboolean
qof_instance_get_dirty(QofInstance * inst)693 qof_instance_get_dirty (QofInstance *inst)
694 {
695     QofInstancePrivate *priv;
696     QofCollection *coll;
697 
698     if (!inst)
699     {
700         return FALSE;
701     }
702 
703     priv = GET_PRIVATE(inst);
704     return priv->dirty;
705 }
706 
707 void
qof_instance_set_dirty(QofInstance * inst)708 qof_instance_set_dirty(QofInstance* inst)
709 {
710     QofInstancePrivate *priv;
711     QofCollection *coll;
712 
713     priv = GET_PRIVATE(inst);
714     priv->dirty = TRUE;
715 }
716 
717 gboolean
qof_instance_get_infant(const QofInstance * inst)718 qof_instance_get_infant(const QofInstance *inst)
719 {
720     g_return_val_if_fail(QOF_IS_INSTANCE(inst), FALSE);
721     return GET_PRIVATE(inst)->infant;
722 }
723 
724 gint32
qof_instance_get_version(gconstpointer inst)725 qof_instance_get_version (gconstpointer inst)
726 {
727     g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
728     return GET_PRIVATE(inst)->version;
729 }
730 
731 void
qof_instance_set_version(gpointer inst,gint32 vers)732 qof_instance_set_version (gpointer inst, gint32 vers)
733 {
734     g_return_if_fail(QOF_IS_INSTANCE(inst));
735     GET_PRIVATE(inst)->version = vers;
736 }
737 
738 void
qof_instance_copy_version(gpointer to,gconstpointer from)739 qof_instance_copy_version (gpointer to, gconstpointer from)
740 {
741     g_return_if_fail(QOF_IS_INSTANCE(to));
742     g_return_if_fail(QOF_IS_INSTANCE(from));
743     GET_PRIVATE(to)->version = GET_PRIVATE(from)->version;
744 }
745 
746 guint32
qof_instance_get_version_check(gconstpointer inst)747 qof_instance_get_version_check (gconstpointer inst)
748 {
749     g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
750     return GET_PRIVATE(inst)->version_check;
751 }
752 
753 void
qof_instance_set_version_check(gpointer inst,guint32 value)754 qof_instance_set_version_check (gpointer inst, guint32 value)
755 {
756     g_return_if_fail(QOF_IS_INSTANCE(inst));
757     GET_PRIVATE(inst)->version_check = value;
758 }
759 
760 void
qof_instance_copy_version_check(gpointer to,gconstpointer from)761 qof_instance_copy_version_check (gpointer to, gconstpointer from)
762 {
763     g_return_if_fail(QOF_IS_INSTANCE(to));
764     g_return_if_fail(QOF_IS_INSTANCE(from));
765     GET_PRIVATE(to)->version_check = GET_PRIVATE(from)->version_check;
766 }
767 
qof_instance_get_idata(gconstpointer inst)768 guint32 qof_instance_get_idata (gconstpointer inst)
769 {
770     if (!inst)
771     {
772         return 0;
773     }
774     g_return_val_if_fail(QOF_IS_INSTANCE(inst), 0);
775     return GET_PRIVATE(inst)->idata;
776 }
777 
qof_instance_set_idata(gpointer inst,guint32 idata)778 void qof_instance_set_idata(gpointer inst, guint32 idata)
779 {
780     if (!inst)
781     {
782         return;
783     }
784     g_return_if_fail(QOF_IS_INSTANCE(inst));
785     GET_PRIVATE(inst)->idata = idata;
786 }
787 
788 /* ========================================================== */
789 
790 /* Returns a displayable name to represent this object */
qof_instance_get_display_name(const QofInstance * inst)791 gchar* qof_instance_get_display_name(const QofInstance* inst)
792 {
793     g_return_val_if_fail( inst != NULL, NULL );
794 
795     if ( QOF_INSTANCE_GET_CLASS(inst)->get_display_name != NULL )
796     {
797         return QOF_INSTANCE_GET_CLASS(inst)->get_display_name(inst);
798     }
799     else
800     {
801         /* Not implemented - return default string */
802         return g_strdup_printf("Object %s %p",
803                                qof_collection_get_type(qof_instance_get_collection(inst)),
804                                inst);
805     }
806 }
807 
808 typedef struct
809 {
810     const QofInstance* inst;
811     GList* list;
812 } GetReferringObjectHelperData;
813 
814 static void
get_referring_object_instance_helper(QofInstance * inst,gpointer user_data)815 get_referring_object_instance_helper(QofInstance* inst, gpointer user_data)
816 {
817     QofInstance** pInst = (QofInstance**)user_data;
818 
819     if (*pInst == NULL)
820     {
821         *pInst = inst;
822     }
823 }
824 
825 static void
get_referring_object_helper(QofCollection * coll,gpointer user_data)826 get_referring_object_helper(QofCollection* coll, gpointer user_data)
827 {
828     QofInstance* first_instance = NULL;
829     GetReferringObjectHelperData* data = (GetReferringObjectHelperData*)user_data;
830 
831     qof_collection_foreach(coll, get_referring_object_instance_helper, &first_instance);
832 
833     if (first_instance != NULL)
834     {
835         GList* new_list = qof_instance_get_typed_referring_object_list(first_instance, data->inst);
836         data->list = g_list_concat(data->list, new_list);
837     }
838 }
839 
840 /* Returns a list of objects referring to this object */
qof_instance_get_referring_object_list(const QofInstance * inst)841 GList* qof_instance_get_referring_object_list(const QofInstance* inst)
842 {
843     GetReferringObjectHelperData data;
844 
845     g_return_val_if_fail( inst != NULL, NULL );
846 
847     /* scan all collections */
848     data.inst = inst;
849     data.list = NULL;
850 
851     qof_book_foreach_collection(qof_instance_get_book(inst),
852                                 get_referring_object_helper,
853                                 &data);
854     return data.list;
855 }
856 
857 static void
get_typed_referring_object_instance_helper(QofInstance * inst,gpointer user_data)858 get_typed_referring_object_instance_helper(QofInstance* inst, gpointer user_data)
859 {
860     GetReferringObjectHelperData* data = (GetReferringObjectHelperData*)user_data;
861 
862     if (qof_instance_refers_to_object(inst, data->inst))
863     {
864         data->list = g_list_prepend(data->list, inst);
865     }
866 }
867 
868 GList*
qof_instance_get_referring_object_list_from_collection(const QofCollection * coll,const QofInstance * ref)869 qof_instance_get_referring_object_list_from_collection(const QofCollection* coll, const QofInstance* ref)
870 {
871     GetReferringObjectHelperData data;
872 
873     g_return_val_if_fail( coll != NULL, NULL );
874     g_return_val_if_fail( ref != NULL, NULL );
875 
876     data.inst = ref;
877     data.list = NULL;
878 
879     qof_collection_foreach(coll, get_typed_referring_object_instance_helper, &data);
880     return data.list;
881 }
882 
883 GList*
qof_instance_get_typed_referring_object_list(const QofInstance * inst,const QofInstance * ref)884 qof_instance_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
885 {
886     g_return_val_if_fail( inst != NULL, NULL );
887     g_return_val_if_fail( ref != NULL, NULL );
888 
889     if ( QOF_INSTANCE_GET_CLASS(inst)->get_typed_referring_object_list != NULL )
890     {
891         return QOF_INSTANCE_GET_CLASS(inst)->get_typed_referring_object_list(inst, ref);
892     }
893     else
894     {
895         /* Not implemented - by default, loop through all objects of this object's type and check
896            them individually. */
897         QofCollection* coll;
898 
899         coll = qof_instance_get_collection(inst);
900         return qof_instance_get_referring_object_list_from_collection(coll, ref);
901     }
902 }
903 
904 /* Check if this object refers to a specific object */
qof_instance_refers_to_object(const QofInstance * inst,const QofInstance * ref)905 gboolean qof_instance_refers_to_object(const QofInstance* inst, const QofInstance* ref)
906 {
907     g_return_val_if_fail( inst != NULL, FALSE );
908     g_return_val_if_fail( ref != NULL, FALSE );
909 
910     if ( QOF_INSTANCE_GET_CLASS(inst)->refers_to_object != NULL )
911     {
912         return QOF_INSTANCE_GET_CLASS(inst)->refers_to_object(inst, ref);
913     }
914     else
915     {
916         /* Not implemented - default = NO */
917         return FALSE;
918     }
919 }
920 
921 /* g_object_set/get wrappers */
922 void
qof_instance_get(const QofInstance * inst,const gchar * first_prop,...)923 qof_instance_get (const QofInstance *inst, const gchar *first_prop, ...)
924 {
925     va_list ap;
926     g_return_if_fail (QOF_IS_INSTANCE (inst));
927 
928     va_start (ap, first_prop);
929     g_object_get_valist (G_OBJECT (inst), first_prop, ap);
930     va_end (ap);
931 }
932 
933 void
qof_instance_set(QofInstance * inst,const gchar * first_prop,...)934 qof_instance_set (QofInstance *inst, const gchar *first_prop, ...)
935 {
936     va_list ap;
937     g_return_if_fail (QOF_IS_INSTANCE (inst));
938 
939     qof_instance_set_dirty (inst);
940     va_start (ap, first_prop);
941     g_object_set_valist (G_OBJECT (inst), first_prop, ap);
942     va_end (ap);
943 }
944 
945 
946 /* =================================================================== */
947 /* Entity edit and commit utilities */
948 /* =================================================================== */
949 
950 gboolean
qof_begin_edit(QofInstance * inst)951 qof_begin_edit (QofInstance *inst)
952 {
953     QofInstancePrivate *priv;
954 
955     if (!inst) return FALSE;
956 
957     priv = GET_PRIVATE(inst);
958     priv->editlevel++;
959     if (1 < priv->editlevel) return FALSE;
960     if (0 >= priv->editlevel)
961         priv->editlevel = 1;
962 
963     auto be = qof_book_get_backend(priv->book);
964     if (be)
965         be->begin(inst);
966     else
967         priv->dirty = TRUE;
968 
969     return TRUE;
970 }
971 
qof_commit_edit(QofInstance * inst)972 gboolean qof_commit_edit (QofInstance *inst)
973 {
974     QofInstancePrivate *priv;
975 
976     if (!inst) return FALSE;
977 
978     priv = GET_PRIVATE(inst);
979     priv->editlevel--;
980     if (0 < priv->editlevel) return FALSE;
981 
982     if (0 > priv->editlevel)
983     {
984         PERR ("unbalanced call - resetting (was %d)", priv->editlevel);
985         priv->editlevel = 0;
986     }
987     return TRUE;
988 }
989 
990 gboolean
qof_commit_edit_part2(QofInstance * inst,void (* on_error)(QofInstance *,QofBackendError),void (* on_done)(QofInstance *),void (* on_free)(QofInstance *))991 qof_commit_edit_part2(QofInstance *inst,
992                       void (*on_error)(QofInstance *, QofBackendError),
993                       void (*on_done)(QofInstance *),
994                       void (*on_free)(QofInstance *))
995 {
996     QofInstancePrivate *priv;
997 
998     priv = GET_PRIVATE(inst);
999 
1000     if (priv->dirty &&
1001         !(priv->infant && priv->do_free)) {
1002       qof_collection_mark_dirty(priv->collection);
1003       qof_book_mark_session_dirty(priv->book);
1004     }
1005 
1006     /* See if there's a backend.  If there is, invoke it. */
1007     auto be = qof_book_get_backend(priv->book);
1008     if (be)
1009     {
1010         QofBackendError errcode;
1011 
1012         /* clear errors */
1013         do
1014         {
1015             errcode = be->get_error();
1016         }
1017         while (errcode != ERR_BACKEND_NO_ERR);
1018 
1019         be->commit(inst);
1020         errcode = be->get_error();
1021         if (errcode != ERR_BACKEND_NO_ERR)
1022         {
1023             /* XXX Should perform a rollback here */
1024             priv->do_free = FALSE;
1025 
1026             /* Push error back onto the stack */
1027             be->set_error (errcode);
1028             if (on_error)
1029                 on_error(inst, errcode);
1030             return FALSE;
1031         }
1032         if (!priv->dirty) //Cleared if the save was successful
1033             priv->infant = FALSE;
1034     }
1035 
1036     if (priv->do_free)
1037     {
1038         if (on_free)
1039             on_free(inst);
1040         return TRUE;
1041     }
1042 
1043     if (on_done)
1044         on_done(inst);
1045     return TRUE;
1046 }
1047 
1048 gboolean
qof_instance_has_kvp(QofInstance * inst)1049 qof_instance_has_kvp (QofInstance *inst)
1050 {
1051     return (inst->kvp_data != NULL && !inst->kvp_data->empty());
1052 }
1053 
qof_instance_set_path_kvp(QofInstance * inst,GValue const * value,std::vector<std::string> const & path)1054 void qof_instance_set_path_kvp (QofInstance * inst, GValue const * value, std::vector<std::string> const & path)
1055 {
1056     delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
1057 }
1058 
1059 void
qof_instance_set_kvp(QofInstance * inst,GValue const * value,unsigned count,...)1060 qof_instance_set_kvp (QofInstance * inst, GValue const * value, unsigned count, ...)
1061 {
1062     std::vector<std::string> path;
1063     va_list args;
1064     va_start (args, count);
1065     for (unsigned i{0}; i < count; ++i)
1066         path.push_back (va_arg (args, char const *));
1067     va_end (args);
1068     delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
1069 }
1070 
qof_instance_get_path_kvp(QofInstance * inst,GValue * value,std::vector<std::string> const & path)1071 void qof_instance_get_path_kvp (QofInstance * inst, GValue * value, std::vector<std::string> const & path)
1072 {
1073     auto temp = gvalue_from_kvp_value (inst->kvp_data->get_slot (path));
1074     if (G_IS_VALUE (temp))
1075     {
1076         if (G_IS_VALUE (value))
1077             g_value_unset (value);
1078         g_value_init (value, G_VALUE_TYPE (temp));
1079         g_value_copy (temp, value);
1080         gnc_gvalue_free (temp);
1081     }
1082 }
1083 
1084 void
qof_instance_get_kvp(QofInstance * inst,GValue * value,unsigned count,...)1085 qof_instance_get_kvp (QofInstance * inst, GValue * value, unsigned count, ...)
1086 {
1087     std::vector<std::string> path;
1088     va_list args;
1089     va_start (args, count);
1090     for (unsigned i{0}; i < count; ++i)
1091         path.push_back (va_arg (args, char const *));
1092     va_end (args);
1093     auto temp = gvalue_from_kvp_value (inst->kvp_data->get_slot (path));
1094     if (G_IS_VALUE (temp))
1095     {
1096         if (G_IS_VALUE (value))
1097             g_value_unset (value);
1098         g_value_init (value, G_VALUE_TYPE (temp));
1099         g_value_copy (temp, value);
1100         gnc_gvalue_free (temp);
1101     }
1102 }
1103 
1104 void
qof_instance_copy_kvp(QofInstance * to,const QofInstance * from)1105 qof_instance_copy_kvp (QofInstance *to, const QofInstance *from)
1106 {
1107     delete to->kvp_data;
1108     to->kvp_data = new KvpFrame(*from->kvp_data);
1109 }
1110 
1111 void
qof_instance_swap_kvp(QofInstance * a,QofInstance * b)1112 qof_instance_swap_kvp (QofInstance *a, QofInstance *b)
1113 {
1114     std::swap(a->kvp_data, b->kvp_data);
1115 }
1116 
1117 int
qof_instance_compare_kvp(const QofInstance * a,const QofInstance * b)1118 qof_instance_compare_kvp (const QofInstance *a, const QofInstance *b)
1119 {
1120     return compare(a->kvp_data, b->kvp_data);
1121 }
1122 
1123 char*
qof_instance_kvp_as_string(const QofInstance * inst)1124 qof_instance_kvp_as_string (const QofInstance *inst)
1125 {
1126     //The std::string is a local temporary and doesn't survive this function.
1127     return g_strdup(inst->kvp_data->to_string().c_str());
1128 }
1129 
1130 void
qof_instance_kvp_add_guid(const QofInstance * inst,const char * path,time64 time,const char * key,const GncGUID * guid)1131 qof_instance_kvp_add_guid (const QofInstance *inst, const char* path,
1132                            time64 time, const char *key,
1133                            const GncGUID *guid)
1134 {
1135     g_return_if_fail (inst->kvp_data != NULL);
1136 
1137     auto container = new KvpFrame;
1138     Time64 t{time};
1139     container->set({key}, new KvpValue(const_cast<GncGUID*>(guid)));
1140     container->set({"date"}, new KvpValue(t));
1141     delete inst->kvp_data->set_path({path}, new KvpValue(container));
1142 }
1143 
1144 inline static gboolean
kvp_match_guid(KvpValue * v,std::vector<std::string> const & path,const GncGUID * guid)1145 kvp_match_guid (KvpValue *v, std::vector<std::string> const & path, const GncGUID *guid)
1146 {
1147     if (v->get_type() != KvpValue::Type::FRAME)
1148         return FALSE;
1149     auto frame = v->get<KvpFrame*>();
1150     auto val = frame->get_slot(path);
1151     if (val == nullptr || val->get_type() != KvpValue::Type::GUID)
1152         return FALSE;
1153     auto this_guid = val->get<GncGUID*>();
1154 
1155     return guid_equal (this_guid, guid);
1156 }
1157 
1158 gboolean
qof_instance_kvp_has_guid(const QofInstance * inst,const char * path,const char * key,const GncGUID * guid)1159 qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
1160                            const char* key, const GncGUID *guid)
1161 {
1162     g_return_val_if_fail (inst->kvp_data != NULL, FALSE);
1163     g_return_val_if_fail (guid != NULL, FALSE);
1164 
1165     auto v = inst->kvp_data->get_slot({path});
1166     if (v == nullptr) return FALSE;
1167 
1168     switch (v->get_type())
1169     {
1170     case KvpValue::Type::FRAME:
1171         return kvp_match_guid (v, {key}, guid);
1172         break;
1173     case KvpValue::Type::GLIST:
1174     {
1175         auto list = v->get<GList*>();
1176         for (auto node = list; node != NULL; node = node->next)
1177         {
1178             auto val = static_cast<KvpValue*>(node->data);
1179             if (kvp_match_guid (val, {key}, guid))
1180             {
1181                 return TRUE;
1182             }
1183         }
1184         break;
1185     }
1186     default:
1187         PWARN ("Instance KVP on path %s contains the wrong type.", path);
1188         break;
1189     }
1190     return FALSE;
1191 }
1192 
1193 void
qof_instance_kvp_remove_guid(const QofInstance * inst,const char * path,const char * key,const GncGUID * guid)1194 qof_instance_kvp_remove_guid (const QofInstance *inst, const char *path,
1195                           const char *key, const GncGUID *guid)
1196 {
1197     g_return_if_fail (inst->kvp_data != NULL);
1198     g_return_if_fail (guid != NULL);
1199 
1200     auto v = inst->kvp_data->get_slot({path});
1201     if (v == NULL) return;
1202 
1203     switch (v->get_type())
1204     {
1205     case KvpValue::Type::FRAME:
1206         if (kvp_match_guid (v, {key}, guid))
1207         {
1208             delete inst->kvp_data->set_path({path}, nullptr);
1209             delete v;
1210         }
1211         break;
1212     case KvpValue::Type::GLIST:
1213     {
1214         auto list = v->get<GList*>();
1215         for (auto node = list; node != nullptr; node = node->next)
1216         {
1217             auto val = static_cast<KvpValue*>(node->data);
1218             if (kvp_match_guid (val, {key}, guid))
1219             {
1220                 list = g_list_delete_link (list, node);
1221                 v->set(list);
1222                 delete val;
1223                 break;
1224             }
1225         }
1226         break;
1227     }
1228     default:
1229         PWARN ("Instance KVP on path %s contains the wrong type.", path);
1230         break;
1231     }
1232     return;
1233 }
1234 
1235 void
qof_instance_kvp_merge_guids(const QofInstance * target,const QofInstance * donor,const char * path)1236 qof_instance_kvp_merge_guids (const QofInstance *target,
1237                               const QofInstance *donor, const char *path)
1238 {
1239     g_return_if_fail (target != NULL);
1240     g_return_if_fail (donor != NULL);
1241 
1242     if (! qof_instance_has_slot (donor, path)) return;
1243     auto v = donor->kvp_data->get_slot({path});
1244     if (v == NULL) return;
1245 
1246     auto target_val = target->kvp_data->get_slot({path});
1247     switch (v->get_type())
1248     {
1249     case KvpValue::Type::FRAME:
1250         if (target_val)
1251             target_val->add(v);
1252         else
1253             target->kvp_data->set_path({path}, v);
1254         donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
1255         break;
1256     case KvpValue::Type::GLIST:
1257         if (target_val)
1258         {
1259             auto list = target_val->get<GList*>();
1260             list = g_list_concat(list, v->get<GList*>());
1261             target_val->set(list);
1262         }
1263         else
1264             target->kvp_data->set({path}, v);
1265         donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
1266         break;
1267     default:
1268         PWARN ("Instance KVP on path %s contains the wrong type.", path);
1269         break;
1270     }
1271 }
1272 
qof_instance_has_path_slot(QofInstance const * inst,std::vector<std::string> const & path)1273 bool qof_instance_has_path_slot (QofInstance const * inst, std::vector<std::string> const & path)
1274 {
1275     return inst->kvp_data->get_slot (path) != nullptr;
1276 }
1277 
1278 gboolean
qof_instance_has_slot(const QofInstance * inst,const char * path)1279 qof_instance_has_slot (const QofInstance *inst, const char *path)
1280 {
1281     return inst->kvp_data->get_slot({path}) != NULL;
1282 }
1283 
qof_instance_slot_path_delete(QofInstance const * inst,std::vector<std::string> const & path)1284 void qof_instance_slot_path_delete (QofInstance const * inst, std::vector<std::string> const & path)
1285 {
1286     delete inst->kvp_data->set (path, nullptr);
1287 }
1288 
1289 void
qof_instance_slot_delete(QofInstance const * inst,char const * path)1290 qof_instance_slot_delete (QofInstance const *inst, char const * path)
1291 {
1292     delete inst->kvp_data->set ({path}, nullptr);
1293 }
1294 
qof_instance_slot_path_delete_if_empty(QofInstance const * inst,std::vector<std::string> const & path)1295 void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vector<std::string> const & path)
1296 {
1297     auto slot = inst->kvp_data->get_slot (path);
1298     if (slot)
1299     {
1300         auto frame = slot->get <KvpFrame*> ();
1301         if (frame && frame->empty())
1302             delete inst->kvp_data->set (path, nullptr);
1303     }
1304 }
1305 
1306 void
qof_instance_slot_delete_if_empty(QofInstance const * inst,char const * path)1307 qof_instance_slot_delete_if_empty (QofInstance const *inst, char const * path)
1308 {
1309     auto slot = inst->kvp_data->get_slot ({path});
1310     if (slot)
1311     {
1312         auto frame = slot->get <KvpFrame*> ();
1313         if (frame && frame->empty ())
1314             delete inst->kvp_data->set ({path}, nullptr);
1315     }
1316 }
1317 
1318 std::vector <std::pair <std::string, KvpValue*>>
qof_instance_get_slots_prefix(QofInstance const * inst,std::string const & prefix)1319 qof_instance_get_slots_prefix (QofInstance const * inst, std::string const & prefix)
1320 {
1321     std::vector <std::pair <std::string, KvpValue*>> ret;
1322     inst->kvp_data->for_each_slot_temp ([&prefix, &ret] (std::string const & key, KvpValue * val) {
1323         if (key.find (prefix) == 0)
1324             ret.emplace_back (key, val);
1325     });
1326     return ret;
1327 }
1328 
1329 namespace {
1330 struct wrap_param
1331 {
1332     void (*proc)(const char*, const GValue*, void*);
1333     void *user_data;
1334 };
1335 }
1336 
1337 static void
wrap_gvalue_function(const char * key,KvpValue * val,wrap_param & param)1338 wrap_gvalue_function (const char* key, KvpValue *val, wrap_param & param)
1339 {
1340     GValue *gv;
1341     if (val->get_type() != KvpValue::Type::FRAME)
1342         gv = gvalue_from_kvp_value(val);
1343     else
1344     {
1345         gv = g_slice_new0 (GValue);
1346         g_value_init (gv, G_TYPE_STRING);
1347         g_value_set_string (gv, nullptr);
1348     }
1349     param.proc(key, gv, param.user_data);
1350     g_slice_free (GValue, gv);
1351 }
1352 
1353 void
qof_instance_foreach_slot(const QofInstance * inst,const char * head,const char * category,void (* proc)(const char *,const GValue *,void *),void * data)1354 qof_instance_foreach_slot (const QofInstance *inst, const char* head, const char* category,
1355                            void (*proc)(const char*, const GValue*, void*), void* data)
1356 {
1357     std::vector<std::string> path {head};
1358     if (category)
1359         path.emplace_back (category);
1360 
1361     auto slot = inst->kvp_data->get_slot(path);
1362     if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
1363         return;
1364     auto frame = slot->get<KvpFrame*>();
1365     wrap_param new_data {proc, data};
1366     frame->for_each_slot_temp(&wrap_gvalue_function, new_data);
1367 }
1368 
1369 /* ========================== END OF FILE ======================= */
1370 
1371