]> Dogcows Code - chaz/homebank/commitdiff
Merge branch 'master' into ext-perl
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Thu, 25 Apr 2019 05:07:35 +0000 (23:07 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Thu, 25 Apr 2019 05:07:35 +0000 (23:07 -0600)
19 files changed:
1  2 
configure.ac
src/Makefile.am
src/dsp-mainwindow.c
src/ext-perl.xs
src/hb-account.c
src/hb-archive.c
src/hb-assign.c
src/hb-category.c
src/hb-payee.c
src/hb-preferences.c
src/hb-preferences.h
src/hb-tag.c
src/hb-transaction.c
src/hb-xml.c
src/homebank.c
src/homebank.h
src/ui-pref.c
src/ui-pref.h
themes/hicolor/Makefile.am

diff --cc configure.ac
Simple merge
diff --cc src/Makefile.am
index 20da8f18246114c77313051187aa34980ea3da4f,9e4fd20426d55c6f41e1d895f77f79f3822e0e2e..7acfc873883dffc4ec74c3778e3d6841d5f13d44
@@@ -121,21 -128,13 +129,23 @@@ homebank_SOURCES =  
        ui-transaction.h \
        ui-txn-multi.c \
        ui-txn-multi.h \
+       ui-widgets-data.c \
        ui-widgets.c \
 -      ui-widgets.h
 +      ui-widgets.h \
 +      refcount.h \
 +      ext.c \
 +      ext.h \
 +      ext-value.c \
 +      ext-value.h \
 +      ext-native.c \
 +      ext-perl.xs
 +
 +EXTRA_homebank_DEPENDENCIES = $(PERL_OBJS)
  
  homebank_LDADD = $(DEPS_LIBS) \
 -      $(LIBSOUP_LIBS)
 +      $(LIBSOUP_LIBS) \
 +      $(PERL_OBJS)
  
  AM_CPPFLAGS = \
        $(DEPS_CFLAGS) \
index c733f4cb730be8c3f443f05f2c3bf5fc3a850152,1f53410322aea8c6b5a33953f9ad1f7d86eb8c68..0b21f9d701093801db9dde295f65c50a1ee13a75
  
  #include "homebank.h"
  
- #include "dsp_mainwindow.h"
+ #include "dsp-mainwindow.h"
  
- #include "list_account.h"
- #include "list_upcoming.h"
- #include "list_topspending.h"
 +#include "ext.h"
 +
+ #include "list-account.h"
  
- #include "dsp_account.h"
+ #include "hub-account.h"
+ #include "hub-scheduled.h"
+ #include "hub-spending.h"
+ #include "hub-transaction.h"
+ #include "dsp-account.h"
  #include "ui-assist-import.h"
  #include "ui-assist-start.h"
  #include "ui-account.h"
@@@ -149,26 -149,9 +153,10 @@@ void ui_mainwindow_update(GtkWidget *wi
  void ui_mainwindow_addtransactions(GtkWidget *widget, gpointer user_data);
  void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
  
- static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data);
- static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data);
- void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data);
  void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
  
 +static void ui_mainwindow_showprefs(gint page);
  
- static void ui_panel_accounts_setup(struct hbfile_data *data);
- extern gchar *CYA_ACC_TYPE[];
- gchar *CYA_CATSUBCAT[] = { 
-       N_("Category"), 
-       N_("Subcategory"), 
-       NULL
- };
  static GtkActionEntry entries[] = {
  
    /* name, icon-name, label */
    { "Quit"       , ICONNAME_QUIT           , N_("_Quit")         , "<control>Q", N_("Quit HomeBank"),    G_CALLBACK (ui_mainwindow_action_quit) },
  
    /* Exchange */
-   { "ImportQIF" , ICONNAME_HB_FILE_IMPORT  , N_("QIF file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
-   { "ImportOFX" , ICONNAME_HB_FILE_IMPORT  , N_("OFX/QFX file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
-   { "ImportCSV" , ICONNAME_HB_FILE_IMPORT  , N_("CSV file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
-       
-   { "ExportQIF" , ICONNAME_HB_FILE_EXPORT  , N_("Export QIF file...")     , NULL, N_("Export all account in a QIF file"),    G_CALLBACK (ui_mainwindow_action_export) },
+   { "Import" , ICONNAME_HB_FILE_IMPORT  , N_("Import...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
+   //{ "ImportQIF" , ICONNAME_HB_FILE_IMPORT  , N_("QIF file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
+   //{ "ImportOFX" , ICONNAME_HB_FILE_IMPORT  , N_("OFX/QFX file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
+   //{ "ImportCSV" , ICONNAME_HB_FILE_IMPORT  , N_("CSV file...")     , NULL, N_("Open the import assistant"),    G_CALLBACK (ui_mainwindow_action_import) },
 -      
++
+   { "ExportQIF" , ICONNAME_HB_FILE_EXPORT  , N_("Export as QIF...")     , NULL, N_("Export all account in a QIF file"),    G_CALLBACK (ui_mainwindow_action_export) },
  
    /* EditMenu */
    { "Preferences", ICONNAME_PREFERENCES    , N_("Preferences..."), NULL,    N_("Configure HomeBank"),    G_CALLBACK (ui_mainwindow_action_preferences) },
    { "Welcome"     , NULL              , N_("Show welcome dialog...")  , NULL, NULL, G_CALLBACK (ui_mainwindow_action_help_welcome) },
    { "FileStats"   , NULL              , N_("File statistics...")  , NULL, NULL,    G_CALLBACK (ui_mainwindow_action_file_statistics) },
    { "Anonymize"   , NULL              , N_("Anonymize...")  , NULL, NULL,    G_CALLBACK (ui_mainwindow_action_anonymize) },
--      
++
 +  /* Plugins */
 +  { "PluginPreferences", "prf-plugins", N_("_Plugins..."), "<control>U", N_("Configure plugin preferences"), G_CALLBACK(ui_mainwindow_action_pluginprefs) },
 +
    /* HelpMenu */
    { "Contents"    , ICONNAME_HELP     , N_("_Contents")                    , "F1", N_("Documentation about HomeBank"), G_CALLBACK (ui_mainwindow_action_help) },
    { "Online"      , "lpi-help"        , N_("Get Help Online...")           , NULL, N_("Connect to the LaunchPad website for online help"), G_CALLBACK (ui_mainwindow_action_help_online) },
@@@ -399,7 -383,7 +398,7 @@@ gint result
        title = g_strdup_printf (
                _("Revert unsaved changes to file '%s'?"), basename);
  
--      secondtext = 
++      secondtext =
                _("- Changes made to the file will be permanently lost\n"
                "- File will be reloaded from the last save (.xhb~)");
  
        if( result == GTK_RESPONSE_OK )
        {
                DB( g_print(" - should revert\n") );
--              
++
                hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~"));
                ui_mainwindow_open_internal(widget, NULL);
                hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
@@@ -431,7 -415,7 +430,7 @@@ activate_url (GtkAboutDialog *about
              gpointer        data)
  {
        DB( g_print("activate url %s\n", link) );
--      
++
        homebank_util_url_show (link);
  }
  
@@@ -471,18 -454,18 +470,18 @@@ gchar *version
                                                       gtk_get_major_version (),
                                                       gtk_get_minor_version (),
                                                       gtk_get_micro_version ());
--      
++
        dialog = gtk_about_dialog_new();
  
        gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(GLOBALS->mainwindow));
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
--      
++
        gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG(dialog), g_get_application_name ());
        gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), version);
        gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
        gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), _("Free, easy, personal accounting for everyone"));
        gtk_about_dialog_set_license_type (GTK_ABOUT_DIALOG(dialog), GTK_LICENSE_GPL_2_0);
--      
++
        //gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(dialog), );
        gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://homebank.free.fr");
        gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), "Visit the HomeBank website");
                gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
                g_object_unref (pixbuf);
        }
--      
++
        gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog), authors);
        gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(dialog), artists);
        //gtk_about_dialog_set_documenters(GTK_ABOUT_DIALOG(dialog), );
        gtk_widget_destroy (dialog);
  
        g_free(version);
--      
++
  }
  
  
@@@ -602,7 -584,7 +606,7 @@@ gchar *secondtext
  
        title = _("Are you sure you want to anonymize the file?");
  
--      secondtext = 
++      secondtext =
                _("Proceeding will anonymize any text, \n"
                "like 'account x', 'payee y', 'memo z', ...");
  
  
        //#1707201
        //if( result == GTK_RESPONSE_CANCEL )
--      //      return; 
++      //      return;
        if( result == GTK_RESPONSE_OK )
        {
                hbfile_anonymize();
@@@ -699,12 -682,16 +704,21 @@@ static void ui_mainwindow_action_defass
  }
  
  
+ static void ui_mainwindow_action_deftag(void)
+ {
+       ui_tag_manage_dialog();
+       //ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
+ }
  static void ui_mainwindow_action_preferences(void)
 +{
 +      ui_mainwindow_showprefs(PREF_GENERAL);
 +}
 +
 +static void ui_mainwindow_showprefs(gint page)
  {
  struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
  
@@@ -756,8 -743,8 +770,8 @@@ struct hbfile_data *data = g_object_get
  
        // top spending
        gtk_chart_show_minor(GTK_CHART(data->RE_pie), GLOBALS->minor);
--      
-       ui_panel_topspending_update(data->window, data);
++
+       ui_hub_spending_update(data->window, data);
  
  }
  
@@@ -802,7 -807,7 +834,7 @@@ static void ui_mainwindow_action_statis
  static void ui_mainwindow_action_trendtime(void)
  {
  struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
--      
++
        ui_reptime_window_new(data->acc != NULL ? data->acc->key : 0);
  }
  
@@@ -930,7 -935,7 +962,7 @@@ GtkWidget *mainvbox, *widget, *label
                        NULL);
  
        content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
--      
++
        mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
        gtk_box_pack_start (GTK_BOX (content_area), mainvbox, FALSE, FALSE, 0);
        gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
        widget = gtk_button_new_with_mnemonic(_("Read HomeBank _Manual"));
        gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
        g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome1), dialog);
--      
++
        widget = gtk_button_new_with_mnemonic(_("Configure _preferences"));
        gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
        g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome2), dialog);
--      
++
        widget = gtk_button_new_with_mnemonic(_("Create a _new file"));
        gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
        g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome3), dialog);
@@@ -1095,10 -1100,10 +1127,10 @@@ gboolean file_clear = GPOINTER_TO_INT(u
        gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc))));
        gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc))));
        gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top))));
--      
++
        data->showall = FALSE;
-       ui_panel_accounts_setup(data);
-       
+       ui_hub_account_setup(data);
 -      
++
        hbfile_cleanup(file_clear);
        hbfile_setup(file_clear);
  
@@@ -1143,6 -1148,8 +1175,8 @@@ gint account, count
  
                        if( PREFS->heritdate == FALSE ) //fix: 318733
                                ope->date = GLOBALS->today;
 -                      
++
+                       da_transaction_set_default_template(ope);
                }
  
                // normally we can't be in addkeep without initialized ope with add
  
                        DB( g_print(" - added 1 transaction to %d\n", ope->kacc) );
  
-                       ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
-                       
+                       ui_hub_account_populate(GLOBALS->mainwindow, NULL);
 -                      
++
                        count++;
                        //todo: still usefull ? store last date
                        date = ope->date;
        }
  }
  
- struct tmptop
- {
-       guint32         key;
-       gdouble         value;
- };
- #define MAX_TOPSPENDING 10
- static gint tmptop_compare_func(struct tmptop *tt1, struct tmptop *tt2)
- {
-       return tt1->value > tt2->value ? 1 : -1;
- }
  
static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data)
gboolean ui_mainwindow_open_backup_check_confirm(gchar *filepath)
  {
- struct hbfile_data *data;
- GtkTreeModel *model;
- gchar *title;
- gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
-       DB( g_print("\n[ui-mainwindow] topspending_update\n") );
+ gboolean retval = FALSE;
+ gchar *basename, *secondtext;
+ gboolean result;
  
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
+       basename = g_path_get_basename(filepath);
+       secondtext = g_strdup_printf (
 -      _("Your are about to open the backup file '%s'.\n\nAre you sure you want to do this ?"), basename);             
++      _("Your are about to open the backup file '%s'.\n\nAre you sure you want to do this ?"), basename);
  
-       hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->toptotal, GLOBALS->kcur, GLOBALS->minor);        
-       //hb_label_set_amount(GTK_LABEL(data->TX_topamount), total, GLOBALS->kcur, GLOBALS->minor);
-       title = g_strdup_printf("%s %s", _("Top spending"), strbuffer);
+       result = ui_dialog_msg_confirm_alert(
+               GTK_WINDOW(GLOBALS->mainwindow),
+               _("Open the backup file ?"),
+               secondtext,
+               _("_Open backup")
 -      );      
