]> Dogcows Code - chaz/homebank/blobdiff - src/ext-perl.xs
add plugin engine (supports C and Perl plugins)
[chaz/homebank] / src / ext-perl.xs
diff --git a/src/ext-perl.xs b/src/ext-perl.xs
new file mode 100644 (file)
index 0000000..8002047
--- /dev/null
@@ -0,0 +1,1042 @@
+
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#include <string.h>
+
+#undef _
+#include "homebank.h"
+#include "ext.h"
+#include "refcount.h"
+
+extern struct HomeBank *GLOBALS;
+#include "dsp_mainwindow.h"
+#include "dsp_account.h"
+#include "ui-transaction.h"
+
+
+static gint ext_perl_init(int* argc, char** argv[], char** env[]);
+static void ext_perl_term(void);
+static gboolean ext_perl_check_file(const gchar* plugin_filepath);
+static GHashTable* ext_perl_read_plugin_metadata(const gchar* plugin_filepath);
+static gint ext_perl_load_plugin(const gchar* plugin_filepath);
+static void ext_perl_unload_plugin(const gchar* plugin_filepath);
+static void ext_perl_execute_action(const gchar* plugin_filepath);
+static void ext_perl_call_hook(const gchar* hook_id, GList* args);
+
+static SV* val_to_sv(GValue* val);
+static GValue* sv_to_val(SV* sv);
+
+static gboolean gperl_value_from_sv(GValue* value, SV* sv);
+static SV*      gperl_sv_from_value(const GValue* value, gboolean copy_boxed);
+
+
+static inline GValue* EXT_SV(GValue* v, SV* sv, GType type)
+{
+       g_value_init(v, type);
+       gperl_value_from_sv(v, sv);
+       return v;
+}
+
+
+#define EXT_P2C_OBJECT(PKG, ARG, VAR, TYP)  \
+if (sv_derived_from(ARG, PKG)) {            \
+    IV iv = SvIV((SV*)SvRV(ARG));           \
+    VAR = INT2PTR(TYP, iv);                 \
+} else {                                    \
+    croak(#VAR" is not of type "PKG);       \
+}
+
+#define EXT_C2P_OBJECT(PKG, ARG, VAR)       \
+sv_setref_pv(ARG, PKG, (void*)VAR)
+
+
+static inline GPtrArray* SvGptrarray(const SV* sv)
+{
+       if (SvROK(sv)) {
+               sv = MUTABLE_SV(SvRV(sv));
+       }
+       if (SvTYPE(sv) == SVt_PVAV) {
+               AV* av = (AV*)sv;
+               int i;
+               int top = av_len(av);
+               GPtrArray* array = g_ptr_array_new();
+               for (i = 0; i <= top; ++i) {
+                       SV** item = av_fetch(av, i, 0);
+                       if (!item) continue;
+                       g_ptr_array_add(array, sv_to_val(*item));
+               }
+               return array;
+               // TODO- leaking
+       } else {
+               croak("var is not an array");
+       }
+}
+
+static inline SV* newSVgptrarray(const GPtrArray* a)
+{
+       if (a) {
+               AV* av = newAV();
+               int i;
+               for (i = 0; i < a->len; ++i) {
+                       GValue* item = g_ptr_array_index(a, i);
+                       av_push(av, val_to_sv(item));
+               }
+               return newRV((SV*)av);
+       }
+       return &PL_sv_undef;
+}
+
+
+static inline GHashTable* SvGhashtable(const SV* sv)
+{
+       if (SvROK(sv)) {
+               sv = MUTABLE_SV(SvRV(sv));
+       }
+       if (SvTYPE(sv) == SVt_PVHV) {
+               HV* hv = (HV*)sv;
+               hv_iterinit(hv);
+               gchar* key;
+               I32 len;
+               SV* item;
+               GHashTable* hash = g_hash_table_new(g_str_hash, g_str_equal);
+               while ((item = hv_iternextsv(hv, &key, &len))) {
+                       g_hash_table_insert(hash, key, sv_to_val(item));
+               }
+               return hash;
+               // TODO- leaking
+       } else {
+               croak("var is not a hash");
+       }
+}
+
+static inline SV* newSVghashtable(GHashTable* h)
+{
+       if (h) {
+               HV* hv = newHV();
+               GHashTableIter it;
+               g_hash_table_iter_init(&it, h);
+               gchar* key = NULL;
+               GValue* item = NULL;
+               while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&item)) {
+                       hv_store(hv, key, -g_utf8_strlen(key, -1), val_to_sv(item), 0);
+               }
+               return newRV((SV*)hv);
+       }
+       return &PL_sv_undef;
+}
+
+
+static inline gboolean SvGboolean(SV* sv)
+{
+       if (!sv) {
+               return FALSE;
+       }
+       if (SvROK(sv)) {
+               return !!SvIV(SvRV(sv));
+       } else {
+               return SvTRUE(sv);
+       }
+}
+
+static inline SV* newSVgboolean(gboolean b)
+{
+       return sv_setref_iv(newSV(0), "HomeBank::Boolean", !!b);
+}
+
+
+static inline gchar* SvGchar_ptr(SV* sv)
+{
+       return SvPVutf8_nolen(sv);
+}
+
+static inline SV* newSVgchar_ptr(const gchar* str)
+{
+       if (!str) return &PL_sv_undef;
+
+       SV* sv = newSVpv(str, 0);
+       SvUTF8_on(sv);
+       return sv;
+}
+
+
+static inline GObject* SvGobject(const SV* sv)
+{
+       GObject* (*func)(const SV*) = ext_symbol_lookup("gperl_get_object");
+       if (func) {
+               return func(sv);
+       }
+       return NULL;
+}
+
+static inline SV* newSVgobject(const GObject* o)
+{
+    SV* (*func)(const GObject*, gboolean) = ext_symbol_lookup("gperl_new_object");
+    if (func) {
+        return func(o, FALSE);
+    }
+       return &PL_sv_undef;
+}
+
+
+static PerlInterpreter* context = NULL;
+
+
+static gint ext_perl_init(int* argc, char** argv[], char** env[])
+{
+       int ret = 0;
+
+       PERL_SYS_INIT3(argc, argv, env);
+       context = perl_alloc();
+       perl_construct(context);
+
+       PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+       PL_origalen = 1;
+       PL_perl_destruct_level = 1;
+
+       gchar* bootstrap = g_strdup_printf("-e"
+               "use lib '%s';"
+               "use HomeBank;"
+               "HomeBank->bootstrap;",
+               homebank_app_get_pkglib_dir());
+       char *args[] = { "", bootstrap };
+
+       EXTERN_C void xs_init(pTHX);
+       if (perl_parse(context, xs_init, 2, args, NULL) || perl_run(context)) {
+               ext_perl_term();
+               ret = -1;
+       }
+
+       g_free(bootstrap);
+       return ret;
+}
+
+static void ext_perl_term(void)
+{
+       if (context) {
+               perl_destruct(context);
+               perl_free(context);
+               context = NULL;
+       }
+       PERL_SYS_TERM();
+}
+
+static gboolean ext_perl_check_file(const gchar* plugin_filepath)
+{
+       if (g_str_has_suffix(plugin_filepath, ".pl")) {
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static GHashTable* ext_perl_read_plugin_metadata(const gchar* plugin_filepath)
+{
+       GHashTable* table = NULL;
+
+       if (!context) return NULL;
+       PERL_SET_CONTEXT(context);
+
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       mXPUSHs(newSVgchar_ptr(plugin_filepath));
+       PUTBACK;
+
+       int ret = call_pv("HomeBank::read_metadata", G_SCALAR | G_EVAL);
+
+       SPAGAIN;
+
+       if (ret == 1) {
+               table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+               SV* sv = POPs;
+               if (SvROK(sv)) {
+                       sv = MUTABLE_SV(SvRV(sv));
+               }
+               if (SvTYPE(sv) == SVt_PVHV) {
+                       HV* hv = (HV*)sv;
+                       hv_iterinit(hv);
+                       gchar* key;
+                       I32 len;
+                       SV* item;
+                       while ((item = hv_iternextsv(hv, &key, &len))) {
+                               if (SvPOK(item)) {
+                                       gchar* val = SvPVutf8_nolen(item);
+                                       g_hash_table_insert(table, g_strdup(key), g_strdup(val));
+                               }
+                       }
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+       return table;
+}
+
+static gint ext_perl_load_plugin(const gchar* plugin_filepath)
+{
+       if (!context) return -1;
+       PERL_SET_CONTEXT(context);
+
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       mXPUSHs(newSVgchar_ptr(plugin_filepath));
+       PUTBACK;
+       call_pv("HomeBank::load_plugin", G_DISCARD | G_EVAL);
+       SPAGAIN;
+
+       gint ret = 0;
+       if (SvTRUE(ERRSV)) {
+               g_printerr("%s", SvPV_nolen(ERRSV));
+               ret = -1;
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+       return ret;
+}
+
+static void ext_perl_unload_plugin(const gchar* plugin_filepath)
+{
+       if (!context) return;
+       PERL_SET_CONTEXT(context);
+
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       mXPUSHs(newSVgchar_ptr(plugin_filepath));
+       PUTBACK;
+       call_pv("HomeBank::unload_plugin", G_DISCARD | G_EVAL);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+               g_printerr("%s", SvPV_nolen(ERRSV));
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void ext_perl_execute_action(const gchar* plugin_filepath)
+{
+       if (!context) return;
+       PERL_SET_CONTEXT(context);
+
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       mXPUSHs(newSVgchar_ptr(plugin_filepath));
+       PUTBACK;
+       call_pv("HomeBank::execute_action", G_DISCARD | G_EVAL);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+               g_printerr("%s", SvPV_nolen(ERRSV));
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void ext_perl_call_hook(const gchar* hook_id, GList* args)
+{
+       if (!context) return;
+       PERL_SET_CONTEXT(context);
+
+       dSP;
+       ENTER;
+       SAVETMPS;
+       PUSHMARK(SP);
+       mXPUSHs(newSVgchar_ptr(hook_id));
+
+       GList *list = g_list_first(args);
+       while (list) {
+               GValue* val = list->data;
+               XPUSHs(sv_2mortal(val_to_sv(val)));
+               list = g_list_next(list);
+       }
+
+       PUTBACK;
+       call_pv("HomeBank::call_hook", G_ARRAY);
+       SPAGAIN;
+       POPi;
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+
+static SV* val_to_sv(GValue* val)
+{
+       if (!val || !G_IS_VALUE(val) || G_VALUE_TYPE(val) == G_TYPE_NONE) {
+               return &PL_sv_undef;
+       }
+       if (G_VALUE_TYPE(val) == G_TYPE_BOOLEAN) {
+               return newSVgboolean(g_value_get_boolean(val));
+       }
+       if (G_VALUE_TYPE(val) == G_TYPE_PTR_ARRAY) {
+               return newSVgptrarray((GPtrArray*)g_value_get_boxed(val));
+       }
+       if (G_VALUE_TYPE(val) == G_TYPE_HASH_TABLE) {
+               return newSVghashtable((GHashTable*)g_value_get_boxed(val));
+       }
+#define obj(CTYPE, _2, PART, GTYPE, _5)                         \
+       if (G_VALUE_TYPE(val) == GTYPE) {                           \
+               SV* sv = newSV(0);                                      \
+               CTYPE* ptr = (CTYPE*)g_value_get_##PART(val);           \
+               EXT_C2P_OBJECT("HomeBank::"#CTYPE, sv, rc_ref(ptr));    \
+               return sv;                                              \
+       }
+#include "ext-value.h"
+#undef obj
+       return gperl_sv_from_value(val, FALSE);
+}
+
+static GValue* sv_to_val(SV* sv)
+{
+       GValue* val = g_new0(GValue, 1);
+
+       if (SvUOK(sv)) return EXT_SV(val, sv, G_TYPE_UINT);
+       if (SvIOK(sv)) return EXT_SV(val, sv, G_TYPE_INT);
+       if (SvNOK(sv)) return EXT_SV(val, sv, G_TYPE_DOUBLE);
+       if (SvPOK(sv)) return EXT_SV(val, sv, G_TYPE_STRING);
+       if (sv_isobject(sv)) {
+               if (sv_derived_from(sv, "HomeBank::Boolean")) {
+                       return EXT_BOOLEAN(val, SvGboolean(sv));
+               }
+#define obj(CTYPE, NAME, _3, _4, _5)                                \
+               if (sv_derived_from(sv, "HomeBank::"#CTYPE)) {              \
+                       CTYPE* ptr;                                             \
+                       EXT_P2C_OBJECT("HomeBank::"#CTYPE, sv, ptr, CTYPE*);    \
+                       return EXT_##NAME(val, ptr);                            \
+               }
+#include "ext-value.h"
+#undef obj
+               return EXT_SV(val, sv, G_TYPE_OBJECT);
+       }
+       if (SvROK(sv)) {
+               sv = SvRV(sv);
+               switch (SvTYPE(sv)) {
+                       case SVt_IV:
+                               return EXT_BOOLEAN(val, SvGboolean(sv));
+                       case SVt_PVAV:
+                               return EXT_ARRAY(val, SvGptrarray(sv));
+                       case SVt_PVHV:
+                               return EXT_HASH_TABLE(val, SvGhashtable(sv));
+                       default:
+                               break;
+               }
+       }
+       switch (SvTYPE(sv)) {
+               case SVt_PVAV:
+                       return EXT_ARRAY(val, SvGptrarray(sv));
+               case SVt_PVHV:
+                       return EXT_HASH_TABLE(val, SvGhashtable(sv));
+               default:
+                       break;
+       }
+
+       g_free(val);
+       return NULL;
+}
+
+
+static gboolean gperl_value_from_sv(GValue* value, SV* sv)
+{
+       gboolean (*func)(GValue*, SV*) = ext_symbol_lookup("gperl_value_from_sv");
+       if (func) return func(value, sv);
+
+       GType type = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
+       if (!SvOK(sv)) return TRUE;
+       switch (type) {
+               case G_TYPE_CHAR:
+               {
+                       gchar *tmp = SvGchar_ptr(sv);
+                       g_value_set_schar(value, (gint8)(tmp ? tmp[0] : 0));
+                       break;
+               }
+               case G_TYPE_UCHAR:
+               {
+                       char *tmp = SvPV_nolen(sv);
+                       g_value_set_uchar(value, (guchar)(tmp ? tmp[0] : 0));
+                       break;
+               }
+               case G_TYPE_BOOLEAN:
+                       g_value_set_boolean(value, SvTRUE(sv));
+                       break;
+               case G_TYPE_INT:
+                       g_value_set_int(value, SvIV(sv));
+                       break;
+               case G_TYPE_UINT:
+                       g_value_set_uint(value, SvIV(sv));
+                       break;
+               case G_TYPE_LONG:
+                       g_value_set_long(value, SvIV(sv));
+                       break;
+               case G_TYPE_ULONG:
+                       g_value_set_ulong(value, SvIV(sv));
+                       break;
+               case G_TYPE_FLOAT:
+                       g_value_set_float(value, (gfloat)SvNV(sv));
+                       break;
+               case G_TYPE_DOUBLE:
+                       g_value_set_double(value, SvNV(sv));
+                       break;
+               case G_TYPE_STRING:
+                       g_value_set_string(value, SvGchar_ptr(sv));
+                       break;
+       }
+       return TRUE;
+}
+
+static SV* gperl_sv_from_value(const GValue* value, gboolean copy_boxed)
+{
+       SV* (*func)(const GValue*, gboolean) = ext_symbol_lookup("gperl_sv_from_value");
+       if (func) return func(value, copy_boxed);
+
+       GType type = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
+       switch (type) {
+               case G_TYPE_CHAR:
+                       return newSViv(g_value_get_schar(value));
+               case G_TYPE_UCHAR:
+                       return newSVuv(g_value_get_uchar(value));
+               case G_TYPE_BOOLEAN:
+                       return newSViv(g_value_get_boolean(value));
+               case G_TYPE_INT:
+                       return newSViv(g_value_get_int(value));
+               case G_TYPE_UINT:
+                       return newSVuv(g_value_get_uint(value));
+               case G_TYPE_LONG:
+                       return newSViv(g_value_get_long(value));
+               case G_TYPE_ULONG:
+                       return newSVuv(g_value_get_ulong(value));
+               case G_TYPE_FLOAT:
+                       return newSVnv(g_value_get_float(value));
+               case G_TYPE_DOUBLE:
+                       return newSVnv(g_value_get_double(value));
+               case G_TYPE_STRING:
+                       return newSVgchar_ptr(g_value_get_string(value));
+       }
+       return &PL_sv_undef;
+}
+
+
+static void _register(void) __attribute__((constructor));
+static void _register()
+{
+       ext_register("perl",
+                       ext_perl_init,
+                       ext_perl_term,
+                       ext_perl_check_file,
+                       ext_perl_read_plugin_metadata,
+                       ext_perl_load_plugin,
+                       ext_perl_unload_plugin,
+                       ext_perl_execute_action,
+                       ext_perl_call_hook);
+}
+
+
+MODULE = HomeBank  PACKAGE = HomeBank
+
+PROTOTYPES: ENABLE
+
+const gchar*
+version(void)
+       CODE:
+               RETVAL = VERSION;
+       OUTPUT:
+               RETVAL
+
+const gchar*
+config_dir(void)
+       CODE:
+               RETVAL = homebank_app_get_config_dir();
+       OUTPUT:
+               RETVAL
+
+gboolean
+has(const gchar* CLASS, ...)
+       PREINIT:
+               int i;
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               RETVAL = TRUE;
+               for (i = 1; i < items; ++i) {
+                       gchar* feature = SvGchar_ptr(ST(i));
+                       if (!feature || !ext_has(feature)) {
+                               RETVAL = FALSE;
+                               break;
+                       }
+               }
+       OUTPUT:
+               RETVAL
+
+GObject*
+main_window(void)
+       CODE:
+               RETVAL = G_OBJECT(GLOBALS->mainwindow);
+       OUTPUT:
+               RETVAL
+
+GObject*
+main_ui_manager(void)
+       PREINIT:
+               struct hbfile_data *data;
+       CODE:
+               RETVAL = NULL;
+               if (GLOBALS->mainwindow) {
+                       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GLOBALS->mainwindow, GTK_TYPE_WINDOW)), "inst_data");
+                       if (data) {
+                               RETVAL = G_OBJECT(data->manager);
+                       }
+               }
+       OUTPUT:
+               RETVAL
+
+void
+info(const gchar* CLASS, const gchar* title, const gchar* text)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               ext_run_modal(title, text, "info");
+
+void
+warn(const gchar* CLASS, const gchar* title, const gchar* text)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               ext_run_modal(title, text, "warn");
+
+void
+error(const gchar* CLASS, const gchar* title, const gchar* text)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               ext_run_modal(title, text, "error");
+
+void
+hook(const gchar* CLASS, const gchar* hook_name, ...)
+       PREINIT:
+               int i;
+               GList* list = NULL;
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               for (i = 2; i < items; ++i) {
+                       SV* sv = ST(i);
+                       GValue *val = sv_to_val(sv);
+                       list = g_list_append(list, val);
+               }
+       CLEANUP:
+               ext_vhook(hook_name, list);
+               g_list_free(list);
+               // TODO free all the things
+
+GObject*
+open_prefs(const gchar* CLASS)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               RETVAL = G_OBJECT(defpref_dialog_new(PREF_GENERAL));
+       OUTPUT:
+               RETVAL
+
+
+MODULE = HomeBank  PACKAGE = HomeBank::File
+
+const gchar*
+owner(const gchar* CLASS, ...)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               if (1 < items) {
+                       hbfile_change_owner(g_strdup(SvGchar_ptr(ST(1))));
+               }
+               RETVAL = GLOBALS->owner;
+       OUTPUT:
+               RETVAL
+
+void
+transactions(const gchar* CLASS)
+       PPCODE:
+               PERL_UNUSED_ARG(CLASS);
+               GList* list = g_list_first(GLOBALS->ope_list);
+               for (; list; list = g_list_next(list)) {
+                       GValue val = G_VALUE_INIT;
+                       SV* sv = val_to_sv(EXT_TRANSACTION(&val, list->data));
+                       mXPUSHs(sv);
+               }
+
+void
+anonymize(void)
+       CODE:
+               hbfile_anonymize();
+
+void
+baz(const gchar* CLASS, Account* account)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               g_print("hello: %s\n", account->name);
+
+GPtrArray*
+meh(const gchar* CLASS, GPtrArray* asdf)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               g_print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW\n");
+               if (asdf) {
+                       ;
+               } else {
+                       g_print("the array is nil\n");
+               }
+               RETVAL = asdf;
+       OUTPUT:
+               RETVAL
+       CLEANUP:
+               g_ptr_array_unref(asdf);
+
+GHashTable*
+foo(const gchar* CLASS, GHashTable* asdf)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               g_print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW\n");
+               if (asdf) {
+                       GHashTableIter it;
+                       g_hash_table_iter_init(&it, asdf);
+                       gchar* key = NULL;
+                       GValue* item = NULL;
+                       while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&item)) {
+                               g_print("hash with key: %s\n", key);
+                       }
+               } else {
+                       g_print("the hash is nil\n");
+               }
+               RETVAL = asdf;
+       OUTPUT:
+               RETVAL
+       CLEANUP:
+               g_hash_table_unref(asdf);
+
+
+MODULE = HomeBank  PACKAGE = HomeBank::Account
+
+void
+compute_balances(const gchar* CLASS)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               account_compute_balances();
+
+Account*
+new(void)
+       CODE:
+               RETVAL = da_acc_malloc();
+       OUTPUT:
+               RETVAL
+
+Account*
+clone(Account* SELF)
+       CODE:
+               RETVAL = da_acc_clone(SELF);
+               RETVAL->key = 0;
+       OUTPUT:
+               RETVAL
+
+void
+DESTROY(Account* SELF)
+       CODE:
+               da_acc_free(SELF);
+
+Account*
+get(const gchar* CLASS, guint key)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               RETVAL = rc_ref(da_acc_get(key));
+       OUTPUT:
+               RETVAL
+
+Account*
+get_by_name(const gchar* CLASS, const gchar* name)
+       CODE:
+               PERL_UNUSED_ARG(CLASS);
+               RETVAL = rc_ref(da_acc_get_by_name((gchar*)name));
+       OUTPUT:
+               RETVAL
+
+const gchar*
+name(Account* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       account_rename(SELF, SvGchar_ptr(ST(1)));
+               }
+               RETVAL = SELF->name;
+       OUTPUT:
+               RETVAL
+
+const gchar*
+number(Account* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       g_free(SELF->number);
+                       SELF->number = g_strdup(SvGchar_ptr(ST(1)));
+               }
+               RETVAL = SELF->number;
+       OUTPUT:
+               RETVAL
+
+const gchar*
+bankname(Account* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       g_free(SELF->bankname);
+                       SELF->bankname = g_strdup(SvGchar_ptr(ST(1)));
+               }
+               RETVAL = SELF->bankname;
+       OUTPUT:
+               RETVAL
+
+gdouble
+initial(Account* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       SELF->initial = SvNV(ST(1));
+               }
+               RETVAL = SELF->initial;
+       OUTPUT:
+               RETVAL
+
+gdouble
+minimum(Account* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       SELF->minimum = SvNV(ST(1));
+               }
+               RETVAL = SELF->minimum;
+       OUTPUT:
+               RETVAL
+
+guint
+cheque1(Account* SELF, ...)
+       ALIAS:
+               check1 = 1
+       CODE:
+               PERL_UNUSED_VAR(ix);
+               if (1 < items) {
+                       SELF->cheque1 = SvUV(ST(1));
+               }
+               RETVAL = SELF->cheque1;
+       OUTPUT:
+               RETVAL
+
+guint
+cheque2(Account* SELF, ...)
+       ALIAS:
+               check2 = 1
+       CODE:
+               PERL_UNUSED_VAR(ix);
+               if (1 < items) {
+                       SELF->cheque2 = SvUV(ST(1));
+               }
+               RETVAL = SELF->cheque2;
+       OUTPUT:
+               RETVAL
+
+gdouble
+balance(Account* SELF)
+       ALIAS:
+               bank_balance    = 1
+               future_balance  = 2
+       CODE:
+               switch (ix) {
+                       case 1:
+                               RETVAL = SELF->bal_bank;
+                               break;
+                       case 2:
+                               RETVAL = SELF->bal_future;
+                               break;
+                       default:
+                               RETVAL = SELF->bal_today;
+                               break;
+               }
+       OUTPUT:
+               RETVAL
+
+gboolean
+is_inserted(Account* SELF)
+       CODE:
+               RETVAL = da_acc_get(SELF->key) == SELF;
+       OUTPUT:
+               RETVAL
+
+gboolean
+is_used(Account* SELF)
+       CODE:
+               RETVAL = account_is_used(SELF->key);
+       OUTPUT:
+               RETVAL
+
+gboolean
+insert(Account* SELF)
+       CODE:
+               if (SELF->key == 0 || account_is_used(SELF->key))
+                       RETVAL = da_acc_append(rc_ref(SELF));
+               else
+                       RETVAL = da_acc_insert(rc_ref(SELF));
+       OUTPUT:
+               RETVAL
+
+void
+remove(Account* SELF)
+       CODE:
+               da_acc_remove(SELF->key);
+
+void
+transactions(Account* SELF)
+       PPCODE:
+               GList* list = g_list_first(GLOBALS->ope_list);
+               for (; list; list = g_list_next(list)) {
+                       Transaction* txn = list->data;
+                       if (txn->kacc == SELF->key) {
+                               GValue val = G_VALUE_INIT;
+                               SV* sv = val_to_sv(EXT_TRANSACTION(&val, txn));
+                               mXPUSHs(sv);
+                       }
+               }
+
+GObject*
+open(Account* SELF)
+       CODE:
+               RETVAL = G_OBJECT(register_panel_window_new(SELF->key, SELF));
+       OUTPUT:
+               RETVAL
+
+
+MODULE = HomeBank  PACKAGE = HomeBank::Transaction
+
+Transaction*
+new(void)
+       CODE:
+               RETVAL = da_transaction_malloc();
+       OUTPUT:
+               RETVAL
+
+void
+DESTROY(Transaction* SELF)
+       CODE:
+               da_transaction_free(SELF);
+
+gdouble
+amount(Transaction* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       SELF->amount = SvNV(ST(1));
+               }
+               RETVAL = SELF->amount;
+       OUTPUT:
+               RETVAL
+
+guint
+account_num(Transaction* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       SELF->kacc = SvIV(ST(1));
+               }
+               RETVAL = SELF->kacc;
+       OUTPUT:
+               RETVAL
+
+guint
+paired_account_num(Transaction* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       SELF->kxferacc = SvIV(ST(1));
+               }
+               RETVAL = SELF->kxferacc;
+       OUTPUT:
+               RETVAL
+
+void
+date(Transaction* SELF, ...)
+       PPCODE:
+               if (1 < items) {
+                       SELF->date = SvIV(ST(1));
+               }
+               if (GIMME_V == G_ARRAY) {
+                       GDate* d = g_date_new_julian(SELF->date);
+                       mXPUSHp("day", 3);
+                       mXPUSHi(g_date_get_day(d));
+                       mXPUSHp("month", 5);
+                       mXPUSHi(g_date_get_month(d));
+                       mXPUSHp("year", 4);
+                       mXPUSHi(g_date_get_year(d));
+                       g_date_free(d);
+                       XSRETURN(6);
+               } else {
+                       XSRETURN_IV(SELF->date);
+               }
+
+const gchar*
+wording(Transaction* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       if (SELF->wording) g_free(SELF->wording);
+                       SELF->wording = g_strdup(SvGchar_ptr(ST(1)));
+               }
+               RETVAL = SELF->wording ? SELF->wording : "";
+       OUTPUT:
+               RETVAL
+
+const gchar*
+info(Transaction* SELF, ...)
+       CODE:
+               if (1 < items) {
+                       if (SELF->info) g_free(SELF->info);
+                       SELF->info = g_strdup(SvGchar_ptr(ST(1)));
+               }
+               RETVAL = SELF->info ? SELF->info : "";
+       OUTPUT:
+               RETVAL
+
+GObject*
+open(Transaction* SELF)
+       CODE:
+               RETVAL = G_OBJECT(create_deftransaction_window(NULL, TRANSACTION_EDIT_MODIFY));
+               deftransaction_set_transaction(GTK_WIDGET(RETVAL), SELF);
+       OUTPUT:
+               RETVAL
+
+Transaction*
+pair_with(Transaction* SELF, Transaction* other, ...)
+       PREINIT:
+               int i;
+               GList* list = NULL;
+       CODE:
+               if (2 < items) {
+                       list = g_list_append(list, other);
+                       for (i = 2; i < items; ++i) {
+                               Transaction* ptr = NULL;
+                               SV* sv = ST(i);
+                               EXT_P2C_OBJECT("HomeBank::Transaction", sv, ptr, Transaction*);
+                               list = g_list_append(list, ptr);
+                       }
+                       other = ui_dialog_transaction_xfer_select_child(list);
+               }
+               if (other) {
+                       transaction_xfer_change_to_child(SELF, other);
+                       SELF->paymode = PAYMODE_INTXFER;
+               }
+               RETVAL = other;
+       OUTPUT:
+               RETVAL
+       CLEANUP:
+               g_list_free(list);
+
+void
+dump(Transaction* SELF)
+       CODE:
+               g_print("txn: %p (%s) at %u (%d/%d) flags:%d, paymode:%d, kpay:%d, kcat:%d", SELF,
+                       SELF->wording, SELF->date, SELF->kacc, SELF->kxferacc, SELF->flags, SELF->paymode, SELF->kpay, SELF->kcat);
+
This page took 0.041562 seconds and 4 git commands to generate.