+
+#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);
+