++      );
  
-       model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
-       
-       gtk_chart_set_color_scheme(GTK_CHART(data->RE_pie), PREFS->report_color_scheme);
-       gtk_chart_set_currency(GTK_CHART(data->RE_pie), GLOBALS->kcur);
-       gtk_chart_set_datas(GTK_CHART(data->RE_pie), model, LST_TOPSPEND_AMOUNT, title, NULL);
+       g_free(secondtext);
+       g_free(basename);
  
-       g_free(title);
+       if( result == GTK_RESPONSE_OK )
+               retval = TRUE;
 -      
 +
-       //future usage
-       gchar *fu = _("Top %d spending"); title = fu;
+       return retval;
  }
  
  
@@@ -1331,906 -1253,179 +1280,179 @@@ gchar *filename = NULL
                                }
                                else
                                {
-                               struct tmptop *item;
-                                       pos = category_report_id(ope->kcat, type);
-       
-                                       //#1297054 if( trn_amount < 0 ) {
-                                               item = &g_array_index (garray, struct tmptop, pos);
-                                               item->key = pos;
-                                               item->value += trn_amount;
-                                               //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos)  );
-                                       //}
+                                       g_free(filename);
+                                       return;
                                }
-                       }
-                       list = g_list_next(list);
-               }
-               g_queue_free (txn_queue);
-               
-               // we need to sort this and limit before
-               g_array_sort(garray, (GCompareFunc)tmptop_compare_func);
-               n_items = MIN(garray->len,MAX_TOPSPENDING);
-               other = 0;
-               for(i=0 ; i<garray->len ; i++)
-               {
-               struct tmptop *item;
-               
-                       item = &g_array_index (garray, struct tmptop, i);
-                       if(item->value < 0)
-                       {
-                               total += item->value;
-                               if(i >= n_items)
-                                       other += item->value;
-                               DB( g_print(" - %d : k='%d' v='%f' t='%f'\n", i, item->key, item->value, total) );
 -                      }                       
 +                      }
-               }
  
-               model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
-               gtk_list_store_clear (GTK_LIST_STORE(model));
-               g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
-               gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), NULL); /* Detach model from view */
-               /* insert into the treeview */
-               for(i=0 ; i<MIN(garray->len,MAX_TOPSPENDING) ; i++)
-               {
-               gchar *name;
-               Category *entry;
-               struct tmptop *item;
-               gdouble value;
-               
-                       item = &g_array_index (garray, struct tmptop, i);
-                       if(!item->value) continue;
-                       value = hb_amount_round(item->value, 2);
-                       entry = da_cat_get(item->key);
-                       if(entry == NULL) continue;
-                       name = entry->key == 0 ? _("(no category)") : da_cat_get_fullname(entry);
-                       // append test
-                       gtk_list_store_append (GTK_LIST_STORE(model), &iter);
-                       gtk_list_store_set (GTK_LIST_STORE(model), &iter,
-                                 LST_TOPSPEND_ID, i,
-                                 LST_TOPSPEND_KEY, 0,
-                                 LST_TOPSPEND_NAME, name,
-                                 LST_TOPSPEND_AMOUNT, value,
-                                 //LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5),
-                                 -1);
-               }
-               // append test
-               if(ABS(other) > 0)
-               {
-                       gtk_list_store_append (GTK_LIST_STORE(model), &iter);
-                       gtk_list_store_set (GTK_LIST_STORE(model), &iter,
-                                 LST_TOPSPEND_ID, n_items,
-                                 LST_TOPSPEND_KEY, 0,
-                                 LST_TOPSPEND_NAME, _("Other"),
-                                 LST_TOPSPEND_AMOUNT, other,
-                                 //LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5),
-                                 -1);
-               }
-                       
-               /* Re-attach model to view */
-               gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), model);
-               g_object_unref(model);
-               
-               
-               // update chart and widgets
-               {
-               gchar *daterange;
-                       data->toptotal = total;
-                       ui_panel_topspending_update(widget, data);
-                       
-                       daterange = filter_daterange_text_get(data->filter);
-                       gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
-                       g_free(daterange);
+                       hbfile_change_filepath(filename);
+                       ui_mainwindow_open_internal(widget, NULL);
                }
        }
-       
-       /* free our memory */
-       g_array_free (garray, TRUE);
  }
  
  
- /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
- /* scheduled */
- /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
- static Archive *
- ui_mainwindow_scheduled_get_selected_item(GtkTreeView *treeview)
+ /*
+  *    open the file stored in GLOBALS->xhb_filepath
+  */
+ void ui_mainwindow_open_internal(GtkWidget *widget, gpointer user_data)
  {
- GtkTreeSelection *treeselection;
- GtkTreeModel           *model;
- GtkTreeIter                    iter;
-       treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
-       if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
-       {
-       Archive *arc;
+ struct hbfile_data *data;
+ gint r;
  
-               gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPUPC_DATAS, &arc, -1);
-               return arc;
-       }
+       DB( g_print("\n[ui-mainwindow] open internal\n") );
  
-       return NULL;
- }
+       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
  
+       if( GLOBALS->xhb_filepath != NULL )
+       {
+               DB( g_print(" - filename: '%s'\n", GLOBALS->xhb_filepath) );
  
- static void ui_mainwindow_scheduled_onRowActivated (GtkTreeView        *treeview,
-                        GtkTreePath        *path,
-                        GtkTreeViewColumn  *col,
-                        gpointer            userdata)
- {
- //struct hbfile_data *data;
- Archive *arc;
+               ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
+               GLOBALS->hbfile_is_new = FALSE;
  
-       DB( g_print ("\n[ui-mainwindow] A scheduled row has been double-clicked!\n") );
+               r = homebank_load_xml(GLOBALS->xhb_filepath);
+               if( r == XML_OK )
+               {
+                       DB( g_print(" - file loaded ok : rcode=%d\n", r) );
  
-       //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(treeview, GTK_TYPE_WINDOW)), "inst_data");
+                       GLOBALS->xhb_timemodified = hbfile_file_get_time_modified(GLOBALS->xhb_filepath);
+                       hbfile_file_hasrevert(GLOBALS->xhb_filepath);
 -                      
 +
-       arc = ui_mainwindow_scheduled_get_selected_item(treeview);
-       ui_mainwindow_defarchive(arc);
- }
+                       if(PREFS->appendscheduled)
+                               scheduled_post_all_pending();
  
+                       if(PREFS->do_update_currency)
+                               ui_cur_manage_dialog_update_currencies(GTK_WINDOW(GLOBALS->mainwindow));
  
- static void ui_mainwindow_scheduled_do_post(Archive *arc, gboolean doedit, gpointer user_data)
- {
- struct hbfile_data *data = user_data;
- GtkWidget *window;
- gint result;
- Transaction *txn;
+                       homebank_lastopenedfiles_save();
  
-       window =  create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_ADD, TRUE);
+                       //todo: delete this after computing done at xml read
+                       account_compute_balances();
  
-       /* fill in the transaction */
-       txn = da_transaction_malloc();
-       da_transaction_init_from_template(txn, arc);
-       txn->date = scheduled_get_postdate(arc, arc->nextdate);
+                       ui_mainwindow_recent_add(data, GLOBALS->xhb_filepath);
+               }
+               else
+               {
+               gchar *msg = _("Unknown error");
  
-       deftransaction_set_transaction(window, txn);
+                       switch(r)
+                       {
+                               case XML_IO_ERROR:
+                                       msg = _("I/O error for file '%s'.");
+                                       break;
+                               case XML_FILE_ERROR:
+                                       msg = _("The file '%s' is not a valid HomeBank file.");
+                                       break;
 -                              case XML_VERSION_ERROR: 
++                              case XML_VERSION_ERROR:
+                                       msg = _("The file '%s' was saved with a higher version of HomeBank\nand cannot be loaded by the current version.");
+                                       break;
+                       }
  
-       result = gtk_dialog_run (GTK_DIALOG (window));
+                       ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
+                               _("File error"),
+                               msg,
+                               GLOBALS->xhb_filepath
+                               );
  
-       DB( g_print(" - dialog result is %d\n", result) );
+                       ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
  
-       if(result == GTK_RESPONSE_ADD || result == GTK_RESPONSE_ACCEPT)
-       {
-               deftransaction_get(window, NULL);
-               transaction_add(txn);
-               GLOBALS->changes_count++;
+               }
  
-               scheduled_date_advance(arc);
+               ui_hub_account_populate(GLOBALS->mainwindow, NULL);
+               ui_hub_scheduled_populate(GLOBALS->mainwindow, NULL);
+               ui_hub_spending_populate(GLOBALS->mainwindow, NULL);
+               ui_hub_transaction_populate(data);
 -      
 +
-               DB( g_print(" - added 1 transaction to %d\n", txn->kacc) );
+               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
        }
  
-       da_transaction_free(txn);
-       deftransaction_dispose(window, NULL);
-       gtk_widget_destroy (window);
  
  }
  
  
- static void ui_mainwindow_scheduled_editpost_cb(GtkWidget *widget, gpointer user_data)
+ /*
+ **
+ */
+ void ui_mainwindow_save(GtkWidget *widget, gpointer user_data)
  {
- struct hbfile_data *data = user_data;
-       Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
-       if( (arc != NULL) )
-       {
-               ui_mainwindow_scheduled_do_post(arc, TRUE, data);
-               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
-       }
- }
+ struct hbfile_data *data;
+ gboolean saveas = GPOINTER_TO_INT(user_data);
+ gchar *filename = NULL;
+ gint r = XML_UNSET;
  
+       DB( g_print("\n[ui-mainwindow] save\n") );
  
- static void ui_mainwindow_scheduled_post_cb(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data = user_data;
+       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
  
-       DB( g_print("\n[ui-mainwindow] scheduled post\n") );
+       if( GLOBALS->hbfile_is_new == TRUE )
+               saveas = 1;
  
-       Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
+       //#1710955 test for backup open
+       if( GLOBALS->hbfile_is_bak == TRUE )
+       {
+               //todo: later for backup, should also remove datetime and .bak
+               hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
+               saveas = 1;
+       }
  
-       if( (arc != NULL) )
+       if(saveas == 1)
        {
-               if( scheduled_is_postable(arc) )
+               if(ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_SAVE, &filename, FALSE) == TRUE)
                {
-               Transaction *txn = da_transaction_malloc ();
-                       da_transaction_init_from_template(txn, arc);
-                       txn->date = scheduled_get_postdate(arc, arc->nextdate);
-                       transaction_add(txn);
-                       GLOBALS->changes_count++;
-                       scheduled_date_advance(arc);
-                       da_transaction_free (txn);
+                       DB( g_print(" + should save as '%s'\n", filename) );
+                       homebank_file_ensure_xhb(filename);
+                       homebank_backup_current_file();
+                       r = homebank_save_xml(GLOBALS->xhb_filepath);
+                       GLOBALS->hbfile_is_new = FALSE;
+                       GLOBALS->hbfile_is_bak = FALSE;
                }
                else
-               {
-                       ui_mainwindow_scheduled_do_post(arc, FALSE, data);
-               }
-               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
+                       return;
        }
- }
+       else
+       {
+       guint64 time_modified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
+       gint result = GTK_RESPONSE_OK;
  
+               DB( g_print(" + should quick save '%s'\n + time: open=%lu :: now=%lu\n", GLOBALS->xhb_filepath, GLOBALS->xhb_timemodified, time_modified) );
  
- static void ui_mainwindow_scheduled_skip_cb(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data = user_data;
-       Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
-       if( (arc != NULL) && (arc->flags & OF_AUTO) )
-       {
-               GLOBALS->changes_count++;
-               scheduled_date_advance(arc);
-               ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
-               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
-       }
- }
- static void ui_mainwindow_scheduled_update(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
- //gint filter;
-       DB( g_print("\n[ui-mainwindow] scheduled update\n") );
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       //filter = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_sched_filter));
-       Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
-       if(arc)
-       {
-               DB( g_print("archive is %s\n", arc->memo) );
-               
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), TRUE);
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), TRUE);
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), TRUE);
-       }
-       else
-       {
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), FALSE);
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), FALSE);
-               gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), FALSE);
-       }
- }
- static void ui_mainwindow_scheduled_selection_cb(GtkTreeSelection *treeselection, gpointer user_data)
- {
-       
-       ui_mainwindow_scheduled_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
- }
- /*
- ** called after load, importamiga, on demand
- */
- void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data)
- {
- //struct hbfile_data *data;
- gint count;
- gint usermode = GPOINTER_TO_INT(user_data);
-       DB( g_print("\n[ui-mainwindow] check scheduled\n") );
-       //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       count = scheduled_post_all_pending();
-       //inform the user
-       if(usermode == TRUE)
-       {
-       gchar *txt;
-               //#125534
-               if( count > 0 )
-               {
-                       ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_REFRESHALL));
-               }
-               
-               if(count == 0)
-                       txt = _("No transaction to add");
-               else
-                       txt = _("transaction added: %d");
-               ui_dialog_msg_infoerror(GTK_WINDOW(GLOBALS->mainwindow), GTK_MESSAGE_INFO,
-                       _("Check scheduled transactions result"),
-                       txt,
-                       count);
-       }
- }
- static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
- GtkTreeModel *model;
- GtkTreeIter  iter;
- GList *list;
- gdouble totexp = 0;
- gdouble totinc = 0;
- gint count = 0;
- gchar buffer[256];
- guint32 maxpostdate;
- GDate *date;
- //Account *acc;
-       DB( g_print("\n[ui-mainwindow] scheduled populate list\n") );
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
-       gtk_list_store_clear (GTK_LIST_STORE(model));
-       homebank_app_date_get_julian();
-       maxpostdate = scheduled_date_get_post_max();
-       date = g_date_new_julian (maxpostdate);
-       g_date_strftime (buffer, 256-1, PREFS->date_format, date);
-       g_date_free(date);
-       gtk_label_set_text(GTK_LABEL(data->LB_maxpostdate), buffer);
-       
-       list = g_list_first(GLOBALS->arc_list);
-       while (list != NULL)
-       {
-       Archive *arc = list->data;
-       Account *acc;
-       gdouble inc, exp;
-       guint nbdays, nblate;
-               if((arc->flags & OF_AUTO) ) //&& arc->kacc > 0)
-               {
-                       count++;
-                       nbdays = arc->nextdate - maxpostdate;
-                       nblate = scheduled_get_latepost_count(arc, GLOBALS->today);
-                       
-                       DB( g_print(" - append '%s' : %d\n", arc->memo, nbdays) );
-                       if(arc->flags & OF_INCOME)
-                       {
-                               inc = arc->amount;
-                               exp = 0.0;
-                       }
-                       else
-                       {
-                               exp = arc->amount;
-                               inc = 0.0;
-                       }
-                       /* insert normal txn */
-                       acc = da_acc_get(arc->kacc);
-                       if( acc)
-                       {
-                               totinc += hb_amount_base(inc, acc->kcur);
-                               totexp += hb_amount_base(exp, acc->kcur);
-                       }
-                       gtk_list_store_append (GTK_LIST_STORE(model), &iter);
-                       gtk_list_store_set (GTK_LIST_STORE(model), &iter,
-                                 LST_DSPUPC_DATAS, arc,
-                                 LST_DSPUPC_ACCOUNT, acc,
-                             LST_DSPUPC_MEMO, arc->memo,
-                             LST_DSPUPC_EXPENSE, exp,
-                             LST_DSPUPC_INCOME, inc,
-                                 LST_DSPUPC_REMAINING, nbdays,
-                             LST_DSPUPC_NB_LATE, nblate,
-                                 -1);
-                       /* insert internal xfer txn : 1378836 */
-                       if(arc->paymode == PAYMODE_INTXFER)
-                       {
-                               acc = da_acc_get(arc->kxferacc);
-                               if( acc)
-                               {
-                                       totinc += hb_amount_base(-inc, acc->kcur);
-                                       totexp += hb_amount_base(-exp, acc->kcur);
-                               }
-                               gtk_list_store_append (GTK_LIST_STORE(model), &iter);
-                               gtk_list_store_set (GTK_LIST_STORE(model), &iter,
-                                         LST_DSPUPC_DATAS, arc,
-                                         LST_DSPUPC_ACCOUNT, acc,
-                                         LST_DSPUPC_MEMO, arc->memo,
-                                         LST_DSPUPC_EXPENSE, -inc,
-                                         LST_DSPUPC_INCOME, -exp,
-                                         LST_DSPUPC_REMAINING, nbdays,
-                                         LST_DSPUPC_NB_LATE, nblate,
-                                         -1);
-                       }
-               }
-               list = g_list_next(list);
-       }
-       // insert total
-       if(count > 0 )
-       {
-               gtk_list_store_append (GTK_LIST_STORE(model), &iter);
-               gtk_list_store_set (GTK_LIST_STORE(model), &iter,
-                         LST_DSPUPC_DATAS, NULL,
-                         LST_DSPUPC_ACCOUNT, NULL,
-                         LST_DSPUPC_MEMO, _("Total"),
-                         LST_DSPUPC_EXPENSE, totexp,
-                     LST_DSPUPC_INCOME, totinc,
-                 -1);
-       }
-       ui_mainwindow_scheduled_update(widget, NULL);
-       
- }
- gboolean ui_mainwindow_open_backup_check_confirm(gchar *filepath)
- {
- gboolean retval = FALSE;
- gchar *basename, *secondtext;
- gboolean result;
-       basename = g_path_get_basename(filepath);
-       secondtext = g_strdup_printf (
-       _("Your are about to open the backup file '%s'.\n\nAre you sure you want to do this ?"), basename);             
-       result = ui_dialog_msg_confirm_alert(
-               GTK_WINDOW(GLOBALS->mainwindow),
-               _("Open a backup file ?"),
-               secondtext,
-               _("_Open backup")
-       );      
-       g_free(secondtext);
-       g_free(basename);
-       if( result == GTK_RESPONSE_OK )
-               retval = TRUE;
-       
-       return retval;
- }
- /*
- **
- */
- void ui_mainwindow_open(GtkWidget *widget, gpointer user_data)
- {
- //struct hbfile_data *data;
- gchar *filename = NULL;
-       DB( g_print("\n[ui-mainwindow] open\n") );
-       //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
-       {
-               if( ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_OPEN, &filename) == TRUE )
-               {
-                       //#1710955 test for backup open
-                       if( hbfile_file_isbackup(filename) )
-                       {
-                               if( ui_mainwindow_open_backup_check_confirm(filename) == TRUE )
-                               {
-                                       GLOBALS->hbfile_is_bak = TRUE;
-                               }
-                               else
-                               {
-                                       g_free(filename);
-                                       return;
-                               }
-                       }                       
-                       hbfile_change_filepath(filename);
-                       ui_mainwindow_open_internal(widget, NULL);
-               }
-       }
- }
- /*
-  *    open the file stored in GLOBALS->xhb_filepath
-  */
- void ui_mainwindow_open_internal(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
- gint r;
-       DB( g_print("\n[ui-mainwindow] open internal\n") );
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       if( GLOBALS->xhb_filepath != NULL )
-       {
-               DB( g_print(" - filename: '%s'\n", GLOBALS->xhb_filepath) );
-               ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
-               GLOBALS->hbfile_is_new = FALSE;
-               r = homebank_load_xml(GLOBALS->xhb_filepath);
-               if( r == XML_OK )
-               {
-                       DB( g_print(" - file loaded ok : rcode=%d\n", r) );
-                       
-                       hbfile_file_hasbackup(GLOBALS->xhb_filepath);
-                       
-                       if(PREFS->appendscheduled)
-                               scheduled_post_all_pending();
-                       if(PREFS->do_update_currency)
-                               ui_cur_manage_dialog_update_currencies(GTK_WINDOW(GLOBALS->mainwindow));
-                       homebank_lastopenedfiles_save();
-                       //todo: delete this after computing done at xml read
-                       account_compute_balances();
-                       ui_mainwindow_recent_add(data, GLOBALS->xhb_filepath);
-               }
-               else
-               {
-               gchar *msg = _("Unknow error");
-                       switch(r)
-                       {
-                               case XML_IO_ERROR:
-                                       msg = _("I/O error for file '%s'.");
-                                       break;
-                               case XML_FILE_ERROR:
-                                       msg = _("The file '%s' is not a valid HomeBank file.");
-                                       break;
-                               case XML_VERSION_ERROR: 
-                                       msg = _("The file '%s' was saved with a higher version of HomeBank\nand cannot be loaded by the current version.");
-                                       break;
-                       }
-                       ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
-                               _("File error"),
-                               msg,
-                               GLOBALS->xhb_filepath
-                               );
-                       ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
-               }
-               ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
-               ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
-               ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
-               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
-       }
- }
- /*
- **
- */
- void ui_mainwindow_save(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
- gboolean saveas = GPOINTER_TO_INT(user_data);
- gchar *filename = NULL;
- gint r = XML_UNSET;
-       DB( g_print("\n[ui-mainwindow] save\n") );
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       if( GLOBALS->hbfile_is_new == TRUE )
-               saveas = 1;
-       //#1710955 test for backup open
-       if( GLOBALS->hbfile_is_bak == TRUE )
-       {
-               //todo: later for backup, should also remove datetime and .bak
-               hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
-               saveas = 1;
-       }
-       if(saveas == 1)
-       {
-               if(ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_SAVE, &filename) == TRUE)
-               {
-                       DB( g_print(" + should save as '%s'\n", filename) );
-                       homebank_file_ensure_xhb(filename);
-                       homebank_backup_current_file();
-                       r = homebank_save_xml(GLOBALS->xhb_filepath);
-                       GLOBALS->hbfile_is_new = FALSE;
-                       GLOBALS->hbfile_is_bak = FALSE;
-               }
-               else
-                       return;
-       }
-       else
-       {
-               DB( g_print(" + should quick save %s\n", GLOBALS->xhb_filepath) );
-               homebank_file_ensure_xhb(NULL);
-               homebank_backup_current_file();
-               r = homebank_save_xml(GLOBALS->xhb_filepath);
-       }
-       if(r == XML_OK)
-       {
-               GLOBALS->changes_count = 0;
-               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
-       }
-       else
-       {
-       gchar *msg = _("I/O error for file '%s'.");
-               ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
-                       _("File error"),
-                       msg,
-                       GLOBALS->xhb_filepath
-                       );
-       }
- }
- static void ui_panel_accounts_expand_all(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
- }
- static void ui_panel_accounts_collapse_all(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_acc));
- }
- static GHashTable *ui_panel_accounts_groups_get(GList *lacc, gint groupby, gboolean showall)
- {
- GHashTable *hash;
- GList *elt;
- gchar *groupname;
- gint nballoc;
-       DB( g_print("\n[ui-mainwindow] accounts_groups_get\n") );
-       nballoc = da_acc_length ();
-       
-       DB( g_print(" %d accounts\n", nballoc) );
-       
-       hash = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
-       elt = g_list_first(lacc);
-       while (elt != NULL)
-       {
-       Account *acc = elt->data;
-       GPtrArray *group;
-       
-               //#1674045 ony rely on nosummary
-               //if( showall || !(acc->flags & (AF_CLOSED|AF_NOSUMMARY)) )
-               if( showall || !(acc->flags & AF_NOSUMMARY) )
-               {
-                       if( groupby == DSPACC_GROUP_BY_BANK )
-                       {
-                               groupname = _("(no institution)");
-                               if( (acc->bankname != NULL) && strlen(acc->bankname) > 0 ) 
-                                       groupname = acc->bankname;
-                       }
-                       else
-                       {
-                               //pre 5.1.3 historical by type display
-                               groupname = _(CYA_ACC_TYPE[acc->type]);
-                       }
-                       
-                       if( g_hash_table_contains(hash, groupname) == FALSE )
-                       {
-                               g_hash_table_insert(hash, g_strdup(groupname), g_ptr_array_sized_new(nballoc) );
-                               //DB( g_print(" - type hash insert '%s' = %d\n", groupname, inserted) );
-                       }
-                       group = g_hash_table_lookup(hash, groupname);
-                       if( group != NULL )
-                       {
-                               g_ptr_array_add(group, (gpointer)acc);
-                               DB( g_print(" -- add '%s' to group '%s'\n", acc->name, groupname) );
-                       }
-               }
-               elt = g_list_next(elt);
-       }
-       return hash;
- }
- void ui_mainwindow_populate_accounts(GtkWidget *widget, gpointer user_data)
- {
- struct hbfile_data *data;
- GtkTreeModel *model;
- GtkTreeIter  iter1, child_iter;
- GList *lacc, *elt;
- Account *acc;
- guint j, nbtype;
- gdouble gtbank, gttoday, gtfuture;
- GHashTable *h_group;
- GHashTableIter grp_iter;
- gpointer key, value;
-       DB( g_print("\n[ui-mainwindow] populate accounts\n") );
-       data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
-       /* here we create a count and a list of every account pointer by type */
-       lacc = elt = g_hash_table_get_values(GLOBALS->h_acc);
-       
-       h_group = ui_panel_accounts_groups_get(lacc, PREFS->pnl_acc_show_by, data->showall);
-       g_list_free(lacc);
-       gtbank = gttoday = gtfuture = 0;
-       DB( g_print(" - populate listview, %d group(s)\n", g_hash_table_size(h_group)) );
-       model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
-       gtk_tree_store_clear (GTK_TREE_STORE(model));
-       nbtype = 0;
-       g_hash_table_iter_init (&grp_iter, h_group);
-       while (g_hash_table_iter_next (&grp_iter, &key, &value))
-       {
-       GPtrArray *gpa = value;
-       gdouble tbank, ttoday, tfuture;
-       gint position;
-               if(gpa != NULL)
+               if( GLOBALS->xhb_timemodified != time_modified )
                {
-                       nbtype++;
-                       //1: Header: Bank, Cash, ...
-                       DB( g_print(" - add group '%s'\n", (gchar *)key) );
-                       //#1663399 keep type position like in dropdown
-                       position = 0;
-                       if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE )
-                       {
-                       gint t = 0;     
-                               while(CYA_ACC_TYPE[t] != NULL && t < 15)
-                               {
-                                       if( !strcmp(CYA_ACC_TYPE[t], key) )
-                                               break;
-                                       t++;
-                               }
-                               position = t;
-                       }
-                       gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
-                       gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
-                                         LST_DSPACC_POS, position,
-                                         LST_DSPACC_DATATYPE, DSPACC_TYPE_HEADER,
-                                         LST_DSPACC_NAME, key,
-                                         -1);
-                       tbank = ttoday = tfuture = 0;
-                       //2: Accounts for real
-                       for(j=0;j<gpa->len;j++)
-                       {
-                               acc = g_ptr_array_index(gpa, j);
-                               //tbank += acc->bal_bank;
-                               //ttoday += acc->bal_today;
-                               //tfuture += acc->bal_future;
-                               tbank += hb_amount_base(acc->bal_bank, acc->kcur);
-                               ttoday += hb_amount_base(acc->bal_today, acc->kcur);
-                               tfuture += hb_amount_base(acc->bal_future, acc->kcur);
-                               DB( g_print(" - add account '%s' :: %.2f %.2f %.2f\n", acc->name, acc->bal_bank, acc->bal_today, acc->bal_future) );
-                               gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
-                               gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
-                                               LST_DSPACC_DATAS, acc,
-                                               LST_DSPACC_DATATYPE, DSPACC_TYPE_NORMAL,
-                                               LST_DSPACC_BANK, acc->bal_bank,
-                                               LST_DSPACC_TODAY, acc->bal_today,
-                                               LST_DSPACC_FUTURE, acc->bal_future,
-                                         -1);
-                       }
-                       if(gpa->len > 1)
-                       {
-                               DB( g_print(" - group total :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
-                               // insert the total line
-                               gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
-                               gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
-                                               LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
-                                               LST_DSPACC_NAME, _("Total"),
-                                               LST_DSPACC_BANK, tbank,
-                                               LST_DSPACC_TODAY, ttoday,
-                                               LST_DSPACC_FUTURE, tfuture,
-                                                 -1);
-                       }
-                       /* set balance to header to display when collasped */
-                       DB( g_print(" - enrich group total header :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
-                       gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
-                                       LST_DSPACC_BANK, tbank,
-                                       LST_DSPACC_TODAY, ttoday,
-                                       LST_DSPACC_FUTURE, tfuture,
-                                         -1);
-                       /* add to grand total */
-                       gtbank += tbank;
-                       gttoday += ttoday;
-                       gtfuture += tfuture;
+                       result = ui_dialog_msg_confirm_alert(
+                                       GTK_WINDOW(GLOBALS->mainwindow),
+                                       _("The file has been modified since reading it."),
+                                       _("If you save it, all the external changes could be lost. Save it anyway?"),
+                                       _("S_ave Anyway")
+                               );
 -                              
 +
+                       if( result != GTK_RESPONSE_OK )
+                               return;
                }
  
+               DB( g_print(" + saving...\n") );
+               homebank_file_ensure_xhb(NULL);
+               homebank_backup_current_file();
+               r = homebank_save_xml(GLOBALS->xhb_filepath);
        }
  
-       DB( g_print(" - grand total :: %.2f %.2f %.2f\n", gtbank, gttoday, gtfuture) );
-       // Grand total
-       if( nbtype > 1 )
+       if(r == XML_OK)
        {
-               gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
-               gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
-                                       LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
-                                       LST_DSPACC_NAME, _("Grand total"),
-                                       LST_DSPACC_BANK, gtbank,
-                                       LST_DSPACC_TODAY, gttoday,
-                                       LST_DSPACC_FUTURE, gtfuture,
-                                 -1);
+               DB( g_print(" + OK...\n") );
+               GLOBALS->changes_count = 0;
+               GLOBALS->xhb_timemodified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
+               ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
        }
-       gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
-       DB( g_print(" - free ressources\n") );
-       g_hash_table_iter_init (&grp_iter, h_group);
-       while (g_hash_table_iter_next (&grp_iter, &key, &value))
+       else
        {
-               g_ptr_array_free (value, TRUE);
-       }
-       g_hash_table_destroy (h_group);  
+       gchar *msg = _("I/O error for file '%s'.");
  
+               ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
+                       _("File error"),
+                       msg,
+                       GLOBALS->xhb_filepath
+                       );
+       }
  }
  
  
@@@ -2262,8 -1457,12 +1484,12 @@@ gint flags
  
                changed = (GLOBALS->changes_count > 0) ? "*" : "";
  
+ #if MYDEBUG == 1
+               data->wintitle = g_strdup_printf("%s%s (%d)- %s - " PROGNAME, changed, basename, GLOBALS->changes_count, GLOBALS->owner);
+ #else
                data->wintitle = g_strdup_printf("%s%s - %s - " PROGNAME, changed, basename, GLOBALS->owner);
 -              
+ #endif
 +
            gtk_window_set_title (GTK_WINDOW (gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), data->wintitle);
  
                g_free(basename);
                        gtk_widget_hide(GTK_WIDGET(data->GR_top));
  
  
--              
++
                DB( g_print(" - show upcoming=%d\n", PREFS->wal_upcoming) );
                if(PREFS->wal_upcoming)
                        gtk_widget_show(GTK_WIDGET(data->GR_upc));
@@@ -2502,9 -1690,12 +1720,12 @@@ gboolean retval = FALSE
  
        PREFS->wal_vpaned = gtk_paned_get_position(GTK_PANED(data->vpaned));
        PREFS->wal_hpaned = gtk_paned_get_position(GTK_PANED(data->hpaned));
        DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
  
 -      
+       if(PREFS->pnl_list_tab)
+               g_free(PREFS->pnl_list_tab);
+       PREFS->pnl_list_tab = g_strdup(gtk_stack_get_visible_child_name(GTK_STACK(data->stack)));
++
        //todo
        if(ui_dialog_msg_savechanges(widget, NULL) == FALSE)
        {
                gtk_widget_destroy(data->LV_top);
  
                g_free(data->wintitle);
-               da_filter_free(data->filter);
+               da_flt_free(data->filter);
                g_free(user_data);
--              
++
                gtk_main_quit();
        }
  
@@@ -2656,8 -1822,9 +1852,9 @@@ static void ui_mainwindow_drag_data_rec
  {
  gchar **uris, **str;
  gchar *newseldata;
- gint filetype, slen;
+ gint n_uris, filetype, slen;
+ GError *error = NULL;
 -      
 +
        if (info != TARGET_URI_LIST)
                return;
  
        newseldata = g_new (gchar, slen + 1);
        memcpy (newseldata, gtk_selection_data_get_data(selection_data), slen);
        newseldata[slen] = 0;
 -      
+       //DB( g_print(" - seldata ='%s'\n", gtk_selection_data_get_data(selection_data) ) );
+       //DB( g_print(" - newseldata ='%s'\n", newseldata ) );
 +
        uris = g_uri_list_extract_uris (newseldata);
+       n_uris = g_strv_length(uris);
+       DB( g_print(" - dragged %d files (len=%d)\n", n_uris, slen ) );
  
-       DB( g_print(" - dragged %d %d files\n", slen, g_strv_length(uris) ) );
+       g_free(newseldata);
  
-       str = uris;
-       //for (str = uris; *str; str++)
-       if( *str )
+       //single file: check for xhb
+       if(n_uris == 1)
 -      {               
 +      {
-               GError *error = NULL;
-               gchar *path = g_filename_from_uri (*str, NULL, &error);
+               filetype = hb_filename_type_get_by_extension(*uris);
  
-               if (path)
-               {
-                       filetype = homebank_alienfile_recognize(path);
+               DB( g_print(" - filetype is homebank (%d)\n", filetype) );
  
-                       DB( g_print(" - dragged %s, type is %d\n", path, filetype ) );
+               if( filetype == FILETYPE_HOMEBANK )
+               {
+               gchar *path = g_filename_from_uri (*uris, NULL, &error);
  
-                       if( filetype == FILETYPE_HOMEBANK)
+                       if( path != NULL )
                        {
+                               DB( g_print(" - path is '%s'\n", path) );
                                hbfile_change_filepath(g_strdup(path));
                                ui_mainwindow_open_internal(GTK_WIDGET(window), NULL);
+                               goto end_drop;
                        }
                        else
                        {
-                               //todo: future here to implement import for other filetype
-                               //      ui_import_assistant_new();
-                               // + write a method into assistant to catch other filename
-                               ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_ERROR,
+                               g_warning ("Could not convert uri to local path: %s", error->message);
+                               g_error_free (error);
+                       }
+                       g_free (path);
+               }
+               /* we no more manage error here
+               ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_ERROR,
                                        _("File error"),
                                        _("The file %s is not a valid HomeBank file."),
-                                       path
-                                       );
+                                       path);
+               */
+       }
  
-                       }
+       //collect known filetype to import
+       DB( g_print(" - collect %d files\n", n_uris) );
 -      
 +
-               }
-               else
+       gchar **paths = g_new (gchar *, n_uris + 1);
+       slen = 0;
+       for (str = uris; *str; str++)
+       {
+               filetype = hb_filename_type_get_by_extension(*str);
+               if( filetype != FILETYPE_HOMEBANK && filetype != FILETYPE_UNKNOWN )
                {
-                       g_warning ("Could not convert uri to local path: %s", error->message);
+               gchar *path = g_filename_from_uri (*str, NULL, NULL);
  
-                       g_error_free (error);
+                       if( path != NULL )
+                       {
+                               DB( g_print(" - append %d '%s'\n", slen, path ) );
+                               paths[slen++] = path;
+                       }
 -              }   
 +              }
-               g_free (path);
        }
 -      
 -      
+       paths[slen] = NULL;
+       if( slen > 0 )
+       {
+               ui_import_assistant_new( paths );
+       }
++
++
+ end_drop:
        g_strfreev (uris);
-       
-       g_free(newseldata);
  }
  
  
@@@ -3246,7 -2105,7 +2135,7 @@@ GtkWidget *bar, *label
        gtk_label_set_markup (GTK_LABEL(label), "Unstable Development Version");
        gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label, FALSE, FALSE, 0);
  #endif
--      
++
        /* Add the main area */
        vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
      //gtk_container_set_border_width (GTK_CONTAINER(vbox), SPACING_MEDIUM);
                //gtk_widget_set_size_request (widget, -1, 100);
                gtk_paned_pack2 (GTK_PANED(hpaned), widget, TRUE, FALSE);
  
 -      
+       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       data->GR_upc = box;
+       gtk_paned_pack2 (GTK_PANED(vpaned), box, TRUE, FALSE);
+       sidebar = gtk_stack_sidebar_new ();
+       gtk_box_pack_start (GTK_BOX (box), sidebar, FALSE, FALSE, 0);
+       stack = gtk_stack_new ();
+       //gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+       gtk_stack_sidebar_set_stack (GTK_STACK_SIDEBAR (sidebar), GTK_STACK (stack));
+       data->stack = stack;
+     gtk_box_pack_start (GTK_BOX (box), stack, TRUE, TRUE, 0);
 -      
++
+               page = ui_hub_scheduled_create(data);
+               gtk_stack_add_titled (GTK_STACK (stack), page, "sched", _("Scheduled"));
+               //gtk_paned_pack2 (GTK_PANED(vpaned), widget, TRUE, FALSE);
+               page = ui_hub_transaction_create(data, HUB_TXN_TYPE_FUTURE);
+               gtk_stack_add_titled (GTK_STACK (stack), page, "futur", _("Future"));
+               page = ui_hub_transaction_create(data, HUB_TXN_TYPE_REMIND);
+               gtk_stack_add_titled (GTK_STACK (stack), page, "remin", _("Remind"));
++
  
        //setup, init and show window
        wg = &PREFS->wal_wg;
        if(PREFS->wal_vpaned > 0)
                gtk_paned_set_position(GTK_PANED(data->vpaned), PREFS->wal_vpaned);
  
 -      
+       if( PREFS->pnl_list_tab != NULL )
+               gtk_stack_set_visible_child_name (GTK_STACK(data->stack), PREFS->pnl_list_tab);
++
        //todo: move this elsewhere
        DB( g_print(" - setup stuff\n") );
  
diff --cc src/ext-perl.xs
index 4890db85a56e2c7cd4b91548dc5009f6fa083ada,0000000000000000000000000000000000000000..c8e1f25b5a4f82e4435cbac1658583c4e2ac2bff
mode 100644,000000..100644
--- /dev/null
@@@ -1,1043 -1,0 +1,1043 @@@
- #include "dsp_mainwindow.h"
- #include "dsp_account.h"
 +
 +#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;
-               RETVAL = G_OBJECT(register_panel_window_new(SELF->key, SELF));
++#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* acc_list = g_hash_table_get_values(GLOBALS->h_acc);
 +              GList* acc_link = g_list_first(acc_list);
 +              for (; acc_link; acc_link = g_list_next(acc_link)) {
 +                      Account *acc = acc_link->data;
 +
 +                      GList* txn_link = g_queue_peek_head_link(acc->txn_queue);
 +                      for (; txn_link; txn_link = g_list_next(txn_link)) {
 +                              Transaction* txn = txn_link->data;
 +
 +                              GValue val = G_VALUE_INIT;
 +                              SV* sv = val_to_sv(EXT_TRANSACTION(&val, txn));
 +                              mXPUSHs(sv);
 +                      }
 +              }
 +
 +              g_list_free(acc_list);
 +
 +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
 +
 +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_queue_peek_head_link(SELF->txn_queue);
 +              for (; list; list = g_list_next(list)) {
 +                      Transaction* txn = list->data;
 +                      GValue val = G_VALUE_INIT;
 +                      SV* sv = val_to_sv(EXT_TRANSACTION(&val, txn));
 +                      mXPUSHs(sv);
 +              }
 +
 +GObject*
 +open(Account* SELF)
 +      CODE:
-               RETVAL = G_OBJECT(create_deftransaction_window(NULL, TRANSACTION_EDIT_MODIFY, FALSE));
++              RETVAL = G_OBJECT(register_panel_window_new(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*
 +memo(Transaction* SELF, ...)
 +      CODE:
 +              if (1 < items) {
 +                      if (SELF->memo) g_free(SELF->memo);
 +                      SELF->memo = g_strdup(SvGchar_ptr(ST(1)));
 +              }
 +              RETVAL = SELF->memo ? SELF->memo : "";
 +      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:
-                       other = ui_dialog_transaction_xfer_select_child(SELF, list);
++              RETVAL = G_OBJECT(create_deftransaction_window(NULL, TRANSACTION_EDIT_MODIFY, FALSE, 0));
 +              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);
 +                      }
++                      ui_dialog_transaction_xfer_select_child(NULL, SELF, list, &other);
 +              }
 +              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->memo, SELF->date, SELF->kacc, SELF->kxferacc, SELF->flags, SELF->paymode, SELF->kpay, SELF->kcat);
 +
index ee5055f954374c75a4f6ecfbd4f6e2c32b5ddc87,b6888c9144f2838ca7f9eb1886d3f6643511fa53..2ecbb6f93c5aa42ba6a1f14b5d8eff39c458328e
@@@ -51,10 -47,10 +50,10 @@@ da_acc_free(Account *item
                g_free(item->number);
                g_free(item->bankname);
                g_free(item->notes);
--              
++
                g_queue_free (item->txn_queue);
--              
 -              g_free(item);
++
 +              rc_free(item);
        }
  }
  
@@@ -65,7 -61,8 +64,8 @@@ da_acc_malloc(void
  Account *item;
  
        DB( g_print("da_acc_malloc\n") );
 -      item = g_malloc0(sizeof(Account));
 +      item = rc_alloc(sizeof(Account));
+       item->kcur = GLOBALS->kcur;
        item->txn_queue = g_queue_new ();
        return item;
  }
@@@ -182,28 -177,13 +183,17 @@@ Account *existitem
  
        DB( g_print("da_acc_append\n") );
  
-       /* ensure no duplicate */
-       g_strstrip(item->name);
-       if(item->name != NULL)
+       existitem = da_acc_get_by_name( item->name );
+       if( existitem == NULL )
        {
-               existitem = da_acc_get_by_name( item->name );
-               if( existitem == NULL )
-               {
-                       new_key = g_new0(guint32, 1);
-                       *new_key = da_acc_get_max_key() + 1;
-                       item->key = *new_key;
-                       item->pos = da_acc_length() + 1;
+               item->key = da_acc_get_max_key() + 1;
+               item->pos = da_acc_length() + 1;
+               da_acc_insert(item);
 +
-                       DB( g_print(" -> insert id: %d\n", *new_key) );
++              GValue item_val = G_VALUE_INIT;
++              ext_hook("account_inserted", EXT_ACCOUNT(&item_val, item), NULL);
 +
-                       g_hash_table_insert(GLOBALS->h_acc, new_key, item);
-                       GValue item_val = G_VALUE_INIT;
-                       ext_hook("account_inserted", EXT_ACCOUNT(&item_val, item), NULL);
-                       return TRUE;
-               }
+               return TRUE;
        }
  
        DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
@@@ -240,19 -211,24 +221,24 @@@ static gboolean da_acc_name_grfunc(gpoi
   *
   */
  Account *
- da_acc_get_by_name(gchar *name)
+ da_acc_get_by_name(gchar *rawname)
  {
+ Account *retval = NULL;
+ gchar *stripname;
        DB( g_print("da_acc_get_by_name\n") );
  
-       return g_hash_table_find(GLOBALS->h_acc, (GHRFunc)da_acc_name_grfunc, name);
- }
+       if( rawname )
+       {
+               stripname = g_strdup(rawname);
+               g_strstrip(stripname);
+               if( strlen(stripname) > 0 )
+                       retval = g_hash_table_find(GLOBALS->h_acc, (GHRFunc)da_acc_name_grfunc, stripname);
  
- Account *
- da_acc_get_by_imp_name(gchar *name)
- {
-       DB( g_print("da_acc_get_by_imp_name\n") );
+               g_free(stripname);
+       }
  
-       return g_hash_table_find(GLOBALS->h_acc, (GHRFunc)da_acc_imp_name_grfunc, name);
 -      return retval; 
++      return retval;
  }
  
  
@@@ -369,14 -345,14 +355,14 @@@ gboolean retval
        while (lnk_acc != NULL)
        {
        Account *acc = lnk_acc->data;
--      
++
                if(acc->key != key)
                {
                        lnk_txn = g_queue_peek_head_link(acc->txn_queue);
                        while (lnk_txn != NULL)
                        {
                        Transaction *entry = lnk_txn->data;
--                      
++
                                if( key == entry->kxferacc)
                                {
                                        retval = TRUE;
@@@ -439,21 -415,24 +425,24 @@@ account_rename(Account *item, gchar *ne
  Account *existitem;
  gchar *stripname = account_get_stripname(newname);
  
-       existitem = da_acc_get_by_name(stripname);
-       if( existitem == NULL )
+       if( strlen(stripname) > 0 )
        {
-               g_free(item->name);
-               item->name = g_strdup(stripname);
-               return TRUE;
-       }
+               existitem = da_acc_get_by_name(stripname);
+               if( existitem == NULL )
+               {
+                       g_free(item->name);
+                       item->name = g_strdup(stripname);
+                       return TRUE;
+               }
  
-       g_free(stripname);
+               g_free(stripname);
+       }
 -      
 +
        return FALSE;
  }
  
  
--/* 
++/*
   * change the account currency
   * change every txn to currency
   * ensure dst xfer transaction account will be set to same currency
@@@ -494,7 -473,7 +483,7 @@@ guint32 maxkey, i
  
        acc->kcur = kcur;
        DB( g_print(" - '%s'\n", acc->name) );
--      
++
        for(i=1;i<maxkey;i++)
        {
                DB( g_print(" - %d '%d'\n", i, xfer_list[i]) );
@@@ -589,25 -568,25 +578,25 @@@ GList *lnk_txn
        while (lnk_acc != NULL)
        {
        Account *acc = lnk_acc->data;
--      
++
                /* set initial amount */
                acc->bal_bank = acc->initial;
                acc->bal_today = acc->initial;
                acc->bal_future = acc->initial;
--              
++
                /* add every txn */
                lnk_txn = g_queue_peek_head_link(acc->txn_queue);
                while (lnk_txn != NULL)
                {
                Transaction *txn = lnk_txn->data;
--              
++
                        if(!(txn->status == TXN_STATUS_REMIND))
                        {
                                account_balances_add_internal(acc, txn);
                        }
                        lnk_txn = g_list_next(lnk_txn);
                }
--              
++
                lnk_acc = g_list_next(lnk_acc);
        }
        g_list_free(lst_acc);
index 628e978a9ce7c0ec4125909f3cc16d7a54e04ad4,890cd7f5c15bae7fb397a3f4884b68bcbfef3433..db3b10188739f87895cadb4e58bef5d746c8c9ca
@@@ -41,7 -38,11 +41,11 @@@ extern struct HomeBank *GLOBALS
  
  Archive *da_archive_malloc(void)
  {
-       return rc_alloc(sizeof(Archive));
+ Archive *item;
 -      item = g_malloc0(sizeof(Archive));
++      item = rc_alloc(sizeof(Archive));
+       item->key = 1;
+       return item;
  }
  
  
@@@ -67,11 -75,9 +78,9 @@@ void da_archive_free(Archive *item
        {
                if(item->memo != NULL)
                        g_free(item->memo);
-               da_splits_free(item->splits);
-               //item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared            
-               
+               if(item->splits != NULL)
+                       da_split_destroy(item->splits);
 -              g_free(item);
 +              rc_free(item);
        }
  }
  
@@@ -111,6 -117,64 +120,64 @@@ guint da_archive_length(void
  }
  
  
 -              max_key = MAX(item->key, max_key);              
+ /* append a fav with an existing key (from xml file only) */
+ gboolean
+ da_archive_append(Archive *item)
+ {
+       GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
+       return TRUE;
+ }
+ gboolean
+ da_archive_append_new(Archive *item)
+ {
+       item->key = da_archive_get_max_key() + 1;
+       GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
+       return TRUE;
+ }
+ guint32
+ da_archive_get_max_key(void)
+ {
+ GList *tmplist = g_list_first(GLOBALS->arc_list);
+ guint32 max_key = 0;
+       while (tmplist != NULL)
+       {
+       Archive *item = tmplist->data;
 -      
++              max_key = MAX(item->key, max_key);
+               tmplist = g_list_next(tmplist);
+       }
++
+       return max_key;
+ }
+ Archive *
+ da_archive_get(guint32 key)
+ {
+ GList *tmplist;
+ Archive *retval = NULL;
+       tmplist = g_list_first(GLOBALS->arc_list);
+       while (tmplist != NULL)
+       {
+       Archive *item = tmplist->data;
+               if(item->key == key)
+               {
+                       retval = item;
+                       break;
+               }
+               tmplist = g_list_next(tmplist);
+       }
+       return retval;
+ }
  void da_archive_consistency(Archive *item)
  {
  Account *acc;
@@@ -125,9 -190,20 +193,20 @@@ guint nbsplit
                item->kcat = 0;
                GLOBALS->changes_count++;
        }
-       
-       split_cat_consistency(item->splits);
-       
 -      //#1340142 check split category         
++      //#1340142 check split category
+       if( item->splits != NULL )
+       {
+               nbsplit = da_splits_consistency(item->splits);
+               //# 1416624 empty category when split
+               if(nbsplit > 0 && item->kcat > 0)
+               {
+                       g_warning("txn consistency: fixed invalid cat on split txn");
+                       item->kcat = 0;
+                       GLOBALS->changes_count++;
+               }
+       }
 -      
++
        // check payee exists
        pay = da_pay_get(item->kpay);
        if(pay == NULL)
@@@ -169,13 -245,15 +248,15 @@@ Archive *da_archive_init_from_transacti
        arc->kpay               = txn->kpay;
        arc->kcat               = txn->kcat;
        if(txn->memo != NULL)
-               arc->memo       = g_strdup(txn->memo);
+               arc->memo = g_strdup(txn->memo);
        else
-               arc->memo       = g_strdup(_("(new archive)"));
+               arc->memo = g_strdup(_("(new archive)"));
  
-       if( da_splits_clone(txn->splits, arc->splits) > 0)
+       arc->tags    = tags_clone(txn->tags);
+       arc->splits  = da_splits_clone(txn->splits);
+       if( da_splits_length (arc->splits) > 0 )
                arc->flags |= OF_SPLIT; //Flag that Splits are active
--      
++
        return arc;
  }
  
@@@ -211,7 -289,7 +292,7 @@@ guint32 nextpostdate = nextdate
  
        /* get the final post date and free */
        nextpostdate = g_date_get_julian(tmpdate);
--      
++
        return nextpostdate;
  }
  
@@@ -239,7 -317,7 +320,7 @@@ gint shift
  
  
        finalpostdate = postdate;
--      
++
        tmpdate = g_date_new_julian(finalpostdate);
        /* manage weekend exception */
        if( arc->weekend > 0 )
                        }
                }
        }
--      
++
        /* get the final post date and free */
        finalpostdate = g_date_get_julian(tmpdate);
        g_date_free(tmpdate);
--      
++
        return finalpostdate;
  }
  
@@@ -314,10 -392,10 +395,10 @@@ guint32 nblate = 0
  
        if(arc->flags & OF_LIMIT)
                nblate = MIN(nblate, arc->limit);
--      
++
        nblate = MIN(nblate, 11);
        */
--      
++
  
        // pre 5.1 way
        post_date = g_date_new();
@@@ -373,7 -451,7 +454,7 @@@ gushort lastday
                        }
  
                        arc->daygap = CLAMP(lastday - g_date_get_day(post_date), 0, 3);
--              
++
                        DB( g_print(" daygap is %d\n", arc->daygap) );
                }
                else
@@@ -410,10 -488,10 +491,10 @@@ GDate *today, *maxdate
        DB( g_print("\n[scheduled] date_get_post_max\n") );
  
        //add until xx of the next month (excluded)
--      if(GLOBALS->auto_smode == 0)    
++      if(GLOBALS->auto_smode == 0)
        {
                DB( g_print(" - max is %d of next month\n", GLOBALS->auto_weekday) );
--              
++
                today = g_date_new_julian(GLOBALS->today);
  
                //we compute user xx weekday of next month
                g_date_set_day(maxdate, GLOBALS->auto_weekday);
                if(g_date_get_day (today) >= GLOBALS->auto_weekday)
                        g_date_add_months(maxdate, 1);
--              
++
                nbdays = g_date_days_between(today, maxdate);
--      
++
                g_date_free(maxdate);
                g_date_free(today);
        }
@@@ -454,7 -532,7 +535,7 @@@ Transaction *txn
        maxpostdate = scheduled_date_get_post_max();
  
        txn = da_transaction_malloc();
--      
++
        list = g_list_first(GLOBALS->arc_list);
        while (list != NULL)
        {
                                while(mydate < maxpostdate)
                                {
                                        DB( hb_print_date(mydate, arc->memo) );
--                                      
++
                                        da_transaction_init_from_template(txn, arc);
                                        txn->date = scheduled_get_postdate(arc, mydate);
                                        /* todo: ? fill in cheque number */
@@@ -500,7 -578,7 +581,7 @@@ nextarchive
        }
  
        da_transaction_free (txn);
--      
++
        return count;
  }
  
diff --cc src/hb-assign.c
Simple merge
index 948788649f591bcd242a47e0c898993beff7ce08,3dc34e402c382dd06e66772dd6d3714399e628b6..e26b2b8117d8ca687b4b1321bc202d852004b089
@@@ -64,7 -62,8 +65,8 @@@ da_cat_free(Category *item
                DB( g_print(" => %d, %s\n", item->key, item->name) );
  
                g_free(item->name);
 -              g_free(item);
+               g_free(item->fullname);
 +              rc_free(item);
        }
  }
  
@@@ -149,6 -165,57 +168,57 @@@ da_cat_remove(guint32 key
        return g_hash_table_foreach_remove(GLOBALS->h_cat, (GHRFunc)da_cat_remove_grfunc, &key);
  }
  
 -      
+ static void
+ da_cat_build_fullname(Category *item)
+ {
+ Category *parent;
+       g_free(item->fullname);
+       if( item->parent == 0 )
+               item->fullname = g_strdup(item->name);
+       else
+       {
+               parent = da_cat_get(item->parent);
+               if( parent != NULL )
+                       item->fullname = g_strconcat(parent->name, ":", item->name, NULL);
+       }
 -      
++
+       DB( g_print("- updated %d:'%s' fullname='%s'\n", item->key, item->name, item->fullname) );
+ }
+ static void
+ da_cat_rename(Category *item, gchar *newname)
+ {
+       DB( g_print("- renaming %s' => '%s'\n", item->name, newname) );
 -      
++
+       g_free(item->name);
+       item->name = g_strdup(newname);
+       da_cat_build_fullname(item);
 -              
++
+       if( item->parent == 0 )
+       {
+       GHashTableIter iter;
+       gpointer value;
+               DB( g_print("- updating subcat fullname\n") );
 -                      
++
+               g_hash_table_iter_init (&iter, GLOBALS->h_cat);
+               while (g_hash_table_iter_next (&iter, NULL, &value))
+               {
+               Category *subcat = value;
++
+                       if( subcat->parent == item->key )
+                               da_cat_build_fullname(subcat);
+               }
+       }
+ }
  /**
   * da_cat_insert:
   *
@@@ -162,12 -229,16 +232,16 @@@ da_cat_insert(Category *item
  {
  guint32 *new_key;
  
-       DB( g_print("da_cat_insert\n") );
+       DB( g_print("\nda_cat_insert\n") );
+       DB( g_print("- '%s'\n", item->name) );
 -      
 +
        new_key = g_new0(guint32, 1);
        *new_key = item->key;
        g_hash_table_insert(GLOBALS->h_cat, new_key, item);
  
 -      
+       da_cat_build_fullname(item);
++
        return TRUE;
  }
  
@@@ -184,30 -256,18 +259,18 @@@ gboolea
  da_cat_append(Category *cat)
  {
  Category *existitem;
- guint32 *new_key;
- gchar *fullname;
-       DB( g_print("da_cat_append\n") );
-       if( cat->name != NULL)
-       {
-               fullname = da_cat_get_fullname(cat);
-               existitem = da_cat_get_by_fullname( fullname );
-               g_free(fullname);
  
-               if( existitem == NULL )
-               {
-                       new_key = g_new0(guint32, 1);
-                       *new_key = da_cat_get_max_key() + 1;
-                       cat->key = *new_key;
-                       DB( g_print(" -> insert id: %d\n", *new_key) );
+       DB( g_print("\nda_cat_append\n") );
  
-                       g_hash_table_insert(GLOBALS->h_cat, new_key, cat);
-                       return TRUE;
-               }
+       if( !cat->fullname )
+               da_cat_build_fullname(cat);
 -      
 +
+       existitem = da_cat_get_by_fullname( cat->fullname );
+       if( existitem == NULL )
+       {
+               cat->key = da_cat_get_max_key() + 1;
+               da_cat_insert(cat);
+               return TRUE;
        }
  
        DB( g_print(" -> %s already exist\n", cat->name) );
@@@ -334,69 -311,71 +314,71 @@@ struct fullcatcontext ctx
  }
  
  
- /* fullname i.e. car:refuel */
- struct fullcatcontext
+ static gchar **da_cat_get_by_fullname_split_clean(gchar *rawfullname, guint *outlen)
  {
      guint   parent;
      gchar   *name;
};
gchar **partstr = g_strsplit(rawfullname, ":", 2);
guint len = g_strv_length(partstr);
gboolean valid = TRUE;
  
+       DB( g_print("- spliclean '%s' - %d parts\n", rawfullname, g_strv_length(partstr)) );
  
- static gboolean
- da_cat_fullname_grfunc(gpointer key, Category *item, struct fullcatcontext *ctx)
- {
+       if( outlen != NULL )
+               *outlen = len;
 -      
 +
-       //DB( g_print("'%s' == '%s'\n", ctx->name, item->name) );
-       if( item->parent == ctx->parent )
+       if(len >= 1)
        {
-               if(!strcasecmp(ctx->name, item->name))
-                       return TRUE;
+               g_strstrip(partstr[0]);
+               if( strlen(partstr[0]) == 0 )
+                  valid = FALSE;
+               if(len == 2)
+               {
+                       g_strstrip(partstr[1]);
+                       if( strlen(partstr[1]) == 0 )
+                          valid = FALSE;
+          }
        }
-       return FALSE;
+       if(valid == TRUE)
+               return partstr;
+       DB( g_print("- is invalid\n") );
+       g_strfreev(partstr);
+       return NULL;
  }
  
  Category *
- da_cat_get_by_fullname(gchar *fullname)
+ da_cat_get_by_fullname(gchar *rawfullname)
  {
- struct fullcatcontext ctx;
- gchar **typestr;
- Category *item = NULL;
+ gchar **partstr;
+ Category *parent = NULL;
+ Category *retval = NULL;
+ guint len;
 -      
 +
-       DB( g_print("da_cat_get_by_fullname\n") );
+       DB( g_print("\nda_cat_get_by_fullname\n") );
  
-       typestr = g_strsplit(fullname, ":", 2);
-       if( g_strv_length(typestr) == 2 )
+       if( rawfullname )
        {
-               ctx.parent = 0;
-               ctx.name = typestr[0];
-               DB( g_print(" [x:x] try to find the parent : '%s'\n", typestr[0]) );
-               Category *parent = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
-               if( parent != NULL )
+               if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
                {
-                       ctx.parent = parent->key;
-                       ctx.name = typestr[1];
+                       if( len >= 1 )
+                       {
+                               parent = da_cat_get_by_name_find_internal(0, partstr[0]);
+                               retval = parent;
+                       }
 -                      
 +
-                       DB( g_print(" [x:x] and searching sub %d '%s'\n", ctx.parent, ctx.name) );
+                       if( len == 2 && parent != NULL )
+                       {
+                               retval = da_cat_get_by_name_find_internal(parent->key, partstr[1]);
+                       }
  
-                       item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
+                       g_strfreev(partstr);
                }
        }
-       else
-       {
-               ctx.parent = 0;
-               ctx.name = fullname;
-               DB( g_print(" [x] try to '%s'\n", fullname) );
-               item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
-       }
-       g_strfreev(typestr);
 -              
 +
-       DB( g_print(" return value %p\n", item) );
-       return item;
+       return retval;
  }
  
  
   *
   */
  Category *
- da_cat_append_ifnew_by_fullname(gchar *fullname, gboolean imported)
+ da_cat_append_ifnew_by_fullname(gchar *rawfullname)
  {
- struct fullcatcontext ctx;
- gchar **typestr;
- Category *newcat, *item, *retval = NULL;
- guint32 *new_key;
+ gchar **partstr;
+ Category *parent = NULL;
+ Category *newcat = NULL;
+ Category *retval = NULL;
+ guint len;
  
-       DB( g_print("da_cat_append_ifnew_by_fullname\n") );
+       DB( g_print("\nda_cat_append_ifnew_by_fullname\n") );
  
-       DB( g_print(" -> fullname: '%s' %d\n", fullname, strlen(fullname)) );
-       if( strlen(fullname) > 0 )
+       if( rawfullname )
        {
-               typestr = g_strsplit(fullname, ":", 2);
-               /* if we have a subcategory : aaaa:bbb */
-               if( g_strv_length(typestr) == 2 )
+               if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
                {
-                       ctx.parent = 0;
-                       ctx.name = typestr[0];
-                       DB( g_print(" try to find the parent:'%s'\n", typestr[0]) );
-                       Category *parent = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
-                       if( parent == NULL )
-                       {
-                               DB( g_print(" -> not found\n") );
-                               // append a new category
-                               new_key = g_new0(guint32, 1);
-                               *new_key = da_cat_get_max_key() + 1;
-                               newcat = da_cat_malloc();
-                               newcat->key = *new_key;
-                               newcat->name = g_strdup(typestr[0]);
-                               newcat->imported = imported;
-                               parent = newcat;
-                               DB( g_print(" -> insert cat '%s' id: %d\n", newcat->name, newcat->key) );
-                               g_hash_table_insert(GLOBALS->h_cat, new_key, newcat);
-                       }
-                       ctx.parent = parent->key;
-                       ctx.name = typestr[1];
-                       DB( g_print(" searching %d '%s'\n", ctx.parent, ctx.name) );
-                       item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
-                       if( item == NULL )
+                       if( len >= 1 )
                        {
-                               // append a new subcategory
-                               new_key = g_new0(guint32, 1);
-                               *new_key = da_cat_get_max_key() + 1;
-                               newcat = da_cat_malloc();
-                               newcat->key = *new_key;
-                               newcat->parent = parent->key;
-                               newcat->name = g_strdup(typestr[1]);
-                               newcat->imported = imported;
-                               newcat->flags |= GF_SUB;
-                               //#1713413 take parent type into account
-                               if(parent->flags & GF_INCOME)
-                                       newcat->flags |= GF_INCOME;
-                               DB( g_print(" -> insert subcat '%s' id: %d\n", newcat->name, newcat->key) );
-                               g_hash_table_insert(GLOBALS->h_cat, new_key, newcat);
-                               retval = newcat;
+                               parent = da_cat_get_by_name_find_internal(0, partstr[0]);
+                               if( parent == NULL )
+                               {
+                                       parent = da_cat_malloc();
+                                       parent->key = da_cat_get_max_key() + 1;
+                                       parent->name = g_strdup(partstr[0]);
+                                       da_cat_insert(parent);
+                               }
+                               retval = parent;
                        }
-                       else
-                               retval = item;
-               }
-               /* this a single category : aaaa */
-               else
-               {
-                       ctx.parent = 0;
-                       ctx.name = typestr[0];
-                       DB( g_print(" searching %d '%s'\n", ctx.parent, ctx.name) );
 -                              
 +
-                       item = g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
-                       if( item == NULL )
+                       /* if we have a subcategory - xxx:xxx */
+                       if( len == 2 && parent != NULL )
                        {
-                               // append a new category
-                               new_key = g_new0(guint32, 1);
-                               *new_key = da_cat_get_max_key() + 1;
-                               newcat = da_cat_malloc();
-                               newcat->key = *new_key;
-                               newcat->name = g_strdup(typestr[0]);
-                               newcat->imported = imported;
-                               DB( g_print(" -> insert cat '%s' id: %d\n", newcat->name, newcat->key) );
-                               g_hash_table_insert(GLOBALS->h_cat, new_key, newcat);
+                               newcat = da_cat_get_by_name_find_internal(parent->key, partstr[1]);
+                               if( newcat == NULL )
+                               {
+                                       newcat = da_cat_malloc();
+                                       newcat->key = da_cat_get_max_key() + 1;
+                                       newcat->parent = parent->key;
+                                       newcat->name = g_strdup(partstr[1]);
+                                       newcat->flags |= GF_SUB;
+                                       //#1713413 take parent type into account
+                                       if(parent->flags & GF_INCOME)
+                                               newcat->flags |= GF_INCOME;
+                                       da_cat_insert(newcat);
+                               }
                                retval = newcat;
                        }
-                       else
-                               retval = item;
 -                                 
 +
+                       g_strfreev(partstr);
                }
-               g_strfreev(typestr);
        }
  
        return retval;
@@@ -565,8 -498,17 +501,17 @@@ gboolean isIncome
                        GLOBALS->changes_count++;
                }
        }
-       
-       g_strstrip(item->name);
+       if( item->name != NULL )
+               g_strstrip(item->name);
+       else
+       {
+               item->name = g_strdup("void");
+               da_cat_build_fullname(item);
+               g_warning("category consistency: fixed null name");
+               GLOBALS->changes_count++;
+       }
 -      
++
  }
  
  
@@@ -623,11 -565,11 +568,11 @@@ guint32 retval = 0
  }
  
  
--void 
++void
  category_delete_unused(void)
  {
  GList *lcat, *list;
--      
++
        lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
        while (list != NULL)
        {
  }
  
  
--static void 
++static void
  category_fill_usage_count(guint32 kcat)
  {
  Category *cat = da_cat_get (kcat);
@@@ -696,16 -638,16 +641,16 @@@ guint i, nbsplit
                        //#1689308 count split as well
                        if( txn->flags & OF_SPLIT )
                        {
-                               nbsplit = da_splits_count(txn->splits);
+                               nbsplit = da_splits_length(txn->splits);
                                for(i=0;i<nbsplit;i++)
                                {
-                               Split *split = txn->splits[i];
-                                       
+                               Split *split = da_splits_get(txn->splits, i);
 -                                      
++
                                        category_fill_usage_count(split->kcat);
                                }
                        }
                        else
--                              category_fill_usage_count(txn->kcat);           
++                              category_fill_usage_count(txn->kcat);
  
                        lnk_txn = g_list_next(lnk_txn);
                }
                //#1689308 count split as well
                if( entry->flags & OF_SPLIT )
                {
-                       nbsplit = da_splits_count(entry->splits);
+                       nbsplit = da_splits_length(entry->splits);
                        for(i=0;i<nbsplit;i++)
                        {
-                       Split *split = entry->splits[i];
-                               
+                       Split *split = da_splits_get(entry->splits, i);
 -                              
++
                                category_fill_usage_count(split->kcat);
                        }
                }
@@@ -778,7 -720,7 +723,7 @@@ guint i, nbsplit
                while (lnk_txn != NULL)
                {
                Transaction *txn = lnk_txn->data;
--              
++
                        if(txn->kcat == key1)
                        {
                                txn->kcat = key2;
  
                        lnk_txn = g_list_next(lnk_txn);
                }
--              
++
                lnk_acc = g_list_next(lnk_acc);
        }
        g_list_free(lst_acc);
@@@ -1131,8 -1057,15 +1060,15 @@@ category_type_get(Category *item
  }
  
  
+ gchar
+ category_get_type_char(Category *item)
+ {
+       return (item->flags & GF_INCOME) ? '+' : '-';
+ }
  
- static gint category_change_type_eval(Category *item, gboolean isIncome)
 -static gint 
++static gint
+ category_change_type_eval(Category *item, gboolean isIncome)
  {
        if( (item->flags & (GF_INCOME)) && !isIncome )
                return 1;
  }
  
  
- gint category_change_type(Category *item, gboolean isIncome)
 -gint 
++gint
+ category_change_type(Category *item, gboolean isIncome)
  {
  gint changes = 0;
  GList *lcat, *list;
  
        changes += category_change_type_eval(item, isIncome);
--      
++
        item->flags &= ~(GF_INCOME);    //delete flag
        if(isIncome == TRUE)
                item->flags |= GF_INCOME;
diff --cc src/hb-payee.c
Simple merge
Simple merge
Simple merge
diff --cc src/hb-tag.c
Simple merge
index eb825093e8b674239d7c4a3c4a326cde61e1d7bd,0368946f9fbb4267bb383d7ace7200f70f03a6a2..966508092798840858a0a77cd74c814860eb9b59
  #include "hb-tag.h"
  #include "hb-split.h"
  
 +#include "ext.h"
 +#include "refcount.h"
 +
  /****************************************************************************/
- /* Debug macros                                                                                                       */
+ /* Debug macro                                                                    */
  /****************************************************************************/
  #define MYDEBUG 0
  
@@@ -140,9 -128,31 +131,31 @@@ Transaction *da_transaction_init_from_t
  }
  
  
+ Transaction *da_transaction_set_default_template(Transaction *txn)
+ {
+ Account *acc;
+ Archive *arc;
+       DB( g_print("da_transaction_set_default_template\n") );
+       acc = da_acc_get(txn->kacc);
+       if(acc != NULL && acc->karc > 0)
+       {
+               arc = da_archive_get(acc->karc);
+               if( arc )
+               {
+                       DB( g_print(" - init with default template\n") );
+                       da_transaction_init_from_template(txn, arc);
+               }
+       }
+       return txn;
+ }
  Transaction *da_transaction_clone(Transaction *src_item)
  {
 -Transaction *new_item = g_memdup(src_item, sizeof(Transaction));
 +Transaction *new_item = rc_dup(src_item, sizeof(Transaction));
  
        DB( g_print("da_transaction_clone\n") );
  
@@@ -950,11 -987,8 +993,11 @@@ Account *acc
  
                if(newope->paymode == PAYMODE_INTXFER)
                {
-                       transaction_xfer_search_or_add_child(NULL, newope, FALSE);
+                       transaction_xfer_search_or_add_child(parent, newope, newope->kxferacc);
                }
 +
 +              GValue txn_value = G_VALUE_INIT;
 +              ext_hook("transaction_inserted", EXT_TRANSACTION(&txn_value, newope), NULL);
        }
        
        return newope;
diff --cc src/hb-xml.c
index da67dad488e9b9a95a3c5b901d6140914e56736b,ae3ea9b6635321de51c6416dae674d4b71733442..cc99d48e44cab33ca9d54a23b2e21e94d2d7ab53
@@@ -1605,42 -1664,35 +1669,38 @@@ gchar *outstr
  gint retval = XML_OK;
  GError *error = NULL;
  
 +      GValue filename_val = G_VALUE_INIT;
 +      ext_hook("save_file", EXT_STRING(&filename_val, filename), NULL);
 +
        io = g_io_channel_new_file(filename, "w", &error);
-       if(io == NULL)
+       if(error)
        {
-               g_message("file error on: %s", filename);
-               retval = XML_IO_ERROR;
-               
-               if(error)
-                       g_print("failed: %s\n", error->message);
-               
+               g_warning("unable to save file %s: %s", filename, error->message);
                g_error_free(error);
+               return(XML_IO_ERROR);
        }
-       else
-       {
-               g_io_channel_write_chars(io, "<?xml version=\"1.0\"?>\n", -1, NULL, NULL);
  
-               outstr = g_strdup_printf("<homebank v=\"%s\" d=\"%06d\">\n", g_ascii_dtostr (buf1, sizeof (buf1), FILE_VERSION), HB_VERSION_NUM);
-               g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
-               g_free(outstr);
+       g_io_channel_write_chars(io, "<?xml version=\"1.0\"?>\n", -1, NULL, NULL);
  
-               retval = homebank_save_xml_prop(io);
-               retval = homebank_save_xml_cur(io);
-               retval = homebank_save_xml_acc(io);
-               retval = homebank_save_xml_pay(io);
-               retval = homebank_save_xml_cat(io);
-               retval = homebank_save_xml_tag(io);
-               retval = homebank_save_xml_asg(io);
-               retval = homebank_save_xml_arc(io);
-               retval = homebank_save_xml_ope(io);
+       outstr = g_strdup_printf("<homebank v=\"%s\" d=\"%06d\">\n", g_ascii_dtostr (buf1, sizeof (buf1), FILE_VERSION), HB_VERSION_NUM);
+       g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
+       g_free(outstr);
  
-               g_io_channel_write_chars(io, "</homebank>\n", -1, NULL, NULL);
+       retval = homebank_save_xml_prop(io);
+       retval = homebank_save_xml_cur(io);
+       retval = homebank_save_xml_acc(io);
+       retval = homebank_save_xml_pay(io);
+       retval = homebank_save_xml_cat(io);
+       //retval = homebank_save_xml_tag(io);
+       retval = homebank_save_xml_asg(io);
+       retval = homebank_save_xml_arc(io);
+       retval = homebank_save_xml_ope(io);
+       g_io_channel_write_chars(io, "</homebank>\n", -1, NULL, NULL);
+       g_io_channel_unref (io);
  
-               g_io_channel_unref (io);
-       }
        return retval;
  }
  
diff --cc src/homebank.c
index 60e87c88650dbc4ccc7bc0329cec3e07db271bf4,f33a1cb97bd2353c89d5831bce6d38e98d6175dd..2eff668abf92d5347c69dd59c96c67316330e20c
@@@ -19,9 -19,8 +19,9 @@@
  
  
  #include "homebank.h"
 +#include "ext.h"
  
- #include "dsp_mainwindow.h"
+ #include "dsp-mainwindow.h"
  #include "hb-preferences.h"
  #include "language.h"
  
diff --cc src/homebank.h
Simple merge
diff --cc src/ui-pref.c
index bcee6b1848e09e19df8a61921583935c0496a3a9,a5c56eb6db19bbbedd5cac7f52cf9fe87dc52908..c9d435a72c622faefcb2dd4612bf09023002265f
  #include "homebank.h"
  
  #include "ui-pref.h"
- #include "dsp_mainwindow.h"
+ #include "dsp-mainwindow.h"
  #include "gtk-chart-colors.h"
  
 +#include "ext.h"
 +
  #include "ui-currency.h"
  
  
@@@ -66,24 -69,27 +66,29 @@@ enu
  static gchar *pref_iconname[PREF_MAX] = {
  "prf-general",
  "prf-interface",
+ "prf-locale",
  "prf-columns",
- "prf-display",
 -//"prf-display", 
++//"prf-display",
  "prf-import",
  "prf-report",
+ "prf-backup",
+ "prf-folder",
  "prf-euro",                   // to be renamed
 +"prf-plugins",
  //"prf_charts.svg"
  };
  
  static gchar *pref_name[PREF_MAX]    = {
  N_("General"),
  N_("Interface"),
+ N_("Locale"),
  N_("Transactions"),
- N_("Display format"),
  N_("Import/Export"),
  N_("Report"),
 -N_("Euro minor")
+ N_("Backup"),
+ N_("Folders"),
 +N_("Euro minor"),
 +N_("Plugins")
  //
  };
  
@@@ -1122,12 -1148,12 +1152,12 @@@ gint crow, row
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
        label = make_label_group(_("OFX/QFX options"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
 -      
 +
        row = 1;
-       label = make_label_widget(_("_Name field:"));
+       label = make_label_widget(_("OFX _Name:"));
        //----------------------------------------- l, r, t, b
        gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
        widget = make_cycle(label, CYA_IMPORT_OFXNAME);
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
        label = make_label_group(_("QIF options"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
 -      
 +
        row = 1;
-       label = make_label_widget(_("Memos:"));
-       gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
-       widget = gtk_check_button_new_with_mnemonic (_("_Import"));
+       widget = gtk_check_button_new_with_mnemonic (_("_Import memos"));
        data->CM_dtex_qifmemo = widget;
-       gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
-       widget = gtk_check_button_new_with_mnemonic (_("_Swap with payees"));
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
+       widget = gtk_check_button_new_with_mnemonic (_("_Swap memos with payees"));
        data->CM_dtex_qifswap = widget;
-       gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
  
-       // group :: Files folder
+       // group :: other options
      group_grid = gtk_grid_new ();
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
-       label = make_label_group(_("Files folder"));
+       label = make_label_group(_("Other options"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
  
        row = 1;
-       label = make_label_widget(_("_Import:"));
-       //----------------------------------------- l, r, t, b
-       gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
-       hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-       gtk_widget_set_hexpand (hbox, TRUE);
-       gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
-       widget = make_string(label);
-       data->ST_path_import = widget;
-       gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_LINKED);
-       gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
-       //widget = gtk_button_new_with_label("...");
-       widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
-       data->BT_path_import = widget;
-       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
-       row++;
-       label = make_label_widget(_("_Export:"));
-       //----------------------------------------- l, r, t, b
-       gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
-       hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-       gtk_widget_set_hexpand (hbox, TRUE);
-       gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
-       widget = make_string(label);
-       data->ST_path_export = widget;
-       gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_LINKED);
-       gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+       widget = gtk_check_button_new_with_mnemonic (_("Sentence _case memo/payee"));
+       data->CM_dtex_ucfirst = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
 -      
 +
-       //widget = gtk_button_new_with_label("...");
-       widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
-       data->BT_path_export = widget;
-       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
  
        return content_grid;
  }
@@@ -1263,12 -1254,12 +1258,12 @@@ gint crow, row
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
        label = make_label_group(_("Initial filter"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
 -      
 +
        row = 1;
-       label = make_label_widget(_("Date _range:"));
+       label = make_label_widget(_("_Range:"));
        //----------------------------------------- l, r, t, b
        gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
        widget = make_daterange(label, FALSE);
@@@ -1489,8 -1480,8 +1484,8 @@@ gint crow, row
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
-       label = make_label_group(_("Date"));
+       label = make_label_group(_("User interface"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
  
        row = 1;
@@@ -1629,6 -1658,27 +1662,27 @@@ gint crow, row
        data->CM_herit_date = widget;
        gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
  
 -      
+       // group :: Memo autocomplete
+     group_grid = gtk_grid_new ();
+       gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
+       gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
+       gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
++
+       label = make_label_group(_("Memo autocomplete"));
+       gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
+       row = 1;
+       widget = gtk_check_button_new_with_mnemonic (_("Active"));
+       data->CM_memoacp = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
+       row++;
+       widget = make_numeric(NULL, 0, 1460);
+       data->ST_memoacp_days = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
+       label = make_label(_("rolling days"), 0, 0.5);
+       gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
        // group :: Column list
        /*
      group_grid = gtk_grid_new ();
@@@ -1734,8 -1785,9 +1789,9 @@@ gint crow, row
        //----------------------------------------- l, r, t, b
        gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
        hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
+       data->GR_colors = hbox;
        gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
 -      
 +
        widget = gtk_color_button_new ();
        data->CP_exp_color = widget;
        gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
@@@ -1773,8 -1825,8 +1829,8 @@@ gint crow, row
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
-       label = make_label_group(_("Program start"));
+       label = make_label_group(_("Backup"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
  
        row = 1;
@@@ -1802,8 -1873,8 +1877,8 @@@ gint crow, row
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
-       label = make_label_group(_("Fiscal year"));
+       label = make_label_group(_("HomeBank files"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
  
        row = 1;
        gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
        gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
        gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
 +
-       label = make_label_group(_("Main window reports"));
+       label = make_label_group(_("Exchange files"));
        gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
 -      
 +
        row = 1;
-       label = make_label_widget(_("Date _range:"));
+       label = make_label_widget(_("_Import:"));
+       //----------------------------------------- l, r, t, b
        gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
-       widget = make_daterange(label, FALSE);
-       data->CY_daterange_wal = widget;
-       gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
  
-       // group :: Files folder
-     group_grid = gtk_grid_new ();
-       gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
-       gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
-       gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
+       hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+       gtk_widget_set_hexpand (hbox, TRUE);
+       gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
  
-       label = make_label_group(_("Files folder"));
-       gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
+       widget = make_string(label);
+       data->ST_path_import = widget;
+       gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_LINKED);
+       gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
  
-       row = 1;
-       label = make_label_widget(_("_Default:"));
+       //widget = gtk_button_new_with_label("...");
+       widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
+       data->BT_path_import = widget;
+       gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+       row++;
+       label = make_label_widget(_("_Export:"));
+       //----------------------------------------- l, r, t, b
        gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
  
        hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  
        //widget = gtk_button_new_with_label("...");
        widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
-       data->BT_path_hbfile = widget;
+       data->BT_path_export = widget;
        gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
  
 -      
+       return content_grid;
+ }
+ static GtkWidget *defpref_page_general (struct defpref_data *data)
+ {
+ GtkWidget *content_grid, *group_grid, *label, *widget;
+ gint crow, row;
+       content_grid = gtk_grid_new();
+       gtk_grid_set_row_spacing (GTK_GRID (content_grid), SPACING_LARGE);
+       gtk_orientable_set_orientation(GTK_ORIENTABLE(content_grid), GTK_ORIENTATION_VERTICAL);
+       crow = 0;
+       // group :: Program start
+     group_grid = gtk_grid_new ();
+       gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
+       gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
+       gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
 -      
++
+       label = make_label_group(_("Program start"));
+       gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
+       row = 1;
+       widget = gtk_check_button_new_with_mnemonic (_("Show splash screen"));
+       data->CM_show_splash = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
+       row++;
+       widget = gtk_check_button_new_with_mnemonic (_("Load last opened file"));
+       data->CM_load_last = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
+       row++;
+       widget = gtk_check_button_new_with_mnemonic (_("Post pending scheduled transactions"));
+       data->CM_append_scheduled = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
+       row++;
+       widget = gtk_check_button_new_with_mnemonic (_("Update currencies online"));
+       data->CM_do_update_currency = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
+       // group :: Main window reports
+     group_grid = gtk_grid_new ();
+       gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
+       gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
+       gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
++
+       label = make_label_group(_("Main window reports"));
+       gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
+       row = 1;
+       label = make_label(_("_Range:"), 0, 0.5);
+       gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
+       widget = make_daterange(label, FALSE);
+       data->CY_daterange_wal = widget;
+       gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
        return content_grid;
  }
  
@@@ -2314,12 -2267,13 +2470,13 @@@ GtkWidget *hbox, *vbox, *sw, *widget, *
        //connect all our signals
        g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window);
  
-       g_signal_connect (G_OBJECT (data.BT_clear), "clicked", G_CALLBACK (defpref_clear), NULL);
+       g_signal_connect (data.CM_bak_is_automatic, "toggled", G_CALLBACK (defpref_backuptoggle), NULL);
 -      
 +
        //path selector
-       g_signal_connect (data.BT_path_hbfile, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(1));
-       g_signal_connect (data.BT_path_import, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(2));
-       g_signal_connect (data.BT_path_export, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(3));
+       g_signal_connect (data.BT_path_hbfile, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(PRF_PATH_WALLET));
+       g_signal_connect (data.BT_path_import, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(PRF_PATH_IMPORT));
+       g_signal_connect (data.BT_path_export, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(PRF_PATH_EXPORT));
  
        g_signal_connect (data.CM_custom_colors, "toggled", G_CALLBACK (defpref_colortoggle), NULL);
      g_signal_connect (data.CY_colors, "changed", G_CALLBACK (defpref_colorpreset), NULL);
  
                                g_free(old_lang);
                                break;
+                       case 55:
+                               defpref_reset (window, NULL);
+                               break;
                }
 -      
 +
  
        // cleanup and destroy
        //defhbfile_cleanup(&data, result);
diff --cc src/ui-pref.h
index 1ec90ba22a200f429b973224e147b932f3ba6251,19d387636cc1a48fcb8327b666a9509395df8e87..1ceb45751cd09b7396ca45ea61cb360a7e89c098
@@@ -48,12 -67,9 +67,9 @@@ struct defpref_dat
        GtkWidget       *BT_go_up;
        GtkWidget       *BT_go_down;
  
-       GtkWidget   *NB_fiscyearday;
-       GtkWidget   *CY_fiscyearmonth;
--      
++
        GtkWidget       *CM_runwizard;
  
-       GtkWidget       *ST_path_hbfile, *BT_path_hbfile;
        GtkWidget       *ST_path_import, *BT_path_import;
        GtkWidget       *ST_path_export, *BT_path_export;
  
        GtkWidget       *CY_daterange_txn;
        GtkWidget   *ST_datefuture_nbdays;
        GtkWidget       *CY_daterange_rep;
--      
++
        /* currencies */
        GtkWidget       *LB_default;
--      GtkWidget       *BT_default; 
--      
++      GtkWidget       *BT_default;
++
        GtkWidget       *CM_euro_enable;
        GtkWidget       *GRP_currency;
        GtkWidget       *GRP_rate;
        GtkWidget       *GRP_format;
--       
++
        GtkWidget       *CY_euro_preset;
        GtkWidget       *ST_euro_country;
        GtkWidget       *NB_euro_value;
  
        GtkWidget       *ST_euro_symbol;
        GtkWidget       *CM_euro_isprefix;
--      GtkWidget       *ST_euro_decimalchar;   
--      GtkWidget       *ST_euro_groupingchar;  
++      GtkWidget       *ST_euro_decimalchar;
++      GtkWidget       *ST_euro_groupingchar;
        GtkWidget       *NB_euro_fracdigits;
        GtkWidget       *LB_numbereuro;
  
  
        gint            country;
  
 +      GtkWidget       *PI_plugin_columns;
  };
  
-       PREF_TRANSACTIONS,
-       PREF_DISPLAY_FORMAT,
-       PREF_IMPORT_EXPORT,
 +enum
 +{
 +      PREF_GENERAL,
 +      PREF_INTERFACE,
-       PREF_EURO_MINOR,
++      PREF_LOCALE,    //old DISPLAY
++      PREF_TXN,               //old COLUMNS
++      PREF_IMPORT,
 +      PREF_REPORT,
++      PREF_BACKUP,
++      PREF_FOLDERS,
++      PREF_EURO,
 +      PREF_PLUGINS,
 +      PREF_MAX
 +};
  
 -typedef struct 
 +
- typedef struct 
++typedef struct
  {
        gchar   *locale;
        gchar   *name;
index 91ced1bb5e66320fa97f5e7cc4c8daef952dd75e,48f5746cfbe21fcb06cce894aceba01c436277e9..fd5bcc945ccaeea65f2ea065b25fc5f52f229b93
@@@ -77,11 -83,12 +83,13 @@@ private_icons = 
        hicolor_status_48x48_prf-general.png \
        hicolor_status_48x48_prf-import.png \
        hicolor_status_48x48_prf-interface.png \
+       hicolor_status_48x48_prf-locale.png \
        hicolor_status_48x48_prf-report.png \
 +      hicolor_status_48x48_prf-plugins.png \
+       hicolor_actions_scalable_edit-split-symbolic.svg \
        hicolor_actions_scalable_toggle-sign-symbolic.svg \
-       hicolor_actions_scalable_btn-collapse-symbolic.svg \
-       hicolor_actions_scalable_btn-expand-symbolic.svg \
+       hicolor_actions_scalable_list-collapse-all-symbolic.svg \
+       hicolor_actions_scalable_list-expand-all-symbolic.svg \
        $(NULL)
  
  
This page took 0.15767 seconds and 5 git commands to generate.