homebank-5.9.7/ 0000775 0001750 0001750 00000000000 15123472260 012616 5 ustar franam franam homebank-5.9.7/src/ 0000775 0001750 0001750 00000000000 15123472257 013413 5 ustar franam franam homebank-5.9.7/src/rep-time.h 0000644 0001750 0001750 00000005137 14736461415 015315 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_REPTIME_H__
#define __HOMEBANK_REPTIME_H__
enum
{
LST_HUBREPTIME_POS,
LST_HUBREPTIME_KEY,
LST_HUBREPTIME_LABEL,
LST_HUBREPTIME_EXPENSE,
LST_HUBREPTIME_INCOME,
LST_HUBREPTIME_TOTAL,
LST_HUBREPTIME_FLAGS,
NUM_LST_HUBREPTIME
};
/* for choose options */
enum
{
FOR_REPTIME_ACCOUNT,
FOR_REPTIME_CATEGORY,
FOR_REPTIME_PAYEE,
NUM_FOR_REPTIME
};
/* view by choose options */
enum
{
GROUPBY_REPTIME_DAY,
GROUPBY_REPTIME_WEEK,
GROUPBY_REPTIME_MONTH,
GROUPBY_REPTIME_QUARTER,
GROUPBY_REPTIME_YEAR,
};
enum {
HID_REPTIME_MINDATE,
HID_REPTIME_MAXDATE,
HID_REPTIME_RANGE,
HID_REPTIME_VIEW,
MAX_REPTIME_HID
};
struct reptime_data
{
GQueue *txn_queue;
Filter *filter;
gdouble average;
gboolean detail;
guint32 accnum;
gint charttype;
gdouble *tmp_income;
gdouble *tmp_expense;
GtkWidget *window;
GActionGroup *actions;
gboolean mapped_done;
GtkWidget *TB_bar;
GtkWidget *BT_list;
GtkWidget *BT_line;
GtkWidget *BT_column;
GtkWidget *BT_detail;
GtkWidget *BT_filter;
GtkWidget *BT_refresh;
GtkWidget *BT_reset;
GtkWidget *BT_print;
GtkWidget *BT_export;
GtkWidget *TX_info;
GtkWidget *TX_fltactive, *TT_fltactive;
GtkWidget *TX_daterange;
GtkWidget *CY_mode;
GtkWidget *CY_intvl;
GtkWidget *RG_zoomx, *LB_zoomx;
GtkWidget *CM_minor;
GtkWidget *CM_cumul;
GtkWidget *CM_balance;
GtkWidget *LV_report;
GtkWidget *GR_itemtype;
GtkWidget *CY_src;
GtkWidget *BT_all, *BT_non, *BT_inv;
GtkWidget *SW_acc, *LV_acc;
GtkWidget *SW_cat, *LV_cat;
GtkWidget *SW_pay, *LV_pay;
GtkWidget *SW_tag, *LV_tag;
GtkWidget *CM_showempty;
GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *CY_range;
GtkWidget *GR_result;
GtkWidget *RE_chart;
GtkWidget *GR_detail;
GtkWidget *LV_detail;
gulong hid[MAX_REPTIME_HID];
};
GtkWidget *reptime_window_new(guint32 accnum);
#endif
homebank-5.9.7/src/hub-account.h 0000664 0001750 0001750 00000002253 14736461415 016001 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "dsp-mainwindow.h"
#ifndef __HUB_ACCOUNT_H__
#define __HUB_ACCOUNT_H__
void ui_hub_account_populate(GtkWidget *widget, gpointer user_data);
void ui_hub_account_compute(GtkWidget *widget, gpointer user_data);
void ui_hub_account_setup(struct hbfile_data *data);
void ui_hub_account_dispose(struct hbfile_data *data);
GtkWidget *ui_hub_account_create(struct hbfile_data *data);
#endif homebank-5.9.7/src/ui-hbfile.h 0000644 0001750 0001750 00000002220 14736461415 015425 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_HBFILE_GTK_H__
#define __HB_HBFILE_GTK_H__
struct defhbfile_data
{
GtkWidget *ST_owner;
GtkWidget *RA_postmode;
GtkWidget *GR_payout, *GR_advance;
GtkWidget *NU_weekday, *NU_nbmonths;
GtkWidget *NU_nbdays;
GtkWidget *LB_maxpostdate;
GtkWidget *PO_grp;
GtkWidget *ST_earnbyh;
gint change;
};
GtkWidget *create_defhbfile_dialog (void);
#endif
homebank-5.9.7/src/hb-types.h 0000664 0001750 0001750 00000002277 14736461415 015332 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_TYPES_H__
#define __HB_TYPES_H__
#pragma once
typedef struct _account Account;
typedef struct _archive Archive;
typedef struct _assign Assign;
typedef struct _category Category;
typedef struct _currency Currency;
typedef struct _payee Payee;
typedef struct _split Split;
typedef struct _tag Tag;
typedef struct _transaction Transaction;
typedef struct _filter Filter;
#endif homebank-5.9.7/src/ui-widgets-data.c 0000664 0001750 0001750 00000023314 15120466747 016556 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-widgets.h"
/* = = = = = = = = = = = = = = = = = = = = */
//chart
gchar *CHART_CATEGORY = N_("Category");
/* = = = = = = = = = = = = = = = = = = = = */
//hub, acc, imp
HbKvData CYA_ACC_TYPE[] =
{
{ ACC_TYPE_NONE, N_("(no type)") },
{ ACC_TYPE_BANK, N_("Bank") },
{ ACC_TYPE_CASH, N_("Cash") },
{ ACC_TYPE_ASSET, N_("Asset") },
{ ACC_TYPE_CREDITCARD, N_("Credit card") },
{ ACC_TYPE_LIABILITY, N_("Liability") },
{ ACC_TYPE_CHECKING, N_("Checking") },
{ ACC_TYPE_SAVINGS, N_("Savings") },
// { ACC_TYPE_MUTUALFUND, N_("Mutual Fund") },
// { ACC_TYPE_INCOME, N_("Income") },
// { ACC_TYPE_EXPENSE, N_("Expense") },
// { ACC_TYPE_EQUITY, N_("Equity") },
{ 0, NULL }
};
//bud, cat
gchar *CYA_CAT_TYPE[] = {
N_("Expense"),
N_("Income"),
NULL
};
gchar *CYA_ARC_FREQ[] = {
N_("Daily"),
N_("Weekly"),
N_("Monthly"),
N_("Yearly"),
NULL
};
gchar *CYA_ARC_FREQ2[] = {
N_("day(s)"),
N_("week(s)"),
N_("month(s)"),
N_("year(s)"),
NULL
};
//arc
HbKvData CYA_ARC_ORDINAL[] = {
{ AUTO_ORDINAL_FIRST, N_("First") },
{ AUTO_ORDINAL_SECOND, N_("Second") },
{ AUTO_ORDINAL_THIRD, N_("Third") },
{ AUTO_ORDINAL_FOURTH, N_("Fourth") },
{ AUTO_ORDINAL_LAST, N_("Last") },
{ 0, NULL }
};
//arc
HbKvData CYA_ARC_WEEKDAY[] = {
{ AUTO_WEEKDAY_DAY, N_("Day") },
{ AUTO_WEEKDAY_MONDAY, N_("Monday") },
{ AUTO_WEEKDAY_TUESDAY, N_("Tuesday") },
{ AUTO_WEEKDAY_WEDNESDAY, N_("Wednesday") },
{ AUTO_WEEKDAY_THURSDAY, N_("Thursday") },
{ AUTO_WEEKDAY_FRIDAY, N_("Friday") },
{ AUTO_WEEKDAY_SATURDAY, N_("Saturday") },
{ AUTO_WEEKDAY_SUNDAY, N_("Sunday") },
{ 0, NULL }
};
//arc
HbKvData CYA_ARC_WEEKEND[] = {
{ ARC_WEEKEND_POSSIBLE, N_("Possible") },
{ ARC_WEEKEND_BEFORE, N_("Before") },
{ ARC_WEEKEND_AFTER, N_("After") },
{ ARC_WEEKEND_SKIP, N_("Skip") }, //added 5.6
{ 0, NULL }
};
//txn, arc
gchar *CYA_TXN_TYPE[] = {
N_("Expense"),
N_("Income"),
N_("Transfer"),
NULL
};
/*HbKvData CYA_TXN_STATUS[] =
{
{ TXN_STATUS_NONE, N_("None") },
{ TXN_STATUS_CLEARED, N_("Cleared") },
{ TXN_STATUS_RECONCILED, N_("Reconciled") },
{ TXN_STATUS_REMIND, N_("Remind") },
{ TXN_STATUS_VOID, N_("Void") },
{ 0, NULL }
};*/
//this is a test
//txn
HbKivData CYA_TXN_STATUSIMG[] =
{
{ TXN_STATUS_NONE, NULL, N_("None") },
{ TXN_STATUS_CLEARED, ICONNAME_HB_ITEM_CLEAR, N_("Cleared") },
{ TXN_STATUS_RECONCILED, ICONNAME_HB_ITEM_RECON, N_("Reconciled") },
//{ TXN_STATUS_REMIND, ICONNAME_HB_ITEM_REMIND, N_("Remind") },
{ TXN_STATUS_VOID, ICONNAME_HB_ITEM_VOID, N_("Void") },
{ 0, NULL, NULL }
};
//asg
gchar *CYA_ASG_FIELD[] = {
N_("Memo"),
N_("Payee"),
NULL
};
/* = = = = = = = = = = = = = = = = = = = = */
//bal, bud, sta
gchar *CYA_REPORT_MODE[] =
{
N_("Total"),
N_("Time"),
NULL
};
HbKvData CYA_REPORT_SRC[] = {
{ REPORT_GRPBY_CATEGORY, N_("Category") },
//{ REPORT_GRPBY_SUBCATEGORY, N_("Subcategory") },
{ REPORT_GRPBY_PAYEE, N_("Payee") },
{ REPORT_GRPBY_ACCOUNT, N_("Account") },
{ REPORT_GRPBY_ACCGROUP, N_("Account Group") },
{ REPORT_GRPBY_TAG, N_("Tag") },
{ REPORT_GRPBY_MONTH, N_("Month") },
{ REPORT_GRPBY_YEAR, N_("Year") },
{ 0, NULL }
};
HbKvData CYA_REPORT_TYPE[] = {
{ REPORT_TYPE_EXPENSE, N_("Expense") },
{ REPORT_TYPE_INCOME, N_("Income") },
{ REPORT_TYPE_TOTAL, N_("Total")} ,
{ 0, NULL }
};
HbKvData CYA_REPORT_GRPBY_TREND[] = {
{ REPORT_GRPBY_ACCOUNT, N_("Account") },
{ REPORT_GRPBY_CATEGORY, N_("Category") },
{ REPORT_GRPBY_PAYEE, N_("Payee") },
{ REPORT_GRPBY_TAG, N_("Tag") },
{ 0, NULL }
};
HbKvData CYA_REPORT_INTVL[] = {
{ REPORT_INTVL_DAY, N_("Day") },
{ REPORT_INTVL_WEEK, N_("Week") },
{ REPORT_INTVL_FORTNIGHT, N_("Fortnight") },
{ REPORT_INTVL_MONTH, N_("Month") },
{ REPORT_INTVL_QUARTER, N_("Quarter") },
{ REPORT_INTVL_HALFYEAR, N_("Half Year") },
{ REPORT_INTVL_YEAR, N_("Year") },
{ 0, NULL }
};
/* = = = = = = = = = = = = = = = = = = = = */
//flt
gchar *RA_FILTER_MODE[] =
{
N_("Include"),
N_("Exclude"),
NULL
};
HbKvData CYA_FLT_RANGE_DWF[] = {
{ FLT_RANGE_LAST_DAY , N_("Yesterday") },
{ FLT_RANGE_THIS_DAY , N_("Today") },
{ FLT_RANGE_NEXT_DAY , N_("Tomorrow") },
{ FLT_RANGE_LAST_WEEK , N_("Last Week") },
{ FLT_RANGE_THIS_WEEK , N_("This Week") },
{ FLT_RANGE_NEXT_WEEK , N_("Next Week") },
{ FLT_RANGE_LAST_FORTNIGHT , N_("Last Fortnight") },
{ FLT_RANGE_THIS_FORTNIGHT , N_("This Fortnight") },
{ FLT_RANGE_NEXT_FORTNIGHT , N_("Next Fortnight") },
{ 0, NULL }
};
HbKvData CYA_FLT_RANGE_MQY[] = {
{ FLT_RANGE_LAST_MONTH , N_("Last Month") },
{ FLT_RANGE_THIS_MONTH , N_("This Month") },
{ FLT_RANGE_NEXT_MONTH , N_("Next Month") },
{ FLT_RANGE_LAST_QUARTER , N_("Last Quarter") },
{ FLT_RANGE_THIS_QUARTER , N_("This Quarter") },
{ FLT_RANGE_NEXT_QUARTER , N_("Next Quarter") },
{ FLT_RANGE_LAST_YEAR , N_("Last Year") },
{ FLT_RANGE_THIS_YEAR , N_("This Year") },
{ FLT_RANGE_NEXT_YEAR , N_("Next Year") },
{ HBTK_IS_SEPARATOR, "" },
{ HBTK_IS_SEPARATOR, "" },
{ HBTK_IS_SEPARATOR, "" },
{ 0, NULL }
};
HbKvData CYA_FLT_RANGE_YTO[] = {
{ FLT_RANGE_TODATE_YEAR , N_("Year to date") },
{ FLT_RANGE_TODATE_MONTH , N_("Month to date") },
{ FLT_RANGE_TODATE_ALL , N_("All to date") },
{ 0, NULL }
};
HbKvData CYA_FLT_RANGE_LASTXXD[] = {
{ FLT_RANGE_LAST_90DAYS , N_("Last 90 Days") },
{ FLT_RANGE_LAST_60DAYS , N_("Last 60 Days") },
{ FLT_RANGE_LAST_30DAYS , N_("Last 30 Days") },
{ 0, NULL }
};
HbKvData CYA_FLT_RANGE_COMMON[] = {
{ FLT_RANGE_LAST_12MONTHS , N_("Last 12 Months") },
{ FLT_RANGE_MISC_30DAYS , N_("30 Days Around") },
{ FLT_RANGE_MISC_ALLDATE , N_("All Date") },
{ 0, NULL }
};
HbKvData CYA_FLT_RANGE_CUSTOM[] = {
//5.7 added back
{ FLT_RANGE_MISC_CUSTOM , N_("Custom") },
{ 0, NULL }
};
HbKvData CYA_FLT_SCHEDULED[] = {
{ FLT_SCHEDULED_THISMONTH, N_("This month") },
{ FLT_SCHEDULED_NEXTMONTH, N_("Next month") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_SCHEDULED_NEXT30DAYS, N_("Next 30 days") },
{ FLT_SCHEDULED_NEXT60DAYS, N_("Next 60 days") },
{ FLT_SCHEDULED_NEXT90DAYS, N_("Next 90 days") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_SCHEDULED_MAXPOSTDATE, N_("Maximum Post Date") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_SCHEDULED_ALLDATE, N_("All") },
{ 0, NULL }
};
//repbud
HbKvData CYA_KIND[] = {
{ REPORT_TYPE_ALL, N_("Exp. & Inc.") },
{ REPORT_TYPE_EXPENSE, N_("Expense") },
{ REPORT_TYPE_INCOME, N_("Income") },
{ 0, NULL }
};
//ledger
HbKvData CYA_FLT_TYPE[] = {
{ FLT_TYPE_ALL, N_("Any Type") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_TYPE_EXPENSE, N_("Expense") },
{ FLT_TYPE_INCOME, N_("Income") },
{ FLT_TYPE_INTXFER, N_("Transfer") },
{ FLT_TYPE_NOTXFER, N_("Not Transfer") },
{ 0, NULL }
};
//ledger
HbKvData CYA_FLT_STATUS[] = {
{ FLT_STATUS_ALL, N_("Any Status") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_STATUS_CLEARED, N_("Cleared") },
{ FLT_STATUS_UNCLEARED, N_("Uncleared") },
{ FLT_STATUS_RECONCILED, N_("Reconciled") },
{ FLT_STATUS_UNRECONCILED, N_("Unreconciled") },
{ HBTK_IS_SEPARATOR, "" },
{ FLT_STATUS_UNCATEGORIZED, N_("Uncategorized") },
//5.9
{ FLT_STATUS_UNAPPROVED, N_("Unapproved") },
{ 0, NULL }
};
/* = = = = = = = = = = = = = = = = = = = = */
HbKvData CYA_TOOLBAR_STYLE[] = {
{ 0, N_("System defaults") },
{ 1, N_("Icons only") },
{ 2, N_("Text only") },
{ 3, N_("Text under icons") },
{ 4, N_("Text beside icons") },
{ 0, NULL }
};
HbKvData CYA_GRID_LINES[] = {
{ GTK_TREE_VIEW_GRID_LINES_NONE, N_("None") },
{ GTK_TREE_VIEW_GRID_LINES_HORIZONTAL, N_("Horizontal") },
{ GTK_TREE_VIEW_GRID_LINES_VERTICAL, N_("Vertical") },
{ GTK_TREE_VIEW_GRID_LINES_BOTH, N_("Both") },
{ 0, NULL }
};
HbKvData CYA_IMPORT_DATEORDER[] = {
{ PRF_DATEFMT_MDY, N_("m-d-y") },
{ PRF_DATEFMT_DMY, N_("d-m-y") },
{ PRF_DATEFMT_YMD, N_("y-m-d") },
{ 0, NULL }
};
HbKvData CYA_IMPORT_OFXNAME[] = {
{ PRF_OFXNAME_IGNORE, N_("Ignore") },
{ PRF_OFXNAME_MEMO, N_("Memo") },
{ PRF_OFXNAME_PAYEE, N_("Payee") },
{ PRF_OFXNAME_NUMBER, N_("Number") },
{ 0, NULL }
};
HbKvData CYA_IMPORT_OFXMEMO[] = {
{ PRF_OFXMEMO_IGNORE, N_("Ignore") },
{ PRF_OFXMEMO_NUMBER, N_("Append to Number") },
{ PRF_OFXMEMO_MEMO, N_("Append to Memo") },
{ PRF_OFXMEMO_PAYEE, N_("Append to Payee") },
{ 0, NULL }
};
HbKvData CYA_IMPORT_CSVSEPARATOR[] = {
{ PRF_DTEX_CSVSEP_TAB, N_("Tab") },
{ PRF_DTEX_CSVSEP_COMMA, N_("Comma") },
{ PRF_DTEX_CSVSEP_SEMICOLON, N_("Semicolon") },
{ PRF_DTEX_CSVSEP_SPACE, N_("Space") },
{ 0, NULL }
};
//pref
HbKvData CYA_MONTHS[] =
{
{ 1, N_("January") },
{ 2, N_("February") },
{ 3, N_("March") },
{ 4, N_("April") },
{ 5, N_("May") },
{ 6, N_("June") },
{ 7, N_("July") },
{ 8, N_("August") },
{ 9, N_("September") },
{ 10, N_("October") },
{ 11, N_("November") },
{ 12, N_("December") },
{ 0, NULL }
};
//rep, bud, repbud
gchar *CYA_ABMONTHS[] =
{
NULL,
N_("Jan"),
N_("Feb"),
N_("Mar"),
N_("Apr"),
N_("May"),
N_("Jun"),
N_("Jul"),
N_("Aug"),
N_("Sep"),
N_("Oct"),
N_("Nov"),
N_("Dec"),
NULL
};
homebank-5.9.7/src/ui-currency.c 0000644 0001750 0001750 00000145364 15005624304 016026 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-currency.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern Currency4217 iso4217cur[];
extern guint n_iso4217cur;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* ui_cur_combobox_get_name:
*
* get the name of the active curee or -1
*
* Return value: a new allocated name tobe freed with g_free
*
*/
gchar *
ui_cur_combobox_get_name(GtkComboBox *entry_box)
{
gchar *cbname;
gchar *name = NULL;
cbname = (gchar *)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
if( cbname != NULL)
{
name = g_strdup(cbname);
g_strstrip(name);
}
return name;
}
/**
* ui_cur_combobox_get_key:
*
* get the key of the active curee
*
* Return value: the key or 0
*
*/
guint32
ui_cur_combobox_get_key(GtkComboBox *entry_box)
{
GtkTreeModel *model;
GtkTreeIter iter;
guint32 key = 0;
if (gtk_combo_box_get_active_iter(entry_box, &iter) == TRUE)
{
model = gtk_combo_box_get_model(entry_box);
gtk_tree_model_get (model, &iter, 1, &key, -1);
}
return key;
}
gboolean
ui_cur_combobox_set_active(GtkComboBox *entry_box, guint32 key)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
guint32 cbkey;
model = gtk_combo_box_get_model(entry_box);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gtk_tree_model_get (model, &iter, 1, &cbkey, -1);
if(cbkey == key)
break;
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
gtk_combo_box_set_active_iter(entry_box, &iter);
return FALSE;
}
/**
* ui_cur_combobox_add:
*
* Add a single element (useful for dynamics add)
*
* Return value: --
*
*/
void
ui_cur_combobox_add(GtkComboBox *entry_box, Currency *cur)
{
if( cur->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter, 0, cur->name, 1, cur->key, -1);
}
}
static void
ui_cur_combobox_populate_ghfunc(gpointer key, gpointer value, struct curPopContext *ctx)
{
GtkTreeIter iter;
Currency *cur = value;
if( ( cur->key != ctx->except_key ) )
{
gtk_list_store_append (GTK_LIST_STORE(ctx->model), &iter);
gtk_list_store_set (GTK_LIST_STORE(ctx->model), &iter, 0, cur->name, 1, cur->key, -1);
}
}
void
ui_cur_combobox_populate_except(GtkComboBox *entry_box, GHashTable *hash, guint32 except_key)
{
GtkTreeModel *model;
struct curPopContext ctx;
DB( g_print ("ui_cur_combobox_populate\n") );
model = gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box));
/* keep our model alive and detach from combobox and completion */
g_object_ref(model);
gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), NULL);
/* clear and populate */
ctx.model = model;
ctx.except_key = except_key;
gtk_list_store_clear (GTK_LIST_STORE(model));
g_hash_table_foreach(hash, (GHFunc)ui_cur_combobox_populate_ghfunc, &ctx);
/* reatach our model */
gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), model);
g_object_unref(model);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
/**
* ui_cur_combobox_populate:
*
* Populate the list and completion
*
* Return value: --
*
*/
void
ui_cur_combobox_populate(GtkComboBox *entry_box, GHashTable *hash)
{
ui_cur_combobox_populate_except(entry_box, hash, -1);
}
static gint
ui_cur_combobox_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint ret = 0;
gchar *name1, *name2;
gtk_tree_model_get(model, a, 0, &name1, -1);
gtk_tree_model_get(model, b, 0, &name2, -1);
if (name1 == NULL || name2 == NULL)
{
if (name1 == NULL && name2 == NULL)
goto end;
ret = (name1 == NULL) ? -1 : 1;
}
else
{
ret = g_utf8_collate(name1,name2);
}
end:
g_free(name1);
g_free(name2);
return ret;
}
static void
ui_cur_combobox_test (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
gpointer data)
{
gchar *name;
gtk_tree_model_get(tree_model, iter,
0, &name,
-1);
if( !name )
g_object_set(cell, "text", _("(none)"), NULL);
else
g_object_set(cell, "text", name, NULL);
//leak
g_free(name);
}
/**
* ui_cur_combobox_new:
*
* Create a new curee combobox
*
* Return value: the new widget
*
*/
GtkWidget *
ui_cur_combobox_new(GtkWidget *label)
{
GtkListStore *store;
GtkWidget *combobox;
GtkCellRenderer *renderer;
store = gtk_list_store_new (2,
G_TYPE_STRING,
G_TYPE_INT
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_cur_combobox_compare_func, NULL, NULL);
// dothe same for combobox
combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
gtk_cell_layout_clear(GTK_CELL_LAYOUT (combobox));
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, "text", 0, NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combobox),
renderer,
ui_cur_combobox_test,
NULL, NULL);
g_object_unref(store);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), combobox);
gtk_widget_set_size_request (combobox, 10, -1);
return combobox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_cur_listview_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFCUR_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFCUR_TOGGLE, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
}
static gint
ui_cur_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint result = 0;
Currency *entry1, *entry2;
gchar *name1, *name2;
gtk_tree_model_get(model, a, LST_DEFCUR_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DEFCUR_DATAS, &entry2, -1);
name1 = (entry1->key == GLOBALS->kcur) ? NULL : entry1->iso_code;
name2 = (entry2->key == GLOBALS->kcur) ? NULL : entry2->iso_code;
result = hb_string_compare(name1,name2);
return result;
}
static void
ui_cur_listview_name_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Currency *entry;
gchar *string;
gint weight;
gtk_tree_model_get(model, iter, LST_DEFCUR_DATAS, &entry, -1);
weight = entry->key == GLOBALS->kcur ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
#if MYDEBUG
if( entry->key == GLOBALS->kcur )
string = g_strdup_printf ("[%d] %s - %s\n(%s)", entry->key, entry->iso_code, entry->name, _("Base currency"));
else
string = g_strdup_printf ("[%d] %s - %s", entry->key, entry->iso_code, entry->name);
g_object_set(renderer, "weight", weight, "markup", string, NULL);
g_free(string);
#else
if( entry->key == GLOBALS->kcur )
string = g_strdup_printf ("%s - %s\n(%s)", entry->iso_code, entry->name, _("Base currency"));
else
string = g_strdup_printf ("%s - %s", entry->iso_code, entry->name);
g_object_set(renderer, "weight", weight, "markup", string, NULL);
g_free(string);
#endif
}
static void
ui_cur_listview_symbol_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Currency *entry;
gint weight;
gtk_tree_model_get(model, iter, LST_DEFCUR_DATAS, &entry, -1);
weight = entry->key == GLOBALS->kcur ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
g_object_set(renderer, "weight", weight, "text", entry->symbol, NULL);
}
static void
ui_cur_listview_lastmodified_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Currency *entry;
gchar buffer[256];
GDate date;
gint weight;
gtk_tree_model_get(model, iter, LST_DEFCUR_DATAS, &entry, -1);
weight = entry->key == GLOBALS->kcur ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
if(entry->mdate > 0)
{
g_date_set_julian (&date, entry->mdate);
g_date_strftime (buffer, 256-1, PREFS->date_format, &date);
//g_snprintf(buf, sizeof(buf), "%d", ope->ope_Date);
g_object_set(renderer, "weight", weight, "text", buffer, NULL);
}
else
g_object_set(renderer, "weight", weight, "text", "-", NULL);
}
static void
ui_cur_listview_rate_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Currency *entry;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gtk_tree_model_get(model, iter, LST_DEFCUR_DATAS, &entry, -1);
if(entry->key == GLOBALS->kcur)
g_object_set(renderer, "text", "-", NULL);
else
{
//g_ascii_formatd(formatd_buf, sizeof (formatd_buf), "%.6f", entry->rate);
//g_snprintf(formatd_buf, sizeof (formatd_buf), "%f", entry->rate);
hb_str_rate(formatd_buf, sizeof (formatd_buf), entry->rate);
g_object_set(renderer, "text", formatd_buf, NULL);
}
}
/* = = = = = = = = = = = = = = = = */
void
ui_cur_listview_add(GtkTreeView *treeview, Currency *item)
{
if( item->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(treeview);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFCUR_TOGGLE, FALSE,
LST_DEFCUR_DATAS, item,
-1);
}
}
guint32
ui_cur_listview_get_selected_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Currency *item;
gtk_tree_model_get(model, &iter, LST_DEFCUR_DATAS, &item, -1);
if( item != NULL )
return item->key;
}
return 0;
}
void
ui_cur_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
}
static void ui_cur_listview_populate_ghfunc(gpointer key, gpointer value, GtkTreeModel *model)
{
GtkTreeIter iter;
Currency *item = value;
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFCUR_TOGGLE , FALSE,
LST_DEFCUR_DATAS, item,
-1);
}
void ui_cur_listview_populate(GtkWidget *view)
{
GtkTreeModel *model;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
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(view), NULL); /* Detach model from view */
/* populate */
g_hash_table_foreach(GLOBALS->h_cur, (GHFunc)ui_cur_listview_populate_ghfunc, model);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
}
/* test */
/*
static void
ui_cur_listivew_rate_edited_func (GtkCellRendererText *cell,
const gchar *path_string,
const gchar *new_text,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
GtkTreeIter iter;
g_print("cell edited '%s' path %s\n", new_text, path_string);
gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
gtk_tree_model_get_iter (model, &iter, path);
Currency *item;
gtk_tree_model_get(model, &iter, LST_DEFCUR_DATAS, &item, -1);
item->rate = atof(new_text);
GLOBALS->changes_count++;
gtk_tree_path_free (path);
}
*/
GtkWidget *
ui_cur_listview_new(gboolean withtoggle)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// create list store
store = gtk_list_store_new(
NUM_LST_DEFCUR,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
// column 1: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer,
"active", LST_DEFCUR_TOGGLE,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
g_signal_connect (renderer, "toggled",
G_CALLBACK (ui_cur_listview_toggled_cb), store);
}
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
// column 1: name
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Name"));
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cur_listview_name_cell_data_function, GINT_TO_POINTER(LST_DEFCUR_DATAS), NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column 2: code
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Symbol"));
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cur_listview_symbol_cell_data_function, GINT_TO_POINTER(LST_DEFCUR_DATAS), NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column 3: base rate
renderer = gtk_cell_renderer_text_new ();
//g_object_set (renderer, "editable", TRUE, NULL);
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Exchange rate"));
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cur_listview_rate_cell_data_function, GINT_TO_POINTER(LST_DEFCUR_DATAS), NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//g_signal_connect (renderer, "edited", G_CALLBACK (ui_cur_listivew_rate_edited_func), GTK_TREE_MODEL(store));
// column 4: last modified
renderer = gtk_cell_renderer_text_new ();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Last modified"));
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cur_listview_lastmodified_cell_data_function, GINT_TO_POINTER(LST_DEFCUR_DATAS), NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeview attribute
//gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), 1);
//gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), TRUE);
//gtk_tree_view_set_reorderable (GTK_TREE_VIEW(view), TRUE);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCUR_DATAS, ui_cur_listview_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFCUR_DATAS, GTK_SORT_ASCENDING);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
** update the number sample label
*/
static void ui_cur_edit_dialog_update_sample(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_edit_dialog_data *data;
Currency cur;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar buf[128];
DB( g_printf("[ui_cur_edit] update sample\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
cur.symbol = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_symbol));
cur.sym_prefix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_symisprefix));
cur.decimal_char = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_decimalchar));
cur.grouping_char = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_groupingchar));
cur.frac_digits = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_fracdigits));
da_cur_initformat (&cur);
DB( g_print("fmt: %s\n", cur.format) );
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), cur.format, HB_NUMBER_SAMPLE);
hb_str_formatd(buf, 127, formatd_buf, &cur, TRUE);
gtk_label_set_text(GTK_LABEL(data->LB_sample), buf);
}
static void ui_cur_edit_dialog_set(GtkWidget *widget, Currency *cur)
{
struct ui_cur_edit_dialog_data *data;
Currency *base;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar label[128];
DB( g_printf("[ui_cur_edit] set\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
g_snprintf(label, 127, "%s - %s", cur->iso_code, cur->name);
gtk_label_set_text (GTK_LABEL(data->LB_name), label);
base = da_cur_get(GLOBALS->kcur);
g_snprintf(label, 127, "1 %s _=", base->iso_code);
gtk_label_set_text_with_mnemonic (GTK_LABEL(data->LB_rate), label);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_rate), cur->rate);
da_cur_initformat(cur);
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), cur->format, HB_NUMBER_SAMPLE);
hb_str_formatd(label, 127, formatd_buf, cur, TRUE);
gtk_label_set_text (GTK_LABEL(data->LB_sample), label);
hbtk_entry_set_text(GTK_ENTRY(data->ST_symbol), cur->symbol);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_symisprefix), cur->sym_prefix);
hbtk_entry_set_text(GTK_ENTRY(data->ST_decimalchar), cur->decimal_char);
hbtk_entry_set_text(GTK_ENTRY(data->ST_groupingchar), cur->grouping_char);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_fracdigits), cur->frac_digits);
}
static void ui_cur_edit_dialog_get(GtkWidget *widget, Currency *cur)
{
struct ui_cur_edit_dialog_data *data;
gdouble rate;
DB( g_printf("[ui_cur_edit] get\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
hbtk_entry_replace_text(GTK_ENTRY(data->ST_symbol), &cur->symbol);
cur->sym_prefix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_symisprefix));
hbtk_entry_replace_text(GTK_ENTRY(data->ST_decimalchar), &cur->decimal_char);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_groupingchar), &cur->grouping_char);
cur->frac_digits = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_fracdigits));
da_cur_initformat(cur);
rate = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_rate));
if(cur->rate != rate)
{
cur->rate = rate;
cur->mdate = GLOBALS->today;
}
}
void ui_cur_edit_dialog_new(GtkWindow *parent, Currency *cur)
{
struct ui_cur_edit_dialog_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *label, *widget, *expander;
gint crow, row;
data = g_malloc0(sizeof(struct ui_cur_edit_dialog_data));
if(!data) return;
dialog = gtk_dialog_new_with_buttons (
_("Edit currency"),
GTK_WINDOW (parent),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_printf("[ui_cur_edit] new dialog=%p, inst_data=%p\n", dialog, data) );
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: Currency
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(_("Currency"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = make_label(NULL, 0, 0.5);
data->LB_name = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
// group :: exchange
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(_("Exchange rate"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label(NULL, 0, 0.5);
data->LB_rate = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_exchange_rate(label);
data->NB_rate = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
gtk_widget_set_sensitive(group_grid, (GLOBALS->kcur == cur->key) ? FALSE : TRUE);
// group :: format
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(_("Format"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row++;
widget = make_label(NULL, 0, 0.5);
data->LB_sample = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
expander = gtk_expander_new_with_mnemonic (_("_Customize"));
gtk_grid_attach (GTK_GRID (group_grid), expander, 1, row, 2, 1);
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_expander_set_child (GTK_EXPANDER(expander), group_grid);
row = 1;
label = make_label_widget(_("_Symbol:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 3);
data->ST_symbol = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Is pre_fix"));
data->CM_symisprefix = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Decimal char:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 1);
data->ST_decimalchar = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Frac digits:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(label, 0.0, 8.0);
data->NB_fracdigits = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Grouping char:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 1);
data->ST_groupingchar = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//gtk_window_resize(GTK_WINDOW(dialog), 400/PHI, 400);
ui_cur_edit_dialog_set(dialog, cur);
gtk_widget_show_all(content_area);
//signals
g_signal_connect (data->ST_symbol , "changed", G_CALLBACK (ui_cur_edit_dialog_update_sample), NULL);
g_signal_connect (data->CM_symisprefix, "toggled", G_CALLBACK (ui_cur_edit_dialog_update_sample), NULL);
g_signal_connect (data->ST_decimalchar , "changed", G_CALLBACK (ui_cur_edit_dialog_update_sample), NULL);
g_signal_connect (data->ST_groupingchar, "changed", G_CALLBACK (ui_cur_edit_dialog_update_sample), NULL);
g_signal_connect (data->NB_fracdigits, "value-changed", G_CALLBACK (ui_cur_edit_dialog_update_sample), NULL);
// wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
ui_cur_edit_dialog_get(dialog, cur);
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
enum {
LST_CURSEL_NAME,
LST_CURSEL_ISO,
LST_CURSEL_FULLNAME,
LST_CURSEL_DATA,
NUM_LST_CURSEL
};
static void ui_cur_select_rowactivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
struct ui_cur_select_dialog_data *data = userdata;
gtk_dialog_response(GTK_DIALOG(data->dialog), GTK_RESPONSE_ACCEPT);
}
static GtkTreeModel *ui_cur_select_model_create (void)
{
guint i = 0;
GtkListStore *store;
GtkTreeIter iter;
Currency4217 *cur;
gchar buffer[255];
/* create list store */
store = gtk_list_store_new (NUM_LST_CURSEL,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_POINTER,
NULL
);
for (i = 0; i< n_iso4217cur; i++)
{
cur = &iso4217cur[i];
g_snprintf(buffer, 255-1, "%s - %s", cur->curr_iso_code, cur->name);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
LST_CURSEL_NAME, cur->name,
LST_CURSEL_ISO, cur->curr_iso_code,
LST_CURSEL_FULLNAME, buffer,
LST_CURSEL_DATA, cur,
-1);
}
return GTK_TREE_MODEL (store);
}
static Currency4217 *ui_cur_select_dialog_get_langue(struct ui_cur_select_dialog_data *data)
{
GtkTreeSelection *treeselection;
gboolean selected;
GtkTreeModel *model;
GtkTreeIter iter;
Currency4217 *curfmt = NULL;
DB( g_printf("\n[ui_cur_select] get langue\n") );
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_cur));
selected = gtk_tree_selection_get_selected(treeselection, &model, &iter);
if(selected)
{
gtk_tree_model_get(model, &iter, LST_CURSEL_DATA, &curfmt, -1);
DB( g_printf(" - iso is '%s'\n", curfmt->curr_iso_code) );
}
return curfmt;
}
static void
ui_cur_select_search_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct ui_cur_select_dialog_data *data = user_data;
DB( g_printf("\n[ui_cur_select] search changed\n") );
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data->modelfilter));
}
/* valid iso is empoty or 3 capital digit */
static guint currency_iso_code_valid(gchar *str)
{
guint n = 0;
while( *str )
{
if( *str >= 'A' && *str <= 'Z' )
n++;
str++;
}
return n;
}
static void
ui_cur_select_custom_validate_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_select_dialog_data *data = user_data;
gboolean custom;
gboolean valid = TRUE;
const gchar *iso, *name;
guint len;
DB( g_printf("\n[ui_cur_select] custom validate\n") );
custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_custom));
DB( g_print(" custom=%d\n", custom) );
//custom
if( custom == TRUE )
{
valid = FALSE;
name = gtk_entry_get_text (GTK_ENTRY (data->ST_custname));
iso = gtk_entry_get_text (GTK_ENTRY (data->ST_custiso));
len = currency_iso_code_valid((gchar *)iso);
DB( g_print(" name='%d', iso='%d'\n", (gint)strlen(name), len) );
if( (len==0 || len==3) && (strlen(name) >= 3 ) )
{
valid = TRUE;
// don't allow to enter stand 4217 iso code
if( len == 3 )
{
Currency4217 *stdcur = iso4217format_get((gchar *)iso);
if(stdcur != NULL)
valid = FALSE;
}
}
}
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), GTK_RESPONSE_ACCEPT, valid);
}
static void
ui_cur_select_custom_activate_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_select_dialog_data *data = user_data;
gboolean custom;
DB( g_printf("\n[ui_cur_select] custom activate\n") );
custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_custom));
DB( g_print(" custom=%d\n", custom) );
gtk_widget_set_sensitive(data->ST_search, !custom);
gtk_widget_set_sensitive(data->LV_cur, !custom);
hb_widget_visible (data->LB_custname, custom);
hb_widget_visible (data->ST_custname, custom);
hb_widget_visible (data->LB_custiso, custom);
hb_widget_visible (data->ST_custiso, custom);
if(custom)
{
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_cur)));
gtk_window_set_focus(GTK_WINDOW(data->dialog), data->ST_custname);
}
ui_cur_select_custom_validate_cb(data->dialog, data);
}
static gboolean
ui_cur_select_model_func_visible (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
// Visible if row is non-empty and first column is “HI”
gchar *str;
gboolean visible = TRUE;
GtkEntry *entry = data;
if(!GTK_IS_ENTRY(entry))
return TRUE;
gchar *needle = g_ascii_strdown(gtk_entry_get_text(entry), -1);
gtk_tree_model_get (model, iter, LST_CURSEL_FULLNAME, &str, -1);
gchar *haystack = g_ascii_strdown(str, -1);
if (str && g_strrstr (haystack, needle) == NULL)
{
visible = FALSE;
}
DB( g_print("filter: '%s' '%s' %d\n", str, needle, visible) );
g_free(haystack);
g_free(needle);
g_free (str);
return visible;
}
gint ui_cur_select_dialog_new(GtkWindow *parent, gint select_mode, struct curSelectContext *ctx)
{
struct ui_cur_select_dialog_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *scrollwin, *treeview, *label, *widget;
gint crow, row;
data = g_malloc0(sizeof(struct ui_cur_select_dialog_data));
if(!data) return 0;
dialog = gtk_dialog_new_with_buttons (
(select_mode == CUR_SELECT_MODE_BASE) ? _("Select base currency") : _("Select currency"),
GTK_WINDOW (parent),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_printf("\n[ui_cur_select] new dialog=%p, inst_data=%p\n", dialog, data) );
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: Search
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);
row = 0;
widget = make_search();
data->ST_search = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
row++;
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_grid_attach (GTK_GRID (group_grid), scrollwin, 1, row, 4, 1);
gtk_widget_set_vexpand (scrollwin, TRUE);
//test treefilter
data->model = ui_cur_select_model_create();
data->modelfilter = gtk_tree_model_filter_new(GTK_TREE_MODEL(data->model), NULL);
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(data->modelfilter), ui_cur_select_model_func_visible, data->ST_search, NULL);
data->sortmodel = gtk_tree_model_sort_new_with_model(data->modelfilter);
//treeview
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(data->sortmodel));
data->LV_cur = treeview;
//gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview), LST_CURSEL_NAME);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(data->sortmodel), LST_CURSEL_NAME, GTK_SORT_ASCENDING);
//g_object_unref (model);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
// populate list
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, "text", LST_CURSEL_NAME, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_CURSEL_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("ISO Code"), renderer, "text", LST_CURSEL_ISO, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_CURSEL_ISO);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
// group :: Custom
row++;
widget = gtk_check_button_new_with_mnemonic (_("Add a custom _currency"));
data->CM_custom = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
//custom currency (crypto and discontinued)
row++;
label = make_label_widget(_("_Name:"));
data->LB_custname = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_custname = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
label = make_label_widget(_("_ISO:"));
data->LB_custiso = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
widget = make_string_maxlength(label, 3);
data->ST_custiso = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
g_signal_connect (G_OBJECT (data->CM_custom) , "toggled", G_CALLBACK (ui_cur_select_custom_activate_cb), data);
g_signal_connect (G_OBJECT (data->ST_custname), "changed", G_CALLBACK (ui_cur_select_custom_validate_cb), data);
g_signal_connect (G_OBJECT (data->ST_custiso) , "changed", G_CALLBACK (ui_cur_select_custom_validate_cb), data);
gtk_window_resize(GTK_WINDOW(dialog), 400/PHI, 400);
gtk_widget_show_all(content_area);
if( select_mode != CUR_SELECT_MODE_CUSTOM )
{
hb_widget_visible (data->CM_custom, FALSE);
}
hb_widget_visible (data->LB_custname, FALSE);
hb_widget_visible (data->ST_custname, FALSE);
hb_widget_visible (data->LB_custiso, FALSE);
hb_widget_visible (data->ST_custiso, FALSE);
// signals
g_signal_connect (G_OBJECT(data->ST_search), "search-changed", G_CALLBACK (ui_cur_select_search_changed_cb), data);
g_signal_connect (G_OBJECT(data->LV_cur), "row-activated", G_CALLBACK (ui_cur_select_rowactivated), data);
//init picker struct
memset(ctx, 0, sizeof(struct curSelectContext));
// wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
gboolean custom;
custom = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_custom));
if(!custom)
{
ctx->cur_4217 = ui_cur_select_dialog_get_langue(data);
}
else
//never fill custom in base mode
if( select_mode != CUR_SELECT_MODE_BASE )
{
ctx->cur_name = g_strdup(gtk_entry_get_text (GTK_ENTRY(data->ST_custname)));
ctx->cur_iso = g_strdup(gtk_entry_get_text (GTK_ENTRY(data->ST_custiso)));
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return result;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gint ui_cur_manage_dialog_update_currencies(GtkWindow *parent, GString *node)
{
GError *error = NULL;
gboolean retcode = FALSE;
DB( g_printf("\n[ui_cur_manage] update currencies\n") );
// do nothing if just the base currency
if(da_cur_length() <= 1)
{
DB( g_print(" abort: no currency\n") );
return TRUE;
}
//TODO: add a force option ?
// add 5.6.2 as the online currency only update every 24h
// avoid to call the API too often
// this set into hbfile_file_get_time_modified()
// removed in 5.7
/*if( GLOBALS->xhb_obsoletecurr == FALSE )
{
DB( g_print(" abort: file saved less than 24h\n") );
//TODO maybe
ui_dialog_msg_infoerror(GTK_WINDOW(parent), GTK_MESSAGE_ERROR,
_("Update online error"),
_("Already been updated in last 24h"),
NULL
);
return TRUE;
}*/
retcode = currency_online_sync(&error, node);
DB( g_print("retcode: %d\n", retcode) );
if(!retcode)
{
gchar *msg = _("Unknown error");
if( error )
msg = error->message;
g_warning("update online: '%s'", msg);
ui_dialog_msg_infoerror(GTK_WINDOW(parent), GTK_MESSAGE_ERROR,
_("Update online error"),
msg,
NULL
);
if( error )
g_error_free (error);
}
return retcode;
}
static void
ui_cur_manage_dialog_sync(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
GtkTextBuffer *buffer;
GtkTextIter iter;
GString *node;
gboolean retcode;
DB( g_printf("\n[ui_cur_manage] sync online\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
node = g_string_new(NULL);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data->TB_log));
gtk_text_buffer_set_text (buffer, "", 0);
retcode = ui_cur_manage_dialog_update_currencies(GTK_WINDOW(data->dialog), node);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
gtk_text_buffer_insert (buffer, &iter, node->str, -1);
g_string_free(node, TRUE);
if(retcode == TRUE)
{
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_cur));
//todo: (or not) msg with changes
}
}
static void ui_cur_manage_dialog_update(GtkWidget *treeview, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Currency *item;
gboolean sensitive;
DB( g_printf("\n[ui_cur_manage] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
sensitive = da_cur_length() <= 1 ? FALSE : TRUE;
gtk_widget_set_sensitive (data->BB_update, sensitive);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cur));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFCUR_DATAS, &item, -1);
gtk_widget_set_sensitive(data->BT_edit, TRUE);
sensitive = !(currency_is_used(item->key));
//gtk_widget_set_sensitive(data->BT_mov, sensitive);
//gtk_widget_set_sensitive(data->BT_mod, sensitive);
gtk_widget_set_sensitive(data->BT_del, sensitive);
//disable set as base on actual base currency
//disable on custom currency
sensitive = TRUE;
if( (item->key == GLOBALS->kcur) || (item->flags & CF_CUSTOM) )
sensitive = FALSE;
gtk_widget_set_sensitive(data->BT_base, sensitive);
}
else
{
gtk_widget_set_sensitive(data->BT_edit, FALSE);
gtk_widget_set_sensitive(data->BT_del , FALSE);
gtk_widget_set_sensitive(data->BT_base, FALSE);
}
}
static void
ui_cur_manage_dialog_add(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
struct curSelectContext selectCtx;
gint result;
gboolean added;
DB( g_printf("\n[ui_cur_manage] add\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
result = ui_cur_select_dialog_new(GTK_WINDOW(data->dialog), CUR_SELECT_MODE_CUSTOM, &selectCtx);
if( result == GTK_RESPONSE_ACCEPT )
{
Currency *item = NULL;
added = FALSE;
if( selectCtx.cur_4217 != NULL )
{
Currency4217 *curfmt;
curfmt = selectCtx.cur_4217;
DB( g_printf("- user selected: '%s' '%s'\n", curfmt->curr_iso_code, curfmt->name) );
item = da_cur_get_by_iso_code(curfmt->curr_iso_code);
if( item == NULL )
{
item = currency_add_from_user(curfmt);
added = TRUE;
}
}
else
{
DB( g_printf("- user custom: '%s' '%s'\n", selectCtx.cur_iso, selectCtx.cur_name) );
item = da_cur_malloc ();
item->flags |= CF_CUSTOM;
item->name = g_strdup(selectCtx.cur_name);
item->iso_code = g_strdup(selectCtx.cur_iso);
item->symbol = g_strdup(item->iso_code);
item->frac_digits = 2;
item->sym_prefix = FALSE;
item->decimal_char = g_strdup(".");
item->grouping_char = NULL;
added = da_cur_append(item);
if( !added )
{
//not append (duplicate)
da_cur_free (item);
item = NULL;
}
g_free(selectCtx.cur_iso);
g_free(selectCtx.cur_name);
}
if( added )
{
ui_cur_listview_add(GTK_TREE_VIEW(data->LV_cur), item);
gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cur))));
ui_cur_manage_dialog_update (widget, user_data);
GLOBALS->changes_count++;
}
}
}
static void
ui_cur_manage_dialog_modify(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_printf("\n[ui_cur_manage] modify\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cur));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Currency *item;
gtk_tree_model_get(model, &iter, LST_DEFCUR_DATAS, &item, -1);
if( item!= NULL )
{
ui_cur_edit_dialog_new(GTK_WINDOW(data->dialog), item);
GLOBALS->changes_count++;
}
}
}
static void ui_cur_manage_dialog_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
guint32 key;
gboolean do_delete, result;
DB( g_printf("\n[ui_cur_manage] delete\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
do_delete = TRUE;
key = ui_cur_listview_get_selected_key(GTK_TREE_VIEW(data->LV_cur));
if( key > 0 )
{
Currency *cur;
gchar *title;
gchar *secondtext;
if( currency_is_used(key) == TRUE )
{
do_delete = FALSE;
}
else
{
cur = da_cur_get(key);
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), cur->name);
secondtext = _("If you delete a currency, it will be permanently lost.");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
do_delete = (result == GTK_RESPONSE_OK) ? TRUE :FALSE;
}
if( do_delete )
{
ui_cur_listview_remove_selected(GTK_TREE_VIEW(data->LV_cur));
da_cur_delete(key);
ui_cur_manage_dialog_update (widget, user_data);
data->change++;
}
}
}
static void ui_cur_manage_dialog_setbase(GtkWidget *widget, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
guint32 key;
gboolean do_change;
DB( g_printf("\n[ui_cur_manage] setbase\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
key = ui_cur_listview_get_selected_key(GTK_TREE_VIEW(data->LV_cur));
if( key > 0 )
{
do_change = ui_dialog_msg_question(
GTK_WINDOW(data->dialog),
_("Change the base currency"),
_("If you proceed, rates of other currencies\n"
"will be set to 0, don't forget to update it"),
NULL
);
if(do_change == GTK_RESPONSE_YES)
{
hbfile_change_basecurrency(key);
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(data->LV_cur));
}
}
}
static void ui_cur_manage_dialog_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_cur_manage_dialog_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void ui_cur_manage_dialog_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
//model = gtk_tree_view_get_model(treeview);
//gtk_tree_model_get_iter_first(model, &iter);
//if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter) == FALSE)
//{
ui_cur_manage_dialog_modify(GTK_WIDGET(treeview), NULL);
//}
}
static void ui_cur_manage_dialog_setup(struct ui_cur_manage_dialog_data *data)
{
DB( g_printf("\n[ui_cur_manage] setup\n") );
DB( g_print(" init data\n") );
data->change = 0;
DB( g_print(" populate\n") );
ui_cur_listview_populate(data->LV_cur);
//ui_cur_combobox_populate(data->CY_curr, GLOBALS->h_cur);
//ui_cur_combobox_set_active(GTK_COMBO_BOX(data->CY_curr), GLOBALS->kcur);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
//g_signal_connect (G_OBJECT (data->ST_name), "activate", G_CALLBACK (ui_cur_manage_dialog_add), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cur)), "changed", G_CALLBACK (ui_cur_manage_dialog_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_cur), "row-activated", G_CALLBACK (ui_cur_manage_dialog_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_add), "clicked", G_CALLBACK (ui_cur_manage_dialog_add), NULL);
g_signal_connect (G_OBJECT (data->BT_del), "clicked", G_CALLBACK (ui_cur_manage_dialog_delete), NULL);
g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_cur_manage_dialog_modify), NULL);
//g_signal_connect (G_OBJECT (data->BT_mov), "clicked", G_CALLBACK (ui_cur_manage_dialog_move), NULL);
g_signal_connect (G_OBJECT (data->BT_base), "clicked", G_CALLBACK (ui_cur_manage_dialog_setbase), NULL);
}
static gboolean ui_cur_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_cur_manage_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_printf("\n[ui_cur_manage] mapped\n") );
ui_cur_manage_dialog_setup(data);
ui_cur_manage_dialog_update(data->LV_cur, NULL);
data->mapped_done = TRUE;
return FALSE;
}
GtkWidget *ui_cur_manage_dialog (void)
{
struct ui_cur_manage_dialog_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid, *vbox, *bbox, *tbar;
GtkWidget *widget, *label, *scrollwin, *treeview, *expander;
gint crow, row, w, h, dw, dh;
data = g_malloc0(sizeof(struct ui_cur_manage_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Currencies"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 1:1
dw = (dh * 1) / 1;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_printf("[ui_cur_manage] new dialog=%p, inst_data=%p\n", dialog, data) );
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: --------
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);
row = 1;
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->BB_update = bbox;
gtk_grid_attach (GTK_GRID(group_grid), bbox, 0, row, 2, 1);
widget = make_image_button (ICONNAME_HB_REFRESH, NULL);
gtk_box_prepend (GTK_BOX(bbox), widget);
g_signal_connect (G_OBJECT (widget), "clicked", G_CALLBACK (ui_cur_manage_dialog_sync), NULL);
widget = make_label_widget (_("Update online"));
gtk_box_prepend(GTK_BOX(bbox), widget);
//5.7.2 log
row++;
expander = gtk_expander_new_with_mnemonic(_("Call log"));
gtk_widget_set_hexpand(expander, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), expander, 0, row, 2, 1);
label = gtk_text_view_new();
data->TB_log = label;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (scrollwin, -1, 128);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), label);
//gtk_widget_set_hexpand (scrollwin, TRUE);
//gtk_widget_set_vexpand (scrollwin, TRUE);
gtk_expander_set_child(GTK_EXPANDER(expander), scrollwin);
//hb_widget_set_margin(GTK_WIDGET(scrollwin), SPACING_MEDIUM);
// list
row++;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_grid_attach (GTK_GRID (group_grid), vbox, 0, row, 2, 1);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
treeview = ui_cur_listview_new(FALSE);
data->LV_cur = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_set_vexpand (scrollwin, TRUE);
gtk_widget_set_hexpand (scrollwin, TRUE);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_del = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
data->BT_edit = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = gtk_button_new_with_mnemonic(_("Set as base"));
data->BT_base = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_cur_manage_mapped), &dialog);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
//wait for the user
gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
GLOBALS->changes_count += data->change;
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-pref-data.c 0000664 0001750 0001750 00000025527 14736461407 016030 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-pref-data.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/*
source:
http://en.wikipedia.org/wiki/Currencies_of_the_European_Union
http://www.xe.com/euro.php
http://fr.wikipedia.org/wiki/Liste_des_unit%C3%A9s_mon%C3%A9taires_remplac%C3%A9es_par_l%27euro
http://www.inter-locale.com/LocalesDemo.jsp
*/
EuroParams euro_params[] =
{
// id, mceii, ISO , country , rate , symb , prfx , dec, grp, frac
// ---------------------------------------------------------------------
{ 0, TRUE , "" , "--------" , 0.0 , "" , FALSE, ",", ".", 2 },
{ 1, TRUE , "ATS", "Austria" , 13.7603 , "S" , TRUE , ",", ".", 2 }, // -S 1.234.567,89
{ 2, TRUE , "BEF", "Belgium" , 40.3399 , "BF" , TRUE , ",", ".", 2 }, // BF 1.234.567,89 -
{ 20, FALSE, "BGN", "Bulgaria*" , 1.95583 , "лв." , TRUE , ",", " ", 2 }, // fixé
{ 24, TRUE , "HRK", "Croatia" , 7.5345 , "kn" , FALSE, "" , ".", 0 }, //
{ 14, TRUE , "CYP", "Cyprus" , 0.585274, "£" , TRUE , ",", "" , 2 }, //
{ 23, FALSE, "CZK", "Czech Republic*" , 0.0 , "Kč" , FALSE, ",", " ", 2 }, // non-fixé - 2015 earliest
{ 26, FALSE, "DKK", "Denmark*" , 7.4603 , "kr." , TRUE , ".", ",", 2 }, // fixé
{ 17, TRUE , "EEK", "Estonia" , 15.6466 , "kr" , FALSE, ",", " ", 2 }, //
{ 3, TRUE , "FIM", "Finland" , 5.94573 , "mk" , FALSE, ",", " ", 2 }, // -1 234 567,89 mk
{ 4, TRUE , "FRF", "France" , 6.55957 , "F" , FALSE, ",", " ", 2 }, // -1 234 567,89 F
{ 5, TRUE , "DEM", "Germany" , 1.95583 , "DM" , FALSE, ",", ".", 2 }, // -1.234.567,89 DM
{ 6, TRUE , "GRD", "Greece" , 340.750 , "d" , TRUE , ".", ",", 2 }, // ??
{ 21, FALSE, "HUF", "Hungary*" , 0.0 , "Ft" , TRUE , ",", " ", 2 }, // non-fixé - No current target for euro
{ 7, TRUE , "IEP", "Ireland" , 0.787564, "£" , TRUE , ".", ",", 2 }, // -£1,234,567.89
{ 8, TRUE , "ITL", "Italy" , 1936.27 , "L" , TRUE , "" , ".", 0 }, // L -1.234.567
{ 18, TRUE , "LVL", "Latvia" , 0.702804, "lat.", FALSE, ",", "" , 2 }, // jan. 2014
{ 19, TRUE , "LTL", "Lithuania" , 3.45280 , "Lt" , FALSE, ",", "" , 2 }, // jan. 2015
{ 9, TRUE , "LUF", "Luxembourg" , 40.3399 , "LU" , TRUE , ",", ".", 2 }, // LU 1.234.567,89 -
{ 15, TRUE , "MTL", "Malta" , 0.429300, "Lm" , TRUE , ",", "" , 2 }, //
{ 10, TRUE , "NLG", "Netherlands" , 2.20371 , "F" , TRUE , ",", ".", 2 }, // F 1.234.567,89-
{ 25, FALSE, "PLN", "Poland*" , 0.0 , "zł" , FALSE, ",", "" , 2 }, // non-fixé - No current target for euro
{ 11, TRUE , "PTE", "Portugal" , 200.482 , "Esc.", FALSE, "$", ".", 2 }, // -1.234.567$89 Esc.
{ 22, FALSE, "RON", "Romania*" , 0.0 , "Leu" , FALSE, ",", ".", 2 }, // non-fixé - 2015 target for euro earliest
{ 16, TRUE , "SKK", "Slovak Republic" , 30.12600, "Sk" , FALSE, ",", " ", 2 }, //
{ 13, TRUE , "SIT", "Slovenia" , 239.640 , "tol" , TRUE , ",", ".", 2 }, //
{ 12, TRUE , "ESP", "Spain" , 166.386 , "Pts" , TRUE , "" , ".", 0 }, // -Pts 1.234.567
{ 27, FALSE, "SEK", "Sweden*" , 0.0 , "kr" , FALSE, ",", " ", 2 }, // non-fixé
//United Kingdom
// { " ", "" , 1.00000 , "" , "" , FALSE, ",", "", 2 },
};
guint nb_euro_params = G_N_ELEMENTS(euro_params);
//European — €1.234.567,89 EUR
EuroParams euro_params_euro =
{
// id, mceii, ISO , country , rate , symb , prfx , dec, grp, frac
// ---------------------------------------------------------------------
0, TRUE , "EUR" , "Non MCEII" , 0.0 , "€" , TRUE, ",", ".", 2 ,
};
EuroParams *euro_country_get(guint ctryid)
{
DB( g_print("\n[pref-data] euro_country_get\n") );
for (guint i = 0; i < G_N_ELEMENTS (euro_params); i++)
{
if( euro_params[i].id == ctryid )
{
return &euro_params[i];
}
}
return NULL;
}
gboolean euro_country_is_mceii(gint ctryid)
{
gboolean retval = FALSE;
DB( g_print("\n[pref-data] euro_country_is_mceii\n") );
for (guint i = 0; i < G_N_ELEMENTS (euro_params); i++)
{
if( euro_params[i].id == ctryid )
{
retval = euro_params[i].mceii;
DB( g_print(" id (country)=%d => %d mceii %d\n", ctryid, i, euro_params[i].mceii) );
break;
}
}
return retval;
}
gboolean euro_country_notmceii_rate_update(guint ctryid)
{
DB( g_print("\n[pref-data] euro_country_notmceii_rate_update\n") );
if( PREFS->euro_mceii == FALSE )
{
Currency *base = da_cur_get (GLOBALS->kcur);
EuroParams *ctry = euro_country_get(ctryid);
if( base && ctry )
{
DB( g_print(" check update minor rate: %s == %s ?\n", base->iso_code, ctry->iso ) );
if( !strcmp(base->iso_code, ctry->iso) )
{
Currency *eur = da_cur_get_by_iso_code("EUR");
if( eur != NULL )
{
PREFS->euro_value = eur->rate;
DB( g_print(" >update euro minor rate to %.6f for %s\n", eur->rate, ctry->iso ) );
return TRUE;
}
}
else
{
DB( g_print(" >skip: base is different\n" ) );
}
}
}
return FALSE;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
LangName languagenames[] =
{
// af ar ast be bg ca cs cy da de el en_AU en_CA en_GB es et eu fa fi fr ga gl he hr hu id is it
//ja ka ko lt lv ms nb nds nl oc pl pt_BR pt pt_PT ro ru si sk sl sr sv tr uk vi zh_CN zh_TW
{ "aa", "Afar" },
{ "ab", "Abkhazian" },
{ "ae", "Avestan" },
{ "af", "Afrikaans" },
{ "ak", "Akan" },
{ "am", "Amharic" },
{ "an", "Aragonese" },
{ "ar", "Arabic" },
{ "as", "Assamese" },
{ "ast", "Asturian, Bable, Leonese, Asturleonese" },
{ "av", "Avaric" },
{ "ay", "Aymara" },
{ "az", "Azerbaijani" },
{ "ba", "Bashkir" },
{ "be", "Belarusian" },
{ "bg", "Bulgarian" },
{ "bh", "Bihari" },
{ "bi", "Bislama" },
{ "bm", "Bambara" },
{ "bn", "Bengali" },
{ "bo", "Tibetan" },
{ "br", "Breton" },
{ "bs", "Bosnian" },
{ "ca", "Catalan" },
{ "ce", "Chechen" },
{ "ch", "Chamorro" },
{ "ckb", "Kurdish, Central" },
{ "co", "Corsican" },
{ "cr", "Cree" },
{ "cs", "Czech" },
{ "cu", "Old Church Slavonic" },
{ "cv", "Chuvash" },
{ "cy", "Welsh" },
{ "da", "Danish" },
{ "de", "German" },
{ "dv", "Divehi" },
{ "dz", "Dzongkha" },
{ "ee", "Ewe" },
{ "el", "Greek" },
{ "en", "English" },
{ "eo", "Esperanto" },
{ "es", "Spanish" },
{ "et", "Estonian" },
{ "eu", "Basque" },
{ "fa", "Persian" },
{ "ff", "Fulah" },
{ "fi", "Finnish" },
{ "fj", "Fijian" },
{ "fo", "Faroese" },
{ "fr", "French" },
{ "fy", "Western Frisian" },
{ "ga", "Irish" },
{ "gd", "Scottish Gaelic" },
{ "gl", "Galician" },
{ "gn", "Guarani" },
{ "gu", "Gujarati" },
{ "gv", "Manx" },
{ "ha", "Hausa" },
{ "he", "Hebrew" },
{ "hi", "Hindi" },
{ "ho", "Hiri Motu" },
{ "hr", "Croatian" },
{ "ht", "Haitian" },
{ "hu", "Hungarian" },
{ "hy", "Armenian" },
{ "hz", "Herero" },
{ "ia", "Interlingua" },
{ "id", "Indonesian" },
{ "ie", "Interlingue" },
{ "ig", "Igbo" },
{ "ii", "Sichuan Yi" },
{ "ik", "Inupiaq" },
{ "io", "Ido" },
{ "is", "Icelandic" },
{ "it", "Italian" },
{ "iu", "Inuktitut" },
{ "ja", "Japanese" },
{ "jv", "Javanese" },
{ "ka", "Georgian" },
{ "kg", "Kongo" },
{ "ki", "Kikuyu" },
{ "kj", "Kwanyama" },
{ "kk", "Kazakh" },
{ "kl", "Kalaallisut" },
{ "km", "Khmer" },
{ "kn", "Kannada" },
{ "ko", "Korean" },
{ "kr", "Kanuri" },
{ "ks", "Kashmiri" },
{ "ku", "Kurdish" },
{ "kv", "Komi" },
{ "kw", "Cornish" },
{ "ky", "Kirghiz" },
{ "la", "Latin" },
{ "lb", "Luxembourgish" },
{ "lg", "Ganda" },
{ "li", "Limburgish" },
{ "ln", "Lingala" },
{ "lo", "Lao" },
{ "lt", "Lithuanian" },
{ "lu", "Luba-Katanga" },
{ "lv", "Latvian" },
{ "mg", "Malagasy" },
{ "mh", "Marshallese" },
{ "mi", "Māori" },
{ "mk", "Macedonian" },
{ "ml", "Malayalam" },
{ "mn", "Mongolian" },
{ "mo", "Moldavian" },
{ "mr", "Marathi" },
{ "ms", "Malay" },
{ "mt", "Maltese" },
{ "my", "Burmese" },
{ "na", "Nauru" },
{ "nb", "Norwegian Bokmål" },
{ "nd", "North Ndebele" },
{ "nds", "Low German, Low Saxon" },
{ "ne", "Nepali" },
{ "ng", "Ndonga" },
{ "nl", "Dutch" },
{ "nn", "Norwegian Nynorsk" },
{ "no", "Norwegian" },
{ "nr", "South Ndebele" },
{ "nv", "Navajo" },
{ "ny", "Chichewa" },
{ "oc", "Occitan" },
{ "oj", "Ojibwa" },
{ "om", "Oromo" },
{ "or", "Oriya" },
{ "os", "Ossetian" },
{ "pa", "Panjabi" },
{ "pi", "Pāli" },
{ "pl", "Polish" },
{ "ps", "Pashto" },
{ "pt", "Portuguese" },
{ "qu", "Quechua" },
{ "rm", "Romansh" },
{ "rn", "Kirundi" },
{ "ro", "Romanian" },
{ "ru", "Russian" },
{ "rw", "Kinyarwanda" },
{ "sa", "Sanskrit" },
{ "sc", "Sardinian" },
{ "sd", "Sindhi" },
{ "se", "Northern Sami" },
{ "sg", "Sango" },
{ "si", "Sinhalese" },
{ "sk", "Slovak" },
{ "sl", "Slovene" },
{ "sm", "Samoan" },
{ "sn", "Shona" },
{ "so", "Somali" },
{ "sq", "Albanian" },
{ "sr", "Serbian" },
{ "ss", "Swati" },
{ "st", "Sotho" },
{ "su", "Sundanese" },
{ "sv", "Swedish" },
{ "sw", "Swahili" },
{ "ta", "Tamil" },
{ "te", "Telugu" },
{ "tg", "Tajik" },
{ "th", "Thai" },
{ "ti", "Tigrinya" },
{ "tk", "Turkmen" },
{ "tl", "Tagalog" },
{ "tn", "Tswana" },
{ "to", "Tonga" },
{ "tr", "Turkish" },
{ "ts", "Tsonga" },
{ "tt", "Tatar" },
{ "tw", "Twi" },
{ "ty", "Tahitian" },
{ "ug", "Uighur" },
{ "uk", "Ukrainian" },
{ "ur", "Urdu" },
{ "uz", "Uzbek" },
{ "ve", "Venda" },
{ "vi", "Viêt Namese" },
{ "vo", "Volapük" },
{ "wa", "Walloon" },
{ "wo", "Wolof" },
{ "xh", "Xhosa" },
{ "yi", "Yiddish" },
{ "yo", "Yoruba" },
{ "za", "Zhuang" },
{ "zh", "Chinese" },
{ "zu", "Zulu" }
};
gchar *languagename_get(const gchar *locale)
{
DB( g_print("\n[pref-data] languagename_get\n") );
for (guint i = 0; i < G_N_ELEMENTS (languagenames); i++)
{
if( g_ascii_strncasecmp(locale, languagenames[i].locale, -1) == 0 )
return languagenames[i].name;
}
return NULL;
}
homebank-5.9.7/src/rep-budget.c 0000644 0001750 0001750 00000221234 15121313346 015606 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "rep-budget.h"
#include "list-operation.h"
#include "gtk-chart-progress.h"
#include "gtk-dateentry.h"
#include "hbtk-switcher.h"
#include "dsp-mainwindow.h"
#include "ui-transaction.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* prototypes */
static void repbudget_compute(GtkWidget *widget, gpointer user_data);
static void repbudget_sensitive(GtkWidget *widget, gpointer user_data);
static void repbudget_update_daterange(GtkWidget *widget, gpointer user_data);
static void repbudget_update_chart(GtkWidget *widget, gpointer user_data);
static void repbudget_update_detail(GtkWidget *widget, gpointer user_data);
static void repbudget_detail(GtkWidget *widget, gpointer user_data);
static gchar *repbudget_compute_title(gint mode);
static void repbudget_selection(GtkTreeSelection *treeselection, gpointer user_data);
extern gchar *CYA_REPORT_MODE[];
extern HbKvData CYA_KIND[];
/* = = = = = = = = = = = = = = = = */
static void lst_repbud_to_string_row(GString *node, ToStringMode mode, HbRepBudMode uimode, GtkTreeModel *model, GtkTreeIter *iter)
{
guint32 key;
gchar sep, *name, *status;
gdouble spent, budget, result;
gint fulfilled;
gtk_tree_model_get (model, iter,
LST_BUDGET_KEY, &key,
LST_BUDGET_NAME, &name,
LST_BUDGET_SPENT, &spent,
LST_BUDGET_BUDGET, &budget,
LST_BUDGET_FULFILLED, &fulfilled,
LST_BUDGET_RESULT, &result,
LST_BUDGET_STATUS, &status,
-1);
//#2033298 we get fullname for export
if( uimode == REP_BUD_MODE_TOTAL )
{
Category *catitem = da_cat_get(key);
if( catitem != NULL )
{
g_free(name);
name = g_strdup( da_cat_get_fullname(catitem) );
}
}
//2023696 add unbudgeted
if( key != LST_BUDGET_POS_UNBUDGETED )
{
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
g_string_append (node, name );
g_string_append_c(node, sep);
_format_decimal(node, mode, spent);
g_string_append_c(node, sep);
_format_decimal(node, mode, budget);
g_string_append_c(node, sep);
g_string_append_printf(node, "%d %%", fulfilled);
g_string_append_c(node, sep);
_format_decimal(node, mode, result);
g_string_append_c(node, sep);
g_string_append (node, status );
g_string_append_c(node, '\n');
}
//leak
g_free(name);
g_free(status);
}
static GString *lst_repbud_to_string(ToStringMode mode, GtkTreeView *treeview, HbRepBudMode uimode, gchar *title, gboolean clipboard)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
gchar sep;
node = g_string_new(NULL);
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
// header
g_string_append (node, (title == NULL) ? _("Category") : title );
g_string_append_c (node, sep );
g_string_append (node, _("Spent") );
g_string_append_c (node, sep );
g_string_append (node, _("Budget") );
g_string_append_c (node, sep );
g_string_append (node, _("Fulfilled") );
g_string_append_c (node, sep );
g_string_append (node, _("Result") );
g_string_append_c (node, sep );
g_string_append_c(node, '\n');
// lines
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
lst_repbud_to_string_row(node, mode, uimode, model, &iter);
// children ?
valid = gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while (valid)
{
lst_repbud_to_string_row(node, mode, uimode, model, &child);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
/*
**
** The function should return:
** a negative integer if the first value comes before the second,
** 0 if they are equal,
** or a positive integer if the first value comes after the second.
*/
static gint budget_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
gint pos1, pos2;
HbRepBudMode tmpmode;
gint csid;
GtkSortType cso;
gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &csid, &cso);
tmpmode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "mode-data"));
//DB( g_print(" budget compare column=%d order=%s tmpmode=%d\n", sortcol, sort_order == GTK_SORT_ASCENDING ? "ASC" : "DESC" , tmpmode) );
gtk_tree_model_get(model, a, LST_BUDGET_POS, &pos1, -1);
gtk_tree_model_get(model, b, LST_BUDGET_POS, &pos2, -1);
//total always at bottom
if( pos1 == LST_BUDGET_POS_UNBUDGETED )
{
retval = cso == GTK_SORT_ASCENDING ? 1 : -1;
//DB( g_print(" sort p1=%d ? p2=%d = %d\n", pos1, pos2, retval) );
}
else
{
if( pos2 == LST_BUDGET_POS_UNBUDGETED )
{
retval = cso == GTK_SORT_ASCENDING ? -1 : 1;
//DB( g_print(" sort p1=%d ? p2=%d = %d\n", pos1, pos2, retval) )
}
else
{
switch( sortcol )
{
case LST_BUDGET_NAME:
{
if( tmpmode == REP_BUD_MODE_TIME )
{
//DB( g_print(" retval = %d - %d\n", pos1, pos2) );
retval = pos1 - pos2;
}
else
{
gchar *entry1, *entry2;
gtk_tree_model_get(model, a, LST_BUDGET_NAME, &entry1, -1);
gtk_tree_model_get(model, b, LST_BUDGET_NAME, &entry2, -1);
retval = hb_string_utf8_compare(entry1, entry2);
//leak
g_free(entry2);
g_free(entry1);
}
}
break;
default:
{
gdouble val1, val2;
gtk_tree_model_get(model, a, csid, &val1, -1);
gtk_tree_model_get(model, b, csid, &val2, -1);
//DB( g_print(" retval = %.2f - %2f\n", val1, val2) );
retval = (gint)(val1 - val2);
}
break;
}
}
}
//DB( g_print(" retval = %d\n", retval) );
return retval;
}
static void
lst_repbud_cell_data_function_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gint tmpmode;
tmpmode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "mode-data"));
if( tmpmode == REP_BUD_MODE_TOTAL )
{
Category *item;
gint pos;
guint32 key;
gchar *markup = NULL;
gtk_tree_model_get(model, iter,
LST_BUDGET_POS, &pos,
LST_BUDGET_KEY, &key,
-1);
if( pos != LST_BUDGET_POS_UNBUDGETED )
{
item = da_cat_get(key);
if(item)
{
markup = item->typename;
}
}
else
{
goto libname;
}
g_object_set(renderer, "markup", markup, NULL);
}
else
libname:
{
gchar *name;
gtk_tree_model_get(model, iter,
LST_BUDGET_NAME, &name,
-1);
g_object_set(renderer, "text", name, NULL);
g_free(name);
}
}
static void lst_repbud_cell_data_function_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar *color;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint column_id = GPOINTER_TO_INT(user_data);
gtk_tree_model_get(model, iter, column_id, &value, -1);
if( value )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
if( column_id == LST_BUDGET_RESULT)
color = get_minimum_color_amount (value, 0.0);
else
color = get_normal_color_amount(value);
g_object_set(renderer,
"foreground", color,
"text", buf,
NULL);
}
else
{
g_object_set(renderer, "text", "", NULL);
}
}
static void lst_repbud_cell_data_function_result (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar *color;
gchar *status;
gint column_id = GPOINTER_TO_INT(user_data);
gtk_tree_model_get(model, iter,
column_id, &value,
LST_BUDGET_STATUS, &status,
-1);
if( value )
{
color = get_minimum_color_amount (value, 0.0);
g_object_set(renderer,
"foreground", color,
"text", status,
NULL);
}
else
{
g_object_set(renderer, "text", "", NULL);
}
//leak
g_free(status);
}
static void lst_repbud_cell_data_function_fulfilled (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble budget;
gint rawrate;
gchar buf[16];
gtk_tree_model_get(model, iter,
LST_BUDGET_BUDGET, &budget,
LST_BUDGET_FULFILLED, &rawrate,
-1);
if( hb_amount_cmp(budget, 0.0) != 0 )
{
g_snprintf(buf, sizeof(buf), "%d %%", rawrate);
g_object_set(renderer, "text", buf, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static GtkTreeViewColumn *lst_repbud_column_create_amount(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbud_cell_data_function_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
/*
** create our statistic list
*/
static GtkWidget *lst_repbud_create(void)
{
GtkTreeStore *store;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("\n[repbudget] create list\n") );
/* create list store */
store = gtk_tree_store_new(
NUM_LST_BUDGET,
G_TYPE_INT, //pos
G_TYPE_INT, //key
G_TYPE_STRING, //name
G_TYPE_DOUBLE, //spent
G_TYPE_DOUBLE, //budget
G_TYPE_INT, //fulfilled
G_TYPE_DOUBLE, //result
G_TYPE_STRING //status
);
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
/* column: Name */
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Category"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbud_cell_data_function_name, GINT_TO_POINTER(LST_BUDGET_NAME), NULL);
//gtk_tree_view_column_add_attribute(column, renderer, "text", LST_BUDGET_NAME);
gtk_tree_view_column_set_sort_column_id (column, LST_BUDGET_NAME);
gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Expense */
column = lst_repbud_column_create_amount(_("Spent"), LST_BUDGET_SPENT);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Income */
column = lst_repbud_column_create_amount(_("Budget"), LST_BUDGET_BUDGET);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Fulfilled */
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Fulfilled"));
gtk_tree_view_column_set_sort_column_id (column, LST_BUDGET_FULFILLED);
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbud_cell_data_function_fulfilled, GINT_TO_POINTER(LST_BUDGET_FULFILLED), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Result */
column = lst_repbud_column_create_amount(_("Result"), LST_BUDGET_RESULT);
//right part
renderer = gtk_cell_renderer_text_new ();
//g_object_set(renderer, "xalign", 0.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbud_cell_data_function_result, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column last: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* sort */
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), budget_listview_compare_func, NULL, NULL);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_BUDGET_NAME, GTK_SORT_ASCENDING);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_NAME , budget_listview_compare_func, GINT_TO_POINTER(LST_BUDGET_NAME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_SPENT , budget_listview_compare_func, GINT_TO_POINTER(LST_BUDGET_SPENT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_BUDGET, budget_listview_compare_func, GINT_TO_POINTER(LST_BUDGET_BUDGET), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_FULFILLED, budget_listview_compare_func, GINT_TO_POINTER(LST_BUDGET_FULFILLED), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_RESULT, budget_listview_compare_func, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
return(view);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gchar *budget_mode_label(gint mode)
{
gchar *retval = (mode == REP_BUD_MODE_TIME) ? _("Month") : _("Category");
return (gchar *)retval;
}
/* action functions -------------------- */
static void repbudget_action_viewlist(GtkWidget *toolbutton, gpointer user_data)
{
struct repbudget_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 0);
repbudget_sensitive(data->window, NULL);
}
static void repbudget_action_viewstack(GtkWidget *toolbutton, gpointer user_data)
{
struct repbudget_data *data = user_data;
//#1860905 we redraw chart in case a sort changed
repbudget_update_chart(data->window, NULL);
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
repbudget_sensitive(data->window, NULL);
}
static void repbudget_action_print(GtkWidget *toolbutton, gpointer user_data)
{
struct repbudget_data *data = user_data;
gint tmpmode, page;
gchar *name, *coltitle, *title;
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
name = g_strdup_printf("hb-repbudget_%s", budget_mode_label(tmpmode) );
if( page == 0 )
{
GString *node;
title = repbudget_compute_title(tmpmode);
coltitle = budget_mode_label(tmpmode);
node = lst_repbud_to_string(HB_STRING_PRINT, GTK_TREE_VIEW(data->LV_report), tmpmode, coltitle, TRUE);
gint8 leftcols[4] = { 0, 5, -1 };
hb_print_listview(GTK_WINDOW(data->window), node->str, leftcols, title, name, FALSE);
g_string_free(node, TRUE);
g_free(title);
}
else
{
gtk_chart_progress_print(GTK_CHARTPROGRESS(data->RE_progress), GTK_WINDOW(data->window), PREFS->path_export, name);
}
g_free(name);
}
/* ======================== */
static guint _date_getmonth(guint date)
{
GDate *date1;
guint month;
date1 = g_date_new_julian(date);
month = g_date_get_month(date1);
/*#if MYDEBUG == 1
g_print("\n[repbudget] getmonth\n");
gchar buffer1[128];
g_date_strftime (buffer1, 128-1, "%x", date1);
g_print(" date is '%s', month=%d\n", buffer1, month);
#endif*/
g_date_free(date1);
return(month);
}
//#
static void _date_clamp_today(Filter *flt, gboolean today)
{
if(today)
{
if( (GLOBALS->today > flt->mindate) && (GLOBALS->today < flt->maxdate) )
flt->maxdate = GLOBALS->today;
}
}
static guint _date_countmonth(guint32 mindate, guint32 maxdate)
{
GDate *date1, *date2;
guint nbmonth;
if( mindate > maxdate )
return 0;
date1 = g_date_new_julian(mindate);
date2 = g_date_new_julian(maxdate);
nbmonth = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1) + 1;
g_date_free(date2);
g_date_free(date1);
return(nbmonth);
}
static gdouble budget_compute_result(gdouble budget, gdouble spent)
{
gdouble retval;
//original formula
//result = ABS(budget) - ABS(spent);
retval = spent - budget;
return hb_amount_round(retval, 2);
}
static void repbudget_cb_date_monthyear(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gboolean tmptoday;
gdouble minval, maxval;
DB( g_print("\n[repbudget] monthyear change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmptoday = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_untiltoday));
//todo
g_signal_handler_block(data->SB_mindate, data->handler_id[HID_REPBUDGET_MINMONTHYEAR]);
g_signal_handler_block(data->SB_maxdate, data->handler_id[HID_REPBUDGET_MAXMONTHYEAR]);
//ensure always minval < maxval
minval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->SB_mindate));
maxval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->SB_maxdate));
if( minval > maxval)
{
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->SB_mindate), minval);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->SB_maxdate), minval);
}
if( maxval < minval)
{
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->SB_mindate), maxval);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->SB_maxdate), maxval);
}
data->filter->mindate = hbtk_monthyear_getmin(GTK_SPIN_BUTTON(data->SB_mindate));
data->filter->maxdate = hbtk_monthyear_getmax(GTK_SPIN_BUTTON(data->SB_maxdate));
_date_clamp_today(data->filter, tmptoday);
g_signal_handler_unblock(data->SB_maxdate, data->handler_id[HID_REPBUDGET_MAXMONTHYEAR]);
g_signal_handler_unblock(data->SB_mindate, data->handler_id[HID_REPBUDGET_MINMONTHYEAR]);
g_signal_handler_block(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
repbudget_sensitive(widget, user_data);
repbudget_compute(widget, NULL);
repbudget_update_daterange(widget, NULL);
}
static gchar *
repbudget_compute_title(gint mode)
{
gchar *title;
if( mode == REP_BUD_MODE_TOTAL )
title = g_strdup(_("Budget by category"));
else
title = g_strdup(_("Budget by month"));
return title;
}
/*static void repbudget_date_change(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
DB( g_print("\n[repbudget] date change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
// set min/max date for both widget
gtk_date_entry_set_maxdate(GTK_DATE_ENTRY(data->PO_mindate), data->filter->maxdate);
gtk_date_entry_set_mindate(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->mindate);
g_signal_handler_block(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
repbudget_compute(widget, NULL);
repbudget_update_daterange(widget, NULL);
}*/
static void repbudget_cb_date_range(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gboolean tmptoday;
gint range;
DB( g_print("\n[repbudget] range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
tmptoday = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_untiltoday));
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, 0);
if( range == FLT_RANGE_MISC_ALLDATE )
{
data->filter->mindate = hb_date_get_jbound(data->filter->mindate, HB_DATE_BOUND_FIRST);
data->filter->maxdate = hb_date_get_jbound(data->filter->maxdate, HB_DATE_BOUND_LAST);
}
_date_clamp_today(data->filter, tmptoday);
//5.7
g_signal_handler_block(data->SB_mindate, data->handler_id[HID_REPBUDGET_MINMONTHYEAR]);
g_signal_handler_block(data->SB_maxdate, data->handler_id[HID_REPBUDGET_MAXMONTHYEAR]);
hbtk_monthyear_set(GTK_SPIN_BUTTON(data->SB_mindate), data->filter->mindate);
hbtk_monthyear_set(GTK_SPIN_BUTTON(data->SB_maxdate), data->filter->maxdate);
g_signal_handler_unblock(data->SB_maxdate, data->handler_id[HID_REPBUDGET_MAXMONTHYEAR]);
g_signal_handler_unblock(data->SB_mindate, data->handler_id[HID_REPBUDGET_MINMONTHYEAR]);
repbudget_compute(widget, NULL);
repbudget_update_daterange(widget, NULL);
}
}
static void repbudget_update_daterange(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gchar *daterange;
DB( g_print("\n[repbudget] update daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
daterange = filter_daterange_text_get(data->filter);
gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
g_free(daterange);
}
static void repbudget_toggle_detail(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repbudget] toggle detail\n") );
data->detail ^= 1;
repbudget_update_detail(widget, user_data);
repbudget_sensitive(widget, NULL);
}
static void repbudget_detail_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct repbudget_data *data;
Transaction *active_txn;
gboolean result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[repbudget] A detail row has been double-clicked!\n") );
active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_detail));
if(active_txn)
{
Transaction *old_txn, *new_txn;
//#1909749 skip reconciled if lock is ON
if( PREFS->safe_lock_recon == TRUE && active_txn->status == TXN_STATUS_RECONCILED )
return;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path = NULL;
//1936806 keep the selection
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
path = gtk_tree_model_get_path(model, &iter);
}
//#1640885
GLOBALS->changes_count++;
repbudget_compute(data->window, NULL);
if( path != NULL )
{
gtk_tree_selection_select_path(treeselection, path);
gtk_tree_path_free(path);
}
}
da_transaction_free (old_txn);
}
}
static void repbudget_update_detail(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repbudget] update detail\n") );
if(GTK_IS_TREE_VIEW(data->LV_report))
{
//#2018039
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_detail), PREFS->safe_lock_recon);
if(data->detail)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
guint key;
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_BUDGET_KEY, &key, -1);
//DB( g_print(" active is %d\n", key) );
repbudget_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
}
gtk_widget_show(data->GR_detail);
}
else
gtk_widget_hide(data->GR_detail);
}
}
static void repbudget_export_result_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repbudget_data *data;
GtkClipboard *clipboard;
GString *node;
gint tmpmode;
gchar *coltitle;
DB( g_print("\n[repbudget] export result clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
coltitle = budget_mode_label(tmpmode);
node = lst_repbud_to_string(HB_STRING_CLIPBOARD, GTK_TREE_VIEW(data->LV_report), tmpmode, coltitle, TRUE);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void repbudget_export_result_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repbudget_data *data;
gchar *filename = NULL;
GString *node;
GIOChannel *io;
gchar *name;
gint tmpmode;
gchar *coltitle;
DB( g_print("\n[repbudget] export result csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
name = g_strdup_printf("hb-repbudget_%s.csv", budget_mode_label(tmpmode) );
if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
coltitle = budget_mode_label(tmpmode);
node = lst_repbud_to_string(HB_STRING_EXPORT, GTK_TREE_VIEW(data->LV_report), tmpmode, coltitle, FALSE);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
g_free( filename );
}
g_free(name);
}
static void repbudget_export_detail_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repbudget_data *data;
GtkClipboard *clipboard;
GString *node;
guint flags;
DB( g_print("\n[repbudget] export detail clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
flags = LST_TXN_EXP_CLR | LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), TRUE, FALSE, FALSE, flags);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void repbudget_export_detail_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repbudget_data *data;
gchar *filepath = NULL;
GString *node;
GIOChannel *io;
gchar *name;
gint tmpmode;
gboolean hassplit, hasstatus;
DB( g_print("\n[repbudget] export detail csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
name = g_strdup_printf("hb-repbudget-detail_%s.csv", budget_mode_label(tmpmode));
filepath = g_build_filename(PREFS->path_export, name, NULL);
//#2019312
//if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filepath, name) == TRUE )
if( ui_dialog_export_csv(GTK_WINDOW(data->window), &filepath, &hassplit, &hasstatus, FALSE) == GTK_RESPONSE_ACCEPT )
{
DB( g_print(" + filename is %s\n", filepath) );
io = g_io_channel_new_file(filepath, "w", NULL);
if(io != NULL)
{
guint flags;
flags = LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
if( hasstatus )
flags |= LST_TXN_EXP_CLR;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), FALSE, hassplit, FALSE, flags);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
}
g_free( filepath );
g_free(name);
}
static void repbudget_detail(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gint active = GPOINTER_TO_INT(user_data);
GList *list;
guint tmpmode, tmptype;
GtkTreeModel *model;
GtkTreeIter iter, child;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repbudget] detail\n") );
DB( g_print(" active: %d\n", active) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if(data->detail && data->txn_queue && active != -1)
{
/* clear and detach our 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_detail), NULL); /* Detach model from view */
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
tmptype = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
/* fill in the model */
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
gdouble dtlamt = ope->amount;
gint pos = 0;
gboolean match = FALSE;
GPtrArray *matchsplit = NULL;
DB( g_print(" ope: %s :: acc=%d, cat=%d, mnt=%.2f\n", ope->memo, ope->kacc, ope->kcat, ope->amount) );
//filter here
//#2039995 filter txn with type
// flt: expense
if( tmptype == REPORT_TYPE_EXPENSE && (ope->flags & GF_INCOME) )
goto txnnext;
// flt: income
if( tmptype == REPORT_TYPE_INCOME && !(ope->flags & GF_INCOME) )
goto txnnext;
//time: month
if( tmpmode == REP_BUD_MODE_TIME )
{
pos = report_interval_get_pos(REPORT_INTVL_MONTH, data->filter->mindate, ope);
if( pos == active )
{
if( !(ope->flags & OF_SPLIT) )
{
match = category_key_budget_active(ope->kcat);
}
else
{
guint nbsplit = da_splits_length(ope->splits);
dtlamt = 0.0;
matchsplit = g_ptr_array_new();
for(guint i=0;isplits, i);
if( category_key_budget_active(split->kcat) )
{
match = TRUE;
dtlamt += split->amount;
g_ptr_array_add(matchsplit, split);
}
}
}
}
}
//total: category
else
{
if( active != LST_BUDGET_POS_UNBUDGETED )
{
Category *active_cat;
gboolean is_subcat = FALSE;
//get cat/subcat
active_cat = da_cat_get(active);
if( active_cat )
is_subcat = (active_cat->parent == 0) ? FALSE : TRUE;
//category
if( !(ope->flags & OF_SPLIT) )
{
pos = category_report_id(ope->kcat, is_subcat);
if( pos == active )
match = TRUE;
}
else
{
guint nbsplit = da_splits_length(ope->splits);
dtlamt = 0.0;
matchsplit = g_ptr_array_new();
for(guint i=0;isplits, i);
pos = category_report_id(split->kcat, is_subcat);
if( pos == active )
{
match = TRUE;
dtlamt += split->amount;
g_ptr_array_add(matchsplit, split);
}
}
}
}
// LST_BUDGET_POS_UNBUDGETED
else
{
DB( g_print(" unbudgeted test\n") );
// we match txn cat with no budget set
if( !(ope->flags & OF_SPLIT) )
{
match = category_key_unbudgeted(ope->kcat);
DB( g_print(" unbudgeted match: %d\n", match) );
}
//splits
else
{
guint nbsplit = da_splits_length(ope->splits);
dtlamt = 0.0;
matchsplit = g_ptr_array_new();
for(guint i=0;isplits, i);
match = category_key_unbudgeted(split->kcat);
dtlamt += split->amount;
g_ptr_array_add(matchsplit, split);
}
}
}
}
//insert
if( match == TRUE )
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &iter, NULL, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITAMT, dtlamt,
-1);
if( matchsplit != NULL )
{
for(guint i=0;ilen;i++)
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &child, &iter, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITPTR, g_ptr_array_index(matchsplit, i),
-1);
}
DB( g_print(" free matchsplit\n") );
g_ptr_array_free(matchsplit, TRUE);
matchsplit = NULL;
}
}
txnnext:
list = g_list_next(list);
}
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
g_object_unref(model);
gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
}
}
static void repbudget_update_total(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
DB( g_print("\n[repbudget] update total\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
hb_label_set_colvalue(GTK_LABEL(data->TX_total[0]), data->total_spent, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_total[1]), data->total_budget, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_total[2]), budget_compute_result(data->total_budget, data->total_spent), GLOBALS->kcur, GLOBALS->minor);
}
static void repbudget_update_chart(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
GtkTreeModel *model;
gint tmpmode;
gchar *title;
DB( g_print("\n[repbudget] update chart\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
/* update stack chart */
title = repbudget_compute_title(tmpmode);
ui_chart_progress_set_currency(GTK_CHARTPROGRESS(data->RE_progress), GLOBALS->kcur);
/* set chart color scheme */
ui_chart_progress_set_color_scheme(GTK_CHARTPROGRESS(data->RE_progress), PREFS->report_color_scheme);
ui_chart_progress_set_dualdatas(GTK_CHARTPROGRESS(data->RE_progress), model, _("Budget"), _("Result"), title, NULL);
g_free(title);
}
static void repbudget_fill_budget_for_category(Category *catitem, gdouble *tmp_budget, gint startmonth, gint nbmonth)
{
if( catitem == NULL)
return;
if( (catitem->flags & GF_BUDGET) == FALSE )
return;
//debug
/*#if MYDEBUG == 1
gint k;
g_print(" budget vector: ");
for(k=0;k<13;k++)
g_print( " %d:[%.2f]", k, entry->budget[k]);
g_print("\n");
#endif*/
DB( g_print(" %d:'%s' issub=%d hasbudget=%d custom=%d\n",
catitem->key, catitem->name, (catitem->flags & GF_SUB), (catitem->flags & GF_BUDGET), (catitem->flags & GF_CUSTOM)) );
// same value each month ?
if(!(catitem->flags & GF_CUSTOM))
{
DB( g_print(" - monthly %.2f\n", catitem->budget[0]) );
tmp_budget[catitem->key] += catitem->budget[0]*nbmonth;
if( catitem->flags & GF_SUB )
{
tmp_budget[catitem->parent] += catitem->budget[0]*nbmonth;
}
}
//otherwise sum each month from mindate month
else
{
gint j, month = startmonth;
DB( g_print(" - custom each month for %d months\n", nbmonth) );
for(j=0;jbudget[month]) );
tmp_budget[catitem->key] += catitem->budget[month];
if( catitem->flags & GF_SUB )
{
tmp_budget[catitem->parent] += catitem->budget[month];
}
month++;
if(month > 12) { month = 1; }
}
}
}
static void _repbudget_compute_cat_spent(guint32 key, gint tmptype, gdouble amount, gdouble *tmp_spent, gdouble *unbudgeted)
{
Category *cat;
//5.7.3 filter on type
if( (tmptype == REPORT_TYPE_EXPENSE && amount > 0) || (tmptype == REPORT_TYPE_INCOME && amount< 0) )
return;
cat = da_cat_get(key);
if(cat)
{
DB( g_print(" cat %02d:%02d (sub=%d)\n", cat->parent, cat->key, (cat->flags & GF_SUB)) );
if( (cat->flags & (GF_FORCED|GF_BUDGET)) )
{
DB( g_print(" + spend %.2f to cat %d\n", amount, cat->key) );
tmp_spent[cat->key] += amount;
}
//#2023696
else
{
//here if prefs we ignore if this is subcat but aprent have budget
if( category_key_unbudgeted(key) == TRUE )
*unbudgeted += amount;
}
//#1825653 subcat without budget must be computed
if( (cat->flags & GF_SUB) )
{
Category *pcat = da_cat_get(cat->parent);
if(pcat)
{
if( (cat->flags & (GF_FORCED|GF_BUDGET)) || (pcat->flags & (GF_FORCED|GF_BUDGET)) )
{
DB( g_print(" + spend %.2f to parent %d\n", amount, cat->parent) );
tmp_spent[pcat->key] += amount;
}
}
}
}
}
static void budget_compute_category(struct repbudget_data *data, GtkTreeModel *model, gdouble *tmp_spent, gdouble *tmp_budget, guint startmonth, guint nbmonth, gboolean tmptype, gboolean tmponlyout)
{
GList *lcat, *list;
guint id, i;
gdouble unbudgeted;
DB( g_print("\n+ compute budget for category\n") );
// fill tmp_budget
//get ordered cat/subcat
DB( g_print(" + cat fill budget\n") );
lcat = category_glist_sorted(HB_GLIST_SORT_KEY);
list = lcat;
while (list != NULL)
{
Category *catitem = list->data;
repbudget_fill_budget_for_category(catitem, tmp_budget, startmonth, nbmonth);
list = g_list_next(list);
}
// compute spent for each transaction
// fill tmp_spent
DB( g_print(" + cat fill spent\n") );
unbudgeted = 0.0;
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
DB( g_print(" ope: %s :: acc=%d, cat=%d, mnt=%.2f\n", ope->memo, ope->kacc, ope->kcat, ope->amount) );
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
Split *split;
for(i=0;isplits, i);
_repbudget_compute_cat_spent(split->kcat, tmptype, hb_amount_base(split->amount, ope->kcur), tmp_spent, &unbudgeted);
}
}
else
{
_repbudget_compute_cat_spent(ope->kcat, tmptype, hb_amount_base(ope->amount, ope->kcur), tmp_spent, &unbudgeted);
}
list = g_list_next(list);
}
DB( g_print("\n -- populate budget listview --\n") );
DB( g_printf(" type=%d, onlyout=%d\n", tmptype, tmponlyout) );
DB( g_printf(" unbudgeted=%.2f\n\n", unbudgeted) );
id = 0;
list = lcat;
while (list != NULL)
{
Category *catitem = list->data;
gchar *name;
gboolean outofbudget;
gboolean doinsert;
gdouble result, rawrate;
gint fulfilled;
gchar *status;
if( catitem == NULL)
continue;
name = catitem->key == 0 ? "(None)" : catitem->name;
guint pos = catitem->key;
DB( g_print(" eval %d: %d '%s' b:%d f:%d : spen=%.2f bud=%.2f \n",
id, pos, name, (catitem->flags & GF_BUDGET), (catitem->flags & GF_FORCED),
tmp_spent[pos], tmp_budget[pos] ) );
doinsert = FALSE;
if( (hb_amount_cmp(tmp_budget[pos], 0.0) != 0) || (hb_amount_cmp(tmp_spent[pos], 0.0) != 0) )
doinsert = TRUE;
//filter expense/income
if( tmptype != REPORT_TYPE_ALL )
{
Category *parent = catitem;
gint cattype = 0;
if(catitem->parent > 0)
parent = da_cat_get(catitem->parent);
cattype = category_type_get(parent);
if( (tmptype == REPORT_TYPE_EXPENSE) && (cattype > 0) )
{
doinsert = FALSE;
DB( g_printf(" >skip: expense filter\n") );
}
if( (tmptype == REPORT_TYPE_INCOME) && (cattype < 0) )
{
doinsert = FALSE;
DB( g_printf(" >skip: income filter\n") );
}
}
result = budget_compute_result(tmp_budget[pos], tmp_spent[pos]);
rawrate = 0.0;
if(ABS(tmp_budget[pos]) > 0)
{
rawrate = tmp_spent[pos] / tmp_budget[pos];
}
else if(tmp_budget[pos] == 0.0)
rawrate = ABS(tmp_spent[pos]);
status = "";
outofbudget = FALSE;
if( result )
{
if(rawrate > 1.0)
{
status = _(" over");
outofbudget = TRUE;
}
else
{
if(tmp_budget[pos] < 0.0)
status = _(" left");
else if(tmp_budget[pos] > 0.0)
{
status = _(" under");
outofbudget = TRUE;
}
}
}
if((tmponlyout == TRUE && outofbudget == FALSE) && !(catitem->flags & GF_MIXED) )
{
DB( g_printf(" >skip: only out filter is on\n") );
doinsert = FALSE;
}
if( doinsert )
{
GtkTreeIter parent;
GtkTreeIter *tmpparent = NULL;
Category *tmpcat = da_cat_get(pos);
if( tmpcat != NULL)
{
//if( lst_repbud_get_top_level (GTK_TREE_MODEL(model), tmpcat->parent, &parent) == TRUE )
if( hbtk_tree_store_get_top_level(GTK_TREE_MODEL(model), LST_BUDGET_KEY, tmpcat->parent, &parent) )
{
tmpparent = &parent;
}
else
{
//DB( g_print(" !! no parent %d found for %d '%s'\n", entry->parent, entry->key, entry->fullname) );
}
}
DB( g_print(" >insert '%s' s:%.2f b:%.2f r:%.2f (%%%.2f) '%s' '%d'\n", name, tmp_spent[pos], tmp_budget[pos], result, rawrate, status, outofbudget ) );
//5.7.3 dont fulfill if no budget
fulfilled = 0;
if( hb_amount_cmp(tmp_budget[pos], 0.0) != 0 )
fulfilled = (gint)hb_amount_round(rawrate*100, 0);
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), NULL, tmpparent, -1,
LST_BUDGET_POS, id++,
LST_BUDGET_KEY, pos,
LST_BUDGET_NAME, name,
LST_BUDGET_SPENT, tmp_spent[pos],
LST_BUDGET_BUDGET, tmp_budget[pos],
//#2043223
//LST_BUDGET_FULFILLED, (gint)(rawrate*100),
LST_BUDGET_FULFILLED, fulfilled,
LST_BUDGET_RESULT, result,
LST_BUDGET_STATUS, status,
-1);
//#2036703 only sum level1, as level are already into the sum
if( catitem->parent == 0 )
{
data->total_spent += tmp_spent[pos];
data->total_budget += tmp_budget[pos];
}
}
list = g_list_next(list);
}
//2023696 add unbudgeted
if( hb_amount_cmp(unbudgeted, 0.0) != 0 )
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), NULL, NULL, -1,
LST_BUDGET_POS, LST_BUDGET_POS_UNBUDGETED,
LST_BUDGET_KEY, LST_BUDGET_POS_UNBUDGETED,
LST_BUDGET_NAME, _("(unbudgeted)"),
LST_BUDGET_SPENT, unbudgeted,
-1);
}
DB( g_print(" -- end populate category\n") );
// free cat list
g_list_free(lcat);
}
static void budget_compute_month(struct repbudget_data *data, GtkTreeModel *model, gdouble *tmp_spent, gdouble *tmp_budget, guint startmonth, guint nbmonth, gboolean tmptype, gboolean tmponlyout)
{
GList *lcat, *list;
GtkTreeIter iter;
guint pos, i;
DB( g_print("\n+ compute budget for month\n") );
// fill tmp_budget
//get ordered cat/subcat
DB( g_print(" + month compute budget, startmonth %d\n", startmonth) );
lcat = category_glist_sorted(HB_GLIST_SORT_KEY);
list = lcat;
while (list != NULL)
{
Category *catitem = list->data;
DB( g_print(" %d:'%s' issub=%d hasbudget=%d custom=%d\n",
catitem->key, catitem->name, (catitem->flags & GF_SUB), (catitem->flags & GF_BUDGET), (catitem->flags & GF_CUSTOM)) );
// flt: expense
if( tmptype == REPORT_TYPE_EXPENSE && (catitem->flags & GF_INCOME) )
goto budnext;
// flt: income
if( tmptype == REPORT_TYPE_INCOME && !(catitem->flags & GF_INCOME) )
goto budnext;
if( (catitem->flags & GF_BUDGET) )
{
// same value each month ?
if(!(catitem->flags & GF_CUSTOM))
{
DB( g_print(" add same %.2f for '%s'\n =>", catitem->budget[0], catitem->fullname) );
//we add same amount each month
for(pos=0 ; posbudget[0];
}
DB( g_print("\n") );
}
// different value each month
else
{
guint month = startmonth;
DB( g_print(" add custom for '%s'\n =>", catitem->fullname) );
for(pos=0 ; posbudget[month]) );
tmp_budget[pos] += catitem->budget[month];
month++;
if(month > 12) { month = 1; }
}
}
}
budnext:
list = g_list_next(list);
}
// free cat list
g_list_free(lcat);
// compute spent for each transaction
// fill tmp_spent
DB( g_print(" + month compute spent\n") );
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
pos = report_interval_get_pos(REPORT_INTVL_MONTH, data->filter->mindate, ope);
DB( g_print(" ope: %s :: acc=%d, cat=%d, mnt=%.2f, pos=%d\n", ope->memo, ope->kacc, ope->kcat, ope->amount, pos) );
//#2039995 filter txn with type
if(
(tmptype == REPORT_TYPE_EXPENSE && (ope->flags & GF_INCOME)) // flt: expense
||
(tmptype == REPORT_TYPE_INCOME && !(ope->flags & GF_INCOME)) // flt: income txn
)
{
DB( g_print(" skipped by type filter\n") );
goto txnnext;
}
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
Split *split;
for(i=0;isplits, i);
if( category_key_budget_active(split->kcat) == TRUE )
{
tmp_spent[pos] += hb_amount_base(split->amount, ope->kcur);
}
else
{
DB( g_print(" skipped not budget|forced\n") );
}
}
}
else
{
if( category_key_budget_active(ope->kcat) == TRUE )
{
tmp_spent[pos] += hb_amount_base(ope->amount, ope->kcur);
}
else
{
DB( g_print(" skipped not budget|forced\n") );
}
}
DB( g_print(" spent[%d]=%.2f\n", pos, tmp_spent[pos]) );
txnnext:
list = g_list_next(list);
}
DB( g_print("\n -- populate budget listview --\n") );
for( i=0 ; i < nbmonth ; i++ )
{
guint pos = i;
gboolean outofbudget;
gchar intvlname[64];
report_interval_snprint_name(intvlname, sizeof(intvlname)-1, REPORT_INTVL_MONTH, data->filter->mindate, i);
DB( g_print(" eval %d '%s' : spen=%.2f bud=%.2f \n", pos, intvlname, tmp_spent[pos], tmp_budget[pos] ) );
if( tmp_budget[pos] /*|| tmp_spent[pos]*/)
{
gdouble result, rawrate;
gchar *status;
result = budget_compute_result(tmp_budget[pos], tmp_spent[pos]);
rawrate = 0.0;
if(ABS(tmp_budget[pos]) > 0)
{
rawrate = tmp_spent[pos] / tmp_budget[pos];
}
else if(tmp_budget[pos] == 0.0)
rawrate = ABS(tmp_spent[pos]);
status = "";
outofbudget = FALSE;
if( result )
{
if(rawrate > 1.0)
{
status = _(" over");
outofbudget = TRUE;
}
else
{
if(tmp_budget[pos] < 0.0)
status = _(" left");
else if(tmp_budget[pos] > 0.0)
{
status = _(" under");
outofbudget = TRUE;
}
}
}
if(tmponlyout == TRUE && outofbudget == FALSE)
goto nextins;
DB( g_print(" => insert '%s' s:%.2f b:%.2f r:%.2f (%%%.2f) '%s' '%d'\n\n", intvlname, tmp_spent[pos], tmp_budget[pos], result, rawrate, status, outofbudget ) );
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &iter, NULL, -1,
LST_BUDGET_POS, pos,
LST_BUDGET_KEY, pos,
LST_BUDGET_NAME, intvlname,
LST_BUDGET_SPENT, tmp_spent[pos],
LST_BUDGET_BUDGET, tmp_budget[pos],
LST_BUDGET_FULFILLED, (gint)(rawrate*100),
LST_BUDGET_RESULT, result,
LST_BUDGET_STATUS, status,
-1);
nextins:
data->total_spent += tmp_spent[pos];
data->total_budget += tmp_budget[pos];
}
}
DB( g_print(" -- end populate month\n") );
}
static void repbudget_compute(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
GtkTreeModel *model;
gint tmpmode, tmptype, tmponlyout;
guint32 mindate, maxdate;
guint nbkeycat, nbmonth, n_result;
gdouble *tmp_spent, *tmp_budget;
DB( g_print("\n[repbudget] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
mindate = data->filter->mindate;
maxdate = data->filter->maxdate;
//0=Total / 1=Time
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
tmptype = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
tmponlyout = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_onlyout));
DB( g_print(" mode:%d type:%d only:%d\n", tmpmode, tmptype, tmponlyout) );
//#2019876 return is invalid date range
if(maxdate < mindate) return;
if( data->filter->maxdate < data->filter->mindate )
return;
//TODO: not necessary until date range change
//free previous txn
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
data->txn_queue = hbfile_transaction_get_partial_budget(mindate, maxdate);
nbkeycat = da_cat_get_max_key();
nbmonth = _date_countmonth(mindate, maxdate);
DB( g_print(" date: min=%d max=%d nbcat=%d nbmonth=%d\n", mindate, maxdate, nbkeycat, nbmonth) );
// allocate some memory
n_result = (tmpmode == REP_BUD_MODE_TIME) ? nbmonth : nbkeycat;
tmp_spent = g_malloc0((n_result+1) * sizeof(gdouble));
tmp_budget = g_malloc0((n_result+1) * sizeof(gdouble));
if(tmp_spent && tmp_budget)
{
guint startmonth;
// compute the results
data->total_spent = 0.0;
data->total_budget = 0.0;
startmonth = _date_getmonth(mindate);
DB( g_print("\nblock handlers\n") );
g_signal_handlers_block_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report))), G_CALLBACK (repbudget_selection), NULL);
g_signal_handlers_block_by_func (G_OBJECT (data->LV_detail), G_CALLBACK (repbudget_detail_onRowActivated), NULL);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
gtk_tree_store_clear (GTK_TREE_STORE(model));
DB( g_print("\nclear and detach model\n") );
// clear and detach our model
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
gtk_tree_store_clear (GTK_TREE_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_report), NULL); // Detach model from view
// compute budget for each category
g_object_set_data(G_OBJECT(model), "mode-data", GINT_TO_POINTER(tmpmode));
if( tmpmode == REP_BUD_MODE_TIME )
{
budget_compute_month(data, model, tmp_spent, tmp_budget, startmonth, nbmonth, tmptype, tmponlyout);
}
else
{
budget_compute_category(data, model, tmp_spent, tmp_budget, startmonth, nbmonth, tmptype, tmponlyout);
}
// update column 0 title
GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
gtk_tree_view_column_set_title(column, budget_mode_label(tmpmode) );
// Re-attach model to view
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
g_object_unref(model);
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report));
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(data->LV_report));
repbudget_update_total(widget, NULL);
repbudget_update_chart(widget, NULL);
DB( g_print("\nunblock handlers\n") );
g_signal_handlers_unblock_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report))), G_CALLBACK (repbudget_selection), NULL);
g_signal_handlers_unblock_by_func (G_OBJECT (data->LV_detail), G_CALLBACK (repbudget_detail_onRowActivated), NULL);
}
//DB( g_print(" inserting %i, %f %f\n", i, total_expense, total_income) );
// free our memory
g_free(tmp_spent);
g_free(tmp_budget);
}
/*
** update sensitivity
*/
static void repbudget_sensitive(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gboolean visible, sensitive;
gint tmppage, tmpmode, tmptoday, count;
DB( g_print("\n[repbudget] sensitive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmppage = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
tmpmode = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_mode));
tmptoday = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_untiltoday));
visible = (tmppage == 0) ? TRUE : FALSE;
hb_widget_visible (data->BT_detail, visible);
hb_widget_visible (data->BT_export, visible);
//5.7
visible = (tmpmode == 0) ? TRUE : FALSE;
//hb_widget_visible (data->BT_print, !visible);
hb_widget_visible (data->GR_listbar, visible);
//5.9
sensitive = tmptoday ? FALSE : TRUE;
gtk_widget_set_sensitive(data->LB_maxdate, sensitive);
gtk_widget_set_sensitive(data->SB_maxdate, sensitive);
count = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail)), NULL);
sensitive = ((count > 0) && data->detail) ? TRUE : FALSE;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detclip")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detcsv")), sensitive);
}
static void repbudget_toggle(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
gboolean minor;
DB( g_print("\n[repbudget] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
repbudget_update_total(widget, NULL);
//hbfile_update(data->LV_acc, (gpointer)4);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
ui_chart_progress_show_minor(GTK_CHARTPROGRESS(data->RE_progress), minor);
}
static void repbudget_cb_expand_all(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repdist] expand all (data=%p)\n", data) );
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report));
}
static void repbudget_cb_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct repbudget_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repdist] collapse all (data=%p)\n", data) );
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_report));
}
static void repbudget_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
gint key = -1;
DB( g_print("\n[repbudget] selection\n") );
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_BUDGET_KEY, &key, -1);
}
DB( g_print(" - active is %d\n", key) );
repbudget_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
repbudget_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static const GActionEntry win_actions[] = {
{ "resclip" , repbudget_export_result_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "rescsv" , repbudget_export_result_csv, NULL, NULL, NULL, {0,0,0} },
{ "detclip" , repbudget_export_detail_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "detcsv" , repbudget_export_detail_csv, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
static GtkWidget *
repbudget_toolbar_create(struct repbudget_data *data)
{
GtkWidget *toolbar, *button;
toolbar = gtk_toolbar_new();
button = (GtkWidget *)gtk_radio_tool_button_new(NULL);
data->BT_list = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LIST, "label", _("List"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as list"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_progress = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_PROGRESS, "label", _("Stack"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as stack bars"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_OPE_SHOW,
"label", _("Detail"),
"tooltip-text", _("Toggle detail"),
"active", PREFS->budg_showdetail,
NULL);
data->BT_detail = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REFRESH, _("Refresh"), _("Refresh results"));
data->BT_refresh = button;
//export button
button = gtk_menu_button_new();
data->BT_export = button;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(button)), GTK_STYLE_CLASS_FLAT);
GtkWidget *image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_EXPORT, GTK_ICON_SIZE_LARGE_TOOLBAR);
g_object_set (button, "image", image, NULL);
GtkToolItem *toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), button);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(toolitem), -1);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Result to clipboard"), "win.resclip");
g_menu_append (section, _("_Result to CSV") , "win.rescsv");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Detail to clipboard"), "win.detclip");
g_menu_append (section, _("_Detail to CSV") , "win.detcsv");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (button, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), G_MENU_MODEL (menu));
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_PRINT, _("Print"), _("Print"));
data->BT_print = button;
return toolbar;
}
//reset the filter
static void repbudget_filter_setup(struct repbudget_data *data)
{
DB( g_print("\n[repbudget] reset filter\n") );
filter_reset(data->filter);
filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
/* 3.4 : make int transfer out of stats */
filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
if( PREFS->date_range_rep == FLT_RANGE_MISC_ALLDATE )
{
data->filter->mindate = hb_date_get_jbound(data->filter->mindate, HB_DATE_BOUND_FIRST);
data->filter->maxdate = hb_date_get_jbound(data->filter->maxdate, HB_DATE_BOUND_LAST);
}
//g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
//g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
//gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
//gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
//5.7
hbtk_monthyear_set(GTK_SPIN_BUTTON(data->SB_mindate), data->filter->mindate);
hbtk_monthyear_set(GTK_SPIN_BUTTON(data->SB_maxdate), data->filter->maxdate);
//g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
//g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
}
// setup default for our object/memory
static void repbudget_window_setup(struct repbudget_data *data)
{
DB( g_print("\n[repbudget] setup\n") );
DB( g_print(" init data\n") );
repbudget_filter_setup(data);
DB( g_print(" set widgets default\n") );
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_detail), PREFS->budg_showdetail);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail))), "minor", (gpointer)data->CM_minor);
DB( g_print(" connect widgets signals\n") );
g_signal_connect (data->CM_untiltoday, "toggled", G_CALLBACK (repbudget_cb_date_monthyear), NULL);
g_signal_connect (data->CM_onlyout, "toggled", G_CALLBACK (repbudget_compute), NULL);
g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repbudget_toggle), NULL);
g_signal_connect (G_OBJECT (data->BT_expand), "clicked", G_CALLBACK (repbudget_cb_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapse), "clicked", G_CALLBACK (repbudget_cb_collapse_all), NULL);
data->handler_id[HID_REPBUDGET_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repbudget_cb_date_range), NULL);
//g_signal_connect (data->CY_for , "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
g_signal_connect (data->RA_mode, "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
g_signal_connect (data->CY_type, "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
//data->handler_id[HID_REPBUDGET_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
//data->handler_id[HID_REPBUDGET_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
data->handler_id[HID_REPBUDGET_MINMONTHYEAR] = g_signal_connect (data->SB_mindate, "value-changed", G_CALLBACK (repbudget_cb_date_monthyear), NULL);
data->handler_id[HID_REPBUDGET_MAXMONTHYEAR] = g_signal_connect (data->SB_maxdate, "value-changed", G_CALLBACK (repbudget_cb_date_monthyear), NULL);
g_signal_connect (G_OBJECT (data->BT_list), "clicked", G_CALLBACK (repbudget_action_viewlist), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_progress), "clicked", G_CALLBACK (repbudget_action_viewstack), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_detail), "clicked", G_CALLBACK (repbudget_toggle_detail), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_refresh), "clicked", G_CALLBACK (repbudget_compute), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_print), "clicked", G_CALLBACK (repbudget_action_print), (gpointer)data);
//export is a menu
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), "changed", G_CALLBACK (repbudget_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (repbudget_detail_onRowActivated), NULL);
}
static gboolean repbudget_window_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repbudget_data *data;
guint32 rid;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[repbudget] window mapped\n") );
//setup, init and show window
repbudget_window_setup(data);
//5.9 ensure we will trigger a valid id
rid = FLT_RANGE_MISC_ALLDATE;
if( rid == FLT_RANGE_LAST_MONTH || rid == FLT_RANGE_THIS_MONTH || rid == FLT_RANGE_NEXT_MONTH
|| rid == FLT_RANGE_LAST_QUARTER || rid == FLT_RANGE_THIS_QUARTER || rid == FLT_RANGE_NEXT_QUARTER
|| rid == FLT_RANGE_LAST_YEAR || rid == FLT_RANGE_THIS_YEAR || rid == FLT_RANGE_NEXT_YEAR
|| rid == FLT_RANGE_LAST_12MONTHS || rid == FLT_RANGE_LAST_6MONTHS || rid == FLT_RANGE_MISC_ALLDATE
|| rid == FLT_RANGE_TODATE_YEAR || rid == FLT_RANGE_TODATE_MONTH || rid == FLT_RANGE_TODATE_ALL
)
{
rid = PREFS->date_range_rep;
}
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), rid);
//hb_window_set_busy(GTK_WINDOW(data->window), FALSE);
//check for any account included into the budget or warn
{
guint count =0;
GList *lacc, *list;
lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc;
acc = list->data;
//#1674045 ony rely on nosummary
//if((acc->flags & (AF_CLOSED|AF_NOREPORT))) goto next1;
if((acc->flags & (AF_NOREPORT))) goto next1;
if(!(acc->flags & AF_NOBUDGET))
count++;
next1:
list = g_list_next(list);
}
g_list_free(lacc);
if(count <= 0)
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_WARNING,
_("No account is defined to be part of the budget."),
_("You should include some accounts from the account dialog.")
);
}
}
data->mapped_done = TRUE;
return FALSE;
}
static gboolean repbudget_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repbudget_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[repbudget] start dispose\n") );
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
da_flt_free(data->filter);
g_free(data);
//store position and size
wg = &PREFS->bud_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
//enable define windows
GLOBALS->define_off--;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
//unref window to our open window list
GLOBALS->openwindows = g_slist_remove(GLOBALS->openwindows, widget);
DB( g_print("\n[repbudget] end dispose\n") );
return FALSE;
}
//allocate our object/memory
static void repbudget_window_acquire(struct repbudget_data *data)
{
DB( g_print("\n[repbudget] acquire\n") );
data->txn_queue = g_queue_new ();
data->filter = da_flt_malloc();
data->detail = PREFS->budg_showdetail;
data->legend = 1;
}
// the window creation
GtkWidget *repbudget_window_new(void)
{
struct repbudget_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *hbox, *vbox, *tbar, *bbox, *notebook, *treeview, *vpaned, *scrollwin;
GtkWidget *label, *widget, *table, *entry;
gint row;
DB( g_print("\n[repbudget] new\n") );
data = g_malloc0(sizeof(struct repbudget_data));
if(!data) return NULL;
repbudget_window_acquire(data);
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//ref window to our open window list
GLOBALS->openwindows = g_slist_prepend(GLOBALS->openwindows, window);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
gtk_window_set_title (GTK_WINDOW (window), _("Budget report"));
//window contents
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_SMALL);
gtk_window_set_child(GTK_WINDOW(window), mainbox);
//control part
table = gtk_grid_new ();
gtk_box_prepend (GTK_BOX (mainbox), table);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
label = make_label_group(_("Display"));
gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
row++;
label = make_label_widget(_("Mode:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_REPORT_MODE, TRUE);
data->RA_mode = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Type:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_KIND);
data->CY_type = widget;
gtk_grid_attach (GTK_GRID (table), data->CY_type, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Only out of budget"));
data->CM_onlyout = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//-- filter
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
row++;
label = make_label_group(_("Filter"));
gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
row++;
label = make_label_group(_("Date"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_BUDGET_MODE | DATE_RANGE_FLAG_CUSTOM_DISABLE);
gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
row++;
label = make_label_widget(_("_From:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = make_monthyear(NULL);
data->SB_mindate = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
data->LB_maxdate = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = make_monthyear(NULL);
data->SB_maxdate = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("U_ntil today"));
data->CM_untiltoday = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//part: info + report
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
//toolbar
widget = repbudget_toolbar_create(data);
data->TB_bar = widget;
gtk_box_prepend (GTK_BOX (vbox), widget);
//infos
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
hb_widget_set_margin(GTK_WIDGET(hbox), SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = make_label(NULL, 0.5, 0.5);
gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
data->TX_daterange = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
entry = gtk_label_new(NULL);
data->TX_total[2] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Result:"));
gtk_box_append (GTK_BOX (hbox), label);
entry = gtk_label_new(NULL);
data->TX_total[1] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Budget:"));
gtk_box_append (GTK_BOX (hbox), label);
entry = gtk_label_new(NULL);
data->TX_total[0] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Spent:"));
gtk_box_append (GTK_BOX (hbox), label);
/* report area */
notebook = gtk_notebook_new();
data->GR_result = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
hbtk_box_prepend (GTK_BOX (vbox), notebook);
//page: list
vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vpaned, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = lst_repbud_create();
data->LV_report = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack1 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
//5.8 moved collapse/expand here
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_listbar = tbar;
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
//detail
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->GR_detail = scrollwin;
treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_det_columns);
data->LV_detail = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack2 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
list_txn_set_save_column_width(GTK_TREE_VIEW(treeview), TRUE);
//page: 2d bar
//widget = gtk_chart_new(CHART_TYPE_COL);
widget = ui_chart_progress_new();
data->RE_progress = widget;
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
// connect dialog signals
g_signal_connect (window, "delete-event", G_CALLBACK (repbudget_window_dispose), (gpointer)data);
g_signal_connect (window, "map-event" , G_CALLBACK (repbudget_window_mapped), NULL);
// setup, init and show window
wg = &PREFS->bud_wg;
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
// toolbar
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_widget_show_all (window);
//minor ?
hb_widget_visible(data->CM_minor, PREFS->euro_active);
repbudget_sensitive(window, NULL);
repbudget_update_detail(window, NULL);
return(window);
}
homebank-5.9.7/src/hb-archive.c 0000644 0001750 0001750 00000050130 15016351225 015554 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-archive.h"
#include "hb-split.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
#define DB2(x);
//#define DB2(x) (x);
/* our global datas */
extern struct HomeBank *GLOBALS;
#if MYDEBUG
#include "ui-widgets.h"
extern HbKvData CYA_ARC_WEEKEND[];
gchar *WDAY[] = { "bad", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
da_archive_clean(Archive *item)
{
if(item != NULL)
{
g_free(item->memo);
item->memo = NULL;
g_free(item->number);
item->number = NULL;
//5.3 added as it was a leak
g_free(item->tags);
item->tags = NULL;
if(item->splits != NULL)
{
da_split_destroy(item->splits);
item->splits = NULL;
item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
}
}
}
void
da_archive_free(Archive *item)
{
if(item != NULL)
{
da_archive_clean(item);
g_free(item);
}
}
Archive *
da_archive_malloc(void)
{
Archive *item;
item = g_malloc0(sizeof(Archive));
item->key = 1;
return item;
}
Archive *
da_archive_clone(Archive *src_item)
{
Archive *new_item = g_memdup(src_item, sizeof(Archive));
if(new_item)
{
//duplicate the string
new_item->memo = g_strdup(src_item->memo);
new_item->number = g_strdup(src_item->number);
//duplicate tags
//no g_free here to avoid free the src tags (memdup copie dthe ptr)
new_item->tags = tags_clone(src_item->tags);
//duplicate splits
//no g_free here to avoid free the src tags (memdup copie dthe ptr)
new_item->splits = da_splits_clone(src_item->splits);
if( da_splits_length (new_item->splits) > 0 )
new_item->flags |= OF_SPLIT; //Flag that Splits are active
}
return new_item;
}
void
da_archive_destroy(GList *list)
{
GList *tmplist = g_list_first(list);
while (tmplist != NULL)
{
Archive *item = tmplist->data;
da_archive_free(item);
tmplist = g_list_next(tmplist);
}
g_list_free(list);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint
da_archive_length(void)
{
return g_list_length(GLOBALS->arc_list);
}
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;
}
//delete
//insert
/* append a fav with an existing key (from xml file only) */
gboolean
da_archive_append(Archive *item)
{
//perf must use preprend, see glib doc
//GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
GLOBALS->arc_list = g_list_prepend(GLOBALS->arc_list, item);
return TRUE;
}
gboolean
da_archive_append_new(Archive *item)
{
item->key = da_archive_get_max_key() + 1;
//TODO: perf must use preprend, see glib doc
GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
return TRUE;
}
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;
}
//#1968249 build a non empty label, when memo/payee/category are empty
void da_archive_get_display_label(GString *tpltitle, Archive *item)
{
g_string_truncate(tpltitle, 0);
if(item->memo != NULL)
{
g_string_append(tpltitle, item->memo);
}
else
{
g_string_append(tpltitle, _("(no memo)") );
if(item->kpay > 0)
{
Payee *pay = da_pay_get(item->kpay);
if(pay != NULL)
{
g_string_append_c(tpltitle, ' ');
g_string_append(tpltitle, pay->name);
}
}
if(item->kcat > 0)
{
Category *cat = da_cat_get(item->kcat);
if(cat != NULL)
{
g_string_append(tpltitle, " / ");
g_string_append(tpltitle, cat->fullname);
}
}
}
}
void da_archive_consistency(Archive *item)
{
Account *acc;
Category *cat;
Payee *pay;
guint nbsplit;
// check category exists
cat = da_cat_get(item->kcat);
if(cat == NULL)
{
g_warning("tpl consistency: fixed invalid cat %d", item->kcat);
item->kcat = 0;
GLOBALS->changes_count++;
}
//#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("tpl 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)
{
g_warning("tpl consistency: fixed invalid pay %d", item->kpay);
item->kpay = 0;
GLOBALS->changes_count++;
}
// 5.3: fix split on intxfer
if( ((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) && (item->splits != NULL) )
{
g_warning("tpl consistency: fixed invalid split on xfer");
item->flags &= ~(OF_INTXFER);
item->paymode = PAYMODE_XFER;
item->kxferacc = 0;
}
// reset dst acc for non xfer transaction
if( !((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) )
item->kxferacc = 0;
// delete automation if dst_acc not exists
if( (item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER) )
{
acc = da_acc_get(item->kxferacc);
if(acc == NULL)
{
item->rec_flags &= ~(TF_RECUR); //delete flag
}
}
}
/* = = = = = = = = = = = = = = = = = = = = */
static gint
da_archive_glist_name_compare_func(Archive *a, Archive *b)
{
return hb_string_utf8_compare(a->memo, b->memo);
}
static gint
da_archive_glist_key_compare_func(Archive *a, Archive *b)
{
return hb_string_utf8_compare(a->memo, b->memo);
}
GList *da_archive_glist_sorted(gint column)
{
switch(column)
{
case HB_GLIST_SORT_NAME:
GLOBALS->arc_list = g_list_sort(GLOBALS->arc_list, (GCompareFunc)da_archive_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
GLOBALS->arc_list = g_list_sort(GLOBALS->arc_list, (GCompareFunc)da_archive_glist_key_compare_func);
break;
}
return GLOBALS->arc_list;
}
void da_archive_stats(gint *nbtpl, gint *nbsch)
{
GList *tmplist = g_list_first(GLOBALS->arc_list);
gint nbt, nbs;
nbt = nbs = 0;
while (tmplist != NULL)
{
Archive *item = tmplist->data;
if(item->rec_flags & TF_RECUR)
nbs++;
else
nbt++;
tmplist = g_list_next(tmplist);
}
if( nbtpl != NULL)
*nbtpl = nbt;
if( nbsch != NULL)
*nbsch = nbs;
}
//#1872140 don't prefix if not from ledger
Archive *da_archive_init_from_transaction(Archive *arc, Transaction *txn, gboolean fromledger)
{
DB( g_print("\n[scheduled] init from txn\n") );
da_archive_clean(arc);
//fill it
arc->amount = txn->amount;
arc->kacc = txn->kacc;
//#1673260
arc->xferamount = txn->xferamount;
arc->kxferacc = txn->kxferacc;
arc->paymode = txn->paymode;
arc->flags = txn->flags;
arc->status = txn->status;
arc->kpay = txn->kpay;
arc->kcat = txn->kcat;
//5.9 clean flags
arc->flags &= ~(OF_ISIMPORT|OF_ISPAST);
//5.3.3 prefixed with prefilled
//#1883063 **PREFILLED** only when fromledger==TRUE, test order was wrogn here
if( fromledger == FALSE )
{
if(txn->memo != NULL)
arc->memo = g_strdup(txn->memo);
}
else
{
arc->dspflags |= FLAG_TMP_PREFILLED;
//#2018680
if(txn->memo != NULL)
arc->memo = g_strdup( txn->memo );
//arc->memo = g_strdup_printf("%s %s", _("**PREFILLED**"), txn->memo );
//else
// arc->memo = g_strdup(_("**PREFILLED**"));
}
if(txn->number != NULL)
arc->number = g_strdup(txn->number);
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;
}
/* = = = = = = = = = = = = = = = = = = = = */
gboolean
template_is_account_used(Archive *arc)
{
GList *lacc, *list;
gboolean retval = FALSE;
lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc = list->data;
if( acc->karc == arc->key )
retval = TRUE;
list = g_list_next(list);
}
g_list_free(lacc);
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = */
static guint32 _sched_nextdate_relative(GDate *date, guint ordinal, guint weekday, guint every)
{
guint daysinmonth, firstwd, gap;
g_date_set_day(date, 1);
g_date_add_months(date, every);
daysinmonth = g_date_get_days_in_month(g_date_get_month(date), g_date_get_year(date));
if( weekday == AUTO_WEEKDAY_DAY )
{
gap = (ordinal-1);
if( ordinal == AUTO_ORDINAL_LAST )
gap = daysinmonth - 1;
}
else
{
//M=1 T=2 W=3 T=4 F=5 S=6 S=7
firstwd = g_date_get_weekday(date);
if( weekday < firstwd )
gap = 7 - (firstwd - weekday);
else
gap = weekday - firstwd;
DB( g_print(" fwd=%d weekday=%d gap=%d\n", firstwd, weekday, gap) );
g_date_add_days(date, gap);
//add fisrt/second...
gap = 7 * (ordinal-1);
//but retrieve 7 if next month
if( g_date_get_day(date) + gap > daysinmonth )
gap = gap -7;
}
g_date_add_days(date, gap);
return g_date_get_julian(date);
}
static void _sched_nextdate_weekend_adjust(GDate *date)
{
GDateWeekday wday = g_date_get_weekday(date);
if( wday == G_DATE_SATURDAY )
g_date_add_days (date, 2);
else
if( wday == G_DATE_SUNDAY )
g_date_add_days (date, 1);
}
static guint32 _sched_date_get_next_post(GDate *date, Archive *arc, guint32 nextdate)
{
guint32 nextpostdate = nextdate;
DB2( g_print("\n[scheduled] date_get_next_post\n") );
g_date_set_julian(date, nextpostdate);
DB2( hb_print_date(g_date_get_julian(date), "in:") );
if( !(arc->rec_flags & TF_RELATIVE) )
{
switch(arc->rec_freq)
{
case AUTO_FREQ_DAY:
g_date_add_days(date, arc->rec_every);
break;
case AUTO_FREQ_WEEK:
g_date_add_days(date, 7 * arc->rec_every);
break;
case AUTO_FREQ_MONTH:
g_date_add_months(date, arc->rec_every);
break;
case AUTO_FREQ_YEAR:
g_date_add_years(date, arc->rec_every);
break;
}
DB2( hb_print_date(g_date_get_julian(date), "out:") );
//#1906953 add skip weekend
if( arc->weekend == ARC_WEEKEND_SKIP )
{
_sched_nextdate_weekend_adjust(date);
}
}
//#2066993/#2067399 relative monthly
else
{
_sched_nextdate_relative(date, arc->rec_ordinal, arc->rec_weekday, arc->rec_every);
}
DB2( hb_print_date(g_date_get_julian(date), "out:") );
/* get the final post date */
nextpostdate = g_date_get_julian(date);
return nextpostdate;
}
//#1906953 add skip weekend
//ui-archive
void scheduled_nextdate_weekend_adjust(Archive *arc)
{
if( arc->weekend == ARC_WEEKEND_SKIP )
{
GDate date;
g_date_set_julian(&date, arc->nextdate);
_sched_nextdate_weekend_adjust(&date);
arc->nextdate = g_date_get_julian(&date);
}
}
//ui-archive
guint32 scheduled_date_get_next_relative(GDate *date, guint ordinal, guint weekday, guint every)
{
return _sched_nextdate_relative(date, ordinal, weekday, every);
}
//hb-report
guint32 scheduled_date_get_next_post(GDate *date, Archive *arc, guint32 nextdate)
{
return _sched_date_get_next_post(date, arc, nextdate);
}
// hub_scheduled / hb-report
gboolean scheduled_is_postable(Archive *arc)
{
if( !(arc->rec_flags & TF_RECUR) )
return FALSE;
if( arc->kacc == 0 )
return FALSE;
if( hb_amount_cmp(arc->amount, 0.0) == 0 )
return FALSE;
return TRUE;
}
// hub_scheduled
guint32 scheduled_get_latepost_count(GDate *date, Archive *arc, guint32 jrefdate)
{
guint32 jcurdate = arc->nextdate;
guint32 nblate = 0;
while (jcurdate <= jrefdate)
{
jcurdate = _sched_date_get_next_post(date, arc, jcurdate);
nblate++;
// break if over limit or at 11 max (to display +10)
if( nblate >= 11 || ( (arc->rec_flags & TF_LIMIT) && (nblate >= arc->limit) ) )
break;
}
return nblate;
}
//used here + hub-scheduled
//return date shifted with weekend settings
guint32 scheduled_get_txn_real_postdate(guint32 postdate, gint weekend)
{
DB2( g_print("\n[scheduled] get_txn_real_postdate\n") );
/* manage weekend exception */
if( (weekend == ARC_WEEKEND_BEFORE) || (weekend == ARC_WEEKEND_AFTER) )
{
GDate tmpdate;
GDateWeekday wday;
g_date_set_julian(&tmpdate, postdate);
wday = g_date_get_weekday(&tmpdate);
DB2( g_print(" wday=%d '%s'\n", wday, WDAY[wday]) );
if( wday >= G_DATE_SATURDAY )
{
//here wday is G_DATE_SATURDAY(6) OR G_DATE_SUNDAY(7)
if( weekend == ARC_WEEKEND_BEFORE )
return (wday == G_DATE_SATURDAY) ? postdate-1 : postdate-2;
else //ARC_WEEKEND_AFTER
return (wday == G_DATE_SATURDAY) ? postdate+2 : postdate+1;
}
}
return postdate;
}
/* return 0 is max number of post is reached */
//used here + hub_scheduled
//this modify the arc
guint32 scheduled_date_advance(Archive *arc)
{
GDate date;
gushort lastday;
DB2( g_print("\n[scheduled] date_advance\n") );
g_date_set_julian(&date, arc->nextdate);
// saved the current day number
lastday = g_date_get_day(&date);
arc->nextdate = _sched_date_get_next_post(&date, arc, arc->nextdate);
DB2( g_print(" raw next post date: %02d-%02d-%4d\n", g_date_get_day(&date), g_date_get_month (&date), g_date_get_year(&date) ) );
//todo: review this
//#
if( !(arc->rec_flags & TF_RELATIVE) )
{
//for day > 28 we might have a gap to compensate later
if( (arc->rec_freq==AUTO_FREQ_MONTH) || (arc->rec_freq==AUTO_FREQ_YEAR) )
{
if( lastday >= 28 )
{
DB2( g_print(" lastday:%d, daygap:%d\n", lastday, arc->daygap) );
if( arc->daygap > 0 )
{
g_date_add_days (&date, arc->daygap);
arc->nextdate = g_date_get_julian (&date);
lastday += arc->daygap;
DB2( g_print(" adjusted post date: %2d-%2d-%4d\n", g_date_get_day(&date), g_date_get_month (&date), g_date_get_year(&date) ) );
}
arc->daygap = CLAMP(lastday - g_date_get_day(&date), 0, 3);
DB2( g_print(" daygap is %d\n", arc->daygap) );
}
else
arc->daygap = 0;
}
}
//#1556289
//#2112135 fix flags removal, secured decrease & remove nextdat=0
/* check limit, update and maybe break */
if(arc->rec_flags & TF_LIMIT)
{
if( arc->limit >= 1 )
arc->limit--;
if( arc->limit == 0 )
{
arc->rec_flags &= ~(TF_LIMIT | TF_RECUR);
arc->limit = 0;
//arc->nextdate = 0;
}
}
return arc->nextdate;
}
//hb-scheduled
void scheduled_date_get_show_minmax(gint select, guint32 *mindate, guint32 *maxdate)
{
GDate *date;
guint16 month, year;
if( (mindate == NULL) || (maxdate == NULL) )
return;
date = g_date_new_julian(GLOBALS->today);
switch( select )
{
case FLT_SCHEDULED_THISMONTH:
case FLT_SCHEDULED_NEXTMONTH:
g_date_set_day(date, 1);
if( select == FLT_SCHEDULED_NEXTMONTH)
g_date_add_months(date, 1);
*mindate = g_date_get_julian(date);
month = g_date_get_month(date);
year = g_date_get_year(date);
g_date_add_days(date, g_date_get_days_in_month(month, year));
*maxdate = g_date_get_julian(date) - 1;
break;
case FLT_SCHEDULED_NEXT30DAYS:
*mindate = GLOBALS->today;
*maxdate = GLOBALS->today + 30;
break;
case FLT_SCHEDULED_NEXT60DAYS:
*mindate = GLOBALS->today;
*maxdate = GLOBALS->today + 60;
break;
case FLT_SCHEDULED_NEXT90DAYS:
*mindate = GLOBALS->today;
*maxdate = GLOBALS->today + 90;
break;
default:
*mindate = HB_MINDATE;
*maxdate = HB_MAXDATE;
break;
}
g_date_free(date);
}
/*
* return the maximum date a scheduled txn can be posted to
*/
//hb-scheduled / ui-hbfile
guint32 scheduled_date_get_post_max(guint32 start, gint auto_smode, gint auto_nbdays, gint auto_weekday, gint nbmonth)
{
guint32 nbdays;
DB2( g_print("\n[scheduled] get max post date\n") );
switch( auto_smode)
{
//5.5.8 original algo
/*
//add until xx of 1 month (excluded)
today = g_date_new_julian(GLOBALS->today);
//we compute user xx weekday of next month
maxdate = g_date_new_julian(GLOBALS->today);
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);
*/
//add until xx of the yy month (excluded)
//if(auto_smode == 0)
case ARC_POSTMODE_PAYOUT:
{
GDate *tdate, *date;
gshort tday;
DB2( g_print(" payout %d of %d months\n", auto_weekday, nbmonth) );
tdate = g_date_new_julian(start);
tday = g_date_get_day(tdate);
//set /xx/xxxx
date = g_date_new_julian(start);
g_date_set_day(date, auto_weekday);
//if today is payout day (or future)
if( tday >= auto_weekday )
{
DB2( g_print(" day: %d >= %d\n", tday, auto_weekday) );
//we add nbmonth
g_date_add_months(date, 1);
if( nbmonth > 1 )
{
//here we consider start is january
//TODO: ? use also fisc_year_day + fisc_year_month
for(gshort i=1;iadd 1 month\n") );
g_date_add_months(date, 1);
}
}
}
//#2065740 as we now post maxpostdate included: -1
g_date_subtract_days(date, 1);
nbdays = g_date_days_between(tdate, date);
g_date_free(date);
g_date_free(tdate);
}
break;
//add xx days in advance the current date
//if(auto_smode == 1)
case ARC_POSTMODE_ADVANCE:
nbdays = auto_nbdays;
break;
case ARC_POSTMODE_DUEDATE:
default:
nbdays = 0;
break;
}
DB2( g_print(" nbdays=%d\n", nbdays) );
DB2( hb_print_date(start + nbdays, "out:") );
return start + nbdays;
}
//mainwindows / hub-scheduled
gint scheduled_post_all_pending(void)
{
GList *list;
gint count = 0;
guint32 maxpostdate;
DB( g_print("\n[scheduled] -- post_all_pending --\n") );
maxpostdate = scheduled_date_get_post_max(GLOBALS->today, GLOBALS->auto_smode, GLOBALS->auto_nbdays, GLOBALS->auto_weekday, GLOBALS->auto_nbmonths);
DB( hb_print_date(maxpostdate, " >maxpostdate") );
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *arc = list->data;
if( !(arc->rec_flags & TF_RECUR) )
goto nextarchive;
DB( g_print("----\neval %d w.e=%s limit=%d '%s' flags=0x%04x\n",
arc->nextdate,
hbtk_get_label(CYA_ARC_WEEKEND, arc->weekend),
arc->rec_flags & TF_LIMIT ? arc->limit : -1,
arc->memo,
arc->flags) );
if( !scheduled_is_postable(arc) )
{
DB( g_print(" >skip: not postable - auto=%d kacc=%d amt=%.2f \n", arc->flags & TF_RECUR, arc->kacc, arc->amount) );
goto nextarchive;
}
//TODO: maybe add a security here
for(;;)
{
Transaction *txn;
//#2065955 to get weekend before/after posted
guint32 jpostdate = scheduled_get_txn_real_postdate(arc->nextdate, arc->weekend);
#if MYDEBUG
if( arc->nextdate != jpostdate )
DB( g_print(" >shift: %d >> %d\n", arc->nextdate, jpostdate) );
#endif
//#2064839 <=
if( jpostdate <= maxpostdate )
{
DB( g_print(" >post: date=%d (%+d)\n", jpostdate, jpostdate - arc->nextdate) );
//5.5.3 fixed leak as this was outside the loop
txn = da_transaction_malloc();
da_transaction_init_from_template(txn, arc);
txn->date = jpostdate;
/* todo: ? fill in cheque number */
transaction_add(NULL, FALSE, txn);
da_transaction_free (txn);
count++;
//can switch TF_RECUR off, if limit reached
scheduled_date_advance(arc);
if( !(arc->rec_flags & TF_RECUR) )
{
DB( g_print(" >stop: limit reached\n") );
goto nextarchive;
}
}
else
{
DB( g_print(" >skip: no pending\n") );
goto nextarchive;
}
}
nextarchive:
list = g_list_next(list);
}
GLOBALS->changes_count += count;
return count;
}
homebank-5.9.7/src/rep-stats.c 0000644 0001750 0001750 00000226647 15005633707 015516 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "rep-stats.h"
#include "list-report.h"
#include "list-operation.h"
#include "gtk-chart.h"
#include "gtk-dateentry.h"
#include "hbtk-switcher.h"
#include "dsp-mainwindow.h"
#include "ui-filter.h"
#include "ui-transaction.h"
#include "ui-flt-widget.h"
#define HB_STATS_DO_TOTAL 1
#define HB_STATS_DO_TIME 1
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* prototypes */
static void repstats_range_change(GtkWidget *widget, gpointer user_data);
static void repstats_filter_setup(struct repstats_data *data);
static void repstats_compute(GtkWidget *widget, gpointer user_data);
static void repstats_sensitive(GtkWidget *widget, gpointer user_data);
static void repstats_update_daterange(GtkWidget *widget, gpointer user_data);
static void repstats_update_date_widget(GtkWidget *widget, gpointer user_data);
static void repstats_selection(GtkTreeSelection *treeselection, gpointer user_data);
static void repstats_selection2(GtkTreeSelection *treeselection, gpointer user_data);
static gchar *repstats_compute_title(gint mode, gint src, gint type, gint intvl);
extern HbKvData CYA_REPORT_SRC[];
extern HbKvData CYA_REPORT_TYPE[];
extern gchar *CYA_REPORT_MODE[];
extern HbKvData CYA_REPORT_INTVL[];
/* action functions -------------------- */
static void repstats_action_viewlist(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gint tmpmode;
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
hb_widget_visible (data->SW_total, tmpmode ? FALSE : TRUE);
hb_widget_visible (data->SW_trend, tmpmode ? TRUE : FALSE);
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 0);
data->charttype = 0;
repstats_sensitive(data->window, NULL);
}
static void repstats_action_mode_changed(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_list), TRUE);
repstats_action_viewlist(toolbutton, data);
if( data->detail == TRUE )
{
gint tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
if( tmpmode == 0 )
repstats_selection(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), NULL);
else
repstats_selection2(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report2)), NULL);
}
}
static void repstats_action_viewbar(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
gtk_chart_set_type (GTK_CHART(data->RE_chart), CHART_TYPE_COL);
data->charttype = CHART_TYPE_COL;
repstats_sensitive(data->window, NULL);
}
static void repstats_action_viewpie(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
gtk_chart_set_type (GTK_CHART(data->RE_chart), CHART_TYPE_PIE);
data->charttype = CHART_TYPE_PIE;
repstats_sensitive(data->window, NULL);
}
static void repstats_action_viewstack(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 2);
gtk_chart_set_type (GTK_CHART(data->RE_chart2), CHART_TYPE_STACK);
data->charttype = CHART_TYPE_STACK;
repstats_sensitive(data->window, NULL);
}
static void repstats_action_viewstack100(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 2);
gtk_chart_set_type (GTK_CHART(data->RE_chart2), CHART_TYPE_STACK100);
data->charttype = CHART_TYPE_STACK100;
repstats_sensitive(data->window, NULL);
}
static void repstats_action_print(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gint tmpsrc, tmpmode, tmptype, tmpintvl, page;
gchar *title, *name;
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmptype = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
name = g_strdup_printf("hb-repstat_%s", hbtk_get_label(CYA_REPORT_SRC,tmpsrc));
if( page == 0 )
{
GString *node;
title = repstats_compute_title(tmpmode, tmpsrc, tmptype, tmpintvl);
if( tmpmode == 0 )
node = lst_report_to_string(HB_STRING_PRINT, GTK_TREE_VIEW(data->LV_report), tmpsrc, hbtk_get_label(CYA_REPORT_SRC, tmpsrc));
else
node = lst_rep_time_to_string(HB_STRING_PRINT, GTK_TREE_VIEW(data->LV_report2), tmpsrc, NULL);
hb_print_listview(GTK_WINDOW(data->window), node->str, NULL, title, name, FALSE);
g_string_free(node, TRUE);
g_free(title);
}
else
{
if( tmpmode == 0 )
gtk_chart_print(GTK_CHART(data->RE_chart), GTK_WINDOW(data->window), PREFS->path_export, name);
else
gtk_chart_print(GTK_CHART(data->RE_chart2), GTK_WINDOW(data->window), PREFS->path_export, name);
}
g_free(name);
}
static void repstats_action_filter(GtkWidget *toolbutton, gpointer user_data)
{
struct repstats_data *data = user_data;
gint response_id;
DB( g_print("\n[repdist] filter\n") );
//debug
//create_deffilter_window(data->filter, TRUE);
response_id = ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, TRUE, FALSE);
if( response_id != GTK_RESPONSE_REJECT)
{
switch( response_id )
{
case HB_RESPONSE_FLT_RESET:
DB( g_print(" reset filter\n") );
repstats_filter_setup(data);
break;
case HB_RESPONSE_FLT_SAVE_USE:
ui_flt_popover_hub_save(data->PO_hubfilter, NULL);
break;
case GTK_RESPONSE_ACCEPT:
if( data->filter->range == FLT_RANGE_MISC_CUSTOM )
{
filter_preset_daterange_set(data->filter, data->filter->range, 0);
}
break;
}
//beta 5.8
ui_flt_manage_header_sensitive(data->PO_hubfilter, NULL);
g_signal_handler_block(data->CY_range, data->hid[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPDIST_RANGE]);
repstats_update_date_widget(data->window, NULL);
repstats_update_daterange(data->window, NULL);
repstats_compute(data->window, NULL);
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void beta_repstats_filter_cb_reset(GtkWidget *widget, gpointer user_data);
static void beta_repstats_filter_cb_preset_change(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
Filter *newflt;
DB( g_print("\n[repdist] filter change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
newflt = ui_flt_popover_hub_get(GTK_BOX(data->PO_hubfilter), NULL);
if( newflt )
{
DB( g_print(" key:%d, copy filter\n", newflt->key) );
da_flt_copy(newflt, data->filter);
//#2073805 same range will not trigger
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
g_signal_handler_block(data->CY_range, data->hid[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPDIST_RANGE]);
////#2076458 force update
repstats_range_change(widget, user_data);
ui_flt_manage_header_sensitive(data->PO_hubfilter, NULL);
}
else
beta_repstats_filter_cb_reset(widget, user_data);
}
static void beta_repstats_filter_cb_reset(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
GtkWidget * combobox;
DB( g_print("\n[repdist] filter reset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
combobox = ui_flt_popover_hub_get_combobox(GTK_BOX(data->PO_hubfilter), NULL);
g_signal_handlers_block_by_func (G_OBJECT (combobox), G_CALLBACK (beta_repstats_filter_cb_preset_change), NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
g_signal_handlers_unblock_by_func (G_OBJECT (combobox), G_CALLBACK (beta_repstats_filter_cb_preset_change), NULL);
repstats_filter_setup(data);
g_signal_handler_block(data->CY_range, data->hid[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPDIST_RANGE]);
repstats_update_date_widget(data->window, NULL);
repstats_update_daterange(data->window, NULL);
repstats_compute(data->window, NULL);
ui_flt_manage_header_sensitive(data->PO_hubfilter, NULL);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gchar *
repstats_compute_title(gint mode, gint src, gint type, gint intvl)
{
gchar *x, *y;
//5.7.3 fix title
if( mode == REPORT_MODE_TOTAL )
{
x = hbtk_get_label(CYA_REPORT_TYPE,type);
y = hbtk_get_label(CYA_REPORT_SRC, src);
}
else
{
x = hbtk_get_label(CYA_REPORT_SRC, src);
y = hbtk_get_label(CYA_REPORT_INTVL, intvl);
}
//TRANSLATORS: example 'Expense by Category'
//TRANSLATORS: example 'Category by Month'
return g_strdup_printf(_("%s by %s"), x, y );
}
static void repstats_date_change(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DB( g_print("\n[repdist] date change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->CY_range, data->hid[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPDIST_RANGE]);
repstats_compute(widget, NULL);
repstats_update_daterange(widget, NULL);
}
static void repstats_range_change(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
gint range;
DB( g_print("\n[repdist] range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
//should never happen
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, 0);
}
//#2046032 set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
repstats_update_date_widget(data->window, NULL);
//5.7.1 test add disable forecast
/*g_signal_handler_block(data->CM_forecast, data->hid[HID_REPDIST_FORECAST]);
gint can_future = filter_preset_daterange_future_enable(data->filter, range);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forecast), can_future ? PREFS->rep_forcast : FALSE);
gtk_widget_set_sensitive(data->CM_forecast, can_future);
g_signal_handler_unblock(data->CM_forecast, data->hid[HID_REPDIST_FORECAST]);
*/
repstats_compute(data->window, NULL);
repstats_update_daterange(data->window, NULL);
}
static void repstats_update(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
gboolean byamount;
GtkTreeModel *model;
gint tmpmode, tmpsrc, tmptype, tmpintvl, usrcomp, column;
gboolean xval;
gchar *title;
DB( g_print("\n[repdist] update\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_report));
byamount = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_byamount));
usrcomp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_compare));
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmptype = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
//debug option
DB( g_print(" option: byamount=%d tmptype=%d '%s' tmpsrc=%d '%s'\n\n", byamount, tmptype, hbtk_get_label(CYA_REPORT_TYPE,tmptype), tmpsrc, hbtk_get_label(CYA_REPORT_SRC,tmpsrc)) );
// define view/sort column
column = LST_REPORT_POS;
if( byamount )
{
switch( tmptype )
{
case REPORT_TYPE_TOTAL:
column = LST_REPORT_TOTAL;
break;
case REPORT_TYPE_EXPENSE:
column = LST_REPORT_EXPENSE;
break;
case REPORT_TYPE_INCOME:
column = LST_REPORT_INCOME;
break;
}
}
DB( g_print(" sort on column %d\n\n", column) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), column, GTK_SORT_ASCENDING);
title = repstats_compute_title(tmpmode, tmpsrc, tmptype, tmpintvl);
#if HB_STATS_DO_TOTAL == 1
gtk_chart_set_color_scheme(GTK_CHART(data->RE_chart), PREFS->report_color_scheme);
/* update absolute or not */
gboolean abs = (tmptype == REPORT_TYPE_EXPENSE || tmptype == REPORT_TYPE_INCOME) ? TRUE : FALSE;
gtk_chart_set_absolute(GTK_CHART(data->RE_chart), abs);
/* show xval for month/year and no by amount display */
xval = FALSE;
if( !byamount && (tmpsrc == REPORT_GRPBY_MONTH || tmpsrc == REPORT_GRPBY_YEAR) )
{
xval = TRUE;
/*switch( tmpsrc)
{
case REPORT_GRPBY_MONTH:
gtk_chart_set_every_xval(GTK_CHART(data->RE_chart), 4);
break;
case REPORT_GRPBY_YEAR:
gtk_chart_set_every_xval(GTK_CHART(data->RE_chart), 2);
break;
}*/
}
gtk_chart_show_xval(GTK_CHART(data->RE_chart), xval);
/* update bar chart */
if( (tmptype == REPORT_TYPE_TOTAL) && (usrcomp == TRUE) ) //dual exp/inc
{
DB( g_print(" set bar to dual exp %d/inc %d\n\n", LST_REPORT_EXPENSE, LST_REPORT_INCOME) );
//set column1 != column2 will dual display
gtk_chart_set_datas_total(GTK_CHART(data->RE_chart), model, LST_REPORT_EXPENSE, LST_REPORT_INCOME, title, NULL);
}
else
{
switch(tmptype)
{
case REPORT_TYPE_EXPENSE:
column = LST_REPORT_EXPENSE;
break;
case REPORT_TYPE_INCOME:
column = LST_REPORT_INCOME;
break;
default:
case REPORT_TYPE_TOTAL:
column = LST_REPORT_TOTAL;
break;
}
DB( g_print(" set data total to col=%d\n\n", column) );
//set column1 != column2 will dual display
gtk_chart_set_datas_total(GTK_CHART(data->RE_chart), model, column, column, title, NULL);
}
#endif
//time chart
#if HB_STATS_DO_TIME == 1
gtk_chart_set_color_scheme(GTK_CHART(data->RE_chart2), PREFS->report_color_scheme);
gtk_chart_show_xval(GTK_CHART(data->RE_chart2), TRUE);
//5.7 trendrow is unused, we pass the treeview to get the column labels
DB( g_print(" set data time\n") );
gtk_chart_set_datas_time(GTK_CHART(data->RE_chart2), GTK_TREE_VIEW(data->LV_report2), data->trend, data->trendrows, data->trendcols, title, NULL);
#endif
//test 5.8
da_flt_count_item(data->filter);
gchar *txt = filter_text_summary_get(data->filter);
ui_label_set_integer(GTK_LABEL(data->TX_fltactive), data->filter->n_active);
gtk_widget_set_tooltip_text(data->TT_fltactive, txt);
g_free(txt);
g_free(title);
}
static void repstats_update_date_widget(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DB( g_print("\n[repdist] update date widget\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
g_signal_handler_block(data->PO_mindate, data->hid[HID_REPDIST_MINDATE]);
g_signal_handler_block(data->PO_maxdate, data->hid[HID_REPDIST_MAXDATE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
g_signal_handler_unblock(data->PO_mindate, data->hid[HID_REPDIST_MINDATE]);
g_signal_handler_unblock(data->PO_maxdate, data->hid[HID_REPDIST_MAXDATE]);
}
static void repstats_update_daterange(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
gchar *daterange;
DB( g_print("\n[repdist] update daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
daterange = filter_daterange_text_get(data->filter);
gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
g_free(daterange);
}
static void repstats_update_total(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
//gboolean minor;
DB( g_print("\n[repdist] update total\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
hb_label_set_colvalue(GTK_LABEL(data->TX_total[0]), data->total_expense, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_total[1]), data->total_income, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_total[2]), data->total_expense + data->total_income, GLOBALS->kcur, GLOBALS->minor);
}
static void repstats_export_result_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repstats_data *data;
GtkClipboard *clipboard;
GString *node;
gchar *title, *catsubcat;
gint tmpmode, tmpsrc;
DB( g_print("\n[repdist] export result clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
//#1886299/1900281
catsubcat = NULL;
title = hbtk_get_label(CYA_REPORT_SRC,tmpsrc);
/*if(tmpsrc == REPORT_GRPBY_SUBCATEGORY)
{
catsubcat = g_strjoin(":",hbtk_get_label(CYA_REPORT_SRC, REPORT_GRPBY_CATEGORY), hbtk_get_label(CYA_REPORT_SRC, REPORT_GRPBY_SUBCATEGORY), NULL);
title = catsubcat;
}*/
if( tmpmode == 0 )
node = lst_report_to_string(HB_STRING_CLIPBOARD, GTK_TREE_VIEW(data->LV_report), tmpsrc, title);
else
node = lst_rep_time_to_string(HB_STRING_CLIPBOARD, GTK_TREE_VIEW(data->LV_report2), tmpsrc, title);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
g_free(catsubcat);
}
static void repstats_export_result_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repstats_data *data;
gchar *filename = NULL;
GString *node;
GIOChannel *io;
gchar *name, *title, *catsubcat;
gint tmpmode, tmpsrc;
DB( g_print("\n[repdist] export result csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
name = g_strdup_printf("hb-repstat_%s.csv", hbtk_get_label(CYA_REPORT_SRC,tmpsrc));
if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
//#1886299/1900281
catsubcat = NULL;
title = hbtk_get_label(CYA_REPORT_SRC,tmpsrc);
/*if(tmpsrc == REPORT_GRPBY_SUBCATEGORY)
{
catsubcat = g_strjoin(":",hbtk_get_label(CYA_REPORT_SRC, REPORT_GRPBY_CATEGORY), hbtk_get_label(CYA_REPORT_SRC, REPORT_GRPBY_SUBCATEGORY), NULL);
title = catsubcat;
}*/
if( tmpmode == 0 )
node = lst_report_to_string(HB_STRING_EXPORT, GTK_TREE_VIEW(data->LV_report), tmpsrc, title);
else
node = lst_rep_time_to_string(HB_STRING_EXPORT, GTK_TREE_VIEW(data->LV_report2), tmpsrc, title);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
g_free(catsubcat);
}
g_free( filename );
}
g_free(name);
}
static void repstats_export_detail_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repstats_data *data;
GtkClipboard *clipboard;
GString *node;
guint flags;
DB( g_print("\n[repdist] export detail clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
flags = LST_TXN_EXP_CLR | LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), TRUE, FALSE, FALSE, flags);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void repstats_export_detail_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct repstats_data *data;
gchar *filepath = NULL;
GString *node;
GIOChannel *io;
gchar *name;
gint tmpsrc;
gboolean hassplit, hasstatus;
DB( g_print("\n[repdist] export detail csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
name = g_strdup_printf("hb-repstat-detail_%s.csv", hbtk_get_label(CYA_REPORT_SRC,tmpsrc));
filepath = g_build_filename(PREFS->path_export, name, NULL);
//#2019312
//if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
if( ui_dialog_export_csv(GTK_WINDOW(data->window), &filepath, &hassplit, &hasstatus, FALSE) == GTK_RESPONSE_ACCEPT )
{
DB( g_print(" + filepath is %s\n", filepath) );
io = g_io_channel_new_file(filepath, "w", NULL);
if(io != NULL)
{
guint flags;
flags = LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
if( hasstatus )
flags |= LST_TXN_EXP_CLR;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), FALSE, hassplit, FALSE, flags);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
}
g_free( filepath );
g_free(name);
}
static void repstats_detail(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
guint active = GPOINTER_TO_INT(user_data);
guint tmpmode, tmpsrc, tmpintvl;
GList *list;
GtkTreeModel *model;
GtkTreeIter iter, child;
Category *active_cat;
gboolean is_subcat = FALSE;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repdist] detail populate\n") );
/* clear and detach our model */
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if( data->detail == 0 || data->txn_queue == NULL )
{
DB( g_print(" > skipped: detail hidden or no txn\n") );
return;
}
//get cat/subcat
active_cat = da_cat_get(active);
if( active_cat )
is_subcat = (active_cat->parent == 0) ? FALSE : TRUE;
tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
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_detail), NULL); /* Detach model from view */
/* fill in the model */
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
gdouble dtlamt = ope->amount;
if(tmpmode == 1 && data->sel_colid != -1 && data->sel_colid < LST_REP_COLID_MAX)
{
gint txn_colid = report_interval_get_pos(tmpintvl, data->filter->mindate, ope);
//DB( g_print(" sel_colid=%d txncolid=%d\n", data->sel_colid, txn_colid) );
if( data->sel_colid != txn_colid )
goto next_txn;
}
if(filter_txn_match(data->filter, ope) == 1)
{
gboolean match = FALSE;
guint i, key = 0;
switch( tmpsrc )
{
case REPORT_GRPBY_CATEGORY:
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
Split *split;
dtlamt = 0.0;
for(i=0;isplits, i);
//key = category_report_id(split->kcat, (tmpsrc == REPORT_GRPBY_SUBCATEGORY) ? TRUE : FALSE);
key = category_report_id(split->kcat, is_subcat);
if( key == active )
{
match = TRUE;
dtlamt += split->amount;
// no more break here as we need to compute split total
//break;
}
}
}
else
{
key = category_report_id(ope->kcat, is_subcat);
if( key == active )
{
match = TRUE;
}
}
break;
/* the TAG process is particular */
case REPORT_GRPBY_TAG:
if(ope->tags != NULL)
{
guint32 *tptr = ope->tags;
while(*tptr)
{
key = *tptr;
if( key == active )
{
match = TRUE;
break;
}
tptr++;
}
}
else
match = ( key == active ) ? TRUE : FALSE;
break;
default:
key = report_items_get_key(tmpsrc, data->filter->mindate, ope);
if( key == active )
{
match = TRUE;
}
break;
}
if( match == TRUE )
{
DB( g_print(" txn match to key=%d\n", active) );
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &iter, NULL, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITAMT, dtlamt,
-1);
//#1875801 show split detail
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
Split *split;
gboolean sinsert = TRUE;
for(i=0;isplits, i);
//if( (tmpsrc == REPORT_GRPBY_CATEGORY || tmpsrc == REPORT_GRPBY_SUBCATEGORY) )
if( tmpsrc == REPORT_GRPBY_CATEGORY )
{
//key = category_report_id(split->kcat, (tmpsrc == REPORT_GRPBY_SUBCATEGORY) ? TRUE : FALSE);
key = category_report_id(split->kcat, FALSE);
DB( g_print(" %d =? %d => %d\n", key, active, key != active ? FALSE : TRUE) );
if( key != active )
{
sinsert = FALSE;
}
}
if( sinsert == TRUE )
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &child, &iter, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITPTR, split,
-1);
}
}
}
}
}
next_txn:
list = g_list_next(list);
}
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
g_object_unref(model);
gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
//gtk_tree_view_expand_all( GTK_TREE_VIEW(data->LV_detail) );
}
static gboolean
reptime_list_click_func ( GtkWidget *widget, GdkEvent *event, gpointer user_data )
{
GdkEventType type;
guint button = 0;
gdouble win_x, win_y;
type = gdk_event_get_event_type(event);
gdk_event_get_button(event, &button);
gdk_event_get_coords(event, &win_x, &win_y);
if (type == GDK_BUTTON_PRESS)
{
struct repstats_data *data;
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeViewColumn *column;
GtkTreeIter iter;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" capture row/col\n") );
data->sel_colid = -1;
if( gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), win_x, win_y, &path, &column, NULL, NULL) )
{
data->sel_colid = gtk_tree_view_column_get_sort_column_id (column);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
if(gtk_tree_model_get_iter(model, &iter, path) )
{
gint key;
gtk_tree_model_get(model, &iter, LST_REPORT2_KEY, &key, -1);
DB( g_print(" reptime click key=%d col=%d '%s'\n", key, data->sel_colid, gtk_tree_view_column_get_title (column)) );
repstats_detail(widget, GINT_TO_POINTER(key));
}
gtk_tree_path_free (path);
}
}
return FALSE;
}
static void repstats_compute(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DataTable *dt;
gint tmpsrc, tmptype, tmpintvl;
gdouble totexp, totinc;
gboolean tmpaccbal, tmpforecast;
guint i, n_inserted;
GtkTreeModel *model;
GtkTreeIter iter, parent, *tmpparent;
DB( g_print("\n----------------\n[repdist] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//clear all
#if HB_STATS_DO_TOTAL == 1
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
gtk_tree_store_clear (GTK_TREE_STORE(model));
gtk_chart_set_datas_none(GTK_CHART(data->RE_chart));
#endif
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
data->txn_queue = NULL;
//refresh the datatable
#if HB_STATS_DO_TIME == 1
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report2));
gtk_tree_store_clear (GTK_TREE_STORE(model));
gtk_chart_set_datas_none(GTK_CHART(data->RE_chart2));
#endif
if(data->trend)
da_datatable_free (data->trend);
data->trend = NULL;
data->total_expense = 0.0;
data->total_income = 0.0;
repstats_update_total(widget, user_data);
//#2019876 return is invalid date range
if( data->filter->maxdate < data->filter->mindate )
return;
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmptype = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
tmpaccbal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_balance));
tmpforecast = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forecast));
if( tmpsrc != REPORT_GRPBY_ACCOUNT && tmpsrc != REPORT_GRPBY_ACCGROUP)
tmpaccbal = FALSE;
DB( hb_print_date(data->filter->mindate, "min:") );
DB( hb_print_date(data->filter->maxdate, "max:") );
//#2030334 get the forecat max date
guint32 jmaxdateforecast = data->filter->maxdate;
//#2036228 don't force for custom range
gint range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
if( range != FLT_RANGE_MISC_CUSTOM )
{
if( tmpforecast == TRUE )
jmaxdateforecast = filter_get_maxdate_forecast(data->filter);
DB( hb_print_date(jmaxdateforecast, "maxforecast:") );
}
//TODO: not necessary until date range change
//data->txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
data->txn_queue = hbfile_transaction_get_partial(data->filter->mindate, jmaxdateforecast);
DB( g_print(" for=%d,kind=%d\n", tmpsrc, tmptype) );
DB( g_print(" nb-txn=%d\n", g_queue_get_length (data->txn_queue) ) );
//set filter
/*if(!tmpaccbal)
filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, (PREFS->stat_includexfer == FALSE) ? FLT_EXCLUDE : FLT_INCLUDE);
else
filter_preset_type_set(data->filter, FLT_TYPE_ALL, FLT_OFF);*/
#if HB_STATS_DO_TOTAL == 1
DB( g_print(" -- start total --\n") );
//TODO: add if mode==0
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
n_inserted = 0;
totexp = totinc = 0.0;
dt = report_compute(tmpsrc, tmpintvl, data->filter, data->txn_queue, (tmpaccbal == TRUE) ? REPORT_COMP_FLG_BALANCE : REPORT_COMP_FLG_NONE);
if(dt)
{
struct lst_report_data *lst_data;
DataRow *dr;
//store the totals from total datarow
dr = dt->totrow;
lst_data = g_object_get_data(G_OBJECT(data->LV_report), "inst_data");
if( lst_data != NULL )
{
lst_data->tot_exp = dr->rowexp;
lst_data->tot_inc = dr->rowinc;
}
data->total_expense = dr->rowexp;
data->total_income = dr->rowinc;
//total_total = dt->totexp + dt->totinc;
//#2018145 rate total must remains ABS to get accurate %
//ratetotal = ABS(dr->rowexp) + ABS(dr->rowinc);
DB( g_printf(" total exp %9.2f\n", dr->rowexp) );
DB( g_printf(" total inc %9.2f\n", dr->rowinc) );
//DB( g_printf(" total tot %9.2f\n", total_total) );
// clear and detach our 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_report), NULL); // Detach model from view
DB( g_print("\n -- populate total listview : %d rows --\n", dt->nbrows) );
// insert into the treeview
for(i=0; i < dt->nbrows; i++)
{
//gdouble drtotal;
gboolean insert;
guint32 reskey;
gchar *overlabel = NULL;
//since 5.7 we use the dt-keylst here to insert cat before subcat
reskey = dt->keylist[i];
DB( g_print(" get row for key=%d\n", reskey) );
dr = report_data_get_row(dt, reskey);
// skip empty results
insert = TRUE;
if( !(tmpsrc == REPORT_GRPBY_MONTH || tmpsrc == REPORT_GRPBY_YEAR) )
{
if( (tmptype == REPORT_TYPE_EXPENSE && !dr->rowexp) ||
(tmptype == REPORT_TYPE_INCOME && !dr->rowinc) ||
(!dr->rowexp && !dr->rowinc)
)
insert = FALSE;
}
//#2065531 skip tag filtered
if( tmpsrc == REPORT_GRPBY_TAG && data->filter->option[FLT_GRP_TAG] != 0)
{
if( !da_flt_status_tag_get(data->filter, reskey) )
insert = FALSE;
}
// skip no account
//if( (tmpsrc == REPORT_GRPBY_ACCOUNT) && (i == 0) )
// insert = FALSE;
if( insert == FALSE )
{
DB( g_printf(" skip: %2d, '%s', %9.2f %9.2f %9.2f\n", i, dr->label,
dr->rowexp, dr->rowinc, dr->rowexp - dr->rowinc) );
continue;
}
n_inserted++;
// manage the toplevel for category
tmpparent = NULL;
if( tmpsrc == REPORT_GRPBY_CATEGORY )
{
Category *tmpcat = da_cat_get(reskey);
if( tmpcat != NULL )
{
overlabel = (tmpcat->key == 0) ? NULL : tmpcat->typename;
if( tmpcat->parent != 0 )
{
//if( lst_report_get_top_level (GTK_TREE_MODEL(model), tmpcat->parent, &parent) == TRUE )
if( hbtk_tree_store_get_top_level(GTK_TREE_MODEL(model), LST_REPORT_KEY, tmpcat->parent, &parent) )
{
tmpparent = &parent;
}
else
{
DB( g_print(" !! no parent %d found for %d %s\n", tmpcat->parent, tmpcat->key, tmpcat->fullname) );
}
}
else
//if( tmpcat->parent == 0 )
//only sum for category, not subcat
{
totexp += dr->rowexp;
totinc += dr->rowinc;
}
}
}
//if not category, sum
else
{
totexp += dr->rowexp;
totinc += dr->rowinc;
}
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &iter, tmpparent, -1,
LST_REPORT_POS , dr->pos,
LST_REPORT_KEY , reskey,
LST_REPORT_LABEL, dr->label,
LST_REPORT_OVERLABEL, overlabel,
//LST_REPORT_ROW, dr,
LST_REPORT_EXPENSE, dr->rowexp,
LST_REPORT_INCOME , dr->rowinc,
LST_REPORT_TOTAL , dr->rowexp + dr->rowinc,
-1);
DB( g_printf(" insert: %2d, '%s', %9.2f %9.2f %9.2f\n", i, dr->label,
dr->rowexp, dr->rowinc, dr->rowexp - dr->rowinc) );
}
// update column 0 title
GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
gtk_tree_view_column_set_title(column, hbtk_get_label(CYA_REPORT_SRC,tmpsrc));
// Re-attach model to view
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
g_object_unref(model);
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
}
da_datatable_free (dt);
//insert total
if( n_inserted > 1 )
{
DB( g_print(" insert total\n") );
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &iter, NULL, -1,
LST_REPORT_POS, LST_REPORT_POS_TOTAL,
LST_REPORT_KEY, -1,
LST_REPORT_LABEL, _("Total"),
LST_REPORT_OVERLABEL, NULL,
LST_REPORT_EXPENSE, totexp,
LST_REPORT_INCOME , totinc,
LST_REPORT_TOTAL, totexp + totinc,
-1);
}
#endif
/* ---- trend with test multi to remove ---- */
//TODO: add if mode==1 && choose a better max column value
#if HB_STATS_DO_TIME == 1
DB( g_print(" -- start time --\n") );
n_inserted = 0;
//check limit
i = report_interval_count(tmpintvl, data->filter->mindate, data->filter->maxdate);
if( i > LST_REP_COLID_MAX )
{
//lst_rep_time_renewcol(GTK_TREE_VIEW(data->LV_report2), 0, data->filter->mindate, tmpintvl);
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Too much columns to display"),
_("Please select a wider interval and / or a narrower date range")
);
}
else
{
//#2030334 to avoid forecast column to remains
//we force the date here only when forecast is ON
guint32 saveddate = data->filter->maxdate;
data->filter->maxdate = jmaxdateforecast;
DB( hb_print_date(saveddate, "saveddate:") );
DB( hb_print_date(data->filter->maxdate, "maxdate:") );
guint flags = REPORT_COMP_FLG_NONE;
if( tmpforecast )
flags |= REPORT_COMP_FLG_FORECAST;
if( tmpaccbal )
flags |= REPORT_COMP_FLG_BALANCE;
dt = report_compute(tmpsrc, tmpintvl, data->filter, data->txn_queue, flags);
data->trend = dt;
data->filter->maxdate = saveddate;
DB( hb_print_date(data->filter->maxdate, "maxdate after compute:") );
//end of forceddate
DB( g_print("\n -- populate time listview : %d rows, %d cols --\n", dt->nbrows, dt->nbcols) );
// clear and detach our model
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report2));
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_report2), NULL); // Detach model from view
lst_rep_time_renewcol(GTK_TREE_VIEW(data->LV_report2), model, dt, tmpaccbal ? FALSE : TRUE);
// update column 0 title
GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report2), 0);
gtk_tree_view_column_set_title(column, hbtk_get_label(CYA_REPORT_SRC,tmpsrc));
//#1955046 treeview with child was a test faulty released
for(i=0; i < dt->nbrows; i++)
{
DataRow *dr;
guint32 reskey;
gchar *overlabel = NULL;
reskey = dt->keylist[i];
dr = report_data_get_row(dt, reskey);
//#2024940 test on exp/inc individually
if( (hb_amount_cmp(dr->rowexp, 0.0)==0) && (hb_amount_cmp(dr->rowinc, 0.0)==0) )
{
DB( g_printf(" %4d:'%s' %.2f :: hide (no data)\n", i, dr->label, dr->rowexp + dr->rowinc ) );
continue;
}
//#2065531 skip tag filtered
if( tmpsrc == REPORT_GRPBY_TAG && data->filter->option[FLT_GRP_TAG] != 0)
{
if( !da_flt_status_tag_get(data->filter, reskey) )
{
DB( g_printf(" %4d:'%s' %.2f :: hide (tag filtered)\n", i, dr->label, dr->rowexp + dr->rowinc ) );
continue;
}
}
DB( g_printf(" %4d:'%s' %.2f :: insert\n", i, dr->label, dr->rowexp + dr->rowinc ) );
n_inserted++;
// manage the toplevel for category
tmpparent = NULL;
//if( tmpsrc == REPORT_GRPBY_CATEGORY || tmpsrc == REPORT_GRPBY_SUBCATEGORY )
if( tmpsrc == REPORT_GRPBY_CATEGORY )
{
Category *tmpcat = da_cat_get(reskey);
if( tmpcat != NULL)
{
overlabel = (tmpcat->key == 0) ? NULL : tmpcat->typename;
//if( lst_rep_time_get_top_level (GTK_TREE_MODEL(model), tmpcat->parent, &parent) == TRUE )
if( hbtk_tree_store_get_top_level(GTK_TREE_MODEL(model), LST_REPORT2_KEY, tmpcat->parent, &parent) )
{
tmpparent = &parent;
}
else
{
DB( g_print(" !! no parent %d found for %d %s\n", tmpcat->parent, tmpcat->key, tmpcat->fullname) );
}
}
}
DB( g_printf(" --> insert\n") );
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, tmpparent, -1,
LST_REPORT2_POS, n_inserted,
LST_REPORT2_KEY, reskey,
LST_REPORT2_LABEL, dr->label,
LST_REPORT2_OVERLABEL, overlabel,
LST_REPORT2_ROW, dr,
-1);
}
data->trendrows = n_inserted;
data->trendcols = dt->nbcols;
//insert total
if( n_inserted > 1 )
{
DataRow *dr = dt->totrow;
dr->pos = LST_REPORT_POS_TOTAL;
DB( g_printf(" eval item total:'%s'\n", dr->label ) );
DB( g_printf(" --> insert total\n") );
n_inserted++;
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, NULL, -1,
LST_REPORT2_POS, LST_REPORT_POS_TOTAL,
LST_REPORT2_KEY, -1,
LST_REPORT2_LABEL, _("Total"),
LST_REPORT2_OVERLABEL, NULL,
LST_REPORT2_ROW, dr,
-1);
}
// Re-attach model to view
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report2), model);
g_object_unref(model);
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report2));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report2));
// test liststore
//liststore_benchmark();
// test multi end
}
#endif
//here we use data->total_expense/income
repstats_update_total(widget, NULL);
repstats_update(widget, user_data);
}
/*
** update visibility/sensitivity
*/
static void repstats_sensitive(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
gboolean visible, sensitive;
gint mode, type, tmpsrc, page;
DB( g_print("\n[repdist] sensitive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
mode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
type = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
//total
visible = mode == 0 ? TRUE : FALSE;
hb_widget_visible(data->LB_type, visible);
hb_widget_visible(data->CY_type, visible);
hb_widget_visible(data->CM_byamount, visible);
hb_widget_visible(data->CM_compare, visible);
hb_widget_visible(data->BT_column, visible);
hb_widget_visible(data->BT_donut, visible);
hb_widget_visible(data->BT_stack, !visible);
hb_widget_visible(data->BT_stack100, !visible);
hb_widget_visible(data->LB_intvl, !visible);
hb_widget_visible(data->CY_intvl, !visible);
visible = mode == 0 ? FALSE : PREFS->rep_forcast;
hb_widget_visible(data->CM_forecast, visible);
visible = (mode==0 && page == 0) ? TRUE : FALSE;
hb_widget_visible(data->BT_rate, visible);
visible = (tmpsrc == REPORT_GRPBY_ACCOUNT) || (tmpsrc == REPORT_GRPBY_ACCGROUP) ? TRUE : FALSE;
hb_widget_visible(data->CM_balance, visible);
//zoom
visible = (data->charttype == CHART_TYPE_COL
|| data->charttype == CHART_TYPE_STACK
|| data->charttype == CHART_TYPE_STACK100) ? TRUE : FALSE;
hb_widget_visible(data->LB_zoomx, visible);
hb_widget_visible(data->RG_zoomx, visible);
//compare
visible = (type == REPORT_TYPE_TOTAL) && (data->charttype == CHART_TYPE_COL) ? TRUE : FALSE;
hb_widget_visible(data->CM_compare, visible);
visible = page == 0 ? TRUE : FALSE;
hb_widget_visible (data->BT_detail , visible);
hb_widget_visible (data->BT_export , visible);
visible = (page == 0 && tmpsrc == REPORT_GRPBY_CATEGORY) ? TRUE : FALSE;
hb_widget_visible (data->GR_listbar, visible);
visible = (page > 0) ? TRUE : FALSE;
hb_widget_visible (data->BT_legend, visible);
//hb_widget_visible (data->BT_print, visible);
page = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail)), NULL);
sensitive = ((page > 0) && data->detail) ? TRUE : FALSE;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detclip")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detcsv")), sensitive);
}
static void repstats_detail_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct repstats_data *data;
Transaction *active_txn;
gboolean result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[repdist] A detail row has been double-clicked!\n") );
active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_detail));
if(active_txn)
{
Transaction *old_txn, *new_txn;
//#1909749 skip reconciled if lock is ON
if( PREFS->safe_lock_recon == TRUE && active_txn->status == TXN_STATUS_RECONCILED )
return;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path = NULL;
//1936806 keep the selection
gint tmpmode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tmpmode == 0 ? data->LV_report : data->LV_report2));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
path = gtk_tree_model_get_path(model, &iter);
}
//#1640885
GLOBALS->changes_count++;
repstats_compute(data->window, NULL);
if( path != NULL )
{
gtk_tree_selection_select_path(treeselection, path);
gtk_tree_path_free(path);
}
}
da_transaction_free (old_txn);
}
}
static void repstats_update_detail(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DB( g_print("\n[repdist] update detail\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if(GTK_IS_TREE_VIEW(data->LV_report))
{
DB( g_print(" showdetail=%d\n", data->detail) );
//#2018039
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_detail), PREFS->safe_lock_recon);
if(data->detail)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
guint key;
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_REPORT_KEY, &key, -1);
DB( g_print(" - active is %d\n", key) );
repstats_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
}
gtk_widget_show(data->GR_detail);
}
else
gtk_widget_hide(data->GR_detail);
}
}
static void repstats_toggle_detail(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->detail ^= 1;
DB( g_print("\n[repdist] toggledetail to %d\n", data->detail) );
repstats_update_detail(widget, user_data);
repstats_sensitive(widget, NULL);
}
/*
** change the chart legend visibility
*/
static void repstats_toggle_legend(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DB( g_print("\n[repdist] toggle legend\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->legend ^= 1;
gtk_chart_show_legend(GTK_CHART(data->RE_chart), data->legend, FALSE);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart));
gtk_chart_show_legend(GTK_CHART(data->RE_chart2), data->legend, FALSE);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart2));
}
static void repstats_zoomx_callback(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
gdouble value;
DB( g_print("\n[repdist] zoomx\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
value = gtk_range_get_value(GTK_RANGE(data->RG_zoomx));
DB( g_print(" + scale is %.2f\n", value) );
gtk_chart_set_barw(GTK_CHART(data->RE_chart), value);
gtk_chart_set_barw(GTK_CHART(data->RE_chart2), value);
}
/*
** change the chart rate columns visibility
*/
static void repstats_toggle_rate(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
GtkTreeViewColumn *column;
DB( g_print("\n[repdist] toggle rate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->rate ^= 1;
if(GTK_IS_TREE_VIEW(data->LV_report))
{
column = gtk_tree_view_get_column (GTK_TREE_VIEW(data->LV_report), 2);
gtk_tree_view_column_set_visible(column, data->rate);
column = gtk_tree_view_get_column (GTK_TREE_VIEW(data->LV_report), 4);
gtk_tree_view_column_set_visible(column, data->rate);
column = gtk_tree_view_get_column (GTK_TREE_VIEW(data->LV_report), 6);
gtk_tree_view_column_set_visible(column, data->rate);
}
}
static void repstats_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
DB( g_print("\n[repdist] toggle minor\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
repstats_update_total(widget,NULL);
//hbfile_update(data->LV_acc, (gpointer)4);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
gtk_chart_show_minor(GTK_CHART(data->RE_chart), GLOBALS->minor);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart));
gtk_chart_show_minor(GTK_CHART(data->RE_chart2), GLOBALS->minor);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart2));
}
static void repstats_cb_expand_all(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repdist] expand all (data=%p)\n", data) );
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report));
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_report2));
}
static void repstats_cb_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct repstats_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[repdist] collapse all (data=%p)\n", data) );
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_report));
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_report2));
}
static void repstats_cb_sortcolumnchanged(GtkTreeSortable *sortable, gpointer user_data)
{
DB( g_print("\n[repdist] sort column chnaged\n") );
}
static void repstats_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
guint key = -1;
DB( g_print("\n[repdist] selection total\n") );
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_REPORT_KEY, &key, -1);
}
DB( g_print(" - total active is %d\n", key) );
repstats_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
repstats_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void repstats_selection2(GtkTreeSelection *treeselection, gpointer user_data)
{
//GtkTreeModel *model;
//GtkTreeIter iter;
//guint key = -1;
DB( g_print("\n[repdist] selection time\n") );
/*if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_REPORT2_KEY, &key, -1);
}
DB( g_print(" - time active is %d\n", key) );
repstats_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));*/
repstats_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static const GActionEntry win_actions[] = {
{ "resclip" , repstats_export_result_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "rescsv" , repstats_export_result_csv, NULL, NULL, NULL, {0,0,0} },
{ "detclip" , repstats_export_detail_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "detcsv" , repstats_export_detail_csv, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
static GtkWidget *
repstats_toolbar_create(struct repstats_data *data)
{
GtkWidget *toolbar, *button;
toolbar = gtk_toolbar_new();
button = (GtkWidget *)gtk_radio_tool_button_new(NULL);
data->BT_list = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LIST, "label", _("List"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as list"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_column = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_COLUMN, "label", _("Column"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as column"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_donut = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_DONUT, "label", _("Donut"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as donut"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_stack = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_STACK, "label", _("Stack"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as stack"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_stack100 = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_STACK100, "label", _("Stack 100%"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as stack 100%"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_OPE_SHOW,
"label", _("Detail"),
"tooltip-text", _("Toggle detail"),
"active", PREFS->stat_showdetail,
NULL);
data->BT_detail = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_SHOW_LEGEND,
"label", _("Legend"),
"tooltip-text", _("Toggle legend"),
"active", TRUE,
NULL);
data->BT_legend = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_SHOW_RATE,
"label", _("Rate"),
"tooltip-text", _("Toggle rate"),
"active", PREFS->stat_showrate,
NULL);
data->BT_rate = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
//button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_FILTER, _("Filter"), _("Edit filter"));
//data->BT_filter = button;
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REFRESH, _("Refresh"), _("Refresh results"));
data->BT_refresh = button;
//export button
button = gtk_menu_button_new();
data->BT_export = button;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(button)), GTK_STYLE_CLASS_FLAT);
GtkWidget *image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_EXPORT, GTK_ICON_SIZE_LARGE_TOOLBAR);
g_object_set (button, "image", image, NULL);
GtkToolItem *toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), button);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(toolitem), -1);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Result to clipboard"), "win.resclip");
g_menu_append (section, _("_Result to CSV") , "win.rescsv");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Detail to clipboard"), "win.detclip");
g_menu_append (section, _("_Detail to CSV") , "win.detcsv");
g_object_unref (section);
GActionGroup *actiongroup = (GActionGroup*)g_simple_action_group_new ();
data->actions = actiongroup;
g_action_map_add_action_entries (G_ACTION_MAP (actiongroup), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (button, "win", G_ACTION_GROUP(actiongroup));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), G_MENU_MODEL (menu));
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_PRINT, _("Print"), _("Print"));
data->BT_print = button;
return toolbar;
}
//reset the filter
static void repstats_filter_setup(struct repstats_data *data)
{
DB( g_print("\n[repdist] reset filter\n") );
filter_reset(data->filter);
filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
//#1989211 option to include xfer by default
/* 3.4 : make int transfer out of stats */
if(PREFS->stat_includexfer == FALSE)
filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
DB( hb_print_date(data->filter->mindate, "min:") );
DB( hb_print_date(data->filter->maxdate, "max:") );
}
// setup default for our object/memory
static void repstats_window_setup(struct repstats_data *data)
{
DB( g_print("\n[repdist] setup\n") );
DB( g_print(" init data\n") );
repstats_filter_setup(data);
//DB( g_print(" populate\n") );
DB( g_print(" set widgets default\n") );
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_detail), PREFS->stat_showdetail);
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_legend), TRUE);
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_rate) , PREFS->stat_showrate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor), GLOBALS->minor);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_byamount), PREFS->stat_byamount);
//5.8 EXP > TOTAL
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_type), REPORT_TYPE_TOTAL);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forecast), PREFS->rep_forcast);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail))), "minor", (gpointer)data->CM_minor);
gtk_chart_set_smallfont (GTK_CHART(data->RE_chart), PREFS->rep_smallfont);
gtk_chart_set_smallfont (GTK_CHART(data->RE_chart2), PREFS->rep_smallfont);
repstats_toggle_rate(data->window, NULL);
DB( g_print(" connect widgets signals\n") );
g_signal_connect( ui_flt_popover_hub_get_combobox(GTK_BOX(data->PO_hubfilter), NULL), "changed", G_CALLBACK (beta_repstats_filter_cb_preset_change), NULL);
g_signal_connect (data->BT_reset , "clicked", G_CALLBACK (beta_repstats_filter_cb_reset), NULL);
g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repstats_toggle_minor), NULL);
g_signal_connect (G_OBJECT (data->BT_expand), "clicked", G_CALLBACK (repstats_cb_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapse), "clicked", G_CALLBACK (repstats_cb_collapse_all), NULL);
data->hid[HID_REPDIST_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repstats_date_change), (gpointer)data);
data->hid[HID_REPDIST_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repstats_date_change), (gpointer)data);
data->hid[HID_REPDIST_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repstats_range_change), NULL);
g_signal_connect (data->CY_src, "changed", G_CALLBACK (repstats_compute), (gpointer)data);
g_signal_connect (data->RA_mode, "changed", G_CALLBACK (repstats_action_mode_changed), (gpointer)data);
g_signal_connect (data->CY_intvl, "changed", G_CALLBACK (repstats_compute), (gpointer)data);
data->hid[HID_REPDIST_VIEW] = g_signal_connect (data->CY_type, "changed", G_CALLBACK (repstats_compute), (gpointer)data);
g_signal_connect (data->RG_zoomx, "value-changed", G_CALLBACK (repstats_zoomx_callback), NULL);
g_signal_connect (data->CM_balance, "toggled", G_CALLBACK (repstats_compute), NULL);
data->hid[HID_REPDIST_FORECAST] = g_signal_connect (data->CM_forecast, "toggled", G_CALLBACK (repstats_compute), NULL);
g_signal_connect (data->CM_byamount, "toggled", G_CALLBACK (repstats_update), NULL);
g_signal_connect (data->CM_compare, "toggled", G_CALLBACK (repstats_update), NULL);
g_signal_connect (G_OBJECT (data->BT_list), "clicked", G_CALLBACK (repstats_action_viewlist), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_column), "clicked", G_CALLBACK (repstats_action_viewbar), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_donut), "clicked", G_CALLBACK (repstats_action_viewpie), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_stack), "clicked", G_CALLBACK (repstats_action_viewstack), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_stack100), "clicked", G_CALLBACK (repstats_action_viewstack100), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_detail), "clicked", G_CALLBACK (repstats_toggle_detail), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_legend), "clicked", G_CALLBACK (repstats_toggle_legend), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_rate), "clicked", G_CALLBACK (repstats_toggle_rate), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_filter), "clicked", G_CALLBACK (repstats_action_filter), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_refresh), "clicked", G_CALLBACK (repstats_compute), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_print), "clicked", G_CALLBACK (repstats_action_print), (gpointer)data);
//export is a menu
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report )), "changed", G_CALLBACK (repstats_selection), NULL);
g_signal_connect (gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report)), "sort-column-changed", G_CALLBACK (repstats_cb_sortcolumnchanged), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report2)), "changed", G_CALLBACK (repstats_selection2), NULL);
//we use this to intercep column click
g_signal_connect ( G_OBJECT (data->LV_report2), "button-press-event", G_CALLBACK (reptime_list_click_func), data );
g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (repstats_detail_onRowActivated), NULL);
}
static gboolean repstats_window_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repstats_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[repdist] window mapped\n") );
//setup, init and show window
repstats_window_setup(data);
repstats_update_date_widget(data->window, NULL);
repstats_update_daterange(data->window, NULL);
repstats_compute(data->window, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static gboolean repstats_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repstats_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[repdist] dispose\n") );
/* test multi */
if(data->trend)
da_datatable_free (data->trend);
if(data->txn_queue)
g_queue_free (data->txn_queue);
da_flt_free(data->filter);
g_free(data);
//store position and size
wg = &PREFS->sta_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
//enable define windows
GLOBALS->define_off--;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
//unref window to our open window list
GLOBALS->openwindows = g_slist_remove(GLOBALS->openwindows, widget);
return FALSE;
}
//allocate our object/memory
static void repstats_window_acquire(struct repstats_data *data)
{
DB( g_print("\n[repdist] acquire\n") );
data->txn_queue = g_queue_new ();
data->filter = da_flt_malloc();
data->detail = PREFS->stat_showdetail;
data->legend = 1;
data->rate = PREFS->stat_showrate^1;
}
// the window creation
GtkWidget *repstats_window_new(void)
{
struct repstats_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *hbox, *vbox, *tbar, *bbox, *fbox, *notebook, *treeview, *vpaned, *scrollwin;
GtkWidget *label, *widget, *table, *entry;
gint row;
DB( g_print("\n[repdist] new\n") );
data = g_malloc0(sizeof(struct repstats_data));
if(!data) return NULL;
repstats_window_acquire(data);
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//ref window to our open window list
GLOBALS->openwindows = g_slist_prepend(GLOBALS->openwindows, window);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
gtk_window_set_title (GTK_WINDOW (window), _("Statistics Report"));
//window contents
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_SMALL);
gtk_window_set_child(GTK_WINDOW(window), mainbox);
//control part
table = gtk_grid_new ();
gtk_widget_set_hexpand (GTK_WIDGET(table), FALSE);
gtk_box_prepend (GTK_BOX (mainbox), table);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
//label = make_label_group(_("Display"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
fbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_grid_attach (GTK_GRID (table), fbox, 0, row, 3, 1);
label = make_label_group(_("Display"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
gtk_box_prepend (GTK_BOX (fbox), label);
row++;
label = make_label_widget(_("Mode:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_REPORT_MODE, TRUE);
data->RA_mode = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_View by:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_SRC);
data->CY_src = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("_Balance mode"));
data->CM_balance = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Type:"));
data->LB_type = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_TYPE);
data->CY_type = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Inter_val:"));
data->LB_intvl = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_INTVL);
data->CY_intvl = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_intvl), REPORT_INTVL_MONTH);
//5.7
row++;
widget = gtk_check_button_new_with_mnemonic (_("_Forecast"));
data->CM_forecast = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Sort by _amount"));
data->CM_byamount = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Compare Exp. & Inc."));
data->CM_compare = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Zoom X:"));
data->LB_zoomx = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = make_scale(label);
data->RG_zoomx = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
/*
row++;
widget = gtk_check_button_new_with_mnemonic ("Legend");
data->CM_legend = widget;
gtk_grid_attach (GTK_GRID (table), widget, 1, 2, row, row+1);
*/
//-- filter
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
//gtk_widget_set_margin_bottom(widget, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
//5.8 test
row++;
fbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_grid_attach (GTK_GRID (table), fbox, 0, row, 3, 1);
label = make_label_group(_("Filter"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
gtk_box_prepend (GTK_BOX (fbox), label);
// active
label = make_label_widget(_("Active:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (fbox), label);
label = make_label(NULL, 0.0, 0.5);
gtk_widget_set_margin_start(label, SPACING_SMALL);
data->TX_fltactive = label;
gtk_box_prepend (GTK_BOX (fbox), label);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
data->TT_fltactive = fbox;
gtk_box_prepend (GTK_BOX (fbox), widget);
//test button
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (bbox), GTK_STYLE_CLASS_LINKED);
gtk_box_append (GTK_BOX (fbox), bbox);
widget = make_image_button2(ICONNAME_HB_FILTER, _("Edit filter"));
data->BT_filter = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button2(ICONNAME_HB_CLEAR, _("Clear filter"));
data->BT_reset = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
row++;
widget = create_popover_widget(GTK_WINDOW(data->window), data->filter);
data->PO_hubfilter = widget;
gtk_grid_attach (GTK_GRID (table), widget, 1, row, 2, 1);
row++;
//label = make_label_group(_("Date filter"));
label = make_label_group(_("Date"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_DISABLE);
gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
row++;
label = make_label_widget(_("_From:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_mindate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_maxdate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
//part: info + report
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
//toolbar
widget = repstats_toolbar_create(data);
data->TB_bar = widget;
gtk_box_prepend (GTK_BOX (vbox), widget);
//infos + total
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
hb_widget_set_margin(GTK_WIDGET(hbox), SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = make_label(NULL, 0.5, 0.5);
gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
data->TX_daterange = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
entry = gtk_label_new(NULL);
data->TX_total[2] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Total:"));
gtk_box_append (GTK_BOX (hbox), label);
entry = gtk_label_new(NULL);
data->TX_total[1] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Income:"));
gtk_box_append (GTK_BOX (hbox), label);
entry = gtk_label_new(NULL);
data->TX_total[0] = entry;
gtk_box_append (GTK_BOX (hbox), entry);
label = gtk_label_new(_("Expense:"));
gtk_box_append (GTK_BOX (hbox), label);
/* report area */
notebook = gtk_notebook_new();
data->GR_result = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
hbtk_box_prepend (GTK_BOX (vbox), notebook);
// page list/detail
vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vpaned, NULL);
// list group
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_paned_pack1 (GTK_PANED(vpaned), vbox, TRUE, TRUE);
// list total
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->SW_total = scrollwin;
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
treeview = lst_report_create();
data->LV_report = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
// list trend
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->SW_trend = scrollwin;
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
treeview = lst_rep_time_create();
data->LV_report2 = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
//5.8 moved collapse/expand here
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_listbar = tbar;
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
//detail
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->GR_detail = scrollwin;
//gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW (scrollwin), GTK_CORNER_TOP_RIGHT);
treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_det_columns);
data->LV_detail = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack2 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
list_txn_set_save_column_width(GTK_TREE_VIEW(treeview), TRUE);
//page: 2d bar /pie
widget = gtk_chart_new(CHART_TYPE_COL);
data->RE_chart = widget;
gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
gtk_chart_set_currency(GTK_CHART(widget), GLOBALS->kcur);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
//page: 2d stack/stack100
widget = gtk_chart_new(CHART_TYPE_NONE);
data->RE_chart2 = widget;
gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
gtk_chart_set_currency(GTK_CHART(widget), GLOBALS->kcur);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
// connect dialog signals
g_signal_connect (window, "delete-event", G_CALLBACK (repstats_window_dispose), (gpointer)data);
g_signal_connect (window, "map-event" , G_CALLBACK (repstats_window_mapped), NULL);
// setup, init and show window
wg = &PREFS->sta_wg;
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
// toolbar
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_widget_show_all (window);
//minor ?
hb_widget_visible(data->CM_minor, PREFS->euro_active);
gtk_widget_hide(data->SW_trend);
repstats_sensitive(window, NULL);
repstats_update_detail(window, NULL);
return window;
}
homebank-5.9.7/src/hb-account.c 0000644 0001750 0001750 00000040077 15057055117 015606 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-account.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = */
void
da_acc_free(Account *item)
{
DB( g_print("da_acc_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->name) );
g_free(item->name);
g_free(item->number);
g_free(item->bankname);
g_free(item->notes);
g_free(item->website);
g_free(item->xferexpname);
g_free(item->xferincname);
g_queue_free (item->txn_queue);
g_free(item);
}
}
Account *
da_acc_malloc(void)
{
Account *item;
DB( g_print("da_acc_malloc\n") );
item = g_malloc0(sizeof(Account));
item->kcur = GLOBALS->kcur;
item->txn_queue = g_queue_new ();
return item;
}
void
da_acc_destroy(void)
{
DB( g_print("da_acc_destroy\n") );
g_hash_table_destroy(GLOBALS->h_acc);
}
void
da_acc_new(void)
{
DB( g_print("da_acc_new\n") );
GLOBALS->h_acc = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_acc_free);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* da_acc_length:
*
* Return value: the number of elements
*/
guint
da_acc_length(void)
{
return g_hash_table_size(GLOBALS->h_acc);
}
static void da_acc_max_key_ghfunc(gpointer key, Account *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
/**
* da_acc_get_max_key:
*
* Get the biggest key from the GHashTable
*
* Return value: the biggest key value
*
*/
guint32
da_acc_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_acc, (GHFunc)da_acc_max_key_ghfunc, &max_key);
return max_key;
}
/**
* da_acc_remove:
*
* delete an account from the GHashTable
*
* Return value: TRUE if the key was found and deleted
*
*/
gboolean
da_acc_delete(guint32 key)
{
gboolean retval = FALSE;
DB( g_print("da_acc_remove %d\n", key) );
retval = g_hash_table_remove(GLOBALS->h_acc, &key);
da_acc_pos_sanitize();
return retval;
}
static void
da_acc_build_xfername(Account *item)
{
g_free(item->xferexpname);
g_free(item->xferincname);
item->xferexpname = g_strdup_printf("> %s", item->name);
item->xferincname = g_strdup_printf("< %s", item->name);
DB( g_print("- updated %d:'%s' xferexpname='%s' xferincname='%s'\n", item->key, item->name, item->xferexpname, item->xferincname) );
}
//#1889659: ensure name != null/empty
static gboolean
da_acc_ensure_name(Account *item)
{
// (no account) do not exists
if( item->key > 0 )
{
if( item->name == NULL || strlen(item->name) == 0 )
{
g_free(item->name);
item->name = g_strdup_printf("no name %d", item->key);
return TRUE;
}
}
return FALSE;
}
static void
da_acc_rename(Account *item, gchar *newname)
{
DB( g_print("- renaming '%s' => '%s'\n", item->name, newname) );
g_free(item->name);
item->name = g_strdup(newname);
//#1889659: ensure name != null/empty
da_acc_ensure_name(item);
da_acc_build_xfername(item);
}
/**
* da_acc_insert:
*
* insert an account into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_acc_insert(Account *item)
{
guint32 *new_key;
DB( g_print("da_acc_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
//#1889659: ensure name != null/empty
da_acc_ensure_name(item);
da_acc_build_xfername(item);
g_hash_table_insert(GLOBALS->h_acc, new_key, item);
return TRUE;
}
/**
* da_acc_append:
*
* insert an account into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_acc_append(Account *item)
{
Account *existitem;
DB( g_print("da_acc_append\n") );
existitem = da_acc_get_by_name( item->name );
if( existitem == NULL )
{
item->key = da_acc_get_max_key() + 1;
item->pos = da_acc_length() + 1;
da_acc_insert(item);
da_acc_pos_sanitize();
return TRUE;
}
DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
return FALSE;
}
static gboolean da_acc_name_grfunc(gpointer key, Account *item, gchar *name)
{
if( name && item->name )
{
if(!strcasecmp(name, item->name))
return TRUE;
}
return FALSE;
}
/**
* da_acc_get_by_name:
*
* Get an account structure by its name
*
* Return value: Account * or NULL if not found
*
*/
Account *
da_acc_get_by_name(gchar *rawname)
{
Account *retval = NULL;
gchar *stripname;
DB( g_print("da_acc_get_by_name\n") );
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);
g_free(stripname);
}
return retval;
}
/**
* da_acc_get:
*
* Get an account structure by key
*
* Return value: Account * or NULL if not found
*
*/
Account *
da_acc_get(guint32 key)
{
//DB( g_print("da_acc_get\n") );
return g_hash_table_lookup(GLOBALS->h_acc, &key);
}
guint32
da_acc_get_first_key(void)
{
GList *lacc, *list;
guint32 retval = 0;
lacc = list = account_glist_sorted(HB_GLIST_SORT_POS);
if( list != NULL )
{
Account *accitem = list->data;
retval = accitem->key;
}
g_list_free(lacc);
return retval;
}
//
void
da_acc_pos_sanitize(void)
{
GList *lacc, *list;
guint32 pos = 1;
lacc = list = account_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Account *accitem = list->data;
accitem->pos = pos++;
list = g_list_next(list);
}
g_list_free(lacc);
}
void da_acc_consistency(Account *item)
{
g_strstrip(item->name);
//#1889659: ensure name != null/empty
da_acc_ensure_name(item);
}
//#2026641
void da_acc_anonymize(Account *item)
{
g_free(item->name);
item->name = g_strdup_printf("account %d", item->key);
g_free(item->number);
item->number = NULL;
g_free(item->bankname);
item->bankname = NULL;
//#2026641 account notes, start balance, overdraft
g_free(item->notes);
item->notes = NULL;
item->initial = 0.0;
item->minimum = 0.0;
da_acc_build_xfername(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG
static void
da_acc_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
{
guint32 *id = key;
Account *item = value;
DB( g_print(" %d :: %s\n", *id, item->name) );
}
static void
da_acc_debug_list(void)
{
DB( g_print("\n** debug **\n") );
g_hash_table_foreach(GLOBALS->h_acc, da_acc_debug_list_ghfunc, NULL);
DB( g_print("\n** end debug **\n") );
}
#endif
static gint
account_glist_name_compare_func(Account *a, Account *b)
{
return hb_string_utf8_compare(a->name, b->name);
}
static gint
account_glist_key_compare_func(Account *a, Account *b)
{
return a->key - b->key;
}
static gint
account_glist_pos_compare_func(Account *a, Account *b)
{
return a->pos - b->pos;
}
GList *account_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_acc);
switch(column)
{
case HB_GLIST_SORT_POS:
return g_list_sort(list, (GCompareFunc)account_glist_pos_compare_func);
break;
case HB_GLIST_SORT_NAME:
return g_list_sort(list, (GCompareFunc)account_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)account_glist_key_compare_func);
break;
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void
account_transaction_sort(void)
{
GList *lst_acc, *lnk_acc;
DB( g_print("\n[account] transaction sort\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
da_transaction_queue_sort(acc->txn_queue);
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
/**
* account_is_used:
*
* controls if an account is used by any archive or transaction
*
* Return value: TRUE if used, FALSE, otherwise
*/
guint
account_is_used(guint32 key)
{
Account *acc;
GList *list;
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
guint retval;
DB( g_print("\n[account] is used\n") );
retval = ACC_USAGE_NONE;
lst_acc = NULL;
acc = da_acc_get(key);
if( g_queue_get_length(acc->txn_queue) > 0 )
{
retval = ACC_USAGE_TXN;
goto end;
}
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
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 = ACC_USAGE_TXN_XFER;
goto end;
}
lnk_txn = g_list_next(lnk_txn);
}
}
lnk_acc = g_list_next(lnk_acc);
}
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
if(key == entry->kacc)
{
retval = ACC_USAGE_ARC;
goto end;
}
if(key == entry->kxferacc)
{
retval = ACC_USAGE_ARC_XFER;
goto end;
}
list = g_list_next(list);
}
end:
g_list_free(lst_acc);
return retval;
}
gboolean
account_has_website(Account *item)
{
gboolean retval = FALSE;
if( item != NULL && item->website != NULL )
{
//TODO: reinforce controls here
if( strlen(item->website) > 4 )
retval = TRUE;
}
return retval;
}
void
account_set_dirty(Account *acc, guint32 key, gboolean isdirty)
{
if( acc == NULL )
acc = da_acc_get(key);
if( acc != NULL )
{
if( isdirty )
acc->dspflags |= FLAG_ACC_TMP_DIRTY;
else
acc->dspflags &= ~(FLAG_ACC_TMP_DIRTY);
}
}
gboolean
account_exists(gchar *name)
{
Account *existitem;
gchar *stripname;
stripname = g_strdup(name);
g_strstrip(stripname);
existitem = da_acc_get_by_name(stripname);
g_free(stripname);
return existitem == NULL ? FALSE : TRUE;
}
gboolean
account_rename(Account *item, gchar *newname)
{
Account *existitem;
gchar *stripname;
gboolean retval = FALSE;
DB( g_print("\n[account] rename\n") );
stripname = g_strdup(newname);
g_strstrip(stripname);
if( strlen(stripname) > 0 )
{
existitem = da_acc_get_by_name(stripname);
//#2083124 enable case renaming
if( existitem != NULL && existitem->key != item->key )
{
DB( g_print("- error, same name already exist with other key %d <> %d\n", existitem->key, item->key) );
}
else
{
DB( g_print("- renaming\n") );
da_acc_rename (item, stripname);
retval = TRUE;
}
}
g_free(stripname);
return retval;
}
/*
* change the account currency
* change every txn to currency
* #2026594 no more change target currency
* #1673260 internal transfer with different currency
* => no more ensure dst xfer transaction account will be set to same currency
*/
void account_set_currency(Account *acc, guint32 kcur)
{
GList *list;
/*Account *dstacc;
gboolean *xfer_list;
guint32 maxkey, i;*/
DB( g_print("\n[account] set currency\n") );
if(acc->kcur == kcur)
{
DB( g_print(" - already ok, return\n") );
return;
}
DB( g_print(" - set for '%s'\n", acc->name) );
//#1673260 internal transfer with different currency
/*maxkey = da_acc_get_max_key () + 1;
xfer_list = g_malloc0(sizeof(gboolean) * maxkey );
DB( g_print(" - alloc for %d account\n", da_acc_length() ) );*/
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *txn = list->data;
txn->kcur = kcur;
/*if( (txn->flags & OF_INTXFER) && (txn->kxferacc > 0) && (txn->kxfer > 0) )
{
xfer_list[txn->kxferacc] = TRUE;
}*/
list = g_list_next(list);
}
acc->kcur = kcur;
DB( g_print(" - '%s'\n", acc->name) );
//#1673260 internal transfer with different currency
/*for(i=1;ibal_future -= txn->amount;
if(txn->date <= GLOBALS->today)
acc->bal_today -= txn->amount;
if(txn->status == TXN_STATUS_CLEARED)
acc->bal_clear -= txn->amount;
if(txn->status == TXN_STATUS_RECONCILED)
{
acc->bal_recon -= txn->amount;
acc->bal_clear -= txn->amount;
}
}
/**
* private function to add transaction amount from account balances
*/
static void account_balances_add_internal(Account *acc, Transaction *txn)
{
acc->bal_future += txn->amount;
if(txn->date <= GLOBALS->today)
acc->bal_today += txn->amount;
if(txn->status == TXN_STATUS_CLEARED)
acc->bal_clear += txn->amount;
if(txn->status == TXN_STATUS_RECONCILED)
{
acc->bal_recon += txn->amount;
acc->bal_clear += txn->amount;
}
}
/**
* public function to sub transaction amount from account balances
*/
gboolean account_balances_sub(Transaction *txn)
{
if( transaction_is_balanceable(txn) )
//if(!(txn->flags & OF_REMIND))
{
Account *acc = da_acc_get(txn->kacc);
if(acc == NULL) return FALSE;
account_balances_sub_internal(acc, txn);
return TRUE;
}
return FALSE;
}
/**
* public function to add transaction amount from account balances
*/
gboolean account_balances_add(Transaction *txn)
{
if( transaction_is_balanceable(txn) )
//if(!(txn->flags & OF_REMIND))
{
Account *acc = da_acc_get(txn->kacc);
if(acc == NULL) return FALSE;
account_balances_add_internal(acc, txn);
return TRUE;
}
return FALSE;
}
void account_flags_eval(Account *item)
{
g_return_if_fail(item != NULL);
//#2109854 fix residual flag
item->flags &= ~(AF_HASNOTICE);
if( item->nb_pending > 0 )
{
item->flags |= AF_HASNOTICE;
}
}
//todo: optim called 2 times from dsp_mainwindow
void account_compute_balances(gboolean init)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
DB( g_print("\naccount_compute_balances start\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
acc->nb_pending = 0;
/* set initial amount */
acc->bal_clear = acc->initial;
acc->bal_recon = 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( transaction_is_balanceable(txn) )
{
account_balances_add_internal(acc, txn);
}
//5.9 moved completion memo here
if( (init == TRUE) && (PREFS->txn_memoacp == TRUE) )
{
da_transaction_insert_memos(txn);
}
//5.9 add flags
if( txn->flags & (OF_ISPAST | OF_ISIMPORT) )
{
acc->nb_pending++;
}
lnk_txn = g_list_next(lnk_txn);
}
account_flags_eval(acc);
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
DB( g_print("\naccount_compute_balances end\n") );
}
void account_convert_euro(Account *acc)
{
GList *lnk_txn;
//5.9: ignore already EUR account
if( currency_is_euro(acc->kcur) == TRUE )
return;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
gdouble oldamount = txn->amount;
txn->amount = hb_amount_to_euro(oldamount);
DB( g_print("%10.6f => %10.6f, %s\n", oldamount, txn->amount, txn->memo) );
//todo: sync child xfer
lnk_txn = g_list_next(lnk_txn);
}
acc->initial = hb_amount_to_euro(acc->initial);
// acc->warning = hb_amount_to_euro(acc->warning);
acc->minimum = hb_amount_to_euro(acc->minimum);
acc->maximum = hb_amount_to_euro(acc->maximum);
}
homebank-5.9.7/src/ui-filter.h 0000644 0001750 0001750 00000004621 15054251707 015463 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_FILTER_GTK_H__
#define __HB_FILTER_GTK_H__
#define FLT_PAGE_NAME_DAT "dat"
#define FLT_PAGE_NAME_TYP "typ"
#define FLT_PAGE_NAME_STA "sta"
#define FLT_PAGE_NAME_ACC "acc"
#define FLT_PAGE_NAME_CAT "cat"
#define FLT_PAGE_NAME_PAY "pay"
#define FLT_PAGE_NAME_TAG "tag"
#define FLT_PAGE_NAME_PMT "pmt"
#define FLT_PAGE_NAME_TXT "txt"
/* official GTK_RESPONSE are negative */
#define HB_RESPONSE_FLT_SAVE_USE 33
#define HB_RESPONSE_FLT_RESET 55
enum
{
LST_DEFFLT_TOGGLE,
LST_DEFFLT_DATAS,
NUM_LST_DEFFLT
};
struct ui_flt_list_data
{
GtkWidget *gr_criteria;
GtkWidget *tb_bar, *bt_all, *bt_non, *bt_inv;
};
struct ui_flt_manage_data
{
Filter *filter;
gboolean saveable;
gboolean show_account;
gboolean txnmode;
GtkWidget *dialog;
GtkWidget *stack;
GtkWidget *SW_enabled[FLT_GRP_MAX];
GtkWidget *RA_matchmode[FLT_GRP_MAX];
GtkWidget *GR_page[FLT_GRP_MAX];
GtkWidget *CY_range;
GtkWidget *LB_mindate, *LB_maxdate;
GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *CY_month, *NB_year;
GtkWidget *CM_typnexp, *CM_typninc, *CM_typxexp, *CM_typxinc;
GtkWidget *CM_stanon, *CM_staclr, *CM_starec;
GtkWidget *GR_force;
GtkWidget *CM_forceadd, *CM_forcechg, *CM_forceremind, *CM_forcevoid;
GtkWidget *CM_paymode[NUM_PAYMODE_MAX];
GtkWidget *ST_minamount, *ST_maxamount;
GtkWidget *CM_exact;
GtkWidget *ST_number, *ST_memo;
GtkWidget *LV_acc;
GtkWidget *LV_pay;
GtkWidget *LV_tag;
GtkWidget *LV_cat;
GtkWidget *BT_expand, *BT_collapse;
};
/* = = = = = = = = = = */
gint ui_flt_manage_dialog_new(GtkWindow *parentwindow, Filter *filter, gboolean show_account, gboolean txnmode);
#endif
homebank-5.9.7/src/hub-scheduled.c 0000664 0001750 0001750 00000053365 15120033322 016266 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-mainwindow.h"
#include "list-scheduled.h"
#include "hub-scheduled.h"
#include "ui-transaction.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern HbKvData CYA_FLT_SCHEDULED[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* scheduled */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_hub_scheduled_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct hbfile_data *data;
GtkTreeModel *model;
GList *selection, *list;
gint count;
DB( g_print ("\n[hub-scheduled] row double-clicked\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)));
if( count == 1 )
{
//model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), &model);
list = g_list_first(selection);
if(list != NULL)
{
GtkTreeIter iter;
Archive *arc;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arc, -1);
//fix: don't open for total line
if( arc != NULL )
ui_wallet_defarchive(arc);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
}
}
static void ui_hub_scheduled_do_post(Archive *arc, gboolean doedit, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkWidget *window;
gint result;
Transaction *txn;
DB( g_print("\n[hub-scheduled] do post\n") );
window = create_deftransaction_window(GTK_WINDOW(data->window), TXN_DLG_ACTION_ADD, TXN_DLG_TYPE_SCH, 0);
/* fill in the transaction */
txn = da_transaction_malloc();
da_transaction_init_from_template(txn, arc);
txn->date = scheduled_get_txn_real_postdate(arc->nextdate, arc->weekend);
deftransaction_set_transaction(window, txn);
result = gtk_dialog_run (GTK_DIALOG (window));
DB( g_print(" - dialog result is %d\n", result) );
if(result == HB_RESPONSE_ADD || result == GTK_RESPONSE_ACCEPT)
{
deftransaction_get(window, NULL);
transaction_add(GTK_WINDOW(GLOBALS->mainwindow), FALSE, txn);
GLOBALS->changes_count++;
scheduled_date_advance(arc);
DB( g_print(" - added 1 transaction to %d\n", txn->kacc) );
}
da_transaction_free(txn);
deftransaction_dispose(window, NULL);
gtk_window_destroy (GTK_WINDOW(window));
}
static void ui_hub_scheduled_editpost_cb(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
DB( g_print("\n[hub-scheduled] editpost\n") );
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), &model);
list = g_list_first(selection);
while(list != NULL)
{
GtkTreeIter iter;
Archive *arc;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arc, -1);
if( (arc != NULL) )
{
ui_hub_scheduled_do_post(arc, TRUE, data);
}
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
}
static void ui_hub_scheduled_post_cb(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
DB( g_print("\n[hub-scheduled] post\n") );
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), &model);
list = g_list_first(selection);
while(list != NULL)
{
GtkTreeIter iter;
Archive *arc;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arc, -1);
if( (arc != NULL) )
{
if( scheduled_is_postable(arc) )
{
Transaction *txn = da_transaction_malloc ();
da_transaction_init_from_template(txn, arc);
txn->date = scheduled_get_txn_real_postdate(arc->nextdate, arc->weekend);
transaction_add(GTK_WINDOW(GLOBALS->mainwindow), FALSE, txn);
GLOBALS->changes_count++;
scheduled_date_advance(arc);
da_transaction_free (txn);
}
else
{
ui_hub_scheduled_do_post(arc, FALSE, data);
}
}
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
}
static void ui_hub_scheduled_skip_cb(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
DB( g_print("\n[hub-scheduled] skip\n") );
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), &model);
list = g_list_first(selection);
while(list != NULL)
{
GtkTreeIter iter;
Archive *arc;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arc, -1);
DB( g_print(" %s %f\n", arc->memo, arc->amount) );
if( (arc != NULL) && (arc->rec_flags & TF_RECUR) )
{
GLOBALS->changes_count++;
scheduled_date_advance(arc);
DB( g_print(" >skipping\n") );
//ui_hub_scheduled_populate(GLOBALS->mainwindow, NULL);
}
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
ui_hub_scheduled_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
}
static void ui_hub_scheduled_update(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeSelection *selection;
gchar *msg;
gint count;
//gint filter;
DB( g_print("\n[hub-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));
// sensitive against selection
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc));
count = gtk_tree_selection_count_selected_rows(selection);
//Archive *arc = ui_hub_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
if(count >= 1)
{
//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);
}
//#1996505 add sum of selected
if( count > 1 )
{
GList *list, *tmplist;
GtkTreeModel *model;
GtkTreeIter iter;
gchar buf1[64];
gchar buf2[64];
gchar buf3[64];
gdouble sumexp, suminc;
DB( g_print(" update list info\n") );
sumexp = suminc = 0.0;
//model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc));
list = gtk_tree_selection_get_selected_rows(selection, &model);
tmplist = g_list_first(list);
while (tmplist != NULL)
{
Archive *arc;
Account *acc = NULL;
gtk_tree_model_get_iter(model, &iter, tmplist->data);
gtk_tree_model_get(model, &iter,
LST_DSPUPC_DATAS, &arc,
-1);
//DB( g_print(" collect %f - %f = %f %s\n", txninc, txnexp, txninc + txnexp, item->memo) );
acc = da_acc_get(arc->kacc);
if( acc != NULL )
{
if( arc->flags & OF_INCOME )
suminc += hb_amount_base(arc->amount, acc->kcur);
else
sumexp += hb_amount_base(arc->amount, acc->kcur);
}
/* insert internal xfer txn : 1378836 */
if( (arc->flags & OF_INTXFER) )
{
gdouble amount = -arc->amount;
if( arc->flags & OF_ADVXFER )
{
amount = arc->xferamount;
DB( g_print(" xfer amount is != curr %.17g\n", amount ) );
}
/* opposite here */
acc = da_acc_get(arc->kxferacc);
if( acc != NULL )
{
if( arc->flags & OF_INCOME )
sumexp += hb_amount_base(amount, acc->kcur);
else
suminc += hb_amount_base(amount, acc->kcur);
}
}
DB( g_print(" %.17g - %.17g = %.17g temp\n", suminc, sumexp, suminc + sumexp) );
tmplist = g_list_next(tmplist);
}
g_list_free(list);
DB( g_print(" %.17g - %.17g = %.17g final\n", suminc, sumexp, suminc + sumexp) );
hb_strfmon(buf1, 64-1, suminc + sumexp, GLOBALS->kcur, GLOBALS->minor);
hb_strfmon(buf2, 64-1, sumexp, GLOBALS->kcur, GLOBALS->minor);
hb_strfmon(buf3, 64-1, suminc, GLOBALS->kcur, GLOBALS->minor);
//TRANSLATORS: example 'sum: 3 (-1 + 4)'
msg = g_strdup_printf(_("sum: %s (%s + %s)"), buf1, buf2, buf3);
gtk_label_set_markup(GTK_LABEL(data->TX_selection), msg);
g_free (msg);
}
else
gtk_label_set_markup(GTK_LABEL(data->TX_selection), "");
}
static void ui_hub_scheduled_selection_cb(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_print("\n[hub-scheduled] selection\n") );
ui_hub_scheduled_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
}
/*
** called after load, importamiga, on demand
*/
void ui_hub_scheduled_postall(GtkWidget *widget, gpointer user_data)
{
//struct hbfile_data *data;
gint count;
gint usermode = GPOINTER_TO_INT(user_data);
DB( g_print("\n[hub-scheduled] post all\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_wallet_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);
}
}
void ui_hub_scheduled_populate(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
GList *list;
gdouble totexp = 0;
gdouble totinc = 0;
gint count = 0;
gchar buffer[256], *tooltip;
guint32 maxpostdate = 0;
guint32 fltmindate, fltmaxdate;
GDate *date;
DB( g_print("\n[hub-scheduled] populate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc));
g_signal_handlers_block_by_func (G_OBJECT (selection), G_CALLBACK (ui_hub_scheduled_selection_cb), NULL);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
gtk_list_store_clear (GTK_LIST_STORE(model));
//5.7.4 add
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_upc), NULL); /* Detach model from view */
ui_arc_listview_widget_columns_order_load(GTK_TREE_VIEW(data->LV_upc));
homebank_app_date_get_julian();
PREFS->pnl_upc_range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_sched_range));
//set tooltip text
maxpostdate = scheduled_date_get_post_max(GLOBALS->today, GLOBALS->auto_smode, GLOBALS->auto_nbdays, GLOBALS->auto_weekday, GLOBALS->auto_nbmonths);
date = g_date_new_julian (maxpostdate);
g_date_strftime (buffer, 256-1, PREFS->date_format, date);
//post when program start: ON/OFF
tooltip = g_strdup_printf("%s: %s\n%s: %s",
_("Post when program start"), PREFS->appendscheduled ? _("On") : _("Off"),
_("maximum post date"), buffer);
//gtk_label_set_text(GTK_LABEL(data->LB_maxpostdate), buffer);
gtk_widget_set_tooltip_text(data->IM_info, tooltip);
g_free(tooltip);
fltmindate = HB_MINDATE;
fltmaxdate = HB_MAXDATE;
scheduled_date_get_show_minmax(PREFS->pnl_upc_range, &fltmindate, &fltmaxdate);
//#1909851 5.7 override if FLT_SCHEDULED_MAXPOSTDATE
if( PREFS->pnl_upc_range == FLT_SCHEDULED_MAXPOSTDATE )
fltmaxdate = maxpostdate;
DB( hb_print_date(GLOBALS->today, "today" ) );
DB( hb_print_date(maxpostdate, "maxpostdate" ) );
DB( hb_print_date(fltmindate, "fltmindate" ) );
DB( hb_print_date(fltmaxdate, "fltmaxdate" ) );
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *arc = list->data;
Account *acc;
gdouble inc, exp;
gint nbdays, nblate;
if( (arc->rec_flags & TF_RECUR) ) //&& arc->kacc > 0)
{
count++;
nbdays = arc->nextdate - maxpostdate;
//nblate = scheduled_get_latepost_count(date, arc, GLOBALS->today);
nblate = scheduled_get_latepost_count(date, arc, maxpostdate);
DB( g_print(" eval %d in [%d-%d] ? days %d late %d, memo='%s'\n", arc->nextdate, fltmindate, fltmaxdate, nbdays, nblate, arc->memo) );
if( nblate == 0 )
{
//#1857636 hide due date scheduled > custom user config if > 0
if( (arc->nextdate < fltmindate) || (arc->nextdate > fltmaxdate) )
{
DB( g_print(" skip '%s' : next %d >= maxshow %d\n", arc->memo, arc->nextdate, PREFS->pnl_upc_range) );
//TODO: count hidden
goto next;
}
}
exp = inc = 0.0;
if( arc->flags & OF_INCOME )
inc = arc->amount;
else
exp = arc->amount;
acc = da_acc_get(arc->kacc);
if( acc != NULL )
{
DB( g_print(" add totals: %.17g %.17g\n", exp, inc) );
if( arc->flags & OF_INCOME )
totinc += hb_amount_base(arc->amount, acc->kcur);
else
totexp += hb_amount_base(arc->amount, acc->kcur);
}
/* good */
/* insert internal xfer txn : 1378836 */
if( (arc->flags & OF_INTXFER) )
{
gdouble amount = -arc->amount;
if( arc->flags & OF_ADVXFER )
{
amount = arc->xferamount;
DB( g_print(" xfer amount is != curr %.17g\n", amount ) );
}
/* opposite here */
if( arc->flags & OF_INCOME )
exp = amount;
else
inc = amount;
acc = da_acc_get(arc->kxferacc);
if( acc != NULL )
{
DB( g_print(" add totals: %.17g %.17g\n", exp, inc) );
if( arc->flags & OF_INCOME )
totexp += hb_amount_base(amount, acc->kcur);
else
totinc += hb_amount_base(amount, 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_NEXT, nbdays,
//LST_DSPUPC_ACCOUNT, acc,
LST_DSPUPC_MEMO, arc->memo,
LST_DSPUPC_EXPENSE, exp,
LST_DSPUPC_INCOME , inc,
LST_DSPUPC_NB_LATE, nblate,
-1);
DB( g_print(" totals: %.17g %.17g\n", totexp, totinc) );
}
next:
list = g_list_next(list);
}
g_date_free(date);
// insert total
if(count > 0 )
{
DB( g_print(" insert totals: %.17g %.17g\n", totexp, totinc) );
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DSPUPC_DATAS, NULL,
LST_DSPUPC_MEMO, _("Total"),
LST_DSPUPC_EXPENSE, totexp,
LST_DSPUPC_INCOME, totinc,
-1);
}
//added 5.7.4
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_upc), model); /* Re-attach model to view */
g_object_unref(model);
g_signal_handlers_unblock_by_func (G_OBJECT (selection), G_CALLBACK (ui_hub_scheduled_selection_cb), NULL);
ui_hub_scheduled_update(widget, NULL);
}
static void
ui_hub_scheduled_clipboard (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkClipboard *clipboard;
GString *node;
//g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
node = lst_sch_widget_to_string(GTK_TREE_VIEW(data->LV_upc), HB_STRING_CLIPBOARD);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void
ui_hub_scheduled_print (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GString *node;
//g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
gint8 leftcols[8] = { 0, 1, 2, 3, 6, 7, -1 };
node = lst_sch_widget_to_string(GTK_TREE_VIEW(data->LV_upc), HB_STRING_PRINT);
hb_print_listview(GTK_WINDOW(data->window), node->str, leftcols, _("Scheduled"), NULL, FALSE);
g_string_free(node, TRUE);
}
static const GActionEntry actions[] = {
// name, function(), type, state,
{ "clipboard" , ui_hub_scheduled_clipboard , NULL, NULL , NULL, {0,0,0} },
{ "print" , ui_hub_scheduled_print , NULL, NULL , NULL, {0,0,0} },
// { "paste", activate_action, NULL, NULL, NULL, {0,0,0} },
};
GtkWidget *ui_hub_scheduled_create(struct hbfile_data *data)
{
GtkWidget *hub, *vbox, *bbox, *scrollwin, *treeview, *tbar;
GtkWidget *label, *widget;
DB( g_print("\n[hub-scheduled] create\n") );
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margin(GTK_WIDGET(hub), SPACING_SMALL);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (hub), vbox);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
treeview = (GtkWidget *)lst_sch_widget_new(LIST_SCH_TYPE_DISPLAY);
data->LV_upc = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(bbox), TRUE);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = gtk_button_new_with_mnemonic (_("_Skip"));
data->BT_sched_skip = widget;
hbtk_box_prepend (GTK_BOX (bbox), widget);
widget = gtk_button_new_with_mnemonic(_("Edit & P_ost"));
data->BT_sched_editpost = widget;
hbtk_box_prepend (GTK_BOX (bbox), widget);
//TRANSLATORS: Posting a scheduled transaction is the action to materialize it into its target account.
//TRANSLATORS: Before that action the automated transaction occurrence is pending and not yet really existing.
widget = gtk_button_new_with_mnemonic (_("_Post"));
data->BT_sched_post = widget;
hbtk_box_prepend (GTK_BOX (bbox), widget);
//info icon
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
data->IM_info = widget;
gtk_box_prepend (GTK_BOX (tbar), widget);
//#1996505 add sum of selected
label = make_label(NULL, 0.5, 0.5);
gtk_widget_set_margin_end(GTK_WIDGET(label), SPACING_MEDIUM);
data->TX_selection = label;
hbtk_box_prepend (GTK_BOX (tbar), label);
//#1857636 add setting to input max due date to show
widget = hbtk_combo_box_new_with_data (NULL, CYA_FLT_SCHEDULED);
data->CY_sched_range = widget;
gtk_box_prepend (GTK_BOX (tbar), widget);
//gmenu test (see test folder into gtk)
GMenu *menu, *section;
GtkWidget *image;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Copy to clipboard"), "actions.clipboard");
g_menu_append (section, _("Print..."), "actions.print");
g_object_unref (section);
GSimpleActionGroup *group = g_simple_action_group_new ();
//2130483 stupid overwrite here
//data->action_group_acc = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(bbox), TRUE);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = gtk_menu_button_new();
gtk_box_prepend (GTK_BOX (bbox), widget);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
//setup
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_sched_range), PREFS->pnl_upc_range);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), "changed", G_CALLBACK (ui_hub_scheduled_selection_cb), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_upc), "row-activated", G_CALLBACK (ui_hub_scheduled_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_sched_skip), "clicked", G_CALLBACK (ui_hub_scheduled_skip_cb), data);
g_signal_connect (G_OBJECT (data->BT_sched_editpost), "clicked", G_CALLBACK (ui_hub_scheduled_editpost_cb), data);
g_signal_connect (G_OBJECT (data->BT_sched_post), "clicked", G_CALLBACK (ui_hub_scheduled_post_cb), data);
g_signal_connect (data->CY_sched_range, "changed", G_CALLBACK (ui_hub_scheduled_populate), NULL);
return hub;
}
homebank-5.9.7/src/ui-hbfile.c 0000644 0001750 0001750 00000031035 15005624200 015405 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#include "ui-hbfile.h"
#include "ui-category.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
gchar *CYA_TXN_POSTMODE[] = {
N_("Due Date"),
N_("Next Payout"),
N_("In Advance"),
NULL
};
/* = = = = = = = = = = = = = = = = */
static void defhbfile_cb_update_maxpostdate(GtkWidget *widget, gpointer user_data)
{
struct defhbfile_data *data;
gint smode, weekday, nbdays, nbmonths;
guint32 maxpostdate;
gchar buffer[256], *newtext;
GDate *date;
DB( g_print("\n[ui-hbfile] update maxpostdate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
smode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_postmode));
nbdays = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_nbdays));
weekday = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_weekday));
nbmonths = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_nbmonths));
DB( g_print(" -> postmode=%d\n", smode) );
hb_widget_visible(data->GR_payout, smode == ARC_POSTMODE_PAYOUT ? TRUE : FALSE);
hb_widget_visible(data->GR_advance, smode == ARC_POSTMODE_ADVANCE ? TRUE : FALSE);
//fill in the max date evaluation
maxpostdate = scheduled_date_get_post_max(GLOBALS->today, smode, nbdays, weekday, nbmonths);
date = g_date_new_julian (maxpostdate);
g_date_strftime (buffer, 256-1, PREFS->date_format, date);
//#2102726
newtext = g_strdup_printf(_("Maximum post date is %s (included)"), buffer);
gtk_label_set_text(GTK_LABEL(data->LB_maxpostdate), newtext);
g_date_free(date);
g_free(newtext);
}
/*
** get widgets contents from the selected account
*/
static void defhbfile_get(GtkWidget *widget, gpointer user_data)
{
struct defhbfile_data *data;
gchar *owner;
guint32 vehicle;
gint smode, weekday, nbdays, nbmonths;
gdouble earnbyh;
DB( g_print("\n[ui-hbfile] get\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
// get values
owner = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_owner));
//vehicle = ui_cat_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_grp));
vehicle = ui_cat_entry_popover_get_key(GTK_BOX(data->PO_grp));
smode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_postmode));
weekday = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_weekday));
nbdays = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_nbdays));
nbmonths = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NU_nbmonths));
earnbyh = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_earnbyh));
// check for changes
if(strcasecmp(owner, GLOBALS->owner))
data->change++;
if(smode != GLOBALS->auto_smode)
data->change++;
if(weekday != GLOBALS->auto_weekday)
data->change++;
if(nbmonths != GLOBALS->auto_nbmonths)
data->change++;
if(nbdays != GLOBALS->auto_nbdays)
data->change++;
if(earnbyh != GLOBALS->lifen_earnbyh)
data->change++;
if(vehicle != GLOBALS->vehicle_category)
data->change++;
// update
if (owner && *owner)
hbfile_change_owner(g_strdup(owner));
GLOBALS->vehicle_category = vehicle;
GLOBALS->auto_smode = smode;
GLOBALS->auto_weekday = weekday;
GLOBALS->auto_nbmonths = nbmonths;
GLOBALS->auto_nbdays = nbdays;
GLOBALS->lifen_earnbyh = earnbyh;
DB( g_print(" -> owner %s\n", GLOBALS->owner) );
DB( g_print(" -> ccgrp %d\n", GLOBALS->vehicle_category) );
DB( g_print(" -> smode %d\n", GLOBALS->auto_smode) );
DB( g_print(" -> weekday %d\n", GLOBALS->auto_weekday) );
DB( g_print(" -> nbmonths %d\n", GLOBALS->auto_nbmonths) );
DB( g_print(" -> nbdays %d\n", GLOBALS->auto_nbdays) );
}
/*
** set widgets contents from the selected account
*/
static void defhbfile_set(GtkWidget *widget, gpointer user_data)
{
struct defhbfile_data *data;
DB( g_print("\n[ui-hbfile] set\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" -> ccgrp %d\n", GLOBALS->vehicle_category) );
DB( g_print(" -> autoinsert %d\n", GLOBALS->auto_nbdays) );
if(GLOBALS->owner) gtk_entry_set_text(GTK_ENTRY(data->ST_owner), GLOBALS->owner);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_grp), GLOBALS->vehicle_category);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_grp), GLOBALS->vehicle_category);
hbtk_switcher_set_active (HBTK_SWITCHER(data->RA_postmode), GLOBALS->auto_smode);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NU_nbdays), GLOBALS->auto_nbdays);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NU_weekday), GLOBALS->auto_weekday);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NU_nbmonths), GLOBALS->auto_nbmonths);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_earnbyh), GLOBALS->lifen_earnbyh);
defhbfile_cb_update_maxpostdate(widget, user_data);
}
static void defhbfile_cb_postmode_toggled(GtkWidget *radiobutton, gpointer user_data)
{
//struct defhbfile_data *data;
//gint postmode;
DB( g_print("\n[ui-hbfile] toggle postmode\n") );
defhbfile_cb_update_maxpostdate(radiobutton, NULL);
}
static gboolean defhbfile_cleanup(struct defhbfile_data *data, gint result)
{
gboolean doupdate = FALSE;
DB( g_print("\n[ui-hbfile] cleanup\n") );
if(result == GTK_RESPONSE_ACCEPT)
{
defhbfile_get(data->ST_owner, NULL);
DB( g_print(" -> GLOBAL change = %d\n", GLOBALS->changes_count) );
DB( g_print(" -> we update, change = %d\n", data->change) );
GLOBALS->changes_count += data->change;
}
return doupdate;
}
static void defhbfile_setup(struct defhbfile_data *data)
{
DB( g_print("\n[ui-hbfile] setup\n") );
data->change = 0;
//5.5 done in popover
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_grp), GLOBALS->h_cat);
defhbfile_set(data->ST_owner, NULL);
}
GtkWidget *create_defhbfile_dialog (void)
{
struct defhbfile_data *data;
GtkWidget *dialog, *content_area, *hbox, *content_grid, *group_grid;
GtkWidget *label, *widget;
gint crow, row;
DB( g_print("\n[ui-hbfile] new\n") );
data = g_malloc0(sizeof(struct defhbfile_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("File properties"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" - window=%p, inst_data=%p\n", dialog, data) );
gtk_window_set_resizable(GTK_WINDOW (dialog), FALSE);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: General
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(_("General"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_widget(_("_Title:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_owner = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Scheduled transactions
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(_("Scheduled transactions"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
//message of post date
label = make_label(NULL, 0.0, 0.5);
data->LB_maxpostdate = label;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(label)), GTK_STYLE_CLASS_DIM_LABEL);
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("Mode:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_TXN_POSTMODE, TRUE);
data->RA_postmode = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//next payout group
row++;
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_payout = hbox;
gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
widget = make_numeric(NULL, 1, 28);
data->NU_weekday = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label(_("of each"), 0.0, 0.5);
gtk_box_prepend (GTK_BOX (hbox), label);
widget = make_numeric(NULL, 1, 12);
data->NU_nbmonths = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label(_("month"), 0.0, 0.5);
gtk_box_prepend (GTK_BOX (hbox), label);
//in advance group
row++;
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_advance = hbox;
gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
widget = make_numeric(NULL, 1, 366);
data->NU_nbdays = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
//TRANSLATORS: there is a spinner on the left of this label, and so you have 0....x days in advance the current date
label = make_label(_("days"), 0.0, 0.5);
gtk_box_prepend (GTK_BOX (hbox), label);
// group :: life energy
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(_("Life Energy"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
label = make_label_widget(_("_Earn by hour:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_amount_pos(label);
data->ST_earnbyh = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Vehicle cost
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(_("Vehicle cost"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_widget(_("_Category:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
data->PO_grp = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
gtk_widget_show_all (dialog);
//setup, init and show window
defhbfile_setup(data);
//defhbfile_update(data->LV_arc, NULL);
g_signal_connect (data->RA_postmode, "changed", G_CALLBACK (defhbfile_cb_postmode_toggled), &dialog);
g_signal_connect (data->NU_nbdays, "value-changed", G_CALLBACK (defhbfile_cb_update_maxpostdate), NULL);
g_signal_connect (data->NU_weekday, "value-changed", G_CALLBACK (defhbfile_cb_update_maxpostdate), NULL);
g_signal_connect (data->NU_nbmonths, "value-changed", G_CALLBACK (defhbfile_cb_update_maxpostdate), NULL);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
defhbfile_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return dialog;
}
homebank-5.9.7/src/rep-balance.h 0000644 0001750 0001750 00000004330 14736461415 015736 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_REPBALANCE_H__
#define __HOMEBANK_REPBALANCE_H__
/* list stat */
enum
{
LST_OVER_POS,
LST_OVER_KEY,
LST_OVER_LABEL,
LST_OVER_EXPENSE,
LST_OVER_INCOME,
LST_OVER_TOTAL,
LST_OVER_FLAGS,
NUM_LST_OVER
};
/* -- flags -- */
#define REPORT_FLAG_OVER (1<<1)
#define REPORT_FLAG_TODAY (1<<2)
enum {
HID_REPBALANCE_MINDATE,
HID_REPBALANCE_MAXDATE,
HID_REPBALANCE_RANGE,
MAX_REPBALANCE_HID
};
struct repbalance_data
{
GQueue *txn_queue;
Filter *filter;
gdouble minimum;
gboolean detail;
guint32 accnum;
gdouble *tmp_income;
gdouble *tmp_expense;
gdouble firstbalance;
guint n_result;
guint nbovrdraft, nbope;
guint32 usrkcur;
GtkWidget *window;
GActionGroup *actions;
gboolean mapped_done;
GtkWidget *TB_bar;
GtkWidget *BT_list;
GtkWidget *BT_line;
GtkWidget *BT_detail;
GtkWidget *BT_refresh;
GtkWidget *BT_reset;
GtkWidget *BT_print;
GtkWidget *TX_info;
GtkWidget *TX_fltactive, *TT_fltactive;
GtkWidget *TX_daterange;
GtkWidget *CM_minor;
GtkWidget *LV_report;
GtkWidget *BT_all, *BT_non, *BT_inv;
GtkWidget *SW_acc, *LV_acc;
GtkWidget *CY_intvl;
GtkWidget *CM_showempty;
GtkWidget *RG_zoomx, *LB_zoomx;
GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *CM_inclxfer;
GtkWidget *CY_range;
GtkWidget *GR_result;
GtkWidget *RE_chart;
GtkWidget *GR_detail;
GtkWidget *LV_detail;
gulong hid[MAX_REPBALANCE_HID];
};
GtkWidget *repbalance_window_new(guint32 accnum);
#endif
homebank-5.9.7/src/gtk-chart-colors.h 0000664 0001750 0001750 00000006755 14736461415 016767 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __GTK_COLORS_H__
#define __GTK_COLORS_H__
#include
#define MASKCOL 255
//#define MASKCOL 000
#define COLTO16(col8) ( (col8 | col8<<8 ) )
#define COLTOOVER(col8) ( (col8 + MASKCOL) / 2 )
#define AREA_ALPHA .33
#define OVER_ALPHA .15
#define OVER_COLOR (MASKCOL * OVER_ALPHA)
#define COLTOCAIRO(col8) ( (col8 / 255.0) )
#define COLTOCAIROOVER(col8) ( ((col8 * (1 - OVER_ALPHA)) + OVER_COLOR ) / 255.0 )
//typedef struct _rgbcol RgbCol;
typedef struct _ColorScheme GtkColorScheme;
struct rgbcol
{
guint8 r, g, b;
};
#define SHADE_FACTOR 0.15
#define LIGHT_FACTOR 0.15
#define HOVER_FACTOR 0.15
struct _ColorScheme
{
struct rgbcol *colors;
gint nb_cols;
gint cs_red;
gint cs_green;
gint cs_blue;
gint cs_orange;
};
#define HBTK_CHART_FLAGS_NONE 0
#define HBTK_CHART_FLAGS_HOVER 1 << 1
#define HBTK_CHART_FLAGS_SHADED 1 << 2
//TODO: get rid of unused here
enum {
BLACK,
WHITE,
GREY1,
TEXT,
XYLINES,
THBASE,
THTEXT
};
enum colmap
{
CHART_COLMAP_HOMEBANK,
CHART_COLMAP_MSMONEY,
CHART_COLMAP_SAP,
CHART_COLMAP_QUICKEN,
CHART_COLMAP_OFFICE2010,
CHART_COLMAP_OFFICE2013,
CHART_COLMAP_ANALYTICS,
CHART_COLMAP_YNAB,
CHART_COLMAP_QUICKEN2017,
CHART_COLMAP_MINT,
CHART_COLMAP_MATERIAL,
CHART_COLMAP_NORD,
};
enum {
CHART_FONT_SIZE_TITLE,
CHART_FONT_SIZE_SUBTITLE,
CHART_FONT_SIZE_NORMAL,
CHART_FONT_SIZE_SMALL
};
extern char *chart_colors[];
extern struct rgbcol global_colors[];
extern struct rgbcol money_colors[];
extern struct rgbcol quicken_colors[];
extern struct rgbcol analytics_colors[];
extern struct rgbcol office2010_colors[];
extern struct rgbcol office2013_colors[];
extern struct rgbcol sap_colors[];
extern struct rgbcol homebank_colors[];
extern struct rgbcol ynab_colors[];
extern struct rgbcol quicken2017_colors[];
extern struct rgbcol mint_colors[];
extern struct rgbcol material_colors[];
extern struct rgbcol nord_colors[];
extern int money_nbcolors;
extern int quicken_nbcolors;
extern int analytics_nbcolors;
extern int office2010_nbcolors;
extern int office2013_nbcolors;
extern int sap_nbcolors;
extern int homebank_nbcolors;
extern int ynab_nbcolors;
extern int quicken2017_nbcolors;
extern int mint_nbcolors;
extern int material_nbcolors;
extern int nord_nbcolors;
void chart_color_global_default(void);
void cairo_user_set_rgbcol(cairo_t *cr, struct rgbcol *col);
void cairo_user_set_rgbacol(cairo_t *cr, struct rgbcol *col, double alpha);
void cairo_user_set_rgbacol_over(cairo_t *cr, struct rgbcol *col, gboolean over, double alpha);
void cairo_user_set_rgbcol_over(cairo_t *cr, struct rgbcol *col, gboolean over);
void colorsheme_col8_to_rgba(struct rgbcol *col8, GdkRGBA *rgba);
void colorscheme_init(GtkColorScheme *scheme, gint index);
#endif /* __GTK_COLORS_H__ */
homebank-5.9.7/src/ui-assist-import.h 0000644 0001750 0001750 00000005452 15120467200 017006 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_IMPORT_H__
#define __HOMEBANK_IMPORT_H__
#define PAGE_WELCOME 0
#define PAGE_FILES 1
#define PAGE_IMPORT 2
// other are dynamic
enum
{
LST_GENFILE_POINTER,
LST_GENFILE_NAME,
NUM_LST_FILE
};
enum
{
LST_GENACC_NAME,
LST_GENACC_KEY,
NUM_LST_GENACC
};
enum
{
LST_GENTXN_POINTER,
NUM_LST_GENTXN
};
enum
{
LST_SELECT_UNSET,
LST_SELECT_ALL,
LST_SELECT_NONE,
LST_SELECT_INVERT,
LST_SELECT_VALID
};
typedef struct _import_txndata ImpTxnData;
struct _import_txndata
{
// account page
GtkWidget *IM_txn, *LB_txn;
GtkWidget *LB_acc_info;
GtkWidget *LB_acc_title;
//GtkWidget *LB_acc_count;
GtkWidget *LB_txn_title;
GtkWidget *BT_all, *BT_non, *BT_inv, *BT_val;
GtkWidget *CY_acc;
GtkWidget *IM_unamed;
GtkWidget *LV_gentxn;
GtkWidget *EX_duptxn;
GtkWidget *LV_duptxn;
GtkWidget *ST_stack;
GtkWidget *GR_misc;
GtkWidget *GR_msg;
GtkWidget *GR_date;
GtkWidget *GR_ofx;
GtkWidget *GR_qif;
GtkWidget *GR_select;
GtkWidget *EX_log, *TV_log;
GtkWidget *CY_txn_dateorder;
GtkWidget *NB_txn_daygap;
GtkWidget *CM_txn_ucfirst;
GtkWidget *CM_txn_togamount;
GtkWidget *CY_txn_ofxname;
GtkWidget *CY_txn_ofxmemo;
GtkWidget *CM_txn_qifmemo;
GtkWidget *CM_txn_qifswap;
};
struct import_data
{
GtkWidget *assistant;
//intro
GtkWidget *CM_dsta;
// filechooser
GtkWidget *filechooser;
GtkWidget *LV_file;
GtkWidget *BT_file_add;
GtkWidget *BT_file_delete;
//struct import_txndata txndata[TXN_MAX_ACCOUNT];
GArray *txndata;
//summary
GtkWidget *TX_summary;
GtkWidget *CM_set_pending;
GtkWidget *CM_do_auto_payee;
GtkWidget *CM_do_auto_assign;
// import context
ImportContext ictx;
};
struct import_target_data
{
GtkWidget *label1, *label2;
GtkWidget *getwidget1;
GtkWidget *getwidget2;
GtkWidget *radio[2];
};
GtkWidget *ui_import_assistant_new (gchar **paths);
Account *import_create_account(gchar *name, gchar *number);
const gchar *homebank_file_getencoding(gchar *filename);
gchar *homebank_utf8_ensure(gchar *buffer);
#endif
homebank-5.9.7/src/hbtk-switcher.c 0000664 0001750 0001750 00000022627 14736461407 016352 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//#include
#include /* atoi, atof, atol */
#include /* gettext */
#include
#include
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#define _(str) gettext (str)
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
enum {
CHANGED,
LAST_SIGNAL
};
static guint switcher_signals[LAST_SIGNAL] = {0,};
//G_DEFINE_TYPE(HbtkSwitcher, hbtk_radio_switcher, GTK_TYPE_BOX)
G_DEFINE_TYPE_WITH_CODE (HbtkSwitcher, hbtk_switcher, GTK_TYPE_BOX, G_ADD_PRIVATE (HbtkSwitcher))
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
_radiotest_cb_button_toggled (GtkToggleButton *togglebutton, gpointer user_data);
static GtkWidget *
_radiotest_get_nth(GtkRadioButton *rbutton, gint nth)
{
GtkWidget *widget = NULL;
GSList *list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
gint nb = g_slist_length(list) - 1;
if(nth <= nb)
{
widget = g_slist_nth_data(list, nb-nth);
}
return widget;
}
static void
_radiotest_set_active(GtkRadioButton *rbutton, gint nth)
{
GtkWidget *widget = _radiotest_get_nth(rbutton, nth);
if(widget != NULL)
{
g_signal_handlers_block_by_func (widget, G_CALLBACK(_radiotest_cb_button_toggled), NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
g_signal_handlers_unblock_by_func (widget, G_CALLBACK(_radiotest_cb_button_toggled), NULL);
}
}
static gint
_radiotest_get_active(GtkRadioButton *rbutton)
{
GSList *list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
gint nb, i = 0;
gint active = -1;
//list is build with prepend
nb = g_slist_length(list) - 1;
//g_print("-------- from %p, nb:%d\n", togglebutton, nb+1);
while(nb >= 0 && list)
{
GtkToggleButton *tmp = g_slist_nth_data(list, nb);
/*g_print("%d %d: %p %d %s\n", nb, i,
tmp,
gtk_toggle_button_get_active(tmp),
gtk_button_get_label(GTK_BUTTON(tmp))
);*/
if( gtk_toggle_button_get_active(tmp) == TRUE )
{
active = i;
break;
}
i++;
nb--;
}
//g_print(" get active:%d\n", active);
return active;
}
static void
_radiotest_cb_button_toggled (GtkToggleButton *togglebutton, gpointer user_data)
{
HbtkSwitcher *switcher = HBTK_SWITCHER (user_data);
HbtkSwitcherPrivate *priv = switcher->priv;
gint active;
if(gtk_toggle_button_get_active(togglebutton) == FALSE)
return;
//g_print("\n--------\n button toggled (%p)\n", togglebutton);
active = _radiotest_get_active(GTK_RADIO_BUTTON(togglebutton));
//g_print(" > active:%d\n", active);
if(priv->active != active)
{
DB( g_print(" **emit 'changed' signal** %d\n", active) );
priv->active = active;
g_signal_emit_by_name (switcher, "changed", NULL, NULL);
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
hbtk_switcher_destroy (GtkWidget *object)
{
//HbtkSwitcher *switcher = HBTK_SWITCHER (object);
//HbtkSwitcherPrivate *priv = switcher->priv;
g_return_if_fail(object != NULL);
g_return_if_fail(HBTK_IS_SWITCHER(object));
DB( g_print("\n[switcher] destroy\n") );
DB( g_print(" free switcher: %p\n", object) );
GTK_WIDGET_CLASS (hbtk_switcher_parent_class)->destroy (object);
}
static void
hbtk_switcher_dispose (GObject *gobject)
{
//HbtkSwitcher *self = HBTK_SWITCHER (gobject);
DB( g_print("\n[switcher] dispose\n") );
//g_clear_object (&self->priv->an_object);
G_OBJECT_CLASS (hbtk_switcher_parent_class)->dispose (gobject);
}
static void
hbtk_switcher_finalize (GObject *gobject)
{
//HbtkSwitcher *self = HBTK_SWITCHER (gobject);
DB( g_print("\n[switcher] finalize\n") );
//g_date_free(self->date);
//g_free (self->priv->a_string);
/* Always chain up to the parent class; as with dispose(), finalize()
* is guaranteed to exist on the parent's class virtual function table
*/
G_OBJECT_CLASS(hbtk_switcher_parent_class)->finalize (gobject);
}
static void
hbtk_switcher_class_init (HbtkSwitcherClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = G_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
DB( g_print("\n[switcher] class_init\n") );
//object_class->constructor = hbtk_switcher_constructor;
//object_class->set_property = hbtk_switcher_set_property;
//object_class->get_property = hbtk_switcher_get_property;
object_class->dispose = hbtk_switcher_dispose;
object_class->finalize = hbtk_switcher_finalize;
widget_class->destroy = hbtk_switcher_destroy;
switcher_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (HbtkSwitcherClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
//g_type_class_add_private (object_class, sizeof (HbtkSwitcherPrivate));
}
static void
hbtk_switcher_init (HbtkSwitcher *switcher)
{
HbtkSwitcherPrivate *priv;
DB( g_print("\n[switcher] init\n") );
priv = switcher->priv = hbtk_switcher_get_instance_private(switcher);
priv->active = 0;
//TODO ??
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
GtkWidget *
hbtk_switcher_new (GtkOrientation orientation)
{
HbtkSwitcher *switcher;
DB( g_print("\n[switcher] new\n") );
switcher = g_object_new (HBTK_TYPE_SWITCHER,
"orientation", orientation,
NULL);
return GTK_WIDGET(switcher);
}
void
hbtk_switcher_setup (HbtkSwitcher *switcher, gchar **items, gboolean buttonstyle)
{
HbtkSwitcherPrivate *priv = switcher->priv;
GtkWidget *button, *newbutton;
guint i;
button = gtk_radio_button_new_with_label (NULL, _(items[0]));
priv->first = GTK_RADIO_BUTTON(button);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), !buttonstyle);
g_signal_connect(button, "toggled", G_CALLBACK(_radiotest_cb_button_toggled), switcher);
gtk_box_prepend (GTK_BOX (switcher), button);
for (i = 1; items[i] != NULL; i++)
{
newbutton = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button), _(items[i]));
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (newbutton), !buttonstyle);
g_signal_connect(newbutton, "toggled", G_CALLBACK(_radiotest_cb_button_toggled), switcher);
gtk_box_prepend (GTK_BOX (switcher), newbutton);
}
if(buttonstyle)
{
GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(switcher));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_RAISED);
}
}
void
hbtk_switcher_setup_with_data (HbtkSwitcher *switcher, GtkWidget *label, HbKivData *kivdata, gboolean buttonstyle)
{
HbtkSwitcherPrivate *priv = switcher->priv;
GtkWidget *button, *image, *newbutton;
HbKivData *tmp = &kivdata[0];
guint i;
//button = gtk_radio_button_new_with_label (NULL, _(items[0]));
button = gtk_radio_button_new(NULL);
priv->first = GTK_RADIO_BUTTON(button);
image = hbtk_image_new_from_icon_name_16 (tmp->iconname);
g_object_set (button, "image", image, "tooltip-text", _(tmp->name), NULL);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), !buttonstyle);
//#2065592
g_signal_connect(button, "toggled", G_CALLBACK(_radiotest_cb_button_toggled), switcher);
gtk_box_prepend (GTK_BOX (switcher), button);
for (i = 1; ; i++)
{
tmp = &kivdata[i];
if( tmp->name == NULL )
break;
//newbutton = gtk_radio_button_new_with_label_from_widget (GTK_BUTTON (button), _(items[i]));
newbutton = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (button));
image = hbtk_image_new_from_icon_name_16 (tmp->iconname);
g_object_set (newbutton, "image", image, "tooltip-text", _(tmp->name), NULL);
gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (newbutton), !buttonstyle);
g_signal_connect(newbutton, "toggled", G_CALLBACK(_radiotest_cb_button_toggled), switcher);
gtk_box_prepend (GTK_BOX (switcher), newbutton);
}
if(buttonstyle)
{
GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(switcher));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_RAISED);
}
}
gint
hbtk_switcher_get_active (HbtkSwitcher *switcher)
{
HbtkSwitcherPrivate *priv = switcher->priv;
//return _radiotest_get_active(
return priv->active;
}
void
hbtk_switcher_set_active (HbtkSwitcher *switcher, gint active)
{
HbtkSwitcherPrivate *priv = switcher->priv;
_radiotest_set_active(priv->first, active);
priv->active = active;
}
void
hbtk_switcher_set_nth_sensitive (HbtkSwitcher *switcher, gint nth, gboolean sensitive)
{
HbtkSwitcherPrivate *priv = switcher->priv;
GtkWidget *widget = _radiotest_get_nth(priv->first, nth);
if(widget != NULL)
{
gtk_widget_set_sensitive(widget, sensitive);
}
}
homebank-5.9.7/src/hb-encoding.c 0000644 0001750 0001750 00000017572 14736461407 015752 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-encoding.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
* The original versions of the following tables are taken from profterm
*
* Copyright (C) 2002 Red Hat, Inc.
*/
static const GeditEncoding utf8_encoding = {
GEDIT_ENCODING_UTF_8,
"UTF-8",
"Unicode"
};
static const GeditEncoding encodings [] = {
{ GEDIT_ENCODING_ISO_8859_1,
"ISO-8859-1", "Western" },
{ GEDIT_ENCODING_ISO_8859_2,
"ISO-8859-2", "Central European" },
{ GEDIT_ENCODING_ISO_8859_3,
"ISO-8859-3", "South European" },
{ GEDIT_ENCODING_ISO_8859_4,
"ISO-8859-4", "Baltic" },
{ GEDIT_ENCODING_ISO_8859_5,
"ISO-8859-5", "Cyrillic" },
{ GEDIT_ENCODING_ISO_8859_6,
"ISO-8859-6", "Arabic" },
{ GEDIT_ENCODING_ISO_8859_7,
"ISO-8859-7", "Greek" },
{ GEDIT_ENCODING_ISO_8859_8,
"ISO-8859-8", "Hebrew Visual" },
{ GEDIT_ENCODING_ISO_8859_8_I,
"ISO-8859-8-I", "Hebrew" },
{ GEDIT_ENCODING_ISO_8859_9,
"ISO-8859-9", "Turkish" },
{ GEDIT_ENCODING_ISO_8859_10,
"ISO-8859-10", "Nordic" },
{ GEDIT_ENCODING_ISO_8859_13,
"ISO-8859-13", "Baltic" },
{ GEDIT_ENCODING_ISO_8859_14,
"ISO-8859-14", "Celtic" },
{ GEDIT_ENCODING_ISO_8859_15,
"ISO-8859-15", "Western" },
{ GEDIT_ENCODING_ISO_8859_16,
"ISO-8859-16", "Romanian" },
{ GEDIT_ENCODING_UTF_7,
"UTF-7", "Unicode" },
{ GEDIT_ENCODING_UTF_16,
"UTF-16", "Unicode" },
{ GEDIT_ENCODING_UTF_16_BE,
"UTF-16BE", "Unicode" },
{ GEDIT_ENCODING_UTF_16_LE,
"UTF-16LE", "Unicode" },
{ GEDIT_ENCODING_UTF_32,
"UTF-32", "Unicode" },
{ GEDIT_ENCODING_UCS_2,
"UCS-2", "Unicode" },
{ GEDIT_ENCODING_UCS_4,
"UCS-4", "Unicode" },
{ GEDIT_ENCODING_ARMSCII_8,
"ARMSCII-8", "Armenian" },
{ GEDIT_ENCODING_BIG5,
"BIG5", "Chinese Traditional" },
{ GEDIT_ENCODING_BIG5_HKSCS,
"BIG5-HKSCS", "Chinese Traditional" },
{ GEDIT_ENCODING_CP_866,
"CP866", "Cyrillic/Russian" },
{ GEDIT_ENCODING_EUC_JP,
"EUC-JP", "Japanese" },
{ GEDIT_ENCODING_EUC_JP_MS,
"EUC-JP-MS", "Japanese" },
{ GEDIT_ENCODING_CP932,
"CP932", "Japanese" },
{ GEDIT_ENCODING_EUC_KR,
"EUC-KR", "Korean" },
{ GEDIT_ENCODING_EUC_TW,
"EUC-TW", "Chinese Traditional" },
{ GEDIT_ENCODING_GB18030,
"GB18030", "Chinese Simplified" },
{ GEDIT_ENCODING_GB2312,
"GB2312", "Chinese Simplified" },
{ GEDIT_ENCODING_GBK,
"GBK", "Chinese Simplified" },
{ GEDIT_ENCODING_GEOSTD8,
"GEORGIAN-ACADEMY", "Georgian" }, /* FIXME GEOSTD8 ? */
{ GEDIT_ENCODING_HZ,
"HZ", "Chinese Simplified" },
{ GEDIT_ENCODING_IBM_850,
"IBM850", "Western" },
{ GEDIT_ENCODING_IBM_852,
"IBM852", "Central European" },
{ GEDIT_ENCODING_IBM_855,
"IBM855", "Cyrillic" },
{ GEDIT_ENCODING_IBM_857,
"IBM857", "Turkish" },
{ GEDIT_ENCODING_IBM_862,
"IBM862", "Hebrew" },
{ GEDIT_ENCODING_IBM_864,
"IBM864", "Arabic" },
{ GEDIT_ENCODING_ISO_2022_JP,
"ISO-2022-JP", "Japanese" },
{ GEDIT_ENCODING_ISO_2022_KR,
"ISO-2022-KR", "Korean" },
{ GEDIT_ENCODING_ISO_IR_111,
"ISO-IR-111", "Cyrillic" },
{ GEDIT_ENCODING_JOHAB,
"JOHAB", "Korean" },
{ GEDIT_ENCODING_KOI8_R,
"KOI8R", "Cyrillic" },
{ GEDIT_ENCODING_KOI8__R,
"KOI8-R", "Cyrillic" },
{ GEDIT_ENCODING_KOI8_U,
"KOI8U", "Cyrillic/Ukrainian" },
{ GEDIT_ENCODING_SHIFT_JIS,
"SHIFT_JIS", "Japanese" },
{ GEDIT_ENCODING_TCVN,
"TCVN", "Vietnamese" },
{ GEDIT_ENCODING_TIS_620,
"TIS-620", "Thai" },
{ GEDIT_ENCODING_UHC,
"UHC", "Korean" },
{ GEDIT_ENCODING_VISCII,
"VISCII", "Vietnamese" },
{ GEDIT_ENCODING_WINDOWS_1250,
"WINDOWS-1250", "Central European" },
{ GEDIT_ENCODING_WINDOWS_1251,
"WINDOWS-1251", "Cyrillic" },
{ GEDIT_ENCODING_WINDOWS_1252,
"WINDOWS-1252", "Western" },
{ GEDIT_ENCODING_WINDOWS_1253,
"WINDOWS-1253", "Greek" },
{ GEDIT_ENCODING_WINDOWS_1254,
"WINDOWS-1254", "Turkish" },
{ GEDIT_ENCODING_WINDOWS_1255,
"WINDOWS-1255", "Hebrew" },
{ GEDIT_ENCODING_WINDOWS_1256,
"WINDOWS-1256", "Arabic" },
{ GEDIT_ENCODING_WINDOWS_1257,
"WINDOWS-1257", "Baltic" },
{ GEDIT_ENCODING_WINDOWS_1258,
"WINDOWS-1258", "Vietnamese" }
};
const GeditEncoding *
gedit_encoding_get_from_index (gint index)
{
//g_return_val_if_fail (index >= 0, NULL);
if (index >= GEDIT_ENCODING_LAST)
return NULL;
//gedit_encoding_lazy_init ();
return &encodings [index];
}
const GeditEncoding *
gedit_encoding_get_utf8 (void)
{
//gedit_encoding_lazy_init ();
return &utf8_encoding;
}
static gchar *homebank_utf8_convert(gchar *buffer, const gchar **charset)
{
GError *conv_error;
gchar* conv_buffer = NULL;
gsize new_len;
guint i;
gboolean valid;
const struct _GeditEncoding *enc;
DB( g_print("(homebank) homebank_utf8_convert\n") );
for (i=0 ; i should try %s\n", enc->charset) );
conv_buffer = g_convert(buffer, -1, "UTF-8", enc->charset, NULL, &new_len, &conv_error);
valid = g_utf8_validate (conv_buffer, -1, NULL);
if ((conv_error != NULL) || !valid )
{
DB( g_print (" -> Couldn't convert from %s to UTF-8.\n", enc->charset) );
}
else
{
DB( g_print (" -> file compatible with '%s'\n", enc->charset) );
if(charset != NULL)
*charset = enc->charset;
return conv_buffer;
}
}
if(charset != NULL)
*charset = NULL;
return NULL;
}
/*
* Ensure a buffer to be utf-8, and convert if necessary
*
*/
gchar *homebank_utf8_ensure(gchar *buffer)
{
gboolean isvalid;
gchar *converted;
DB( g_print("(homebank) homebank_utf8_ensure\n") );
if(buffer == NULL)
return NULL;
isvalid = g_utf8_validate(buffer, -1, NULL);
DB( g_print(" -> is valid utf8: %d\n", isvalid) );
if(!isvalid)
{
converted = homebank_utf8_convert(buffer, NULL);
if(converted != NULL)
{
//g_warn here ?
g_free(buffer);
return converted;
}
//g_warn here ?
}
return buffer;
}
const gchar *homebank_file_getencoding(gchar *filename)
{
const gchar *charset = NULL;
gchar *buffer;
gsize length;
GError *error = NULL;
gboolean isutf8;
const gchar *locale_charset;
const struct _GeditEncoding *enc;
DB( g_print("(homebank) test encoding\n") );
if (g_get_charset (&locale_charset) == FALSE)
{
//unknown_encoding.charset = g_strdup (locale_charset);
}
DB( g_print(" -> locale charset is '%s'\n", locale_charset) );
if (g_file_get_contents (filename, &buffer, &length, &error))
{
isutf8 = g_utf8_validate(buffer, -1, NULL);
DB( g_print(" -> is valid utf8: %d\n", isutf8) );
if( isutf8 == FALSE )
{
gchar *converted;
converted = homebank_utf8_convert(buffer, &charset);
DB( g_print(" -> converted charset match: '%s'\n", charset) );
DB( g_print(" -> converted: '%p' %s\n", converted, converted) );
if(converted != NULL)
g_free(converted);
}
else
{
enc = gedit_encoding_get_utf8();
charset = enc->charset;
}
g_free(buffer);
}
DB( g_print (" -> charset is '%s'\n", charset) );
return charset;
}
homebank-5.9.7/src/ui-dialogs.c 0000644 0001750 0001750 00000114417 14736461407 015626 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-xml.h"
#include "ui-widgets.h"
#include "ui-dialogs.h"
#include "list-operation.h"
#include "ui-currency.h"
/* = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = */
gchar *dialog_get_name(gchar *title, gchar *origname, GtkWindow *parentwindow)
{
GtkWidget *dialog, *content, *mainvbox, *getwidget;
gchar *retval = NULL;
dialog = gtk_dialog_new_with_buttons (title,
GTK_WINDOW (parentwindow),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
getwidget = gtk_entry_new();
gtk_entry_set_width_chars(GTK_ENTRY(getwidget), 24);
gtk_box_prepend (GTK_BOX (mainvbox), getwidget);
gtk_widget_show_all(mainvbox);
if(origname != NULL)
gtk_entry_set_text(GTK_ENTRY(getwidget), origname);
gtk_widget_grab_focus (getwidget);
gtk_entry_set_activates_default (GTK_ENTRY(getwidget), TRUE);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
const gchar *name;
name = gtk_entry_get_text(GTK_ENTRY(getwidget));
/* ignore if entry is empty */
if (name && *name)
{
retval = g_strdup(name);
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
return retval;
}
static void my_ui_dialog_add_action_class(GtkDialog *dialog, gint response_id, gchar *class_name)
{
GtkWidget *button = gtk_dialog_get_widget_for_response(dialog, response_id);
if( button)
{
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(button)), class_name);
}
}
/* Confirmation Alert dialog */
gint ui_dialog_msg_confirm_alert(GtkWindow *parent, gchar *title, gchar *secondtext, gchar *actionverb, gboolean destructive)
{
GtkWidget *dialog;
gint retval;
dialog = gtk_message_dialog_new (GTK_WINDOW(parent),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
title,
NULL
);
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
actionverb, GTK_RESPONSE_OK,
NULL);
if(secondtext)
{
g_object_set(GTK_MESSAGE_DIALOG (dialog), "secondary-text", secondtext, NULL);
//gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), secondtext);
}
//5.6 style button
gchar *style = destructive ? GTK_STYLE_CLASS_DESTRUCTIVE_ACTION : GTK_STYLE_CLASS_SUGGESTED_ACTION;
my_ui_dialog_add_action_class(GTK_DIALOG(dialog), GTK_RESPONSE_OK, style);
gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
retval = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_window_destroy (GTK_WINDOW(dialog));
return retval;
}
/* Message dialog */
gint ui_dialog_msg_question(GtkWindow *parent, gchar *title, gchar *message_format, ...)
{
GtkWidget *dialog;
gchar* msg = NULL;
va_list args;
gint retval;
dialog = gtk_message_dialog_new (GTK_WINDOW(parent),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
title,
NULL
);
if (message_format)
{
va_start (args, message_format);
msg = g_strdup_vprintf (message_format, args);
va_end (args);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", msg);
g_free (msg);
}
gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_NO);
retval = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_window_destroy (GTK_WINDOW(dialog));
return retval;
}
/*
** open a info/error dialog for user information purpose
*/
void ui_dialog_msg_infoerror(GtkWindow *parent, GtkMessageType type, gchar *title, gchar *message_format, ...)
{
GtkWidget *dialog;
gchar* msg = NULL;
va_list args;
dialog = gtk_message_dialog_new (GTK_WINDOW(parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
type,
GTK_BUTTONS_OK,
"%s",
title
);
if (message_format)
{
va_start (args, message_format);
msg = g_strdup_vprintf (message_format, args);
va_end (args);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", msg);
g_free (msg);
}
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_window_destroy (GTK_WINDOW(dialog));
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void ui_dialog_file_statistics(void)
{
GtkWidget *dialog, *content_area, *box, *group_grid;
GtkWidget *label, *widget;
//gchar *tmpstr;
gint row, count, count2;
dialog = gtk_dialog_new_with_buttons (_("File statistics"),
GTK_WINDOW (GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_default_size (GTK_WINDOW(dialog), HB_MINWIDTH_LIST, -1);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(box), SPACING_LARGE);
gtk_box_prepend (GTK_BOX (content_area), box);
// group :: file title
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_box_prepend (GTK_BOX (box), group_grid);
row = 1;
label = make_label_widget(_("Account"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_acc_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Transaction"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_transaction_length();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
da_archive_stats(&count, &count2);
row++;
label = make_label_widget(_("Scheduled"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
ui_label_set_integer(GTK_LABEL(widget), count2);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Template"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Assignment"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_asg_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
widget = gtk_separator_new( GTK_ORIENTATION_VERTICAL);
gtk_box_prepend (GTK_BOX (box), widget);
// group :: file title
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_box_prepend (GTK_BOX (box), group_grid);
row = 1;
label = make_label_widget(_("Payee"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_pay_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Category"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_cat_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Tag"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_tag_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Currency"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label(NULL, 1.0, 0.5);
count = da_cur_length ();
ui_label_set_integer(GTK_LABEL(widget), count);
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
gtk_widget_show_all(box);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct dialog_currency_data
{
GtkWidget *dialog;
GtkWidget *LB_currency;
GtkWidget *BT_change;
Currency4217 *curfmt;
};
static void ui_dialog_upgrade_choose_currency_change_action(GtkWidget *widget, gpointer user_data)
{
struct dialog_currency_data *data = user_data;
struct curSelectContext selectCtx;
data->curfmt = NULL;
ui_cur_select_dialog_new(GTK_WINDOW(data->dialog), CUR_SELECT_MODE_BASE, &selectCtx);
if( selectCtx.cur_4217 != NULL )
{
Currency4217 *curfmt;
gchar label[128];
gchar *name;
curfmt = selectCtx.cur_4217;
DB( g_printf("- user selected: '%s' '%s'\n", curfmt->curr_iso_code, curfmt->name) );
data->curfmt = curfmt;
name = curfmt->name;
g_snprintf(label, 127, "%s - %s", curfmt->curr_iso_code, name);
gtk_label_set_text (GTK_LABEL(data->LB_currency), label);
}
}
static void ui_dialog_upgrade_choose_currency_fill(struct dialog_currency_data *data)
{
Currency *cur;
gchar label[128];
data->curfmt = NULL;
cur = da_cur_get (GLOBALS->kcur);
g_snprintf(label, 127, "%s - %s", cur->iso_code, cur->name);
gtk_label_set_text (GTK_LABEL(data->LB_currency), label);
}
void ui_dialog_upgrade_choose_currency(void)
{
struct dialog_currency_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *label, *widget;
gint crow, row;
data = g_malloc0(sizeof(struct dialog_currency_data));
if(!data) return;
dialog = gtk_dialog_new_with_buttons (_("Upgrade"),
GTK_WINDOW (GLOBALS->mainwindow),
0,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
widget = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_focus(GTK_WINDOW(dialog), widget);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: file title
group_grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (content_grid), group_grid, 0, crow++, 1, 1);
row = 0;
label = make_label(_("Select a base currency"), 0, 0);
gimp_label_set_attributes(GTK_LABEL(label),
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
-1);
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 3, 1);
row++;
label = make_label(
_("Starting v5.1, HomeBank can manage several currencies\n" \
"if the currency below is not correct, please change it:"), 0, 0);
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 3, 1);
row++;
label = make_label_widget(_("Currency:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = make_label (NULL, 0, 0.5);
data->LB_currency = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = gtk_button_new_with_mnemonic (_("_Change"));
data->BT_change = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
g_signal_connect (G_OBJECT (data->BT_change), "clicked", G_CALLBACK (ui_dialog_upgrade_choose_currency_change_action), data);
ui_dialog_upgrade_choose_currency_fill(data);
gtk_widget_show_all(content_grid);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
if( data->curfmt != NULL )
{
hbfile_replace_basecurrency(data->curfmt);
}
}
// in any case set every accounts to base currency
GList *list;
list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc = list->data;
account_set_currency(acc, GLOBALS->kcur);
list = g_list_next(list);
}
g_list_free(list);
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
// make sure dialog is gone
hb_window_run_pending();
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkFileFilter *ui_file_chooser_add_filter(GtkFileChooser *chooser, gchar *name, gchar *pattern)
{
GtkFileFilter *filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, name);
gtk_file_filter_add_pattern (filter, pattern);
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(chooser), filter);
return filter;
}
gboolean ui_file_chooser_qif(GtkWindow *parent, gchar **storage_ptr)
{
GtkWidget *chooser;
gboolean retval;
DB( g_print("(homebank) chooser save qif\n") );
chooser = gtk_file_chooser_dialog_new (
_("Export as QIF"),
GTK_WINDOW(parent),
GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
//todo: change this ?
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), PREFS->path_export);
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("QIF files"), "*.[Qq][Ii][Ff]");
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("All files"), "*");
retval = FALSE;
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
gchar *tmpfilename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
*storage_ptr = hb_filename_new_with_extension(tmpfilename, "qif");
g_free(tmpfilename);
retval = TRUE;
}
gtk_window_destroy (GTK_WINDOW(chooser));
return retval;
}
/*
** open a file chooser dialog and store filename to GLOBALS if OK
*/
gboolean ui_file_chooser_csv(GtkWindow *parent, GtkFileChooserAction action, gchar **storage_ptr, gchar *name)
{
GtkWidget *chooser;
gchar *title;
gchar *button;
gboolean retval;
gchar *path;
DB( g_print("(hombank) csvfile chooser csv %d\n", action) );
if( action == GTK_FILE_CHOOSER_ACTION_OPEN )
{
title = _("Import from CSV");
button = _("_Open");
path = PREFS->path_import;
}
else
{
title = _("Export as CSV");
button = _("_Save");
path = PREFS->path_export;
}
chooser = gtk_file_chooser_dialog_new (title,
GTK_WINDOW(parent),
action, //GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
button, GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), path);
if(name != NULL)
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(chooser), name);
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("CSV files"), "*.[Cc][Ss][Vv]");
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("All files"), "*");
retval = FALSE;
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
gchar *tmpfilename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
if( action == GTK_FILE_CHOOSER_ACTION_SAVE )
{
*storage_ptr = hb_filename_new_with_extension(tmpfilename, "csv");
g_free(tmpfilename);
}
else
{
*storage_ptr = tmpfilename;
}
retval = TRUE;
}
gtk_window_destroy (GTK_WINDOW(chooser));
return retval;
}
/*
** open a file chooser dialog and store filename to GLOBALS if OK
*/
gboolean ui_file_chooser_xhb(GtkFileChooserAction action, gchar **storage_ptr, gboolean bakmode)
{
GtkWidget *chooser;
gchar *title;
gchar *button;
gboolean retval;
DB( g_print("(ui-dialog) file chooser xhb %d\n", action) );
if( action == GTK_FILE_CHOOSER_ACTION_OPEN )
{
title = (bakmode==FALSE) ? _("Open HomeBank file") : _("Open HomeBank backup file");
button = _("_Open");
}
else
{
title = _("Save HomeBank file as");
button = _("_Save");
}
chooser = gtk_file_chooser_dialog_new (title,
GTK_WINDOW(GLOBALS->mainwindow),
action, //GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
button, GTK_RESPONSE_ACCEPT,
NULL);
if( action == GTK_FILE_CHOOSER_ACTION_OPEN )
{
if( bakmode == FALSE )
{
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), PREFS->path_hbfile);
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("HomeBank files"), "*.[Xx][Hh][Bb]");
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("All files"), "*");
}
else
{
gchar *pattern;
GtkFileFilter *flt;
//#1864176 open backup should open the backup folder
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), PREFS->path_hbbak);
pattern = hb_filename_backup_get_filtername(GLOBALS->xhb_filepath);
flt = ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("File backup"), pattern);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), flt);
g_free(pattern);
ui_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), _("All backups"), "*.[Bb][Aa][Kk]");
}
}
else /* GTK_FILE_CHOOSER_ACTION_SAVE */
{
gchar *basename, *dirname;
basename = g_path_get_basename(GLOBALS->xhb_filepath);
dirname = g_path_get_dirname (GLOBALS->xhb_filepath);
//gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(chooser), GLOBALS->xhb_filepath);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(chooser), dirname);
gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(chooser), basename);
g_free(dirname);
g_free(basename);
}
retval = FALSE;
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
*storage_ptr = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
retval = TRUE;
}
gtk_window_destroy (GTK_WINDOW(chooser));
return retval;
}
/*
**
*/
gboolean ui_file_chooser_folder(GtkWindow *parent, gchar *title, gchar **storage_ptr)
{
GtkWidget *chooser;
gboolean retval;
DB( g_print("(ui-dialog) folder chooser\n") );
chooser = gtk_file_chooser_dialog_new (title,
parent,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
DB( g_print(" - set folder %s\n", *storage_ptr) );
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(chooser), *storage_ptr);
retval = FALSE;
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
gchar *filename;
//nb: filename must be freed with g_free
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
DB( g_print("- folder %s\n", filename) );
//todo: dangerous to do this here, review please !
g_free(*storage_ptr);
*storage_ptr = filename;
DB( g_print("- folder stored: %s\n", *storage_ptr) );
retval = TRUE;
}
gtk_window_destroy (GTK_WINDOW(chooser));
return retval;
}
/*
** request the user to save last change
*/
gboolean ui_dialog_msg_savechanges(GtkWidget *widget, gpointer user_data)
{
gboolean retval = TRUE;
GtkWidget *dialog;
if(GLOBALS->changes_count)
{
gint result;
dialog = gtk_message_dialog_new
(
GTK_WINDOW(GLOBALS->mainwindow),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
//GTK_MESSAGE_INFO,
GTK_BUTTONS_NONE,
_("Save changes to the file before closing?")
);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("If you don't save, changes will be permanently lost.\nNumber of changes: %d."),
GLOBALS->changes_count
);
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("Close _without saving"), 0,
_("_Cancel"), 1,
_("_Save"), 2,
NULL);
//5.6 style button
my_ui_dialog_add_action_class(GTK_DIALOG(dialog), 0, GTK_STYLE_CLASS_DESTRUCTIVE_ACTION);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), 2);
result = gtk_dialog_run( GTK_DIALOG( dialog ) );
gtk_window_destroy (GTK_WINDOW(dialog));
if(result == 1 || result == GTK_RESPONSE_DELETE_EVENT)
{
retval = FALSE;
}
else
{
if(result == 2)
{
//#2090668 save new file as
if( GLOBALS->hbfile_is_new == TRUE )
{
gchar *filename = NULL;
if(ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_SAVE, &filename, FALSE) == TRUE)
{
DB( g_print(" + should save as '%s'\n", filename) );
homebank_file_ensure_xhb(filename);
homebank_backup_current_file();
homebank_save_xml(GLOBALS->xhb_filepath);
GLOBALS->hbfile_is_new = FALSE;
}
}
else
{
DB( g_print(" + should quick save %s\n", GLOBALS->xhb_filepath) );
//todo: should migrate this
//#1720377 also backup
homebank_file_ensure_xhb(NULL);
homebank_backup_current_file();
homebank_save_xml(GLOBALS->xhb_filepath);
}
}
}
}
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct dialog_export_csv_data
{
gboolean showall;
GtkWidget *dialog;
GtkWidget *CM_split, *CM_status;
GtkWidget *IM_warn, *LB_warn;
};
static void ui_dialog_export_csv_update(GtkWidget *widget, gpointer user_data)
{
struct dialog_export_csv_data *data = user_data;
gboolean hassplit, hasstatus, visible;
hassplit = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_split));
hasstatus = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_status));
visible = data->showall | hassplit | hasstatus;
hb_widget_visible(data->IM_warn, visible);
hb_widget_visible(data->LB_warn, visible);
}
gint ui_dialog_export_csv(GtkWindow *parent, gchar **storage_ptr, gboolean *split_ptr, gboolean *status_ptr, gboolean showall)
{
struct dialog_export_csv_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *label, *widget, *BT_folder, *ST_name;
gchar *tmpstr;
gint crow, row;
data = g_malloc0(sizeof(struct dialog_export_csv_data));
if(!data) return GTK_RESPONSE_CANCEL;
data->showall = showall;
dialog = gtk_dialog_new_with_buttons (_("Export as CSV"),
GTK_WINDOW (parent),
0,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Export as _CSV"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_default_size (GTK_WINDOW(dialog), HB_MINWIDTH_LIST, -1);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: file title
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);
row = 0;
row++;
label = make_label_widget(_("Folder:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
BT_folder = gtk_file_chooser_button_new (_("Pick a Folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
gtk_grid_attach (GTK_GRID (group_grid), BT_folder, 2, row, 1, 1);
row++;
label = make_label_widget(_("Filename:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
ST_name = make_string (label);
gtk_widget_set_hexpand(ST_name, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), ST_name, 2, row, 1, 1);
row++;
label = make_label_group(_("Options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 3, 1);
row++;
data->CM_status = gtk_check_button_new_with_mnemonic (_("Add Status column"));
gtk_grid_attach (GTK_GRID (group_grid), data->CM_status, 1, row, 2, 1);
row++;
data->CM_split = gtk_check_button_new_with_mnemonic (_("Detail split lines"));
gtk_grid_attach (GTK_GRID (group_grid), data->CM_split, 1, row, 2, 1);
//warning text
row++;
widget = hbtk_image_new_from_icon_name_32 (ICONNAME_WARNING);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
gtk_widget_set_margin_top (widget, SPACING_MEDIUM);
data->IM_warn = widget;
// 123456789012345678901234567890123456789012345678901234567890
label = gtk_label_new(_("The file will not be in HomeBank CSV format, because you export\n" \
"from 'All transaction', or you selected an option."));
data->LB_warn = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
/* signals */
g_signal_connect (data->CM_split , "toggled", G_CALLBACK (ui_dialog_export_csv_update), data);
g_signal_connect (data->CM_status , "toggled", G_CALLBACK (ui_dialog_export_csv_update), data);
//setup
tmpstr = g_path_get_dirname(*storage_ptr);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(BT_folder), tmpstr);
g_free(tmpstr);
tmpstr = g_path_get_basename(*storage_ptr);
gtk_entry_set_text(GTK_ENTRY(ST_name), tmpstr);
g_free(tmpstr);
gtk_widget_show_all(content_grid);
ui_dialog_export_csv_update(dialog, data);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
gchar *hostname;
//#300380 fixed export path problem (was always the export of preference)
//not to be used -- gchar *nufolder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(BT_folder));
gchar *urifolder = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(BT_folder));
gchar *nufolder = g_filename_from_uri(urifolder, &hostname, NULL);
gchar *nufilename = hb_filename_new_with_extension((gchar *)gtk_entry_get_text (GTK_ENTRY(ST_name)), "csv");
g_free(*storage_ptr);
*storage_ptr = g_build_filename(nufolder, nufilename, NULL);
g_free(nufilename);
g_free(nufolder);
g_free(urifolder);
if( split_ptr != NULL )
{
*split_ptr = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_split));
}
if( status_ptr != NULL )
{
*status_ptr = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_status));
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return result;
}
gint ui_dialog_export_pdf(GtkWindow *parent, gchar **storage_ptr)
{
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *label, *widget, *BT_folder, *ST_name;
gchar *tmpstr;
gint crow, row;
dialog = gtk_dialog_new_with_buttons (_("Export as PDF"),
GTK_WINDOW (parent),
0,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Export as _PDF"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_default_size (GTK_WINDOW(dialog), HB_MINWIDTH_LIST, -1);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: file title
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);
row = 0;
label = make_label_widget(_("Folder:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
BT_folder = gtk_file_chooser_button_new (_("Pick a Folder"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
gtk_grid_attach (GTK_GRID (group_grid), BT_folder, 1, row, 1, 1);
row++;
label = make_label_widget(_("Filename:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
ST_name = make_string (label);
gtk_widget_set_hexpand(ST_name, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), ST_name, 1, row, 1, 1);
row++;
row++;
widget = hbtk_image_new_from_icon_name_32 (ICONNAME_HB_QUICKTIPS);
gtk_grid_attach (GTK_GRID (group_grid), widget, 0, row, 1, 1);
// 123456789012345678901234567890123456789012345678901234567890
label = gtk_label_new(_("With HomeBank, printing is oriented towards an eco-responsible\n" \
"attitude towards the most widespread digital format: PDF format. "));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
//setup
tmpstr = g_path_get_dirname(*storage_ptr);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(BT_folder), tmpstr);
g_free(tmpstr);
tmpstr = g_path_get_basename(*storage_ptr);
gtk_entry_set_text(GTK_ENTRY(ST_name), tmpstr);
g_free(tmpstr);
gtk_widget_show_all(content_grid);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
gchar *hostname;
//#300380 fixed export path problem (was always the export of preference)
//not to be used -- gchar *nufolder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(BT_folder));
gchar *urifolder = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(BT_folder));
gchar *nufolder = g_filename_from_uri(urifolder, &hostname, NULL);
gchar *nufilename = hb_filename_new_with_extension((gchar *)gtk_entry_get_text (GTK_ENTRY(ST_name)), "pdf");
g_free(*storage_ptr);
*storage_ptr = g_build_filename(nufolder, nufilename, NULL);
g_free(nufilename);
g_free(nufolder);
g_free(urifolder);
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
return result;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct xfer_data
{
GtkWidget *dialog;
GtkWidget *srctreeview;
GtkWidget *lb_several;
GtkWidget *treeview;
};
//#1982036 make releavant column to be displayed
static gint lst_xfer_columns[NUM_LST_DSPOPE+1] = {
LST_DSPOPE_STATUS,
LST_DSPOPE_MATCH,
LST_DSPOPE_ACCOUNT,
LST_DSPOPE_DATE,
LST_DSPOPE_AMOUNT,
LST_DSPOPE_CLR,
LST_DSPOPE_MEMO,
LST_DSPOPE_PAYNUMBER,
LST_DSPOPE_PAYEE,
LST_DSPOPE_CATEGORY,
LST_DSPOPE_TAGS,
-LST_DSPOPE_EXPENSE,
-LST_DSPOPE_INCOME,
-LST_DSPOPE_BALANCE
};
static void ui_dialog_transaction_xfer_select_child_cb(GtkWidget *widget, gpointer user_data)
{
struct xfer_data *data;
GtkTreeSelection *selection;
gboolean sensitive;
gint count;
DB( g_print("\n(xfer select) toggle choice\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview));
count = gtk_tree_selection_count_selected_rows(selection);
sensitive = (count > 0) ? TRUE : FALSE;
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), HB_RESPONSE_SELECTION, sensitive);
DB( g_print(" test count %d sensitive %d\n", count, sensitive) );
}
static void ui_dialog_transaction_xfer_select_child_selection_cb(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_dialog_transaction_xfer_select_child_cb(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
gint ui_dialog_transaction_xfer_select_child(GtkWindow *parent, Transaction *stxn, GList *matchlist, Transaction **child)
{
struct xfer_data *data;
GtkWidget *dialog, *content, *mainvbox, *scrollwin, *label;
GtkTreeModel *newmodel;
GtkTreeIter newiter;
gint w, h, dw, dh;
gint nbmatch;
DB( g_print("\n(xfer select) new\n") );
data = g_malloc0(sizeof(struct xfer_data));
if(!data) return 0;
dialog = gtk_dialog_new_with_buttons (
_("Select action for target creation"),
parent,
0,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Create _New"), HB_RESPONSE_CREATE_NEW,
_("Use _Selection"), HB_RESPONSE_SELECTION,
NULL);
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
data->dialog = dialog;
//5.8 set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, -1);
//hide close button
//gtk_window_set_deletable(GTK_WINDOW(dialog), FALSE);
//gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
//gtk_window_set_default_size (GTK_WINDOW (dialog), 800, 494);
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_LARGE);
label = make_label_group(_("Source transfer"));
gtk_box_prepend (GTK_BOX (mainvbox), label);
// source listview
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
// gtk_widget_set_size_request(sw, -1, HB_MINWIDTH_LIST/2);
gtk_widget_set_margin_left(scrollwin, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (mainvbox), scrollwin);
data->srctreeview = create_list_transaction(LIST_TXN_TYPE_XFERSOURCE, lst_xfer_columns);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->srctreeview)), GTK_SELECTION_NONE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), data->srctreeview);
// target listview
label = make_label_group(_("Target association suggested"));
gtk_widget_set_margin_top(label, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainvbox), label);
label = make_label(_(
"HomeBank has found some transaction that may be " \
"the associated transaction for the internal transfer."), 0.0, 0.5
);
data->lb_several = label;
gtk_widget_set_margin_left(label, SPACING_MEDIUM);
/*gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
-1);*/
gtk_box_prepend (GTK_BOX (mainvbox), label);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request(scrollwin, -1, HB_MINWIDTH_LIST*1.5);
gtk_widget_set_margin_left(scrollwin, SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainvbox), scrollwin);
data->treeview = create_list_transaction(LIST_TXN_TYPE_XFERTARGET, lst_xfer_columns);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview)), GTK_SELECTION_SINGLE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), data->treeview);
DB( g_print(" populate src\n") );
/* populate source */
if( stxn != NULL )
{
newmodel = gtk_tree_view_get_model(GTK_TREE_VIEW(data->srctreeview));
gtk_tree_store_clear (GTK_TREE_STORE(newmodel));
gtk_tree_store_append (GTK_TREE_STORE(newmodel), &newiter, NULL);
//#1830523/#1840393
gtk_tree_store_set (GTK_TREE_STORE(newmodel), &newiter,
MODEL_TXN_POINTER, stxn,
MODEL_TXN_SPLITAMT, stxn->amount,
-1);
}
DB( g_print(" populate src\n") );
/* populate target */
newmodel = gtk_tree_view_get_model(GTK_TREE_VIEW(data->treeview));
gtk_tree_store_clear (GTK_TREE_STORE(newmodel));
nbmatch = 0;
GList *tmplist = g_list_first(matchlist);
while (tmplist != NULL)
{
Transaction *tmp = tmplist->data;
/* append to our treeview */
gtk_tree_store_append (GTK_TREE_STORE(newmodel), &newiter, NULL);
//#1830523/#1840393
gtk_tree_store_set (GTK_TREE_STORE(newmodel), &newiter,
MODEL_TXN_POINTER, tmp,
MODEL_TXN_SPLITAMT, tmp->amount,
-1);
//DB( g_print(" - fill: %s %.2f %x\n", item->memo, item->amount, (unsigned int)item->same) );
tmplist = g_list_next(tmplist);
nbmatch++;
}
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview)), "changed", G_CALLBACK (ui_dialog_transaction_xfer_select_child_selection_cb), NULL);
//5.8 chnage text
gtk_label_set_text(GTK_LABEL(data->lb_several), _("No transaction match.") );
gtk_widget_show_all(mainvbox);
//#1982036 show essential field on the left : no need to leave space
DB( g_print(" autosize\n") );
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(data->srctreeview));
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(data->treeview));
//5.5.7: add sort by match descending
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(newmodel), LST_DSPOPE_MATCH, GTK_SORT_DESCENDING);
//initialize
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), HB_RESPONSE_SELECTION, FALSE);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
*child = NULL;
if(result == HB_RESPONSE_SELECTION)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, child, -1);
}
}
DB( g_print(" return %d child = %p\n", result, child) );
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return result;
}
homebank-5.9.7/src/ui-txn-multi.h 0000664 0001750 0001750 00000003063 14736461415 016145 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __UI_TXN_MULTI_H__
#define __UI_TXN_MULTI_H__
struct ui_multipleedit_dialog_data
{
GtkWidget *dialog;
GtkWidget *CM_date, *PO_date;
GtkWidget *CM_amount, *ST_amount;
GtkWidget *LB_acc, *CM_acc, *PO_acc;
GtkWidget *LB_mode, *CM_mode, *NU_mode;
GtkWidget *CM_number, *ST_number;
GtkWidget *CM_pay, *PO_pay;
GtkWidget *CM_cat, *PO_cat;
GtkWidget *CM_memo, *ST_memo;
GtkWidget *CM_tags, *ST_tags, *CY_tags;
GtkWidget *CM_xfer, *PO_accto;
GtkTreeView *treeview;
guint32 kacc;
gboolean has_xfer;
};
void ui_multipleedit_dialog_prefill( GtkWidget *widget, Transaction *ope, gint column_id );
gint ui_multipleedit_dialog_apply( GtkWidget *widget, gboolean *do_sort );
GtkWidget *ui_multipleedit_dialog_new(GtkWindow *parent, GtkTreeView *treeview);
#endif /* __UI_TXN_MULTI_H__ */
homebank-5.9.7/src/homebank.c 0000644 0001750 0001750 00000100665 15052415320 015336 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-mainwindow.h"
#include "hb-preferences.h"
#include "language.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include
#ifdef G_OS_WIN32
#include
#endif
#define APPLICATION_NAME "HomeBank"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
struct HomeBank *GLOBALS;
struct Preferences *PREFS;
/* installation paths */
static gchar *config_dir = NULL;
static gchar *images_dir = NULL;
static gchar *pixmaps_dir = NULL;
static gchar *locale_dir = NULL;
static gchar *help_dir = NULL;
static gchar *datas_dir = NULL;
//#define MARKUP_STRING "%s"
/* Application arguments */
static gchar *arg_filepath = NULL;
static const GOptionEntry option_entries[] =
{
/* Version */
{
"version", 'V', 0, G_OPTION_ARG_NONE, NULL,
N_("Show the application’s version"), NULL
},
/* collects file arguments */
{
G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, NULL,
N_("[FILE]")
},
{ NULL }
};
/* = = = = = = = = = = = = = = = = = = = = */
/*
** ensure the filename ends with '.xhb'
*/
void homebank_file_ensure_xhb(gchar *filename)
{
DB( g_print("\n[homebank] file_ensure_xhb\n") );
filename = (filename == NULL) ? g_strdup(GLOBALS->xhb_filepath) : filename;
DB( g_print(" in filepath: '%s'\n", GLOBALS->xhb_filepath) );
if( g_str_has_suffix (filename, ".xhb") == FALSE )
{
gchar *newfilename;
newfilename = hb_filename_new_with_extension(filename, "xhb");
hbfile_change_filepath(newfilename);
}
//#1460390
else
{
hbfile_change_filepath(filename);
}
DB( g_print(" out filepath: '%s'\n", GLOBALS->xhb_filepath) );
}
static gboolean homebank_file_copy(gchar *srcfile, gchar *dstfile)
{
gchar *buffer;
gsize length;
//GError *error = NULL;
gboolean retval = FALSE;
DB( g_print("\n[homebank] file copy\n") );
if (g_file_get_contents (srcfile, &buffer, &length, NULL))
{
if(g_file_set_contents(dstfile, buffer, length, NULL))
{
retval = TRUE;
}
g_free(buffer);
}
DB( g_print(" - copied '%s' => '%s' :: %d\n", srcfile, dstfile, retval) );
return retval;
}
static gboolean homebank_file_delete_existing(gchar *filepath)
{
gboolean retval = FALSE;
DB( g_print("\n[homebank] file delete existing\n") );
if( g_file_test(filepath, G_FILE_TEST_EXISTS) )
{
DB( g_print(" - deleting: '%s'\n", filepath) );
g_remove(filepath);
retval = TRUE;
}
else
{
DB( g_print(" - cannot delete: '%s'\n", filepath) );
}
return retval;
}
void homebank_backup_current_file(void)
{
gchar *bakfilename;
GPtrArray *array;
gint i;
DB( g_print("\n[homebank] backup_current_file\n") );
//do normal linux backup file
DB( g_print(" normal backup with ~\n") );
bakfilename = hb_filename_new_with_extension (GLOBALS->xhb_filepath, "xhb~");
homebank_file_delete_existing(bakfilename);
//#512046 copy file not to broke potential links
//retval = g_rename(pathname, newname);
homebank_file_copy (GLOBALS->xhb_filepath, bakfilename);
g_free(bakfilename);
//do safe backup according to user preferences
DB( g_print(" user pref backup\n") );
if( PREFS->bak_is_automatic == TRUE )
{
bakfilename = hb_filename_new_for_backup(GLOBALS->xhb_filepath);
if( g_file_test(bakfilename, G_FILE_TEST_EXISTS) == FALSE )
{
homebank_file_copy (GLOBALS->xhb_filepath, bakfilename);
}
g_free(bakfilename);
//delete any offscale backup
DB( g_print(" clean old backup\n") );
array = hb_filename_backup_list(GLOBALS->xhb_filepath);
DB( g_print(" found %d match\n", array->len) );
//#1847645
//gchar *dirname = g_path_get_dirname(GLOBALS->xhb_filepath);
gchar *dirname = PREFS->path_hbbak;
for(i=0;i<(gint)array->len;i++)
{
gchar *offscalefilename = g_ptr_array_index(array, i);
DB( g_print(" %d : '%s'\n", i, offscalefilename) );
if( i >= PREFS->bak_max_num_copies )
{
gchar *bakdelfilepath = g_build_filename(dirname, offscalefilename, NULL);
DB( g_print(" - should delete '%s'\n", bakdelfilepath) );
homebank_file_delete_existing(bakdelfilepath);
g_free(bakdelfilepath);
}
}
g_ptr_array_free(array, TRUE);
//g_free(dirname);
}
}
/* = = = = = = = = = = = = = = = = = = = = */
static void
homebank_util_check_backup(void)
{
if( PREFS->bak_is_automatic == FALSE )
return;
if(! g_file_test(PREFS->path_hbbak, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
{
ui_dialog_msg_infoerror(GTK_WINDOW(GLOBALS->mainwindow), GTK_MESSAGE_ERROR,
_("Invalid Backup directory."),
_("The files backup will not work, directory is:\n'%s'."),
PREFS->path_hbbak
);
}
}
//5.7 test check update online
/*
static gint homebank_util_check_update(GtkWindow *parent)
{
SoupSession *session;
SoupMessage *msg;
GCancellable *cancellable;
GBytes *body;
gchar *query;
guint status;
gboolean retval = TRUE;
DB( g_printf("\n[homebank] check version update\n") );
query = HOMEBANK_URL_BASE "/tools/updates.php?h=d1ea6a421b5ab0d451eead9de647c22e";
DB( g_printf("query: '%s'\n", query) );
session = soup_session_new_with_options (
SOUP_SESSION_USER_AGENT, "HomeBankProgram",
NULL
);
msg = soup_message_new ("GET", query);
if(msg != NULL)
{
//soup_session_send_message (session, msg);
body = soup_session_send_and_read (session, msg, cancellable, error);
status = soup_message_get_status (msg);
DB( g_print("status_code: %d %d\n", msg->status_code, SOUP_STATUS_IS_SUCCESSFUL(status) ) );
DB( g_print("reason: %s\n", msg->reason_phrase) );
DB( g_print("datas: %s\n", msg->response_body->data) );
if( SOUP_STATUS_IS_SUCCESSFUL(status) == TRUE )
{
//#1750426 ignore the retval here (false when no rate was found, as we don't care)
DB( g_print("datas ok: %s\n", msg->response_body->data) );
}
else
{
*error = g_error_new_literal(1, status, soup_message_get_reason_phrase(msg) );
}
g_object_unref(msg);
}
else
{
error = g_error_new_literal(1, 0, "cannot parse URI");
}
soup_session_abort (session);
g_object_unref(session);
if( error )
{
DB( g_print("error: %s\n", error->message) );
g_error_free (error);
}
return 0;
}
*/
/* = = = = = = = = = = = = = = = = = = = = */
/* url open */
#ifdef G_OS_WIN32
#define SW_NORMAL 1
static gboolean
homebank_util_url_show_win32 (const gchar *url)
{
int retval;
gchar *errmsg;
/* win32 API call */
retval = ShellExecuteA (NULL, "open", url, NULL, NULL, SW_NORMAL);
if (retval < 0 || retval > 32)
return TRUE;
errmsg = g_win32_error_message(retval);
DB( g_print ("%s\n", errmsg) );
g_free(errmsg);
return FALSE;
}
#else
static gboolean
homebank_util_url_show_unix (const gchar *url)
{
gboolean retval;
GError *err = NULL;
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
retval = gtk_show_uri_on_window (GTK_WINDOW(GLOBALS->mainwindow), url, GDK_CURRENT_TIME, &err);
#else
retval = gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (GLOBALS->mainwindow)), url, GDK_CURRENT_TIME, &err);
#endif
if (!retval)
{
ui_dialog_msg_infoerror(GTK_WINDOW(GLOBALS->mainwindow), GTK_MESSAGE_ERROR,
_("Browser error."),
_("Could not display the URL '%s'"),
url
);
}
if(err != NULL)
{
g_print ("%s\n", err->message);
g_error_free (err);
}
return retval;
}
#endif
gboolean
homebank_util_url_show (const gchar *url)
{
if(url == NULL)
return FALSE;
#ifdef G_OS_WIN32
return homebank_util_url_show_win32 (url);
#else
return homebank_util_url_show_unix (url);
#endif
}
/* = = = = = = = = = = = = = = = = = = = = */
/* lastopenedfiles */
/*
** load lastopenedfiles from homedir/.homebank
*/
gchar *homebank_lastopenedfiles_load(void)
{
GKeyFile *keyfile;
gchar *group, *filename, *tmpfilename;
gchar *lastfilename = NULL;
GError *error = NULL;
DB( g_print("\n[homebank] lastopenedfiles load\n") );
keyfile = g_key_file_new();
if(keyfile)
{
filename = g_build_filename(homebank_app_get_config_dir(), "lastopenedfiles", NULL );
if(g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
{
group = "HomeBank";
if(g_key_file_has_key(keyfile, group, "LastOpenedFile", NULL))
{
tmpfilename = g_key_file_get_string (keyfile, group, "LastOpenedFile", NULL);
// #593082
if (g_file_test (tmpfilename, G_FILE_TEST_EXISTS) != FALSE)
{
lastfilename = tmpfilename;
}
}
}
if( error )
{
g_print("failed: %s\n", error->message);
g_error_free (error);
}
g_free(filename);
g_key_file_free (keyfile);
}
return lastfilename;
}
/*
** save lastopenedfiles to homedir/.homebank (HB_DATA_PATH)
*/
gboolean homebank_lastopenedfiles_save(void)
{
GKeyFile *keyfile;
gboolean retval = FALSE;
gchar *group, *filename;
gsize length;
GError *error = NULL;
DB( g_print("\n[homebank] lastopenedfiles save\n") );
if( GLOBALS->xhb_filepath != NULL )
{
//don't save bakup files
if( hbfile_file_isbackup(GLOBALS->xhb_filepath) == FALSE )
{
keyfile = g_key_file_new();
if(keyfile )
{
DB( g_print(" - saving '%s'\n", GLOBALS->xhb_filepath) );
group = "HomeBank";
g_key_file_set_string (keyfile, group, "LastOpenedFile", GLOBALS->xhb_filepath);
gchar *contents = g_key_file_to_data( keyfile, &length, NULL);
//DB( g_print(" keyfile:\n%s\nlen=%d\n", contents, length) );
filename = g_build_filename(homebank_app_get_config_dir(), "lastopenedfiles", NULL );
g_file_set_contents(filename, contents, length, &error);
g_free(filename);
if( error )
{
g_print("failed: %s\n", error->message);
g_error_free (error);
}
g_free(contents);
g_key_file_free (keyfile);
}
}
}
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = */
/* Main homebank */
#ifdef G_OS_WIN32
static GtkCssProvider *provider;
static void
homebank_theme_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
if (pspec == NULL || g_str_equal (pspec->name, "gtk-theme-name"))
{
gchar *theme;
GdkScreen *screen;
g_object_get (settings, "gtk-theme-name", &theme, NULL);
screen = gdk_screen_get_default ();
DB( g_print("theme %s\n", theme) );
if (g_str_equal (theme, "gtk-win32"))
{
if (provider == NULL)
{
gchar *filename;
filename = g_build_filename(homebank_app_get_datas_dir(), "homebank-gtk-win32.css", NULL );
DB( g_print("tweak file %s\n", filename) );
if( g_file_test(filename, G_FILE_TEST_EXISTS) )
{
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_path (provider, filename, NULL);
}
g_free (filename);
}
if(provider != NULL)
{
DB( g_print(" assign provider %p to screen %p\n", provider, screen) );
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}
else if (provider != NULL)
{
gtk_style_context_remove_provider_for_screen (screen,
GTK_STYLE_PROVIDER (provider));
g_clear_object (&provider);
}
g_free (theme);
}
}
static void
homebank_setup_theme_extensions (void)
{
GtkSettings *settings;
settings = gtk_settings_get_default ();
provider = NULL;
g_signal_connect (settings, "notify", G_CALLBACK (homebank_theme_changed), NULL);
homebank_theme_changed (settings, NULL, NULL);
}
#endif
static void
homebank_icon_theme_setup()
{
DB( g_print("\n[homebank] icon_theme_setup\n") );
//TODO: never used
GLOBALS->icontheme = gtk_icon_theme_get_default();
DB( g_print(" - prepend theme search path: %s\n", homebank_app_get_pixmaps_dir()) );
gtk_icon_theme_prepend_search_path (GLOBALS->icontheme, homebank_app_get_pixmaps_dir());
//DB( g_print(" - append theme search path: %s\n", homebank_app_get_pixmaps_dir()) );
//gtk_icon_theme_append_search_path (GLOBALS->icontheme, homebank_app_get_pixmaps_dir());
#if MYDEBUG == 1
GtkIconTheme *ic = gtk_icon_theme_get_default();
guint i;
gchar **paths;
DB( g_print(" - get default icon theme\n") );
gtk_icon_theme_get_search_path(ic, &paths, NULL);
for(i=0;i PREFER_LIGHT)
color_scheme = DEFAULT;
GLOBALS->color_scheme = color_scheme;
DB( g_print(" set color-scheme: %d\n", color_scheme) );
homebank_pref_apply_scheme();
}
static void
_settings_portal_changed_cb (GDBusProxy *proxy,
const char *sender_name,
const char *signal_name,
GVariant *parameters,
gpointer *user_data)
{
const char *namespace;
const char *name;
g_autoptr (GVariant) value = NULL;
if (g_strcmp0 (signal_name, "SettingChanged"))
return;
g_variant_get (parameters, "(&s&sv)", &namespace, &name, &value);
if (g_strcmp0 (namespace, "org.freedesktop.appearance") ||
g_strcmp0 (name, "color-scheme"))
return;
_color_scheme_set (value);
}
static gboolean
_color_scheme_read (GDBusProxy *proxy,
GVariant **out)
{
g_autoptr (GError) error = NULL;
g_autoptr (GVariant) ret = NULL;
g_autoptr (GVariant) child = NULL;
ret = g_dbus_proxy_call_sync (proxy,
"Read",
g_variant_new ("(ss)",
"org.freedesktop.appearance",
"color-scheme"),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (error) {
if (error->domain == G_DBUS_ERROR &&
error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) {
g_debug ("Portal not found: %s", error->message);
return FALSE;
}
if (error->domain == G_DBUS_ERROR &&
error->code == G_DBUS_ERROR_UNKNOWN_METHOD) {
g_debug ("Portal doesn't provide settings: %s", error->message);
return FALSE;
}
g_critical ("Couldn't read the color-scheme setting: %s", error->message);
return FALSE;
}
g_variant_get (ret, "(v)", &child);
g_variant_get (child, "v", out);
return TRUE;
}
static void
init_portal (void)
{
g_autoptr (GError) error = NULL;
g_autoptr (GVariant) value = NULL;
GLOBALS->settings_portal = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings",
NULL,
&error);
if (error) {
g_debug ("Settings portal not found: %s", error->message);
return;
}
if (!_color_scheme_read (GLOBALS->settings_portal, &value))
return;
_color_scheme_set (value);
g_signal_connect (GLOBALS->settings_portal, "g-signal",
G_CALLBACK (_settings_portal_changed_cb), NULL);
}
#endif
/* = = = = = = = = = = = = = = = = = = = = */
const gchar *
homebank_app_get_config_dir (void)
{
return config_dir;
}
const gchar *
homebank_app_get_images_dir (void)
{
return images_dir;
}
const gchar *
homebank_app_get_pixmaps_dir (void)
{
return pixmaps_dir;
}
const gchar *
homebank_app_get_locale_dir (void)
{
return locale_dir;
}
const gchar *
homebank_app_get_help_dir (void)
{
return help_dir;
}
const gchar *
homebank_app_get_datas_dir (void)
{
return datas_dir;
}
/* build package paths at runtime */
static void
build_package_paths (void)
{
DB( g_print("\n[homebank] build_package_paths\n") );
#ifdef G_OS_WIN32
gchar *prefix;
prefix = g_win32_get_package_installation_directory_of_module (NULL);
locale_dir = g_build_filename (prefix, "share", "locale", NULL);
images_dir = g_build_filename (prefix, "share", PACKAGE, "images", NULL);
pixmaps_dir = g_build_filename (prefix, "share", PACKAGE, "icons", NULL);
help_dir = g_build_filename (prefix, "share", PACKAGE, "help", NULL);
datas_dir = g_build_filename (prefix, "share", PACKAGE, "datas", NULL);
#ifdef PORTABLE_APP
DB( g_print(" - app is portable under windows\n") );
config_dir = g_build_filename(prefix, "config", NULL);
#else
config_dir = g_build_filename(g_get_user_config_dir(), HB_DATA_PATH, NULL);
#endif
g_free (prefix);
#else
locale_dir = g_build_filename (DATA_DIR, "locale", NULL);
images_dir = g_build_filename (SHARE_DIR, "images", NULL);
pixmaps_dir = g_build_filename (DATA_DIR, PACKAGE, "icons", NULL);
help_dir = g_build_filename (DATA_DIR, PACKAGE, "help", NULL);
datas_dir = g_build_filename (DATA_DIR, PACKAGE, "datas", NULL);
config_dir = g_build_filename(g_get_user_config_dir(), HB_DATA_PATH, NULL);
//#870023 Ubuntu packages the help files in "/usr/share/doc/homebank-data/help/" for some strange reason
if(! g_file_test(help_dir, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
{
g_free (help_dir);
help_dir = g_build_filename ("/usr", "share", "doc", "homebank-data", "help", NULL);
}
#endif
DB( g_print(" - config_dir : %s\n", config_dir) );
DB( g_print(" - images_dir : %s\n", images_dir) );
DB( g_print(" - pixmaps_dir: %s\n", pixmaps_dir) );
DB( g_print(" - locale_dir : %s\n", locale_dir) );
DB( g_print(" - help_dir : %s\n", help_dir) );
DB( g_print(" - datas_dir : %s\n", datas_dir) );
}
guint32 homebank_app_date_get_julian(void)
{
GDate *date;
//init global default value
date = g_date_new();
g_date_set_time_t(date, time(NULL));
GLOBALS->today = g_date_get_julian(date);
g_date_free(date);
return GLOBALS->today;
}
static gboolean homebank_check_app_dir_migrate_file(gchar *srcdir, gchar *dstdir, gchar *filename)
{
gchar *srcpath;
gchar *dstpath;
gchar *buffer;
gsize length;
//GError *error = NULL;
gboolean retval = FALSE;
DB( g_print("\n[homebank] check_app_dir_migrate_file\n") );
srcpath = g_build_filename(srcdir, filename, NULL );
dstpath = g_build_filename(dstdir, filename, NULL );
if (g_file_get_contents (srcpath, &buffer, &length, NULL))
{
if(g_file_set_contents(dstpath, buffer, length, NULL))
{
//g_print("sould delete %s\n", srcpath);
g_remove(srcpath);
retval = TRUE;
}
}
g_free(dstpath);
g_free(srcpath);
return retval;
}
/*
* check/create user home directory for .homebank (HB_DATA_PATH) directory
*/
static void homebank_check_app_dir()
{
gchar *homedir;
const gchar *configdir;
gboolean exists;
DB( g_print("\n[homebank] check_app_dir\n") );
/* check if /.config exist */
#ifndef G_OS_WIN32
configdir = g_get_user_config_dir();
DB( g_print(" - check '%s' exists\n", configdir) );
if(!g_file_test(configdir, G_FILE_TEST_IS_DIR))
{
DB( g_print(" - creating dir\n") );
g_mkdir(configdir, 0755);
}
#endif
/* check for XDG .config/homebank */
configdir = homebank_app_get_config_dir();
DB( g_print(" - config_dir is: '%s'\n", configdir) );
exists = g_file_test(configdir, G_FILE_TEST_IS_DIR);
if(exists)
{
/* just update folder security */
DB( g_print(" - chmod 0700\n") );
g_chmod(configdir, 0700);
GLOBALS->first_run = FALSE;
}
else
{
/* create the config dir */
DB( g_print(" - create config_dir\n") );
g_mkdir(configdir, 0755);
g_chmod(configdir, 0700);
/* any old homedir configuration out there ? */
homedir = g_build_filename(g_get_home_dir (), ".homebank", NULL );
DB( g_print(" - homedir is: '%s'\n", homedir) );
exists = g_file_test(homedir, G_FILE_TEST_IS_DIR);
if(exists)
{
gboolean f1, f2;
/* we must do the migration properly */
DB( g_print(" - migrate old 2 files\n") );
f1 = homebank_check_app_dir_migrate_file(homedir, config_dir, "preferences");
f2 = homebank_check_app_dir_migrate_file(homedir, config_dir, "lastopenedfiles");
if(f1 && f2)
{
DB( g_print(" - removing old dir\n") );
g_rmdir(homedir);
}
}
g_free(homedir);
GLOBALS->first_run = TRUE;
}
}
static void free_package_paths(void)
{
DB( g_print("\n[homebank] free package paths\n") );
g_free (config_dir);
g_free (images_dir);
g_free (pixmaps_dir);
g_free (locale_dir);
g_free (help_dir);
}
/*
** application cleanup: icons, GList, memory
*/
static void homebank_cleanup(void)
{
DB( g_print("\n[homebank] app cleanup\n") );
//v3.4 save windows size/position
homebank_pref_save();
hbfile_cleanup(TRUE);
}
/*
** application setup: icons, GList, memory
*/
static gboolean homebank_setup()
{
DB( g_print("\n[homebank] app setup\n") );
// check homedir for .homebank dir
homebank_check_app_dir();
homebank_pref_setdefault();
homebank_pref_load();
homebank_pref_apply();
hbfile_setup(TRUE);
homebank_icon_theme_setup();
#ifdef G_OS_WIN32
homebank_setup_theme_extensions();
#endif
homebank_app_date_get_julian();
#if MYDEBUG == 1
g_print(" - user_name: %s\n", g_get_user_name ());
g_print(" - real_name: %s\n", g_get_real_name ());
g_print(" - user_cache_dir: %s\n", g_get_user_cache_dir());
g_print(" - user_data_dir: %s\n", g_get_user_data_dir ());
g_print(" - user_config_dir: %s\n", g_get_user_config_dir ());
//g_print(" - system_data_dirs: %s\n", g_get_system_data_dirs ());
//g_print(" - system_config_dirs: %s\n", g_get_system_config_dirs ());
g_print(" - home_dir: %s\n", g_get_home_dir ());
g_print(" - tmp_dir: %s\n", g_get_tmp_dir ());
g_print(" - current_dir: %s\n", g_get_current_dir ());
#endif
return TRUE;
}
/* = = = = = = = = = = = = = = = = = = = = */
/* Main homebank */
static void
homebank_app_splash_hide(GtkWidget *splash)
{
DB( g_print("\n[homebank] splash hide\n") );
if( PREFS->showsplash == TRUE )
{
gtk_widget_hide (splash);
gtk_window_destroy (GTK_WINDOW(splash));
/* make sure splash is gone */
hb_window_run_pending();
}
}
static GtkWidget *
homebank_app_splash_show(void)
{
GtkWidget *window = NULL;
GtkWidget *frame, *vbox, *image;
//gchar *ver_string, *markup, *version;
gchar *pathfilename;
DB( g_print("\n[homebank] splash show \n") );
if( PREFS->showsplash == TRUE )
{
window = gtk_window_new(GTK_WINDOW_POPUP); //TOPLEVEL DONT WORK
gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "HomeBank");
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
pathfilename = g_build_filename(homebank_app_get_images_dir(), "splash.png", NULL);
image = gtk_image_new_from_file((const gchar *)pathfilename);
g_free(pathfilename);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
gtk_window_set_child(GTK_WINDOW(window), frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_frame_set_child(GTK_FRAME(frame), vbox);
/*
ver_string = g_strdup_printf(_("Version: HomeBank-%s"), VERSION);
version = gtk_label_new(NULL);
markup = g_markup_printf_escaped(MARKUP_STRING, ver_string);
gtk_label_set_markup(GTK_LABEL(version), markup);
g_free(markup);
g_free(ver_string);
*/
gtk_box_prepend (GTK_BOX (vbox), image);
gtk_window_set_auto_startup_notification (FALSE);
gtk_widget_show_all (window);
gtk_window_set_auto_startup_notification (TRUE);
// make sure splash is up
hb_window_run_pending();
DB( g_print(" splash screen %p\n", gtk_window_get_screen(GTK_WINDOW(window))) );
g_usleep( G_USEC_PER_SEC * 1 );
}
return window;
}
static void
homebank_init_i18n (void)
{
/* We may change the locale later if the user specifies a language
* in the gimprc file. Here we are just initializing the locale
* according to the environment variables and set up the paths to
* the message catalogs.
*/
setlocale (LC_ALL, "");
//#1842292 as indicated in gtk+ win32 gtk_get_localedir [1], bindtextdomain() is not
// UTF-8 aware on win32, so it needs a filename in locale encoding
#ifdef G_OS_WIN32
gchar *localedir = g_win32_locale_filename_from_utf8 (homebank_app_get_locale_dir ());
bindtextdomain (GETTEXT_PACKAGE, localedir);
g_free(localedir);
#else
bindtextdomain (GETTEXT_PACKAGE, homebank_app_get_locale_dir ());
#endif
//#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
//#endif
textdomain (GETTEXT_PACKAGE);
/*#ifdef G_OS_WIN32
gchar *wl = g_win32_getlocale ();
DB( g_print(" - win32 locale is '%s'\n", wl) );
g_free(wl);
#endif*/
}
GtkWindow *
homebank_app_find_window(gint needle_key)
{
GList *l = gtk_application_get_windows(GLOBALS->application);
GtkWindow *window = NULL;
gint key;
DB( g_print("\n[homebank] app find window %d\n", needle_key) );
g_return_val_if_fail(needle_key != 0, NULL);
while (l != NULL)
{
GtkWindow *tmpwin = l->data;
key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tmpwin), "key"));
DB( g_print(" window: %p: key=%d '%s' \n", tmpwin, key, gtk_window_get_title(tmpwin)) );
if( key == needle_key )
{
DB( g_print(" >found\n") );
window = tmpwin;
break;
}
l = g_list_next(l);
}
//no need to free glist
return window;
}
//#2080864 handle version
static gint
homebank_app_handle_local_options (GApplication *application,
GVariantDict *options)
{
if (g_variant_dict_contains (options, "version"))
{
g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
return 0;
}
return -1;
}
static gint
homebank_app_commandline(GApplication *application, GApplicationCommandLine *cmdline, gpointer user_data)
{
GVariantDict *options;
gchar **remaining_args;
DB( g_print("\n[homebank] app commandline\n") );
options = g_application_command_line_get_options_dict (cmdline);
/* Parse filenames */
if (g_variant_dict_lookup (options, G_OPTION_REMAINING, "^a&ay", &remaining_args))
{
guint i = g_strv_length(remaining_args);
if( i > 0 )
{
arg_filepath = g_strdup(remaining_args[0]);
DB( g_print(" %s\n", arg_filepath) );
}
/*for (i = 0; remaining_args[i]; i++)
{
g_print(" args[%d] ='%s'\n", i, remaining_args[i]);
}*/
g_free (remaining_args);
}
g_application_activate (application);
return 0;
}
static void
homebank_app_activate (GtkApplication *app, gpointer user_data)
{
GtkWidget *mainwin;
GtkWidget *splash = NULL;
DB( g_print("\n[homebank] app activate\n") );
//check if already a window opened
if( GLOBALS->mainwindow != NULL )
{
//TODO change here to enable multiple ?
gtk_window_present (GTK_WINDOW (GLOBALS->mainwindow));
}
else
//if( homebank_app_commandline(app) == TRUE )
{
/* Pass NULL here since we parsed the gtk+ args already...
* from this point all we need a DISPLAY variable to be set.
*/
//gtk_init (NULL, NULL);
//todo: sanity check gtk version here ?
if( homebank_setup() )
{
GLOBALS->application = app;
splash = homebank_app_splash_show();
// change language to the user
#if HB_PRIV_FORCE_ENUS == FALSE
language_init (PREFS->language);
#else
// but not for unstable, to always get native app text
language_init ("en-US");
#endif
g_set_application_name (APPLICATION_NAME);
gtk_window_set_default_icon_name ("homebank");
DB( g_print(" app creating window\n" ) );
mainwin = (GtkWidget *)ui_wallet_window_new (NULL);
if(mainwin)
{
gchar *rawfilepath = NULL;
gtk_application_add_window(app, GTK_WINDOW(mainwin));
// make sure mainwin is up
hb_window_run_pending();
DB( g_print(" mainwin screen %p\n", gtk_window_get_screen(GTK_WINDOW(mainwin))) );
DB( g_print(" - app win should be visible\n" ) );
//g_usleep( G_USEC_PER_SEC * 2 );
homebank_app_splash_hide(splash);
//priority here:
// - command line file
// - welcome dialog
// - last opened file, if welcome dialog was not opened
if( arg_filepath != NULL )
{
DB( g_print(" command line open '%s'\n", arg_filepath ) );
rawfilepath = g_strdup(arg_filepath);
g_free (arg_filepath);
ui_wallet_open_check(mainwin, rawfilepath);
}
else
if( PREFS->showwelcome )
{
ui_wallet_update(mainwin, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
ui_wallet_action_help_welcome();
}
else
if( PREFS->loadlast )
{
rawfilepath = homebank_lastopenedfiles_load();
ui_wallet_open_check(mainwin, rawfilepath);
}
else
/* update the mainwin display */
ui_wallet_update(mainwin, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
//#2121204 check backup dir is reachable
#ifndef PORTABLE_APP
homebank_util_check_backup();
#endif
//5.7 test
//homebank_util_check_update(mainwin);
/* -- start: hack here to generate a big file -- */
/* -- end: hack here to generate a big file -- */
DB( g_print(" app gtk_main()\n" ) );
gtk_main ();
//DB( g_print(" app call destroy mainwin\n" ) );
//gtk_window_destroy (GTK_WINDOW(mainwin));
}
}
homebank_cleanup();
}
}
static void
homebank_app_open (GApplication *application,
GFile **files,
gint n_files,
const gchar *hint)
{
DB( g_print("\n[homebank] app open\n") );
}
static void
homebank_app_startup (GApplication *application)
{
DB( g_print("\n[homebank] app startup\n") );
//#2043886
//#ifdef PORTABLE_APP
//g_object_set (gtk_settings_get_default (), "gtk-recent-files-enabled", FALSE, NULL);
//#endif
GLOBALS->color_scheme = DEFAULT;
#ifdef G_OS_UNIX
init_portal();
#endif
}
static void
homebank_app_shutdown (GApplication *app)
{
DB( g_print("\n[homebank] app shutdown\n") );
}
//gtk4 future application id
// fr.free.mdoyen.HomeBank
//g_set_prgname("fr.free.mdoyen.HomeBank");
int
main (int argc, char *argv[])
{
GtkApplication *app;
int exit_code = EXIT_FAILURE;
DB( g_print("\n--------------------------------" ) );
DB( g_print("\n[homebank] main starting\n") );
GLOBALS = g_malloc0(sizeof(struct HomeBank));
PREFS = g_malloc0(sizeof(struct Preferences));
if( GLOBALS != NULL && PREFS != NULL )
{
build_package_paths();
homebank_init_i18n();
app = gtk_application_new ("fr.free.mdoyen.HomeBank", G_APPLICATION_HANDLES_COMMAND_LINE);
//app = gtk_application_new ("fr.free.mdoyen.HomeBank", G_APPLICATION_FLAGS_NONE);
g_application_add_main_option_entries (G_APPLICATION (app), option_entries);
g_signal_connect (app, "startup" , G_CALLBACK (homebank_app_startup) , NULL);
g_signal_connect (app, "command-line" , G_CALLBACK (homebank_app_commandline) , NULL);
g_signal_connect (app, "handle-local-options" , G_CALLBACK (homebank_app_handle_local_options) , NULL);
g_signal_connect (app, "activate" , G_CALLBACK (homebank_app_activate) , NULL);
g_signal_connect (app, "open" , G_CALLBACK (homebank_app_open) , NULL);
g_signal_connect (app, "shutdown" , G_CALLBACK (homebank_app_shutdown) , NULL);
exit_code = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
free_package_paths();
exit_code = EXIT_SUCCESS;
}
/* free our global datas */
if( PREFS )
{
homebank_pref_free();
g_free(PREFS);
}
if( GLOBALS )
{
g_free(GLOBALS);
}
return exit_code;
}
#ifdef G_OS_WIN32
/* In case we build this as a windows application */
#ifdef __GNUC__
#define _stdcall __attribute__((stdcall))
#endif
int _stdcall
WinMain (struct HINSTANCE__ *hInstance,
struct HINSTANCE__ *hPrevInstance,
char *lpszCmdLine,
int nCmdShow)
{
return main (__argc, __argv);
}
#endif
homebank-5.9.7/src/list-scheduled.h 0000644 0001750 0001750 00000003563 14736461415 016505 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LIST_SCHEDULED__H__
#define __LIST_SCHEDULED__H__
enum
{
LIST_SCH_TYPE_MANAGE = 0,
LIST_SCH_TYPE_DISPLAY
};
enum
{
LST_DSPUPC_DATAS, //shared
LST_DSPUPC_NEXT,
LST_DSPUPC_MEMO,
LST_DSPUPC_EXPENSE,
LST_DSPUPC_INCOME,
LST_DSPUPC_NB_LATE,
NUM_LST_DSPUPC
};
// UID are used to save column position
enum
{
COL_SCH_UID_LATE = 1,
COL_SCH_UID_STILL,
//-- last fixed
COL_SCH_UID_NEXTDATE = 9,
//-- allow reorder start here --
COL_SCH_UID_PAYNUMBER = 10,
COL_SCH_UID_PAYEE,
COL_SCH_UID_CATEGORY,
COL_SCH_UID_CLR,
COL_SCH_UID_AMOUNT,
COL_SCH_UID_EXPENSE,
COL_SCH_UID_INCOME,
COL_SCH_UID_MEMO,
COL_SCH_UID_ACCOUNT
};
#define NUM_COL_SCH_UID 9
struct lst_sch_data
{
GtkWidget *treeview;
GtkWidget *menu;
};
gchar *ui_arc_listview_get_freq_label(gint index);
void ui_arc_listview_widget_columns_order_load(GtkTreeView *treeview);
void ui_arc_listview_widget_columns_order_save(GtkTreeView *treeview);
GString *lst_sch_widget_to_string(GtkTreeView *treeview, ToStringMode mode);
GtkWidget *lst_sch_widget_new(gint listtype);
GtkWidget *ui_arc_listview_widget_new(void);
#endif
homebank-5.9.7/src/hb-import-csv.c 0000664 0001750 0001750 00000021613 15032564466 016257 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-import.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
// 0:date; 1:paymode; 2:info; 3:payee, 4:wording; 5:amount; 6:category; 7:tags
static gint csvtype[8] = {
CSV_DATE, CSV_INT, CSV_STRING, CSV_STRING, CSV_STRING, CSV_DOUBLE, CSV_STRING, CSV_STRING
};
//5.9.2 multiple enclosed were wrong decoded
static gchar *hb_csv_strndup (gchar *str, gsize n)
{
gchar *new_str = NULL;
if (str)
{
gboolean enclosed = FALSE;
//test for enclosed
if( n >= 2)
{
if( str[0]=='\"' && str[n-1]=='\"' )
enclosed = TRUE;
}
if( !enclosed )
new_str = g_strndup(str, n);
else
{
gchar *s, *d;
gsize len = n-2;
//duplicate without enclosed + decode double quote
new_str = g_new (gchar, len+1);
s = str+1;
d = new_str;
while( *s && len > 0 )
{
//todo: ? replace & < > ' "
*d++ = *s;
if( *s=='\"' && s[1]=='\"' )
{
s++;
len--;
}
s++;
len--;
}
*d = 0;
}
}
return new_str;
}
static gchar *hb_csv_find_delimiter(gchar *string, gchar delimiter)
{
gchar *s = string;
gboolean enclosed = FALSE;
while( *s != '\0' )
{
if( (*s == delimiter) && (enclosed == FALSE) )
break;
if( *s == '\"' )
{
enclosed = !enclosed;
}
s++;
}
return s;
}
static gboolean hb_csv_row_valid(GString *node, guint n_line, gchar **str_array, guint nbcolumns, gint *csvtype)
{
gboolean valid = TRUE;
guint n_arrcol, i;
extern int errno;
#if MYDEBUG == 1
gchar *type[5] = { "string", "date", "int", "double" };
gint lasttype;
#endif
//DB( g_print("\n[import-csv] row valid\n") );
n_arrcol = g_strv_length( str_array );
if( n_arrcol != nbcolumns )
{
valid = FALSE;
g_string_append_printf(node, "line %d: %d column(s) are missing\n", n_line, nbcolumns - n_arrcol );
DB( g_print(" >fail: %d columns\n", n_arrcol ) );
goto csvend;
}
for(i=0;ifail: column %d, type: %s\n", i, type[lasttype]) );
goto csvend;
}
switch( csvtype[i] )
{
case CSV_DATE:
valid = hb_string_isdate(str_array[i]);
break;
case CSV_STRING:
valid = hb_string_isprint(str_array[i]);
break;
case CSV_INT:
valid = hb_string_isdigit(str_array[i]);
break;
case CSV_DOUBLE :
//todo: use strtod (to take care or . or ,)
g_ascii_strtod(str_array[i], NULL);
//todo: see this errno
if( errno )
{
DB( g_print("errno: %d\n", errno) );
valid = FALSE;
}
break;
}
}
csvend:
return valid;
}
static gchar **hb_csv_row_get(gchar *string, gchar delimiter, gint max_tokens)
{
GSList *string_list = NULL, *slist;
gchar **str_array, *s;
guint n = 0;
gchar *remainder;
g_return_val_if_fail (string != NULL, NULL);
g_return_val_if_fail (delimiter != '\0', NULL);
if (max_tokens < 1)
max_tokens = G_MAXINT;
remainder = string;
s = hb_csv_find_delimiter (remainder, delimiter);
if (s)
{
while (--max_tokens && s && *s != '\0')
{
gsize len;
len = s - remainder;
string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
DB( g_print(" array[%d] = '%s'\n", n, (gchar *)string_list->data) );
n++;
remainder = s + 1;
s = hb_csv_find_delimiter (remainder, delimiter);
}
}
if (*string)
{
gsize len;
len = s - remainder;
string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
DB( g_print(" array[%d] = '%s'\n", n, (gchar *)string_list->data) );
n++;
}
str_array = g_new (gchar*, n + 1);
str_array[n--] = NULL;
for (slist = string_list; slist; slist = slist->next)
str_array[n--] = slist->data;
g_slist_free (string_list);
return str_array;
}
static gchar hb_csv_get_separator(void)
{
static const gchar sep[] = PRF_DTEX_CSVSEP_BUFFER;
return sep[PREFS->dtex_csvsep];
}
// 5.9.2: we fast check columns+enclosed and no more type
gboolean hb_csv_test_line(gchar *rawline)
{
gchar sep;
gchar *s;
gboolean isvalid = FALSE;
guint n_column = 1;
guint n_enclosed = 0;
gboolean enclosed = FALSE;
sep = hb_csv_get_separator();
s = rawline;
while( *s != '\0' )
{
if( *s == sep && (enclosed == FALSE) )
n_column++;
else
if( *s == '\"' )
{
enclosed = !enclosed;
n_enclosed++;
}
s++;
}
if( (n_column == 8) && ((n_enclosed % 2) == 0) )
isvalid = TRUE;
DB( g_print(" >%d || n_col:%d n_enclosed:%d\n", isvalid, n_column, n_enclosed) );
return isvalid;
}
GList *homebank_csv_import(ImportContext *ictx, GenFile *genfile)
{
GIOChannel *io;
//GList *list = NULL;
DB( g_print("\n[import-csv] homebank csv\n") );
io = g_io_channel_new_file(genfile->filepath, "r", NULL);
if(io != NULL)
{
GString *node;
gchar *tmpstr;
gchar sep;
gint io_stat;
gboolean isvalid;
GenAcc *newacc;
GError *err = NULL;
node = g_string_new(NULL);
newacc = hb_import_gen_acc_get_next(ictx, FILETYPE_CSV_HB, NULL, NULL);
if( genfile->encoding != NULL )
{
g_io_channel_set_encoding(io, genfile->encoding, NULL);
}
sep = hb_csv_get_separator();
genfile->n_error = 0;
for(guint n_line = 1;;n_line++)
{
gsize length, eol_pos;
io_stat = g_io_channel_read_line(io, &tmpstr, &length, &eol_pos, &err);
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_ERROR )
{
DB (g_print(" + ERROR %s\n",err->message));
break;
}
if( io_stat == G_IO_STATUS_NORMAL)
{
gchar **str_array;
//test strip crlf
if( eol_pos > 0 )
tmpstr[eol_pos] = 0;
DB( g_print("\n--------\nreadline %d: [%s] %ld %ld\n", n_line, tmpstr, length, eol_pos) );
//hex_dump(tmpstr, (guint)length+2);
//#1844892 wish: detect/skip UTF-8 BOM (Excel CSV files)
if(n_line == 1)
hb_string_strip_utf8_bom(tmpstr);
hb_string_strip_crlf(tmpstr);
str_array = hb_csv_row_get(tmpstr, sep, 8);
isvalid = hb_csv_row_valid(node, n_line, str_array, 8, csvtype);
if( !isvalid )
{
DB( g_print("csv parse: line %d, invalid column count or data", n_line) );
genfile->n_error++;
}
else
{
GenTxn *newope = da_gen_txn_malloc();
DB( g_print(" adding txn\n" ) );
//5.8 #2063416 same date txn
newope->row = n_line;
/* convert to generic transaction */
newope->date = g_strdup(str_array[0]);
newope->paymode = atoi(str_array[1]);
//todo: reinforce controls here
// csv file are standalone, so no way to link a target txn
//added 5.1.8 forbid to import 5=internal xfer
if(newope->paymode == OLDPAYMODE_INTXFER)
newope->paymode = PAYMODE_XFER;
newope->rawnumber = g_strdup(str_array[2]);
newope->rawpayee = g_strdup(g_strstrip(str_array[3]));
newope->rawmemo = g_strdup(str_array[4]);
newope->amount = hb_qif_parser_get_amount(str_array[5]);
newope->category = g_strdup(g_strstrip(str_array[6]));
newope->tags = g_strdup(str_array[7]);
newope->account = g_strdup(newacc->name);
/* todo: move this eval date valid */
//guint32 juliantmp = hb_date_get_julian(str_array[0], ictx->datefmt);
///if( juliantmp == 0 )
// ictx->cnt_err_date++;
/*
DB( g_print(" storing %s : %s : %s :%s : %s : %s : %s : %s\n",
str_array[0], str_array[1], str_array[2],
str_array[3], str_array[4], str_array[5],
str_array[6], str_array[7]
) );
*/
da_gen_txn_append(ictx, newope);
g_strfreev (str_array);
}
g_free(tmpstr);
}
}
g_io_channel_unref (io);
//todo: tmp test
if( genfile->n_error > 0 )
{
genfile->errlog = g_string_free(node, FALSE);
}
/*
ui_dialog_msg_infoerror(data->window, error > 0 ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO,
_("Transaction CSV import result"),
_("%d transactions inserted\n%d errors in the file"),
count, error);
*/
}
return ictx->gen_lst_txn;
}
homebank-5.9.7/src/ui-payee.c 0000644 0001750 0001750 00000160624 15120542600 015267 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkWidget *
container_get_nth(GtkBox *container, gint nth)
{
GList *lchild, *list;
GtkWidget *child;
if(!GTK_IS_CONTAINER(container))
return NULL;
lchild = list = gtk_container_get_children (GTK_CONTAINER(container));
child = g_list_nth_data (list, nth);
g_list_free(lchild);
return child;
}
GtkWidget *
ui_pay_entry_popover_get_entry(GtkBox *box)
{
return container_get_nth(box, 0);
}
Payee
*ui_pay_entry_popover_get(GtkBox *box)
{
GtkWidget *entry;
gchar *name;
Payee *item = NULL;
DB( g_print ("ui_pay_entry_popover_get()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (entry));
item = da_pay_get_by_name(name);
}
return item;
}
guint32
ui_pay_entry_popover_get_key_add_new(GtkBox *box)
{
Payee *item = ui_pay_entry_popover_get(box);
GtkWidget *entry;
GtkTreeModel *store;
if( item == NULL )
{
/* automatic add */
//todo: check prefs + ask the user here 1st time
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
item = da_pay_malloc();
item->name = g_strdup(gtk_entry_get_text(GTK_ENTRY (entry)));
da_pay_append(item);
store = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
if( store )
gtk_list_store_insert_with_values(GTK_LIST_STORE(store), NULL, -1,
0, item->name, -1);
}
}
return item->key;
}
guint32
ui_pay_entry_popover_get_key(GtkBox *box)
{
Payee *item = ui_pay_entry_popover_get(box);
return ((item != NULL) ? item->key : 0);
}
void
ui_pay_entry_popover_set_active(GtkBox *box, guint32 key)
{
GtkWidget *entry;
DB( g_print ("ui_pay_comboboxentry_set_active()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
Payee *item = da_pay_get(key);
hbtk_entry_set_text(GTK_ENTRY(entry), item != NULL ? item->name : "");
}
}
static void
ui_pay_entry_popover_cb_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkEntry *entry = user_data;
if( GTK_IS_ENTRY(entry) )
{
treeselection = gtk_tree_view_get_selection(tree_view);
if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
{
gchar *item;
gtk_tree_model_get(model, &iter, 0, &item, -1);
gtk_entry_set_text(GTK_ENTRY(user_data), item);
g_free(item);
}
}
}
static void
ui_pay_entry_popover_populate(GtkListStore *store)
{
GHashTableIter hiter;
gpointer key, value;
g_hash_table_iter_init (&hiter, GLOBALS->h_pay);
while (g_hash_table_iter_next (&hiter, &key, &value))
{
Payee *item = value;
//#1826360 wish: archive payee/category to lighten the lists
if( !(item->flags & PF_HIDDEN) )
{
gtk_list_store_insert_with_values(GTK_LIST_STORE(store), NULL, -1,
0, item->name, -1);
}
}
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
static void
ui_pay_entry_popover_function (GtkEditable *editable, gpointer user_data)
{
DB( g_print("text changed to %s\n", gtk_entry_get_text(GTK_ENTRY(editable)) ) );
}
static void
ui_pay_entry_popover_cb_toggled (GtkToggleButton *togglebutton, gpointer user_data)
{
GtkWidget *entry = user_data;
GtkAllocation allocation;
GtkPopover *popover;
if(GTK_IS_ENTRY(entry))
{
gtk_widget_get_allocation (entry, &allocation);
popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(togglebutton));
if(GTK_IS_POPOVER(popover))
{
gtk_widget_set_size_request (GTK_WIDGET(popover), allocation.width + (2*SPACING_POPOVER), -1);
DB( g_print("should set width to %d\n", allocation.width + (2*SPACING_POPOVER)) );
}
}
}
static gint
ui_pay_entry_popover_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
gchar *name1, *name2;
gtk_tree_model_get(model, a, 0, &name1, -1);
gtk_tree_model_get(model, b, 0, &name2, -1);
retval = hb_string_utf8_compare(name1, name2);
g_free(name2);
g_free(name1);
return retval;
}
static gboolean
ui_pay_entry_popover_completion_func (GtkEntryCompletion *completion,
const gchar *key,
GtkTreeIter *iter,
gpointer user_data)
{
gchar *name = NULL;
gchar *normalized_string;
gchar *case_normalized_string;
gboolean ret = FALSE;
GtkTreeModel *model;
model = gtk_entry_completion_get_model (completion);
gtk_tree_model_get (model, iter,
0, &name,
-1);
if (name != NULL)
{
normalized_string = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
if (normalized_string != NULL)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
//g_print("match '%s' for '%s' ?\n", key, case_normalized_string);
//if (!strncmp (key, case_normalized_string, strlen (key)))
if (g_strstr_len (case_normalized_string, strlen (case_normalized_string), key ))
{
ret = TRUE;
// g_print(" ==> yes !\n");
}
g_free (case_normalized_string);
}
g_free (normalized_string);
}
return ret;
}
static void
ui_pay_entry_popover_destroy( GtkWidget *widget, gpointer user_data )
{
DB( g_print ("[pay entry popover] destroy\n") );
}
GtkWidget *
ui_pay_entry_popover_new(GtkWidget *label)
{
GtkWidget *mainbox, *box, *entry, *menubutton, *image, *popover, *scrollwin, *treeview;
GtkListStore *store;
GtkEntryCompletion *completion;
DB( g_print ("[pay entry popover] new\n") );
mainbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(mainbox)), GTK_STYLE_CLASS_LINKED);
entry = gtk_entry_new();
hbtk_box_prepend (GTK_BOX(mainbox), entry);
menubutton = gtk_menu_button_new ();
//data->MB_template = menubutton;
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_button_set_image(GTK_BUTTON(menubutton), image);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_LEFT );
//gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
gtk_box_prepend(GTK_BOX(mainbox), menubutton);
completion = gtk_entry_completion_new ();
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_object_unref(completion);
store = gtk_list_store_new (1,
G_TYPE_STRING
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_pay_entry_popover_compare_func, NULL, NULL);
ui_pay_entry_popover_populate(store);
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
gtk_entry_completion_set_match_func(completion, ui_pay_entry_popover_completion_func, NULL, NULL);
g_object_unref(store);
gtk_entry_completion_set_text_column (completion, 0);
gtk_widget_show_all(mainbox);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
//gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_show_all(box);
//gtk_widget_set_can_focus(GTK_WIDGET(treeview), FALSE);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new_with_attributes (NULL,
renderer,
"text",
0,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_BROWSE);
//popover = create_popover (menubutton, box, GTK_POS_BOTTOM);
popover = create_popover (menubutton, box, GTK_POS_LEFT);
//gtk_widget_set_size_request (popover, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
gtk_widget_set_vexpand(popover, TRUE);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
// connect our dispose function
g_signal_connect (entry, "destroy", G_CALLBACK (ui_pay_entry_popover_destroy), NULL);
g_signal_connect_after (entry , "changed", G_CALLBACK (ui_pay_entry_popover_function), NULL);
g_signal_connect (menubutton, "toggled", G_CALLBACK (ui_pay_entry_popover_cb_toggled), entry);
g_signal_connect (treeview, "row-activated", G_CALLBACK (ui_pay_entry_popover_cb_row_activated), entry);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_popover_popdown), popover);
#else
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_widget_hide), popover);
#endif
//g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK (ui_pay_entry_popover_cb_selection), entry);
//g_signal_connect_swapped(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK(gtk_popover_popdown), popover);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), entry);
//gtk_widget_set_size_request(comboboxentry, HB_MINWIDTH_LIST, -1);
return mainbox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint ui_pay_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gboolean toggled;
guint change = 0;
DB( g_print("(ui_pay_listview) toggle_to_filter\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_pay));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Payee *payitem;
gtk_tree_model_get (model, &iter,
LST_DEFPAY_TOGGLE, &toggled,
LST_DEFPAY_DATAS, &payitem,
-1);
DB( g_print(" payee k:%3d = %d (%s)\n", payitem->key, toggled, payitem->name) );
change += da_flt_status_pay_set(filter, payitem->key, toggled);
//payitem->flt_select = toggled;
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
return change;
}
static void
ui_pay_listview_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFPAY_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFPAY_TOGGLE, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
//g_signal_emit_by_name(gtk_tree_view_get_selection(treeview), "changed", NULL);
}
void
ui_pay_listview_quick_select(GtkTreeView *treeview, const gchar *uri)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gboolean toggle;
gint qselect = hb_clicklabel_to_int(uri);
DB( g_print("(ui_acc_listview) quick select\n") );
DB( g_print(" comboboxlink '%s' %d\n", uri, qselect) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
switch(qselect)
{
case HB_LIST_QUICK_SELECT_ALL:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFPAY_TOGGLE, TRUE, -1);
break;
case HB_LIST_QUICK_SELECT_NONE:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFPAY_TOGGLE, FALSE, -1);
break;
case HB_LIST_QUICK_SELECT_INVERT:
gtk_tree_model_get (model, &iter, LST_DEFPAY_TOGGLE, &toggle, -1);
toggle ^= 1;
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFPAY_TOGGLE, toggle, -1);
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static gint
ui_pay_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
Payee *entry1, *entry2;
gint retval = 0;
gtk_tree_model_get(model, a, LST_DEFPAY_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DEFPAY_DATAS, &entry2, -1);
switch (sortcol)
{
case LST_DEFPAY_SORT_NAME:
retval = hb_string_utf8_compare(entry1->name, entry2->name);
break;
case LST_DEFPAY_SORT_USETXN:
retval = entry1->nb_use_all - entry2->nb_use_all;
break;
case LST_DEFPAY_SORT_USECFG:
retval = (entry1->nb_use_all - entry1->nb_use_txn) - (entry2->nb_use_all - entry2->nb_use_txn);
break;
case LST_DEFPAY_SORT_DEFPAY:
retval = entry1->paymode - entry2->paymode;
break;
case LST_DEFPAY_SORT_DEFCAT:
{
Category *c1, *c2;
c1 = da_cat_get(entry1->kcat);
c2 = da_cat_get(entry2->kcat);
if( c1 != NULL && c2 != NULL )
{
retval = hb_string_utf8_compare(c1->fullname, c2->fullname);
}
}
break;
default:
g_return_val_if_reached(0);
}
return retval;
}
static void
ui_pay_listview_count_txn_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Payee *entry;
gchar buffer[256];
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
if(entry->nb_use_txn > 0)
{
g_snprintf(buffer, 256-1, "%d", entry->nb_use_txn);
g_object_set(renderer, "text", buffer, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void
ui_pay_listview_count_cfg_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Payee *entry;
gchar buffer[256];
guint use;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
use = entry->nb_use_all - entry->nb_use_txn;
if(use > 0)
{
g_snprintf(buffer, 256-1, "%d", use);
g_object_set(renderer, "text", buffer, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void
ui_pay_listview_defcat_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Payee *entry;
Category *cat;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
cat = da_cat_get(entry->kcat);
if( cat != NULL )
{
g_object_set(renderer, "text", cat->fullname, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void
ui_pay_listview_info_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Payee *entry;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
g_object_set(renderer, "icon-name", get_paymode_icon_name(entry->paymode), NULL);
}
static void
ui_pay_listview_name_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Payee *entry;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
g_object_set(renderer, "text", da_pay_get_name(entry), NULL);
}
static void
ui_pay_listview_icon_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Payee *entry;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &entry, -1);
if( entry->flags & PF_HIDDEN )
iconname = ICONNAME_HB_BUTTON_HIDE;
g_object_set(renderer, "icon-name", iconname, NULL);
}
#if MYDEBUG == 1
static void
ui_pay_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Payee *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
/* = = = = = = = = = = = = = = = = */
void
ui_pay_listview_add(GtkTreeView *treeview, Payee *item)
{
GtkTreeModel *model;
GtkTreeIter iter;
if( item->name != NULL )
{
model = gtk_tree_view_get_model(treeview);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFPAY_TOGGLE, FALSE,
LST_DEFPAY_DATAS, item,
-1);
}
}
guint32
ui_pay_listview_get_selected_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Payee *item;
gtk_tree_model_get(model, &iter, LST_DEFPAY_DATAS, &item, -1);
if( item!= NULL )
return item->key;
}
return 0;
}
void
ui_pay_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("ui_pay_listview_remove_selected() \n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
}
struct PayListContext {
GtkTreeModel *model;
gchar *needle;
gboolean showhidden;
};
static void ui_pay_listview_populate_ghfunc(gpointer key, gpointer value, struct PayListContext *context)
{
GtkTreeIter iter;
Payee *item = value;
gboolean hastext = FALSE;
gboolean insert = TRUE;
//DB( g_print(" populate: %p\n", key) );
if( context->needle != NULL )
hastext = (strlen(context->needle) >= 2) ? TRUE : FALSE;
if(hastext)
insert = hb_string_utf8_strstr(item->name, context->needle, FALSE);
if(context->showhidden == FALSE)
{
if( item->flags & PF_HIDDEN )
insert = FALSE;
}
if( insert == TRUE )
{
gtk_list_store_insert_with_values(GTK_LIST_STORE(context->model), &iter, -1,
LST_DEFPAY_TOGGLE , FALSE,
LST_DEFPAY_DATAS, item,
-1);
}
}
void ui_pay_listview_populate(GtkWidget *treeview, gchar *needle, gboolean showhidden)
{
struct PayListContext context;
DB( g_print("ui_pay_listview_populate \n") );
context.model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
context.needle = needle;
context.showhidden = showhidden;
gtk_list_store_clear (GTK_LIST_STORE(context.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(view), NULL); /* Detach model from view */
/* populate */
g_hash_table_foreach(GLOBALS->h_pay, (GHFunc)ui_pay_listview_populate_ghfunc, &context);
//gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
//g_object_unref(model);
}
static gboolean ui_pay_listview_search_equal_func (GtkTreeModel *model,
gint column,
const gchar *key,
GtkTreeIter *iter,
gpointer search_data)
{
gboolean retval = TRUE;
gchar *normalized_string;
gchar *normalized_key;
gchar *case_normalized_string = NULL;
gchar *case_normalized_key = NULL;
Payee *item;
//gtk_tree_model_get_value (model, iter, column, &value);
gtk_tree_model_get(model, iter, LST_DEFPAY_DATAS, &item, -1);
if(item != NULL)
{
normalized_string = g_utf8_normalize (item->name, -1, G_NORMALIZE_ALL);
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
if (normalized_string && normalized_key)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
case_normalized_key = g_utf8_casefold (normalized_key, -1);
if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
retval = FALSE;
}
g_free (normalized_key);
g_free (normalized_string);
g_free (case_normalized_key);
g_free (case_normalized_string);
}
return retval;
}
GtkWidget *
ui_pay_listview_new(gboolean withtoggle, gboolean withcount)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("ui_pay_listview_new() \n") );
store = gtk_list_store_new(
NUM_LST_DEFPAY,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
);
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column: hide icon
//#1826360 wish: archive payee/category to lighten the lists
if( withtoggle == FALSE )
{
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_icon_cell_data_function, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
// column: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer, "active", LST_DEFPAY_TOGGLE, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE);
g_signal_connect (G_OBJECT(renderer), "toggled",
G_CALLBACK (ui_pay_listview_toggled_cb), store);
g_object_set_data(G_OBJECT(treeview), "togrdr_data", renderer);
}
// column: usage
if( withcount == TRUE )
{
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
//TRANSLATORS: 'txn' is abbrevation for transaction
gtk_tree_view_column_set_title(column, _("# txn"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_count_txn_cell_data_function, GINT_TO_POINTER(LST_DEFPAY_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFPAY_SORT_USETXN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by default hide this column
gtk_tree_view_column_set_visible(column, FALSE);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
//TRANSLATORS: 'txn' is abbrevation for configuration
gtk_tree_view_column_set_title(column, _("# cfg"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_count_cfg_cell_data_function, GINT_TO_POINTER(LST_DEFPAY_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFPAY_SORT_USECFG);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by default hide this column
gtk_tree_view_column_set_visible(column, FALSE);
}
// column: name
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);
if( withtoggle == FALSE )
{
g_object_set(renderer,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
}
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Payee"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_name_cell_data_function, GINT_TO_POINTER(LST_DEFPAY_DATAS), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
//gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFPAY_SORT_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column: category
if( withtoggle == FALSE )
{
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Category"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_defcat_cell_data_function, GINT_TO_POINTER(LST_DEFPAY_DATAS), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
//gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFPAY_SORT_DEFCAT);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
// column: payment
if( withtoggle == FALSE )
{
column = gtk_tree_view_column_new();
//TRANSLATORS: this is abbreviation for Payment
gtk_tree_view_column_set_title(column, _("Payment"));
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set(renderer, "xalign", 0.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_pay_listview_info_cell_data_function, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFPAY_SORT_DEFPAY);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
if( withtoggle == TRUE )
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_pay_listview_search_equal_func, NULL, NULL);
// treeview attribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), withcount);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_NAME, ui_pay_listview_compare_func, GINT_TO_POINTER(LST_DEFPAY_SORT_NAME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_USETXN, ui_pay_listview_compare_func, GINT_TO_POINTER(LST_DEFPAY_SORT_USETXN), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_USECFG, ui_pay_listview_compare_func, GINT_TO_POINTER(LST_DEFPAY_SORT_USECFG), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_DEFPAY, ui_pay_listview_compare_func, GINT_TO_POINTER(LST_DEFPAY_SORT_DEFPAY), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_DEFCAT, ui_pay_listview_compare_func, GINT_TO_POINTER(LST_DEFPAY_SORT_DEFCAT), NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFPAY_SORT_NAME, GTK_SORT_ASCENDING);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_pay_manage_dialog_refilter(struct ui_pay_manage_dialog_data *data)
{
gboolean showhidden;
gchar *needle;
DB( g_print("(ui_pay_manage_dialog) refilter\n") );
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
showhidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showhidden));
ui_pay_listview_populate(data->LV_pay, needle, showhidden);
}
static void
ui_pay_manage_dialog_delete_unused(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data = user_data;
gboolean result;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_pay_manage_dialog) delete unused - data %p\n", data) );
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
_("Delete unused payee"),
_("Are you sure you want to\npermanently delete unused payee?"),
_("_Delete"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
GtkTreeModel *model;
//#1996275 fill usage before delete !
if( data->usagefilled == FALSE )
{
payee_fill_usage();
data->usagefilled = TRUE;
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_pay));
gtk_list_store_clear (GTK_LIST_STORE(model));
//#1917075
data->change += payee_delete_unused();
ui_pay_manage_dialog_refilter(data);
}
}
/**
* ui_pay_manage_dialog_load_csv:
*
*/
static void
ui_pay_manage_dialog_load_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data = user_data;
gchar *filename = NULL;
gchar *error;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_pay_manage_dialog) load csv - data %p\n", data) );
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
if( !payee_load_csv(filename, &error) )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("File format error"),
_("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
);
}
g_free( filename );
ui_pay_manage_dialog_refilter(data);
}
}
/**
* ui_pay_manage_dialog_save_csv:
*
*/
static void
ui_pay_manage_dialog_save_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data = user_data;
gchar *filename = NULL;
DB( g_print("(ui_pay_manage_dialog) save csv\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
payee_save_csv(filename);
g_free( filename );
}
}
static void
ui_pay_manage_dialog_cb_show_usage (GtkToggleButton *button, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
gboolean showusage;
GtkTreeViewColumn *column;
DB( g_print("(ui_pay_manage_dialog) show usage\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW)), "inst_data");
if( data->usagefilled == FALSE )
{
payee_fill_usage();
data->usagefilled = TRUE;
}
showusage = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showusage));
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_pay), LST_DEFPAY_SORT_USETXN);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_pay), LST_DEFPAY_SORT_USECFG);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
}
static void
ui_pay_manage_dialog_cb_show_hidden (GtkToggleButton *button, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW)), "inst_data");
ui_pay_manage_dialog_refilter(data);
}
/**
* ui_pay_manage_dialog_add:
*
*/
static void
ui_pay_manage_dialog_add(GtkWidget *widget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
gboolean isadded;
Payee *item;
gchar *name;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(defayee) add (data=%p)\n", data) );
name = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_name));
item = da_pay_malloc ();
item->name = g_strdup(name);
g_strstrip(item->name);
isadded = FALSE;
if( strlen(item->name) > 0 )
{
isadded = da_pay_append(item);
if( isadded == TRUE )
{
ui_pay_listview_add(GTK_TREE_VIEW(data->LV_pay), item);
data->change++;
}
}
//#2051349 warn user and free lack
if( isadded == FALSE )
{
DB( g_print(" existing item\n") );
da_pay_free (item);
ui_dialog_msg_infoerror (GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Duplicate payee name. Try another name.") );
}
gtk_entry_set_text(GTK_ENTRY(data->ST_name), "");
}
static void ui_pay_manage_dialog_edit_entry_cb(GtkEditable *editable, gpointer user_data)
{
GtkDialog *window = user_data;
const gchar *buffer;
buffer = gtk_entry_get_text(GTK_ENTRY(editable));
gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT, strlen(buffer) > 0 ? TRUE : FALSE);
}
static void ui_pay_manage_dialog_edit(GtkWidget *dowidget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
GtkWidget *dialog, *content_area, *grid;
GtkWidget *label, *widget;
GtkWidget *ST_name, *PO_cat, *NU_mode, *TB_notes, *scrollwin;
gint row;
guint32 key;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(dowidget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(defayee) modify %p\n", data) );
key = ui_pay_listview_get_selected_key(GTK_TREE_VIEW(data->LV_pay));
if( key > 0 )
{
Payee *item;
item = da_pay_get( key );
dialog = gtk_dialog_new_with_buttons (_("Edit Payee"),
GTK_WINDOW (data->dialog),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
grid = gtk_grid_new();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(grid), SPACING_LARGE);
gtk_box_prepend (GTK_BOX (content_area), grid);
// group :: General
//label = make_label_group(_("Payee"));
//gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 0;
label = make_label_widget(_("_Name:"));
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 1, 1);
widget = gtk_entry_new();
gtk_entry_set_width_chars(GTK_ENTRY(widget), 24);
ST_name = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (grid), widget, 2, row, 1, 1);
//#1932193 add notes for payee
row++;
label = make_label(_("Notes:"), 1.0, 0.0);
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 1, 1);
widget = gtk_text_view_new ();
//#1697171 add wrap
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (scrollwin, -1, 24);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
TB_notes = widget;
gtk_grid_attach (GTK_GRID (grid), scrollwin, 2, row, 1, 1);
// group :: Default
row++;
label = make_label_group(_("Default Fill"));
gtk_widget_set_margin_top(label, SPACING_LARGE);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 3, 1);
row++;
label = make_label_widget(_("_Category:"));
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 1, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
PO_cat = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Pa_yment:"));
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 1, 1);
widget = make_paymode(label);
NU_mode = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 2, row, 1, 1);
//setup
gtk_entry_set_text(GTK_ENTRY(ST_name), item->name);
gtk_widget_grab_focus (ST_name);
gtk_entry_set_activates_default (GTK_ENTRY(ST_name), TRUE);
//5.5 done in popover
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(PO_cat), GLOBALS->h_cat);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(PO_cat), item->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(PO_cat), item->kcat);
kiv_combo_box_set_active(GTK_COMBO_BOX(NU_mode), item->paymode);
//#1932193 add notes for payee
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (TB_notes));
GtkTextIter iter;
gtk_text_buffer_set_text (buffer, "", 0);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
if(item->notes != NULL)
gtk_text_buffer_insert (buffer, &iter, item->notes, -1);
g_signal_connect (G_OBJECT (ST_name), "changed", G_CALLBACK (ui_pay_manage_dialog_edit_entry_cb), dialog);
gtk_widget_show_all(grid);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
const gchar *name;
// 1: manage renaming
name = gtk_entry_get_text(GTK_ENTRY(ST_name));
// ignore if item is empty
if (name && *name)
{
if( payee_rename(item, name) )
{
//to redraw the active entry
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_pay));
data->change++;
}
else
{
ui_dialog_msg_infoerror(GTK_WINDOW(dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot rename this Payee,\n"
"from '%s' to '%s',\n"
"this name already exists."),
item->name,
name
);
}
}
//item->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(PO_cat));
item->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(PO_cat));
item->paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(NU_mode));
//#1932193 add notes for payee
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (TB_notes));
GtkTextIter siter, eiter;
gchar *notes;
gtk_text_buffer_get_iter_at_offset (buffer, &siter, 0);
gtk_text_buffer_get_end_iter(buffer, &eiter);
notes = gtk_text_buffer_get_text(buffer, &siter, &eiter, FALSE);
if(notes != NULL)
item->notes = g_strdup(notes);
//TODO: missing sort here
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
static void ui_pay_manage_dialog_merge_entry_cb(GtkComboBox *widget, gpointer user_data)
{
GtkDialog *window = user_data;
gchar *buffer;
//buffer = (gchar *)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (widget))));
buffer = (gchar *)gtk_entry_get_text(GTK_ENTRY (widget));
gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_OK, strlen(buffer) > 0 ? TRUE : FALSE);
}
static void ui_pay_manage_dialog_merge(GtkWidget *widget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox;
GtkWidget *getwidget, *togglebutton;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(defayee) merge %p\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_pay));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Payee *srcpay;
gchar *title;
gchar *secondtext;
gtk_tree_model_get(model, &iter, LST_DEFPAY_DATAS, &srcpay, -1);
title = g_strdup_printf (
_("Merge payee '%s'"), srcpay->name);
dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
title,
NULL
);
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Merge"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
content = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
secondtext = _("Transactions assigned to this payee,\n"
"will be moved to the payee selected below.");
g_object_set(GTK_MESSAGE_DIALOG (dialog), "secondary-text", secondtext, NULL);
g_free(title);
//getwidget = ui_pay_comboboxentry_new(NULL);
getwidget = ui_pay_entry_popover_new(NULL);
gtk_box_prepend (GTK_BOX (mainvbox), getwidget);
secondtext = g_strdup_printf (
_("_Delete the payee '%s'"), srcpay->name);
togglebutton = gtk_check_button_new_with_mnemonic(secondtext);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), TRUE);
g_free(secondtext);
gtk_box_prepend (GTK_BOX (mainvbox), togglebutton);
//setup
//gtk_combo_box_set_active(GTK_COMBO_BOX(getwidget), oldpos);
g_signal_connect (G_OBJECT (ui_pay_entry_popover_get_entry(GTK_BOX(getwidget))), "changed", G_CALLBACK (ui_pay_manage_dialog_merge_entry_cb), dialog);
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);
//5.5 done in popover, and keep src as well
//ui_pay_comboboxentry_populate_except(GTK_COMBO_BOX(getwidget), GLOBALS->h_pay, srcpay->key);
gtk_widget_grab_focus (getwidget);
gtk_widget_show_all(mainvbox);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_OK)
{
GtkTreeModel *model;
Payee *newpay;
guint dstpaykey;
//dstpaykey = ui_pay_comboboxentry_get_key_add_new(GTK_COMBO_BOX(getwidget));
dstpaykey = ui_pay_entry_popover_get_key_add_new(GTK_BOX(getwidget));
//do nothing if src = dst...
if( srcpay->key != dstpaykey )
{
DB( g_print(" -> move pay to %d)\n", dstpaykey) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_pay));
gtk_list_store_clear (GTK_LIST_STORE(model));
payee_move(srcpay->key, dstpaykey);
newpay = da_pay_get(dstpaykey);
//#1771720: update count
newpay->nb_use_all += srcpay->nb_use_all;
newpay->nb_use_txn += srcpay->nb_use_txn;
srcpay->nb_use_all = 0;
srcpay->nb_use_txn = 0;
// add the new payee to listview
if(newpay)
ui_pay_listview_add(GTK_TREE_VIEW(data->LV_pay), newpay);
// delete the old payee
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)) )
{
DB( g_print(" -> delete %d '%s'\n", srcpay->key, srcpay->name ) );
ui_pay_listview_remove_selected(GTK_TREE_VIEW(data->LV_pay));
da_pay_delete(srcpay->key);
}
data->change++;
//#1958767 if searchbar is active get the text
ui_pay_manage_dialog_refilter(data);
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
/*
** delete the selected payee to our treeview and temp GList
*/
static void ui_pay_manage_dialog_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
Payee *item;
guint32 key;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_pay_manage_dialog) delete (data=%p)\n", data) );
key = ui_pay_listview_get_selected_key(GTK_TREE_VIEW(data->LV_pay));
if( key > 0 )
{
gchar *title;
gchar *secondtext = NULL;
item = da_pay_get(key);
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->name);
if( item->nb_use_all > 0 )
{
secondtext = _("This payee is used.\n"
"Any transaction using that payee will be set to (no payee)");
}
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
payee_move(key, 0);
ui_pay_listview_remove_selected(GTK_TREE_VIEW(data->LV_pay));
da_pay_delete(key);
data->change++;
}
}
}
//#1826360 wish: archive payee/category to lighten the lists
static void ui_pay_manage_dialog_hide(GtkWidget *widget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
Payee *item;
guint32 key;
gboolean showhidden;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_pay_manage_dialog) hide (data=%p)\n", data) );
key = ui_pay_listview_get_selected_key(GTK_TREE_VIEW(data->LV_pay));
if( key > 0 )
{
item = da_pay_get(key);
item->flags ^= PF_HIDDEN;
data->change++;
}
//todo remove row
showhidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showhidden));
if( showhidden == FALSE )
ui_pay_listview_remove_selected(GTK_TREE_VIEW(data->LV_pay));
else
hbtk_listview_redraw_selected_row (GTK_TREE_VIEW(data->LV_pay));
}
static void ui_pay_manage_dialog_update(GtkWidget *treeview, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
gboolean sensitive;
guint32 key;
DB( g_print("\n(ui_pay_manage_dialog) cursor changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
key = ui_pay_listview_get_selected_key(GTK_TREE_VIEW(data->LV_pay));
sensitive = (key > 0) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_merge, sensitive);
gtk_widget_set_sensitive(data->BT_delete, sensitive);
gtk_widget_set_sensitive(data->BT_hide, sensitive);
}
static gboolean ui_pay_manage_dialog_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else
if (keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_pay);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void ui_pay_manage_dialog_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_pay_manage_dialog_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void ui_pay_manage_dialog_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer user_data)
{
//GtkTreeModel *model;
//GtkTreeIter iter;
DB( g_print("ui_pay_manage_dialog_onRowActivated()\n") );
//#1960743 double click payee not working after search
// no need to check is is not none here, done into edit
//model = gtk_tree_view_get_model(treeview);
//gtk_tree_model_get_iter_first(model, &iter);
//gtk_tree_model_get_iter(model, &iter, path);
//if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter) == FALSE)
//{
ui_pay_manage_dialog_edit(GTK_WIDGET(treeview), NULL);
//}
}
static void
ui_pay_manage_search_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data = user_data;
DB( g_printf("\n[ui_pay_manage_dialog] search_changed_cb\n") );
ui_pay_manage_dialog_refilter(data);
}
static void ui_pay_manage_setup(struct ui_pay_manage_dialog_data *data)
{
DB( g_print("\n[ui-budget] setup\n") );
DB( g_print(" init data\n") );
data->change = 0;
data->usagefilled = FALSE;
//#2051419 show hidden by default
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->BT_showhidden), TRUE);
DB( g_print(" populate\n") );
ui_pay_manage_dialog_refilter(data);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
g_signal_connect (G_OBJECT (data->BT_showhidden), "toggled", G_CALLBACK (ui_pay_manage_dialog_cb_show_hidden), NULL);
g_signal_connect (G_OBJECT (data->BT_showusage) , "toggled", G_CALLBACK (ui_pay_manage_dialog_cb_show_usage), NULL);
g_object_bind_property (data->BT_add, "active", data->RE_addreveal, "reveal-child", G_BINDING_BIDIRECTIONAL);
gtk_tree_view_set_search_entry(GTK_TREE_VIEW(data->LV_pay), GTK_ENTRY(data->ST_search));
g_signal_connect (G_OBJECT (data->ST_search), "search-changed", G_CALLBACK (ui_pay_manage_search_changed_cb), data);
g_signal_connect (G_OBJECT (data->ST_name), "activate", G_CALLBACK (ui_pay_manage_dialog_add), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_pay)), "changed", G_CALLBACK (ui_pay_manage_dialog_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_pay), "row-activated", G_CALLBACK (ui_pay_manage_dialog_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_edit) , "clicked", G_CALLBACK (ui_pay_manage_dialog_edit), NULL);
g_signal_connect (G_OBJECT (data->BT_merge) , "clicked", G_CALLBACK (ui_pay_manage_dialog_merge), NULL);
g_signal_connect (G_OBJECT (data->BT_delete), "clicked", G_CALLBACK (ui_pay_manage_dialog_delete), NULL);
g_signal_connect (G_OBJECT (data->BT_hide), "clicked", G_CALLBACK (ui_pay_manage_dialog_hide), NULL);
}
static gboolean ui_pay_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_pay_manage_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n(ui_pay_manage_mapped)\n") );
ui_pay_manage_setup(data);
ui_pay_manage_dialog_update(data->LV_pay, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static const GActionEntry win_actions[] = {
{ "imp" , ui_pay_manage_dialog_load_csv, NULL, NULL, NULL, {0,0,0} },
{ "exp" , ui_pay_manage_dialog_save_csv, NULL, NULL, NULL, {0,0,0} },
{ "del" , ui_pay_manage_dialog_delete_unused, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
GtkWidget *ui_pay_manage_dialog (void)
{
struct ui_pay_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox, *vbox, *bbox, *treeview, *scrollwin, *table;
GtkWidget *widget, *image, *revealer, *tbar;
gint w, h, dw, dh, row;
data = g_malloc0(sizeof(struct ui_pay_manage_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Payees"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"), GTK_RESPONSE_ACCEPT,
NULL);
/*
dialog = g_object_new (GTK_TYPE_DIALOG, "use-header-bar", TRUE, NULL);
gtk_window_set_title (GTK_WINDOW (dialog), _("Manage Payees"));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW(GLOBALS->mainwindow));
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
*/
//gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 2:3
dw = (dh * 2) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print("(ui_pay_manage_dialog) dialog=%p, inst_data=%p\n", dialog, data) );
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_LARGE);
//our table
table = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainvbox), table);
//filter part
row = 0;
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), bbox, 0, row, 2, 1);
//test headerbar
//content = gtk_dialog_get_header_bar(GTK_DIALOG (dialog));
widget = make_image_toggle_button(ICONNAME_HB_BUTTON_HIDE, _("Show Hidden") );
data->BT_showhidden = widget;
gtk_box_prepend(GTK_BOX (bbox), widget);
widget = make_image_toggle_button(ICONNAME_HB_BUTTON_USAGE, _("Show Usage") );
data->BT_showusage = widget;
gtk_box_prepend(GTK_BOX (bbox), widget);
//menubutton
widget = gtk_menu_button_new();
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_box_append(GTK_BOX (bbox), widget);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Import CSV..."), "win.imp");
g_menu_append (section, _("E_xport CSV..."), "win.exp");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Delete unused..."), "win.del");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (widget, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
widget = make_search();
data->ST_search = widget;
gtk_box_append(GTK_BOX (bbox), widget);
// list + toolbar
row++;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_grid_attach (GTK_GRID (table), vbox, 0, row, 2, 1);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
treeview = ui_pay_listview_new(FALSE, TRUE);
data->LV_pay = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
data->BT_add = widget = make_image_toggle_button(ICONNAME_LIST_ADD, _("Add"));
gtk_box_prepend(GTK_BOX(bbox), widget);
data->BT_delete = widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
data->BT_edit = widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
gtk_box_prepend(GTK_BOX(bbox), widget);
data->BT_merge = widget = make_image_button(ICONNAME_HB_LIST_MERGE, _("Move/Merge"));
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
data->BT_hide = widget = make_image_button(ICONNAME_HB_BUTTON_HIDE, _("Show/Hide"));
gtk_box_prepend(GTK_BOX(bbox), widget);
row++;
revealer = gtk_revealer_new ();
data->RE_addreveal = revealer;
gtk_grid_attach (GTK_GRID (table), revealer, 0, row, 2, 1);
data->ST_name = gtk_entry_new ();
gtk_entry_set_placeholder_text(GTK_ENTRY(data->ST_name), _("new payee") );
gtk_widget_set_hexpand (data->ST_name, TRUE);
gtk_revealer_set_child (GTK_REVEALER(revealer), data->ST_name);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_pay_manage_mapped), &dialog);
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_pay_manage_dialog_cb_on_key_press), (gpointer)data);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
// wait for the user
gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
GLOBALS->changes_count += data->change;
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/ui-flt-widget.c 0000664 0001750 0001750 00000032547 15005624220 016237 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "ui-filter.h"
#include "ui-flt-widget.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
gboolean
ui_flt_popover_hub_set (GtkBox *box, gint active_key);
/* = = = = = = = = = = = = = = = = */
/*
**
** The function should return:
** a negative integer if the first value comes before the second,
** 0 if they are equal,
** or a positive integer if the first value comes after the second.
*/
static gint
lst_favfilter_model_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
guint32 key1, key2;
gchar *name1, *name2;
gtk_tree_model_get(model, a, LST_FAVFLT_KEY, &key1, -1);
gtk_tree_model_get(model, b, LST_FAVFLT_KEY, &key2, -1);
if( !key1 )
{
retval = -1;
}
else
if( !key2 )
{
retval = 1;
}
else
{
gtk_tree_model_get(model, a, LST_FAVFLT_NAME, &name1, -1);
gtk_tree_model_get(model, b, LST_FAVFLT_NAME, &name2, -1);
retval = hb_string_utf8_compare(name1, name2);
g_free(name2);
g_free(name1);
}
return retval;
}
static gboolean
lst_favfilter_get_iter(GtkTreeModel *model, gint key, GtkTreeIter *return_iter)
{
GtkTreeIter iter;
gboolean match = FALSE;
if (gtk_tree_model_get_iter_first (model, &iter))
do {
gint tmpkey;
gtk_tree_model_get (model, &iter, LST_FAVFLT_KEY, &tmpkey, -1);
match = (tmpkey == key) ? TRUE : FALSE;
if (match)
{
*return_iter = iter;
break;
}
} while (gtk_tree_model_iter_next (model, &iter));
return match;
}
GtkListStore *
lst_lst_favfilter_model_new(void)
{
GtkListStore *store;
GtkTreeIter iter;
store = gtk_list_store_new (2,
G_TYPE_INT,
G_TYPE_STRING);
// insert none
gtk_list_store_insert_with_values(store, &iter, 0,
LST_FAVFLT_KEY, 0,
LST_FAVFLT_NAME, _("(default)"),
-1);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), lst_favfilter_model_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
return store;
}
/* = = = = = = = = = = = = = = = = */
void ui_flt_manage_header_sensitive(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_popover_data *data;
gboolean sensitive = FALSE;
Filter *curflt;
DB( g_print("\n[Fav Filters] sensitive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_BOX)), "inst_data");
//curflt = ui_flt_popover_hub_get(GTK_BOX(data->box), NULL);
curflt = data->filter;
DB( g_print(" key: %d '%s', changes:%d\n", curflt->key, curflt->name, curflt->nbchanges) );
gtk_widget_set_tooltip_text(data->combobox, curflt->name);
sensitive = (curflt->nbchanges > 0) ? TRUE : FALSE;
//key 0 disable all
sensitive = curflt->key == 0 ? FALSE : sensitive;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "save")), sensitive);
//gtk_widget_set_sensitive(data->MI_sav, sensitive);
//gtk_widget_set_sensitive(data->BT_sav, sensitive);
//we can always saveas
sensitive = (curflt->key == 0) ? FALSE : TRUE;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "rename")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "delete")), sensitive);
//gtk_widget_set_sensitive(data->MI_ren, sensitive);
//gtk_widget_set_sensitive(data->MI_del, sensitive);
}
static void ui_flt_manage_header_action_del(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_flt_popover_data *data = user_data;
Filter *curflt;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[Fav Filters] action del\n") );
curflt = ui_flt_popover_hub_get(GTK_BOX(data->box), NULL);
if( curflt )
{
DB( g_print(" flt:%d '%s'\n", curflt->key, curflt->name) );
//TODO: add refcount test + confirmation here
//TODO: remove from model
model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->combobox));
lst_favfilter_get_iter(model, curflt->key, &iter);
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
da_flt_remove(curflt->key);
//set to none
gtk_combo_box_set_active(GTK_COMBO_BOX(data->combobox), 0);
GLOBALS->changes_count++;
}
}
static void
ui_flt_manage_header_action_ren (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_flt_popover_data *data = user_data;
GtkTreeIter iter;
Filter *curflt;
DB( g_print("\n[Fav Filters] action rename\n") );
if( gtk_combo_box_get_active_iter(GTK_COMBO_BOX(data->combobox), &iter) )
{
gint key;
gtk_tree_model_get(GTK_TREE_MODEL(GLOBALS->fltmodel), &iter,
LST_FAVFLT_KEY, &key,
-1);
curflt = da_flt_get(key);
if( curflt && curflt->key > 0 )
{
DB( g_print(" flt:%d '%s'\n", curflt->key, curflt->name) );
gchar *rawname = dialog_get_name(_("Filter rename"), curflt->name, data->parent);
if(rawname != NULL)
{
//TODO: maybe, no unique label for now
if( g_strcmp0(curflt->name, rawname) )
{
GValue gvalue = G_VALUE_INIT;
g_free(curflt->name);
curflt->name = rawname;
g_value_init (&gvalue, G_TYPE_STRING);
g_value_set_string (&gvalue, (const gchar*)rawname);
gtk_list_store_set_value(GLOBALS->fltmodel, &iter,
LST_FAVFLT_NAME, &gvalue);
GLOBALS->changes_count++;
}
}
}
}
}
static void
ui_flt_manage_header_action_new (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_flt_popover_data *data = user_data;
Filter *curflt, *newflt;
gchar *newname;
DB( g_print("\n[Fav Filters] action save as\n") );
newname = g_strdup_printf(_("New filter %d"), da_flt_get_max_key());
gchar *rawname = dialog_get_name(_("Filter name"), newname, data->parent);
g_free(newname);
if( rawname != NULL )
{
curflt = ui_flt_popover_hub_get(GTK_BOX(data->box), NULL);
newflt = da_flt_malloc();
if(curflt == NULL)
curflt = data->filter;
if(curflt && newflt)
{
da_flt_copy(curflt, newflt);
g_free(newflt->name);
newflt->name = rawname;
da_flt_append(newflt);
GLOBALS->changes_count++;
}
//set combo to new filter
ui_flt_popover_hub_set(GTK_BOX(data->box), newflt->key);
}
}
static void
ui_flt_manage_header_action_sav (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_flt_popover_data *data = user_data;
Filter *curflt;
DB( g_print("\n[Fav Filters] action save\n") );
curflt = ui_flt_popover_hub_get(GTK_BOX(data->box), NULL);
if( curflt && data->filter )
{
data->filter->nbchanges = 0;
da_flt_copy(data->filter, curflt);
ui_flt_manage_header_sensitive(data->box, NULL);
GLOBALS->changes_count++;
}
}
gboolean
ui_flt_popover_hub_set (GtkBox *box, gint active_key)
{
struct ui_flt_popover_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean match = FALSE;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(box), GTK_TYPE_BOX)), "inst_data");
model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->combobox));
if (gtk_tree_model_get_iter_first (model, &iter))
do {
gint key;
gtk_tree_model_get (model, &iter, LST_FAVFLT_KEY, &key, -1);
match = (key == active_key) ? TRUE : FALSE;
if (match)
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX(data->combobox), &iter);
break;
}
} while (gtk_tree_model_iter_next (model, &iter));
return match;
}
Filter *
ui_flt_popover_hub_get (GtkBox *box, gpointer user_data)
{
struct ui_flt_popover_data *data;
Filter *item = NULL;
GtkTreeIter iter;
//DB( g_print("\n[Fav Filters] get key\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(box), GTK_TYPE_BOX)), "inst_data");
if( gtk_combo_box_get_active_iter(GTK_COMBO_BOX(data->combobox), &iter) )
{
gint key;
gtk_tree_model_get(GTK_TREE_MODEL(GLOBALS->fltmodel), &iter,
0, &key,
-1);
item = da_flt_get(key);
//DB( g_print(" found: %p for k:%d\n", item, key) );
}
return item;
}
void
ui_flt_popover_hub_save (GtkWidget *widget, gpointer user_data)
{
struct ui_flt_popover_data *data;
GAction *action;
DB( g_print("\n[Fav Filters] external save\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_BOX)), "inst_data");
//we need to call sensitive here to enable save
ui_flt_manage_header_sensitive(data->box, NULL);
action = g_action_map_lookup_action (G_ACTION_MAP (data->actions), "save");
g_action_activate(action, NULL);
}
GtkWidget *
ui_flt_popover_hub_get_combobox (GtkBox *box, gpointer user_data)
{
struct ui_flt_popover_data *data;
DB( g_print("\n[Fav Filters] get combobox\n") );
if(!GTK_IS_BOX(box))
return NULL;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(box), GTK_TYPE_BOX)), "inst_data");
return data->combobox;
}
static GtkWidget *
hbtk_kvcombobox_new ()
{
GtkWidget *p_combo;
GtkCellRenderer *p_cell = NULL;
p_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(GLOBALS->fltmodel));
p_cell = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (p_combo), p_cell, FALSE);
gtk_cell_layout_set_attributes (
GTK_CELL_LAYOUT (p_combo),
p_cell, "text", 1,
NULL
);
/*g_object_set(p_cell,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 20,
NULL);
*/
gtk_combo_box_set_active(GTK_COMBO_BOX(p_combo), 0);
return p_combo;
}
static const GActionEntry favflt_actions[] = {
// name, function(), type, state,
{ "save" , ui_flt_manage_header_action_sav , NULL, NULL , NULL, {0,0,0} },
{ "new" , ui_flt_manage_header_action_new , NULL, NULL , NULL, {0,0,0} },
{ "rename" , ui_flt_manage_header_action_ren , NULL, NULL , NULL, {0,0,0} },
{ "delete" , ui_flt_manage_header_action_del , NULL, NULL , NULL, {0,0,0} },
};
static GtkWidget *
ui_flt_dialog_create_menubutton (struct ui_flt_popover_data *data)
{
GtkWidget *button, *image;
GMenu *menu, *section;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Save") , "favfltactions.save");
g_menu_append (section, _("_Save as..."), "favfltactions.new");
g_menu_append (section, _("_Rename...") , "favfltactions.rename");
g_menu_append (section, _("_Delete...") , "favfltactions.delete");
g_object_unref (section);
GSimpleActionGroup *group = g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), favflt_actions, G_N_ELEMENTS (favflt_actions), data);
button = gtk_menu_button_new();
gtk_menu_button_set_direction (GTK_MENU_BUTTON(button), GTK_ARROW_DOWN);
gtk_widget_set_halign (button, GTK_ALIGN_END);
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (button, "image", image, NULL);
gtk_widget_insert_action_group (button, "favfltactions", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), G_MENU_MODEL (menu));
return button;
}
static void
ui_flt_popover_hub_destroy ( GtkWidget *widget, gpointer user_data )
{
struct ui_flt_popover_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n\n[Fav Filters] destroy event occurred\n") );
DB( g_print(" - box=%p, inst_data=%p\n", widget, data) );
g_free(data);
}
//only used in stats
GtkWidget *
create_popover_widget (GtkWindow *parent, Filter *filter)
{
struct ui_flt_popover_data *data;
GtkWidget *hbox, *widget;
GtkWidget *prtbox;
DB( g_print ("\n\n[Fav Filters] new\n") );
data = g_malloc0(sizeof(struct ui_flt_popover_data));
if(!data) return NULL;
data->filter = filter;
data->parent = parent;
prtbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
data->box = prtbox;
//hb_widget_set_margin(prtbox, SPACING_SMALL);
g_object_set_data(G_OBJECT(prtbox), "inst_data", (gpointer)data);
DB( g_print(" - box=%p, inst_data=%p\n", prtbox, data) );
// connect our dispose function
g_signal_connect (prtbox, "destroy", G_CALLBACK (ui_flt_popover_hub_destroy), (gpointer)data);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (prtbox), hbox);
widget = hbtk_kvcombobox_new();
data->combobox = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
widget = ui_flt_dialog_create_menubutton(data);
data->menubutton = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
ui_flt_manage_header_sensitive(prtbox, data);
return prtbox;
}
homebank-5.9.7/src/hub-reptime.c 0000664 0001750 0001750 00000040454 14761067663 016017 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-mainwindow.h"
#include "hub-reptime.h"
#include "gtk-chart.h"
#include "list-report.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static gint list_reptime_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
//gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
DataRow *dr1, *dr2;
gtk_tree_model_get(model, a,
LST_REPTIME_ROW, &dr1,
-1);
gtk_tree_model_get(model, b,
LST_REPTIME_ROW, &dr2,
-1);
//total always at bottom
if( dr1->pos == LST_REPORT_POS_TOTAL )
{
retval = -1;
}
else
{
if( dr2->pos == LST_REPORT_POS_TOTAL )
{
retval = 1;
}
else
{
retval = dr2->pos - dr1->pos;
}
}
//DB( g_print(" sort %d=%d or %.2f=%.2f :: %d\n", pos1,pos2, val1, val2, ret) );
return retval;
}
static GtkWidget *create_list_reptime(void)
{
GtkTreeStore *store;
GtkWidget *view;
/* create list store */
store = lst_rep_time_new();
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
//5.7
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPTIME_POS , list_reptime_compare_func, GINT_TO_POINTER(LST_REPTIME_POS), NULL);
//gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPTIME_AMOUNT , list_reptime_compare_func, GINT_TO_POINTER(LST_REPTIME_AMOUNT), NULL);
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), list_reptime_compare_func, NULL, NULL);
return(view);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void ui_hub_reptime_update(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
gchar *title = "";
gboolean showmono = TRUE;
DB( g_print("\n[hub-time] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
switch( PREFS->hub_tim_view )
{
case HUB_TIM_VIEW_SPENDING:
title = _("Spending");
if(PREFS->hub_tim_raw)
title = _("Expense");
break;
case HUB_TIM_VIEW_REVENUE:
title = _("Revenue");
if(PREFS->hub_tim_raw)
title = _("Income");
break;
case HUB_TIM_VIEW_SPEREV:
title = _("Spending & Revenue");
if(PREFS->hub_tim_raw)
title = _("Expense & Income");
break;
case HUB_TIM_VIEW_ACCBALANCE:
title = _("Account Balance");
showmono = FALSE;
break;
case HUB_TIM_VIEW_GRPBALANCE:
title = _("Account Group Balance");
showmono = FALSE;
break;
case HUB_TIM_VIEW_ALLBALANCE:
title = _("Global Balance");
break;
}
//time chart
gtk_chart_set_showmono(GTK_CHART(data->RE_hubtim_chart), showmono);
gtk_chart_show_legend(GTK_CHART(data->RE_hubtim_chart), FALSE, FALSE);
gtk_chart_set_color_scheme(GTK_CHART(data->RE_hubtim_chart), PREFS->report_color_scheme);
gtk_chart_set_smallfont (GTK_CHART(data->RE_hubtim_chart), PREFS->rep_smallfont);
gtk_chart_set_currency(GTK_CHART(data->RE_hubtim_chart), GLOBALS->kcur);
gtk_chart_show_xval(GTK_CHART(data->RE_hubtim_chart), TRUE);
//5.7 trendrow is unused, we pass the treeview to get the column labels
gtk_chart_set_datas_time(GTK_CHART(data->RE_hubtim_chart), GTK_TREE_VIEW(data->LV_hubtim), data->hubtim_dt, data->hubtim_rows , data->hubtim_cols, title, NULL);
}
void ui_hub_reptime_clear(GtkWidget *widget, gpointer user_data)
{
}
void ui_hub_reptime_populate(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
DataTable *dt;
gint range;
gint tmpview, tmpsrc, tmpintvl;
guint i, n_inserted, flags;
DB( g_print("\n[hub-time] populate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->hubtim_filter == NULL)
return;
tmpview = PREFS->hub_tim_view;
//default value
tmpsrc = REPORT_GRPBY_TYPE;
tmpintvl = REPORT_INTVL_MONTH;
switch( tmpview )
{
case HUB_TIM_VIEW_ACCBALANCE:
tmpsrc = REPORT_GRPBY_ACCOUNT;
break;
case HUB_TIM_VIEW_GRPBALANCE:
tmpsrc = REPORT_GRPBY_ACCGROUP;
break;
case HUB_TIM_VIEW_ALLBALANCE:
tmpsrc = REPORT_GRPBY_NONE;
break;
}
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_hubtim_range));
PREFS->hub_tim_range = range;
DB( g_print(" - range=%d\n", range) );
filter_preset_daterange_set(data->hubtim_filter, range, 0);
filter_preset_type_set(data->hubtim_filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
gtk_chart_set_datas_none(GTK_CHART(data->RE_hubtim_chart));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_hubtim));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if(data->hubtim_dt)
{
da_datatable_free (data->hubtim_dt);
data->hubtim_dt = NULL;
}
GQueue *txn_queue = hbfile_transaction_get_partial(data->hubtim_filter->mindate, data->hubtim_filter->maxdate);
flags = REPORT_COMP_FLG_NONE;
DB( g_print(" - forecast=%d\n", PREFS->rep_forcast) );
if( PREFS->rep_forcast == TRUE )
flags |= REPORT_COMP_FLG_FORECAST;
if( tmpview == HUB_TIM_VIEW_SPENDING ||
tmpview == HUB_TIM_VIEW_REVENUE ||
tmpview == HUB_TIM_VIEW_SPEREV
)
{
DB( g_print(" mode: spending/revenue\n") );
DB( g_print(" - rawamount=%d\n", PREFS->hub_tim_raw) );
if( PREFS->hub_tim_raw == FALSE )
flags |= REPORT_COMP_FLG_CATSIGN;
flags |= REPORT_COMP_FLG_SPENDING;
flags |= REPORT_COMP_FLG_REVENUE;
if( tmpview == HUB_TIM_VIEW_SPENDING )
flags &= ~REPORT_COMP_FLG_REVENUE;
if( tmpview == HUB_TIM_VIEW_REVENUE )
flags &= ~REPORT_COMP_FLG_SPENDING;
//filter_preset_type_set(data->hubtim_filter, FLT_TYPE_EXPENSE, FLT_INCLUDE);
dt = report_compute(tmpsrc, tmpintvl, data->hubtim_filter, txn_queue, flags);
}
else
{
DB( g_print(" mode: balance\n") );
filter_preset_type_set(data->hubtim_filter, FLT_TYPE_ALL, FLT_OFF);
dt = report_compute(tmpsrc, tmpintvl, data->hubtim_filter, txn_queue, flags | REPORT_COMP_FLG_BALANCE);
//dt = report_compute_balance(tmpsrc, tmpintvl, data->hubtim_filter);
}
g_queue_free (txn_queue);
if(dt)
{
data->hubtim_dt = dt;
DB( g_print(" rows=%d\n", dt->nbrows) );
// clear and detach our 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_hubtim), NULL); // Detach model from view
//tooltip get column label, so keep this
lst_rep_time_renewcol(GTK_TREE_VIEW(data->LV_hubtim), model, dt, TRUE);
n_inserted = 0;
for(i=0; inbrows; i++)
{
DataRow *dr;
guint32 reskey;
reskey = dt->keylist[i];
dr = report_data_get_row(dt, reskey);
DB( g_printf(" eval %d: %d '%s' %.2f\n", i, reskey, dr->label, dr->rowexp + dr->rowinc ) );
//#2024940 test on exp/inc individually
if( (hb_amount_cmp(dr->rowexp, 0.0)==0) && (hb_amount_cmp(dr->rowinc, 0.0)==0) )
{
DB( g_printf(" >skip: no data %.2f %2f\n", dr->rowexp, dr->rowinc) );
continue;
}
//if( tmpsrc == REPORT_GRPBY_ACCOUNT && (i == 0) )
// continue;
n_inserted++;
DB( g_printf(" >insert\n") );
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, NULL, -1,
LST_REPTIME_POS, n_inserted,
LST_REPTIME_KEY, reskey,
LST_REPTIME_LABEL, dr->label,
LST_REPTIME_ROW, dr,
-1);
}
data->hubtim_rows = n_inserted;
data->hubtim_cols = dt->nbcols;
//add fake total row as chart will retrieve 1 !!
if( n_inserted > 1 )
{
DataRow *dr = dt->totrow;
//TODO: crappy here
dr->pos = LST_REPORT_POS_TOTAL;
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, NULL, -1,
LST_REPTIME_POS, LST_REPORT_POS_TOTAL,
LST_REPTIME_KEY, -1,
LST_REPTIME_LABEL, _("Total"),
LST_REPTIME_ROW, dr,
-1);
}
// Re-attach model to view
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_hubtim), model);
g_object_unref(model);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), LST_REPTIME_POS, GTK_SORT_DESCENDING);
//5.7.3 update chart and widgets
{
gchar *daterange;
//data->hubtim_total = total;
ui_hub_reptime_update(widget, data);
daterange = filter_daterange_text_get(data->hubtim_filter);
gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_hubtim_range), daterange);
g_free(daterange);
}
//we don't free dt here as it it used by graph
}
}
static void
ui_hub_reptime_activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_string (old_state, NULL),
g_variant_get_string (new_state, NULL)) );
PREFS->hub_tim_view = HUB_TIM_VIEW_NONE;
if( !strcmp("expmon", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_SPENDING;
else
if( !strcmp("incmon", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_REVENUE;
else
if( !strcmp("expinc", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_SPEREV;
else
if( !strcmp("accbal", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_ACCBALANCE;
else
if( !strcmp("grpbal", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_GRPBALANCE;
else
if( !strcmp("allbal", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tim_view = HUB_TIM_VIEW_ALLBALANCE;
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
ui_hub_reptime_populate(GLOBALS->mainwindow, NULL);
}
static void
ui_hub_reptime_activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_boolean (old_state),
g_variant_get_boolean (new_state)) );
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
PREFS->hub_tim_raw = g_variant_get_boolean (new_state);
ui_hub_reptime_populate(GLOBALS->mainwindow, NULL);
}
static const GActionEntry actions[] = {
// name, function(), type, state,
{ "view", ui_hub_reptime_activate_radio , "s", "'cat'", NULL, {0,0,0} },
{ "raw" , ui_hub_reptime_activate_toggle, NULL, "false" , NULL, {0,0,0} },
};
void ui_hub_reptime_setup(struct hbfile_data *data)
{
GAction *action;
GVariant *new_state;
DB( g_print("\n[hub-time] setup\n") );
data->hubtim_filter = da_flt_malloc();
filter_reset(data->hubtim_filter);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_hubtim_range), PREFS->hub_tim_range);
if( !G_IS_SIMPLE_ACTION_GROUP(data->hubtim_action_group) )
return;
action = g_action_map_lookup_action (G_ACTION_MAP (data->hubtim_action_group), "view");
if( action )
{
const gchar *value = "expmon";
switch( PREFS->hub_tim_view )
{
case HUB_TIM_VIEW_SPENDING : value = "expmon"; break;
case HUB_TIM_VIEW_REVENUE : value = "incmon"; break;
case HUB_TIM_VIEW_SPEREV : value = "expinc"; break;
case HUB_TIM_VIEW_ACCBALANCE: value = "accbal"; break;
case HUB_TIM_VIEW_GRPBALANCE: value = "grpbal"; break;
case HUB_TIM_VIEW_ALLBALANCE: value = "allbal"; break;
}
new_state = g_variant_new_string (value);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
}
//#2066161 raw amount persist
action = g_action_map_lookup_action (G_ACTION_MAP (data->hubtim_action_group), "raw");
if( action )
{
GVariant *new_bool = g_variant_new_boolean(PREFS->hub_tim_raw);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_bool);
}
}
void ui_hub_reptime_dispose(struct hbfile_data *data)
{
DB( g_print("\n[hub-time] dispose\n") );
DB( g_print(" set chart to none\n") );
gtk_chart_set_datas_none(GTK_CHART(data->RE_hubtim_chart));
da_flt_free(data->hubtim_filter);
data->hubtim_filter = NULL;
DB( g_print(" free dt %p\n", data->hubtim_dt) );
if(data->hubtim_dt)
{
da_datatable_free (data->hubtim_dt);
data->hubtim_dt = NULL;
}
}
GtkWidget *ui_hub_reptime_create(struct hbfile_data *data)
{
GtkWidget *hub, *hbox, *bbox, *tbar;
GtkWidget *label, *widget, *image;
DB( g_print("\n[hub-time] create\n") );
// /!\ this widget has to be freed
widget = (GtkWidget *)create_list_reptime();
data->LV_hubtim = widget;
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margins(GTK_WIDGET(hub), 0, SPACING_SMALL, SPACING_SMALL, SPACING_SMALL);
data->GR_hubtim = hub;
/* chart + listview */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hbtk_box_prepend (GTK_BOX (hub), hbox);
widget = gtk_chart_new(CHART_TYPE_STACK);
data->RE_hubtim_chart = widget;
gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
gtk_chart_set_currency(GTK_CHART(widget), GLOBALS->kcur);
gtk_chart_show_legend(GTK_CHART(widget), TRUE, FALSE);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (hub), tbar);
label = make_label_group(_("Time chart"));
data->LB_hubtim = label;
gtk_box_prepend (GTK_BOX (tbar), label);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = gtk_menu_button_new();
gtk_box_prepend (GTK_BOX (bbox), widget);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
image = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
g_object_set (widget, "image", image, NULL);
//gmenu test (see test folder into gtk)
GMenu *menu, *section;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Spending") , "actions.view::expmon");
//5.7.5
g_menu_append (section, _("Revenue") , "actions.view::incmon");
g_menu_append (section, _("Spending & Revenue"), "actions.view::expinc");
//g_object_unref (section);
//5.8
//section = g_menu_new ();
//g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Raw amount"), "actions.raw");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, _("Balance"), G_MENU_MODEL(section));
g_menu_append (section, _("Account") , "actions.view::accbal");
g_menu_append (section, _("Account group"), "actions.view::grpbal");
g_menu_append (section, _("Global") , "actions.view::allbal");
g_object_unref (section);
GSimpleActionGroup *group = g_simple_action_group_new ();
data->hubtim_action_group = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
data->CY_hubtim_range = make_daterange(NULL, DATE_RANGE_FLAG_CUSTOM_HIDDEN);
gtk_box_append (GTK_BOX (tbar), data->CY_hubtim_range);
//hbtk_radio_button_connect (GTK_CONTAINER(data->RA_type), "toggled", G_CALLBACK (ui_hub_reptime_populate), NULL);
g_signal_connect (data->CY_hubtim_range, "changed", G_CALLBACK (ui_hub_reptime_populate), NULL);
return hub;
}
homebank-5.9.7/src/hbtk-switcher.h 0000664 0001750 0001750 00000005147 14736461415 016354 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HBTK_SWITCHER_H__
#define __HBTK_SWITCHER_H__
G_BEGIN_DECLS
#define HBTK_TYPE_SWITCHER (hbtk_switcher_get_type ())
#define HBTK_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HBTK_TYPE_SWITCHER, HbtkSwitcher))
#define HBTK_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HBTK_TYPE_SWITCHER, HbtkSwitcherClass)
#define HBTK_IS_SWITCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HBTK_TYPE_SWITCHER))
#define HBTK_IS_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HBTK_TYPE_SWITCHER))
#define HBTK_SWITCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), HBTK_TYPE_SWITCHER, HbtkSwitcherClass))
typedef struct _HbtkSwitcher HbtkSwitcher;
typedef struct _HbtkSwitcherClass HbtkSwitcherClass;
typedef struct _HbtkSwitcherPrivate HbtkSwitcherPrivate;
struct _HbtkSwitcher
{
GtkBox box;
/*< private >*/
HbtkSwitcherPrivate *priv;
};
struct _HbtkSwitcherClass
{
GtkBoxClass parent_class;
/* signals */
void (* changed) (HbtkSwitcher *dateentry);
/* Padding for future expansion */
void (*_gtk_reserved0) (void);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
};
struct _HbtkSwitcherPrivate
{
GtkRadioButton *first;
gint active;
};
GType hbtk_switcher_get_type(void) G_GNUC_CONST;
GtkWidget *hbtk_switcher_new (GtkOrientation orientation);
void hbtk_switcher_setup (HbtkSwitcher *switcher, gchar **items, gboolean buttonstyle);
void hbtk_switcher_setup_with_data (HbtkSwitcher *switcher, GtkWidget *label, HbKivData *kivdata, gboolean buttonstyle);
gint hbtk_switcher_get_active (HbtkSwitcher *switcher);
void hbtk_switcher_set_active (HbtkSwitcher *switcher, gint active);
void hbtk_switcher_set_nth_sensitive (HbtkSwitcher *switcher, gint nth, gboolean sensitive);
G_END_DECLS
#endif /* __HBTK_SWITCHER_H__ */
homebank-5.9.7/src/list-scheduled.c 0000644 0001750 0001750 00000125433 15123470257 016474 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "list-scheduled.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern char *CYA_ARC_FREQ2[];
/* = = = = = = = = = = = = = = = = */
gchar *ui_arc_listview_get_freq_label(gint index)
{
if(index <= AUTO_FREQ_YEAR)
return CYA_ARC_FREQ2[index];
return "";
}
static guint32
_arc_get_curr(Archive *arc, gint expinc)
{
Account *acc = NULL;
guint32 kcur = GLOBALS->kcur;
if( arc != NULL ) /* display total */
{
if(arc->flags & OF_INTXFER)
{
//-1 = expense
if( expinc == -1)
acc = da_acc_get(arc->kacc);
else
acc = da_acc_get(arc->kxferacc);
}
else
{
acc = da_acc_get(arc->kacc);
}
}
if(acc != NULL)
kcur = acc->kcur;
return kcur;
}
/* = = = = = = = = = = = = = = = = */
static void lst_sch_widget_to_string_row(GString *node, ToStringMode mode, gchar sep, GtkTreeModel *model, GtkTreeIter *iter, guint flags)
{
gchar strbuf[G_ASCII_DTOSTR_BUF_SIZE];
gdouble expense, income;
Archive *arc;
gint nblate;
Payee *payee;
gchar *memo;
gtk_tree_model_get (model, iter,
LST_DSPUPC_DATAS, &arc,
LST_DSPUPC_NB_LATE, &nblate,
LST_DSPUPC_EXPENSE, &expense,
LST_DSPUPC_INCOME , &income,
LST_DSPUPC_MEMO, &memo,
-1);
DB( g_print("---- %p\n", arc) );
DB( g_print("---- %p\n", arc) );
//late
if(arc && nblate > 0)
{
g_string_append_printf(node, nblate < 10 ? "%d" : "+10", nblate);
}
g_string_append_c (node, sep );
//still
if(arc && (arc->rec_flags & TF_LIMIT) )
{
g_string_append_printf(node, "%d", arc->limit);
}
g_string_append_c (node, sep );
//date
if( arc != NULL )
{
hb_sprint_date(strbuf, arc->nextdate);
g_string_append (node, strbuf );
}
g_string_append_c (node, sep );
//number
//g_string_append(node, arc->number);
//g_string_append_c (node, sep );
//payee
if( arc != NULL )
{
payee = da_pay_get(arc->kpay);
if(payee != NULL)
g_string_append(node, payee->name);
}
g_string_append_c (node, sep );
guint32 kcur = _arc_get_curr(arc, (expense < 0) ? -1 : 0);
//expense
if(expense < 0)
{
//g_snprintf(strbuf, sizeof (strbuf), "%.2f", expense);
hb_strfmon(strbuf, G_ASCII_DTOSTR_BUF_SIZE-1, expense, kcur, FALSE);
g_string_append(node, strbuf);
}
g_string_append_c (node, sep );
//income
if(income > 0)
{
//g_snprintf(strbuf, sizeof (strbuf), "%.2f", income);
hb_strfmon(strbuf, G_ASCII_DTOSTR_BUF_SIZE-1, income, kcur, FALSE);
g_string_append(node, strbuf);
}
g_string_append_c (node, sep );
//memo
g_string_append(node, memo);
g_string_append_c (node, sep );
g_free(memo);
//account
if( arc != NULL )
{
Account *acc = da_acc_get(arc->kacc);
g_string_append (node, (acc != NULL) ? acc->name : "");
}
//eol
g_string_append (node, "\n" );
}
GString *lst_sch_widget_to_string(GtkTreeView *treeview, ToStringMode mode)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gchar sep;
DB( g_print("\n[lst_sch] to string\n") );
node = g_string_new(NULL);
sep = (mode == HB_STRING_PRINT) ? '\t' : ';';
// header (nbcols-2 for icon column)
g_string_append(node, _("Late"));
g_string_append_c(node, sep);
g_string_append(node, _("Still"));
g_string_append_c(node, sep);
g_string_append(node, _("Next date"));
g_string_append_c(node, sep);
g_string_append(node, _("Payee"));
g_string_append_c(node, sep);
g_string_append(node, _("Expense"));
g_string_append_c(node, sep);
g_string_append(node, _("Income"));
g_string_append_c(node, sep);
g_string_append(node, _("Memo"));
g_string_append_c(node, sep);
g_string_append(node, _("Account"));
g_string_append_c(node, '\n');
//lines
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
lst_sch_widget_to_string_row(node, mode, sep, model, &iter, 0);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
return node;
}
#if MYDEBUG == 1
static void
ui_arc_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DSPUPC_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
static void
lst_sch_cell_data_func_lateicon (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gchar *iconname = NULL;
gint nblate;
gtk_tree_model_get(model, iter,
LST_DSPUPC_NB_LATE, &nblate,
-1);
iconname = ( nblate > 0 ) ? ICONNAME_WARNING : NULL;
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
lst_sch_cell_data_func_latetext (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *markuptxt;
gchar *color;
gint nblate;
//gint weight;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
LST_DSPUPC_NB_LATE, &nblate,
-1);
if(arc && nblate > 0)
{
markuptxt = g_strdup_printf(nblate < 10 ? "%d" : "+10", nblate);
color = NULL;
//weight = PANGO_WEIGHT_NORMAL;
if(nblate > 0 && PREFS->custom_colors == TRUE)
{
color = PREFS->color_warn;
}
g_object_set(renderer,
//"weight", weight,
"foreground", color,
"text", markuptxt,
NULL);
g_free(markuptxt);
}
else
g_object_set(renderer, "text", NULL, NULL);
}
static void
lst_sch_cell_data_func_still (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *markuptxt;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
if(arc && (arc->rec_flags & TF_LIMIT) )
{
markuptxt = g_strdup_printf("%d", arc->limit);
g_object_set(renderer, "markup", markuptxt, NULL);
g_free(markuptxt);
}
else
g_object_set(renderer, "text", NULL, NULL);
}
static void
lst_sch_cell_data_func_date (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *text = NULL;
gchar buffer[256];
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
if( arc != NULL )
{
//TODO: g_date_valid(arc->nextdate) ?
if( arc->nextdate > 0 )
{
//#2099918 get date weekend shift
GDate *date = g_date_new_julian (scheduled_get_txn_real_postdate(arc->nextdate, arc->weekend));
g_date_strftime (buffer, 256-1, PREFS->date_format, date);
g_date_free(date);
text = buffer;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
lst_sch_cell_data_func_payee (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
if( arc != NULL )
{
//#bugfix 5.6.3
if(arc->flags & OF_INTXFER)
{
Account *acc = da_acc_get(arc->kxferacc);
//5.6 use acc strings for 5.3 add > < for internal xfer
if( acc )
text = ( arc->flags & OF_INCOME ) ? acc->xferincname : acc->xferexpname;
}
else
{
Payee *pay = da_pay_get(arc->kpay);
text = (pay != NULL) ? pay->name : NULL;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
lst_sch_cell_data_func_category (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
if( arc != NULL )
{
if(arc->flags & OF_SPLIT)
{
text = _("- split -");
}
else
{
Category *cat = da_cat_get(arc->kcat);
text = (cat != NULL) ? cat->fullname : "";
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
lst_sch_cell_data_func_memo (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *memo;
gint weight;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
LST_DSPUPC_MEMO, &memo,
-1);
//to display total
weight = arc == NULL ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
g_object_set(renderer, "weight", weight, "text", memo, NULL);
//leak
g_free(memo);
}
static void
ui_arc_listview_cell_data_function_memo (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *text = NULL;
gtk_tree_model_get(model, iter, LST_DSPUPC_DATAS, &arc, -1);
if(arc)
{
text = arc->memo;
}
g_object_set(renderer, "text", text, NULL);
}
static void
lst_sch_cell_data_func_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gdouble expense, income, amount;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint column = GPOINTER_TO_INT(user_data);
gchar *color;
gint weight;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
LST_DSPUPC_EXPENSE, &expense,
LST_DSPUPC_INCOME , &income,
-1);
amount = (column == -1) ? expense : income;
if( amount != 0.0 )
{
guint32 kcur = _arc_get_curr(arc, column);
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, kcur, GLOBALS->minor);
color = get_normal_color_amount(amount);
weight = (arc == NULL) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
g_object_set(renderer,
"weight", weight,
"foreground", color,
"text", buf,
NULL);
}
else
{
g_object_set(renderer, "text", NULL, NULL);
}
}
static void
ui_arc_listview_cell_data_function_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gdouble amount;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
Account *acc;
gchar *color;
gint weight;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
amount = arc->amount;
if( amount != 0.0)
{
acc = da_acc_get(arc->kacc);
if( acc != NULL )
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, acc->kcur, GLOBALS->minor);
else
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, GLOBALS->kcur, GLOBALS->minor);
color = get_normal_color_amount(amount);
weight = arc == NULL ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;
g_object_set(renderer,
"weight", weight,
"foreground", color,
"text", buf,
NULL);
}
else
{
g_object_set(renderer, "text", NULL, NULL);
}
}
static void
ui_arc_listview_cell_data_func_info (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *iconname = NULL;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
if( arc != NULL )
iconname = (arc->flags & OF_INTXFER) ? ICONNAME_HB_PM_INTXFER : (gchar *)get_paymode_icon_name(arc->paymode);
g_object_set(renderer, "icon-name", iconname, NULL);
break;
case 2:
//list_txn_eval_future(renderer, ope);
if( arc != NULL )
text = arc->number;
#if MYDEBUG
if( arc != NULL )
{
gchar *ds = g_strdup_printf ("%s kx[%d] f[%d]", text == NULL ? "" : text, arc->kxferacc, arc->flags );
g_object_set(renderer, "text", ds, NULL);
g_free(ds);
}
#else
g_object_set(renderer, "text", text, NULL);
#endif
break;
}
}
static void
ui_arc_listview_cell_data_func_clr (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *iconname = NULL;
//const gchar *c = "";
gtk_tree_model_get(model, iter,
LST_DSPUPC_DATAS, &arc,
-1);
if( arc != NULL )
{
switch(GPOINTER_TO_INT(user_data))
{
case 1:
{
if( arc->flags & OF_REMIND )
iconname = ICONNAME_HB_ITEM_REMIND;
break;
}
case 2:
{
switch(arc->status)
{
/*case TXN_STATUS_CLEARED: c = "c"; break;
case TXN_STATUS_RECONCILED: c = "R"; break;
case TXN_STATUS_REMIND: c = "!"; break;*/
case TXN_STATUS_CLEARED: iconname = ICONNAME_HB_ITEM_CLEAR; break;
case TXN_STATUS_RECONCILED: iconname = ICONNAME_HB_ITEM_RECON; break;
//case TXN_STATUS_REMIND: iconname = ICONNAME_HB_ITEM_REMIND; break;
case TXN_STATUS_VOID: iconname = ICONNAME_HB_ITEM_VOID; break;
}
break;
}
}
}
//TODO 5.6 after switch to on the change prevent do not display, maybe gtk bug
//DB( g_print("\n[list_txn] clr lockrecon=%d, icon=%s", data->lockreconciled, iconname) );
//g_object_set(renderer, "text", c, NULL);
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
lst_sch_cell_data_func_account (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *arc;
gchar *text = NULL;
gtk_tree_model_get(model, iter, LST_DSPUPC_DATAS, &arc, -1);
if( arc != NULL )
{
Account *acc = da_acc_get(arc->kacc);
if(acc != NULL)
text = acc->name;
}
g_object_set(renderer, "text", text, NULL);
}
/*
** The function should return:
** a negative integer if the first value comes before the second,
** 0 if they are equal,
** or a positive integer if the first value comes after the second.
*/
static gint ui_arc_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
Archive *item1, *item2;
gdouble tmpval;
gint retval = 0;
gtk_tree_model_get(model, a, LST_DSPUPC_DATAS, &item1, -1);
gtk_tree_model_get(model, b, LST_DSPUPC_DATAS, &item2, -1);
switch (sortcol)
{
case COL_SCH_UID_NEXTDATE:
retval = item1->nextdate - item2->nextdate;
//#2024956 default to next date
if(!retval)
goto domemo;
break;
case COL_SCH_UID_MEMO:
domemo: retval = (item1->flags & GF_INCOME) - (item2->flags & GF_INCOME);
if(!retval)
{
retval = hb_string_utf8_compare(item1->memo, item2->memo);
}
break;
case COL_SCH_UID_PAYNUMBER:
if(!(retval = item1->paymode - item2->paymode))
{
retval = hb_string_utf8_compare(item1->number, item2->number);
}
break;
case COL_SCH_UID_PAYEE:
{
Payee *p1, *p2;
p1 = da_pay_get(item1->kpay);
p2 = da_pay_get(item2->kpay);
if( p1 != NULL && p2 != NULL )
{
retval = hb_string_utf8_compare(p1->name, p2->name);
}
}
break;
case COL_SCH_UID_CATEGORY:
{
Category *c1, *c2;
c1 = da_cat_get(item1->kcat);
c2 = da_cat_get(item2->kcat);
if( c1 != NULL && c2 != NULL )
{
retval = hb_string_utf8_compare(c1->fullname, c2->fullname);
}
}
break;
case COL_SCH_UID_CLR:
retval = item1->status - item2->status;
break;
case COL_SCH_UID_AMOUNT:
tmpval = item1->amount - item2->amount;
retval = tmpval > 0 ? 1 : -1;
break;
//#1928147 sort on account as well
case COL_SCH_UID_ACCOUNT:
{
Account *a1, *a2;
a1 = da_acc_get(item1->kacc);
a2 = da_acc_get(item2->kacc);
if( a1 != NULL && a2 != NULL )
{
retval = hb_string_utf8_compare(a1->name, a2->name);
}
}
break;
default:
g_return_val_if_reached(0);
}
return retval;
}
static void
ui_arc_listview_cell_data_func_icons (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *item;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DSPUPC_DATAS, &item, -1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
iconname = ( item->dspflags & FLAG_TMP_PREFILLED ) ? ICONNAME_HB_ITEM_PREFILLED : NULL;
break;
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
ui_arc_listview_cell_data_function_auto (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Archive *item;
gchar *info, *iconname;
// get the transaction
gtk_tree_model_get(model, iter, LST_DSPUPC_DATAS, &item, -1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
iconname = ( item->rec_flags & TF_RECUR ) ? ICONNAME_HB_ITEM_AUTO : NULL;
g_object_set(renderer, "icon-name", iconname, NULL);
break;
case 2:
info = NULL;
//#1898294 not translated
if( ( item->rec_flags & TF_RECUR ) )
info = g_strdup_printf("%d %s", item->rec_every, ui_arc_listview_get_freq_label(item->rec_freq));
g_object_set(renderer, "text", info, NULL);
g_free(info);
break;
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gboolean
ui_arc_listview_func_column_drop (GtkTreeView *tree_view,
GtkTreeViewColumn *column,
GtkTreeViewColumn *prev_column,
GtkTreeViewColumn *next_column,
gpointer data)
{
gboolean retval = FALSE;
//DB( g_print ("\n[lst_sch] column drop %p %p\n", prev_column, next_column) );
if( prev_column != NULL )
{
gint coluid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(prev_column), "uid"));
retval = coluid < COL_SCH_UID_PAYNUMBER ? FALSE : TRUE;
//DB( g_print (" coluid=%d > %d\n", coluid, retval) );
}
return retval;
}
static gboolean
lst_sch_cb_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
{
GtkTreeIter iter;
Archive *arc;
if(gtk_tree_model_get_iter(model, &iter, path))
{
gtk_tree_model_get(model, &iter,
LST_DSPUPC_DATAS, &arc,
-1);
if( arc == NULL )
return FALSE;
}
return TRUE;
}
static void
lst_sch_popmenu_cb_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
{
GtkTreeViewColumn *column = user_data;
//GtkWidget *treeview;
DB( g_print ("\n[lst_sch] menuitem activated\n") );
if( !GTK_IS_TREE_VIEW_COLUMN(column) )
return;
//TDOO: useless until we link dsp_accoutn balance to this list
//treeview = gtk_tree_view_column_get_tree_view(GTK_TREE_VIEW_COLUMN(column));
gtk_tree_view_column_set_visible(column, gtk_check_menu_item_get_active(checkmenuitem) );
//lst_sch_columns_prefs_get(GTK_TREE_VIEW(treeview));
}
static gint
lst_sch_cb_button_pressed_event (GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
{
GdkEventType type;
guint button = 0;
type = gdk_event_get_event_type(event);
gdk_event_get_button(event, &button);
DB( g_print ("\n[lst_sch] popmenu pop\n") );
if (type == GDK_BUTTON_PRESS && button == 3)
{
// check we ARE in the header but in bin window
if (gdk_event_get_window(event) != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)))
{
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
#else
gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gdk_event_get_time(event));
#endif
// On indique à l'appelant que l'on a géré cet événement.
return TRUE;
}
// On indique à l'appelant que l'on n'a pas géré cet événement.
}
return FALSE;
}
static void
lst_sch_popmenu_destroy(GtkTreeView *treeview, gpointer user_data)
{
DB( g_print ("\n[lst_sch] menu destroy\n") );
}
static GtkWidget *
lst_sch_popmenu_new(GtkTreeView *treeview)
{
GtkWidget *menu, *menuitem;
guint i;
DB( g_print ("\n[lst_sch] create popmenu\n") );
menu = gtk_menu_new();
//data->ME_popmenu = menu;
for(i=0 ; i < gtk_tree_view_get_n_columns(GTK_TREE_VIEW(treeview)) - 1 ; i++ )
{
GtkTreeViewColumn *column = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i);
if( column != NULL )
{
gint uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
if( (uid == COL_SCH_UID_PAYEE)
|| (uid == COL_SCH_UID_CATEGORY)
|| (uid == COL_SCH_UID_MEMO) )
{
menuitem = gtk_check_menu_item_new_with_label ( gtk_tree_view_column_get_title (column) );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gtk_tree_view_column_get_visible (column) );
g_signal_connect (menuitem, "activate", G_CALLBACK (lst_sch_popmenu_cb_activate), column);
DB( g_print (" [%02d] uid=%02d add\n", i, uid) );
}
}
}
g_signal_connect (menu, "destroy", G_CALLBACK (lst_sch_popmenu_destroy), NULL);
gtk_widget_show_all(menu);
return menu;
}
static void
lst_sch_widget_columns_prefs_set(struct lst_sch_data *data)
{
guint i;
DB( g_print ("\n[lst_sch] colums prefs set\n") );
for(i=COL_SCH_UID_PAYEE;itreeview));i++)
{
GtkTreeViewColumn *column = gtk_tree_view_get_column(GTK_TREE_VIEW(data->treeview), i);
if(column)
{
gint coluid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
switch(coluid)
{
case COL_SCH_UID_PAYEE:
gtk_tree_view_column_set_visible(column, PREFS->pnl_upc_col_pay_show);
gtk_tree_view_column_set_fixed_width(column, PREFS->pnl_upc_col_pay_width);
break;
case COL_SCH_UID_CATEGORY:
gtk_tree_view_column_set_visible(column, PREFS->pnl_upc_col_cat_show);
gtk_tree_view_column_set_fixed_width(column, PREFS->pnl_upc_col_cat_width);
break;
case COL_SCH_UID_MEMO:
gtk_tree_view_column_set_visible(column, PREFS->pnl_upc_col_mem_show);
gtk_tree_view_column_set_fixed_width(column, PREFS->pnl_upc_col_mem_width);
break;
}
}
}
}
static void
lst_sch_widget_columns_prefs_get(struct lst_sch_data *data)
{
guint i;
DB( g_print ("\n[lst_sch] colums prefs get\n") );
for(i=COL_SCH_UID_PAYEE;itreeview));i++)
{
GtkTreeViewColumn *column = gtk_tree_view_get_column(GTK_TREE_VIEW(data->treeview), i);
if(column)
{
gint coluid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
//#1830656 use xxx_get_fixed_width instead of width, as if not visible will save 0 otherwise
switch(coluid)
{
case COL_SCH_UID_PAYEE:
PREFS->pnl_upc_col_pay_show = gtk_tree_view_column_get_visible(column);
PREFS->pnl_upc_col_pay_width = gtk_tree_view_column_get_fixed_width(column);
break;
case COL_SCH_UID_CATEGORY:
PREFS->pnl_upc_col_cat_show = gtk_tree_view_column_get_visible(column);
PREFS->pnl_upc_col_cat_width = gtk_tree_view_column_get_fixed_width(column);
break;
case COL_SCH_UID_MEMO:
PREFS->pnl_upc_col_mem_show = gtk_tree_view_column_get_visible(column);
PREFS->pnl_upc_col_mem_width = gtk_tree_view_column_get_fixed_width(column);
break;
}
}
}
}
// test 5.8
static GtkTreeViewColumn *
ui_arc_listview_widget_get_column_by_uid(GtkTreeView *treeview, gint uid)
{
GtkTreeViewColumn *column;
guint i;
gint coluid;
for(i=0;ilst_sch_columns;
DB( g_print ("\n[lst_sch] columns set order\n") );
base_column = ui_arc_listview_widget_get_column_by_uid(treeview, COL_SCH_UID_NEXTDATE);
for(i=0;imove %d '%s' after %d '%s'\n",
colpos_ptr[i],
gtk_tree_view_column_get_title(column),
GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(base_column), "uid")),
gtk_tree_view_column_get_title(base_column)
) );
gtk_tree_view_move_column_after(treeview, column, base_column);
base_column = column;
}
}
}
void
ui_arc_listview_widget_columns_order_save(GtkTreeView *treeview)
{
guint i;
gint *colpos_ptr = PREFS->lst_sch_columns;
DB( g_print ("\n[lst_arc] columns get order\n") );
for(i=0;i= COL_SCH_UID_PAYNUMBER )
{
DB( g_print(" > store %d\n", coluid) );
*colpos_ptr++ = coluid;
//as amount is split into upcoming, fill in exp/inc after
if( coluid == COL_SCH_UID_AMOUNT )
{
*colpos_ptr++ = COL_SCH_UID_EXPENSE;
*colpos_ptr++ = COL_SCH_UID_INCOME;
}
}
}
}
*colpos_ptr = 0;
}
static GtkTreeViewColumn *
ui_arc_listview_column_info_create(gint sortcolumnid)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Pay./Number"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_info, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_info, GINT_TO_POINTER(2), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_PAYNUMBER));
if( sortcolumnid > 0 )
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_PAYNUMBER);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
return column;
}
static GtkTreeViewColumn *
lst_sch_listview_column_text_create(gchar *title, gint uid, GtkTreeCellDataFunc func, gpointer user_data)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
if( uid > 0 )
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(uid));
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
//gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
//gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, func, user_data, NULL);
return column;
}
static void
lst_sch_widget_destroy(GtkTreeView *treeview, gpointer user_data)
{
struct lst_sch_data *data;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
DB( g_print ("\n[lst_sch] destroy\n") );
lst_sch_widget_columns_prefs_get(data);
gtk_widget_destroy (data->menu);
}
GtkWidget *
lst_sch_widget_new(gint listtype)
{
struct lst_sch_data *data;
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print ("\n[lst_sch] create\n") );
data = g_malloc0(sizeof(struct lst_sch_data));
if(!data) return NULL;
/* create list store */
store = gtk_list_store_new(
NUM_LST_DSPUPC,
G_TYPE_POINTER, /* scheduled */
G_TYPE_INT, /* next (sort) */
G_TYPE_STRING, /* memo/total */
G_TYPE_DOUBLE, /* expense */
G_TYPE_DOUBLE, /* income */
G_TYPE_INT /* nb late */
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
data->treeview = treeview;
g_object_unref(store);
//store our window private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, data) );
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_MULTIPLE);
/* column: system */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//add system icon to 1st column
gtk_tree_view_column_set_clickable(column, TRUE);
GtkWidget *img = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
gtk_widget_show(img);
gtk_tree_view_column_set_widget(column, img);
/* column : Late */
column = gtk_tree_view_column_new();
//TRANSLATORS: title of list column to inform the scheduled transaction is Late
gtk_tree_view_column_set_title(column, _("Late"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_lateicon, NULL, NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_latetext, NULL, NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_DSPUPC_NB_LATE);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column : Still (for limited scheduled) */
column = gtk_tree_view_column_new();
//TRANSLATORS: title of list column to inform how many occurence remain to post for limited scheduled txn
gtk_tree_view_column_set_title(column, _("Still"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_still, NULL, NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_DSPUPC_REMAINING);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Next Date */
renderer = gtk_cell_renderer_text_new ();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Next date"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_date, NULL, NULL);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_NEXTDATE));
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// info column
column = ui_arc_listview_column_info_create(-1);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Payee */
column = lst_sch_listview_column_text_create(_("Payee"), COL_SCH_UID_PAYEE, lst_sch_cell_data_func_payee, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Category */
column = lst_sch_listview_column_text_create(_("Category"), COL_SCH_UID_CATEGORY, lst_sch_cell_data_func_category, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Memo */
column = lst_sch_listview_column_text_create(_("Memo"), COL_SCH_UID_MEMO, lst_sch_cell_data_func_memo, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// status column
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Status"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_clr, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_clr, GINT_TO_POINTER(2), NULL);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_CLR));
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Amount */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Expense"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_amount, GINT_TO_POINTER(-1), NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_DSPACC_NAME);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_EXPENSE));
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Amount */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Income"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_amount, GINT_TO_POINTER(1), NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_DSPACC_NAME);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_INCOME));
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Account */
column = lst_sch_listview_column_text_create(_("Account"), COL_SCH_UID_ACCOUNT, lst_sch_cell_data_func_account, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeview func
gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), lst_sch_cb_selection_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DSPUPC_NEXT, GTK_SORT_ASCENDING);
lst_sch_widget_columns_prefs_set(data);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
GtkWidget *popmenu = lst_sch_popmenu_new(GTK_TREE_VIEW(treeview));
data->menu = popmenu;
g_signal_connect (treeview, "destroy", G_CALLBACK (lst_sch_widget_destroy), NULL);
g_signal_connect (treeview, "button-press-event", G_CALLBACK (lst_sch_cb_button_pressed_event), popmenu);
return(treeview);
}
GtkWidget *ui_arc_listview_widget_new(void)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
//store
store = gtk_list_store_new (
1,
G_TYPE_POINTER /* scheduled */
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column: icons
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_icons, GINT_TO_POINTER(1), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Scheduled icon */
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_function_auto, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_function_auto, GINT_TO_POINTER(2), NULL);
gtk_tree_view_column_set_spacing(column, SPACING_TINY);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Date Next on */
renderer = gtk_cell_renderer_text_new ();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Next date"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_sch_cell_data_func_date, NULL, NULL);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_NEXTDATE));
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_NEXTDATE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// info column
column = ui_arc_listview_column_info_create(COL_SCH_UID_PAYNUMBER);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Payee */
column = lst_sch_listview_column_text_create(_("Payee"), COL_SCH_UID_PAYEE, lst_sch_cell_data_func_payee, NULL);
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_PAYEE);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Category */
column = lst_sch_listview_column_text_create(_("Category"), COL_SCH_UID_CATEGORY, lst_sch_cell_data_func_category, NULL);
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_CATEGORY);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column: Memo */
column = lst_sch_listview_column_text_create(_("Memo"), COL_SCH_UID_MEMO, ui_arc_listview_cell_data_function_memo, NULL);
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_MEMO);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// status column
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Status"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_clr, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_func_clr, GINT_TO_POINTER(2), NULL);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_CLR));
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_CLR);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column : amount */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Amount"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_arc_listview_cell_data_function_amount, NULL, NULL);
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_SCH_UID_AMOUNT));
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_AMOUNT);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column : Account */
column = lst_sch_listview_column_text_create(_("Account"), COL_SCH_UID_ACCOUNT, lst_sch_cell_data_func_account, NULL);
gtk_tree_view_column_set_sort_column_id (column, COL_SCH_UID_ACCOUNT);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column : empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//sortable
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_NEXTDATE, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_NEXTDATE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_PAYNUMBER, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_PAYNUMBER), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_PAYEE, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_PAYEE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_CATEGORY, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_CATEGORY), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_CLR, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_CLR), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_AMOUNT, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_AMOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_MEMO, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_MEMO), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SCH_UID_ACCOUNT, ui_arc_listview_compare_func, GINT_TO_POINTER(COL_SCH_UID_ACCOUNT), NULL);
//#2024956 default to next date
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COL_SCH_UID_MEMO, GTK_SORT_ASCENDING);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), COL_SCH_UID_NEXTDATE, GTK_SORT_ASCENDING);
//gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE);
//gtk_tree_view_set_reorderable (GTK_TREE_VIEW(view), TRUE);
gtk_tree_view_set_column_drag_function(GTK_TREE_VIEW(treeview), ui_arc_listview_func_column_drop, NULL, NULL);
return(treeview);
}
homebank-5.9.7/src/ui-assist-start.c 0000644 0001750 0001750 00000055071 15005634020 016624 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-assist-start.h"
#include "dsp-mainwindow.h"
#include "hub-account.h"
#include "ui-currency.h"
#include "ui-widgets.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern HbKvData CYA_ACC_TYPE[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkWidget *
ui_newfile_page_intro_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *mainbox, *label;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
//gtk_widget_set_halign(mainbox, GTK_ALIGN_CENTER);
//gtk_widget_set_valign(mainbox, GTK_ALIGN_CENTER);
//assistant box is 12, so add 6
hb_widget_set_margin(mainbox, SPACING_SMALL);
label = make_label(
_("This assistant will help you setup a minimum configuration\n" \
"for a new HomeBank file."), 0, 0);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
label = make_label(
_("All the elements you setup here can be changed later if required."), 0, 0);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
label = make_label(
_("No changes will be made until you click \"Apply\"\n" \
"at the end of this assistant."), 0., 0.0);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
gtk_widget_show_all (mainbox);
return mainbox;
}
/* = = = = = = = = = = = = = = = = */
static void
ui_newfile_entry_changed (GtkWidget *widget, gpointer data)
{
GtkAssistant *assistant = GTK_ASSISTANT (data);
GtkWidget *current_page;
gint page_number;
gchar *text;
page_number = gtk_assistant_get_current_page (assistant);
current_page = gtk_assistant_get_nth_page (assistant, page_number);
//#1837838: complete space or leadin/trialin space is possible
text = g_strdup(gtk_entry_get_text (GTK_ENTRY (widget)));
g_strstrip(text);
if (strlen(text) > 0)
gtk_assistant_set_page_complete (assistant, current_page, TRUE);
else
gtk_assistant_set_page_complete (assistant, current_page, FALSE);
g_free(text);
}
static void
ui_newfile_page_general_fill (GtkWidget *assistant, struct assist_start_data *data)
{
Currency *cur;
gchar label[128];
DB( g_print("\n[ui-start] property_fill\n") );
gtk_entry_set_text(GTK_ENTRY(data->ST_owner), g_get_real_name ());
cur = da_cur_get (GLOBALS->kcur);
g_snprintf(label, 127, "%s (%s)", cur->iso_code, cur->name);
gtk_label_set_text (GTK_LABEL(data->LB_cur_base), label);
}
static GtkWidget *
ui_newfile_page_general_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *mainbox, *hbox, *label, *widget;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
//gtk_widget_set_halign(mainbox, GTK_ALIGN_CENTER);
//gtk_widget_set_valign(mainbox, GTK_ALIGN_CENTER);
//assistant box is 12, so add 6
hb_widget_set_margin(mainbox, SPACING_SMALL);
// 123456789012345678901234567890123456789012345678901234567890
label = make_label(_("HomeBank will display a title for the main window,\n" \
"it can be a free label or your name."), 0, 0.5);
gtk_widget_set_margin_bottom(label, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
//gtk_widget_set_halign(hbox, GTK_ALIGN_CENTER);
gtk_box_prepend (GTK_BOX (mainbox), hbox);
label = make_label_widget(_("_Title:"));
gtk_box_prepend (GTK_BOX (hbox), label);
widget = make_string(label);
data->ST_owner = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
//TODO: later we will let the user choose to use:
//budget
//cleared/validated ...
//life energy
g_signal_connect (G_OBJECT (data->ST_owner), "changed", G_CALLBACK (ui_newfile_entry_changed), assistant);
gtk_widget_show_all (mainbox);
return mainbox;
}
/* = = = = = = = = = = = = = = = = */
static void ui_newfile_page_currency_add_action(GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data;
struct curSelectContext selectCtx;
gint result;
DB( g_print("\n[ui-start] property_add_action\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
result = ui_cur_select_dialog_new(GTK_WINDOW(data->dialog), CUR_SELECT_MODE_NOCUSTOM, &selectCtx);
if( result == GTK_RESPONSE_ACCEPT )
{
if( selectCtx.cur_4217 != NULL )
{
Currency4217 *curfmt = selectCtx.cur_4217;
DB( g_print(" adding '%s' (%s)\n", curfmt->curr_iso_code, curfmt->name) );
g_ptr_array_add(data->cur_arr, curfmt);
//TODO: refresh the label
GString *strcur = g_string_new(NULL);
for(guint i=0;icur_arr->len;i++)
{
Currency4217 *elt = g_ptr_array_index(data->cur_arr, i);
g_string_append_printf(strcur, "%s (%s)\r\n", elt->curr_iso_code, elt->name);
}
gtk_label_set_text (GTK_LABEL(data->LB_cur_others), strcur->str);
g_string_free(strcur, TRUE);
}
}
}
static void ui_newfile_page_currency_change_action(GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data;
struct curSelectContext selectCtx;
DB( g_print("\n[ui-start] property_change_action\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->curfmt = NULL;
ui_cur_select_dialog_new(GTK_WINDOW(data->dialog), CUR_SELECT_MODE_BASE, &selectCtx);
if( selectCtx.cur_4217 != NULL )
{
Currency4217 *curfmt;
gchar label[128];
gchar *name;
curfmt = selectCtx.cur_4217;
DB( g_printf("- user selected: '%s' '%s'\n", curfmt->curr_iso_code, curfmt->name) );
data->curfmt = curfmt;
name = curfmt->name;
g_snprintf(label, 127, "%s (%s)", curfmt->curr_iso_code, name);
gtk_label_set_text (GTK_LABEL(data->LB_cur_base), label);
}
}
static void
ui_newfile_page_currency_cb_toggle(GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data;
gboolean sensitive;
DB( g_print("\n[ui-start] property_cb_toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cur_add));
gtk_widget_set_sensitive(data->LB_cur_others, sensitive);
gtk_widget_set_sensitive(data->BT_cur_add, sensitive);
}
static GtkWidget *
ui_newfile_page_currency_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *mainbox, *hbox, *label, *widget;
GtkWidget *scrollwin;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
//assistant box is 12, so add 6
hb_widget_set_margin(mainbox, SPACING_SMALL);
// 123456789012345678901234567890123456789012345678901234567890
label = make_label(_("HomeBank support multiple currencies. The base currency is\n" \
"the default for new accounts and reports."), 0, 0.5);
gtk_widget_set_margin_bottom(label, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (mainbox), hbox);
label = make_label_widget(_("Base:"));
gtk_box_prepend (GTK_BOX (hbox), label);
widget = make_label (NULL, 0, 0.5);
data->LB_cur_base = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = gtk_button_new_with_mnemonic (_("_Change"));
data->BT_cur_change = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = gtk_check_button_new_with_mnemonic (_("Setup additional currencies"));
data->CM_cur_add = widget;
gtk_widget_set_margin_top(widget, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), widget);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (mainbox), scrollwin);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), 0.75*HB_MINHEIGHT_LIST);
widget = make_label(NULL, 0.0, 0.0);
data->LB_cur_others = widget;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
widget = gtk_button_new_with_mnemonic (_("_Add"));
gtk_widget_set_halign(widget, GTK_ALIGN_START);
data->BT_cur_add = widget;
gtk_box_prepend (GTK_BOX (mainbox), widget);
gtk_widget_set_sensitive(data->LB_cur_others, FALSE);
gtk_widget_set_sensitive(data->BT_cur_add, FALSE);
g_signal_connect (G_OBJECT (data->BT_cur_change), "clicked", G_CALLBACK (ui_newfile_page_currency_change_action), data);
g_signal_connect (G_OBJECT (data->CM_cur_add), "toggled", G_CALLBACK (ui_newfile_page_currency_cb_toggle), data);
g_signal_connect (G_OBJECT (data->BT_cur_add), "clicked", G_CALLBACK (ui_newfile_page_currency_add_action), data);
gtk_widget_show_all (mainbox);
return mainbox;
}
/* = = = = = = = = = = = = = = = = */
static void
hb_string_clean_csv_category(gchar *str)
{
gchar *s = str;
gchar *d = str;
if(str)
{
while( *s )
{
if( !(*s==';' || *s=='1' || *s=='-' || *s=='+' || *s=='2') )
{
*d++ = *s;
}
if( *s=='2' )
{
*d++ = ' ';
*d++ = '-';
*d++ = ' ';
}
s++;
}
*d = 0;
}
}
static void
ui_newfile_page_categories_cb_toggle(GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data;
gboolean sensitive;
DB( g_print("\n[ui-start] categories_cb_toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_load));
gtk_widget_set_sensitive(data->GR_file, sensitive);
gtk_widget_set_sensitive(data->TX_preview, sensitive);
}
static void
ui_newfile_page_categories_fill (GtkWidget *assistant, struct assist_start_data *data)
{
gchar *lang;
gchar *content;
data->pathfilename = category_find_preset(&lang);
//test no file
//data->pathfilename = NULL;
if(data->pathfilename != NULL)
{
gtk_label_set_label(GTK_LABEL(data->TX_file), lang);
gtk_widget_show(data->CM_load);
gtk_widget_show(data->ok_image);
gtk_widget_hide(data->ko_image);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_load), TRUE);
/* preview */
if( g_file_get_contents(data->pathfilename, &content, NULL, NULL) )
{
hb_string_clean_csv_category(content);
gtk_label_set_label(GTK_LABEL(data->TX_preview), content);
g_free(content);
}
}
else
{
gtk_widget_hide(data->CM_load);
gtk_label_set_label(GTK_LABEL(data->TX_file), _("Not found"));
gtk_widget_show(data->ko_image);
gtk_widget_hide(data->ok_image);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_load), FALSE);
}
}
static GtkWidget *
ui_newfile_page_categories_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *mainbox, *hbox, *label, *widget;
GtkWidget *scrollwin;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
//assistant box is 12, so add 6
hb_widget_set_margin(mainbox, SPACING_SMALL);
// 123456789012345678901234567890123456789012345678901234567890
label = make_label(_("HomeBank can prefill the categories for your language\n" \
"if a CSV file is available and provided by the community."), 0, 0.5);
gtk_widget_set_margin_bottom(label, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), label);
widget = gtk_check_button_new_with_mnemonic (_("Setup categories for my language"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
//gtk_widget_set_margin_bottom(widget, SPACING_LARGE);
data->CM_load = widget;
gtk_box_prepend (GTK_BOX (mainbox), widget);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_file = hbox;
gtk_box_prepend (GTK_BOX (mainbox), hbox);
label = make_label_widget(_("Preset file:"));
gtk_box_prepend (GTK_BOX (hbox), label);
widget = hbtk_image_new_from_icon_name_24(ICONNAME_HB_FILE_VALID);
data->ok_image = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_image_new_from_icon_name_24(ICONNAME_HB_FILE_INVALID);
data->ko_image = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = make_label(NULL, 0.0, 0.5);
data->TX_file = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), 0.75*HB_MINHEIGHT_LIST);
hbtk_box_prepend (GTK_BOX (mainbox), scrollwin);
widget = make_label(NULL, 0.0, 0.5);
data->TX_preview = widget;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
g_signal_connect (G_OBJECT (data->CM_load), "toggled", G_CALLBACK (ui_newfile_page_categories_cb_toggle), data);
gtk_widget_show_all (mainbox);
gtk_widget_hide(data->ok_image);
gtk_widget_hide(data->ko_image);
return mainbox;
}
/* = = = = = = = = = = = = = = = = */
static void
ui_newfile_page_account_cb_eval(GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data;
GtkWidget *current_page;
gint page_number;
gchar *text;
gboolean sensitive, valid;
DB( g_print("\n[ui-start] account_cb_eval\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
page_number = gtk_assistant_get_current_page (GTK_ASSISTANT(data->dialog));
//#1837838: complete space or leading/trailing space is possible
text = g_strdup(gtk_entry_get_text (GTK_ENTRY (data->ST_name)));
g_strstrip(text);
valid = (strlen(text) > 0) ? TRUE : FALSE;
g_free(text);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_acc_add));
gtk_widget_set_sensitive(data->GR_acc, sensitive);
if(!sensitive)
valid = TRUE;
current_page = gtk_assistant_get_nth_page (GTK_ASSISTANT(data->dialog), page_number);
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->dialog), current_page, valid);
}
static GtkWidget *
ui_newfile_page_account_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *mainbox, *group_grid, *label, *widget;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
//assistant box is 12, so add 6
hb_widget_set_margin(mainbox, SPACING_SMALL);
// 123456789012345678901234567890123456789012345678901234567890
label = make_label(_("HomeBank enables to import your accounts from downloaded\n" \
"financial institution files, or you can create your account manually."), 0, 0.5);
gtk_widget_set_margin_bottom(label, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), label);
widget = gtk_check_button_new_with_mnemonic (_("Create my first account"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
//gtk_widget_set_margin_bottom(widget, SPACING_LARGE);
data->CM_acc_add = widget;
gtk_box_prepend (GTK_BOX (mainbox), widget);
group_grid = gtk_grid_new ();
data->GR_acc = group_grid;
gtk_grid_set_row_spacing (GTK_GRID (group_grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (group_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainbox), group_grid);
label = make_label_widget(_("_Name:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 1, 1);
widget = make_string(label);
gtk_widget_set_hexpand(widget, TRUE);
data->ST_name = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, 0, 1, 1);
label = make_label_widget(_("_Type:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 1, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_ACC_TYPE);
gtk_widget_set_hexpand(widget, TRUE);
data->CY_type = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, 1, 1, 1);
gtk_widget_show_all (mainbox);
g_signal_connect (G_OBJECT (data->ST_name), "changed", G_CALLBACK (ui_newfile_page_account_cb_eval), data);
g_signal_connect (G_OBJECT (data->CM_acc_add), "toggled", G_CALLBACK (ui_newfile_page_account_cb_eval), data);
return mainbox;
}
/* = = = = = = = = = = = = = = = = */
static GtkWidget *
ui_newfile_page_confirmation_create (GtkWidget *assistant, struct assist_start_data *data)
{
GtkWidget *label;
label = gtk_label_new (_("This is a confirmation page,\n\npress 'Apply' to apply changes"));
gtk_widget_show (label);
return label;
}
/* = = = = = = = = = = = = = = = = */
static void
ui_newfile_assistant_prepare (GtkWidget *assistant, GtkWidget *page, gpointer user_data)
{
struct assist_start_data *data = user_data;
gint current_page;
//gint n_pages;
DB( g_print("\n[ui-start] prepare\n") );
current_page = gtk_assistant_get_current_page (GTK_ASSISTANT (assistant));
//n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant));
switch( current_page )
{
case PAGE_GENERAL:
ui_newfile_page_general_fill(assistant, data);
break;
case PAGE_CATEGORIES:
ui_newfile_page_categories_fill(assistant, data);
break;
}
}
static void
ui_newfile_assistant_apply (GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data = user_data;
Account *item;
DB( g_print("\n[ui-start] apply\n") );
/* set owner */
gchar *owner = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_owner));
if (owner && *owner)
{
hbfile_change_owner(g_strdup(owner));
GLOBALS->changes_count++;
}
if( data->curfmt != NULL )
{
hbfile_replace_basecurrency(data->curfmt);
}
// init other currencies
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cur_add)))
{
Currency *item = NULL;
guint i;
for(i=0;icur_arr->len;i++)
{
Currency4217 *curfmt = g_ptr_array_index(data->cur_arr, i);
DB( g_printf("- curr creating: '%s' '%s'\n", curfmt->curr_iso_code, curfmt->name) );
item = da_cur_get_by_iso_code(curfmt->curr_iso_code);
if( item == NULL )
{
item = currency_add_from_user(curfmt);
}
}
}
/* load preset categories */
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_load)))
{
if(data->pathfilename != NULL)
{
gchar *error;
category_load_csv(data->pathfilename, &error);
//DB( g_print(" -> loaded=%d\n", ok) );
}
}
/* initialise an account */
// init other currencies
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_acc_add)))
{
item = da_acc_malloc();
gchar *txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_name));
if (txt && *txt)
{
item->name = g_strdup(txt);
//#1837838 remove extra lead/tail space
g_strstrip(item->name);
}
item->kcur = GLOBALS->kcur;
item->type = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
da_acc_append(item);
GLOBALS->changes_count++;
}
//our global list has changed, so update the treeview
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
static void
ui_newfile_assitant_close_cancel (GtkWidget *widget, gpointer user_data)
{
struct assist_start_data *data = user_data;
DB( g_print("\n[ui-start] close/cancel\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
gtk_window_destroy (GTK_WINDOW(data->dialog));
g_free(data->pathfilename);
g_ptr_array_free(data->cur_arr, TRUE);
g_free(data);
}
GtkWidget*
ui_newfile_assitant_new (void)
{
struct assist_start_data *data;
GtkWidget *assistant, *page;
//gint w, h, dw, dh;
DB( g_print("\n[ui-start] new\n") );
data = g_malloc0(sizeof(struct assist_start_data));
if(!data) return NULL;
data->cur_arr = g_ptr_array_new();
assistant = gtk_assistant_new ();
data->dialog = assistant;
//set a nice dialog size
/*
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 2:3
dw = (dh * 2) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(assistant), dw, dh);
*/
//store our window private data
g_object_set_data(G_OBJECT(assistant), "inst_data", (gpointer)data);
//DB( g_print("** (import) window=%x, inst_data=%x\n", assistant, data) );
gtk_window_set_modal(GTK_WINDOW (assistant), TRUE);
gtk_window_set_transient_for(GTK_WINDOW(assistant), GTK_WINDOW(GLOBALS->mainwindow));
page = ui_newfile_page_intro_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Start File Setup"));
gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_INTRO);
gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant), page, TRUE);
page = ui_newfile_page_general_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("File Options"));
gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_INTRO);
page = ui_newfile_page_currency_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Choose Currencies"));
gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant), page, TRUE);
page = ui_newfile_page_categories_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Choose Categories"));
gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant), page, TRUE);
page = ui_newfile_page_account_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Create Account"));
page = ui_newfile_page_confirmation_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_CONFIRM);
gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant), page, TRUE);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Finish File Setup"));
g_signal_connect (G_OBJECT (assistant), "cancel", G_CALLBACK (ui_newfile_assitant_close_cancel), data);
g_signal_connect (G_OBJECT (assistant), "close", G_CALLBACK (ui_newfile_assitant_close_cancel), data);
g_signal_connect (G_OBJECT (assistant), "apply", G_CALLBACK (ui_newfile_assistant_apply), data);
g_signal_connect (G_OBJECT (assistant), "prepare", G_CALLBACK (ui_newfile_assistant_prepare), data);
#ifdef G_OS_WIN32
hbtk_assistant_hack_button_order(GTK_ASSISTANT(assistant));
#endif
gtk_widget_show (assistant);
return assistant;
}
homebank-5.9.7/src/hb-misc.c 0000644 0001750 0001750 00000067747 15120463545 015121 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//nota: this file should be renamed hb-utils
#include "homebank.h"
#include "hb-misc.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static const gdouble fac[9] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
double hb_amount_round(const gdouble d, guint digits)
{
gdouble out;
//fixed 5.6 MIN, not MAX... + #1977796
digits = MIN(digits, 8);
//initial formula
//out = floor((d * fac[digits]) + 0.5) / fac[digits];
//#1977796 fix rounding -0.5 : +0.5...
//out = round(d * fac[digits]) / fac[digits];
//#2018206 fix rounding -0.00...
//out = ((long) (d < 0 ? (d * fac[digits]) - 0.5 : (d * fac[digits]) + 0.5)) / fac[digits];
//#2022049 overflow on windows cause compiled 32bits long is int32 4 bytes...
out = ((gint64) (d < 0 ? (d * fac[digits]) - 0.5 : (d * fac[digits]) + 0.5)) / fac[digits];
//DB( g_print(" in:%17g out:%17g\n", d, out) );
return out;
}
// used to convert from national to euro currency
// used in hb_account.c :: only when convert account to euro
// round option is to 0.5 case so 1.32 is 1.3, but 1.35 is 1.4
gdouble hb_amount_to_euro(gdouble amount)
{
return hb_amount_round((amount * PREFS->euro_value), PREFS->minor_cur.frac_digits);
}
static gdouble hb_amount_minor(gdouble value, guint32 kcur)
{
Currency *cur = da_cur_get(kcur);
gdouble monval = value;
//g_print("\nminor :: %.2f kcur=%s, mceii:%d\n", value, cur->iso_code, PREFS->euro_mceii);
//euro is major
if(PREFS->euro_mceii == TRUE)
{
monval = hb_amount_base(value, kcur);
monval = hb_amount_to_euro(monval);
//g_print("> old behavior %.2f\n", monval);
}
//euro is futur major
else
{
//not EUR
if( strcmp("EUR", cur->iso_code) )
{
monval = hb_amount_base(value, kcur);
monval = hb_amount_to_euro(monval);
//g_print("> convert base/eur %.2f\n", monval);
}
}
return monval;
}
/* new >5.1 currency fct
*
* convert an amount in base currency
*
*/
gdouble hb_amount_base(gdouble value, guint32 kcur)
{
gdouble newvalue;
Currency *cur;
if(kcur == GLOBALS->kcur)
return value;
cur = da_cur_get(kcur);
if(cur == NULL || cur->rate == 0.0)
return 0.0;
newvalue = value / cur->rate;
return hb_amount_round(newvalue, cur->frac_digits);
}
/* we have rate versus base curency */
/* only use in ui-txn */
gdouble hb_amount_convert(gdouble value, guint32 skcur, guint32 dkcur)
{
gdouble newvalue;
Currency *cur;
if( skcur == dkcur )
return value;
/* we can't convert if no base currency as src or dst */
if( skcur != GLOBALS->kcur && dkcur != GLOBALS->kcur )
return 0.0;
if(dkcur == GLOBALS->kcur)
{
cur = da_cur_get(skcur);
if(cur == NULL || cur->rate == 0.0)
return 0.0;
newvalue = value / cur->rate;
}
if(skcur == GLOBALS->kcur)
{
cur = da_cur_get(dkcur);
if(cur == NULL || cur->rate == 0.0)
return 0.0;
newvalue = value * cur->rate;
}
return hb_amount_round(newvalue, cur->frac_digits);
}
gboolean hb_amount_type_match(gdouble amount, gint type)
{
gboolean retval = TRUE;
if( (type == TXN_TYPE_EXPENSE) && (amount > 0) )
retval = FALSE;
if( (type == TXN_TYPE_INCOME ) && (amount < 0) )
retval = FALSE;
return retval;
}
gboolean hb_amount_between(gdouble val, gdouble min, gdouble max)
{
gboolean retval = FALSE;
if(val > 0.0)
retval = (val >= min && val <= max);
else
if(val < 0.0)
retval = (val <= min && val >= max);
return retval;
}
// TODO: frac should be from currency
// return -1 if val1 < val2 ; 0 if val1 = val2 ; 1 if val1 > val2
gint hb_amount_cmp(gdouble val1, gdouble val2)
{
if( hb_amount_round(val1, 2) < hb_amount_round(val2, 2))
return -1;
if( hb_amount_round(val1, 2) == hb_amount_round(val2, 2))
return 0;
return 1;
}
gint hb_amount_forced_sign(const gchar *txt)
{
gint retval = HB_AMT_SIGN_OFF;
if( *txt == '+' )
{
retval = HB_AMT_SIGN_INC;
}
else if( *txt == '-' )
{
retval = HB_AMT_SIGN_EXP;
}
return retval;
}
static Currency *hb_strfmon_check(gchar *outstr, guint32 kcur)
{
Currency *cur = da_cur_get(kcur);
if(cur == NULL)
g_stpcpy(outstr, "nan");
return cur;
}
gdouble hb_rate(gdouble value, gdouble total)
{
return (total) ? ABS((value * 100 / total)) : 0.0;
}
gchar *hb_str_rate(gchar *outstr, gint outlen, gdouble rate)
{
gint count, i;
gchar *p;
count = g_snprintf(outstr, outlen, "%.6f", rate);
//remove trailing 0 and decimal point
p = &outstr[count-1];
for(i=count;i>0;i--)
{
if(*p == '0')
*p = '\0';
else
break;
p--;
}
if(*p == '.' || *p == ',')
*p = '\0';
return outstr;
}
/* this function copy a number 99999.99 at s into d and count
* number of digits for integer part and decimal part
*/
static gchar * _strfnumcopycount(gchar *s, gchar *d, gchar *decchar, gint *plen, gint *pnbint, gint *pnbdec)
{
gint len=0, nbint=0, nbdec=0;
// sign part
if(*s == '-') {
*d++ = *s++;
len++;
}
// integer part
while(*s != 0 && *s != '.') {
*d++ = *s++;
nbint++;
len++;
}
// decimal separator
if(*s == '.') {
d = g_stpcpy(d, decchar);
len++;
s++;
}
// decimal part
while(*s != 0) {
*d++ = *s++;
nbdec++;
len++;
}
// end string | fill external count
*d = 0;
*plen = len;
*pnbint = nbint;
*pnbdec = nbdec;
return d;
}
gchar *hb_str_formatd(gchar *outstr, gint outlen, gchar *buf1, Currency *cur, gboolean showsymbol)
{
gint len, nbd, nbi;
gchar *s, *d, *tmp;
d = tmp = outstr;
if(showsymbol && cur->sym_prefix)
{
d = g_stpcpy (d, cur->symbol);
*d++ = ' ';
tmp = d;
}
d = _strfnumcopycount(buf1, d, cur->decimal_char, &len, &nbi, &nbd);
if( cur->grouping_char != NULL && strlen(cur->grouping_char) > 0 )
{
gint i, grpcnt;
s = buf1;
d = tmp;
if(*s == '-')
*d++ = *s++;
grpcnt = 4 - nbi;
for(i=0;igrouping_char);
}
grpcnt++;
}
if(nbd > 0)
{
d = g_stpcpy(d, cur->decimal_char);
d = g_stpcpy(d, s+1);
}
*d = 0;
}
if(showsymbol && !cur->sym_prefix)
{
*d++ = ' ';
d = g_stpcpy (d, cur->symbol);
}
*d = 0;
return d;
}
void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar formatd_buf[outlen];
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
else
{
monval = hb_amount_minor(value, kcur);
cur = &PREFS->minor_cur;
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
// used only in gtk_chart.c
void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar formatd_buf[outlen];
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, "%0.f", monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
else
{
monval = hb_amount_minor(value, kcur);
cur = &PREFS->minor_cur;
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
//TODO: check where used, maybe use "%.*f" syntax here
void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
//#1868185 print raw number, not with monetary
g_ascii_formatd(outstr, outlen, "%.2f", monval);
}
}
else
{
monval = hb_amount_minor(value, kcur);
cur = &PREFS->minor_cur;
//#1868185 print raw number, not with monetary
g_ascii_formatd(outstr, outlen, "%.2f", monval);
}
}
void hb_strfmongc(gchar *outstr, gint outlen, gdouble value)
{
gchar formatd_buf[outlen];
Currency *cur = hb_strfmon_check(outstr, GLOBALS->kcur);
if(cur != NULL)
{
gdouble monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, FALSE);
}
}
void _format_decimal(GString *node, ToStringMode mode, gdouble value)
{
if(mode == HB_STRING_PRINT)
{
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
hb_strfmongc(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value);
g_string_append(node, buf);
}
else
g_string_append_printf(node, "%.2f", value);
}
void hb_strlifeenergy(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar buf_energy[16];
gdouble monval;
gdouble intval, fracval;
gshort h, m;
//first print monetary
hb_strfmon(outstr, outlen, value, kcur, minor);
if( (hb_amount_cmp(GLOBALS->lifen_earnbyh, 0.0) != 0) && value < 0.0)
{
monval = hb_amount_base(value, kcur);
fracval = modf(ABS(monval) / GLOBALS->lifen_earnbyh, &intval);
h = (gint)intval;
m = (gint)(fracval*60);
if( (gint)(m / 60) > 30 ) m++;
g_sprintf(buf_energy, " (%dh%02dm)", h, m);
strcat(outstr, buf_energy);
}
}
gchar *get_normal_color_amount(gdouble value)
{
gchar *color = NULL;
//fix: 400483
value = hb_amount_round(value, 2);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
}
return color;
}
gchar *get_minimum_color_amount(gdouble value, gdouble minvalue)
{
gchar *color = NULL;
//fix: 400483
value = hb_amount_round(value, 2);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
if( value < minvalue)
color = PREFS->color_warn;
}
return color;
}
void hb_label_set_amount(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
gtk_label_set_text(GTK_LABEL(label), strbuffer);
}
/*
** format/color and set a label text with a amount value
*/
void hb_label_set_colvalue(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
gchar *markuptxt;
gchar *color = NULL;
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = get_normal_color_amount(value);
//g_print("color: %s\n", color);
if(color)
{
markuptxt = g_strdup_printf("%s", color, strbuffer);
gtk_label_set_markup(GTK_LABEL(label), markuptxt);
g_free(markuptxt);
return;
}
}
gtk_label_set_text(GTK_LABEL(label), strbuffer);
}
/*
** String utility
*/
gint hb_string_ascii_compare(gchar *s1, gchar *s2)
{
return g_ascii_strncasecmp(s1 == NULL ? "" : s1, s2 == NULL ? "" : s2, -1);
}
gint hb_string_compare(gchar *s1, gchar *s2)
{
gint retval = 0;
if (s1 == NULL || s2 == NULL)
{
if (s1 == NULL && s2 == NULL)
goto end;
retval = (s1 == NULL) ? -1 : 1;
}
else
{
retval = strcasecmp(s1, s2);
}
end:
return retval;
}
gint hb_string_utf8_strstr(gchar *haystack, gchar *needle, gboolean exact)
{
gint retval = FALSE;
if( exact )
{
if( g_strstr_len(haystack, -1, needle) != NULL )
{
DB( g_print(" found case '%s'\n", needle) );
retval = 1;
}
}
else
{
gchar *nchaystack = g_utf8_casefold(haystack, -1);
gchar *ncneedle = g_utf8_casefold(needle, -1);
if( g_strrstr(nchaystack, ncneedle) != NULL )
{
DB( g_print(" found nocase '%s'\n", ncneedle) );
retval = 1;
}
g_free(nchaystack);
g_free(ncneedle);
}
return retval;
}
/*
* compare 2 utf8 string
*/
gint hb_string_utf8_compare(gchar *s1, gchar *s2)
{
gint retval = 0;
gchar *ns1, *ns2;
if (s1 == NULL || s2 == NULL)
{
if (s1 == NULL && s2 == NULL)
goto end;
retval = (s1 == NULL) ? -1 : 1;
}
else
{
//#1325969
//retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
ns1 = g_utf8_normalize(s1, -1, G_NORMALIZE_DEFAULT);
ns2 = g_utf8_normalize(s2, -1, G_NORMALIZE_DEFAULT);
retval = strcasecmp(ns1, ns2);
g_free(ns2);
g_free(ns1);
}
end:
return retval;
}
void hb_string_strip_utf8_bom(gchar *str)
{
if( g_str_has_prefix(str, "\xEF\xBB\xBF") )
{
gint len;
DB( g_print("BOM is present into '%s'\n", str) );
len = strlen(str);
if(len>3)
{
memmove(str, str+3, len-3);
str[len-3] = 0;
}
}
}
void hb_string_strip_crlf(gchar *str)
{
g_return_if_fail(str != NULL);
//str[strcspn(str, "\r\n")] = 0;
while( *str )
{
if( *str == '\n' || *str == '\r' )
{
*str = '\0';
break;
}
str++;
}
}
gboolean
hb_string_has_leading_trailing(gchar *str)
{
gsize str_len;
g_return_val_if_fail (str != NULL, FALSE);
str_len = strlen (str);
if(*str == ' ' || str[str_len-1] == ' ')
return TRUE;
return FALSE;
}
void hb_string_replace_char(gchar oc, gchar nc, gchar *str)
{
gsize len;
gchar *s = str;
gchar *d = str;
if(str)
{
len = strlen (str);
while( *s && len > 0 )
{
if( *s != oc )
{
*d++ = *s++;
}
else
{
*d++ = nc;
s++;
}
len--;
}
*d = 0;
}
}
void hb_string_remove_char(gchar c, gchar *str)
{
gsize len;
gchar *s = str;
gchar *d = str;
if(str)
{
len = strlen (str);
while( *s && len > 0 )
{
if( *s != c )
{
*d++ = *s;
}
s++;
}
*d = 0;
}
}
#if( (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 68) )
guint g_string_replace (GString *string,
const gchar *find,
const gchar *replace,
guint limit)
{
gsize f_len, r_len, pos;
gchar *cur, *next;
guint n = 0;
g_return_val_if_fail (string != NULL, 0);
g_return_val_if_fail (find != NULL, 0);
g_return_val_if_fail (replace != NULL, 0);
f_len = strlen (find);
r_len = strlen (replace);
cur = string->str;
while ((next = strstr (cur, find)) != NULL)
{
pos = next - string->str;
g_string_erase (string, pos, f_len);
g_string_insert (string, pos, replace);
cur = string->str + pos + r_len;
n++;
/* Only match the empty string once at any given position, to
* avoid infinite loops */
if (f_len == 0)
{
if (cur[0] == '\0')
break;
else
cur++;
}
if (n == limit)
break;
}
return n;
}
#endif
gchar *hb_string_copy_jsonpair(gchar *dst, gchar *str)
{
while( *str!='\0' )
{
if( *str=='}' )
break;
if( *str==',' )
{
*dst = '\0';
return str + 1;
}
if( *str!='{' && *str!='\"' )
{
*dst++ = *str;
}
str++;
}
*dst = '\0';
return NULL;
}
void hb_string_inline(gchar *str)
{
gchar *s = str;
gchar *d = str;
if(str)
{
while( *s )
{
if( !(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') )
{
*d++ = *s;
}
s++;
}
*d = 0;
}
}
/*void strip_extra_spaces(char* str) {
int i,x;
for(i=x=1; str[i]; ++i)
if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
str[x++] = str[i];
str[x] = '\0';
}*/
gchar *
hb_strdup_nobrackets (const gchar *str)
{
const gchar *s;
gchar *new_str, *d;
gsize length;
if (str)
{
length = strlen (str) + 1;
new_str = g_new (char, length);
s = str;
d = new_str;
while(*s != '\0')
{
if( *s != '[' && *s != ']' )
*d++ = *s;
s++;
}
*d = '\0';
}
else
new_str = NULL;
return new_str;
}
/* if we found a . or , within last x digits it might be a dchar */
static gchar hb_string_raw_amount_guess_dchar(const gchar *s, gint len, gshort digit)
{
gint nbc, nbd, i;
gchar gdc='.';
DB( g_print(" digit=%d maxidx=%d\n", digit, len-digit-1) );
nbc = nbd = 0;
for(i=len-1;i>=0;i--)
{
DB( g_print(" [%2d] '%c' %d %d '%c'\n", i, s[i], nbc, nbd, gdc) );
//store rightmost ,. within digit-1
if( i>=(len-digit-1) )
{
if(s[i]=='.' || s[i]==',')
gdc=s[i];
}
if(s[i]=='.') nbd++;
else if(s[i]==',') nbc++;
}
if( gdc=='.' && nbd > 1) gdc='?';
else if( gdc==',' && nbc > 1) gdc='?';
return gdc;
}
//#1876134 windows: pasted numbers from calculator loose dchar
// E2 80 AD 31 C2 A0 31 31 31 2C 31 E2 80 AC : â€1 111,1‬
//https://github.com/microsoft/calculator/issues/504
gchar *hb_string_dup_raw_amount_clean(const gchar *string, gint digits)
{
gint l;
gchar *san_str, *new_str, *d;
gchar gdc;
const gchar *p;
//sanitize the string first: keep -,.0-9
san_str = d = g_strdup(string);
p = string;
while(*p)
{
//if( g_ascii_isdigit(*p) || *p=='-' || *p=='.' || *p==',' )
if( g_ascii_isdigit(*p) || *p=='-' || *p=='+' || *p=='.' || *p==',' )
*d++ = *p;
p++;
}
*d++ = '\0';
l = strlen(san_str);
gdc = hb_string_raw_amount_guess_dchar(san_str, l, digits);
new_str = d = g_malloc (l+1);
p = san_str;
while(*p)
{
//if(*p=='-' || g_ascii_isdigit(*p) )
if( *p=='-' || *p=='+' || g_ascii_isdigit(*p) )
*d++ = *p;
else
if( *p==gdc )
{
*d++ = '.';
}
p++;
}
*d++ = '\0';
g_free(san_str);
return new_str;
}
static gchar *
hb_date_add_separator(gchar *txt, gint dateorder)
{
gchar *newtxt, *d;
gint len;
len = strlen(txt);
newtxt = g_new0(char, len+3);
d = newtxt;
if( (dateorder == PRF_DATEFMT_MDY) || (dateorder == PRF_DATEFMT_DMY) )
{
*d++ = *txt++;
*d++ = *txt++;
*d++ = '/';
*d++ = *txt++;
*d++ = *txt++;
*d++ = '/';
*d++ = *txt++;
*d++ = *txt++;
if( len == 8 )
{
*d++ = *txt++;
*d++ = *txt++;
}
}
else
if( dateorder == PRF_DATEFMT_YMD )
{
*d++ = *txt++;
*d++ = *txt++;
if( len == 8 )
{
*d++ = *txt++;
*d++ = *txt++;
}
*d++ = '/';
*d++ = *txt++;
*d++ = *txt++;
*d++ = '/';
*d++ = *txt++;
*d++ = *txt++;
}
*d++ = '\0';
return newtxt;
}
//https://en.wikipedia.org/wiki/Date_format_by_country
static gboolean
hb_date_parser_get_nums(gchar *string, gint *n1, gint *n2, gint *n3)
{
gboolean retval;
gchar **str_array;
//DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
retval = FALSE;
if( string )
{
str_array = g_strsplit (string, "/", 3);
if( g_strv_length( str_array ) != 3 )
{
g_strfreev (str_array);
str_array = g_strsplit (string, ".", 3);
//#371381 add '-'
if( g_strv_length( str_array ) != 3 )
{
g_strfreev (str_array);
str_array = g_strsplit (string, "-", 3);
}
}
if( g_strv_length( str_array ) == 3 )
{
*n1 = atoi(str_array[0]);
*n2 = atoi(str_array[1]);
*n3 = atoi(str_array[2]);
retval = TRUE;
}
g_strfreev (str_array);
}
return retval;
}
guint32 hb_date_get_julian(gchar *string, gint dateorder)
{
GDate *date;
gint n1, n2, n3, d, m, y;
guint32 julian = 0;
gboolean parsed;
gchar *datewithsep;
DB( g_print("\n[utils] hb_date_get_julian\n") );
//1st try with separator
DB( g_print(" 1st pass str='%s'\n", string) );
parsed = hb_date_parser_get_nums(string, &n1, &n2, &n3);
if( parsed == FALSE )
{
//#1904569 give a try with no separator
datewithsep = hb_date_add_separator(string, dateorder);
DB( g_print(" 2nd pass str='%s'\n", datewithsep) );
parsed = hb_date_parser_get_nums(datewithsep, &n1, &n2, &n3);
g_free(datewithsep);
}
if( parsed == TRUE )
{
DB( g_print(" num= '%d' '%d' '%d'\n", n1, n2, n3) );
switch(dateorder)
{
case PRF_DATEFMT_MDY:
d = n2;
m = n1;
y = n3;
break;
case PRF_DATEFMT_DMY:
d = n1;
m = n2;
y = n3;
break;
default:
case PRF_DATEFMT_YMD:
d = n3;
m = n2;
y = n1;
break;
}
//adjust for 2 digits year
if(y < 1970)
{
if(y < 60)
y += 2000;
else
y += 1900;
}
if(d <= 31 && m <= 12)
{
if( g_date_valid_dmy(d, m, y) )
{
DB( g_print(" ddmmyyyy = '%d' '%d' '%d'\n", d, m, y) );
date = g_date_new_dmy(d, m, y);
julian = g_date_get_julian (date);
g_date_free(date);
}
}
}
DB( g_print(" >%s :: julian=%d\n", parsed ? "OK":"--", julian) );
return julian;
}
guint32 hb_date_get_jbound(guint32 jdate, HbDateBound bound)
{
GDate date;
GDateDay d = 1;
g_date_set_julian(&date, jdate);
if(bound == HB_DATE_BOUND_LAST)
{
GDateMonth m = g_date_get_month(&date);
GDateYear y = g_date_get_day_of_year(&date);
d = g_date_get_days_in_month(m, y);
}
g_date_set_day(&date, d);
return g_date_get_julian(&date);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gint hb_filename_type_get_by_extension(gchar *filepath)
{
gint retval = FILETYPE_UNKNOWN;
gint str_len;
g_return_val_if_fail(filepath != NULL, FILETYPE_UNKNOWN);
str_len = strlen(filepath);
if( str_len >= 4 )
{
if( strcasecmp(filepath + str_len - 4, ".csv") == 0)
retval = FILETYPE_CSV_HB;
else
if( strcasecmp(filepath + str_len - 4, ".tsv") == 0)
retval = FILETYPE_CSV_HB;
else
if( strcasecmp(filepath + str_len - 4, ".qif") == 0)
retval = FILETYPE_QIF;
else
if( strcasecmp(filepath + str_len - 4, ".ofx") == 0)
retval = FILETYPE_OFX;
else
if( strcasecmp(filepath + str_len - 4, ".qfx") == 0)
retval = FILETYPE_OFX;
else
if( strcasecmp(filepath + str_len - 4, ".xhb") == 0)
retval = FILETYPE_HOMEBANK;
}
return retval;
}
static gchar *hb_filename_new_without_extension(gchar *filename)
{
gchar *lastdot;
lastdot = g_strrstr (filename, ".");
if(lastdot != NULL)
{
return g_strndup(filename, strlen(filename) - strlen(lastdot));
}
return g_strdup(filename);
}
static gint hb_filename_backup_list_sort_func(gchar **a, gchar **b)
{
gint da = atoi( *a + strlen(*a) - 12);
gint db = atoi( *b + strlen(*b) - 12);
return db - da;
}
GPtrArray *hb_filename_backup_list(gchar *filename)
{
gchar *dirname, *basename;
gchar *rawfilename, *pattern;
GDir *dir;
const gchar *tmpname;
GPatternSpec *pspec;
GPtrArray *array;
DB( g_print("\n[util] filename backup list\n") );
dirname = g_path_get_dirname(filename);
basename = g_path_get_basename(filename);
DB( g_print(" dir='%s' base='%s'\n", dirname, basename) );
rawfilename = hb_filename_new_without_extension(basename);
pattern = g_strdup_printf("%s-????????.bak", rawfilename);
pspec = g_pattern_spec_new(pattern);
DB( g_print(" pattern='%s'\n", pattern) );
array = g_ptr_array_new_with_free_func(g_free);
//dir = g_dir_open (PREFS->path_hbfile, 0, NULL);
dir = g_dir_open (PREFS->path_hbbak, 0, NULL);
if (dir)
{
while ((tmpname = g_dir_read_name (dir)) != NULL)
{
gboolean match = g_pattern_match_string(pspec, tmpname);
if( match )
{
DB( g_print(" %d => '%s'\n", match, tmpname) );
g_ptr_array_add(array, g_strdup(tmpname));
}
}
//#2121204 move here to avoid console msg when null
g_dir_close (dir);
}
g_free(pattern);
g_pattern_spec_free(pspec);
g_free(rawfilename);
g_free(basename);
g_free(dirname);
g_ptr_array_sort(array, (GCompareFunc)hb_filename_backup_list_sort_func);
return array;
}
gchar *
hb_filename_backup_get_filtername(gchar *filename)
{
gchar *basename;
gchar *rawfilename, *pattern;
DB( g_print("\n[util] filename backup get filtername\n") );
basename = g_path_get_basename(filename);
rawfilename = hb_filename_new_without_extension(basename);
pattern = g_strdup_printf("%s*.[Bb][Aa][Kk]", rawfilename);
g_free(rawfilename);
g_free(basename);
return pattern;
}
gchar *
hb_filename_new_for_backup(gchar *filename)
{
gchar *basename, *rawfilename, *newfilename, *newfilepath;
GDate date;
basename = g_path_get_basename(filename);
rawfilename = hb_filename_new_without_extension(basename);
g_date_clear(&date, 1);
g_date_set_julian (&date, GLOBALS->today);
newfilename = g_strdup_printf("%s-%04d%02d%02d.bak",
rawfilename,
g_date_get_year(&date),
g_date_get_month(&date),
g_date_get_day(&date)
);
newfilepath = g_build_filename(PREFS->path_hbbak, newfilename, NULL);
g_free(newfilename);
g_free(rawfilename);
g_free(basename);
return newfilepath;
}
gchar *hb_filename_new_with_extension(gchar *filename, const gchar *extension)
{
gchar *rawfilename, *newfilename;
DB( g_print("\n[util] filename new with extension\n") );
rawfilename = hb_filename_new_without_extension(filename);
newfilename = g_strdup_printf("%s.%s", rawfilename, extension);
g_free(rawfilename);
DB( g_print(" - '%s' => '%s'\n", filename, newfilename) );
return newfilename;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gboolean hb_string_isdate(gchar *str)
{
gint d, m, y;
return(hb_date_parser_get_nums(str, &d, &m, &y));
}
gboolean hb_string_isdigit(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isdigit(*str++);
return valid;
}
/*
gboolean hb_string_isprint(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isprint(*str++);
return valid;
}
*/
gboolean hb_string_isprint(gchar *str)
{
gboolean valid = TRUE;
gchar *p;
gunichar c;
if(g_utf8_validate(str, -1, NULL))
{
p = str;
while(*p && valid)
{
c = g_utf8_get_char(p);
valid = g_unichar_isprint(c);
p = g_utf8_next_char(p);
}
}
return valid;
}
gchar *hb_sprint_date(gchar *outstr, guint32 julian)
{
GDate date;
g_date_clear(&date, 1);
g_date_set_julian (&date, julian);
switch(PREFS->dtex_datefmt)
{
//#2040010 change / to -
case PRF_DATEFMT_MDY:
{
g_sprintf(outstr, "%02d-%02d-%04d",
g_date_get_month(&date),
g_date_get_day(&date),
g_date_get_year(&date)
);
}
break;
case PRF_DATEFMT_DMY:
{
g_sprintf(outstr, "%02d-%02d-%04d",
g_date_get_day(&date),
g_date_get_month(&date),
g_date_get_year(&date)
);
}
break;
default:
g_sprintf(outstr, "%04d-%02d-%02d",
g_date_get_year(&date),
g_date_get_month(&date),
g_date_get_day(&date)
);
break;
}
return outstr;
}
//used only in DB() macro !!
void hb_print_date(guint32 jdate, gchar *label)
{
gchar buffer1[128];
GDate *date;
date = g_date_new_julian(jdate);
g_date_strftime (buffer1, 128-1, "%a %x", date);
g_date_free(date);
g_print("%s %d - '%s'\n", label != NULL ? label:"date is", jdate, buffer1);
}
/*
** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
**
*/
/* obsolete 4.5
guint32 hb_date_get_julian_parse(gchar *str)
{
gchar **str_array = NULL;
GDate *date;
guint d, m, y;
guint32 julian = GLOBALS->today;
// try with - separator
if( g_strrstr(str, "-") != NULL )
{
str_array = g_strsplit (str, "-", 3);
}
else
{
if( g_strrstr(str, "/") != NULL )
{
str_array = g_strsplit (str, "/", 3);
}
}
if( g_strv_length( str_array ) == 3 )
{
d = atoi(str_array[0]);
m = atoi(str_array[1]);
y = atoi(str_array[2]);
//correct for 2 digits year
if(y < 1970)
{
if(y < 60)
y += 2000;
else
y += 1900;
}
//todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
//or maybe check with g_date_valid_julian(julian)
date = g_date_new();
g_date_set_dmy(date, d, m, y);
julian = g_date_get_julian (date);
g_date_free(date);
DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
}
g_strfreev (str_array);
return julian;
}
*/
/* -------------------- */
#if MYDEBUG == 1
/*
** hex memory dump
*/
#define MAX_DUMP 16
void hex_dump(gchar *ptr, guint length)
{
gchar ascii[MAX_DUMP+4];
guint i,j;
g_print("**hex_dump - %d bytes\n", length);
for(i=0;i= length) break;
//store ascii value
if(ptr[i] >= 32 && ptr[i] <= 126)
ascii[j] = ptr[i];
else
ascii[j] = '.';
g_print("%02x ", ptr[i]);
i++;
}
//newline
ascii[j] = 0;
g_print(" '%s'\n", ascii);
}
}
#endif
homebank-5.9.7/src/list-report.c 0000664 0001750 0001750 00000066756 15120467541 016064 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "list-report.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
// our global datas
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static void lst_report_to_string_row(GString *node, ToStringMode mode, gint src, GtkTreeModel *model, GtkTreeIter *iter)
{
guint32 key;
gchar sep, *name;
gdouble values[4];
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
gtk_tree_model_get (model, iter,
LST_REPORT_KEY , &key,
LST_REPORT_LABEL , &name,
LST_REPORT_EXPENSE, &values[0],
LST_REPORT_INCOME , &values[1],
LST_REPORT_TOTAL , &values[2],
-1);
//#2033298 we get fullname for export
if( src == REPORT_GRPBY_CATEGORY )
{
Category *catitem = da_cat_get(key);
if( catitem != NULL )
{
g_free(name);
name = g_strdup( (catitem->key == 0) ? _("(no category)") : catitem->fullname );
}
}
//format = (mode == HB_STRING_EXPORT) ? "%s;%.2f;%.2f;%.2f\n" : "%s\t%.2f\t%.2f\t%.2f\n";
//g_string_append_printf(node, format, name, exp, inc, bal);
g_string_append(node, name);
for(guint i=0;i<3;i++)
{
g_string_append_c(node, sep);
_format_decimal(node, mode, values[i]);
}
g_string_append_c(node, '\n');
//leak
g_free(name);
}
GString *lst_report_to_string(ToStringMode mode, GtkTreeView *treeview, gint src, gchar *title)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
const gchar *format;
DB( g_print("\n[list-report] to string\n") );
node = g_string_new(NULL);
// header
format = (mode == HB_STRING_EXPORT) ? "%s;%s;%s;%s\n" : "%s\t%s\t%s\t%s\n";
g_string_append_printf(node, format, (title == NULL) ? _("Result") : title, _("Expense"), _("Income"), _("Total"));
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
lst_report_to_string_row(node, mode, src, model, &iter);
if( gtk_tree_model_iter_has_child(model, &iter) )
{
valid = gtk_tree_model_iter_children(model, &child, &iter);
while (valid)
{
lst_report_to_string_row(node, mode, src, model, &child);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
static void
lst_report_cell_data_func_label (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gchar *label, *overlabel;
gint pos;
gtk_tree_model_get(model, iter,
LST_REPORT_POS, &pos,
LST_REPORT_LABEL, &label,
LST_REPORT_OVERLABEL, &overlabel,
-1);
if( overlabel != NULL )
{
g_object_set(renderer,
"weight", PANGO_WEIGHT_NORMAL,
"markup", overlabel,
NULL);
}
else
{
g_object_set(renderer,
"weight", (pos == LST_REPORT_POS_TOTAL) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
"text" , label,
NULL);
}
g_free(label);
g_free(overlabel);
}
static void lst_report_cell_data_func_rate (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
struct lst_report_data *lst_data = NULL;
GtkWidget *widget;
gint pos, colid = GPOINTER_TO_INT(user_data);
gdouble amount, rate = 0.0;
gchar buf[16], *retval = "";
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
lst_data = g_object_get_data(G_OBJECT(widget), "inst_data");
if(lst_data != NULL)
{
gtk_tree_model_get(model, iter,
LST_REPORT_POS, &pos,
colid, &amount,
-1);
//don't display total/total
if( pos != LST_REPORT_POS_TOTAL )
{
switch(colid)
{
case LST_REPORT_EXPENSE:
rate = hb_rate(amount, lst_data->tot_exp);
break;
case LST_REPORT_INCOME:
rate = hb_rate(amount, lst_data->tot_inc);
break;
case LST_REPORT_TOTAL:
rate = hb_rate(amount, -lst_data->tot_exp + lst_data->tot_inc);
break;
}
if( hb_amount_cmp(rate, 0.0) != 0 )
{
g_snprintf(buf, sizeof(buf), "%.2f %%", rate);
retval = buf;
}
}
}
g_object_set(renderer, "text", retval, NULL);
}
static void
lst_report_cell_data_func_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar *color;
gint pos, colid = GPOINTER_TO_INT(user_data);
gint weight = PANGO_WEIGHT_NORMAL;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gtk_tree_model_get(model, iter,
LST_REPORT_POS, &pos,
GPOINTER_TO_INT(user_data), &value,
-1);
//#2026184
value = hb_amount_round(value, 2);
if( (value != 0.0) || (colid == LST_REPORT_TOTAL) )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
color = get_normal_color_amount(value);
if( pos == LST_REPORT_POS_TOTAL )
{
weight = PANGO_WEIGHT_BOLD;
}
g_object_set(renderer,
"foreground", color,
"weight", weight,
"text", buf,
NULL);
}
else
{
g_object_set(renderer, "text", "", NULL);
}
}
static GtkTreeViewColumn *lst_report_amount_column(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_report_cell_data_func_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//#1933164
gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static GtkTreeViewColumn *lst_report_rate_column(gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, "%");
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, "yalign", 1.0, "scale", 0.8, "scale-set", TRUE, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
//gtk_tree_view_column_add_attribute(column, renderer, "text", id);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_report_cell_data_func_rate, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
//gtk_tree_view_column_set_visible(column, FALSE);
return column;
}
static gint lst_report_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
gint pos1, pos2;
gdouble val1, val2;
//#1933164
gint csid;
GtkSortType cso;
gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &csid, &cso);
//DB( g_print(" csid=%d cso=%s\n", csid, cso == GTK_SORT_ASCENDING ? "asc" : "desc") );
gtk_tree_model_get(model, a,
LST_REPORT_POS, &pos1,
sortcol, &val1,
-1);
gtk_tree_model_get(model, b,
LST_REPORT_POS, &pos2,
sortcol, &val2,
-1);
//#1933164 should return
// > 0 if a sorts before b
// = 0 if a sorts with b
// < 0 if a sorts after b
//total always at bottom
if( pos1 == LST_REPORT_POS_TOTAL )
{
retval = cso == GTK_SORT_ASCENDING ? 1 : -1;
//DB( g_print(" sort p1=%d ? p2=%d = %d\n", pos1, pos2, retval) );
}
else
{
if( pos2 == LST_REPORT_POS_TOTAL )
{
retval = cso == GTK_SORT_ASCENDING ? -1 : 1;
//DB( g_print(" sort p1=%d ? p2=%d = %d\n", pos1, pos2, retval) )
}
else
{
switch(sortcol)
{
case LST_REPORT_POS:
retval = pos1 - pos2;
//DB( g_print(" sort %3d = %3d :: %d\n", pos1, pos2, retval) );
break;
case LST_REPORT_EXPENSE:
case LST_REPORT_INCOME:
retval = (ABS(val1) - ABS(val2)) > 0 ? -1 : 1;
break;
default: // should be LST_REPORT_TOTAL
//#1956060 sort with sign (no abs), option is possible but complex
//retval = (ABS(val1) - ABS(val2)) > 0 ? -1 : 1;
retval = (val1 - val2) > 0 ? -1 : 1;
//DB( g_print(" sort %.2f = %.2f :: %d\n", val1, val2, retval) );
break;
}
}
}
return retval;
}
static gboolean
lst_report_selectionfunc(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
{
GtkTreeIter iter;
gint pos;
if(gtk_tree_model_get_iter(model, &iter, path))
{
gtk_tree_model_get(model, &iter,
LST_REPORT_POS, &pos,
-1);
if( pos == LST_REPORT_POS_TOTAL )
return FALSE;
}
return TRUE;
}
static void lst_report_destroy( GtkWidget *widget, gpointer user_data )
{
struct lst_report_data *lst_data;
lst_data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[list-report] destroy event occurred\n") );
DB( g_print(" - view=%p, inst_data=%p\n", widget, lst_data) );
g_free(lst_data);
}
void lst_report_add_columns(GtkTreeView *treeview, GtkTreeModel *model)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// column: Name
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Result"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_report_cell_data_func_label, NULL, NULL);
//gtk_tree_view_column_add_attribute(column, renderer, "text", LST_REPORT_LABEL);
//#1933164
gtk_tree_view_column_set_sort_column_id (column, LST_REPORT_POS);
gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column: Expense
column = lst_report_amount_column(_("Expense"), LST_REPORT_EXPENSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = lst_report_rate_column(LST_REPORT_EXPENSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column: Income
column = lst_report_amount_column(_("Income"), LST_REPORT_INCOME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = lst_report_rate_column(LST_REPORT_INCOME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column: Total
column = lst_report_amount_column(_("Total"), LST_REPORT_TOTAL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = lst_report_rate_column(LST_REPORT_TOTAL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column last: empty
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
GtkTreeStore *lst_report_new(void)
{
GtkTreeStore *store;
store = gtk_tree_store_new(
NUM_LST_REPORT,
G_TYPE_INT, //POS keep for compatibility with chart
G_TYPE_INT, //KEY
//G_TYPE_POINTER, //ROW
G_TYPE_STRING, //ROWLABEL
G_TYPE_DOUBLE, //EXP
G_TYPE_DOUBLE, //INC
G_TYPE_DOUBLE, //TOT
G_TYPE_STRING //OVERRIDELABEL
);
return store;
}
/*
** create our statistic list
*/
GtkWidget *lst_report_create(void)
{
struct lst_report_data *lst_data;
GtkTreeStore *store;
GtkWidget *treeview;
DB( g_print("\n[list-report] create\n") );
lst_data = g_malloc0(sizeof(struct lst_report_data));
if(!lst_data)
return NULL;
// create list store
store = lst_report_new();
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
// store our window private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)lst_data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, lst_data) );
// connect our dispose function
g_signal_connect (treeview, "destroy", G_CALLBACK (lst_report_destroy), NULL);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
lst_report_add_columns(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
// prevent selection of total
gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), lst_report_selectionfunc, NULL, NULL);
// sort
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPORT_POS , lst_report_compare_func, GINT_TO_POINTER(LST_REPORT_POS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPORT_EXPENSE, lst_report_compare_func, GINT_TO_POINTER(LST_REPORT_EXPENSE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPORT_INCOME , lst_report_compare_func, GINT_TO_POINTER(LST_REPORT_INCOME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REPORT_TOTAL , lst_report_compare_func, GINT_TO_POINTER(LST_REPORT_TOTAL), NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_REPORT_POS, GTK_SORT_ASCENDING);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
return(treeview);
}
/*
** ============================================================================
*/
static void lst_rep_time_to_string_row(GString *node, ToStringMode mode, gint src, GtkTreeModel *model, GtkTreeIter *iter)
{
guint32 key;
gchar sep, *name;
DataRow *dr;
gdouble value;
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
gtk_tree_model_get (model, iter,
LST_REPORT2_KEY, &key,
LST_REPORT2_LABEL, &name,
LST_REPORT2_ROW, &dr,
-1);
//#2033298 we get fullname for export
if( src == REPORT_GRPBY_CATEGORY )
{
Category *catitem = da_cat_get(key);
if( catitem != NULL )
{
g_free(name);
name = g_strdup( (catitem->key == 0) ? _("(no category)") : catitem->fullname );
}
}
g_string_append(node, name);
g_string_append_c(node, sep);
//hb_strfmongc(buf, G_ASCII_DTOSTR_BUF_SIZE-1, values[i]);
//iterate row cells
for(guint i=0 ; i < dr->nbcols ; i++ )
{
value = da_datarow_get_cell_sum(dr, i);
DB( g_print(" %2d %.2f\n", i, value) );
_format_decimal(node, mode, value);
if( i < dr->nbcols )
g_string_append_c(node, sep);
}
//average
value = (dr->rowexp + dr->rowinc) / dr->nbcols;
_format_decimal(node, mode, value);
g_string_append_c(node, sep);
//total
value = (dr->rowexp + dr->rowinc);
_format_decimal(node, mode, value);
//newline
g_string_append_c(node, '\n');
//leak
g_free(name);
}
GString *lst_rep_time_to_string(ToStringMode mode, GtkTreeView *treeview, gint src, gchar *title)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
guint32 nbcols, r, i;
gchar sep;
DB( g_print("\n[list_rep_time] to string\n") );
node = g_string_new(NULL);
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
// header (nbcols-1 for empty column)
nbcols = gtk_tree_view_get_n_columns (treeview) - 1;
for( i=0 ; i < nbcols ; i++ )
{
GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
if( GTK_IS_TREE_VIEW_COLUMN(column) )
{
g_string_append(node, gtk_tree_view_column_get_title (column));
if( i < nbcols-1 )
{
g_string_append_c(node, sep);
}
}
}
g_string_append_c(node, '\n');
// data rows
r = 0;
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
lst_rep_time_to_string_row(node, mode, src, model, &iter);
if( gtk_tree_model_iter_has_child(model, &iter) )
{
valid = gtk_tree_model_iter_children(model, &child, &iter);
while (valid)
{
lst_rep_time_to_string_row(node, mode, src, model, &child);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
r++;
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
static gboolean
lst_rep_time_cb_tooltip_query (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
gpointer data)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeModel *model;
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
gchar *label = NULL;
if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
&x, &y,
keyboard_tip,
&model, &path, &iter))
return FALSE;
gtk_tree_model_get(model, &iter,
LST_REPORT2_LABEL, &label,
-1);
gtk_tooltip_set_text (tooltip, label);
gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
gtk_tree_path_free (path);
return TRUE;
}
static void
lst_rep_time_cell_data_func_label (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gchar *label, *overlabel;
gint pos;
gtk_tree_model_get(model, iter,
LST_REPORT2_POS, &pos,
LST_REPORT2_LABEL, &label,
LST_REPORT2_OVERLABEL, &overlabel,
-1);
if( overlabel != NULL )
{
g_object_set(renderer,
"weight", PANGO_WEIGHT_NORMAL,
"markup", overlabel,
NULL);
}
else
{
g_object_set(renderer,
"weight", (pos == LST_REPORT_POS_TOTAL) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
"text" , label,
NULL);
}
g_free(label);
g_free(overlabel);
}
static void
lst_rep_time_cell_data_func_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
DataRow *dr;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint colid = GPOINTER_TO_INT(user_data);
gint pos;
gtk_tree_model_get(model, iter,
LST_REPORT2_POS, &pos,
LST_REPORT2_ROW, &dr,
-1);
if( dr != NULL )
{
gdouble exp, inc, value;
gboolean dodisplay = FALSE;
gint weight = PANGO_WEIGHT_NORMAL;
if( colid==LST_REP_COLID_AVERAGE || colid==LST_REP_COLID_TOTAL)
{
exp = dr->rowexp;
inc = dr->rowinc;
}
else
{
exp = dr->colexp[colid];
inc = dr->colinc[colid];
}
value = exp + inc;
if( hb_amount_cmp(value, 0.0) != 0 )
{
dodisplay = TRUE;
if(colid==LST_REP_COLID_AVERAGE)
value /= dr->nbcols;
}
else
{
//#2091004 we have exact 0.0, do we force display ?
if( pos == LST_REPORT_POS_TOTAL )
{
weight = PANGO_WEIGHT_BOLD;
if( hb_amount_cmp(exp, 0.0) != 0 ) // test exp is enough
dodisplay = TRUE;
}
}
if( dodisplay )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
g_object_set(renderer,
"foreground", get_normal_color_amount(value),
"weight", weight,
"text", buf,
NULL);
return;
}
}
g_object_set(renderer, "text", "", NULL);
}
static GtkTreeViewColumn *lst_rep_time_column_create_amount(gchar *name, gint id, gboolean forecast)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
if( forecast )
{
g_object_set(renderer,
"style-set", TRUE,
"style", PANGO_STYLE_OBLIQUE,
NULL);
}
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_rep_time_cell_data_func_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static gint lst_rep_time_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
//gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
DataRow *dr1, *dr2;
//#2034625
gint csid;
GtkSortType cso;
gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &csid, &cso);
//#2034625 should return
// > 0 if a sorts before b
// = 0 if a sorts with b
// < 0 if a sorts after b
gtk_tree_model_get(model, a,
LST_REPORT2_ROW, &dr1,
-1);
gtk_tree_model_get(model, b,
LST_REPORT2_ROW, &dr2,
-1);
//total always at bottom
if( dr1->pos == LST_REPORT_POS_TOTAL )
{
retval = cso == GTK_SORT_ASCENDING ? 1 : -1;
}
else
{
if( dr2->pos == LST_REPORT_POS_TOTAL )
{
retval = cso == GTK_SORT_ASCENDING ? -1 : 1;
}
else
{
gdouble val1, val2;
switch(csid)
{
case LST_REP_COLID_POS:
retval = dr1->pos - dr2->pos;
break;
case LST_REP_COLID_AVERAGE:
val1 = (dr1->rowexp + dr1->rowinc) / dr1->nbcols;
val2 = (dr2->rowexp + dr2->rowinc) / dr2->nbcols;
retval = hb_amount_cmp(val1, val2);
break;
case LST_REP_COLID_TOTAL:
val1 = (dr1->rowexp + dr1->rowinc);
val2 = (dr2->rowexp + dr2->rowinc);
retval = hb_amount_cmp(val1, val2);
break;
default:
val1 = dr1->colexp[csid] + dr1->colinc[csid];
val2 = dr2->colexp[csid] + dr2->colinc[csid];
retval = hb_amount_cmp(val1, val2);
break;
}
}
}
//DB( g_print(" sort %d=%d or %.2f=%.2f :: %d\n", pos1,pos2, val1, val2, ret) );
return retval;
}
static gboolean
lst_rep_time_selectionfunc(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
{
GtkTreeIter iter;
gint pos;
if(gtk_tree_model_get_iter(model, &iter, path))
{
gtk_tree_model_get(model, &iter,
LST_REPORT2_POS, &pos,
-1);
if( pos == LST_REPORT_POS_TOTAL )
return FALSE;
}
return TRUE;
}
// test new listview
//TODO: optimise params
void lst_rep_time_renewcol(GtkTreeView *treeview, GtkTreeModel *model, DataTable *dt, gboolean avg)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GList *columns, *list;
guint i;
DB( g_print("\n[list_rep_time] renewcol\n") );
// remove all columns
columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(treeview));
i = 0;
list = g_list_first(columns);
while(list != NULL)
{
if( GTK_IS_TREE_VIEW_COLUMN(list->data) )
{
gtk_tree_view_remove_column(treeview, GTK_TREE_VIEW_COLUMN(list->data));
}
i++;
list = g_list_next(list);
}
DB( g_print(" removed %d columns\n", i) );
g_list_free(columns);
//adding columns
// column: Name
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Acc/Cat/Pay"));
//gtk_tree_view_column_set_title(column, "ItemsType (todo)");
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_rep_time_cell_data_func_label, NULL, NULL);
//gtk_tree_view_column_add_attribute(column, renderer, "text", LST_REPORT2_LABEL);
gtk_tree_view_column_set_sort_column_id (column, LST_REP_COLID_POS);
gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
DB( g_print (" create column id:%4d ''\n", LST_REP_COLID_POS) );
// column: Amount * x
//TODO: useless with DataCol
for(i=0;inbcols;i++)
{
//gchar intvlname[64];
//guint32 jdate = report_interval_snprint_name(intvlname, sizeof(intvlname)-1, intvl, jfrom, i);
//gboolean forecast = (jdate > GLOBALS->today) ? TRUE : FALSE;
DataCol *dtcol = report_data_get_col(dt, i);
if ( dtcol )
{
column = lst_rep_time_column_create_amount(dtcol->label, i, (dtcol->flags & RF_FORECAST) );
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
DB( g_print (" create column id:%4d '%s' forecast:%d\n", i, dtcol->label, (dtcol->flags & RF_FORECAST)) );
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), i, lst_rep_time_compare_func, NULL, NULL);
}
}
column = lst_rep_time_column_create_amount(_("Average"), LST_REP_COLID_AVERAGE, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
DB( g_print (" create column id:%4d ''\n", LST_REP_COLID_AVERAGE) );
//#2012576 keep column but hide it
gtk_tree_view_column_set_visible(column, avg);
column = lst_rep_time_column_create_amount(_("Total"), LST_REP_COLID_TOTAL, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
DB( g_print (" create column id:%4d ''\n", LST_REP_COLID_TOTAL) );
// column last: empty
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
static void lst_rep_time_destroy( GtkWidget *widget, gpointer user_data )
{
struct lst_report_data *lst_data;
lst_data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[list_rep_time] destroy event occurred\n") );
DB( g_print(" - view=%p, inst_data=%p\n", widget, lst_data) );
g_free(lst_data);
}
GtkTreeStore *lst_rep_time_new(void)
{
GtkTreeStore *store;
// create list store
store = gtk_tree_store_new(
NUM_LST_REPORT2,
G_TYPE_INT, //POS
G_TYPE_INT, //KEY
G_TYPE_STRING, //ROWLABEL
G_TYPE_POINTER, //ROWDATA (pointer to DataRow)
G_TYPE_STRING //OVERRIDELABEL
);
return store;
}
GtkWidget *lst_rep_time_create(void)
{
struct lst_report_data *lst_data;
GtkTreeStore *store;
GtkWidget *treeview;
DB( g_print("\n[list_rep_time] create\n") );
lst_data = g_malloc0(sizeof(struct lst_report_data));
if(!lst_data)
return NULL;
// create list store
store = lst_rep_time_new();
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
// store our window private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)lst_data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, lst_data) );
// connect our dispose function
g_signal_connect (treeview, "destroy", G_CALLBACK (lst_rep_time_destroy), NULL);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
//prevent selection of total
gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), lst_rep_time_selectionfunc, NULL, NULL);
// sort
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REP_COLID_POS, lst_rep_time_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REP_COLID_AVERAGE, lst_rep_time_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_REP_COLID_TOTAL, lst_rep_time_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store), LST_REP_COLID_POS, GTK_SORT_ASCENDING);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
gtk_widget_set_has_tooltip (GTK_WIDGET (treeview), TRUE);
g_signal_connect (treeview, "query-tooltip",
G_CALLBACK (lst_rep_time_cb_tooltip_query), NULL);
return(treeview);
}
homebank-5.9.7/src/ui-currency.h 0000644 0001750 0001750 00000006264 14736461415 016042 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_CURRENCY_GTK_H__
#define __HB_CURRENCY_GTK_H__
enum
{
LST_DEFCUR_TOGGLE,
LST_DEFCUR_DATAS,
NUM_LST_DEFCUR
};
enum
{
LST_SELCUR_NAME,
LST_SELCUR_DATA,
NUM_LST_SELCUR
};
struct ui_cur_manage_dialog_data
{
GtkWidget *dialog;
gboolean mapped_done;
GtkWidget *LV_cur;
// GtkWidget *CY_curr;
// GtkWidget *BT_curr;
GtkWidget *TB_log;
GtkWidget *BB_update;
GtkWidget *BT_add;
GtkWidget *BT_del;
GtkWidget *BT_edit;
GtkWidget *BT_base;
gint change;
};
struct ui_cur_edit_dialog_data
{
GtkWidget *dialog;
GtkWidget *LB_name;
GtkWidget *LB_rate;
GtkWidget *NB_rate;
GtkWidget *LB_sample;
GtkWidget *ST_symbol;
GtkWidget *CM_symisprefix;
GtkWidget *ST_decimalchar;
GtkWidget *ST_groupingchar;
GtkWidget *NB_fracdigits;
};
enum {
CUR_SELECT_MODE_BASE,
CUR_SELECT_MODE_NOCUSTOM,
CUR_SELECT_MODE_CUSTOM,
};
struct ui_cur_select_dialog_data
{
GtkWidget *dialog;
GtkTreeModel *modelfilter;
GtkTreeModel *sortmodel;
GtkTreeModel *model;
GtkWidget *ST_search;
GtkWidget *LV_cur;
GtkWidget *CM_custom;
GtkWidget *LB_custiso, *ST_custiso;
GtkWidget *LB_custname, *ST_custname;
};
struct curPopContext
{
GtkTreeModel *model;
guint except_key;
};
struct curSelectContext
{
Currency4217 *cur_4217;
gchar *cur_name;
gchar *cur_iso;
};
gchar *ui_cur_combobox_get_name(GtkComboBox *entry_box);
guint32 ui_cur_combobox_get_key(GtkComboBox *entry_box);
guint32 ui_cur_combobox_get_key_add_new(GtkComboBox *entry_box);
gboolean ui_cur_combobox_set_active(GtkComboBox *entry_box, guint32 key);
void ui_cur_combobox_add(GtkComboBox *entry_box, Currency *cur);
void ui_cur_combobox_populate(GtkComboBox *entry_box, GHashTable *hash);
void ui_cur_combobox_populate_except(GtkComboBox *entry_box, GHashTable *hash, guint except_key);
GtkWidget *ui_cur_combobox_new(GtkWidget *label);
/* = = = = = = = = = = */
void ui_cur_listview_add(GtkTreeView *treeview, Currency *item);
guint32 ui_cur_listview_get_selected_key(GtkTreeView *treeview);
void ui_cur_listview_remove_selected(GtkTreeView *treeview);
void ui_cur_listview_populate(GtkWidget *view);
GtkWidget *ui_cur_listview_new(gboolean withtoggle);
gint ui_cur_manage_dialog_update_currencies(GtkWindow *parent, GString *node);
GtkWidget *ui_cur_manage_dialog (void);
gint ui_cur_select_dialog_new(GtkWindow *parent, gint select_mode, struct curSelectContext *ctx);
void ui_cur_edit_dialog_new(GtkWindow *parent, Currency *cur);
#endif
homebank-5.9.7/src/ui-group.c 0000664 0001750 0001750 00000020200 14736461407 015324 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-group.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* ui_grp_comboboxentry_get_key_add_new:
*
* get the key of the active group
* and create the group if it do not exists
*
* Return value: the key or 0
*
*/
guint32
ui_grp_comboboxentry_get_key_add_new(GtkComboBox *entry_box)
{
guint32 retval;
gchar *name;
Group *item;
retval = 0;
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
item = da_grp_get_by_name(name);
if( item == NULL )
{
gchar *stripname;
stripname = g_strdup(name);
g_strstrip(stripname);
if( strlen(stripname) > 0 )
{
/* automatic add */
//todo: check prefs + ask the user here 1st time
item = da_grp_malloc();
item->name = g_strdup(name);
da_grp_append(item);
ui_grp_comboboxentry_add(entry_box, item);
retval = item->key;
}
g_free(stripname);
}
else
retval = item->key;
return retval;
}
/**
* ui_grp_comboboxentry_get_key:
*
* get the key of the active group
*
* Return value: the key or 0
*
*/
guint32
ui_grp_comboboxentry_get_key(GtkComboBox *entry_box)
{
gchar *name;
Group *item;
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
item = da_grp_get_by_name(name);
if( item != NULL )
return item->key;
return 0;
}
Group
*ui_grp_comboboxentry_get(GtkComboBox *entry_box)
{
gchar *name;
Group *item = NULL;
DB( g_print ("ui_grp_comboboxentry_get()\n") );
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
item = da_grp_get_by_name(name);
return item;
}
gboolean
ui_grp_comboboxentry_set_active(GtkComboBox *entry_box, guint32 key)
{
Group *item;
DB( g_print ("ui_grp_comboboxentry_set_active()\n") );
DB( g_print("- key:%d\n", key) );
if( key > 0 )
{
item = da_grp_get(key);
if( item != NULL )
{
DB( g_print("- set combo to '%s'\n", item->name) );
gtk_entry_set_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))), item->name);
return TRUE;
}
}
DB( g_print("- set combo to ''\n") );
gtk_entry_set_text(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))), "");
return FALSE;
}
/**
* ui_grp_comboboxentry_add:
*
* Add a single element (useful for dynamics add)
*
* Return value: --
*
*/
void
ui_grp_comboboxentry_add(GtkComboBox *entry_box, Group *grp)
{
if( grp->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter, 0, grp->name, -1);
}
}
static void
ui_grp_comboboxentry_populate_ghfunc(gpointer key, gpointer value, struct grpPopContext *ctx)
{
GtkTreeIter iter;
Group *grp = value;
if( ( grp->key != ctx->except_key ) )
{
//gtk_list_store_append (GTK_LIST_STORE(ctx->model), &iter);
//gtk_list_store_set (GTK_LIST_STORE(ctx->model), &iter, 0, grp->name, -1);
gtk_list_store_insert_with_values(GTK_LIST_STORE(ctx->model), &iter, -1,
0, grp->name, -1);
}
}
/**
* ui_grp_comboboxentry_populate:
*
* Populate the list and completion
*
* Return value: --
*
*/
void
ui_grp_comboboxentry_populate(GtkComboBox *entry_box, GHashTable *hash)
{
ui_grp_comboboxentry_populate_except(entry_box, hash, -1);
}
void
ui_grp_comboboxentry_populate_except(GtkComboBox *entry_box, GHashTable *hash, guint except_key)
{
GtkTreeModel *model;
//GtkEntryCompletion *completion;
struct grpPopContext ctx;
DB( g_print ("ui_grp_comboboxentry_populate\n") );
model = gtk_combo_box_get_model(GTK_COMBO_BOX(entry_box));
//completion = gtk_entry_get_completion(GTK_ENTRY (gtk_bin_get_child(GTK_BIN (entry_box))));
/* keep our model alive and detach from comboboxentry and completion */
//g_object_ref(model);
//gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), NULL);
//gtk_entry_completion_set_model (completion, NULL);
/* clear and populate */
ctx.model = model;
ctx.except_key = except_key;
gtk_list_store_clear (GTK_LIST_STORE(model));
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_LIST_STORE(model)), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
g_hash_table_foreach(hash, (GHFunc)ui_grp_comboboxentry_populate_ghfunc, &ctx);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
/* reatach our model */
//g_print("reattach\n");
//gtk_combo_box_set_model(GTK_COMBO_BOX(entry_box), model);
//gtk_entry_completion_set_model (completion, model);
//g_object_unref(model);
}
static gint
ui_grp_comboboxentry_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
gchar *name1, *name2;
gtk_tree_model_get(model, a, 0, &name1, -1);
gtk_tree_model_get(model, b, 0, &name2, -1);
retval = hb_string_utf8_compare(name1, name2);
g_free(name2);
g_free(name1);
return retval;
}
static void
ui_grp_comboboxentry_test (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
gpointer data)
{
gchar *name;
gtk_tree_model_get(tree_model, iter,
0, &name,
-1);
if( !name )
g_object_set(cell, "text", _("(no group)"), NULL);
else
g_object_set(cell, "text", name, NULL);
//leak
g_free(name);
}
/**
* ui_grp_comboboxentry_new:
*
* Create a new group comboboxentry
*
* Return value: the new widget
*
*/
GtkWidget *
ui_grp_comboboxentry_new(GtkWidget *label)
{
GtkListStore *store;
GtkWidget *comboboxentry;
GtkEntryCompletion *completion;
GtkCellRenderer *renderer;
DB( g_print ("ui_grp_comboboxentry_new()\n") );
store = gtk_list_store_new (1,
G_TYPE_STRING
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_grp_comboboxentry_compare_func, NULL, NULL);
completion = gtk_entry_completion_new ();
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
g_object_set(completion, "text-column", 0, NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), renderer, "text", 0, NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
renderer,
ui_grp_comboboxentry_test,
NULL, NULL);
// dothe same for combobox
comboboxentry = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(store));
gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(comboboxentry), 0);
gtk_cell_layout_clear(GTK_CELL_LAYOUT (comboboxentry));
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (comboboxentry), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (comboboxentry), renderer, "text", 0, NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (comboboxentry),
renderer,
ui_grp_comboboxentry_test,
NULL, NULL);
gtk_entry_set_completion (GTK_ENTRY (gtk_bin_get_child(GTK_BIN (comboboxentry))), completion);
g_object_unref(store);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), comboboxentry);
gtk_widget_set_size_request(comboboxentry, HB_MINWIDTH_LIST, -1);
return comboboxentry;
}
homebank-5.9.7/src/hb-tag.c 0000644 0001750 0001750 00000034763 14773265341 014740 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-tag.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
#if MYDEBUG
static void da_tag_debug_array(guint32 *tags);
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void da_tag_free(Tag *item)
{
DB( g_print("da_tag_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->name) );
g_free(item->name);
g_free(item);
}
}
Tag *da_tag_malloc(void)
{
DB( g_print("da_tag_malloc\n") );
return g_malloc0(sizeof(Tag));
}
void da_tag_destroy(void)
{
DB( g_print("da_tag_destroy\n") );
g_hash_table_destroy(GLOBALS->h_tag);
}
void da_tag_new(void)
{
Tag *item;
DB( g_print("da_tag_new\n") );
GLOBALS->h_tag = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_tag_free);
// insert our 'no tag'
item = da_tag_malloc();
item->name = g_strdup("");
da_tag_insert(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void da_tag_max_key_ghfunc(gpointer key, Tag *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
static gboolean da_tag_name_grfunc(gpointer key, Tag *item, gchar *name)
{
if( name && item->name )
{
if(!strcasecmp(name, item->name))
return TRUE;
}
return FALSE;
}
/**
* da_tag_length:
*
* Return value: the number of elements
*/
guint da_tag_length(void)
{
return g_hash_table_size(GLOBALS->h_tag);
}
/**
* da_tag_delete:
*
* delete an tag from the GHashTable
*
* Return value: TRUE if the key was found and deleted
*
*/
gboolean da_tag_delete(guint32 key)
{
DB( g_print("da_tag_delete %d\n", key) );
return g_hash_table_remove(GLOBALS->h_tag, &key);
}
/**
* da_tag_insert:
*
* insert an tag into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean da_tag_insert(Tag *item)
{
guint32 *new_key;
DB( g_print("da_tag_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
g_hash_table_insert(GLOBALS->h_tag, new_key, item);
return TRUE;
}
/**
* da_tag_append:
*
* append a new tag into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean da_tag_append(Tag *item)
{
Tag *existitem;
guint32 *new_key;
DB( g_print("da_tag_append\n") );
if( item->name != NULL )
{
/* ensure no duplicate */
//g_strstrip(item->name);
existitem = da_tag_get_by_name( item->name );
if( existitem == NULL )
{
new_key = g_new0(guint32, 1);
*new_key = da_tag_get_max_key() + 1;
item->key = *new_key;
DB( g_print(" -> append id: %d\n", *new_key) );
g_hash_table_insert(GLOBALS->h_tag, new_key, item);
return TRUE;
}
}
DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
return FALSE;
}
Tag *
da_tag_append_if_new(gchar *rawname)
{
Tag *retval = NULL;
retval = da_tag_get_by_name(rawname);
if(retval == NULL)
{
retval = da_tag_malloc();
retval->key = da_tag_get_max_key() + 1;
retval->name = g_strdup(rawname);
g_strstrip(retval->name);
da_tag_insert(retval);
}
return retval;
}
/**
* da_tag_get_max_key:
*
* Get the biggest key from the GHashTable
*
* Return value: the biggest key value
*
*/
guint32 da_tag_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_tag, (GHFunc)da_tag_max_key_ghfunc, &max_key);
return max_key;
}
/**
* da_tag_get_by_name:
*
* Get an tag structure by its name
*
* Return value: Tag * or NULL if not found
*
*/
Tag *da_tag_get_by_name(gchar *name)
{
DB( g_print("da_tag_get_by_name\n") );
return g_hash_table_find(GLOBALS->h_tag, (GHRFunc)da_tag_name_grfunc, name);
}
/**
* da_tag_get:
*
* Get an tag structure by key
*
* Return value: Tag * or NULL if not found
*
*/
Tag *da_tag_get(guint32 key)
{
DB( g_print("da_tag_get_tag\n") );
return g_hash_table_lookup(GLOBALS->h_tag, &key);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gboolean
tags_equal(guint32 *stags, guint32 *dtags)
{
guint count = 0;
DB( g_print("\n[tags] compare\n") );
if( stags == NULL && dtags == NULL )
return TRUE;
if( stags == NULL || dtags == NULL )
return FALSE;
while(*stags != 0 && *dtags != 0 && count < 32)
{
if(*stags++ != *dtags++)
return FALSE;
}
// both should be 0
if(*stags != *dtags)
return FALSE;
return TRUE;
}
guint
tags_count(guint32 *tags)
{
guint count = 0;
DB( g_print("\n[tags] count\n") );
if( tags == NULL )
return 0;
while(*tags++ != 0 && count < 32)
count++;
return count;
}
guint32 *tags_clone(guint32 *tags)
{
guint32 *newtags = NULL;
guint count;
DB( g_print("\n[tags] clone %p\n", tags) );
if( tags == NULL )
return NULL;
count = tags_count(tags);
if(count > 0)
{
//1501962: we must also copy the final 0
newtags = g_memdup(tags, (count+1)*sizeof(guint32));
}
return newtags;
}
static gboolean
tags_key_exists(guint32 *tags, guint32 key)
{
guint count = 0;
if( tags == NULL )
return FALSE;
while(*tags != 0 && count < 32)
{
if( *tags == key )
return TRUE;
tags++;
count++;
}
return FALSE;
}
static void
tags_deduplicate(guint32 *tags)
{
guint32 *tmp, *s, *d;
guint count = 0;
DB( g_print("\n[tags] deduplicate\n") );
if( tags == NULL )
return;
DB( g_print(" tags %p\n", tags) );
tmp = s = tags_clone(tags);
d = tags;
DB( g_print(" tmp %p\n", tmp) );
DB( g_print(" s %p\n", s) );
*d = 0;
while(*s != 0 && count < 32)
{
DB( g_print(" - tst %d\n", *s) );
if( tags_key_exists(tags, *s) == FALSE )
{
*d = *s;
d++;
*d = 0;
DB( g_print(" - add %d\n", *s) );
}
else
{
DB( g_print(" - skip %d\n", *s) );
}
s++;
count++;
}
g_free(tmp);
}
gint
tags_delete_unused(void)
{
GList *ltag, *list;
gint count = 0;
ltag = list = g_hash_table_get_values(GLOBALS->h_tag);
while (list != NULL)
{
Tag *entry = list->data;
if(entry->nb_use_all <= 0 && entry->key > 0)
{
da_tag_delete (entry->key);
count++;
}
list = g_list_next(list);
}
g_list_free(ltag);
return count;
}
static void
_tags_fill_usage(guint32 *tags, gboolean txn)
{
guint count = 0;
if(tags != NULL)
{
while(*tags != 0 && count < 32)
{
Tag *tag = da_tag_get(*tags);
//#2106027 crash
if( tag != NULL )
{
tag->nb_use_all++;
if( txn == TRUE )
tag->nb_use_txn++;
}
tags++;
count++;
}
}
}
void
tags_fill_usage(void)
{
GList *ltag;
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *list;
DB( g_print("[tags] fill usage\n") );
ltag = list = g_hash_table_get_values(GLOBALS->h_tag);
while (list != NULL)
{
Tag *entry = list->data;
entry->nb_use_all = 0;
entry->nb_use_txn = 0;
list = g_list_next(list);
}
g_list_free(ltag);
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
_tags_fill_usage(txn->tags, TRUE);
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
_tags_fill_usage(entry->tags, FALSE);
list = g_list_next(list);
}
//future assign
}
static void
tags_move(guint32 *tags, guint32 key1, guint32 key2)
{
guint count = 0;
guint countdup = 0;
guint32 *p;
if( tags == NULL )
return;
DB( g_print("\n[tags] move\n") );
p = tags;
while(*p != 0 && count < 32)
{
if( *p == key1 )
{
DB( g_print(" change %d to %d\n", key1, key2) );
*p = key2;
}
//count potential duplicate
if( *p == key2 )
countdup++;
p++;
count++;
}
//TODO: ensure no duplicate on key2
if( countdup )
tags_deduplicate(tags);
}
guint32 *
tags_parse(const gchar *tagstring)
{
gchar **str_array;
guint32 *tags = NULL;
guint32 *ptags;
guint count, i;
Tag *tag;
DB( g_print("\n[tags] parse\n") );
if( tagstring )
{
str_array = g_strsplit (tagstring, " ", 0);
count = g_strv_length( str_array );
DB( g_print("- %d tags '%s'\n", count, tagstring) );
if( count > 0 )
{
tags = g_new0(guint32, count + 1);
ptags = tags;
for(i=0;iname = g_strdup(str_array[i]);
da_tag_append(newtag);
tag = da_tag_get_by_name(str_array[i]);
}
DB( g_print("- array add %d '%s'\n", tag->key, tag->name) );
//5.3 fixed duplicate tag in same tags
if( tags_key_exists(tags, tag->key) == FALSE )
*ptags++ = tag->key;
}
*ptags = 0;
}
g_strfreev (str_array);
}
return tags;
}
gchar *
tags_tostring(guint32 *tags)
{
guint count, i;
gchar **str_array, **tptr;
gchar *tagstring;
Tag *tag;
DB( g_print("\n[tags] tostring\n") );
if( tags == NULL )
{
return NULL;
}
else
{
count = tags_count(tags);
str_array = g_new0(gchar*, count+1);
tptr = str_array;
for(i=0;iname;
}
}
*tptr = NULL;
tagstring = g_strjoinv(" ", str_array);
g_free (str_array);
}
return tagstring;
}
void da_tag_consistency(Tag *item)
{
//#2018414 replace any space by -
hb_string_replace_char(' ', '-', item->name);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG
static void
da_tag_debug_array(guint32 *tags)
{
guint32 count = 0;
if( tags == NULL )
{
DB( g_print(" dbg: no tags\n") );
return;
}
while(*tags != 0 && count < 32)
{
DB( g_print(" [%d]=%d\n", count, *tags) );
tags++;
count++;
}
DB( g_print(" [%d]=%d\n", count, *tags) );
return;
}
static void
da_tag_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
{
guint32 *id = key;
Tag *item = value;
DB( g_print(" %d :: %s\n", *id, item->name) );
}
static void
da_tag_debug_list(void)
{
DB( g_print("\n** debug **\n") );
g_hash_table_foreach(GLOBALS->h_tag, da_tag_debug_list_ghfunc, NULL);
DB( g_print("\n** end debug **\n") );
}
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void
tag_move(guint32 key1, guint32 key2)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
DB( g_print("\n[tag] move %d => %d\n", key1, key2) );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
tags_move(txn->tags, key1, key2);
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
gboolean
tag_rename(Tag *item, const gchar *newname)
{
Tag *existitem;
gchar *stripname;
gboolean retval = FALSE;
DB( g_print("\n[tag] rename\n") );
stripname = g_strdup(newname);
g_strstrip(stripname);
//#2018414 replace any space by -
hb_string_replace_char(' ', '-', stripname);
existitem = da_tag_get_by_name(stripname);
if( existitem != NULL && existitem->key != item->key)
{
DB( g_print("- error, same name already exist with other key %d <> %d\n",existitem->key, item->key) );
g_free(stripname);
}
else
{
DB( g_print("- renaming\n") );
g_free(item->name);
item->name = stripname;
retval = TRUE;
}
return retval;
}
static gint
tag_glist_name_compare_func(Tag *a, Tag *b)
{
return hb_string_utf8_compare(a->name, b->name);
}
static gint
tag_glist_key_compare_func(Tag *a, Tag *b)
{
return a->key - b->key;
}
GList *tag_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_tag);
switch(column)
{
case HB_GLIST_SORT_NAME:
return g_list_sort(list, (GCompareFunc)tag_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)tag_glist_key_compare_func);
}
}
gboolean
tag_load_csv(gchar *filename, gchar **error)
{
gboolean retval;
GIOChannel *io;
gchar *tmpstr;
gint io_stat;
const gchar *encoding;
encoding = homebank_file_getencoding(filename);
DB( g_print(" -> encoding should be %s\n", encoding) );
retval = TRUE;
*error = NULL;
io = g_io_channel_new_file(filename, "r", NULL);
if(io != NULL)
{
if( encoding != NULL )
{
g_io_channel_set_encoding(io, encoding, NULL);
}
for(;;)
{
io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, NULL);
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_NORMAL)
{
if( tmpstr != NULL)
{
Tag *tag;
DB( g_print("\n + strip\n") );
hb_string_strip_crlf(tmpstr);
//#2018414 replace any space by -
hb_string_replace_char(' ', '-', tmpstr);
DB( g_print(" add tag:'%s' ?\n", tmpstr) );
tag = da_tag_append_if_new(tmpstr);
if( tag != NULL )
{
GLOBALS->changes_count++;
}
}
g_free(tmpstr);
}
}
g_io_channel_unref (io);
}
return retval;
}
void
tag_save_csv(gchar *filename)
{
GIOChannel *io;
GList *ltag, *list;
gchar *outstr;
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
ltag = list = tag_glist_sorted(HB_GLIST_SORT_NAME);
while (list != NULL)
{
Tag *item = list->data;
if(item->key != 0)
{
outstr = g_strdup_printf("%s\n", item->name);
DB( g_print(" + export %s\n", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
}
list = g_list_next(list);
}
g_list_free(ltag);
g_io_channel_unref (io);
}
}
homebank-5.9.7/src/ui-pref.c 0000644 0001750 0001750 00000300032 15005634041 015110 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#include "hb-pref-data.h"
#include "ui-pref.h"
#include "dsp-mainwindow.h"
#include "gtk-chart-colors.h"
#include "ui-currency.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
enum {
LST_PREF_UID,
LST_PREF_ICONNAME,
LST_PREF_LABEL,
LST_PREF_PAGENUM,
LST_PREF_MAX
};
//TODO: this is not used
//could be to save last page during session
enum
{
PREF_GENERAL,
PREF_INTERFACE,
PREF_THEMING,
PREF_COLOR,
PREF_LOCALE, //old DISPLAY
PREF_TXN, //old COLUMNS
PREF_TXN_DIALOG,
PREF_TXN_TRANSFER,
PREF_PAYMODE,
PREF_IMPORT,
PREF_REPORT,
PREF_FORECAST,
PREF_BACKUP,
PREF_FOLDERS,
PREF_EURO,
PREF_ADVANCED,
PREF_MAX
};
struct pref_list_datas CYA_PREF_GROUP[PREF_MAX+1] =
{
// level, identifier iconname label
{ 1, PREF_GENERAL , "prf-general" , N_("General") },
{ 1, PREF_INTERFACE , "prf-interface", N_("Interface") },
{ 2, PREF_INTERFACE , "prf-interface-theme", N_("Theming") },
{ 2, PREF_INTERFACE , "prf-interface-color", N_("Color") },
{ 1, PREF_LOCALE , "prf-locale", N_("Locale") },
{ 1, PREF_TXN , "prf-transaction", N_("Transactions") },
{ 2, PREF_TXN_DIALOG, "prf-transaction-dialog" , N_("Dialog") },
{ 2, PREF_TXN_TRANSFER, "prf-transaction-transfer", N_("Transfer") },
{ 2, PREF_PAYMODE , "prf-transaction-payment", N_("Payment") },
{ 1, PREF_IMPORT , "prf-import" , N_("Import/Export") },
{ 1, PREF_REPORT , "prf-report" , N_("Report") },
{ 1, PREF_FORECAST , "prf-forecast" , N_("Forecast") },
{ 1, PREF_BACKUP , "prf-backup" , N_("Backup") },
{ 1, PREF_FOLDERS , "prf-folder" , N_("Folders") },
{ 1, PREF_EURO , "prf-euro" , N_("Euro minor") },
{ 1, PREF_ADVANCED , "prf-advanced" , N_("Advanced") },
{ 0, 0, NULL , NULL }
};
extern HbKvData CYA_TOOLBAR_STYLE[];
extern HbKvData CYA_GRID_LINES[];
extern HbKvData CYA_IMPORT_DATEORDER[];
extern HbKvData CYA_IMPORT_OFXNAME[];
extern HbKvData CYA_IMPORT_OFXMEMO[];
extern HbKvData CYA_IMPORT_CSVSEPARATOR[];
extern HbKvData CYA_CHART_COLORSCHEME[];
extern HbKvData CYA_MONTHS[];
extern EuroParams euro_params[];
extern guint nb_euro_params;
extern EuroParams euro_params_euro;
extern LangName languagenames[];
static GtkWidget *pref_list_create(void);
static gint
ui_language_combobox_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
gchar *code1, *code2;
gchar *name1, *name2;
gtk_tree_model_get(model, a, 0, &code1, 1, &name1, -1);
gtk_tree_model_get(model, b, 0, &code2, 1, &name2, -1);
//keep system laguage on top
if(code1 == NULL) name1 = NULL;
if(code2 == NULL) name2 = NULL;
retval = hb_string_utf8_compare(name1, name2);
g_free(name2);
g_free(name1);
return retval;
}
static const gchar *
ui_language_combobox_get_name(const gchar *locale)
{
const gchar *lang;
DB( g_print("[ui_language_combobox_get_name]\n") );
// A locale directory name is typically of the form language[_territory]
lang = languagename_get (locale);
if (! lang)
{
const gchar *delimiter = strchr (locale, '_'); // strip off the territory suffix
if (delimiter)
{
gchar *copy = g_strndup (locale, delimiter - locale);
lang = languagename_get (copy);
g_free (copy);
}
if(! lang)
{
g_warning(" locale name not found '%s'", locale);
lang = locale;
}
}
return lang;
}
static void
ui_language_combobox_populate(GtkWidget *combobox)
{
GtkTreeModel *model;
GtkTreeIter iter;
GDir *dir;
const gchar *dirname;
DB( g_print("\n[ui-pref] lang populate\n") );
model = gtk_combo_box_get_model(GTK_COMBO_BOX(combobox));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
0, NULL,
1, _("System Language"),
-1);
DB( g_print(" open '%s'\n",homebank_app_get_locale_dir () ) );
dir = g_dir_open (homebank_app_get_locale_dir (), 0, NULL);
if (! dir)
return;
while ((dirname = g_dir_read_name (dir)) != NULL)
{
gchar *filename = g_build_filename (homebank_app_get_locale_dir (),
dirname,
"LC_MESSAGES",
GETTEXT_PACKAGE ".mo",
NULL);
DB( g_print("- seek for '%s'\n", filename) );
if (g_file_test (filename, G_FILE_TEST_EXISTS))
{
const gchar *lang;
gchar *label;
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
lang = ui_language_combobox_get_name(dirname);
label = g_strdup_printf ("%s [%s]", lang, dirname);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
0, dirname,
1, label,
-1);
g_free(label);
}
g_free (filename);
}
g_dir_close (dir);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
static GtkWidget *
ui_language_combobox_new(GtkWidget *label)
{
GtkListStore *store;
GtkWidget *combobox;
GtkCellRenderer *renderer;
DB( g_print("\n[ui-pref] lang combo new\n") );
store = gtk_list_store_new (2,
G_TYPE_STRING,
G_TYPE_STRING
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_language_combobox_compare_func, NULL, NULL);
combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, "text", 1, NULL);
gtk_combo_box_set_id_column( GTK_COMBO_BOX(combobox), 0);
g_object_unref(store);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), combobox);
ui_language_combobox_populate(combobox);
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
return combobox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
static gint ui_euro_combobox_id_to_active(gint id)
{
guint retval = 0;
DB( g_print("\n[ui-pref] ui_euro_combobox_id_to_active\n") );
for (guint i = 0; i < nb_euro_params; i++)
{
if( euro_params[i].id == id )
{
retval = i;
DB( g_print("- id (country)=%d => %d - %s\n", id, i, euro_params[i].name) );
break;
}
}
return retval;
}
static gint ui_euro_combobox_active_to_id(gint active)
{
gint id;
DB( g_print("\n[ui-pref] ui_euro_combobox_active_to_id\n") );
DB( g_print("- to %d\n", active) );
id = 0;
if( active < (gint)nb_euro_params )
{
id = euro_params[active].id;
DB( g_print("- id (country)=%d '%s'\n", id, euro_params[active].name) );
}
return id;
}
static GtkWidget *ui_euro_combobox_new(GtkWidget *label)
{
GtkWidget *combobox;
guint i;
DB( g_print("\n[ui-pref] make euro preset\n") );
combobox = gtk_combo_box_text_new();
for (i = 0; i < nb_euro_params; i++)
{
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox), euro_params[i].name);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), combobox);
return combobox;
}
static void defpref_pathselect(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gint type = GPOINTER_TO_INT(user_data);
gchar **path;
gchar *title;
GtkWidget *entry;
gboolean r;
DB( g_print("\n[ui-pref] path select\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
switch( type )
{
case PRF_PATH_WALLET:
path = &PREFS->path_hbfile;
entry = data->ST_path_hbfile;
title = _("Choose a default HomeBank files folder");
break;
case PRF_PATH_BACKUP:
path = &PREFS->path_hbbak;
entry = data->ST_path_hbbak;
title = _("Choose a default HomeBank backup files folder");
break;
case PRF_PATH_IMPORT:
path = &PREFS->path_import;
entry = data->ST_path_import;
title = _("Choose a default import folder");
break;
case PRF_PATH_EXPORT:
path = &PREFS->path_export;
entry = data->ST_path_export;
title = _("Choose a default export folder");
break;
default:
return;
}
DB( g_print(" - hbfile %p %s at %p\n" , PREFS->path_hbfile, PREFS->path_hbfile, &PREFS->path_hbfile) );
DB( g_print(" - import %p %s at %p\n" , PREFS->path_import, PREFS->path_import, &PREFS->path_import) );
DB( g_print(" - export %p %s at %p\n" , PREFS->path_export, PREFS->path_export, &PREFS->path_export) );
DB( g_print(" - before: %s %p\n" , *path, path) );
r = ui_file_chooser_folder(GTK_WINDOW(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), title, path);
DB( g_print(" - after: %s\n", *path) );
if( r == TRUE )
gtk_entry_set_text(GTK_ENTRY(entry), *path);
}
/*
** update the date sample label
*/
static void defpref_date_sample(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gchar buffer[256];
const gchar *fmt;
GDate *date;
DB( g_print("\n[ui-pref] date sample\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
fmt = gtk_entry_get_text(GTK_ENTRY(data->ST_datefmt));
date = g_date_new_julian (GLOBALS->today);
g_date_strftime (buffer, 256-1, fmt, date);
g_date_free(date);
gtk_label_set_text(GTK_LABEL(data->LB_date), buffer);
}
/*
** update the number sample label
*/
static void defpref_numbereuro_sample(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
Currency cur;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar buf[128];
DB( g_print("\n[ui-pref] number sample\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
cur.symbol = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_euro_symbol));
cur.sym_prefix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_euro_isprefix));
cur.decimal_char = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_euro_decimalchar));
cur.grouping_char = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_euro_groupingchar));
cur.frac_digits = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_euro_fracdigits));
da_cur_initformat (&cur);
DB( g_print("fmt: %s\n", cur.format) );
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), cur.format, HB_NUMBER_SAMPLE);
hb_str_formatd(buf, 127, formatd_buf, &cur, TRUE);
gtk_label_set_text(GTK_LABEL(data->LB_numbereuro), buf);
}
/*
** enable/disable euro
*/
static void defpref_eurotoggle(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] euro toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_euro_enable));
gtk_widget_set_sensitive(data->LB_euro_preset, sensitive);
gtk_widget_set_sensitive(data->CY_euro_preset, sensitive);
gtk_widget_set_sensitive(data->GRP_configuration, sensitive);
gtk_widget_set_sensitive(data->GRP_format , sensitive);
}
/*
** set euro value widget from a country
*/
static void defpref_eurosetcurrency(GtkWidget *widget, gint country)
{
struct defpref_data *data;
EuroParams *euro;
gchar *buf, *buf2;
gint active;
DB( g_print("\n[ui-pref] eurosetcurrency\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
active = ui_euro_combobox_id_to_active(country);
euro = &euro_params[active];
buf = g_strdup_printf("%s - %s", euro->iso, euro->name);
gtk_label_set_markup(GTK_LABEL(data->ST_euro_country), buf);
g_free(buf);
//5.9 change label
if(euro->mceii == FALSE)
{
buf = g_strdup_printf("1 %s _=", euro->iso);
buf2 = g_strdup("EUR");
}
else
{
buf = g_strdup("1 EUR _=");
buf2 = g_strdup(euro->iso);
}
gtk_label_set_text_with_mnemonic(GTK_LABEL(data->LB_euro_src), buf);
gtk_label_set_text_with_mnemonic(GTK_LABEL(data->LB_euro_dst), buf2);
g_free(buf);
g_free(buf2);
}
/*
** set euro value widget from a country
*/
static void defpref_europreset(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
EuroParams *euro;
gint active;
DB( g_print("\n[ui-pref] euro preset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
active = gtk_combo_box_get_active (GTK_COMBO_BOX(data->CY_euro_preset));
data->country = ui_euro_combobox_active_to_id (active);
defpref_eurosetcurrency(widget, data->country);
euro = &euro_params[active];
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_value), euro->value);
//#2066110 force EUR for non mceii
if( euro->mceii == FALSE)
{
euro = &euro_params_euro;
if( euro_country_notmceii_rate_update(data->country) )
{
DB( g_print(" >update rate\n") );
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_value), PREFS->euro_value);
}
}
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_fracdigits), euro->frac_digits);
gtk_entry_set_text(GTK_ENTRY(data->ST_euro_symbol) , euro->symbol);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_euro_isprefix), euro->sym_prefix);
gtk_entry_set_text(GTK_ENTRY(data->ST_euro_decimalchar) , euro->decimal_char);
gtk_entry_set_text(GTK_ENTRY(data->ST_euro_groupingchar), euro->grouping_char);
}
static void defpref_colorschemetoggle(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] color scheme toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_use_palette)) == TRUE )
{
GtkColorScheme scheme;
GdkRGBA rgba;
gint index;
index = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_color_scheme));
colorscheme_init(&scheme, index);
colorsheme_col8_to_rgba(&scheme.colors[scheme.cs_orange], &rgba);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_exp_color), &rgba);
colorsheme_col8_to_rgba(&scheme.colors[scheme.cs_green], &rgba);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_inc_color), &rgba);
colorsheme_col8_to_rgba(&scheme.colors[scheme.cs_red], &rgba);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_warn_color), &rgba);
colorsheme_col8_to_rgba(&scheme.colors[scheme.cs_blue], &rgba);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_fut_bg_color), &rgba);
}
sensitive = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_use_palette));
gtk_widget_set_sensitive(data->CP_exp_color, sensitive);
gtk_widget_set_sensitive(data->CP_inc_color, sensitive);
gtk_widget_set_sensitive(data->CP_warn_color, sensitive);
gtk_widget_set_sensitive(data->CP_fut_bg_color, sensitive);
}
static void defpref_memotoggle(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] memo acp toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_memoacp));
gtk_widget_set_sensitive(data->ST_memoacp_days, sensitive);
}
static void defpref_gtkoverridetoggle(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] gtk override toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_gtk_override));
gtk_widget_set_sensitive(data->LB_gtk_fontsize, sensitive);
gtk_widget_set_sensitive(data->NB_gtk_fontsize, sensitive);
}
static void defpref_backuptoggle(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] backup toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_bak_is_automatic));
gtk_widget_set_sensitive(data->LB_bak_max_num_copies, sensitive);
gtk_widget_set_sensitive(data->NB_bak_max_num_copies, sensitive);
gtk_widget_set_sensitive(data->GR_bak_freq , sensitive);
}
static void defpref_color_scheme_changed(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
DB( g_print("\n[ui-pref] color scheme changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
gtk_widget_queue_draw (data->DA_colors);
defpref_colorschemetoggle(widget, user_data);
}
/*
** set :: fill in widgets from PREFS structure
*/
static void defpref_set(struct defpref_data *data)
{
GdkRGBA rgba;
DB( g_print("\n[ui-pref] set\n") );
// general
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_show_splash), PREFS->showsplash);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_load_last), PREFS->loadlast);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_append_scheduled), PREFS->appendscheduled);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_do_update_currency), PREFS->do_update_currency);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_maxspenditems), PREFS->rep_maxspenditems);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_fiscyearday), PREFS->fisc_year_day );
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_fiscyearmonth), PREFS->fisc_year_month);
// files/backup
gtk_entry_set_text(GTK_ENTRY(data->ST_path_hbfile), PREFS->path_hbfile);
gtk_entry_set_text(GTK_ENTRY(data->ST_path_hbbak), PREFS->path_hbbak);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_bak_is_automatic), PREFS->bak_is_automatic);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_bak_max_num_copies), PREFS->bak_max_num_copies);
// interface
if(PREFS->language != NULL)
gtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_language), PREFS->language);
else
gtk_combo_box_set_active (GTK_COMBO_BOX(data->CY_language), 0);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_toolbar), PREFS->toolbar_style);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_gtk_override), PREFS->gtk_override);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_gtk_fontsize), PREFS->gtk_fontsize);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_gtk_darktheme), PREFS->gtk_darktheme);
gtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_icontheme), PREFS->icontheme);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_iconsymbolic), PREFS->icon_symbolic);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_image_size), PREFS->image_size);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_custom_colors), PREFS->custom_colors);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_custom_bg_future), PREFS->custom_colors);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_use_palette), PREFS->color_use_palette);
gdk_rgba_parse(&rgba, PREFS->color_exp);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_exp_color), &rgba);
gdk_rgba_parse(&rgba, PREFS->color_inc);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_inc_color), &rgba);
gdk_rgba_parse(&rgba, PREFS->color_warn);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_warn_color), &rgba);
gdk_rgba_parse(&rgba, PREFS->color_bg_future);
gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(data->CP_fut_bg_color), &rgba);
//gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_ruleshint), PREFS->rules_hint);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_gridlines), PREFS->grid_lines);
// transactions
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_daterange_txn), PREFS->date_range_txn);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_datefuture_nbdays), PREFS->date_future_nbdays);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_hide_reconciled), PREFS->hidereconciled);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_show_remind), PREFS->showremind);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_show_void), PREFS->showvoid);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_include_remind), PREFS->includeremind);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_lock_reconciled), PREFS->safe_lock_recon);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_safe_pend_recon), PREFS->safe_pend_recon);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_safe_pend_past), PREFS->safe_pend_past);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_safe_pend_past_days), PREFS->safe_pend_past_days);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_herit_date), PREFS->heritdate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_show_confirm), PREFS->txn_showconfirm);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_show_template), PREFS->txn_showtemplate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_memoacp), PREFS->txn_memoacp);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_memoacp_days), PREFS->txn_memoacp_days);
//xfer
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_xfer_showdialog), PREFS->xfer_showdialog);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_xfer_daygap), PREFS->xfer_daygap);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_xfer_syncdate), PREFS->xfer_syncdate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_xfer_syncstat), PREFS->xfer_syncstat);
// display format
gtk_entry_set_text(GTK_ENTRY(data->ST_datefmt), PREFS->date_format);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_unitismile), PREFS->vehicle_unit_ismile);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_unitisgal), PREFS->vehicle_unit_isgal);
// import/export
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_dtex_datefmt), PREFS->dtex_datefmt);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_dtex_ucfirst), PREFS->dtex_ucfirst);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_dtex_ofxname), PREFS->dtex_ofxname);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_dtex_ofxmemo), PREFS->dtex_ofxmemo);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_dtex_qifmemo), PREFS->dtex_qifmemo);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_dtex_qifswap), PREFS->dtex_qifswap);
gtk_entry_set_text(GTK_ENTRY(data->ST_path_import), PREFS->path_import);
gtk_entry_set_text(GTK_ENTRY(data->ST_path_export), PREFS->path_export);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_dtex_csvsep), PREFS->dtex_csvsep);
// report
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_daterange_rep), PREFS->date_range_rep);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_color_scheme), PREFS->report_color_scheme);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_rep_smallfont), PREFS->rep_smallfont);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_stat_byamount), PREFS->stat_byamount);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_stat_showrate), PREFS->stat_showrate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_stat_showdetail), PREFS->stat_showdetail);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_stat_incxfer), PREFS->stat_includexfer);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_budg_showdetail), PREFS->budg_showdetail);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_budg_unexclsub), PREFS->budg_unexclsub);
//forecast
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forecast), PREFS->rep_forcast);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_forecast_nbmonth), PREFS->rep_forecat_nbmonth);
/* euro */
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_euro_enable), PREFS->euro_active);
//gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_euro_preset), PREFS->euro_country);
data->country = PREFS->euro_country;
defpref_eurosetcurrency(data->dialog, data->country);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_value), PREFS->euro_value);
hbtk_entry_set_text(GTK_ENTRY(data->ST_euro_symbol), PREFS->minor_cur.symbol);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_euro_isprefix), PREFS->minor_cur.sym_prefix);
hbtk_entry_set_text(GTK_ENTRY(data->ST_euro_decimalchar), PREFS->minor_cur.decimal_char);
hbtk_entry_set_text(GTK_ENTRY(data->ST_euro_groupingchar), PREFS->minor_cur.grouping_char);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_fracdigits), PREFS->minor_cur.frac_digits);
//gtk_entry_set_text(GTK_ENTRY(data->ST_euro_symbol), PREFS->euro_symbol);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_euro_nbdec), PREFS->euro_nbdec);
//gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_euro_thsep), PREFS->euro_thsep);
//advanced
gtk_entry_set_text(GTK_ENTRY(data->ST_adv_apirate_url), PREFS->api_rate_url);
gtk_entry_set_text(GTK_ENTRY(data->ST_adv_apirate_key), PREFS->api_rate_key);
}
/*
** get :: fill PREFS structure from widgets
*/
#define RGBA_TO_INT(x) (int)(x*255)
static gchar *gdk_rgba_to_hex(GdkRGBA *rgba)
{
return g_strdup_printf("#%02x%02x%02x", RGBA_TO_INT(rgba->red), RGBA_TO_INT(rgba->green), RGBA_TO_INT(rgba->blue));
}
static void defpref_get(struct defpref_data *data)
{
GdkRGBA rgba;
const gchar *active_id;
const gchar *datfmt;
DB( g_print("\n[ui-pref] get\n") );
// general
PREFS->showsplash = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_show_splash));
PREFS->loadlast = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_load_last));
PREFS->appendscheduled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_append_scheduled));
PREFS->do_update_currency = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_do_update_currency));
//PREFS->date_range_wal = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_daterange_wal));
PREFS->rep_maxspenditems = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_maxspenditems));
PREFS->fisc_year_day = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_fiscyearday));
PREFS->fisc_year_month = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_fiscyearmonth));
// files/backup
hbtk_entry_replace_text(GTK_ENTRY(data->ST_path_hbfile), &PREFS->path_hbfile);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_path_hbbak) , &PREFS->path_hbbak);
PREFS->bak_is_automatic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_bak_is_automatic));
PREFS->bak_max_num_copies = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_bak_max_num_copies));
g_free(PREFS->language);
PREFS->language = NULL;
active_id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_language));
if(active_id != NULL)
{
PREFS->language = g_strdup(active_id);
}
PREFS->toolbar_style = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_toolbar));
//PREFS->image_size = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_image_size));
PREFS->gtk_override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_gtk_override));
PREFS->gtk_fontsize = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_gtk_fontsize));
PREFS->gtk_darktheme = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_gtk_darktheme));
//icontheme
active_id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_icontheme));
if(active_id != NULL)
{
g_free(PREFS->icontheme);
PREFS->icontheme = g_strdup(active_id);
}
PREFS->icon_symbolic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_iconsymbolic));
PREFS->custom_colors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_custom_colors));
PREFS->custom_bg_future = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_custom_bg_future));
PREFS->color_use_palette = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_use_palette));
gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(data->CP_exp_color), &rgba);
g_free(PREFS->color_exp);
PREFS->color_exp = gdk_rgba_to_hex(&rgba);
gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(data->CP_inc_color), &rgba);
g_free(PREFS->color_inc);
PREFS->color_inc = gdk_rgba_to_hex(&rgba);
gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(data->CP_warn_color), &rgba);
g_free(PREFS->color_warn);
PREFS->color_warn = gdk_rgba_to_hex(&rgba);
gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(data->CP_fut_bg_color), &rgba);
g_free(PREFS->color_bg_future);
PREFS->color_bg_future = gdk_rgba_to_hex(&rgba);
//PREFS->rules_hint = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_ruleshint));
PREFS->grid_lines = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_gridlines));
//list_txn_colpref_get(GTK_TREE_VIEW(data->LV_opecolumns), PREFS->lst_ope_columns);
// transaction
PREFS->date_range_txn = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_daterange_txn));
PREFS->date_future_nbdays = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_datefuture_nbdays));
PREFS->hidereconciled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_hide_reconciled));
PREFS->showremind = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_show_remind));
PREFS->showvoid = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_show_void));
PREFS->includeremind = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_include_remind));
PREFS->safe_lock_recon = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_lock_reconciled));
PREFS->safe_pend_recon = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_safe_pend_recon));
PREFS->safe_pend_past = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_safe_pend_past));
PREFS->safe_pend_past_days = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_safe_pend_past_days));
PREFS->heritdate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_herit_date));
PREFS->txn_showconfirm = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_show_confirm));
PREFS->txn_showtemplate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_show_template));
PREFS->txn_memoacp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_memoacp));
PREFS->txn_memoacp_days = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_memoacp_days));
// txn xfer
PREFS->xfer_showdialog = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_xfer_showdialog));
PREFS->xfer_daygap = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_xfer_daygap));
PREFS->xfer_syncdate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_xfer_syncdate));
PREFS->xfer_syncstat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_xfer_syncstat));
// display format
//1903437 don't allow empty/invalid entry
datfmt = gtk_entry_get_text(GTK_ENTRY(data->ST_datefmt));
if( strlen(datfmt) == 0 )
datfmt = "%x";
g_free(PREFS->date_format);
PREFS->date_format = g_strdup(datfmt);
PREFS->vehicle_unit_ismile = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_unitismile));
PREFS->vehicle_unit_isgal = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_unitisgal));
// import/export
PREFS->dtex_datefmt = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_dtex_datefmt));
PREFS->dtex_ucfirst = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_dtex_ucfirst));
PREFS->dtex_ofxname = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_dtex_ofxname));
PREFS->dtex_ofxmemo = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_dtex_ofxmemo));
PREFS->dtex_qifmemo = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_dtex_qifmemo));
PREFS->dtex_qifswap = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_dtex_qifswap));
hbtk_entry_replace_text(GTK_ENTRY(data->ST_path_import), &PREFS->path_import);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_path_export), &PREFS->path_export);
PREFS->dtex_csvsep = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_dtex_csvsep));
// report
PREFS->date_range_rep = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_daterange_rep));
PREFS->report_color_scheme = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_color_scheme));
PREFS->rep_smallfont = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_rep_smallfont));
PREFS->stat_byamount = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_stat_byamount));
PREFS->stat_showrate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_stat_showrate));
PREFS->stat_showdetail = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_stat_showdetail));
PREFS->stat_includexfer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_stat_incxfer));
PREFS->budg_showdetail = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_budg_showdetail));
PREFS->budg_unexclsub = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_budg_unexclsub));
//forecast
PREFS->rep_forcast = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forecast));
PREFS->rep_forecat_nbmonth = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_forecast_nbmonth));
// euro minor
PREFS->euro_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_euro_enable));
PREFS->euro_country = data->country;
PREFS->euro_value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_euro_value));
//strcpy(PREFS->euro_symbol, gtk_entry_get_text(GTK_ENTRY(data->ST_euro_symbol)));
//PREFS->euro_nbdec = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_euro_nbdec));
//PREFS->euro_thsep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_euro_thsep));
hbtk_entry_replace_text(GTK_ENTRY(data->ST_euro_symbol), &PREFS->minor_cur.symbol);
PREFS->minor_cur.sym_prefix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_euro_isprefix));
hbtk_entry_replace_text(GTK_ENTRY(data->ST_euro_decimalchar), &PREFS->minor_cur.decimal_char);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_euro_groupingchar), &PREFS->minor_cur.grouping_char);
PREFS->minor_cur.frac_digits = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_euro_fracdigits));
//PREFS->chart_legend = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_chartlegend));
//advanced
hbtk_entry_replace_text(GTK_ENTRY(data->ST_adv_apirate_url), &PREFS->api_rate_url);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_adv_apirate_key), &PREFS->api_rate_key);
paymode_list_get_order(GTK_TREE_VIEW(data->LV_paymode));
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkWidget *defpref_page_txn_payment (struct defpref_data *data)
{
GtkWidget *content_grid, *group_grid, *label, *widget, *scrollwin;
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 :: Payment
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(_("Payment shows & chooses"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("Use drag & drop to reorder"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 2, 1);
row++;
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_grid_attach (GTK_GRID (group_grid), scrollwin, 1, row, 2, 1);
widget = make_paymode_list();
data->LV_paymode = widget;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
gtk_widget_set_hexpand(scrollwin, TRUE);
gtk_widget_set_vexpand(scrollwin, TRUE);
return content_grid;
}
static GtkWidget *defpref_page_advanced (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 :: Advanced 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(_("Currency API"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("Url:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_adv_apirate_url = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("Key:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_adv_apirate_key = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
static GtkWidget *defpref_page_import (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 :: Date 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(_("General options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("Date order:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_DATEORDER);
data->CY_dtex_datefmt = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
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, 2, 1);
// group :: OFX/QFX options
crow++;
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(_("OFX/QFX options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("OFX _Name:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_OFXNAME);
data->CY_dtex_ofxname = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("OFX _Memo:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_OFXMEMO);
data->CY_dtex_ofxmemo = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: QIF options
crow++;
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(_("QIF options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Import memos"));
data->CM_dtex_qifmemo = widget;
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, 2, row, 1, 1);
// group :: other options
crow++;
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(_("CSV options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("(transaction import only)"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 2, 1);
row++;
label = make_label_left(_("Separator:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_CSVSEPARATOR);
data->CY_dtex_csvsep = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
#define cube_dim 16
static gboolean
draw_callback (GtkWidget *widget,
cairo_t *cr,
gpointer user_data)
{
struct defpref_data *data = user_data;
gint index;
GtkColorScheme scheme;
gint w, h;
gint i, x, y;
index = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_color_scheme));
colorscheme_init(&scheme, index);
gtk_widget_get_size_request (widget, &w, &h);
x = y = 0;
for(i=0;iCY_daterange_wal = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
*/
// row++;
label = make_label_left(_("Max _items:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(label, 5, 20);
data->ST_maxspenditems = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Initial filter
crow++;
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(_("Initial filter"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_Range:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_HIDDEN);
data->CY_daterange_rep = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Statistics options
crow++;
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(_("Statistics options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Show by _amount"));
data->CM_stat_byamount = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show _rate column"));
data->CM_stat_showrate = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show _details"));
data->CM_stat_showdetail = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Include _transfer"));
data->CM_stat_incxfer = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
// group :: Budget options
crow++;
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(_("Budget options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Show _details"));
data->CM_budg_showdetail = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Exclude subcategories from unbudgeted line"));
data->CM_budg_unexclsub = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
return content_grid;
}
static void defpref_cb_forecast_activate(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gboolean sensitive;
DB( g_print("\n[ui-pref] forecats activate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forecast));
gtk_widget_set_sensitive(data->LB_forecast_nbmonth , sensitive);
gtk_widget_set_sensitive(data->ST_forecast_nbmonth , sensitive);
}
static GtkWidget *defpref_page_forecast (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;
//5.7 forecast
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(_("Forecast"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Enable _forecast"));
data->CM_forecast = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
label = make_label_left(_("Month number:"));
data->LB_forecast_nbmonth = label;
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(NULL, 1, 36);
data->ST_forecast_nbmonth = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
static GtkWidget *defpref_page_euro (struct defpref_data *data)
{
GtkWidget *content_grid, *group_grid, *label, *widget, *expander;
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 :: General
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(_("General"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 4, 1);
row=1;
widget = gtk_check_button_new_with_mnemonic (_("_Enable"));
data->CM_euro_enable = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
label = make_label_left(_("_Preset:"));
data->LB_euro_preset =label;
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = ui_euro_combobox_new (label);
data->CY_euro_preset = widget;
gtk_widget_set_margin_start (label, 2*SPACING_LARGE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Configuration
crow++;
group_grid = gtk_grid_new ();
data->GRP_configuration = group_grid;
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(_("Configuration"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 4, 1);
row=1;
widget = make_label_left(NULL);
data->ST_euro_country = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
label = make_label_left("1 EUR _=");
data->LB_euro_src = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_exchange_rate(label);
data->NB_euro_value = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
label = make_label_left(NULL);
data->LB_euro_dst = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
// group :: Numbers format
crow++;
group_grid = gtk_grid_new ();
data->GRP_format = group_grid;
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(_("Format"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 4, 1);
row = 1;
widget = make_label_left(NULL);
data->LB_numbereuro = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
row++;
expander = gtk_expander_new_with_mnemonic(_("_Customize"));
gtk_grid_attach (GTK_GRID (group_grid), expander, 1, row, 1, 1);
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_expander_set_child(GTK_EXPANDER(expander), group_grid);
row = 0;
label = make_label_left(_("_Symbol:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 3);
data->ST_euro_symbol = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Is pre_fix"));
data->CM_euro_isprefix = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("_Decimal char:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 1);
data->ST_euro_decimalchar = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("_Frac digits:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(label, 0.0, 6.0);
data->NB_euro_fracdigits = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("_Grouping char:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string_maxlength(label, 1);
data->ST_euro_groupingchar = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
static GtkWidget *defpref_page_locale (struct defpref_data *data)
{
GtkWidget *content_grid, *group_grid, *label, *widget, *expander, *hbox;
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 :: General
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(_("User interface"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_Language:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = ui_language_combobox_new(label);
data->CY_language = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
row++;
label = make_label_left(_("Date display:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_label_left(NULL);
data->LB_date = widget;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_DIM_LABEL);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
expander = gtk_expander_new_with_mnemonic(_("C_ustomize"));
gtk_grid_attach (GTK_GRID (group_grid), expander, 2, row, 1, 1);
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_expander_set_child (GTK_EXPANDER(expander), group_grid);
row++;
label = make_label_left(_("_Format:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_datefmt = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
gtk_widget_set_tooltip_text(widget,
_("%a locale's abbreviated weekday name.\n"
"%A locale's full weekday name. \n"
"%b locale's abbreviated month name. \n"
"%B locale's full month name. \n"
"%c locale's appropriate date and time representation. \n"
"%C century number (the year divided by 100 and truncated to an integer) as a decimal number [00-99]. \n"
"%d day of the month as a decimal number [01,31]. \n"
"%D same as %m/%d/%y. \n"
"%e day of the month as a decimal number [1,31]; a single digit is preceded by a space. \n"
"%j day of the year as a decimal number [001,366]. \n"
"%m month as a decimal number [01,12]. \n"
"%p locale's appropriate date representation. \n"
"%y year without century as a decimal number [00,99]. \n"
"%Y year with century as a decimal number.")
);
row++;
widget = make_label_left(NULL);
gtk_label_set_markup (GTK_LABEL(widget), "online reference");
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Fiscal year
crow++;
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(_("Fiscal year"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
//TRANSLATORS: (fiscal year) starts on
label = make_label_left(_("Starts _on:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 1, 1);
widget = make_numeric (label, 1, 28);
data->NB_fiscyearday = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_combo_box_new_with_data (NULL, CYA_MONTHS);
data->CY_fiscyearmonth = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
// group :: Measurement units
crow++;
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(_("Measurement units"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Use _miles for meter"));
data->CM_unitismile = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Use _gallon for fuel"));
data->CM_unitisgal = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
return content_grid;
}
static GtkWidget *defpref_page_txn (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 :: txn list
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(_("General"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Always show remind"));
data->CM_show_remind = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Always show void"));
data->CM_show_void = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Include remind into balance and report"));
data->CM_include_remind = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
crow++;
// group :: safety
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(_("Safety"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Lock reconciled for any changes"));
data->CM_lock_reconciled = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Approve additions before last reconciliation"));
data->CM_safe_pend_recon = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Approve additions"));
data->CM_safe_pend_past = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = make_numeric(NULL, 0, 366);
data->ST_safe_pend_past_days = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//TRANSLATORS: there is a spinner on the left of this label, and so you have 0....x days as pending
label = make_label_left(_("days before today's date"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
crow++;
// group :: txn list
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(_("Ledger window"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_Range:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_HIDDEN);
data->CY_daterange_txn = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_left(_("_Show future:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(NULL, 0, 366);
data->ST_datefuture_nbdays = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//TRANSLATORS: there is a spinner on the left of this label, and so you have 0....x days in advance the current date
label = make_label_left(_("days ahead"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Hide reconciled"));
data->CM_hide_reconciled = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
return content_grid;
}
static GtkWidget *defpref_page_txn_dialog (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);
// group :: txn dialog
crow = 0;
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(_("Transaction dialog"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Keep the last date when multiple add or inherit"));
data->CM_herit_date = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Enable _memo autocomplete with"));
data->CM_memoacp = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
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_left(_("rolling days"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show add confirmation text for 5s"));
data->CM_show_confirm = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show template list when edit"));
data->CM_show_template = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
return content_grid;
}
static GtkWidget *defpref_page_txn_transfer (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);
// group :: transfer
crow = 0;
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(_("Behavior"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("When adding, always show selection _action for target dialog"));
//widget = gtk_check_button_new_with_mnemonic (_("Always prompt"));
data->CM_xfer_showdialog = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
label = make_label_left(_("Date _gap to find a target:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(NULL, 2, 7);
data->ST_xfer_daygap = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
label = make_label_left(_("days"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
// group :: transfer
crow++;
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(_("Synchronize"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Date"));
data->CM_xfer_syncdate = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("_Status"));
data->CM_xfer_syncstat = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
return content_grid;
}
/* test */
static void defpref_icons_changed_cb(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
const gchar *active_id;
gboolean symbolic;
DB( g_print("\n[ui-pref] cb icon changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
active_id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_icontheme));
symbolic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_iconsymbolic));
DB( g_print(" name: %s, sym:%d\n", active_id, symbolic ) );
//gtk_settings_set_string_property (gtk_settings_get_default (), "gtk-icon-theme-name", active_id, "gtkrc:0");
g_object_set(gtk_settings_get_default (), "gtk-icon-theme-name", active_id, NULL);
homebank_pref_icon_symbolic(symbolic);
}
/* end test */
static GtkWidget *defpref_page_intf_theming (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 :: Theming
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(_("Theme"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row=1;
#ifdef G_OS_UNIX
label = make_label_left(_("Dark mode:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
gchar *txt = _("System has no preference");
if( GLOBALS->color_scheme == PREFER_DARK )
txt = _("System prefer dark");
else
if( GLOBALS->color_scheme == PREFER_LIGHT )
txt = _("System prefer light");
label = make_label_left(txt);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(label)), GTK_STYLE_CLASS_DIM_LABEL);
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
row++;
#endif
widget = gtk_check_button_new_with_mnemonic (_("Use _dark mode if available"));
data->CM_gtk_darktheme = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
// group :: Icons
crow++;
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(_("Icons"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row=1;
label = make_label_left(_("_Icon theme:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new (label);
//future
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(widget), "Default", "Default");
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(widget), "hicolor", "Legacy");
data->CY_icontheme = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Use _symbolic icons if available"));
data->CM_iconsymbolic = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
// group :: GTK override
crow++;
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(_("Gtk settings"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Ov_erride"));
data->CM_gtk_override = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
label = make_label_left(_("_Font size:"));
data->LB_gtk_fontsize = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric(label, 8, 16);
data->NB_gtk_fontsize = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
static GtkWidget *defpref_page_intf_color (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 :: Theming
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);
row = 1;
label = make_label_group(_("Chart"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row++;
label = make_label_left(_("_Palette:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_array(label, chart_colors);
data->CY_color_scheme = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
widget = gtk_drawing_area_new ();
data->DA_colors = widget;
gtk_widget_set_size_request (widget, (1+cube_dim)*16, (1+cube_dim)*2);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 3, 1);
g_signal_connect (data->DA_colors, "draw", G_CALLBACK (draw_callback), data);
// group :: Others
crow++;
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(_("Others"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Use colors from the chart palette"));
data->CM_use_palette = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_color_button_new ();
data->CP_exp_color = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_left(_("_Expense"));
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL(label), widget);
row++;
widget = gtk_color_button_new ();
data->CP_inc_color = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_left(_("_Income"));
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL(label), widget);
row++;
widget = gtk_color_button_new ();
data->CP_warn_color = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_left(_("_Warning"));
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL(label), widget);
row++;
widget = gtk_color_button_new ();
data->CP_fut_bg_color = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_left(_("Background _future"));
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL(label), widget);
return content_grid;
}
static GtkWidget *defpref_page_intf (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 :: General
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(_("General"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Color the _amounts"));
data->CM_custom_colors = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Color the _background of future transactions"));
data->CM_custom_bg_future = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
//widget = gtk_check_button_new_with_mnemonic (_("Enable rows in alternating colors"));
//data->CM_ruleshint = widget;
label = make_label_left(_("_Grid line:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_GRID_LINES);
data->CY_gridlines = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group ::Charts options
crow++;
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(_("Charts options"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("Smaller legend _font"));
data->CM_rep_smallfont = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
// group :: Deprecated
crow += 5;
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("Deprecated");
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_Toolbar:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_TOOLBAR_STYLE);
data->CY_toolbar = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
return content_grid;
}
static GtkWidget *defpref_page_filebackup (struct defpref_data *data)
{
GtkWidget *content_grid, *group_grid, *hbox, *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 :: Backup
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(_("Backup"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Enable automatic backups"));
data->CM_bak_is_automatic = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
label = make_label_left(_("_Number of backups to keep:"));
data->LB_bak_max_num_copies = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_numeric (label, 1, 99);
data->NB_bak_max_num_copies = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
data->GR_bak_freq = hbox;
//gtk_widget_set_hexpand (hbox, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 2, 1);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label_left(_("Backup frequency is once a day"));
gtk_box_prepend (GTK_BOX (hbox), label);
return content_grid;
}
static GtkWidget *defpref_page_folders (struct defpref_data *data)
{
GtkWidget *content_grid, *group_grid, *hbox, *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 :: 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);
label = make_label_group(_("HomeBank files"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_Wallets:"));
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_hbfile = widget;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_LINKED);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//widget = gtk_button_new_with_label("...");
widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
data->BT_path_hbfile = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
row++;
label = make_label_left(_("_Backups:"));
//----------------------------------------- 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_hbbak = widget;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_LINKED);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//widget = gtk_button_new_with_label("...");
widget = gtk_button_new_from_icon_name(ICONNAME_FOLDER, GTK_ICON_SIZE_BUTTON);
data->BT_path_hbbak = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
// group :: Files folder
crow++;
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(_("Exchange files"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_left(_("_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);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//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_prepend (GTK_BOX (hbox), widget);
row++;
label = make_label_left(_("_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);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//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_prepend (GTK_BOX (hbox), widget);
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);
return content_grid;
}
static void defpref_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
struct defpref_data *data;
GtkWidget *notebook;
GtkTreeView *treeview;
GtkTreeModel *model;
GtkTreeIter iter;
GValue val = { 0, };
gint page_num;
DB( g_print("\n[ui-pref] selection\n") );
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
notebook = GTK_WIDGET(user_data);
treeview = gtk_tree_selection_get_tree_view(treeselection);
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
gtk_tree_model_get_value(model, &iter, LST_PREF_PAGENUM, &val);
page_num = g_value_get_int (&val);
DB( g_print(" pagenum: %d\n", page_num) );
g_value_unset (&val);
gtk_tree_model_get_value(model, &iter, LST_PREF_LABEL, &val);
gtk_label_set_text (GTK_LABEL (data->label), g_value_get_string (&val));
g_value_unset (&val);
gtk_tree_model_get_value(model, &iter, LST_PREF_ICONNAME, &val);
//gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), g_value_get_object (&val));
gtk_image_set_from_icon_name(GTK_IMAGE (data->image), g_value_get_string (&val), GTK_ICON_SIZE_DIALOG);
g_value_unset (&val);
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
//defpref_change_page(GTK_WIDGET(gtk_tree_selection_get_tree_view(treeselection)), GINT_TO_POINTER(page));
}
}
/*
** add an empty new account to our temp GList and treeview
*/
static void defpref_reset(GtkWidget *widget, gpointer user_data)
{
struct defpref_data *data;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n(defpref_reset) (data=%p)\n", data) );
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
_("Reset All Preferences"),
_("Do you really want to reset\nall preferences to default\nvalues?"),
_("_Reset"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
homebank_pref_setdefault();
defpref_set(data);
}
}
// the dialog creation
GtkWidget *defpref_dialog_new (void)
{
struct defpref_data *data;
GtkWidget *window, *content, *mainvbox;
GtkWidget *hbox, *vbox, *scrollwin, *widget, *notebook, *page, *image, *label;
data = g_malloc0(sizeof(struct defpref_data));
window = gtk_dialog_new_with_buttons (_("Preferences"),
GTK_WINDOW(GLOBALS->mainwindow),
0, //no flags
NULL, //no buttons
NULL);
widget = gtk_dialog_add_button(GTK_DIALOG(window), _("_Reset"), 55);
gtk_widget_set_margin_end(widget, SPACING_LARGE);
gtk_dialog_add_button(GTK_DIALOG(window), _("_Cancel"), GTK_RESPONSE_REJECT);
gtk_dialog_add_button(GTK_DIALOG(window), _("_OK"), GTK_RESPONSE_ACCEPT);
data->dialog = window;
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
content = gtk_dialog_get_content_area(GTK_DIALOG (window)); // return a vbox
mainvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_MEDIUM);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainvbox), hbox);
//left part
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (hbox), vbox);
//list
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
widget = pref_list_create();
data->LV_page = widget;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
//right part : notebook
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (hbox), vbox);
gtk_widget_show (vbox);
//header
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_name(hbox, "hbprfhead");
gtk_box_prepend (GTK_BOX (vbox), hbox);
gtk_widget_show (hbox);
GtkStyleContext *context = gtk_widget_get_style_context (hbox);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION <= 18) )
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LIST_ROW);
gtk_widget_set_state_flags(hbox, GTK_STATE_FLAG_SELECTED, TRUE);
#else
GtkCssProvider *provider;
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider,
"#hbprfhead { color: @theme_selected_fg_color; background-color: @theme_selected_bg_color; }"
, -1, NULL);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER(provider), G_MAXUINT);
// gtk_style_context_set_state(context, GTK_STATE_FLAG_SELECTED);
#endif
label = gtk_label_new (NULL);
hb_widget_set_margins(GTK_WIDGET(label), SPACING_SMALL, 0, SPACING_SMALL, SPACING_SMALL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_XX_LARGE,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_prepend (GTK_BOX (hbox), label);
gtk_widget_show (label);
data->label = label;
image = gtk_image_new ();
hb_widget_set_margins(GTK_WIDGET(image), SPACING_SMALL, SPACING_SMALL, SPACING_SMALL, 0);
gtk_box_append (GTK_BOX (hbox), image);
gtk_widget_show (image);
data->image = image;
//notebook
notebook = gtk_notebook_new();
data->GR_page = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
hbtk_box_prepend (GTK_BOX (vbox), notebook);
//general
page = defpref_page_general(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//interface
page = defpref_page_intf(data);
scrollwin = make_scrolled_window_ns(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), page);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrollwin, NULL);
//theming
page = defpref_page_intf_theming(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//color
page = defpref_page_intf_color(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//locale
page = defpref_page_locale(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//transaction
page = defpref_page_txn(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//dialog
page = defpref_page_txn_dialog(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//transfer
page = defpref_page_txn_transfer(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//5.8 temporary
page = defpref_page_txn_payment(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//import
page = defpref_page_import(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//report
page = defpref_page_reports(data);
scrollwin = make_scrolled_window_ns(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), page);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrollwin, NULL);
//forecast
page = defpref_page_forecast(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//backup
page = defpref_page_filebackup(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//folders
page = defpref_page_folders(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//euro
page = defpref_page_euro(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//advanced
page = defpref_page_advanced(data);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, NULL);
//todo:should move this
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_euro_enable), PREFS->euro_active);
//connect all our signals
g_signal_connect (data->CY_icontheme, "changed", G_CALLBACK (defpref_icons_changed_cb), NULL);
g_signal_connect (data->CM_iconsymbolic, "toggled", G_CALLBACK (defpref_icons_changed_cb), NULL);
g_signal_connect (data->CM_gtk_override, "toggled", G_CALLBACK (defpref_gtkoverridetoggle), NULL);
g_signal_connect (data->CM_bak_is_automatic, "toggled", G_CALLBACK (defpref_backuptoggle), NULL);
g_signal_connect (data->CM_memoacp, "toggled", G_CALLBACK (defpref_memotoggle), NULL);
//path selector
g_signal_connect (data->BT_path_hbfile, "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(PRF_PATH_WALLET));
g_signal_connect (data->BT_path_hbbak , "pressed", G_CALLBACK (defpref_pathselect), GINT_TO_POINTER(PRF_PATH_BACKUP));
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_use_palette, "toggled", G_CALLBACK (defpref_colorschemetoggle), NULL);
//g_signal_connect (data->CM_custom_colors, "toggled", G_CALLBACK (defpref_colortoggle), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)), "changed", G_CALLBACK (defpref_selection), notebook);
g_signal_connect (data->CM_euro_enable, "toggled", G_CALLBACK (defpref_eurotoggle), NULL);
g_signal_connect (data->CY_euro_preset, "changed", G_CALLBACK (defpref_europreset), NULL);
//forecast
g_signal_connect (data->CM_forecast, "toggled", G_CALLBACK (defpref_cb_forecast_activate), NULL);
//date
g_signal_connect (data->ST_datefmt, "changed", G_CALLBACK (defpref_date_sample), NULL);
//report
g_signal_connect (data->CY_color_scheme, "changed", G_CALLBACK (defpref_color_scheme_changed), NULL);
//euro number
g_signal_connect (data->ST_euro_symbol , "changed", G_CALLBACK (defpref_numbereuro_sample), NULL);
g_signal_connect (data->CM_euro_isprefix, "toggled", G_CALLBACK (defpref_numbereuro_sample), NULL);
g_signal_connect (data->ST_euro_decimalchar , "changed", G_CALLBACK (defpref_numbereuro_sample), NULL);
g_signal_connect (data->ST_euro_groupingchar, "changed", G_CALLBACK (defpref_numbereuro_sample), NULL);
g_signal_connect (data->NB_euro_fracdigits, "value-changed", G_CALLBACK (defpref_numbereuro_sample), NULL);
//g_signal_connect (data->BT_default, "pressed", G_CALLBACK (defpref_currency_change), NULL);
//setup, init and show window
//defhbfile_setup(data);
//defhbfile_update(data->LV_arc, NULL);
defpref_set(data);
defpref_gtkoverridetoggle(window, NULL);
defpref_memotoggle(window, NULL);
defpref_backuptoggle (window, NULL);
//defpref_colortoggle(window, NULL);
defpref_eurotoggle(window, NULL);
defpref_cb_forecast_activate(window, NULL);
gtk_window_resize(GTK_WINDOW(window), 640, 256);
gtk_widget_show_all (window);
//gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)));
//select last page
DB( g_print(" select %d:%d\n", PREFS->lastlvl1, PREFS->lastlvl2) );
if( PREFS->lastlvl1 > 0 )
{
GtkTreePath *path = gtk_tree_path_new_from_indices(PREFS->lastlvl1, PREFS->lastlvl2, -1);
gtk_tree_selection_select_path (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)), path);
gtk_tree_path_free(path);
}
else
{
//select first row
DB( g_print(" select first\n") );
GtkTreePath *path = gtk_tree_path_new_first ();
gtk_tree_selection_select_path (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)), path);
gtk_tree_path_free(path);
}
//defpref_selection(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)), NULL);
gint result;
gchar *old_lang, *old_path_hbbak;
//wait for the user
result = gtk_dialog_run (GTK_DIALOG (window));
switch( result )
{
case GTK_RESPONSE_ACCEPT:
//user attention needed if change is
//language/backup path
old_lang = g_strdup(PREFS->language);
old_path_hbbak = g_strdup(PREFS->path_hbbak);
defpref_get(data);
homebank_pref_save();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
DB( g_print("old='%s' new='%s'\n", old_lang, PREFS->language) );
//if(g_ascii_strncasecmp(old_lang == NULL ? "" : old_lang, PREFS->language == NULL ? "" : PREFS->language, -1) != 0)
if( hb_string_ascii_compare(old_lang, PREFS->language) != 0 )
{
ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_INFO,
_("Info"),
_("You will have to restart HomeBank\nfor the language change to take effect.")
);
}
if( hb_string_ascii_compare(old_path_hbbak, PREFS->path_hbbak) != 0 )
{
ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_INFO,
_("Info"),
_("The backup directory has changed,\nyou may need to copy the '.bak' file to this new location.")
);
}
g_free(old_lang);
g_free(old_path_hbbak);
break;
case 55:
defpref_reset (window, NULL);
break;
}
//store last page selection
{
GtkTreeModel *model;
GtkTreeIter iter;
gint depth;
if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_page)), &model, &iter))
{
//store last page
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
gint *indices = gtk_tree_path_get_indices_with_depth(path, &depth);
if( indices != NULL )
{
PREFS->lastlvl1 = indices[0];
if(depth > 1)
PREFS->lastlvl2 = indices[1];
else
PREFS->lastlvl2 = 0;
}
DB( g_print(" stored: %d:%d\n", PREFS->lastlvl1, PREFS->lastlvl2) );
gtk_tree_path_free(path);
}
}
// cleanup and destroy
//defhbfile_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(window));
g_free(data);
return window;
}
// -------------------------------
static GtkWidget *pref_list_create(void)
{
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeIter iter;
struct pref_list_datas *tmp;
gint i;
/* create list store */
store = gtk_tree_store_new(
LST_PREF_MAX,
G_TYPE_INT, //unique id
G_TYPE_STRING, //icon
G_TYPE_STRING, //label
G_TYPE_INT //pagenum
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_SINGLE);
/* column 1: icon */
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set(renderer, "stock-size", GTK_ICON_SIZE_DND, NULL);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_attributes(column, renderer, "icon-name", LST_PREF_ICONNAME, NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_attributes(column, renderer, "text", LST_PREF_LABEL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//populate our combobox model
for(i=0;ilabel == NULL )
break;
if( tmp->level == 1 )
{
gtk_tree_store_insert_with_values(store, &iter, NULL, -1,
LST_PREF_UID , tmp->key,
LST_PREF_ICONNAME , tmp->iconname,
LST_PREF_LABEL , _(tmp->label),
LST_PREF_PAGENUM , i,
-1);
}
else
{
gtk_tree_store_insert_with_values(store, NULL, &iter, -1,
LST_PREF_UID , tmp->key,
LST_PREF_ICONNAME , tmp->iconname,
LST_PREF_LABEL , _(tmp->label),
LST_PREF_PAGENUM , i,
-1);
}
}
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
return(treeview);
}
homebank-5.9.7/src/list-account.h 0000644 0001750 0001750 00000003301 14736461415 016167 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LIST_ACCOUNT__H__
#define __LIST_ACCOUNT__H__
enum
{
DSPACC_GROUP_BY_TYPE,
DSPACC_GROUP_BY_BANK,
DSPACC_GROUP_BY_GROUP
};
/* lst acc datatype */
enum
{
DSPACC_TYPE_NORMAL,
DSPACC_TYPE_HEADER,
DSPACC_TYPE_SUBTOTAL,
DSPACC_TYPE_TOTAL
};
/* list display account */
enum
{
LST_DSPACC_DATATYPE,
LST_DSPACC_POS,
LST_DSPACC_DATAS,
NUM_LST_DSPACC
};
enum
{
COL_DSPACC_STATUS,
COL_DSPACC_ACCOUNTS,
COL_DSPACC_RECON,
COL_DSPACC_CLEAR,
COL_DSPACC_TODAY,
COL_DSPACC_FUTURE,
NUM_LST_COL_DSPACC
};
enum {
LST_TXN_ACC_REC = 1 << 0, //detail/print
LST_TXN_ACC_CLR = 1 << 1, //!print
LST_TXN_ACC_TOD = 1 << 2,
LST_TXN_ACC_FUT = 1 << 3,
};
struct lst_accview_data
{
GtkWidget *treeview;
GtkWidget *menu;
};
GString *lst_accview_to_string(GtkTreeView *treeview, ToStringMode mode);
gboolean list_account_level1_expanded(GtkTreeView *treeview, gchar *name);
GtkWidget *lst_accview_new(void);
#endif
homebank-5.9.7/src/ui-budget.c 0000644 0001750 0001750 00000113477 15120542522 015445 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#include "ui-category.h"
#include "ui-budget.h"
extern gchar *CYA_CAT_TYPE[];
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern gchar *CYA_ABMONTHS[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
**
** The function should return:
** a negative integer if the first value comes before the second,
** 0 if they are equal,
** or a positive integer if the first value comes after the second.
*/
static gint ui_bud_listview_compare_funct (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
Category *entry1, *entry2;
gtk_tree_model_get(model, a, LST_DEFCAT_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DEFCAT_DATAS, &entry2, -1);
retval = (entry1->flags & GF_INCOME) - (entry2->flags & GF_INCOME);
if(!retval)
{
retval = hb_string_utf8_compare(entry1->name, entry2->name);
}
return retval;
}
/*
**
*/
static void ui_bud_listview_icon_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Category *item;
gchar *iconname = NULL;
// get the transaction
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
//5.3 added
if( item->flags & GF_FORCED )
iconname = ICONNAME_HB_ITEM_FORCED;
else
if( item->flags & GF_BUDGET )
iconname = ICONNAME_HB_ITEM_BUDGET;
g_object_set(renderer, "icon-name", iconname, NULL);
}
/*
** draw some text from the stored data structure
*/
static void ui_bud_listview_cell_data_function_text (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Category *entry;
gchar *name;
gchar *string;
gchar type;
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
name = da_cat_get_name(entry);
type = category_get_type_char(entry);
if(entry->key == 0)
string = g_strdup(name);
else
{
if(entry->flags & GF_BUDGET)
{
if( entry->parent == 0 )
string = g_markup_printf_escaped("%s [%c]", name, type);
else
string = g_markup_printf_escaped(" %c %s", type, name);
}
else
{
if( entry->parent == 0 )
string = g_markup_printf_escaped("%s [%c]", name, type);
else
string = g_markup_printf_escaped(" %c %s", type, name);
}
}
g_object_set(renderer, "markup", string, NULL);
g_free(string);
}
#if MYDEBUG == 1
static void
ui_bud_listview_cell_data_function_debug (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Category *entry;
gchar *string;
gchar type;
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
type = category_get_type_char(entry);
string = g_markup_printf_escaped("[%d] f:%d t:%c", entry->key, entry->flags, type );
g_object_set(renderer, "markup", string, NULL);
g_free(string);
}
#endif
static gboolean ui_bud_listview_search_equal_func (GtkTreeModel *model,
gint column,
const gchar *key,
GtkTreeIter *iter,
gpointer search_data)
{
gboolean retval = TRUE;
gchar *normalized_string;
gchar *normalized_key;
gchar *case_normalized_string = NULL;
gchar *case_normalized_key = NULL;
Category *item;
//gtk_tree_model_get_value (model, iter, column, &value);
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
if(item != NULL)
{
normalized_string = g_utf8_normalize (item->name, -1, G_NORMALIZE_ALL);
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
if (normalized_string && normalized_key)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
case_normalized_key = g_utf8_casefold (normalized_key, -1);
if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
retval = FALSE;
}
g_free (normalized_key);
g_free (normalized_string);
g_free (case_normalized_key);
g_free (case_normalized_string);
}
return retval;
}
/*
**
*/
static GtkWidget *ui_bud_listview_new(void)
{
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
//store
store = gtk_tree_store_new (
3,
//NUM_LST_DEFCAT,
G_TYPE_BOOLEAN,
G_TYPE_POINTER,
G_TYPE_UINT
);
//sortable
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCAT_DATAS, ui_bud_listview_compare_funct, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFCAT_DATAS, GTK_SORT_ASCENDING);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW (treeview), TRUE);
#if MYDEBUG == 1
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_bud_listview_cell_data_function_debug, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
/* icon column */
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_bud_listview_icon_cell_data_function, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* category name */
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Category"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_bud_listview_cell_data_function_text, GINT_TO_POINTER(1), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST);
//gtk_tree_view_column_set_sort_column_id (column, LST_DEFACC_NAME);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_bud_listview_search_equal_func, NULL, NULL);
//gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
return(treeview);
}
/*
** index 0 is all month, then 1 -> 12 are months
*/
static gchar *ui_bud_manage_getcsvbudgetstr(Category *item)
{
gchar *retval = NULL;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
//DB( g_print(" get budgetstr for '%s'\n", item->name) );
if( !(item->flags & GF_CUSTOM) )
{
if( item->budget[0] )
{
//g_ascii_dtostr (buf, sizeof (buf), item->budget[0]);
//#1750257 use locale numdigit
g_snprintf(buf, sizeof (buf), "%.2f", item->budget[0]);
retval = g_strdup(buf);
//DB( g_print(" => %d: %s\n", 0, retval) );
}
}
else
{
gint i;
for(i=1;i<=12;i++)
{
//if( item->budget[i] )
//{
gchar *tmp = retval;
//g_ascii_dtostr (buf, sizeof (buf), item->budget[i]);
//#1750257 use locale numdigit
g_snprintf(buf, sizeof (buf), "%.2f", item->budget[i]);
if(retval != NULL)
{
retval = g_strconcat(retval, ";", buf, NULL);
g_free(tmp);
}
else
retval = g_strdup(buf);
//DB( g_print(" => %d: %s\n", i, retval) );
//}
}
}
return retval;
}
static gint ui_bud_manage_category_exists (GtkTreeModel *model, gchar *level, gchar *type, gchar *fullname, GtkTreeIter *return_iter)
{
GtkTreeIter iter, child;
gboolean valid;
Category *entry;
gint pos = 0;
if(model == NULL)
return 0;
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gtk_tree_model_get (model, &iter, LST_DEFCAT_DATAS, &entry, -1);
if(*level == '1')
{
if(entry->fullname && g_ascii_strcasecmp(fullname, entry->fullname) == 0)
{
*return_iter = iter;
return pos;
}
}
else
{
if(*level == '2')
{
gint n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
gtk_tree_model_get(GTK_TREE_MODEL(model), &child,
LST_DEFCAT_DATAS, &entry,
-1);
if(entry->fullname && g_ascii_strcasecmp(fullname, entry->fullname) == 0)
{
*return_iter = child;
return pos;
}
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
pos++;
}
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
pos++;
}
return 0;
}
static void ui_bud_manage_load_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_bud_manage_data *data = user_data;
gchar *filename = NULL;
GIOChannel *io;
const gchar *encoding;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] load csv - data %p\n", data) );
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
encoding = homebank_file_getencoding(filename);
io = g_io_channel_new_file(filename, "r", NULL);
if(io != NULL)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean error = FALSE;
gchar *tmpstr;
gint io_stat;
DB( g_print(" -> encoding should be %s\n", encoding) );
if( encoding != NULL )
{
g_io_channel_set_encoding(io, encoding, NULL);
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
for(;;)
{
io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, NULL);
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_NORMAL)
{
if( tmpstr != NULL)
{
gchar **str_array;
hb_string_strip_crlf(tmpstr);
str_array = g_strsplit (tmpstr, ";", 15);
// type; sign; name
if( (g_strv_length (str_array) < 4 || *str_array[1] != '*') && (g_strv_length (str_array) < 15))
{
error = TRUE;
break;
}
DB( g_print(" csv read '%s : %s : %s ...'\n", str_array[0], str_array[1], str_array[2]) );
gint pos = ui_bud_manage_category_exists(model, str_array[0], str_array[1], str_array[2], &iter);
DB( g_print(" pos=%d\n", pos) );
if( pos != 0 )
{
gboolean budget;
Category *tmpitem;
gint i;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
LST_DEFCAT_DATAS, &tmpitem,
-1);
DB( g_print(" found cat, updating '%s' '%s'\n", tmpitem->name, tmpitem->fullname) );
data->change++;
tmpitem->flags &= ~(GF_CUSTOM); //delete flag
if( *str_array[1] == '*' )
{
//tmpitem->budget[0] = g_ascii_strtod(str_array[3], NULL);
//#1750257 use locale numdigit
tmpitem->budget[0] = g_strtod(str_array[3], NULL);
DB( g_print(" monthly '%.2f'\n", tmpitem->budget[0]) );
}
else
{
tmpitem->flags |= (GF_CUSTOM);
for(i=1;i<=12;i++)
{
//tmpitem->budget[i] = g_ascii_strtod(str_array[2+i], NULL);
//#1750257 use locale numdigit
tmpitem->budget[i] = g_strtod(str_array[2+i], NULL);
DB( g_print(" month %d '%.2f'\n", i, tmpitem->budget[i]) );
}
}
// if any value,set the flag to visual indicator
budget = FALSE;
tmpitem->flags &= ~(GF_BUDGET); //delete flag
for(i=0;i<=12;i++)
{
if(tmpitem->budget[i])
{
budget = TRUE;
break;
}
}
if(budget == TRUE)
tmpitem->flags |= GF_BUDGET;
}
g_strfreev (str_array);
}
g_free(tmpstr);
}
}
//update the treeview
gtk_tree_selection_unselect_all (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)));
g_io_channel_unref (io);
if( error == TRUE )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("File format error"),
_("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
);
}
}
g_free( filename );
}
}
static void ui_bud_manage_save_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_bud_manage_data *data = user_data;
gchar *filename = NULL;
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
GIOChannel *io;
DB( g_print("\n[ui-budget] save csv\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gchar *outstr, *outvalstr;
Category *category;
gchar type;
gtk_tree_model_get (GTK_TREE_MODEL(model), &iter, LST_DEFCAT_DATAS, &category, -1);
if( category->name != NULL )
{
//level 1: category
if( category->flags & GF_BUDGET )
{
type = (category->flags & GF_CUSTOM) ? ' ' : '*';
outvalstr = ui_bud_manage_getcsvbudgetstr(category);
outstr = g_strdup_printf("1;%c;%s;%s\n", type, category->fullname, outvalstr);
DB( g_print("%s", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
g_free(outvalstr);
}
//level 2: subcategory
gint n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
gtk_tree_model_get(GTK_TREE_MODEL(model), &child, LST_DEFCAT_DATAS, &category, -1);
type = (category->flags & GF_CUSTOM) ? ' ' : '*';
outvalstr = ui_bud_manage_getcsvbudgetstr(category);
if( outvalstr )
{
outstr = g_strdup_printf("2;%c;%s;%s\n", type, category->fullname, outvalstr);
DB( g_print("%s", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
}
g_free(outvalstr);
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
g_io_channel_unref (io);
}
g_free( filename );
}
}
static void ui_bud_manage_expand_all(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] expand all (data=%p)\n", data) );
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_cat));
}
static void ui_bud_manage_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] collapse all (data=%p)\n", data) );
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_cat));
}
static void ui_bud_manage_update(GtkWidget *treeview, gpointer user_data)
{
struct ui_bud_manage_data *data;
gboolean name, custom, sensitive;
gint i;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] update %p\n", data) );
name = FALSE;
if(data->cat != NULL)
{
name = data->cat->key == 0 ? FALSE : TRUE;
}
sensitive = name;
gtk_widget_set_sensitive(data->label_budget, sensitive);
gtk_widget_set_sensitive(data->CM_type[0], sensitive);
gtk_widget_set_sensitive(data->CM_type[1], sensitive);
gtk_widget_set_sensitive(data->label_options, sensitive);
gtk_widget_set_sensitive(data->CM_force, sensitive);
gtk_widget_set_sensitive(data->BT_clear, sensitive);
#if MYDEBUG == 1
gint toto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[0]));
DB( g_print(" monthly = %d\n", toto) );
#endif
custom = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[1]));
DB( g_print(" custom = %d\n", custom) );
sensitive = name == FALSE ? FALSE : custom == TRUE ? FALSE: TRUE;
gtk_widget_set_sensitive(data->spinner[0], sensitive);
sensitive = name == FALSE ? FALSE : custom;
for(i=0;i<12;i++)
{
gtk_widget_set_sensitive(data->label[i+1], sensitive);
gtk_widget_set_sensitive(data->spinner[i+1], sensitive);
}
DB( g_print(" -- end update %p\n", data) );
}
static void
ui_bud_manage_compute_total(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
GList *lcat, *list;
gdouble value;
gint j;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] compute total\n") );
data->totexp = 0;
data->totinc = 0;
lcat = list = category_glist_sorted(HB_GLIST_SORT_NAME);
while (list != NULL)
{
Category *item = list->data;
value = 0;
if(!(item->flags & GF_CUSTOM))
{
value += (12*item->budget[0]);
}
//otherwise sum each month from mindate month
else
{
//#1859476 <= vs <
for(j=1;j<=12;j++) {
value += item->budget[j];
}
}
if( value <= 0.0 )
data->totexp += value;
else
data->totinc += value;
list = g_list_next(list);
}
g_list_free(lcat);
hb_label_set_colvalue(GTK_LABEL(data->TX_totexp), data->totexp, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_totinc), data->totinc, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_totbal), data->totexp + data->totinc, GLOBALS->kcur, GLOBALS->minor);
}
static void
ui_bud_manage_cb_forcemonitor_toggled(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
Category *item;
DB( g_print("\n[ui-budget] forced toggled\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
item = data->cat;
if( item )
{
item->flags &= ~(GF_FORCED);
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_force)) && !(item->flags & GF_BUDGET) )
item->flags |= GF_FORCED;
//#1918479 set chnage
data->change++;
hbtk_listview_redraw_selected_row (GTK_TREE_VIEW(data->LV_cat));
}
}
static void
ui_bud_manage_toggle(GtkCheckButton *checkbutton, gpointer user_data)
{
//struct ui_bud_manage_data *data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(radiobutton), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] toggle\n") );
//ui_bud_manage_get(GTK_WIDGET(radiobutton), GINT_TO_POINTER(FIELD_TYPE));
//data->custom ^= 1;
ui_bud_manage_update(GTK_WIDGET(checkbutton), NULL);
}
static void ui_bud_manage_get(struct ui_bud_manage_data *data, Category *item)
{
DB( g_print("\n[ui-budget] get\n") );
if( item != NULL )
{
guint prvflg = item->flags;
gdouble prvsum = 0.0;
gdouble sum = 0.0;
item->flags &= ~(GF_CUSTOM|GF_BUDGET|GF_FORCED);
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_type[0])) == FALSE)
{
item->flags |= GF_CUSTOM;
}
for(guint i=0;i<=12;i++)
{
prvsum += item->budget[i];
gtk_spin_button_update(GTK_SPIN_BUTTON(data->spinner[i]));
//#2052304 ensure sign
item->budget[i] = ABS(gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->spinner[i])));
if( !(item->flags & GF_INCOME) )
item->budget[i] *= -1;
sum += item->budget[i];
if( item->budget[i] )
item->flags |= GF_BUDGET;
}
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_force)) && !(item->flags & GF_BUDGET) )
{
item->flags |= GF_FORCED;
}
//#1861008 count changes here
if( (prvflg != item->flags) || (prvsum != sum) )
data->change++;
}
DB( g_print(" -- end get") );
}
static gboolean ui_bud_manage_cb_budget_changed(GtkSpinButton *spinbutton, gpointer user_data)
{
struct ui_bud_manage_data *data;
gboolean retval = FALSE;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(spinbutton), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] budget changed\n") );
ui_bud_manage_get(data, data->cat);
hbtk_listview_redraw_selected_row (GTK_TREE_VIEW(data->LV_cat));
ui_bud_manage_compute_total(data->dialog, NULL);
DB( g_print(" -- end budget changed\n") );
return retval;
}
static void ui_bud_manage_getlast(struct ui_bud_manage_data *data)
{
Category *item;
DB( g_print("\n[ui-budget] getlast\n") );
item = data->lastcatitem;
if( item != NULL )
{
ui_bud_manage_get(data, item);
hbtk_listview_redraw_selected_row (GTK_TREE_VIEW(data->LV_cat));
ui_bud_manage_compute_total(data->dialog, NULL);
}
DB( g_print(" -- end getlast") );
}
static void ui_bud_manage_set(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
gint active;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] set\n") );
active = (data->cat->flags & GF_CUSTOM) ? 1 : 0;
g_signal_handlers_block_by_func (data->CM_type[0], G_CALLBACK (ui_bud_manage_toggle), NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_type[active]), TRUE);
g_signal_handlers_unblock_by_func (data->CM_type[0], G_CALLBACK (ui_bud_manage_toggle), NULL);
for(guint i=0;i<=12;i++)
{
g_signal_handlers_block_by_func (data->spinner[i], G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->spinner[i]), data->cat->budget[i]);
g_signal_handlers_unblock_by_func (data->spinner[i], G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
}
g_signal_handlers_block_by_func (data->CM_force, G_CALLBACK (ui_bud_manage_cb_forcemonitor_toggled), NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_force), (data->cat->flags & GF_FORCED) ? 1 : 0);
g_signal_handlers_unblock_by_func (data->CM_force, G_CALLBACK (ui_bud_manage_cb_forcemonitor_toggled), NULL);
DB( g_print(" -- end set\n") );
}
static void ui_bud_manage_clear(GtkWidget *widget, gpointer user_data)
{
struct ui_bud_manage_data *data;
gchar *title;
gchar *secondtext;
gint result, i;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] clear\n") );
title = _("Are you sure you want to clear input?");
secondtext = _("If you proceed, every amount will be set to 0.");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Clear"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
//g_signal_handler_block(data->CM_type[0], data->handler_id[HID_CUSTOM]);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_type[0]), TRUE);
//g_signal_handler_unblock(data->CM_type[0], data->handler_id[HID_CUSTOM]);
for(i=0;i<=12;i++)
{
g_signal_handlers_block_by_func (data->spinner[i], G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->spinner[i]), 0);
data->cat->budget[i] = 0;
g_signal_handlers_unblock_by_func (data->spinner[i], G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
}
data->cat->flags &= ~(GF_BUDGET); //delete flag
data->change++;
hbtk_listview_redraw_selected_row (GTK_TREE_VIEW(data->LV_cat));
ui_bud_manage_compute_total(data->dialog, NULL);
}
}
static void ui_bud_manage_selection_change(GtkWidget *treeview, gpointer user_data)
{
struct ui_bud_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gchar *title;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] selection changed\n") );
data->cat = NULL;
if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)), &model, &iter))
{
Category *item;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
LST_DEFCAT_DATAS, &item,
-1);
DB( g_print(" selected %s\n", item->name) );
title = g_strdup_printf(_("Budget for %s"), item->fullname);
gtk_label_set_text(GTK_LABEL(data->label_budget), title);
g_free(title);
if(data->lastcatitem != NULL && item != data->lastcatitem)
{
DB( g_print(" -> should do a get for last selected (%s)\n", data->lastcatitem->name) );
ui_bud_manage_getlast(data);
}
data->cat = item;
data->lastcatitem = item;
ui_bud_manage_set(treeview, NULL);
}
else
{
data->lastcatitem = NULL;
gtk_label_set_text(GTK_LABEL(data->label_budget), NULL);
}
ui_bud_manage_update(treeview, NULL);
DB( g_print(" -- end selection changed\n") );
}
static void ui_bud_manage_populate_listview(struct ui_bud_manage_data *data)
{
gint type;
DB( g_print("\n[ui-budget] populate listview\n") );
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type)) == 1 ? CAT_TYPE_INCOME : CAT_TYPE_EXPENSE;
ui_cat_listview_populate(data->LV_cat, type, NULL, TRUE);
//gtk_tree_view_expand_all (GTK_TREE_VIEW(data->LV_cat));
DB( g_print(" -- end populate listview\n") );
}
static void ui_bud_manage_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_bud_manage_selection_change(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void ui_bud_manage_cb_type_changed (GtkToggleButton *button, gpointer user_data)
{
ui_bud_manage_populate_listview(user_data);
//g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
}
static gboolean ui_bud_manage_cleanup(struct ui_bud_manage_data *data, gint result)
{
gboolean doupdate = FALSE;
DB( g_print("\n[ui-budget] cleanup\n") );
if(data->lastcatitem != NULL)
{
DB( g_print(" -> should do a get for last selected (%s)\n", data->lastcatitem->name) );
ui_bud_manage_getlast(data);
}
//do_application_specific_something ();
DB( g_print(" accept %d changes\n", data->change) );
GLOBALS->changes_count += data->change;
DB( g_print(" free tmp_list\n") );
return doupdate;
}
static void ui_bud_manage_setup(struct ui_bud_manage_data *data)
{
DB( g_print("\n[ui-budget] setup\n") );
DB( g_print(" init data\n") );
data->tmp_list = NULL;
data->change = 0;
data->cat = NULL;
data->lastcatitem = NULL;
DB( g_print(" populate\n") );
ui_bud_manage_populate_listview(data);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
g_signal_connect (data->RA_type, "changed", G_CALLBACK (ui_bud_manage_cb_type_changed), data);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)), "changed", G_CALLBACK (ui_bud_manage_selection), NULL);
//g_signal_connect (GTK_TREE_VIEW(data->LV_cat), "row-activated", G_CALLBACK (ui_bud_manage_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_expand), "clicked", G_CALLBACK (ui_bud_manage_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapse), "clicked", G_CALLBACK (ui_bud_manage_collapse_all), NULL);
g_signal_connect (data->CM_type[0], "toggled", G_CALLBACK (ui_bud_manage_toggle), NULL);
g_signal_connect (G_OBJECT (data->BT_clear), "clicked", G_CALLBACK (ui_bud_manage_clear), NULL);
g_signal_connect (data->CM_force, "toggled", G_CALLBACK (ui_bud_manage_cb_forcemonitor_toggled), NULL);
}
static gboolean
ui_bud_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_bud_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n(ui_bud_manage_mapped)\n") );
ui_bud_manage_setup(data);
ui_bud_manage_compute_total(data->dialog, NULL);
ui_bud_manage_update(data->dialog, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static const GActionEntry win_actions[] = {
{ "imp" , ui_bud_manage_load_csv, NULL, NULL, NULL, {0,0,0} },
{ "exp" , ui_bud_manage_save_csv, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
GtkWidget *ui_bud_manage_dialog (void)
{
struct ui_bud_manage_data *data;
GtkWidget *dialog, *content_area;
GtkWidget *content_grid, *group_grid, *scrollwin, *label;
GtkWidget *treeview, *hpaned, *bbox, *vbox, *hbox;
GtkWidget *widget, *image, *tbar;
GList *fchain;
guint i;
gint w, h, dw, dh;
gint crow, row;
data = g_malloc0(sizeof(struct ui_bud_manage_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Budget"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print("\n[ui-budget] window=%p, inst_data=%p\n", dialog, data) );
//window contents
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
hb_widget_set_margin(GTK_WIDGET(hpaned), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), hpaned);
/* left area */
//list
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_end(vbox, SPACING_SMALL);
gtk_paned_pack1 (GTK_PANED(hpaned), vbox, FALSE, FALSE);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_margin_bottom(hbox, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_CAT_TYPE, TRUE);
data->RA_type = widget;
gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//menubutton
widget = gtk_menu_button_new();
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_box_append(GTK_BOX (hbox), widget);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Import CSV..."), "win.imp");
g_menu_append (section, _("E_xport CSV..."), "win.exp");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (widget, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
treeview = (GtkWidget *)ui_bud_listview_new();
data->LV_cat = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
/* right area */
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);
gtk_widget_set_margin_start(content_grid, SPACING_SMALL);
gtk_paned_pack2 (GTK_PANED(hpaned), content_grid, FALSE, FALSE);
crow = 0;
// group :: General
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);
gtk_widget_set_halign(group_grid, GTK_ALIGN_END);
//label = make_label_group(_("Budget total"));
//gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = gtk_label_new(_("Expense:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
label = gtk_label_new(NULL);
data->TX_totexp = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
label = gtk_label_new(_("Income:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
label = gtk_label_new(NULL);
data->TX_totinc = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 4, row, 1, 1);
label = gtk_label_new(_("Balance:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 5, row, 1, 1);
label = gtk_label_new(NULL);
data->TX_totbal = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 6, row, 1, 1);
// group :: General
crow++;
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(NULL);
data->label_budget = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 5, 1);
fchain = NULL;
row = 1;
widget = gtk_radio_button_new_with_label (NULL, _("is the same each month"));
data->CM_type[0] = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
fchain = g_list_append(fchain, widget);
row++;
widget = make_amount(label);
data->spinner[0] = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
fchain = g_list_append(fchain, widget);
g_signal_connect (G_OBJECT (data->spinner[0]), "value-changed", G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
widget = gtk_button_new_with_mnemonic (_("_Clear input"));
data->BT_clear = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_widget_set_halign(widget, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
fchain = g_list_append(fchain, widget);
// propagate button
/*row++;
button = gtk_button_new_with_label(_("Propagate"));
gtk_grid_attach (GTK_GRID (group_grid), button, 1, 2, row, row+1);
*/
row++;
widget = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (data->CM_type[0]), _("is different per month"));
data->CM_type[1] = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
fchain = g_list_append(fchain, widget);
row++;
for(i=0;i<12;i++)
{
gint l, t;
l = ((i<6) ? 1 : 3);
t = row + ((i<6) ? i : i-6);
//#1826659 budget dialog month widget label decay by one
label = make_label_widget(_(CYA_ABMONTHS[i+1]));
data->label[i+1] = label;
gtk_grid_attach (GTK_GRID (group_grid), label, l, t, 1, 1);
widget = make_amount(label);
data->spinner[i+1] = widget;
fchain = g_list_append(fchain, widget);
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, l+1, t, 1, 1);
g_signal_connect (G_OBJECT (data->spinner[i+1]), "value-changed", G_CALLBACK (ui_bud_manage_cb_budget_changed), NULL);
//DB( g_print("\n[ui-budget] %s, col=%d, row=%d", CYA_ABMONTHS[i], col, row) );
}
gtk_container_set_focus_chain(GTK_CONTAINER(group_grid), fchain);
g_list_free(fchain);
// group :: 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(_("Options"));
data->label_options = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("_Force monitoring this category"));
data->CM_force = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_bud_manage_mapped), &dialog);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
// wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
ui_bud_manage_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/ui-txn-multi.c 0000664 0001750 0001750 00000055223 15021366314 016133 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-txn-multi.h"
#include "ui-account.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "gtk-dateentry.h"
#include "list-operation.h"
#include "ui-tag.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
void ui_multipleedit_dialog_prefill( GtkWidget *widget, Transaction *ope, gint column_id )
{
struct ui_multipleedit_dialog_data *data;
gchar *tagstr;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[ui-multipleedit] prefill\n") );
if(ope != NULL)
//if(col_id >= LST_DSPOPE_DATE && col_id != LST_DSPOPE_BALANCE)
{
switch( column_id )
{
case LST_DSPOPE_DATE:
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_date), (guint)ope->date);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_date), TRUE);
break;
case LST_DSPOPE_AMOUNT:
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME:
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), ope->amount);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_amount), TRUE);
break;
case LST_DSPOPE_PAYNUMBER:
kiv_combo_box_set_active(GTK_COMBO_BOX(data->NU_mode), ope->paymode);
gtk_entry_set_text(GTK_ENTRY(data->ST_number), (ope->number != NULL) ? ope->number : "");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_mode), TRUE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_number), TRUE);
break;
case LST_DSPOPE_PAYEE:
//ui_pay_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_pay), ope->kpay);
ui_pay_entry_popover_set_active(GTK_BOX(data->PO_pay), ope->kpay);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_pay), TRUE);
break;
case LST_DSPOPE_MEMO:
gtk_entry_set_text(GTK_ENTRY(data->ST_memo), (ope->memo != NULL) ? ope->memo : "");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_memo), TRUE);
break;
case LST_DSPOPE_CATEGORY:
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), ope->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), ope->kcat);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_cat), TRUE);
break;
case LST_DSPOPE_TAGS:
tagstr = tags_tostring(ope->tags);
gtk_entry_set_text(GTK_ENTRY(data->ST_tags), (tagstr != NULL) ? tagstr : "");
g_free(tagstr);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_tags), TRUE);
break;
}
}
}
static void ui_multipleedit_dialog_update( GtkWidget *widget, gpointer user_data )
{
struct ui_multipleedit_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[ui-multipleedit] update\n") );
if(data->PO_date)
gtk_widget_set_sensitive (data->PO_date, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_date)) );
if(data->ST_amount)
gtk_widget_set_sensitive (data->ST_amount , gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_amount )) );
if(data->NU_mode && data->ST_number)
{
gtk_widget_set_sensitive (data->NU_mode, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_mode)) );
gtk_widget_set_sensitive (data->ST_number, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_number)) );
}
if(data->PO_acc)
gtk_widget_set_sensitive (data->PO_acc , gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_acc )) );
if(data->PO_pay)
gtk_widget_set_sensitive (data->PO_pay , gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_pay )) );
if(data->PO_cat)
gtk_widget_set_sensitive (data->PO_cat , gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_cat )) );
if(data->ST_tags)
gtk_widget_set_sensitive (data->ST_tags, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_tags)) );
if(data->ST_memo)
gtk_widget_set_sensitive (data->ST_memo, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_memo)) );
if(data->PO_accto)
{
gboolean sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_xfer));
if(data->PO_acc && sensitive)
{
//g_signal_handlers_block_by_func(data->CM_acc, ui_multipleedit_dialog_update, NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_acc), FALSE);
//ui_acc_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_acc), 0);
ui_acc_entry_popover_set_active(GTK_BOX(data->PO_acc), 0);
//g_signal_handlers_unblock_by_func(data->CM_acc, ui_multipleedit_dialog_update, NULL);
}
gtk_widget_set_sensitive (data->LB_acc, !sensitive );
gtk_widget_set_sensitive (data->CM_acc, !sensitive );
gtk_widget_set_sensitive (data->PO_accto, sensitive );
}
}
static void ui_multipleedit_dialog_init( GtkWidget *widget, gpointer user_data )
{
struct ui_multipleedit_dialog_data *data;
GtkTreeModel *model;
GList *selection, *list;
guint32 kcur;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[ui-multipleedit] init\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->treeview));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview)), &model);
data->has_xfer = FALSE;
data->kacc = kcur = 0;
list = g_list_last(selection);
while(list != NULL)
{
Transaction *entry;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &entry, -1);
if((entry->flags & OF_INTXFER))
data->has_xfer = TRUE;
//#1812630 manage change to internal xfer
if( kcur == 0 ) //store first
{
kcur = entry->kcur;
data->kacc = entry->kacc;
}
else if( entry->kcur != kcur )
{
data->kacc = 0;
}
list = g_list_previous(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
DB( g_print(" has_xfer = %d\n", data->has_xfer) );
DB( g_print(" kacc = %d\n", data->kacc) );
}
gint ui_multipleedit_dialog_apply( GtkWidget *widget, gboolean *do_sort )
{
struct ui_multipleedit_dialog_data *data;
GtkTreeModel *model;
GList *selection, *list;
gboolean tmp_sort = FALSE;
guint changes;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[ui-multipleedit] apply\n") );
changes = GLOBALS->changes_count;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->treeview));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview)), &model);
list = g_list_last(selection);
while(list != NULL)
{
Transaction *txn;
GtkTreeIter iter;
const gchar *txt;
gboolean change = FALSE;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &txn, -1);
DB( g_print(" modifying %s %.2f\n", txn->memo, txn->amount) );
//TODO: this is always true
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_DATE) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_date)) )
{
guint32 olddate = txn->date;
txn->date = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_date));
DB( g_print(" -> date: '%d'\n", txn->date) );
//#1270687/1792808: sort if date changed
if(olddate != txn->date)
tmp_sort = TRUE;
change = TRUE;
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_AMOUNT) == TRUE
|| list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_EXPENSE) == TRUE
|| list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_INCOME) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_amount)) )
{
//#1874548 update the balances as well
account_balances_sub(txn);
txn->amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
account_balances_add(txn);
//5.4.2 income flag fix
da_transaction_set_flag(txn);
change = TRUE;
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_PAYNUMBER) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_mode)) )
{
txn->paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
change = TRUE;
}
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_number)) )
{
if(txn->number)
{
g_free(txn->number);
txn->number = NULL;
change = TRUE;
}
txt = gtk_entry_get_text(GTK_ENTRY(data->ST_number));
if (txt && *txt)
{
txn->number = g_strdup(txt);
change = TRUE;
}
}
}
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_acc)) )
{
//guint32 nkacc = ui_acc_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_acc));
guint32 nkacc = ui_acc_entry_popover_get_key(GTK_BOX(data->PO_acc));
if( transaction_acc_move(txn, txn->kacc, nkacc) )
{
GtkTreeIter iter;
DB( g_print(" -> acc: '%d'\n", nkacc) );
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
change = TRUE;
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_PAYEE) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_pay)) )
{
//txn->kpay = ui_pay_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_pay));
txn->kpay = ui_pay_entry_popover_get_key_add_new(GTK_BOX(data->PO_pay));
DB( g_print(" -> payee: '%d'\n", txn->kpay) );
change = TRUE;
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_CATEGORY) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_cat)) )
{
if(!(txn->flags & OF_SPLIT))
{
//txn->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat));
txn->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(data->PO_cat));
DB( g_print(" -> category: '%d'\n", txn->kcat) );
change = TRUE;
}
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_TAGS) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_tags)) )
{
//#2009277
if(txn->tags)
{
g_free(txn->tags);
txn->tags = NULL;
change = TRUE;
}
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_tags));
if (txt && *txt)
{
txn->tags = tags_parse(txt);
DB( g_print(" -> tags: '%s'\n", txt) );
change = TRUE;
}
}
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_MEMO) == TRUE )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_memo)) )
{
if(txn->memo)
{
g_free(txn->memo);
txn->memo = NULL;
change = TRUE;
}
txt = gtk_entry_get_text(GTK_ENTRY(data->ST_memo));
if (txt && *txt)
{
txn->memo = g_strdup(txt);
change = TRUE;
}
}
}
//TODO: not sure this ever happen, to check
if( data->has_xfer && (txn->flags & OF_INTXFER) )
{
Transaction *child;
child = transaction_xfer_child_strong_get(txn);
transaction_xfer_child_sync(txn, child);
}
//#1812630 manage change to internal xfer
if( data->has_xfer == FALSE && data->kacc > 0 )
{
if( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_xfer)) )
{
//guint32 dstkey = ui_acc_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_accto));
guint32 dstkey = ui_acc_entry_popover_get_key(GTK_BOX(data->PO_accto));
//5.9.1 forbid split to go xfer
if( (dstkey > 0) && !(txn->flags & (OF_SPLIT)) )
{
txn->kxferacc = dstkey;
transaction_xfer_search_or_add_child(GTK_WINDOW(data->dialog), FALSE, txn, dstkey);
//#2087750
change = TRUE;
}
}
}
//#2087750 moved
if( change == TRUE )
{
txn->dspflags |= FLAG_TMP_EDITED;
GLOBALS->changes_count++;
}
list = g_list_previous(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
if( do_sort != NULL )
*do_sort = tmp_sort;
return GLOBALS->changes_count - changes;
}
static gboolean ui_multipleedit_dialog_destroy( GtkWidget *widget, gpointer user_data )
{
struct ui_multipleedit_dialog_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[ui-multipleedit] destroy event occurred\n") );
g_free(data);
return FALSE;
}
GtkWidget *ui_multipleedit_dialog_new(GtkWindow *parent, GtkTreeView *treeview)
{
struct ui_multipleedit_dialog_data *data;
struct WinGeometry *wg;
GtkWidget *dialog, *content;
GtkWidget *group_grid, *label, *widget, *toggle, *hbox;
gint row;
DB( g_print ("\n[ui-multipleedit] new\n") );
data = g_malloc0(sizeof(struct ui_multipleedit_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (NULL,
GTK_WINDOW (parent),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
g_signal_connect (dialog, "destroy", G_CALLBACK (ui_multipleedit_dialog_destroy), (gpointer)data);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", dialog, data) );
data->dialog = dialog;
data->treeview = treeview;
ui_multipleedit_dialog_init(dialog, NULL);
gtk_window_set_title (GTK_WINDOW (data->dialog), _("Multiple edit transactions"));
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
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);
hb_widget_set_margin(GTK_WIDGET(group_grid), SPACING_LARGE);
gtk_box_prepend (GTK_BOX (content), group_grid);
row = -1;
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_DATE) == TRUE )
{
row++;
label = make_label_widget(_("_Date:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_date = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = gtk_date_entry_new(label);
data->PO_date = widget;
gtk_widget_set_halign (widget, GTK_ALIGN_START);
//gtk_widget_set_hexpand (widget, FALSE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
g_signal_connect (data->CM_date , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_AMOUNT) == TRUE
|| list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_EXPENSE) == TRUE
|| list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_INCOME) == TRUE )
{
row++;
label = make_label_widget(_("Amou_nt:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_amount = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = make_amount(label);
data->ST_amount = widget;
gtk_widget_set_halign (widget, GTK_ALIGN_START);
//gtk_widget_set_hexpand (widget, FALSE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
g_signal_connect (data->CM_amount , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
row++;
label = make_label_widget(_("A_ccount:"));
data->LB_acc = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_acc = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
//widget = ui_acc_comboboxentry_new(label);
widget = ui_acc_entry_popover_new(label);
data->PO_acc = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 4, 1);
g_signal_connect (data->CM_acc , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
//gtk_widget_set_margin_top(label, SPACING_MEDIUM);
//gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_PAYNUMBER) == TRUE )
{
row++;
label = make_label_widget(_("Pa_yment:"));
data->LB_mode = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
toggle = gtk_check_button_new();
data->CM_mode = toggle;
gtk_grid_attach (GTK_GRID (group_grid), toggle, 1, row, 1, 1);
widget = make_paymode (label);
data->NU_mode = widget;
//gtk_widget_set_hexpand (widget, TRUE);
gtk_widget_set_halign (widget, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
g_signal_connect (data->CM_mode , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
row++;
label = make_label_widget(_("_Number:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_number = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = make_string(label);
data->ST_number = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
g_signal_connect (data->CM_number , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_PAYEE) == TRUE )
{
row++;
label = make_label_widget(_("_Payee:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_pay = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
//widget = ui_pay_comboboxentry_new(label);
widget = ui_pay_entry_popover_new(label);
data->PO_pay = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 4, 1);
g_signal_connect (data->CM_pay , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_CATEGORY) == TRUE )
{
row++;
label = make_label_widget(_("Cate_gory:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_cat = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
data->PO_cat = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 4, 1);
g_signal_connect (data->CM_cat , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_MEMO) == TRUE )
{
row++;
label = make_label_widget(_("M_emo:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_memo = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
widget = make_memo_entry(label);
data->ST_memo = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 4, 1);
g_signal_connect (data->CM_memo , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_TAGS) == TRUE )
{
row++;
label = make_label_widget(_("_Tags:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_tags = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(hbox)), GTK_STYLE_CLASS_LINKED);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 4, 1);
widget = make_string(label);
data->ST_tags = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
widget = ui_tag_popover_list(data->ST_tags);
data->CY_tags = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
g_signal_connect (data->CM_tags , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
//#1812630 change txn to int xfer
// if selection do not contains xfer already and same currency for all
if( data->has_xfer == FALSE && data->kacc > 0 )
{
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_grid_attach (GTK_GRID (group_grid), widget, 0, row, 4, 1);
row++;
label = make_label_widget(_("Type as\ntransfer"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_xfer = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
//widget = ui_acc_comboboxentry_new(label);
widget = ui_acc_entry_popover_new(label);
data->PO_accto = widget;
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 4, 1);
//ui_acc_comboboxentry_populate_except(GTK_COMBO_BOX(data->PO_accto), GLOBALS->h_acc, data->kacc, ACC_LST_INSERT_NORMAL);
ui_acc_entry_popover_populate_except(GTK_BOX(data->PO_accto), GLOBALS->h_acc, data->kacc, ACC_LST_INSERT_NORMAL);
g_signal_connect (data->CM_xfer , "toggled", G_CALLBACK (ui_multipleedit_dialog_update), NULL);
}
ui_multipleedit_dialog_update(dialog, NULL);
//ui_acc_comboboxentry_populate(GTK_COMBO_BOX(data->PO_acc), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
ui_acc_entry_popover_populate(GTK_BOX(data->PO_acc), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
//5.5 done in popover
//ui_pay_comboboxentry_populate(GTK_COMBO_BOX(data->PO_pay), GLOBALS->h_pay);
//5.5 done in popover
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_cat), GLOBALS->h_cat);
//5.8
wg = &PREFS->txn_wg;
gtk_window_set_default_size(GTK_WINDOW(dialog), wg->w, -1);
gtk_widget_show_all (dialog);
if(data->has_xfer == TRUE)
{
hb_widget_visible (data->LB_acc, FALSE);
hb_widget_visible (data->CM_acc, FALSE);
hb_widget_visible (data->PO_acc, FALSE);
}
if( list_txn_column_id_isvisible(GTK_TREE_VIEW(data->treeview), LST_DSPOPE_PAYNUMBER) == TRUE )
{
if(data->has_xfer == TRUE)
{
hb_widget_visible (data->LB_mode, FALSE);
hb_widget_visible (data->CM_mode, FALSE);
hb_widget_visible (data->NU_mode, FALSE);
}
}
return dialog;
}
homebank-5.9.7/src/hb-category.h 0000644 0001750 0001750 00000005633 15120541642 015765 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_CATEGORY_H__
#define __HB_CATEGORY_H__
#include "hb-types.h"
struct _category
{
guint32 key;
guint32 parent;
gushort flags;
gushort _pad1;
gchar *name;
gdouble budget[13]; //0:is same value, 1 ..12 are months
/* unsaved datas */
gchar *fullname;
gchar *typename;
//gboolean flt_select;
guint16 nb_use_txn;
guint16 nb_use_txncat; //cat usage only
guint16 nb_use_all;
guint16 nb_use_allcat; //cat usage only
};
#define GF_SUB (1<<0) //? useful as we can check item->parent= 0
#define GF_INCOME (1<<1)
#define GF_CUSTOM (1<<2)
#define GF_BUDGET (1<<3)
#define GF_FORCED (1<<4)
#define GF_MIXED (1<<5) //#1740368 if cat has mixed exp/inc type
#define GF_HIDDEN (1<<8) //hidden by user
Category *da_cat_clone(Category *src_item);
void da_cat_free(Category *item);
Category *da_cat_malloc(void);
void da_cat_destroy(void);
void da_cat_new(void);
guint da_cat_length(void);
guint32 da_cat_get_max_key(void);
guint32 da_cat_delete(guint32 key);
gboolean da_cat_insert(Category *acc);
gboolean da_cat_append(Category *cat);
Category *da_cat_append_ifnew_by_fullname(gchar *rawfullname);
Category *da_cat_get(guint32 key);
gchar *da_cat_get_name(Category *item);
gchar *da_cat_get_fullname(Category *item);
Category *da_cat_get_by_fullname(gchar *rawfullname);
void da_cat_consistency(Category *item);
void da_cat_anonymize(Category *item);
GList *category_glist_sorted(gint column);
gboolean category_key_budget_active(guint32 key);
gboolean category_key_unbudgeted(guint32 key);
guint32 category_report_id(guint32 key, gboolean subcat);
gint category_delete_unused(void);
void category_fill_usage(void);
void category_move(guint32 srckey, guint32 newkey, gboolean dosubcat);
gboolean category_rename(Category *item, const gchar *newname);
gchar *category_find_preset(gchar **lang);
gint category_type_get(Category *item);
gint category_root_type_get(guint32 key);
gchar category_get_type_char(Category *item);
gint category_change_type(Category *item, gboolean isIncome, gboolean doChild);
gboolean category_load_csv(gchar *filename, gchar **error);
gboolean category_save_csv(gchar *filename, gchar **error);
#endif
homebank-5.9.7/src/hb-currency.c 0000644 0001750 0001750 00000073232 14736461407 016011 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-currency.h"
#include "hb-pref-data.h"
#include
#ifdef G_OS_WIN32
#include
#endif
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern Currency4217 iso4217cur[];
extern guint n_iso4217cur;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void
da_cur_free(Currency *item)
{
DB( g_print("\n[da_cur] free\n") );
if(item != NULL)
{
DB( g_print(" %d, %s\n", item->key, item->iso_code) );
g_free(item->name);
g_free(item->iso_code);
g_free(item->symbol);
g_free(item->decimal_char);
g_free(item->grouping_char);
g_free(item);
}
}
Currency *
da_cur_malloc(void)
{
DB( g_print("\n[da_cur] malloc\n") );
return g_malloc0(sizeof(Currency));
}
void
da_cur_destroy(void)
{
DB( g_print("\n[da_cur] destroy\n") );
g_hash_table_destroy(GLOBALS->h_cur);
}
void
da_cur_new(void)
{
Currency4217 *curfmt;
DB( g_print("\n[da_cur] new\n") );
GLOBALS->h_cur = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_cur_free);
// insert default base currency
curfmt = iso4217format_get(PREFS->IntCurrSymbol);
if(curfmt == NULL)
curfmt = iso4217format_get("USD");
if(curfmt)
{
DB( g_printf(" curfmt %p\n", curfmt) );
currency_add_from_user(curfmt);
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void da_cur_max_key_ghfunc(gpointer key, Currency *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
static gboolean da_cur_name_grfunc(gpointer key, Currency *item, gchar *name)
{
if( name && item->name )
{
if(!strcasecmp(name, item->name))
return TRUE;
}
return FALSE;
}
static gboolean da_cur_iso_grfunc(gpointer key, Currency *item, gchar *iso)
{
if( iso && item->iso_code )
{
if(!strcasecmp(iso, item->iso_code))
return TRUE;
}
return FALSE;
}
guint
da_cur_length(void)
{
return g_hash_table_size(GLOBALS->h_cur);
}
gboolean
da_cur_delete(guint32 key)
{
DB( g_print("\n[da_cur] delete %d\n", key) );
return g_hash_table_remove(GLOBALS->h_cur, &key);
}
void
da_cur_init_from4217(Currency *cur, Currency4217 *curfmt)
{
cur->symbol = g_strdup(curfmt->curr_symbol);
cur->sym_prefix = curfmt->curr_is_prefix;
cur->decimal_char = g_strdup(curfmt->curr_dec_char);
cur->grouping_char = g_strdup(curfmt->curr_grp_char);
cur->frac_digits = curfmt->curr_frac_digit;
da_cur_initformat(cur);
}
void
da_cur_initformat(Currency *item)
{
DB( g_print("\n[da_cur] init format\n") );
DB( g_print(" k:%d iso:'%s' symbol:'%s' digit:%d\n", item->key, item->iso_code, item->symbol, item->frac_digits) );
// for formatd
g_snprintf(item->format , 8-1, "%%.%df", item->frac_digits);
if(item->sym_prefix == TRUE)
g_snprintf(item->monfmt , 32-1, "%s %%s", item->symbol);
else
g_snprintf(item->monfmt , 32-1, "%%s %s", item->symbol);
DB( g_print(" numfmt '%s'\n", item->format) );
DB( g_print(" monfmt '%s'\n", item->monfmt) );
}
gboolean
da_cur_insert(Currency *item)
{
guint32 *new_key;
DB( g_print("\n[da_cur] insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
g_hash_table_insert(GLOBALS->h_cur, new_key, item);
da_cur_initformat(item);
return TRUE;
}
gboolean
da_cur_append(Currency *item)
{
Currency *existitem;
guint32 *new_key;
DB( g_print("\n[da_cur] append\n") );
/* ensure no duplicate */
existitem = da_cur_get_by_name( item->name );
if( existitem == NULL )
{
new_key = g_new0(guint32, 1);
*new_key = da_cur_get_max_key() + 1;
item->key = *new_key;
//item->pos = da_cur_length() + 1;
DB( g_print(" insert id: %d\n", *new_key) );
g_hash_table_insert(GLOBALS->h_cur, new_key, item);
da_cur_initformat(item);
return TRUE;
}
DB( g_print(" %s already exist: %d\n", item->iso_code, item->key) );
return FALSE;
}
guint32
da_cur_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_cur, (GHFunc)da_cur_max_key_ghfunc, &max_key);
return max_key;
}
Currency *
da_cur_get_by_name(gchar *name)
{
DB( g_print("\n[da_cur] get_by_name\n") );
return g_hash_table_find(GLOBALS->h_cur, (GHRFunc)da_cur_name_grfunc, name);
}
Currency *
da_cur_get_by_iso_code(gchar *iso_code)
{
DB( g_print("\n[da_cur] get_by_iso_code\n") );
return g_hash_table_find(GLOBALS->h_cur, (GHRFunc)da_cur_iso_grfunc, iso_code);
}
Currency *
da_cur_get(guint32 key)
{
//only use when needed
//DB( g_print("\n[da_cur] get\n") );
return g_hash_table_lookup(GLOBALS->h_cur, &key);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gboolean
currency_is_euro(guint32 key)
{
Currency *item;
gboolean retval = FALSE;
item = da_cur_get(key);
if( item && item->iso_code )
{
if(!strcasecmp("EUR", item->iso_code))
retval = TRUE;
}
return retval;
}
/**
* currency_is_used:
*
* controls if a currency is used [base or account]
*
* Return value: TRUE if used, FALSE, otherwise
*/
gboolean
currency_is_used(guint32 key)
{
GList *list;
gboolean retval;
DB( g_printf("\n[currency] is used\n") );
if(GLOBALS->kcur == key)
return TRUE;
retval = FALSE;
list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *item = list->data;
if(item->kcur == key)
{
retval = TRUE;
goto end;
}
list = g_list_next(list);
}
end:
g_list_free(list);
return retval;
}
Currency4217 *iso4217format_get(gchar *code)
{
Currency4217 *cur;
guint i;
DB( g_printf("\n[currency] iso4217 format get\n") );
for (i = 0; i< n_iso4217cur; i++)
{
cur = &iso4217cur[i];
if(g_strcmp0(cur->curr_iso_code, code) == 0)
{
return cur;
}
}
return NULL;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd464799%28v=vs.85%29.aspx
//https://github.com/Samsung/icu/blob/master/source/i18n/winnmfmt.cpp
#ifdef G_OS_WIN32
#define NEW_ARRAY(type,count) (type *) g_malloc((count) * sizeof(type))
#define DELETE_ARRAY(array) g_free((void *) (array))
static void win_getCurrencyFormat(CURRENCYFMTW *fmt, const wchar_t *windowsLocaleName)
{
//wchar_t buf[10];
DB( g_printf("\n[currency] win get system format\n") );
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
//GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
//GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONGROUPING, (LPWSTR)buf, sizeof(buf));
//fmt->Grouping = getGrouping(buf);
fmt->lpDecimalSep = NEW_ARRAY(wchar_t, 6);
GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6);
fmt->lpThousandSep = NEW_ARRAY(wchar_t, 6);
GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6);
//GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT));
fmt->lpCurrencySymbol = NEW_ARRAY(wchar_t, 8);
GetLocaleInfoEx(windowsLocaleName, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8);
}
static void win_freeCurrencyFormat(CURRENCYFMTW *fmt)
{
DB( g_printf("\n[currency] win free system format\n") );
if (fmt != NULL) {
DELETE_ARRAY(fmt->lpCurrencySymbol);
DELETE_ARRAY(fmt->lpThousandSep);
DELETE_ARRAY(fmt->lpDecimalSep);
}
}
static void win_currency_get_system_iso(void)
{
LPWSTR lpIntlSymbol;
DB( g_printf("\n[currency] get system ISO\n") );
lpIntlSymbol = NEW_ARRAY(wchar_t, 9);
GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SINTLSYMBOL, lpIntlSymbol, 9);
DB( wprintf(L"raw lpIntlSymbol: '%hs'\n", lpIntlSymbol) );
gchar *tmpstr = g_utf16_to_utf8(lpIntlSymbol, -1, NULL, NULL, NULL);
g_stpcpy (PREFS->IntCurrSymbol, tmpstr);
g_free(tmpstr);
DELETE_ARRAY(lpIntlSymbol);
}
static void win_currency_format_system_fill(Currency *item)
{
CURRENCYFMTW currency;
DB( g_printf("\n[currency] windows format system fill '%s'\n", item->iso_code) );
win_getCurrencyFormat(¤cy, LOCALE_NAME_USER_DEFAULT);
#if MYDEBUG == 1
printf("raw ptr = %p\n", ¤cy);
printf("raw NumDigits: '%d'\n", currency.NumDigits);
//note this will not print well on windows console which is not unicode compliant
wprintf(L"raw lpDecimalSep: '%hs'\n", currency.lpDecimalSep);
wprintf(L"raw lpThousandSep: '%hs'\n", currency.lpThousandSep);
wprintf(L"raw lpCurrencySymbol: '%hs'\n", currency.lpCurrencySymbol);
#endif
item->symbol = g_utf16_to_utf8(currency.lpCurrencySymbol, -1, NULL, NULL, NULL);
item->sym_prefix = (currency.PositiveOrder & 1) ? FALSE : TRUE;
item->decimal_char = g_utf16_to_utf8(currency.lpDecimalSep, -1, NULL, NULL, NULL);
item->grouping_char = g_utf16_to_utf8(currency.lpThousandSep, -1, NULL, NULL, NULL);
item->frac_digits = currency.NumDigits;
win_freeCurrencyFormat(¤cy);
}
#endif
#ifdef G_OS_UNIX
static void unix_currency_get_system_iso(void)
{
struct lconv *lc = localeconv();
DB( g_printf("\n[currency] get system ISO\n") );
g_stpcpy (PREFS->IntCurrSymbol, lc->int_curr_symbol);
}
static void unix_currency_format_system_fill(Currency *item)
{
struct lconv *lc = localeconv();
DB( g_printf("\n[currency] linux format system fill '%s'\n", item->iso_code) );
#if MYDEBUG == 1
g_print("raw ptr = %p\n", lc);
g_print("raw int_curr_symbol = '%s'\n", lc->int_curr_symbol);
g_print("raw ?_cs_precedes = '%d' '%d'\n", lc->p_cs_precedes, lc->n_cs_precedes);
g_print("raw mon_decimal_point = '%s'\n", lc->mon_decimal_point);
g_print("raw mon_thousands_sep = '%s'\n", lc->mon_thousands_sep);
g_print("raw frac_digits = '%d'\n", lc->frac_digits);
#endif
item->symbol = g_strdup(lc->currency_symbol);
item->sym_prefix = ( lc->p_cs_precedes || lc->n_cs_precedes ) ? TRUE : FALSE;
item->decimal_char = g_strdup(lc->mon_decimal_point);
item->grouping_char = g_strdup(lc->mon_thousands_sep);
item->frac_digits = lc->frac_digits;
//fix 378992/421228
if( item->frac_digits > MAX_FRAC_DIGIT )
{
item->frac_digits = 2;
g_free(item->decimal_char);
item->decimal_char = g_strdup(".");
}
}
#endif
void currency_get_system_iso(void)
{
DB( g_print("\n[currency] get system ISO code\n") );
#ifdef G_OS_UNIX
unix_currency_get_system_iso();
#else
#ifdef G_OS_WIN32
win_currency_get_system_iso();
#else
g_stpcpy (PREFS->IntCurrSymbol, "USD");
#endif
#endif
g_strstrip(PREFS->IntCurrSymbol);
DB( g_print("- stored '%s' as default\n", PREFS->IntCurrSymbol) );
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
Currency *currency_add_from_user(Currency4217 *curfmt)
{
Currency *item;
DB( g_printf("\n[currency] found adding %s\n", curfmt->curr_iso_code) );
item = da_cur_malloc();
//no mem alloc here
//item->key = i;
if(curfmt != NULL)
{
item->name = g_strdup(curfmt->name);
//item->country = cur.country_name;
item->iso_code = g_strdup(curfmt->curr_iso_code);
//1634615 if the currency match the system, fill with it
if(!strcmp(item->iso_code, PREFS->IntCurrSymbol))
{
#ifdef G_OS_UNIX
unix_currency_format_system_fill(item);
#else
#ifdef G_OS_WIN32
win_currency_format_system_fill(item);
#endif
#endif
}
else
{
//init from our internal iso 4217 preset
item->frac_digits = curfmt->curr_frac_digit;
item->symbol = g_strdup(curfmt->curr_symbol);
item->sym_prefix = curfmt->curr_is_prefix;
item->decimal_char = g_strdup(curfmt->curr_dec_char);
item->grouping_char = g_strdup(curfmt->curr_grp_char);
}
}
else
{
item->name = g_strdup("unknown");
//item->country = cur.country_name;
item->iso_code = g_strdup("XXX");
item->frac_digits = 2;
item->sym_prefix = FALSE;
}
//#1862540 if symbol/decimal/grouping char are null = crash on hb_str_formatd()
if( !item->symbol )
item->symbol = g_strdup("XXX");
if( !item->decimal_char )
item->decimal_char = g_strdup(".");
if( !item->grouping_char )
item->grouping_char = g_strdup("");
#if MYDEBUG == 1
g_printf(" symbol ='%s'\n", item->symbol);
g_printf(" sym_prefix ='%d'\n", item->sym_prefix);
g_printf(" decimal_char ='%s'\n", item->decimal_char);
g_printf(" grouping_char ='%s'\n", item->grouping_char);
g_printf(" frac_digits ='%d'\n", item->frac_digits);
#endif
da_cur_append(item);
return item;
}
static gboolean currency_rate_update(gchar *isocode, gdouble rate, guint32 date)
{
gboolean retval = FALSE;
Currency *cur;
DB( g_print("\n[currency] rate update\n") );
cur = da_cur_get_by_iso_code (isocode);
if(cur)
{
DB( g_print(" found cur='%s'\n", cur->iso_code) );
//#2002650 test if there is a change
if( rate != cur->rate )
{
GLOBALS->changes_count++;
retval = TRUE;
}
#if MYDEBUG == 1
else
{
DB( g_print(" rate was identical\n") );
}
#endif
cur->rate = rate;
cur->mdate = date;
}
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* currency API
* discontinued see #1730527, #1785210
*/
/* real open source fixer API */
/* DNS should be: https://frankfurter.app
* see https://github.com/fixerAPI/fixer/issues/107
*/
/*
** 5.7
** https://api.exchangerate.host/latest?base=EUR&symbols=USD,CHF,AUD,CAD,JPY,CNY,GBP
** http://data.fixer.io/api/latest?access_key=xxxx&base=EUR&symbols=USD,CHF,AUD,CAD,JPY,CNY,GBP
*/
/* old
** api.fixer.io deprecated since 30/04/2019
** QS: https://api.fixer.io/latest?base=EUR&symbols=USD,CHF,AUD,CAD,JPY,CNY,GBP
**
** test API
** gchar fixeriojson[] =
** "{ }";
** " { \r \"base\" : \"EUR\", \
** \"date\": \n\r \"2017-12-04\", \
** \"rates\" \n\n :{\"AUD\":1.5585,\"CAD\":1.5034,\"CHF\":1.1665,\"CNY\":7.8532,\"GBP\":0.87725,\"JPY\":133.91,\"USD\":1.1865 \
** } }";
*/
static gboolean api_fixerio_parse(GBytes *body, GError **error)
{
gchar *rawjson;
gchar *p;
gchar strbuf[48];
gchar isocode[8];
gdouble rate;
guint32 date = GLOBALS->today;
guint count = 0;
DB( g_print("\n[currency] api fixerio parse\n") );
if(body)
{
//there is no need of a complex JSON parser here, so let's hack !
rawjson = g_strdup(g_bytes_get_data(body, NULL));
hb_string_inline(rawjson);
DB( g_printf(" body: '%s'\n", rawjson ) );
//get date
p = g_strstr_len(rawjson, -1, "\"date\"");
if(p)
{
strncpy(strbuf, p+8, 10);
strbuf[10]='\0';
date = hb_date_get_julian(strbuf, PRF_DATEFMT_YMD);
DB( g_printf(" date: %.10s %d\n", strbuf, date) );
}
//get rates
p = g_strstr_len(rawjson, -1, "\"rates\"");
if(p)
{
p = p+8;
do
{
p = hb_string_copy_jsonpair(strbuf, p);
strncpy(isocode, strbuf, 3);
isocode[3]='\0';
rate = g_ascii_strtod(strbuf+4, NULL);
DB( g_printf(" pair: '%s' > '%s' %f\n", strbuf, isocode, rate ) );
if( currency_rate_update(isocode, rate, date) )
count++;
}
while( p != NULL );
}
g_free(rawjson);
}
return( (count > 0) ? TRUE : FALSE);
}
static gchar *api_fixerio_query_build(void)
{
GList *list;
GString *node;
Currency *base;
Currency *item;
gint i;
DB( g_print("\n[currency] api fixerio query build\n") );
base = da_cur_get (GLOBALS->kcur);
node = g_string_sized_new(512);
//g_string_append_printf(node, "https://api.frankfurter.app/latest?base=%s&symbols=", base->iso_code);
//2017410 + 5.7 base url from prefs
//g_string_append_printf(node, "%s/latest?", PREFS->api_rate_url);
//5.7.2 get full hostname from prefs
g_string_append_printf(node, "%s?", PREFS->api_rate_url);
//2017410 + 5.7 key from prefs
if( PREFS->api_rate_key != NULL && strlen(PREFS->api_rate_key) > 8 )
g_string_append_printf(node, "access_key=%s&", PREFS->api_rate_key);
//base currency
g_string_append_printf(node, "base=%s&symbols=", base->iso_code);
list = g_hash_table_get_values(GLOBALS->h_cur);
i = g_list_length (list);
while (list != NULL)
{
item = list->data;
if( (item->key != GLOBALS->kcur) && (strlen(item->iso_code) == 3) )
{
g_string_append_printf(node, "%s", item->iso_code);
if(i > 1)
{
g_string_append(node, ",");
}
}
i--;
list = g_list_next(list);
}
g_list_free(list);
return g_string_free(node, FALSE);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gboolean currency_online_sync(GError **error, GString *node)
{
SoupSession *session;
SoupMessage *msg;
GBytes *body;
gchar *query;
guint status;
gboolean retval = TRUE;
DB( g_printf("\n[currency] sync online\n") );
query = api_fixerio_query_build();
DB( g_printf(" query: '%s'\n", query) );
/*
//test API
retval = api_fixerio_parse(fixeriojson, error);
*/
if( node != NULL )
{
g_string_append(node, query);
g_string_append(node, "\n");
}
session = soup_session_new ();
msg = soup_message_new ("GET", query);
if(msg != NULL)
{
//soup_session_send_message (session, msg);
//DB( g_print("status_code: %d %d\n", msg->status_code, SOUP_STATUS_IS_SUCCESSFUL(msg->status_code) ) );
body = soup_session_send_and_read (session, msg, NULL, error);
status = soup_message_get_status (msg);
DB( g_print(" status_code: %d %d\n", status, SOUP_STATUS_IS_SUCCESSFUL(status) ) );
DB( g_print(" reason: '%s'\n", soup_message_get_reason_phrase(msg)) );
DB( g_print(" datas: '%s'\n", (gchar *)g_bytes_get_data(body, NULL)) );
if( node != NULL )
{
g_string_append_printf(node, "status_code: %d\n", status);
g_string_append_printf(node, "reason: '%s'\n", soup_message_get_reason_phrase(msg));
g_string_append_printf(node, "datas: '%s'\n", (gchar *)g_bytes_get_data(body, NULL));
}
//if( SOUP_STATUS_IS_SUCCESSFUL(msg->status_code) == TRUE )
if( SOUP_STATUS_IS_SUCCESSFUL(status) == TRUE )
{
//#1750426 ignore the retval here (false when no rate was found, as we don't care)
//api_fixerio_parse(msg->response_body->data, error);
api_fixerio_parse(body, error);
//#2066110 update euro minor rate
if(PREFS->euro_active == TRUE)
{
euro_country_notmceii_rate_update(PREFS->euro_country);
}
}
else
{
//*error = g_error_new_literal(1, msg->status_code, msg->reason_phrase);
*error = g_error_new_literal(1, status, soup_message_get_reason_phrase(msg) );
retval = FALSE;
}
g_bytes_unref (body);
g_object_unref (msg);
}
else
{
*error = g_error_new_literal(1, 0, "cannot parse URI");
retval = FALSE;
}
g_free(query);
soup_session_abort (session);
g_object_unref(session);
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
//struct iso_4217_currency iso_4217_currencies[];
/*debug testing
static void fill_currency(void)
{
gint i;
struct iso_4217_currency cur;
Currency *item;
for (i = 0; i< 500; i++)
{
cur = iso_4217_currencies[i];
if(cur.iso_code == NULL)
break;
item = da_cur_malloc();
//no mem alloc here
item->key = i;
item->name = cur.currency;
item->country = cur.country;
item->iso_code = cur.iso_code;
da_cur_insert(item);
}
}*/
//https://en.wikipedia.org/wiki/ISO_4217
Currency4217 iso4217cur[] =
{
{ "AED", 2, ".", ",", TRUE, "د.إ.", "UAE Dirham" },
{ "AFN", 2, ",", ",", TRUE, "؋", "Afghani" },
{ "ALL", 2, ",", " ", FALSE, "Lekë", "Lek" },
{ "AMD", 2, ".", ",", FALSE, "֏", "Armenian Dram" },
{ "ANG", 2, ",", ",", TRUE, "NAf.", "Netherlands Antillian Guilder" },
{ "AOA", 2, ",", " ", FALSE, "Kz", "Kwanza" },
{ "ARS", 2, ",", ".", TRUE, "$", "Argentine Peso" },
{ "AUD", 2, ".", ",", TRUE, "$", "Australian Dollar" },
{ "AWG", 2, ",", ".", TRUE, "Afl.", "Aruban Guilder" },
{ "AZN", 2, ",", " ", TRUE, "₼", "Azerbaijanian Manat" },
{ "BAM", 2, ",", ".", FALSE, "KM", "Convertible Marks" },
{ "BBD", 2, ".", ",", TRUE, "$", "Barbados Dollar" },
{ "BDT", 2, ".", ",", TRUE, "৳", "Taka" },
{ "BGN", 2, ",", " ", FALSE, "лв.", "Bulgarian Lev" },
{ "BHD", 3, ".", ",", TRUE, "د.ب.", "Bahraini Dinar" },
{ "BIF", 0, ",", " ", FALSE, "FBu", "Burundi Franc" },
{ "BMD", 2, ".", ",", TRUE, "$", "Bermudian Dollar" },
{ "BND", 2, ",", ".", TRUE, "$", "Brunei Dollar" },
{ "BOB", 2, ",", ".", TRUE, "Bs", "Boliviano" },
{ "BOV", 2, ".", "", FALSE, "BOV", "Mvdol" },
{ "BRL", 2, ",", ".", TRUE, "R$", "Brazilian Real" },
{ "BSD", 2, ".", ",", TRUE, "$", "Bahamian Dollar" },
{ "BTN", 2, ".", ",", TRUE, "Nu.", "Ngultrum" },
{ "BWP", 2, ".", " ", TRUE, "P", "Pula" },
{ "BYN", 0, ",", " ", FALSE, "Br", "Belarussian Ruble" },
{ "BYR", 0, ",", " ", FALSE, "Br", "Old Belarussian Ruble" },
{ "BZD", 2, ".", ",", TRUE, "$", "Belize Dollar" },
{ "CAD", 2, ",", " ", TRUE, "$", "Canadian Dollar" },
{ "CDF", 2, ",", " ", TRUE, "FC", "Congolese Franc" },
{ "CHE", 2, ".", "", FALSE, "CHE", "WIR Euro" },
{ "CHF", 2, ",", "'", TRUE, "CHF", "Swiss Franc" },
{ "CHW", 2, ".", "", FALSE, "CHW", "WIR Franc" },
{ "CLF", 4, ".", "", FALSE, "CLF", "Unidades de fomento" },
{ "CLP", 0, ",", ".", TRUE, "$", "Chilean Peso" },
{ "CNY", 2, ".", ",", TRUE, "¥", "Yuan Renminbi" },
{ "COP", 2, ",", ".", TRUE, "$", "Colombian Peso" },
{ "COU", 2, ".", "", FALSE, "COU", "Unidad de Valor Real" },
{ "CRC", 0, ",", ".", TRUE, "₡", "Costa Rican Colon" },
{ "CUP", 2, ".", ",", TRUE, "$", "Cuban Peso" },
{ "CVE", 2, "$", " ", FALSE, "", "Cape Verde Escudo" },
{ "CYP", 2, ".", "", FALSE, "CYP", "Cyprus Pound" },
{ "CZK", 2, ",", " ", FALSE, "Kč", "Czech Koruna" },
{ "DJF", 0, ",", " ", TRUE, "Fdj", "Djibouti Franc" },
{ "DKK", 2, ",", ".", TRUE, "kr", "Danish Krone" },
{ "DOP", 2, ".", ",", TRUE, "$", "Dominican Peso" },
{ "DZD", 2, ",", " ", FALSE, "DA", "Algerian Dinar" },
{ "EEK", 2, ".", "", FALSE, "EEK", "Kroon" },
{ "EGP", 2, ".", ",", TRUE, "ج.م.", "Egyptian Pound" },
{ "ERN", 2, ".", ",", TRUE, "Nfk", "Nakfa" },
{ "ETB", 2, ".", ",", TRUE, "Br", "Ethiopian Birr" },
{ "EUR", 2, ",", " ", TRUE, "€", "Euro" },
{ "FJD", 2, ".", ",", TRUE, "$", "Fiji Dollar" },
{ "FKP", 2, ".", ",", TRUE, "£", "Falkland Islands Pound" },
{ "GBP", 2, ".", ",", TRUE, "£", "Pound Sterling" },
{ "GEL", 2, ",", " ", TRUE, "₾", "Lari" },
{ "GHS", 2, ".", ",", TRUE, "GH₵", "Ghana Cedi" },
{ "GIP", 2, ".", ",", TRUE, "£", "Gibraltar Pound" },
{ "GMD", 2, ".", ",", TRUE, "D", "Dalasi" },
{ "GNF", 0, ",", " ", TRUE, "FG", "Guinea Franc" },
{ "GTQ", 2, ".", ",", TRUE, "Q", "Quetzal" },
{ "GYD", 2, ".", ",", TRUE, "$", "Guyana Dollar" },
{ "HKD", 2, ".", ",", TRUE, "$", "Hong Kong Dollar" },
{ "HNL", 2, ".", ",", TRUE, "L", "Lempira" },
{ "HRK", 2, ",", ".", FALSE, "kn", "Croatian Kuna" },
{ "HTG", 2, ",", " ", FALSE, "G", "Gourde" },
{ "HUF", 2, ",", " ", FALSE, "HUF", "Forint" },
{ "IDR", 2, ",", ".", TRUE, "Rp", "Rupiah" },
{ "ILS", 2, ".", ",", TRUE, "₪", "New Israeli Sheqel" },
{ "INR", 2, ".", ",", TRUE, "₹", "Indian Rupee" },
{ "IQD", 3, ".", ",", TRUE, "د.ع.", "Iraqi Dinar" },
{ "IRR", 2, "/", ",", TRUE, "ريال", "Iranian Rial" },
{ "ISK", 0, ",", ".", FALSE, "ISK", "Iceland Krona" },
{ "JMD", 2, ".", ",", TRUE, "$", "Jamaican Dollar" },
{ "JOD", 3, ".", ",", TRUE, "د.ا.", "Jordanian Dinar" },
{ "JPY", 0, ".", ",", TRUE, "¥", "Yen" },
{ "KES", 2, ".", ",", TRUE, "Ksh", "Kenyan Shilling" },
{ "KGS", 2, ",", " ", FALSE, "сом", "Som" },
{ "KHR", 2, ".", ",", FALSE, "៛", "Riel" },
{ "KMF", 0, ",", " ", TRUE, "CF", "Comoro Franc" },
{ "KPW", 2, ".", "", FALSE, "KPW", "North Korean Won" },
{ "KRW", 0, ".", ",", TRUE, "₩", "Won" },
{ "KWD", 3, ".", ",", TRUE, "د.ك.", "Kuwaiti Dinar" },
{ "KYD", 2, ".", ",", TRUE, "$", "Cayman Islands Dollar" },
{ "KZT", 2, ",", " ", FALSE, "₸", "Tenge" },
{ "LAK", 2, ",", ".", TRUE, "₭", "Kip" },
{ "LBP", 2, ".", ",", TRUE, "ل.ل.", "Lebanese Pound" },
{ "LKR", 2, ".", ",", TRUE, "Rs.", "Sri Lanka Rupee" },
{ "LRD", 2, ".", ",", TRUE, "$", "Liberian Dollar" },
{ "LSL", 2, ".", "", FALSE, "LSL", "Loti" },
{ "LTL", 2, ".", "", FALSE, "LTL", "Lithuanian Litas" },
{ "LVL", 2, ".", "", FALSE, "LVL", "Latvian Lats" },
{ "LYD", 3, ".", ",", TRUE, "د.ل.", "Libyan Dinar" },
{ "MAD", 2, ",", " ", FALSE, "DH", "Moroccan Dirham" },
{ "MDL", 2, ",", " ", FALSE, "L", "Moldovan Leu" },
{ "MGA", 2, ",", " ", TRUE, "Ar", "Malagasy Ariary" },
{ "MKD", 2, ",", " ", TRUE, "den", "Denar" },
{ "MMK", 2, ".", ",", TRUE, "K", "Kyat" },
{ "MNT", 2, ".", ",", TRUE, "₮", "Tugrik" },
{ "MOP", 2, ",", " ", TRUE, "MOP", "Pataca" },
{ "MRO", 0, ",", " ", TRUE, "UM", "Ouguiya" },
{ "MTL", 2, ".", "", FALSE, "MTL", "Maltese Lira" },
{ "MUR", 2, ",", " ", TRUE, "Rs", "Mauritius Rupee" },
{ "MVR", 2, ".", ",", FALSE, "ރ.", "Rufiyaa" },
{ "MWK", 2, ".", ",", TRUE, "MK", "Kwacha" },
{ "MXN", 2, ".", ",", TRUE, "$", "Mexican Peso" },
{ "MXV", 2, ".", "", FALSE, "MXV", "Mexican Unidad de Inversion (UDI)" },
{ "MYR", 2, ".", ",", TRUE, "RM", "Malaysian Ringgit" },
{ "MZN", 2, ",", " ", FALSE, "MTn", "Metical" },
{ "NAD", 2, ",", " ", TRUE, "$", "Namibia Dollar" },
{ "NGN", 2, ".", ",", TRUE, "₦", "Naira" },
{ "NIO", 2, ".", ",", TRUE, "C$", "Cordoba Oro" },
{ "NOK", 2, ",", " ", TRUE, "kr", "Norwegian Krone" },
{ "NPR", 2, ".", ",", TRUE, "रु", "Nepalese Rupee" },
{ "NZD", 2, ".", ",", TRUE, "$", "New Zealand Dollar" },
{ "OMR", 3, ".", ",", TRUE, "ر.ع.", "Rial Omani" },
{ "PAB", 2, ".", ",", TRUE, "B/.", "Balboa" },
{ "PEN", 2, ".", ",", TRUE, "S/.", "Nuevo Sol" },
{ "PGK", 2, ".", ",", TRUE, "K", "Kina" },
{ "PHP", 2, ",", ",", TRUE, "₱", "Philippine Peso" },
{ "PKR", 2, ".", ",", TRUE, "Rs", "Pakistan Rupee" },
{ "PLN", 2, ",", " ", FALSE, "zł", "Zloty" },
{ "PYG", 0, ",", ".", TRUE, "₲", "Guarani" },
{ "QAR", 2, ".", ",", TRUE, "ر.ق.", "Qatari Rial" },
{ "RON", 2, ",", ".", FALSE, "RON", "New Leu" },
{ "RSD", 2, ",", ".", FALSE, "RSD", "Serbian Dinar" },
{ "RUB", 2, ",", " ", TRUE, "₽", "Russian Ruble" },
{ "RWF", 0, ",", " ", TRUE, "RF", "Rwanda Franc" },
{ "SAR", 2, ".", ",", TRUE, "ر.س.", "Saudi Riyal" },
{ "SBD", 2, ".", ",", TRUE, "$", "Solomon Islands Dollar" },
{ "SCR", 2, ",", " ", TRUE, "SR", "Seychelles Rupee" },
{ "SDG", 2, ".", ",", TRUE, "SDG", "Sudanese Pound" },
{ "SEK", 2, ",", ".", FALSE, "kr", "Swedish Krona" },
{ "SGD", 2, ".", ",", TRUE, "$", "Singapore Dollar" },
{ "SHP", 2, ".", ",", TRUE, "£", "Saint Helena Pound" },
{ "SLL", 2, ".", ",", TRUE, "Le", "Leone" },
{ "SOS", 2, ".", ",", TRUE, "S", "Somali Shilling" },
{ "SRD", 2, ",", ".", TRUE, "$", "Surinam Dollar" },
{ "STD", 0, ",", " ", FALSE, "Db", "Dobra" },
{ "SVC", 2, ".", "", FALSE, "SVC", "El Salvador Colon" },
{ "SYP", 2, ",", " ", TRUE, "LS", "Syrian Pound" },
{ "SZL", 2, ",", " ", TRUE, "E", "Lilangeni" },
{ "THB", 2, ".", ",", TRUE, "฿", "Baht" },
{ "TJS", 2, ",", " ", FALSE, "смн", "Somoni" },
{ "TMM", 2, ".", "", FALSE, "TMM", "Manat" },
{ "TND", 3, ",", " ", TRUE, "DT", "Tunisian Dinar" },
{ "TOP", 2, ".", ",", TRUE, "T$", "Pa'anga" },
{ "TRY", 2, ",", ".", FALSE, "₺", "New Turkish Lira" },
{ "TTD", 2, ".", ",", TRUE, "$", "Trinidad and Tobago Dollar" },
{ "TWD", 2, ".", ",", TRUE, "NT$", "New Taiwan Dollar" },
{ "TZS", 2, ".", ",", TRUE, "TSh", "Tanzanian Shilling" },
{ "UAH", 2, ",", " ", FALSE, "₴", "Hryvnia" },
{ "UGX", 0, ".", ",", TRUE, "USh", "Uganda Shilling" },
{ "USD", 2, ",", " ", TRUE, "$", "US Dollar" },
{ "USN", 2, ".", "", FALSE, "USN", "US Dollar (Next day)" },
{ "USS", 2, ".", "", FALSE, "USS", "US Dollar (Same day)" },
{ "UYI", 0, ".", "", FALSE, "UYI", "Uruguay Peso en Unidades Indexadas" },
{ "UYU", 2, ",", ".", TRUE, "$", "Peso Uruguayo" },
{ "UZS", 2, ",", " ", TRUE, "soʻm", "Uzbekistan Sum" },
{ "VEF", 2, ",", ".", TRUE, "Bs.", "Bolivar Fuerte" },
{ "VND", 0, ",", ".", FALSE, "₫", "Dong" },
{ "VUV", 0, ",", " ", TRUE, "VT", "Vatu" },
{ "WST", 2, ".", ",", TRUE, "WS$", "Tala" },
{ "XAF", 0, ",", " ", TRUE, "FCFA", "CFA Franc BEAC" },
{ "XAG", 2, ".", "", FALSE, "XAG", "Silver" },
{ "XAU", 2, ".", "", FALSE, "XAU", "Gold" },
{ "XBA", 2, ".", "", FALSE, "XBA", "European Composite Unit (EURCO)" },
{ "XBB", 2, ".", "", FALSE, "XBB", "European Monetary Unit (E.M.U.-6)" },
{ "XBC", 2, ".", "", FALSE, "XBC", "European Unit of Account 9 (E.U.A.-9)" },
{ "XBD", 2, ".", "", FALSE, "XBD", "European Unit of Account 17 (E.U.A.-17)" },
{ "XCD", 2, ",", " ", TRUE, "$", "East Caribbean Dollar" },
{ "XDR", 2, ",", " ", TRUE, "XDR", "Special Drawing Rights" },
{ "XFO", 0, ".", "", FALSE, "XFO", "Gold-Franc" },
{ "XFU", 2, ".", "", FALSE, "XFU", "UIC-Franc" },
{ "XOF", 0, ",", " ", TRUE, "CFA", "CFA Franc BCEAO" },
{ "XPD", 2, ".", "", FALSE, "XPD", "Palladium" },
{ "XPF", 0, ",", " ", FALSE, "FCFP", "CFP Franc" },
{ "XPT", 2, ".", "", FALSE, "XPT", "Platinum" },
{ "XTS", 2, ".", "", FALSE, "XTS", "Code for testing purposes" },
{ "XXX", 2, ".", "", FALSE, "XXX", "No currency" },
{ "YER", 2, ".", ",", TRUE, "ر.ي.", "Yemeni Rial" },
{ "ZAR", 2, ",", " ", TRUE, "R", "Rand" },
{ "ZMK", 2, ".", "", FALSE, "ZMK", "Zambian Kwacha" },
{ "ZWD", 2, ".", "", FALSE, "ZWD", "Zimbabwe Dollar" }
};
guint n_iso4217cur = G_N_ELEMENTS (iso4217cur);
homebank-5.9.7/src/hb-import.h 0000644 0001750 0001750 00000013517 15016602547 015470 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_IMPORT_H__
#define __HB_IMPORT_H__
#include "hb-split.h"
typedef struct _generic_file GenFile;
typedef struct _generic_acc GenAcc;
typedef struct _generic_split GenSplit;
typedef struct _generic_txn GenTxn;
//those are guin32 special values
#define DST_ACC_GLOBAL 100001
#define DST_ACC_NEW 100002
#define DST_ACC_SKIP 100010
/* csv format validator */
enum
{
CSV_STRING,
CSV_DATE,
CSV_INT,
CSV_DOUBLE
};
struct _generic_file
{
guint32 key;
gchar *filepath;
gint filetype;
const gchar *encoding;
gint datefmt;
gboolean loaded;
gboolean invaliddatefmt;
//#2111468 add error log
guint n_error;
gchar *errlog;
};
struct _generic_acc
{
guint32 kfile;
gint filetype;
guint32 key;
gchar *name;
//maybe new user name
gchar *number;
guint32 kacc; //100001 = NEW, 100002 = SKIP
gboolean is_dupcheck; //if target account was checked for duplicate
gboolean is_ccard;
gboolean is_unamed; //if src account has no name into file
gdouble initial;
guint n_txnall; //nb of txn total
guint n_txnimp; //nb of txn to import
guint n_txnbaddate; //nb of txn with bad date
guint n_txnsimimp; //nb of txn similar import
guint n_txnsimdst; //nb of txn similar dstacccount
};
struct _generic_split
{
gchar *category;
gdouble amount;
gchar *memo;
};
struct _generic_txn
{
guint32 kfile; //todo: remove this
guint32 kacc;
gchar *account;
gchar *rawnumber; // ; check_number
gchar *rawpayee; //P ; name
gchar *rawmemo; //M ; memo
gchar *fitid; // 5.5.1 add OFX fitid
gchar *date; //D ; date_posted
gchar *number; //N ;
gchar *payee;
gchar *memo;
gchar *category; //L
gchar *tags; //
guint row; //5.8 #2063416 same date txn
guint32 julian;
gushort paymode; // ; transactiontype
gdouble amount; //T ; amount
gboolean togamount;
gboolean reconciled; //R
gboolean cleared; //C
gboolean to_import;
gboolean is_imp_similar;
gboolean is_dst_similar;
gint nb_splits;
GenSplit splits[TXN_MAX_SPLIT];
GList *lst_existing;
};
typedef struct _ImportContext ImportContext;
struct _ImportContext
{
GList *gen_lst_file;
GList *gen_lst_acc;
GList *gen_lst_txn;
guint32 gen_next_acckey;
//to keep track of where we are
guint32 curr_kfile;
guint32 curr_kacc;
// ofx stuff
GenAcc *curr_acc;
gboolean curr_acc_isnew;
gint opt_dateorder;
gint opt_daygap;
gint opt_ofxname;
gint opt_ofxmemo;
gboolean opt_qifmemo;
gboolean opt_qifswap;
gboolean opt_ucfirst;
gboolean opt_togamount;
gboolean set_pending;
gboolean do_auto_payee;
gboolean do_auto_assign;
//gboolean is_ccard;
//GList *trans_list; // trn storage
//gint next_acc_key; //max key account when start
//gint datefmt;
//const gchar *encoding;
/*gint nb_src_acc, nb_new_acc;
gint cnt_new_ope;
gint cnt_new_pay;
gint cnt_new_cat;
gint cnt_err_date;
gint nb_duplicate;*/
};
enum QIF_Type
{
QIF_NONE,
QIF_HEADER,
QIF_ACCOUNT,
QIF_CATEGORY,
QIF_CLASS,
QIF_MEMORIZED,
QIF_TRANSACTION,
QIF_SECURITY,
QIF_PRICES
};
void da_import_context_new(ImportContext *ctx);
void da_import_context_destroy(ImportContext *ctx);
GenFile *da_gen_file_malloc(void);
void da_gen_file_free(GenFile *genfile);
GenFile *da_gen_file_get(GList *lst_file, guint32 key);
GenFile *da_gen_file_append_from_filename(ImportContext *ictx, gchar *filename);
GenAcc *da_gen_acc_malloc(void);
void da_gen_acc_free(GenAcc *item);
GenAcc *da_gen_acc_get_by_key(GList *lst_acc, guint32 key);
GenTxn *da_gen_txn_malloc(void);
void da_gen_txn_free(GenTxn *item);
GList *da_gen_txn_sort(GList *list);
void da_gen_txn_destroy(ImportContext *ctx);
void da_gen_txn_new(ImportContext *ctx);
void da_gen_txn_move(GenTxn *sitem, GenTxn *ditem);
void da_gen_txn_append(ImportContext *ctx, GenTxn *item);
gchar *hb_import_filetype_char_get(GenAcc *acc);
GenAcc *hb_import_gen_acc_get_next(ImportContext *ictx, gint filetype, gchar *name, gchar *number);
gint hb_import_gen_acc_count_txn(ImportContext *ictx, GenAcc *genacc);
Transaction *hb_import_convert_txn(GenAcc *genacc, GenTxn *gentxn);
void hb_import_load_all(ImportContext *ictx);
gint hb_import_gen_txn_check_target_similar(ImportContext *ictx, GenAcc *genacc);
gint hb_import_gen_txn_check_duplicate(ImportContext *ictx, GenAcc *genacc);
gint hb_import_option_apply(ImportContext *ictx, GenAcc *genacc);
Account *import_create_account(gchar *name, gchar *number);
Account *hb_import_acc_find_existing(gchar *name, gchar *number);
void hb_import_apply(ImportContext *ictx);
GList *homebank_csv_import(ImportContext *ictx, GenFile *genfile);
GList *homebank_ofx_import(ImportContext *ictx, GenFile *genfile);
GList *homebank_qif_import(ImportContext *ictx, GenFile *genfile);
GList *account_import_qif(gchar *filename, ImportContext *ictx);
gdouble hb_qif_parser_get_amount(gchar *string);
gboolean hb_csv_test_line(gchar *rawline);
#if MYDEBUG
void _import_context_debug_file_list(ImportContext *ctx);
void _import_context_debug_acc_list(ImportContext *ctx);
void _import_context_debug_txn_list(ImportContext *ctx);
#endif
#endif
homebank-5.9.7/src/hb-payee.h 0000644 0001750 0001750 00000003634 15120541717 015255 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_PAYEE_H__
#define __HB_PAYEE_H__
#include "hb-types.h"
struct _payee
{
guint32 key;
gushort paymode;
gushort flags; //_pad1 prior 5.6
guint32 kcat;
gchar *name;
gchar *notes; //added 5.6
/* unsaved datas */
//gboolean flt_select;
guint16 nb_use_txn;
guint16 nb_use_all;
};
#define PF_HIDDEN (1<<8) //hidden by user
void da_pay_free(Payee *item);
Payee *da_pay_malloc(void);
void da_pay_destroy(void);
void da_pay_new(void);
guint da_pay_length(void);
guint32 da_pay_get_max_key(void);
gboolean da_pay_delete(guint32 key);
gboolean da_pay_insert(Payee *acc);
gboolean da_pay_append(Payee *acc);
Payee *da_pay_append_if_new(gchar *rawname);
Payee *da_pay_get(guint32 key);
gchar *da_pay_get_name(Payee *item);
Payee *da_pay_get_by_name(gchar *rawname);
void da_pay_consistency(Payee *item);
gint payee_delete_unused(void);
void payee_fill_usage(void);
GList *payee_glist_sorted(gint column);
void payee_move(guint32 key1, guint32 key2);
gboolean payee_rename(Payee *item, const gchar *newname);
gboolean payee_load_csv(gchar *filename, gchar **error);
void payee_save_csv(gchar *filename);
#endif
homebank-5.9.7/src/gtk-chart.c 0000644 0001750 0001750 00000274632 15005625261 015451 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "homebank.h"
#include "ui-widgets.h"
#include "gtk-chart-colors.h"
#include "gtk-chart.h"
//TODO: move this
#include "list-report.h"
extern gchar *CHART_CATEGORY;
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
//data
#define DBDT(x);
//#define DBDT(x) (x);
//calculation
#define DBC(x);
//#define DBC(x) (x);
//scale
#define DBS(x);
//#define DBS(x) (x);
//legend
#define DBL(x);
//#define DBL(x) (x);
//graph
#define DBG(x);
//#define DBG(x) (x);
//DB dynamics
#define DBD(x);
//#define DBD(x) (x);
//tooltip
#define DBT(x);
//#define DBT(x) (x);
//TODO: ugly hack for total line
#define LST_REPORT_POS_TOTAL G_MAXINT
#define DYNAMICS TRUE
#define DBGDRAW_RECT FALSE
#define DBGDRAW_TEXT FALSE
#define DBGDRAW_ITEM FALSE
static GtkBoxClass *gtk_chart_parent_class = NULL;
const double dashed3[] = {3.0};
//static void chart_set_font_size(GtkChart *chart, PangoLayout *layout, gint font_size);
static gchar *chart_print_int(GtkChart *chart, gdouble value);
static gchar *chart_print_scalerate(GtkChart *chart, gdouble value);
static gboolean drawarea_full_redraw(GtkWidget *widget, gpointer user_data);
static void chart_clear(GtkChart *chart);
/* = = = = = = = = = = = = = = = = */
//5.7 to secure &g_array_index(chart->items, ChartItem, i);
static ChartItem *chart_chartitem_get(GtkChart *chart, guint index)
{
GArray *array;
ChartItem *retval = NULL;
g_return_val_if_fail (GTK_IS_CHART (chart), NULL);
array = chart->items;
if( index < array->len )
{
retval = &g_array_index(array, ChartItem, index);
}
else
g_warning(" chartitem out of bound %d of %d", index, array->len);
return retval;
}
static void chart_set_font_size(GtkChart *chart, PangoLayout *layout, gint font_size)
{
PangoAttrList *attrs;
PangoAttribute *attr;
double scale = PANGO_SCALE_MEDIUM;
//PANGO_SCALE_MEDIUM = normal size
//DB( g_print("\n[chart] set font size\n") );
switch(font_size)
{
case CHART_FONT_SIZE_TITLE:
//size = chart->pfd_size + 3;
scale = PANGO_SCALE_X_LARGE;
break;
case CHART_FONT_SIZE_SUBTITLE:
//size = chart->pfd_size + 1;
scale = PANGO_SCALE_LARGE;
break;
//case CHART_FONT_SIZE_NORMAL:
//size = chart->pfd_size - 1;
// break;
case CHART_FONT_SIZE_SMALL:
if(chart->smallfont == TRUE)
//size = chart->pfd_size - 2;
scale = PANGO_SCALE_SMALL;
else
//size = chart->pfd_size - 1;
scale = PANGO_SCALE_MEDIUM;
break;
}
//DB( g_print(" size=%d\n", size) );
attrs = pango_attr_list_new ();
attr = pango_attr_scale_new(scale);
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (layout, attrs);
pango_layout_set_font_description (layout, chart->pfd);
//pango_attribute_destroy(attr);
pango_attr_list_unref (attrs);
}
/* bar section */
//https://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis
static float CalculateStepSize(float range, float targetSteps)
{
// calculate an initial guess at step size
float tempStep = range/targetSteps;
// get the magnitude of the step size
float mag = (float)floor(log10(tempStep));
float magPow = (float)pow(10, mag);
// calculate most significant digit of the new step size
float magMsd = (int)(tempStep/magPow + 0.5);
// promote the MSD to either 1, 2, or 5
if (magMsd > 5.0)
magMsd = 10.0f;
else if (magMsd > 2.0)
magMsd = 5.0f;
else if (magMsd >= 1.0)
magMsd = 2.0f;
return magMsd*magPow;
}
static void colchart_compute_range(GtkChart *chart, HbtkDrawContext *drawctx)
{
double lobound, hibound;
DBC( g_print("\n[column] compute range\n") );
lobound=chart->rawmin;
hibound=chart->rawmax;
/* compute max ticks */
drawctx->range = chart->rawmax - chart->rawmin;
gint maxticks = MIN(10,floor(drawctx->graph.height / (drawctx->font_h * 2)));
if( chart->type == CHART_TYPE_STACK100 )
{
lobound = 0;
hibound = 100;
drawctx->range = 100;
}
DBC( g_print(" raw :: [%.2f - %.2f] range=%.2f\n", chart->rawmin, chart->rawmax, drawctx->range) );
DBC( g_print(" raw :: maxticks=%d (%g / (%g*2))\n", maxticks, drawctx->graph.height, drawctx->font_h) );
DBC( g_print("\n") );
drawctx->unit = CalculateStepSize((hibound-lobound), maxticks);
drawctx->min = -drawctx->unit * ceil(-lobound/drawctx->unit);
drawctx->max = drawctx->unit * ceil(hibound/drawctx->unit);
drawctx->range = drawctx->max - drawctx->min;
drawctx->div = drawctx->range / drawctx->unit;
DBC( g_print(" end :: interval=%.2f, ticks=%d\n", drawctx->unit, drawctx->div) );
DBC( g_print(" end :: [%.2f - %.2f], range=%.2f\n", drawctx->min, drawctx->max, drawctx->range) );
DBC( g_print("[column] end compute range\n\n") );
}
static void colchart_calculation(GtkChart *chart, HbtkDrawContext *drawctx)
{
gint blkw, maxvisi;
gint nb_items;
DBC( g_print("\n[colchart] calculation\n") );
nb_items = chart->nb_items;
if( chart->type == CHART_TYPE_STACK || chart->type == CHART_TYPE_STACK100)
nb_items = chart->nb_cols;
/* from fusionchart
the bar has a default width of 41
min space is 3 and min barw is 8
*/
// new computing
if( chart->usrbarw > 0.0 )
{
blkw = chart->usrbarw;
drawctx->barw = blkw - 3;
}
else
{
//minvisi = floor(chart->graph.width / (GTK_CHART_MINBARW+3) );
maxvisi = floor(drawctx->graph.width / (GTK_CHART_MAXBARW+3) );
//DBC( g_print(" width=%.2f, nb=%d, minvisi=%d, maxvisi=%d\n", chart->graph.width, nb_items, minvisi, maxvisi) );
if( nb_items <= maxvisi )
{
drawctx->barw = GTK_CHART_MAXBARW;
blkw = GTK_CHART_MAXBARW + ( drawctx->graph.width - (nb_items*GTK_CHART_MAXBARW) ) / nb_items;
}
else
{
blkw = MAX(GTK_CHART_MINBARW, floor(drawctx->graph.width / nb_items));
drawctx->barw = blkw - 3;
}
}
if(chart->dual)
drawctx->barw = drawctx->barw / 2;
DBC( g_print(" blkw=%d, barw=%2.f\n", blkw, drawctx->barw) );
drawctx->blkw = blkw;
drawctx->visible = drawctx->graph.width / blkw;
drawctx->visible = MIN(drawctx->visible, nb_items);
DBC( g_print(" blkw=%.2f, barw=%.2f, visible=%d\n", drawctx->blkw, drawctx->barw, drawctx->visible) );
drawctx->ox = drawctx->l;
drawctx->oy = drawctx->t + drawctx->h;
if(drawctx->range > 0)
drawctx->oy = floor(drawctx->graph.y + (drawctx->max/drawctx->range) * drawctx->graph.height);
DBC( g_print(" + ox=%.2f oy=%.2f\n", drawctx->ox, drawctx->oy) );
}
/*
** draw the scale
*/
static void colchart_draw_scale(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
double x, y, y2;
gdouble curxval;
gint i, first;
PangoLayout *layout;
gchar *valstr, *miscstr;
int tw, th;
DBS( g_print("\n[column] draw scale\n") );
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
/* clip */
//cairo_rectangle(cr, CHART_MARGIN, 0, chart->w, chart->h + CHART_MARGIN);
//cairo_clip(cr);
layout = pango_cairo_create_layout (cr);
chart_set_font_size(chart, layout, CHART_FONT_SIZE_SMALL);
cairo_set_line_width(cr, 1.0);
// Y-scale element (horiz lines + labels (amounts))
DBS( g_print("\n -- scale-y: [%d - %d]\n", 0, drawctx->div) );
curxval = drawctx->max;
cairo_set_dash(cr, 0, 0, 0);
for(i=0 ; i <= drawctx->div ; i++)
{
y = 0.5 + floor (drawctx->graph.y + ((i * drawctx->unit) / drawctx->range) * drawctx->graph.height);
//DB( g_print(" + i=%d :: y=%f (%f / %f) * %f\n", i, y, i*chart->unit, chart->range, chart->graph.height) );
//y-horiz line
if(!drawctx->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], ( curxval == 0.0 ) ? 0.8 : 0.1);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], ( curxval == 0.0 ) ? 0.8 : 0.1);
cairo_move_to (cr, drawctx->graph.x, y);
cairo_line_to (cr, drawctx->graph.x + drawctx->graph.width, y);
cairo_stroke (cr);
//y-axis label
if( chart->type != CHART_TYPE_STACK100 )
valstr = chart_print_int(chart, curxval);
else
valstr = chart_print_scalerate(chart, curxval * 100 / drawctx->range );
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
if(!drawctx->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
cairo_move_to (cr, drawctx->graph.x - (tw / PANGO_SCALE) - 2, y - ((th / PANGO_SCALE)*0.8) );
pango_cairo_show_layout (cr, layout);
curxval -= drawctx->unit;
}
// X-scale element (vert line + label (item name)
if( chart->dual == TRUE || chart->show_xval == TRUE )
{
gint lstr = drawctx->l;
double tx;
x = drawctx->graph.x + (drawctx->blkw/2) + 1;
y = drawctx->graph.y + drawctx->graph.height + 3;
DBS( g_print("\n -- scale-x: [%d - %d] visi %d\n", first, first+drawctx->visible, drawctx->visible) );
for(i=first ; i<(first+drawctx->visible) ; i++)
{
/*if( chart->dual == TRUE )
{
tx = 0.5 + x + (drawctx->blkw/2);
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.8);
cairo_move_to(cr, tx, y0+2);
cairo_line_to(cr, tx, y0-2);
cairo_stroke(cr);
}*/
if( chart->show_xval == TRUE )
{
DataCol *dcol;
gshort flags = 0;
valstr = "";
miscstr = "";
//TODO: we miss a vertical line here
switch(chart->type)
{
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
//case CHART_TYPE_PIE:
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if( item )
{
valstr = item->label;
//5.7
//valstr = item->xlabel;
//flags = item->flags;
//miscstr = item->misclabel;
}
}
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
if( chart->cols != NULL )
{
dcol = chart->cols[i];
flags = dcol->flags;
valstr = dcol->xlabel;
miscstr = dcol->misclabel;
}
//failover
else
valstr = chart->collabel[i];
break;
}
DBS( g_print(" x: %2d '%s'", i, valstr) );
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
tx = x - ((tw / PANGO_SCALE)/2);
if( tx >= lstr )
{
DBS( g_print(" drawed") );
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.78);
cairo_move_to(cr, tx, y);
pango_cairo_show_layout (cr, layout);
lstr = x + ((tw / PANGO_SCALE)/2) + CHART_SPACING;
// draw a marker
tx = 0.5 + x;
cairo_move_to(cr, tx, drawctx->graph.y + drawctx->graph.height+3);
cairo_line_to(cr, tx, drawctx->graph.y + drawctx->graph.height);
cairo_stroke(cr);
}
//5.7 draw a line before current blk
if( flags & RF_NEWYEAR )
{
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.3);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.3);
tx = 0.5 + x - (drawctx->blkw/2);
cairo_move_to(cr, tx, drawctx->graph.y - 3);
cairo_line_to(cr, tx, drawctx->graph.y + drawctx->graph.height + 3);
cairo_stroke(cr);
//draw misc label
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.78);
pango_layout_set_text (layout, miscstr, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, tx + 2, drawctx->graph.y - (th / PANGO_SCALE));
pango_cairo_show_layout (cr, layout);
}
DBS( g_print("\n") );
}
x += drawctx->blkw;
}
}
/* average */
if( chart->show_average )
{
if( chart->average < 0 )
{
y = 0.5 + drawctx->oy + (ABS(chart->average)/drawctx->range) * drawctx->graph.height;
}
else
{
y = 0.5 + drawctx->oy - (ABS(chart->average)/drawctx->range) * drawctx->graph.height;
}
DBS( g_print(" -- average: x%d, y%f, w%d\n", drawctx->l, y, drawctx->w) );
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 1.0);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 1.0);
cairo_set_dash (cr, dashed3, 1, 0);
cairo_move_to(cr, drawctx->graph.x, y);
cairo_line_to(cr, drawctx->graph.x + drawctx->graph.width, y);
cairo_stroke(cr);
}
/* overdrawn */
if( chart->show_over )
{
//if(chart->minimum != 0 && chart->minimum >= chart->min)
if(chart->minimum >= drawctx->min)
{
DBS( g_print(" -- minimum: min=%.2f range=%.2f\n", drawctx->min, drawctx->range) );
if( chart->minimum < 0 )
{
y = 0.5 + drawctx->oy + (ABS(chart->minimum)/drawctx->range) * drawctx->graph.height;
}
else
{
y = 0.5 + drawctx->oy - (ABS(chart->minimum)/drawctx->range) * drawctx->graph.height;
}
y2 = (ABS(drawctx->min)/drawctx->range) * drawctx->graph.height - (y - drawctx->oy) + 1;
cairo_set_source_rgba(cr, COLTOCAIRO(255), COLTOCAIRO(0), COLTOCAIRO(0), AREA_ALPHA / 2);
DBS( g_print(" draw over: x%d, y%f, w%d, h%f\n", drawctx->l, y, drawctx->w, y2) );
cairo_rectangle(cr, drawctx->graph.x, y, drawctx->graph.width, y2 );
cairo_fill(cr);
cairo_set_source_rgb(cr, COLTOCAIRO(255), COLTOCAIRO(0), COLTOCAIRO(0));
cairo_set_dash (cr, dashed3, 1, 0);
cairo_move_to(cr, drawctx->graph.x, y);
cairo_line_to(cr, drawctx->graph.x + drawctx->graph.width, y);
cairo_stroke(cr);
}
}
g_object_unref (layout);
}
/* = = = = = = = = = = = = = = = = */
/* line section */
/*
** draw all visible lines
*/
static void linechart_draw_plot(cairo_t *cr, double x, double y, double r, GtkChart *chart, gboolean isprint)
{
cairo_set_line_width(cr, r / 2);
if(!isprint)
cairo_user_set_rgbcol(cr, &global_colors[THBASE]);
else
cairo_user_set_rgbcol(cr, &global_colors[WHITE]);
cairo_arc(cr, x, y, r, 0, 2*M_PI);
cairo_stroke_preserve(cr);
//cairo_set_source_rgb(cr, COLTOCAIRO(0), COLTOCAIRO(119), COLTOCAIRO(204));
cairo_user_set_rgbcol(cr, &chart->color_scheme.colors[chart->color_scheme.cs_blue]);
cairo_fill(cr);
}
static void linechart_draw_lines(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
double x, y, x2, y2, firstx, lastx, linew;
gint first, i;
DBG( g_print("\n[line] draw lines\n") );
x = drawctx->graph.x;
y = drawctx->oy;
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
/* clip */
//cairo_rectangle(cr, CHART_MARGIN, 0, chart->w, chart->h + CHART_MARGIN);
//cairo_clip(cr);
#if DBGDRAW_ITEM == 1
x2 = x + 0.5;
cairo_set_line_width(cr, 1.0);
double dashlength = 4;
cairo_set_dash (cr, &dashlength, 1, 0);
cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); // violet
for(i=first; i<=(first+drawctx->visible) ;i++)
{
cairo_move_to(cr, x2, drawctx->graph.y);
cairo_line_to(cr, x2, drawctx->graph.x + drawctx->graph.height);
x2 += drawctx->blkw;
}
cairo_stroke(cr);
cairo_set_dash (cr, &dashlength, 0, 0);
#endif
//todo: it should be possible to draw line & plot together using surface and composite fill, or sub path ??
lastx = x;
firstx = x;
linew = 4.0;
if(drawctx->barw < 24)
{
linew = 1 + (drawctx->barw / 8.0);
}
cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
cairo_set_line_width(cr, linew);
cairo_set_dash(cr, 0, 0, 0);
for(i=first; i<(first+drawctx->visible) ;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if( item)
{
x2 = x + (drawctx->blkw)/2;
y2 = drawctx->oy - (item->serie1 / drawctx->range) * drawctx->graph.height;
if( i == first)
{
firstx = x2;
cairo_move_to(cr, x2, y2);
}
else
{
if( i < (chart->nb_items) )
{
cairo_line_to(cr, x2, y2);
lastx = x2;
}
else
lastx = x2 - drawctx->barw;
}
x += drawctx->blkw;
}
}
cairo_user_set_rgbcol(cr, &chart->color_scheme.colors[chart->color_scheme.cs_blue]);
cairo_stroke_preserve(cr);
cairo_line_to(cr, lastx, y);
cairo_line_to(cr, firstx, y);
cairo_close_path(cr);
cairo_user_set_rgbacol(cr, &chart->color_scheme.colors[chart->color_scheme.cs_blue], AREA_ALPHA);
cairo_fill(cr);
x = drawctx->graph.x;
y = drawctx->oy;
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
// draw plots
for(i=first; i<(first+drawctx->visible) ;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if( item)
{
x2 = x + (drawctx->blkw)/2;
y2 = drawctx->oy - (item->serie1 / drawctx->range) * drawctx->graph.height;
//test draw vertical selection line
if( i == chart->hover )
{
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.1);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.1);
//cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); //blue
cairo_set_line_width(cr, 1.0);
cairo_move_to(cr, x2, drawctx->graph.y);
cairo_line_to(cr, x2, drawctx->t + drawctx->h - drawctx->font_h);
cairo_stroke(cr);
}
linechart_draw_plot(cr, x2, y2, i == chart->hover ? linew+1 : linew, chart, drawctx->isprint);
x += drawctx->blkw;
}
}
}
/*
** draw all visible bars
*/
static void colchart_draw_bars(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
double x, x2, y2, h;
gint i, first;
DBG( g_print("\n[column] draw bars\n") );
x = drawctx->graph.x;
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
DBG( g_print(" x=%.2f first=%d, blkw=%.2f, barw=%.2f\n", x, first, drawctx->blkw, drawctx->barw ) );
#if DBGDRAW_ITEM == 1
x2 = x + 0.5;
cairo_set_line_width(cr, 1.0);
double dashlength;
dashlength = 4;
cairo_set_dash (cr, &dashlength, 1, 0);
cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); // violet
for(i=first; i<=(first+drawctx->visible) ;i++)
{
cairo_move_to(cr, x2, drawctx->graph.y);
cairo_line_to(cr, x2, drawctx->graph.x + drawctx->graph.height);
x2 += drawctx->blkw;
}
cairo_stroke(cr);
cairo_set_dash (cr, &dashlength, 0, 0);
#endif
for(i=first; i<(first+drawctx->visible) ;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
gint color;
gint barw = drawctx->barw;
//if(!chart->datas1[i]) goto nextbar;
if( item )
{
if(!chart->show_mono)
color = i % chart->color_scheme.nb_cols;
else
color = chart->color_scheme.cs_green;
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[color], i == chart->hover);
x2 = x + (drawctx->blkw/2) - 1;
x2 = !chart->dual ? x2 - (barw/2) : x2 - barw - 1;
//exp/inc/bal
if(item->serie1)
{
h = floor((item->serie1 / drawctx->range) * drawctx->graph.height);
y2 = drawctx->oy - h;
if(item->serie1 < 0.0)
{
y2 += 1;
if(chart->show_mono)
{
color = chart->color_scheme.cs_red;
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[color], i == chart->hover);
}
}
//DBG( g_print(" + i=%d :: y2=%f h=%f (%f / %f) * %f\n", i, y2, h, chart->datas1[i], chart->range, chart->graph.height ) );
cairo_rectangle(cr, x2+2, y2, barw, h);
cairo_fill(cr);
}
if( chart->dual && item->serie2)
{
x2 = x2 + barw + 1;
h = floor((item->serie2 / drawctx->range) * drawctx->graph.height);
y2 = drawctx->oy - h;
cairo_rectangle(cr, x2+2, y2, barw, h);
cairo_fill(cr);
}
x += drawctx->blkw;
}
}
}
/*
** get the bar under the mouse pointer
*/
static gint colchart_get_hover(GtkWidget *widget, gint x, gint y, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
HbtkDrawContext *drawctx = &chart->context;
gint retval = -1;
gint index, first, px;
if( x <= (drawctx->l+drawctx->w) && x >= drawctx->graph.x && y >= drawctx->graph.y && y <= (drawctx->t+drawctx->h) )
{
px = (x - drawctx->graph.x);
//py = (y - chart->oy);
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
index = first + (px / drawctx->blkw);
if(index < chart->nb_items)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, index);
ChartItem *item = chart_chartitem_get(chart, index);
if( item )
{
retval = index;
if( item->n_child > 0 )
chart->drillable = TRUE;
}
}
}
return(retval);
}
static void colchart_first_changed( GtkAdjustment *adj, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
//gint first;
DB( g_print("\n[column] first changed\n") );
//first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
//DB( g_print(" first=%d\n", first) );
/*
DB( g_print(" scrollbar\n adj=%8x, low=%.2f upp=%.2f val=%.2f step=%.2f page=%.2f size=%.2f\n", adj,
adj->lower, adj->upper, adj->value, adj->step_increment, adj->page_increment, adj->page_size) );
*/
/* Set the number of decimal places to which adj->value is rounded */
//gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
//gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
drawarea_full_redraw (chart->drawarea, chart);
DB( g_print(" gtk_widget_queue_draw\n") );
gtk_widget_queue_draw(chart->drawarea);
}
/*
** scrollbar set values for upper, page size, and also show/hide
*/
static void colchart_scrollbar_setvalues(GtkChart *chart)
{
HbtkDrawContext *drawctx = &chart->context;
GtkAdjustment *adj = chart->adjustment;
gboolean visible = FALSE;
g_return_if_fail (GTK_IS_ADJUSTMENT (adj));
DB( g_print("\n[column] sb_set_values\n") );
g_object_freeze_notify(G_OBJECT(adj));
DB( g_print(" chart->nb_items=%d\n", chart->nb_items) );
if( chart->nb_items <= 0 )
{
DB( g_print(" set default and hide\n") );
gtk_adjustment_configure(GTK_ADJUSTMENT(adj), 0.0, 0.0, 1.0, 1.0, 1.0, 1.0);
}
else
{
gint first, nb_items;
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
nb_items = chart->nb_items;
if( chart->type == CHART_TYPE_STACK || chart->type == CHART_TYPE_STACK100 )
nb_items = chart->nb_cols - 1;
DB( g_print(" nb_items = %d\n", nb_items) );
DB( g_print(" entries=%d, visible=%d\n", nb_items, drawctx->visible) );
DB( g_print(" first=%d, upper=%d, pagesize=%d\n", first, nb_items, drawctx->visible) );
gtk_adjustment_set_upper(adj, (gdouble)nb_items);
gtk_adjustment_set_page_size(adj, (gdouble)drawctx->visible);
gtk_adjustment_set_page_increment(adj, (gdouble)drawctx->visible);
if(first + drawctx->visible > nb_items)
{
DB( g_print(" set value to %d\n", nb_items - drawctx->visible) );
gtk_adjustment_set_value(adj, (gdouble)nb_items - drawctx->visible);
}
// value, lower, upper, step, page, pagesize
//gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0)
if( drawctx->visible < nb_items )
visible = TRUE;
}
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION < 18) )
gtk_adjustment_changed (adj);
#endif
g_object_thaw_notify(G_OBJECT(adj));
DB( g_print(" visible=%d\n", visible) );
if( visible == FALSE )
gtk_widget_hide(GTK_WIDGET(chart->scrollbar));
else
gtk_widget_show(GTK_WIDGET(chart->scrollbar));
}
/* = = = = = = = = = = = = = = = = */
/* stack section */
// used for stack chart
static void chart_data_series(GtkChart *chart, gint indice)
{
GtkTreeModel *model;
GtkTreeIter iter, firstiter;
gboolean valid;
gdouble tmpmin, tmpmax;
guint nbrows, nbcols;
guint rowid, colid;
DBDT( g_print("------\n[chart] time data series\n") );
model = chart->model;
nbcols = chart->nb_cols;
//future with indice, which is a position into the treeview
DBDT( g_print(" time: initial row=%d\n", indice) );
//DBDT( g_print(" time: model %p\n", model) );
chart->show_breadcrumb = FALSE;
chart->colindice = indice;
if( indice < 0 )
{
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &firstiter);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), NULL);
gtk_widget_hide(chart->breadcrumb);
}
else
{
GtkTreePath *path = gtk_tree_path_new_from_indices(indice, -1);
gchar *pathstr, *itrlabel;
chart->show_breadcrumb = TRUE;
pathstr = gtk_tree_path_to_string(path);
DBDT( g_print(" time: path: %s\n", pathstr) );
gtk_tree_model_get_iter(model, &firstiter, path);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), &firstiter);
// update the breadcrumb
gtk_tree_model_get (GTK_TREE_MODEL(model), &firstiter,
LST_REPORT2_LABEL, &itrlabel,
-1);
gchar *bc = g_markup_printf_escaped("%s > %s", _(CHART_CATEGORY), itrlabel);
gtk_label_set_markup(GTK_LABEL(chart->breadcrumb), bc);
g_free(bc);
gtk_widget_show(chart->breadcrumb);
// move to xx:0
gtk_tree_path_append_index(path, 0);
valid = gtk_tree_model_get_iter(model, &firstiter, path);
DBDT( g_print(" time: path: %s\n", pathstr) );
gtk_tree_path_free(path);
g_free(pathstr);
}
//5.7 override
//chart->nb_items = nbrows;
//nbrows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), NULL);
//TODO: crappy here
//if( chart->nb_items > 0 ) //substract TOTAL row
// chart->nb_items--;
nbrows = chart->nb_items;
chart->colsum = g_malloc0(sizeof(gdouble)*nbcols);
chart->items = g_array_sized_new(FALSE, FALSE, sizeof(ChartItem), chart->nb_items);
DBDT( g_print(" time: malloc for %d rows\n", chart->nb_items) );
tmpmin = tmpmax = 0.0;
// foreach column
for(colid=0 ; coliditems, &item, 1);
DBDT( g_print(" append chartiem for colid0: '%s' n_child:%d\n", item.label, item.n_child) );
}
value = da_datarow_get_cell_sum (dr, colid);
/*if( chart->type == CHART_TYPE_STACK100 )
{
value = ABS(value);
}*/
chart->colsum[colid] += ABS(value);
DBDT( g_print(" row=%p pos(row)=%d col=%d '%s', amt=%.2f colsum=%.2f\n", dr, pos, colid, label, value, chart->colsum[colid]) );
if( value < 0.0 )
colmin += value;
else
colmax += value;
next:
valid = gtk_tree_model_iter_next (model, &iter);
rowid++;
}
DBDT( g_print(" col colmin=%.2f colmax=%.2f\n", colmin, colmax) );
tmpmin = MIN(tmpmin, colmin);
tmpmax = MAX(tmpmax, colmax);
}
// ensure rawmin rawmax not equal
if(tmpmin == tmpmax)
{
tmpmin = 0;
tmpmax = 100;
}
DBDT( g_print(" time: rawmin=%.2f rawmax=%.2f\n", tmpmin, tmpmax) );
chart->rawmin = tmpmin;
chart->rawmax = tmpmax;
}
static gboolean colchart_draw_stacks_get_top_level (GtkTreeModel *model, gint indice, GtkTreeIter *return_iter)
{
GtkTreeIter iter;
gboolean valid;
if( indice < 0 )
{
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
*return_iter = iter;
}
else
{
GtkTreePath *path = gtk_tree_path_new_from_indices(indice, 0, -1);
valid = gtk_tree_model_get_iter(model, &iter, path);
*return_iter = iter;
gtk_tree_path_free(path);
}
return valid;
}
static void colchart_draw_stacks(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
double x, x2, y2, h, ypos, yneg, rate, srate;
gint i, r, first;
GtkTreeModel *model = chart->model;
GtkTreeIter iter;
gboolean valid;
DBG( g_print("\n[column] draw stacks\n") );
x = drawctx->graph.x;
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
rate = 0;
cairo_set_line_width(cr, 1.0);
DBG( g_print(" x=%.2f first=%d, blkw=%.2f, barw=%.2f\n", x, first, drawctx->blkw, drawctx->barw ) );
DBG( g_print(" mono=%d\n", chart->show_mono ) );
x2 = floor(x + (drawctx->blkw - drawctx->barw)/2);
DBG( g_print(" x2=%.0f, height=%.0f\n", x2, drawctx->graph.height) );
//foreach cols
for(i=first; i<(first+drawctx->visible) ;i++)
//for(i=0; inb_items ;i++)
{
y2 = floor(drawctx->oy);
ypos = y2;
yneg = y2;
DBG( g_print("\ndraw blk/col %d\n", i) );
//foreach rows
//valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
valid = colchart_draw_stacks_get_top_level(GTK_TREE_MODEL(model), chart->colindice, &iter);
r = 0;
srate = 0;
while (valid)
{
DataRow *dr;
gdouble value;
gint id, color;
gboolean hover;
gtk_tree_model_get (GTK_TREE_MODEL(model), &iter,
LST_REPORT2_POS, &id,
LST_REPORT2_ROW, &dr,
-1);
if( id != LST_REPORT_POS_TOTAL )
{
value = da_datarow_get_cell_sum (dr, i);
if( value == 0.0 )
goto nextrow;
//for stacked
//pre 5.7
//rate = ABS(value/drawctx->range);
if(chart->type == CHART_TYPE_STACK)
rate = value/drawctx->range;
else if(chart->type == CHART_TYPE_STACK100)
{
value = ABS(value);
rate = value / chart->colsum[i];
DBG( g_print(" row=%2d value=%7.2f / %7.2f = %7.2f\n", r, value, chart->colsum[i], rate) );
}
//h = 0.5 + floor(rate * drawctx->graph.height);
h = rate * drawctx->graph.height;
//debug sum of rate
srate += h;
color = chart->color_scheme.cs_green;
if( value > 0 )
{
DBG( g_print(" row %2d value=%7.2f x2=%7.2f, ypos=%7.2f rate=%f height=%7.2f sheight=%f\n", r, value, x2+2, ypos, 100*rate, h, srate) );
cairo_rectangle(cr, x2+2, ypos-h, drawctx->barw, h);
ypos = ypos - h;
}
else
{
DBG( g_print(" row %2d value=%7.2f x2=%7.2f, yneg=%7.2f rate=%f height=%7.2f sheight=%f\n", r, value, x2+2, yneg, 100*rate, h, srate) );
cairo_rectangle(cr, x2+2, yneg-h+1, drawctx->barw, h);
yneg = yneg - h;
color = chart->color_scheme.cs_red;
}
//overwrite color
if(!chart->show_mono)
{
color = r % chart->color_scheme.nb_cols;
}
hover = (i == chart->colhover) && (r == chart->hover) ? TRUE : FALSE;
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[color], hover);
//5.7 test forecast fill
//TODO: future
DataCol *dcol;
gshort flags = 0;
if( chart->cols != NULL )
{
dcol = chart->cols[i];
flags = dcol->flags;
}
if( !(flags & RF_FORECAST) )
cairo_fill(cr);
else
{
double dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
cairo_stroke_preserve(cr);
cairo_user_set_rgbacol_over(cr, &chart->color_scheme.colors[color], hover, 0.5);
cairo_fill(cr);
}
}
nextrow:
valid = gtk_tree_model_iter_next (model, &iter);
r++;
}
x2 += drawctx->blkw;
}
}
/*
** get the bar under the mouse pointer
*/
static gint colchart_stack_get_hover(GtkWidget *widget, gint x, gint y, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
HbtkDrawContext *drawctx = &chart->context;
gint retval;
gint index, first, px, r;
double y2, h, ypos, yneg, rate;
GtkTreeModel *model = chart->model;
GtkTreeIter iter;
gboolean valid;
retval = -1;
if( x <= (drawctx->l+drawctx->w) && x >= drawctx->graph.x && y >= drawctx->graph.y && y <= (drawctx->t+drawctx->h) )
{
px = (x - drawctx->graph.x);
//py = (drawctx->oy - y);
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
index = first + (px / drawctx->blkw);
rate = 0;
DBD( g_print(" x=%d y=%d px=%d oy=%f\n", x, y, px, drawctx->oy) );
//we are hover column 'index'
if(index < chart->nb_cols)
{
y2 = 0.5 + floor(drawctx->oy);
ypos = y2;
yneg = y2+1;
chart->colhover = index;
//Each rows
//valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
valid = colchart_draw_stacks_get_top_level(GTK_TREE_MODEL(model), chart->colindice, &iter);
r = 0;
while (valid)
{
DataRow *dr;
gdouble value;
gint id;
gtk_tree_model_get (GTK_TREE_MODEL(model), &iter,
LST_REPORT2_POS, &id,
LST_REPORT2_ROW, &dr,
-1);
if( id != LST_REPORT_POS_TOTAL )
{
value = da_datarow_get_cell_sum (dr, index);
if( value == 0.0 )
goto nextrow;
//for stacked
//pre 5.7
//rate = ABS(value/drawctx->range);
if(chart->type == CHART_TYPE_STACK)
rate = value/drawctx->range;
else if(chart->type == CHART_TYPE_STACK100)
{
value = ABS(value);
rate = value/chart->colsum[index];
}
//h = 0.5 + floor(rate * drawctx->graph.height);
h = rate * drawctx->graph.height;
if( value > 0 )
{
DBD( g_print(" col=%02d row=%2d ymin=%.0f ymax=%.0f height=%.0f\n", index, r, ypos-h, ypos, h) );
if( y > ypos - h && y < ypos )
{
retval = r;
DBD( g_print(" ** match\n") );
//5.7 drill down
if( gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), &iter) > 0 )
chart->drillable = TRUE;
break;
}
ypos = ypos - h;
}
else
{
DBD( g_print(" col=%02d row=%2d ymin=%.0f ymax=%.0f height=%.0f\n", index, r, yneg, yneg-h, h) );
if( y > yneg && y < yneg - h)
{
retval = r;
DBD( g_print(" ** match\n") );
//5.7 drill down
if( gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), &iter) > 0 )
chart->drillable = TRUE;
break;
}
yneg = yneg - h;
}
}
nextrow:
valid = gtk_tree_model_iter_next (model, &iter);
r++;
}
}
}
DBD( g_print(" stack active=%d\n", retval) );
return(retval);
}
/* = = = = = = = = = = = = = = = = */
/* pie section */
static void piechart_draw_slices(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
if(chart->nb_items <= 0 || chart->total == 0.0)
return;
DBG( g_print("\n[pie] draw slices\n") );
//cairo drawing
double a1 = 0 * (M_PI / 180);
double a2 = 360 * (M_PI / 180);
//g_print("angle1=%.2f angle2=%.2f\n", a1, a2);
double cx = drawctx->ox;
double cy = drawctx->oy;
double radius = drawctx->rayon/2;
gint i;
double dx, dy;
double sum = 0.0;
gint color;
DBG( g_print("rayon=%d\n", drawctx->rayon) );
DBG( g_print("total=%.f\n", chart->total) );
for(i=0; i< chart->nb_items ;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if( item )
{
a1 = ((360 * (sum / chart->total)) - 90) * (M_PI / 180);
sum += ABS(item->serie1);
a2 = ((360 * (sum / chart->total)) - 90) * (M_PI / 180);
//if(i < chart->nb_items-1) a2 += 0.0175;
dx = cx;
dy = cy;
DBG( g_print("- s%2d: %.2f%% a1=%.2f a2=%.2f | %s %.f\n", i, sum / chart->total, a1, a2, item->label, item->serie1) );
cairo_move_to(cr, dx, dy);
cairo_arc(cr, dx, dy, radius, a1, a2);
#if CHART_PARAM_PIE_LINE == TRUE
cairo_set_line_width(cr, 2.0);
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_line_to(cr, cx, cy);
cairo_stroke_preserve(cr);
#endif
//g_print("color : %f %f %f\n", COLTOCAIRO(colors[i].r), COLTOCAIRO(colors[i].g), COLTOCAIRO(colors[i].b));
color = i % chart->color_scheme.nb_cols;
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[color], i == chart->hover);
cairo_fill(cr);
}
}
#if CHART_PARAM_PIE_DONUT == TRUE
a1 = 0;
a2 = 2 * M_PI;
//original
//radius = (gint)((chart->rayon/3) * (1 / PHI));
//5.1
//radius = (gint)((chart->rayon/2) * 2 / 3);
//ynab
//piehole value from 0.4 to 0.6 will look best on most charts
radius = (gint)(drawctx->rayon/2) * CHART_PARAM_PIE_HOLEVALUE;
if(!drawctx->isprint)
cairo_user_set_rgbcol(cr, &global_colors[THBASE]);
else
cairo_user_set_rgbcol(cr, &global_colors[WHITE]);
cairo_arc(cr, cx, cy, radius, a1, a2);
cairo_fill(cr);
#endif
}
static gint piechart_get_hover(GtkWidget *widget, gint x, gint y, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
HbtkDrawContext *drawctx = &chart->context;
gint retval, px, py;
gint index;
double radius, h;
DBD( g_print("\n[pie] get hover\n") );
retval = -1;
px = x - drawctx->ox;
py = y - drawctx->oy;
h = sqrt( pow(px,2) + pow(py,2) );
radius = drawctx->rayon / 2;
if(h <= radius && h >= (radius * CHART_PARAM_PIE_HOLEVALUE) )
{
double angle, b;
b = (acos(px / h) * 180) / M_PI;
angle = py > 0 ? b : 360 - b;
angle += 90;
if(angle > 360) angle -= 360;
//angle = 360 - angle;
//todo optimize
gdouble cumul = 0;
for(index=0; index< chart->nb_items ;index++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, index);
ChartItem *item = chart_chartitem_get(chart, index);
if( item )
{
cumul += ABS(item->serie1/chart->total)*360;
if( cumul > angle )
{
retval = index;
if( item->n_child > 0 )
chart->drillable = TRUE;
break;
}
}
}
//DBD( g_print(" inside: x=%d, y=%d\n", x, y) );
//DBD( g_print(" inside: b=%f angle=%f, slice is %d\n", b, angle, index) );
}
return(retval);
}
static void piechart_calculation(GtkChart *chart, HbtkDrawContext *drawctx)
{
//GtkWidget *drawarea = chart->drawarea;
gint w, h;
DBC( g_print("\n[pie] calculation\n") );
w = drawctx->graph.width;
h = drawctx->graph.height;
drawctx->rayon = MIN(w, h);
drawctx->mark = 0;
#if CHART_PARAM_PIE_MARK == TRUE
gint m = floor(drawctx->rayon / 100);
m = MAX(2, m);
drawctx->rayon -= (m * 2);
drawctx->mark = m;
#endif
drawctx->ox = drawctx->graph.x + (w / 2);
drawctx->oy = drawctx->graph.y + (drawctx->rayon / 2);
DBC( g_print(" center: %g, %g - R=%d, mark=%d\n", drawctx->ox, drawctx->oy, drawctx->rayon, drawctx->mark) );
}
/* = = = = = = = = = = = = = = = = */
/*
** print a integer number
*/
static gchar *chart_print_int(GtkChart *chart, gdouble value)
{
hb_strfmon_int(chart->buffer1, CHART_BUFFER_LENGTH-1, (gdouble)value, chart->kcur, chart->minor);
return chart->buffer1;
}
/*
** print a scale rate number
*/
static gchar *chart_print_scalerate(GtkChart *chart, gdouble value)
{
g_snprintf (chart->buffer1, CHART_BUFFER_LENGTH-1, "%.0f%%", value);
return chart->buffer1;
}
/*
** print a rate number
*/
static gchar *chart_print_rate(GtkChart *chart, gdouble value)
{
g_snprintf (chart->buffer1, CHART_BUFFER_LENGTH-1, "%.2f%%", value);
return chart->buffer1;
}
/*
** print a double number
*/
static gchar *chart_print_double(GtkChart *chart, gchar *buffer, gdouble value)
{
hb_strfmon(buffer, CHART_BUFFER_LENGTH-1, value, chart->kcur, chart->minor);
return buffer;
}
static void chart_clear_items(GtkChart *chart)
{
GArray *array = chart->items;
guint i;
DBDT( g_print("\n[gtkchart] %p clear items\n", chart) );
if(array != NULL)
{
DBDT( g_print(" n_items: %d\n", array->len) );
for(i=0;ilen;i++)
{
//no need to secure access here
ChartItem *item = &g_array_index(array, ChartItem, i);
//DBDT( g_print(" free item %3d: %p: '%s' %p\n", i, item, item->label, item->legend) );
DBDT( g_print(" free item %3d: %p: '%s'\n", i, item, item->label) );
g_free(item->label); //we free label as it comes from a model_get into setup_with_model
//g_free(item->xlabel);
//g_free(item->misclabel);
}
g_array_free(chart->items, TRUE);
chart->items = NULL;
}
g_free(chart->colsum);
chart->colsum = NULL;
chart->nb_items = 0;
chart->colindice = -1;
chart->total = 0;
chart->rawmin = 0;
chart->rawmax = 0;
}
extern HbKvData CYA_REPORT_SRC[];
/*
** clear any allocated memory
*/
static void chart_clear(GtkChart *chart)
{
HbtkDrawContext *drawctx = &chart->context;
DBDT( g_print("\n[gtkchart] %p clear\n", chart) );
DBDT( g_print(" type: %d %s\n", chart->type, hbtk_get_label(CYA_REPORT_SRC, chart->type)) );
//free & clear any previous allocated datas
if(chart->title != NULL)
{
g_free(chart->title);
chart->title = NULL;
}
if(chart->subtitle != NULL)
{
g_free(chart->subtitle);
chart->subtitle = NULL;
}
chart_clear_items(chart);
chart->totmodel = NULL;
drawctx->range = 0;
g_free(chart->collabel);
chart->collabel = NULL;
chart->hover = -1;
chart->lasthover = -1;
chart->colhover = -1;
chart->lastcolhover = -1;
}
/*
** setup our chart with a model and column
*/
static void chart_setup_with_model(GtkChart *chart, gint indice)
{
GtkTreeModel *totmodel;
GtkTreeIter iter;
guint column1;
guint column2;
gint rowid, i;
gboolean valid = FALSE;
DBDT( g_print("\n[chart] total data series\n") );
totmodel = chart->totmodel;
column1 = chart->column1;
column2 = chart->column2;
//future with indice, which is a position into the treeview
DBDT( g_print(" total: indice: %d\n", indice) );
chart->show_breadcrumb = FALSE;
if( indice < 0 )
{
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(totmodel), &iter);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(totmodel), NULL);
gtk_widget_hide(chart->breadcrumb);
}
else
{
GtkTreePath *path = gtk_tree_path_new_from_indices(indice, -1);
gchar *pathstr, *itrlabel, *valstr;
gdouble value1;
chart->show_breadcrumb = TRUE;
pathstr = gtk_tree_path_to_string(path);
DBDT( g_print(" total: path: %s\n", pathstr) );
gtk_tree_model_get_iter(totmodel, &iter, path);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(totmodel), &iter);
// update the breadcrumb
gtk_tree_model_get (GTK_TREE_MODEL(totmodel), &iter,
LST_REPORT_LABEL, &itrlabel,
column1, &value1,
-1);
//5.7.3 // 2042699
valstr = chart_print_double(chart, chart->buffer1, value1);
gchar *bc = g_markup_printf_escaped("%s > %s %s", _(CHART_CATEGORY), itrlabel, valstr);
gtk_label_set_markup(GTK_LABEL(chart->breadcrumb), bc);
g_free(bc);
gtk_widget_show(chart->breadcrumb);
// move to xx:0
gtk_tree_path_append_index(path, 0);
valid = gtk_tree_model_get_iter(totmodel, &iter, path);
DBDT( g_print(" total: path: %s\n", pathstr) );
gtk_tree_path_free(path);
g_free(pathstr);
}
//#1870390 add total into listview & exclude from charts
chart->items = g_array_sized_new(FALSE, FALSE, sizeof(ChartItem), chart->nb_items);
DBDT( g_print(" total: nbitems=%d, struct=%d\n", chart->nb_items, (gint)sizeof(ChartItem)) );
chart->dual = (column1 == column2) ? FALSE : TRUE;
rowid = 0;
while (valid)
{
//DataRow *row;
gint pos;
gchar *label;
gdouble value1, value2;
ChartItem item;
/* column 0: pos (gint) */
/* column 1: key (gint) */
/* column 2: label (gchar) */
/* column x: values (double) */
gtk_tree_model_get (GTK_TREE_MODEL(totmodel), &iter,
LST_REPORT_POS, &pos, //hold total
LST_REPORT_LABEL, &label,
//LST_REPORT_ROW, &row,
column1, &value1,
column2, &value2,
-1);
//#1870390 add total into listview & exclude from charts
if( pos == LST_REPORT_POS_TOTAL )
{
//subtract the LST_REPORT_POS_TOTAL line not to be drawed
chart->nb_items--;
//5.8 fix leak
g_free(label);
goto next;
}
if(chart->dual || chart->abs)
{
value1 = ABS(value1);
value2 = ABS(value2);
}
/* data1 value storage & min, max compute */
chart->rawmin = MIN(chart->rawmin, value1);
chart->rawmax = MAX(chart->rawmax, value1);
if( chart->dual )
{
/* data2 value storage & min, max compute */
chart->rawmin = MIN(chart->rawmin, value2);
chart->rawmax = MAX(chart->rawmax, value2);
}
item.label = label;
//5.7
/*if( row != NULL )
{
if( row->xlabel )
item.xlabel = g_strdup(row->xlabel);
if( row->misclabel )
item.misclabel = g_strdup(row->misclabel);
item.flags = row->flags;
}*/
//item.legend = NULL;
item.serie1 = value1;
item.serie2 = value2;
//add for drill down
item.n_child = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(totmodel), &iter);
g_array_append_vals(chart->items, &item, 1);
DBDT( g_print(" total: append item %3d: '%s' %.2f %2f : %d\n", rowid, label, value1, value2, item.n_child) );
/* ensure rawmin rawmax not equal */
if(chart->rawmin == chart->rawmax)
{
chart->rawmin = 0;
chart->rawmax = 100;
}
/* pie chart total sum */
chart->total += ABS(value1);
//leak
//don't g_free(label); here done into chart_clear
next:
valid = gtk_tree_model_iter_next (totmodel, &iter);
rowid++;
}
// compute rate for legend for bar/pie
for(i=0;inb_items;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
//gchar *strval;
if( item )
{
DBDT( g_print(" total: preset legend %3d: %p : item '%s'\n", i, item, item->label) );
//strval = chart_print_double(chart, chart->buffer1, item->serie1);
item->rate = ABS(item->serie1*100/chart->total);
//item->legend = g_markup_printf_escaped("%s\n%s (%.f%%)", item->label, strval, item->rate);
}
}
//g_print("total is %.2f\n", total);
//ensure the widget is mapped
//gtk_widget_map(chart);
}
static void chart_layout_area(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
PangoLayout *layout;
gchar *valstr;
int tw, th, bch;
gint i, n_legend;
DBC( g_print("----------\n[gtkchart] layout area\n") );
DBC( g_print(" drawctx: %p\n", drawctx) );
DBC( g_print(" area : %d %d %d %d\n", drawctx->l, drawctx->t, drawctx->w, drawctx->h ) );
DBC( g_print(" is print: %d\n", drawctx->isprint) );
/* Create a PangoLayout, set the font and text */
layout = pango_cairo_create_layout (cr);
DBC( g_print(" -- compute header\n") );
// compute title
drawctx->title_zh = 0;
if(chart->title != NULL)
{
chart_set_font_size(chart, layout, CHART_FONT_SIZE_TITLE);
pango_layout_set_text (layout, chart->title, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->title_zh = (th / PANGO_SCALE);
DBC( g_print(" - title: %s w=%d h=%d\n", chart->title, tw, th) );
}
// compute subtitle
drawctx->subtitle_zh = 0;
if(chart->subtitle != NULL)
{
chart_set_font_size(chart, layout, CHART_FONT_SIZE_SUBTITLE);
pango_layout_set_text (layout, chart->subtitle, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->subtitle_zh = (th / PANGO_SCALE);
DBC( g_print(" - subtitle: %s w=%d h=%d\n", chart->subtitle, tw, th) );
}
drawctx->subtitle_y = drawctx->t + drawctx->title_zh;
//breadcrumb top et position
bch = 0;
if( chart->show_breadcrumb )
{
chart_set_font_size(chart, layout, CHART_FONT_SIZE_NORMAL);
pango_layout_set_text (layout, _(CHART_CATEGORY), -1);
pango_layout_get_size (layout, &tw, &th);
bch = (th / PANGO_SCALE);
gtk_widget_set_margin_top(chart->breadcrumb, drawctx->t + drawctx->title_zh + drawctx->subtitle_zh);
}
//graph top & height
drawctx->graph.y = drawctx->t + drawctx->title_zh + drawctx->subtitle_zh + bch;
drawctx->graph.height = drawctx->h - drawctx->title_zh - drawctx->subtitle_zh - bch;
if(drawctx->title_zh > 0 || drawctx->subtitle_zh > 0)
{
drawctx->graph.y += CHART_MARGIN;
drawctx->graph.height -= CHART_MARGIN;
}
// compute other text
chart_set_font_size(chart, layout, CHART_FONT_SIZE_NORMAL);
DBC( g_print(" -- compute y-scale\n") );
// y-axis labels (amounts)
drawctx->scale_w = 0;
colchart_compute_range(chart, drawctx);
valstr = chart_print_int(chart, drawctx->min);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->scale_w = (tw / PANGO_SCALE);
valstr = chart_print_int(chart, drawctx->max);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->scale_w = MAX(drawctx->scale_w, (tw / PANGO_SCALE));
DBC( g_print(" scale : %d,%d %g,%g\n", drawctx->l, 0, drawctx->scale_w, 0.0) );
// compute font height
drawctx->font_h = (th / PANGO_SCALE);
// compute graph region
switch(chart->type)
{
case CHART_TYPE_LINE:
case CHART_TYPE_COL:
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
drawctx->graph.x = drawctx->l + drawctx->scale_w + 2;
drawctx->graph.width = drawctx->w - drawctx->scale_w - 2;
break;
case CHART_TYPE_PIE:
drawctx->graph.x = drawctx->l;
drawctx->graph.width = drawctx->w;
break;
}
DBC( g_print(" - graph : %g,%g %g,%g\n", drawctx->graph.x, drawctx->graph.y, drawctx->graph.width, drawctx->graph.height) );
if( chart->type != CHART_TYPE_PIE )
{
drawctx->graph.y += drawctx->font_h;
drawctx->graph.height -= drawctx->font_h;
if( chart->show_xval )
drawctx->graph.height -= (drawctx->font_h + CHART_SPACING);
}
DBC( g_print(" -- compute legend (if > 1 item)\n") );
//TODO: should not happen check why ?
if( chart->items )
{
//5.7 test aspect ratio
gint ratio;
if( drawctx->graph.width > drawctx->graph.height )
ratio = GTK_ORIENTATION_HORIZONTAL;
else
ratio = GTK_ORIENTATION_VERTICAL;
DBC( g_print(" raw ratio=%d %s :: w=%f h=%f\n", ratio, ratio == GTK_ORIENTATION_HORIZONTAL ? "Horiz" : "Vert", drawctx->graph.width, drawctx->graph.height) );
n_legend = chart->items->len;
DBC( g_print(" n_legend=%d\n", n_legend) );
// compute: each legend column width, and legend width
if(chart->show_legend)
{
double label_w = 0;
double label_wide_w = 0;
chart_set_font_size(chart, layout, CHART_FONT_SIZE_SMALL);
for(i=0 ; i < n_legend ; i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if( item )
{
// label width
pango_layout_set_text (layout, item->label, -1);
pango_layout_get_size (layout, &tw, &th);
label_w = MAX(label_w, (tw / PANGO_SCALE));
}
//DBC( g_print(" %d '%s' %f\n", i, item->label, label_w) );
}
DBC( g_print(" raw label width:%f\n", label_w) );
drawctx->legend_font_h = (th / PANGO_SCALE);
//force ratio to avoid legend at bottom with too much items
if( floor(n_legend * drawctx->legend_font_h * CHART_LINE_SPACING) > drawctx->graph.height / 2 )
{
ratio = GTK_ORIENTATION_HORIZONTAL;
DBC( g_print(" ratio forced to horiz\n") );
}
// labels not much than 1/4 of width graph
if( ratio == GTK_ORIENTATION_HORIZONTAL )
{
gdouble lw = floor(drawctx->graph.width / 4);
drawctx->legend_label_w = MIN(label_w, lw);
DBC( g_print(" clamp label width:%f\n", drawctx->legend_label_w) );
}
//#2037597
else
{
gdouble lw = floor((drawctx->graph.width - drawctx->legend_font_h - CHART_SPACING)*3/4);
drawctx->legend_label_w = MIN(label_w, lw);
}
drawctx->legend.width = drawctx->legend_font_h + CHART_SPACING + drawctx->legend_label_w;
drawctx->legend.height = MIN(floor(n_legend * drawctx->legend_font_h * CHART_LINE_SPACING), drawctx->graph.height);
if(chart->show_legend_wide )
{
//get rate width
pango_layout_set_text (layout, "00.00 %", -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->legend_rate_w = (tw / PANGO_SCALE);
//#1921741 text overlap
//get value width
drawctx->legend_value_w = 0;
valstr = chart_print_double(chart, chart->buffer1, drawctx->min);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->legend_value_w = (tw / PANGO_SCALE);
valstr = chart_print_double(chart, chart->buffer1, drawctx->max);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
drawctx->legend_value_w = MAX(drawctx->legend_value_w, (tw / PANGO_SCALE));
label_wide_w = CHART_SPACING + drawctx->legend_value_w + CHART_SPACING + drawctx->legend_rate_w;
//drawctx->legend.width += CHART_SPACING + drawctx->legend_value_w + CHART_SPACING + drawctx->legend_rate_w;
}
//5.7 hide legend if not enough room
//#1964434 maximize chart size
chart->legend_visible = FALSE;
chart->legend_wide_visible = FALSE;
if(chart->show_legend)
{
double tmp_legend_w = drawctx->legend.width + label_wide_w;
if( ratio == GTK_ORIENTATION_HORIZONTAL )
{
DBC( g_print(" right : %f < %d \n", drawctx->legend.width , (drawctx->w/2)) );
chart->legend_visible = TRUE;
//room for wide labels ?
if( (drawctx->graph.width - tmp_legend_w) > tmp_legend_w )
{
drawctx->legend.width += label_wide_w;
chart->legend_wide_visible = TRUE;
}
drawctx->graph.width -= (drawctx->legend.width + CHART_MARGIN);
drawctx->legend.x = drawctx->graph.x + drawctx->graph.width + CHART_MARGIN;
drawctx->legend.y = drawctx->graph.y;
}
else
{
//bottom
DBC( g_print(" bottom : %f < %d \n", drawctx->legend.width , (drawctx->w/2)) );
chart->legend_visible = TRUE;
if( (drawctx->w - tmp_legend_w) > 0 )
{
drawctx->legend.width += label_wide_w;
chart->legend_wide_visible = TRUE;
}
drawctx->graph.height -= (drawctx->legend.height + CHART_MARGIN);
drawctx->legend.x = drawctx->graph.x + drawctx->graph.width - drawctx->legend.width;
drawctx->legend.y = drawctx->graph.y + drawctx->graph.height + CHART_MARGIN;
// add x-scale room
if( chart->type != CHART_TYPE_PIE )
drawctx->legend.y += drawctx->font_h + 3;
}
}
}
}
DBC( g_print(" graph : %g %g %g %g\n", drawctx->graph.x, drawctx->graph.y, drawctx->graph.width, drawctx->graph.height ) );
DBC( g_print(" legend : %g %g %g %g\n", drawctx->legend.x, drawctx->legend.y, drawctx->legend.width, drawctx->legend.height ) );
// compute graph region
switch(chart->type)
{
case CHART_TYPE_LINE:
case CHART_TYPE_COL:
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
colchart_calculation(chart, drawctx);
break;
case CHART_TYPE_PIE:
piechart_calculation(chart, drawctx);
break;
}
g_object_unref (layout);
}
static void chart_recompute(GtkChart *chart)
{
HbtkDrawContext *drawctx = &chart->context;
//GdkWindow *gdkwindow;
cairo_surface_t *surf = NULL;
cairo_t *cr;
GtkAllocation allocation;
DB( g_print("\n[gtkchart] recompute layout\n") );
if( !gtk_widget_get_realized(chart->drawarea) || chart->surface == NULL )
return;
gtk_widget_get_allocation(chart->drawarea, &allocation);
/*
//removed deprecated call to gdk_cairo_create
gdkwindow = gtk_widget_get_window(chart->drawarea);
if(!gdkwindow)
{
surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
cr = cairo_create (surf);
}
else
cr = gdk_cairo_create (gdkwindow);
*/
cr = cairo_create (chart->surface);
drawctx->l = CHART_MARGIN;
drawctx->t = CHART_MARGIN;
drawctx->w = allocation.width - (CHART_MARGIN*2);
drawctx->h = allocation.height - (CHART_MARGIN*2);
DB( g_print(" raw dimension: l=%d, t=%d : w=%d, h=%d\n", drawctx->l, drawctx->t, drawctx->w, drawctx->h) );
chart_layout_area(cr, chart, drawctx);
cairo_destroy(cr);
cairo_surface_destroy(surf);
//TODO: simplify this
switch(chart->type)
{
case CHART_TYPE_LINE:
case CHART_TYPE_COL:
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
gtk_adjustment_set_value(chart->adjustment, 0);
colchart_scrollbar_setvalues(chart);
break;
case CHART_TYPE_PIE:
gtk_widget_hide(chart->scrollbar);
break;
}
}
/* = = = = = = = = = = = = = = = = */
static void chart_draw_part_static(cairo_t *cr, GtkChart *chart, HbtkDrawContext *drawctx)
{
PangoLayout *layout;
guint n_legend;
int tw, th;
/*debug help draws */
#if DBGDRAW_RECT == 1
//clip area
cairo_set_line_width(cr, 1.0);
cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); //green
cairo_rectangle(cr, drawctx->l+0.5, drawctx->t+0.5, drawctx->w, drawctx->h);
cairo_stroke(cr);
//graph area
cairo_set_source_rgb(cr, 1.0, 0.5, 0.0); //orange
cairo_rectangle(cr, drawctx->graph.x+0.5, drawctx->graph.y+0.5, drawctx->graph.width, drawctx->graph.height);
cairo_stroke(cr);
#endif
layout = pango_cairo_create_layout (cr);
if(!drawctx->isprint)
cairo_user_set_rgbcol(cr, &global_colors[THTEXT]);
else
cairo_user_set_rgbcol(cr, &global_colors[BLACK]);
// draw title
if(chart->title)
{
chart_set_font_size(chart, layout, CHART_FONT_SIZE_TITLE);
pango_layout_set_text (layout, chart->title, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, drawctx->l, drawctx->t);
pango_cairo_show_layout (cr, layout);
#if DBGDRAW_TEXT == 1
double dashlength;
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); //blue
dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, chart->l, chart->t);
cairo_rectangle(cr, drawctx->l+0.5, drawctx->t+0.5, (tw / PANGO_SCALE), (th / PANGO_SCALE));
cairo_stroke(cr);
#endif
}
// draw subtitle
if(chart->subtitle)
{
chart_set_font_size(chart, layout, CHART_FONT_SIZE_SUBTITLE);
pango_layout_set_text (layout, chart->subtitle, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, drawctx->l, drawctx->subtitle_y);
pango_cairo_show_layout (cr, layout);
#if DBGDRAW_TEXT == 1
double dashlength;
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); //blue
dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, chart->l, chart->t);
cairo_rectangle(cr, drawctx->l+0.5, drawctx->subtitle_y+0.5, (tw / PANGO_SCALE), (th / PANGO_SCALE));
cairo_stroke(cr);
#endif
}
g_object_unref (layout);
// draw legend
n_legend = chart->items->len;
if(chart->show_legend && chart->legend_visible )
{
guint i;
gchar *valstr;
gint x, y;
gint radius;
gint color;
DBL( g_print("\n[chart] draw legend\n") );
layout = pango_cairo_create_layout (cr);
chart_set_font_size(chart, layout, CHART_FONT_SIZE_SMALL);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
x = drawctx->legend.x;
y = drawctx->legend.y;
radius = drawctx->legend_font_h;
#if DBGDRAW_RECT == 1
double dashlength;
cairo_set_source_rgb(cr, 1.0, 0.5, 0.0); //orange
dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, x, y);
cairo_rectangle(cr, drawctx->legend.x+0.5, drawctx->legend.y+0.5, drawctx->legend.width, drawctx->legend.height);
cairo_stroke(cr);
#endif
for(i=0; i< n_legend ;i++)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, i);
ChartItem *item = chart_chartitem_get(chart, i);
if(item)
{
DBL( g_print(" draw %2d of (%d) '%s' y=%d\n", i, n_legend, item->label, y) );
#if DBGDRAW_TEXT == 1
double dashlength;
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); //blue
dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, x, y);
cairo_rectangle(cr, x+0.5, y+0.5, drawctx->legend_font_h, drawctx->legend_font_h);
cairo_stroke(cr);
cairo_rectangle(cr, x+drawctx->legend_font_h + CHART_SPACING+0.5, y+0.5, drawctx->legend_label_w, drawctx->legend_font_h);
cairo_stroke(cr);
#endif
// check if enough height to draw
if( chart->nb_items - i > 1 )
{
if( (y + floor(2 * radius * CHART_LINE_SPACING)) > (drawctx->t + drawctx->h) )
{
DBL( g_print(" print ...\n\n") );
pango_layout_set_text (layout, "...", -1);
cairo_move_to(cr, x + radius + CHART_SPACING, y);
pango_cairo_show_layout (cr, layout);
break;
}
}
// 1: palette
cairo_arc(cr, x + (radius/2), y + (radius/2), (radius/2), 0, 2 * M_PI);
color = i % chart->color_scheme.nb_cols;
cairo_user_set_rgbcol(cr, &chart->color_scheme.colors[color]);
cairo_fill(cr);
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.78);
// 2: label
valstr = item->label;
pango_layout_set_text (layout, valstr, -1);
pango_layout_set_width(layout, drawctx->legend_label_w * PANGO_SCALE);
cairo_move_to(cr, x + drawctx->legend_font_h + CHART_SPACING, y);
pango_cairo_show_layout (cr, layout);
if( chart->show_legend_wide && chart->legend_wide_visible )
{
pango_layout_set_width(layout, -1);
#if DBGDRAW_TEXT == 1
// 3: value
double dashlength;
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); //blue
dashlength = 3;
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, x, y);
cairo_rectangle(cr, x + drawctx->legend_font_h + drawctx->legend_label_w + (CHART_SPACING*2), y+0.5, drawctx->legend_value_w, drawctx->legend_font_h);
cairo_stroke(cr);
// 4: rate
cairo_set_dash (cr, &dashlength, 1, 0);
//cairo_move_to(cr, x, y);
cairo_rectangle(cr, x + drawctx->legend_font_h + drawctx->legend_label_w + drawctx->legend_value_w + (CHART_SPACING*3), y+0.5, drawctx->legend_rate_w, drawctx->legend_font_h);
cairo_stroke(cr);
#endif
if(!drawctx->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.78);
// 3: value
valstr = chart_print_double(chart, chart->buffer1, item->serie1);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, x + drawctx->legend_font_h + drawctx->legend_label_w + (CHART_SPACING*2) + drawctx->legend_value_w - (tw/PANGO_SCALE), y);
pango_cairo_show_layout (cr, layout);
// 4: rate
valstr = chart_print_rate(chart, item->rate);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, x + drawctx->legend_font_h + drawctx->legend_label_w + drawctx->legend_value_w + drawctx->legend_rate_w + (CHART_SPACING*3) - (tw/PANGO_SCALE), y);
pango_cairo_show_layout (cr, layout);
}
//the radius contains the font height here
//y += floor(chart->font_h * CHART_LINE_SPACING);
y += floor(radius * CHART_LINE_SPACING);
}
}
g_object_unref (layout);
}
}
static gboolean drawarea_full_redraw(GtkWidget *widget, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
HbtkDrawContext *drawctx = &chart->context;
GtkAllocation allocation;
cairo_t *cr;
DBG( g_print("\n[gtkchart] drawarea full redraw\n") );
cr = cairo_create (chart->surface);
gtk_widget_get_allocation (GTK_WIDGET (widget), &allocation);
GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (widget));
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
gtk_render_background (context, cr, 0.0, 0.0, allocation.width, allocation.height);
gtk_style_context_restore (context);
if( (chart->type == CHART_TYPE_NONE) || (chart->nb_items == 0) )
goto end;
chart_draw_part_static(cr, chart, drawctx);
switch(chart->type)
{
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
{
cairo_t *crs;
crs = cairo_create (chart->surface);
colchart_draw_scale(crs, chart, drawctx);
cairo_destroy(crs);
}
break;
}
end:
cairo_destroy(cr);
return TRUE;
}
//TODO: we should not rely on this, but get the color in realtime
// based on the widget state (to support backdrop & disable state)
static void
drawarea_get_style(GtkWidget *widget, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
GtkStyleContext *context;
PangoFontDescription *desc;
gboolean colfound;
GdkRGBA color;
DB( g_print("\n[gtkchart] drawarea get style \n") );
context = gtk_widget_get_style_context (widget);
chart_color_global_default();
// get base color
colfound = gtk_style_context_lookup_color(context, "theme_base_color", &color);
if(!colfound)
colfound = gtk_style_context_lookup_color(context, "base_color", &color);
if( colfound )
{
struct rgbcol *tcol = &global_colors[THBASE];
tcol->r = color.red * 255;
tcol->g = color.green * 255;
tcol->b = color.blue * 255;
DB( g_print(" theme base col: %x %x %x\n", tcol->r, tcol->g, tcol->b) );
}
//get text color
colfound = gtk_style_context_lookup_color(context, "theme_text_color", &color);
if(!colfound)
//#1916932 colfound was not assigned
colfound = gtk_style_context_lookup_color(context, "text_color", &color);
if( colfound )
{
struct rgbcol *tcol = &global_colors[THTEXT];
tcol->r = color.red * 255;
tcol->g = color.green * 255;
tcol->b = color.blue * 255;
DB( g_print(" theme text (bg) col: %x %x %x\n", tcol->r, tcol->g, tcol->b) );
}
//commented 5.1.5
//drawarea_full_redraw(widget, user_data);
/* get and copy the font */
gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, "font", &desc, NULL);
if(chart->pfd)
{
pango_font_description_free (chart->pfd);
chart->pfd = NULL;
}
chart->pfd = pango_font_description_copy(desc);
//#1919063 overwrite pango size with device unit taken from css
GValue value = G_VALUE_INIT;
gtk_style_context_get_property(context, "font-size", gtk_style_context_get_state(context), &value);
DB( g_print(" font-size is: %f\n", g_value_get_double(&value) ) );
pango_font_description_set_absolute_size (chart->pfd, (gint)g_value_get_double(&value)*PANGO_SCALE);
g_value_unset(&value);
}
static gboolean
drawarea_configure_event_callback (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
GtkAllocation allocation;
DB( g_print("\n[gtkchart] drawarea configure \n") );
gtk_widget_get_allocation (widget, &allocation);
DB( g_print(" w=%d h=%d\n", allocation.width, allocation.height) );
if (chart->surface)
cairo_surface_destroy (chart->surface);
chart->surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
CAIRO_CONTENT_COLOR,
allocation.width,
allocation.height);
if( gtk_widget_get_realized(widget))
{
chart_recompute(chart);
drawarea_full_redraw(widget, chart);
}
/* We've handled the configure event, no need for further processing. */
return TRUE;
}
static void drawarea_realize_callback(GtkWidget *widget, gpointer user_data)
{
//GtkChart *chart = GTK_CHART(user_data);
DB( g_print("\n[gtkchart] drawarea realize\n") );
DB( g_print(" user_data=%p\n", user_data) );
//chart_recompute(chart);
}
static void drawarea_state_changed_callback(GtkWidget *widget, GtkStateFlags flags, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
DB( g_print("\n[gtkchart] drawarea state_changed\n") );
DB( g_print(" user_data=%p\n", user_data) );
drawarea_get_style(widget, chart);
drawarea_full_redraw(widget, chart);
gtk_widget_queue_draw( widget );
}
static void drawarea_style_updated_callback(GtkWidget *widget, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
DB( g_print("\n[gtkchart] drawarea style updated\n") );
DB( g_print(" user_data=%p\n", user_data) );
drawarea_get_style(widget, chart);
drawarea_full_redraw(widget, chart);
gtk_widget_queue_draw( widget );
}
static gboolean drawarea_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
if( !gtk_widget_get_realized(widget) || chart->surface == NULL )
return FALSE;
DB( g_print("\n[gtkchart] drawarea draw callback\n") );
DB( g_print(" state flags: %d\n", gtk_widget_get_state_flags(chart->drawarea)) );
cairo_set_source_surface (cr, chart->surface, 0, 0);
cairo_paint (cr);
//5.7 secure
if( chart->nb_items == 0)
{
DB( g_print(" not item to draw !\n" ) );
goto end;
}
switch(chart->type)
{
case CHART_TYPE_COL:
colchart_draw_bars(cr, chart, &chart->context);
break;
case CHART_TYPE_LINE:
linechart_draw_lines(cr, chart, &chart->context);
break;
case CHART_TYPE_PIE:
piechart_draw_slices(cr, chart, &chart->context);
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
colchart_draw_stacks(cr, chart, &chart->context);
break;
}
end:
// event handled
return TRUE;
}
static gboolean drawarea_querytooltip_callback(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
gchar *strval, *strval2;
gchar *buffer = NULL;
gboolean retval = FALSE;
if(chart->surface == NULL)
return FALSE;
DBT( g_print("\n[gtkchart] drawarea querytooltip\n") );
DBT( g_print(" x=%d, y=%d kbm=%d\n", x, y, keyboard_mode) );
if(chart->lasthover != chart->hover)
{
goto end;
}
if(chart->hover >= 0)
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, chart->hover);
ChartItem *item = chart_chartitem_get(chart, chart->hover);
if( item )
{
gint colid = chart->colhover;
DBT( g_print("\n rowid=%d colid=%d drill=%d\n", chart->hover, colid, chart->drillable) );
if( chart->type != CHART_TYPE_STACK && chart->type != CHART_TYPE_STACK100 )
{
strval = chart_print_double(chart, chart->buffer1, item->serie1);
if( !chart->dual )
{
//#1420495 don't use g_markup_printf_escaped
if( chart->type == CHART_TYPE_PIE )
buffer = g_strdup_printf("%s\n%s\n%.2f%%", item->label, strval, item->rate);
else
buffer = g_strdup_printf("%s\n%s", item->label, strval);
}
else
{
strval2 = chart_print_double(chart, chart->buffer2, item->serie2);
buffer = g_strdup_printf("%s\n+%s\n%s", item->label, strval2, strval);
}
}
else
{
GtkTreeIter iter;
DataRow *dr;
gdouble value, rate = 0.0;
if( colid > -1 )
{
GtkTreePath *path;
gboolean valid;
if( chart->colindice < 0 )
path = gtk_tree_path_new_from_indices(chart->hover, -1);
else
path = gtk_tree_path_new_from_indices(chart->colindice, chart->hover, -1);
valid = gtk_tree_model_get_iter(chart->model, &iter, path);
gtk_tree_path_free(path);
DBT( g_print("\n get iter %d:%d valid=%d\n", chart->colindice, chart->hover, valid) );
if( valid )
{
gtk_tree_model_get (GTK_TREE_MODEL(chart->model), &iter,
//LST_STORE_TRENDROW=6
LST_REPORT2_ROW, &dr,
-1);
DBT( g_print(" row=%p\n", dr) );
value = da_datarow_get_cell_sum (dr, colid);
strval = chart_print_double(chart, chart->buffer1, value);
if( chart->show_mono == TRUE )
buffer = g_strdup_printf("%s\n%s", chart->collabel[colid], strval);
else
{
if( chart->colsum[colid] > 0 )
rate = (value * 100) / chart->colsum[colid];
buffer = g_strdup_printf("%s\n%s\n%s\n%.2f%%", chart->collabel[colid], item->label, strval, rate);
}
DBT( g_print(" colid=%d value=%.2f sum=%.2f rate=%.2f%%\n", colid, value, chart->colsum[colid], rate) );
}
}
}
}
gtk_tooltip_set_text(tooltip, buffer);
//gtk_label_set_markup(GTK_LABEL(chart->ttlabel), buffer);
g_free(buffer);
retval = TRUE;
}
end:
chart->lasthover = chart->hover;
return retval;
}
static gboolean
drawarea_cb_root_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
DB( g_print("\n[chart] pie root breadcrumb clicked\n") );
chart_clear_items(chart);
switch( chart->type )
{
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
case CHART_TYPE_PIE:
chart_setup_with_model(chart, -1);
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
chart_data_series(chart, -1);
break;
}
gtk_chart_queue_redraw(chart);
return TRUE;
}
static gboolean
drawarea_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
guint button = 0;
if (chart->surface == NULL)
return FALSE; /* paranoia check, in case we haven't gotten a configure event */
DBDT( g_print("\n[gtkchart] mouse button press event\n") );
gdk_event_get_button(event, &button);
if (button == GDK_BUTTON_PRIMARY)
{
switch( chart->type )
{
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
case CHART_TYPE_PIE:
if( chart->hover >= 0 )
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, chart->hover);
ChartItem *item = chart_chartitem_get(chart, chart->hover);
if( item && item->n_child > 0 )
{
DBDT( g_print(" should init total with indice %d\n", chart->hover) );
chart_clear_items(chart);
chart_setup_with_model(chart, chart->hover);
gtk_chart_queue_redraw(chart);
}
}
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
if( chart->hover >= 0 )
{
//ChartItem *item = &g_array_index(chart->items, ChartItem, chart->hover);
ChartItem *item = chart_chartitem_get(chart, chart->hover);
if( item && item->n_child > 0 )
{
DBDT( g_print(" should init time with indice %d\n", chart->hover) );
chart_clear_items(chart);
chart_data_series(chart, chart->hover);
gtk_chart_queue_redraw(chart);
}
}
break;
}
}
/* We've handled the event, stop processing */
return TRUE;
}
static gboolean drawarea_motionnotifyevent_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
GtkChart *chart = GTK_CHART(user_data);
HbtkDrawContext *drawctx = &chart->context;
gboolean retval = TRUE;
gdouble x_win, y_win;
gint x, y;
if(chart->surface == NULL || chart->nb_items == 0)
return FALSE;
DBD( g_print("\n[gtkchart] drawarea motionnotifyevent\n") );
gdk_event_get_coords(event, &x_win, &y_win);
x = x_win;
y = y_win;
//DBD( g_print(" x=%d, y=%d\n", x, y) );
chart->hover = -1;
chart->colhover = -1;
chart->drillable = FALSE;
switch(chart->type)
{
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
chart->hover = colchart_get_hover(widget, x, y, chart);
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
chart->hover = colchart_stack_get_hover(widget, x, y, chart);
break;
case CHART_TYPE_PIE:
chart->hover = piechart_get_hover(widget, x, y, chart);
break;
}
//test: eval legend
if( chart->type != CHART_TYPE_STACK && chart->type != CHART_TYPE_STACK100 )
{
if( chart->show_legend && chart->hover == - 1)
{
DBD( g_print(" hover legend\n") );
if( x >= drawctx->legend.x && (x <= (drawctx->legend.x+drawctx->legend.width ))
&& y >= drawctx->legend.y && (y <= (drawctx->legend.y+drawctx->legend.height ))
)
{
//use the radius a font height here
chart->hover = (y - drawctx->legend.y) / floor(drawctx->legend_font_h * CHART_LINE_SPACING);
DBD( g_print(" hover is %d\n", chart->hover) );
if( chart->hover > chart->nb_items - 1)
{
chart->hover = -1;
}
else
{
//TODO
//ChartItem *item = &g_array_index(chart->items, ChartItem, chart->hover);
ChartItem *item = chart_chartitem_get(chart, chart->hover);
if( item )
{
DBD( g_print(" hover is '%s'\n", item->label) );
if( item->n_child > 0 )
chart->drillable = TRUE;
}
}
}
}
}
//5.7 cursor change
{
GdkWindow *gdkwindow;
GdkCursor *cursor;
gdkwindow = gtk_widget_get_window (GTK_WIDGET(widget));
cursor = gdk_cursor_new_for_display(gdk_window_get_display(gdkwindow), (chart->drillable == TRUE) ? GDK_HAND2 : GDK_ARROW );
gdk_window_set_cursor (gdkwindow, cursor);
if(GDK_IS_CURSOR(cursor))
g_object_unref(cursor);
}
// rollover redraw ?
DBD( g_print(" hover: last=%d, curr=%d\n", chart->lasthover, chart->hover) );
if( (chart->lasthover != chart->hover) || (chart->lastcolhover != chart->colhover) )
{
GdkRectangle update_rect;
gint first;
DBD( g_print(" motion rollover redraw :: hover=%d\n", chart->hover) );
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
switch( chart->type )
{
case CHART_TYPE_PIE:
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
//invalidate all graph area
update_rect.x = drawctx->graph.x;
update_rect.y = drawctx->graph.y;
update_rect.width = drawctx->graph.width;
update_rect.height = drawctx->graph.height;
/* Now invalidate the affected region of the drawing area. */
gdk_window_invalidate_rect (gtk_widget_get_window (widget), &update_rect, FALSE);
break;
case CHART_TYPE_COL:
case CHART_TYPE_LINE:
//invalidate last rollover block
if(chart->lasthover != -1)
{
update_rect.x = drawctx->graph.x + (chart->lasthover - first) * drawctx->blkw;
update_rect.y = drawctx->graph.y - 6;
update_rect.width = drawctx->blkw;
update_rect.height = drawctx->graph.height + 12;
// Now invalidate the affected region of the drawing area
gdk_window_invalidate_rect (gtk_widget_get_window (widget), &update_rect, FALSE);
}
//current item block
update_rect.x = drawctx->graph.x + (chart->hover - first) * drawctx->blkw;
update_rect.y = drawctx->graph.y - 6;
update_rect.width = drawctx->blkw;
update_rect.height = drawctx->graph.height + 12;
/* Now invalidate the affected region of the drawing area. */
gdk_window_invalidate_rect (gtk_widget_get_window (widget), &update_rect, FALSE);
break;
}
}
DBD( g_print(" x=%d, y=%d, time=%d\n", x, y, gdk_event_get_time(event)) );
//if(inlegend != TRUE)
// gtk_tooltip_trigger_tooltip_query(gtk_widget_get_display(chart->drawarea));
chart->lastcolhover = chart->colhover;
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* public functions */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, gpointer user_data)
{
GtkChartPrintData *data = user_data;
HbtkDrawContext *drawctx;
cairo_t *cr;
gdouble t, b, l, r, w, h;
if(data->chart->type == CHART_TYPE_NONE )
return;
gtk_print_context_get_hard_margins(context, &t, &b, &l, &r);
w = gtk_print_context_get_width(context);
h = gtk_print_context_get_height(context);
//TODO: test keeping a ratio and handle orientation myself
/*
settings = gtk_print_operation_get_print_settings(operation);
ratio = (height < width) ? width/height : height/width;
DB( g_print(" orientation: %d\n", gtk_print_settings_get_orientation(settings)) );
DB( g_print(" w=%g h=%g // ratio %g\n", width, height, ratio) );
if( height < width )
height = width * ratio;
*/
//setup our context
drawctx = &data->drawctx;
drawctx->isprint = TRUE;
drawctx->l = l;
drawctx->t = t;
drawctx->w = w;
drawctx->h = h;
cr = gtk_print_context_get_cairo_context (context);
cairo_rectangle (cr, l, t, w - r, h - b);
cairo_clip(cr);
cairo_set_line_width (cr, 1);
//draw debug rectangle
/*
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); //red
cairo_rectangle(cr, 0, 0, width-r, height-b);
cairo_stroke(cr);
*/
//save usrbarw
gint tmpusrbar = data->chart->usrbarw;
//hack it to -1
data->chart->usrbarw = 0.0; // to span
chart_layout_area(cr, data->chart, drawctx);
chart_draw_part_static(cr, data->chart, drawctx);
if(data->chart->type != CHART_TYPE_PIE)
colchart_draw_scale(cr, data->chart, drawctx);
switch(data->chart->type)
{
case CHART_TYPE_COL:
colchart_draw_bars(cr, data->chart, drawctx);
break;
case CHART_TYPE_PIE:
piechart_draw_slices(cr, data->chart, drawctx);
break;
case CHART_TYPE_LINE:
linechart_draw_lines(cr, data->chart, drawctx);
break;
case CHART_TYPE_STACK:
case CHART_TYPE_STACK100:
colchart_draw_stacks(cr, data->chart, drawctx);
break;
}
//restore usrbarw
data->chart->usrbarw = tmpusrbar;
}
void gtk_chart_print(GtkChart *chart, GtkWindow *parent, gchar *dirname, gchar *filename)
{
GtkChartPrintData *data;
GtkPrintOperation *operation;
GtkPrintSettings *settings;
gchar *ext, *uri = NULL;
GError *error = NULL;
g_return_if_fail (GTK_IS_CHART (chart));
data = g_new0 (GtkChartPrintData, 1);
data->chart = chart;
settings = gtk_print_settings_new ();
//TODO: this doesn't work for unknown reason...
gtk_print_settings_set_orientation(settings, GTK_PAGE_ORIENTATION_LANDSCAPE);
if( dirname != NULL && filename != NULL )
{
if (g_strcmp0 (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT), "ps") == 0)
ext = ".ps";
else if (g_strcmp0 (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT), "svg") == 0)
ext = ".svg";
else
ext = ".pdf";
uri = g_strconcat ("file://", dirname, "/", filename, ext, NULL);
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
}
operation = gtk_print_operation_new ();
gtk_print_operation_set_n_pages (operation, 1);
gtk_print_operation_set_use_full_page (operation, FALSE);
gtk_print_operation_set_unit (operation, GTK_UNIT_POINTS);
gtk_print_operation_set_embed_page_setup (operation, TRUE);
gtk_print_operation_set_print_settings (operation, settings);
GtkPageSetup *ps = gtk_page_setup_new();
if( ps )
gtk_page_setup_set_orientation(ps, GTK_PAGE_ORIENTATION_LANDSCAPE);
else
g_print("pagesetup fail\n");
gtk_print_operation_set_default_page_setup(operation, ps);
//g_signal_connect (G_OBJECT (operation), "begin-print", G_CALLBACK (begin_print), data);
g_signal_connect (G_OBJECT (operation), "draw-page", G_CALLBACK (draw_page), data);
//g_signal_connect (G_OBJECT (operation), "end-print", G_CALLBACK (end_print), data);
gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW (parent), &error);
//to use with GTK_PRINT_OPERATION_ACTION_EXPORT
//gtk_print_operation_set_export_filename(operation, "/home/max/Desktop/testpdffile.pdf");
//gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_EXPORT, GTK_WINDOW (window), &error);
g_object_unref (operation);
g_object_unref (settings);
g_free (uri);
if (error)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", error->message);
g_error_free (error);
g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
gtk_widget_show (dialog);
}
g_free(data);
}
void gtk_chart_queue_redraw(GtkChart *chart)
{
DB( g_print("\n[gtkchart] queue redraw\n") );
if( gtk_widget_get_realized(chart->drawarea) )
{
chart_recompute(chart);
drawarea_full_redraw(chart->drawarea, chart);
DB( g_print(" gtk_widget_queue_draw\n") );
gtk_widget_queue_draw( chart->drawarea );
}
}
/*
** empty the chart
*/
void gtk_chart_set_datas_none(GtkChart *chart)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set datas none\n") );
chart_clear(chart);
gtk_chart_queue_redraw(chart);
}
/*
** change the model and/or column
** set column1 == column2 will dual display
*/
void gtk_chart_set_datas_total(GtkChart *chart, GtkTreeModel *model, guint column1, guint column2, gchar *title, gchar *subtitle)
{
g_return_if_fail (GTK_IS_CHART (chart));
DBDT( g_print("\n[gtkchart] set datas total\n") );
chart_clear(chart);
if( GTK_IS_TREE_MODEL(model) )
{
DBDT( g_print(" store model %p and columns=%d:%d\n", model, column1, column2) );
chart->totmodel = model;
chart->column1 = column1;
chart->column2 = column2;
if(title != NULL)
chart->title = g_strdup(title);
if(subtitle != NULL)
chart->subtitle = g_strdup(subtitle);
chart_setup_with_model(chart, -1);
gtk_chart_queue_redraw(chart);
}
}
void gtk_chart_set_datas_time(GtkChart *chart, GtkTreeView *treeview, DataTable *dt, guint nbrows, guint nbcols, gchar *title, gchar *subtitle)
{
GtkTreeModel *model;
guint colid;
g_return_if_fail (GTK_IS_CHART (chart));
DBDT( g_print("\n[gtkchart] set datas time\n") );
chart_clear(chart);
model = gtk_tree_view_get_model(treeview);
//#2039493 ensure datatable is set
if( (dt != NULL) && (nbrows > 0) && (nbcols > 0) && GTK_IS_TREE_MODEL(model) )
{
DBDT( g_print(" store model %p and n_cols=%d\n", model, nbcols) );
chart->model = model;
chart->nb_cols = nbcols;
if(title != NULL)
chart->title = g_strdup(title);
if(subtitle != NULL)
chart->subtitle = g_strdup(subtitle);
DBDT( g_print(" store columns x scale\n") );
chart->cols = dt->cols;
//5.7 useless
DBDT( g_print(" store column labels\n") );
chart->collabel = g_malloc0(sizeof(gpointer)*nbcols);
if( chart->collabel != NULL )
{
DBDT( g_print(" collabel: ") );
for(colid=0;colidcollabel[colid] = (gchar *)gtk_tree_view_column_get_title(column);
DBDT( g_print("%s|", chart->collabel[colid]) );
}
}
DBDT( g_print("\n") );
}
chart_data_series(chart, -1);
gtk_chart_queue_redraw(chart);
}
}
/*
** change the type dynamically
*/
void gtk_chart_set_type(GtkChart * chart, gint type)
{
g_return_if_fail (GTK_IS_CHART (chart));
//g_return_if_fail (type < CHART_TYPE_MAX);
DB( g_print("\n[gtkchart] set type %d\n", type) );
chart->type = type;
chart->dual = FALSE;
if( type == CHART_TYPE_STACK || type == CHART_TYPE_STACK100 )
{
chart_clear_items(chart);
chart_data_series(chart, -1);
}
gtk_chart_queue_redraw(chart);
}
/* = = = = = = = = = = parameters = = = = = = = = = = */
void gtk_chart_set_color_scheme(GtkChart * chart, gint index)
{
colorscheme_init(&chart->color_scheme, index);
}
/*
** set the minor parameters
*/
void gtk_chart_set_minor_prefs(GtkChart * chart, gdouble rate, gchar *symbol)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set minor prefs\n") );
chart->minor_rate = rate;
chart->minor_symbol = symbol;
}
void gtk_chart_set_absolute(GtkChart * chart, gboolean abs)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set absolute\n") );
chart->abs = abs;
}
void gtk_chart_set_currency(GtkChart * chart, guint32 kcur)
{
g_return_if_fail (GTK_IS_CHART (chart));
chart->kcur = kcur;
}
/*
** set the overdrawn minimum
*/
void gtk_chart_set_overdrawn(GtkChart * chart, gdouble minimum)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set overdrawn\n") );
chart->minimum = minimum;
//if(chart->type == CHART_TYPE_LINE)
// chart_recompute(chart);
}
/*
** set the barw
*/
void gtk_chart_set_barw(GtkChart * chart, gdouble barw)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set barw\n") );
if( barw >= GTK_CHART_MINBARW && barw <= GTK_CHART_MAXBARW )
chart->usrbarw = barw;
else
chart->usrbarw = 0;
if(chart->type != CHART_TYPE_PIE)
gtk_chart_queue_redraw(chart);
}
/*
** set the show mono (colors)
*/
void gtk_chart_set_showmono(GtkChart * chart, gboolean mono)
{
g_return_if_fail (GTK_IS_CHART (chart));
chart->show_mono = mono;
//if(chart->type != CHART_TYPE_PIE)
// gtk_chart_queue_redraw(chart);
}
void gtk_chart_set_smallfont(GtkChart * chart, gboolean small)
{
g_return_if_fail (GTK_IS_CHART (chart));
chart->smallfont = small;
//if(chart->type != CHART_TYPE_PIE)
// gtk_chart_queue_redraw(chart);
}
/* = = = = = = = = = = visibility = = = = = = = = = = */
/*
** change the legend visibility
*/
void gtk_chart_show_legend(GtkChart * chart, gboolean visible, gboolean showextracol)
{
g_return_if_fail (GTK_IS_CHART (chart));
chart->show_legend = visible;
chart->show_legend_wide = showextracol;
//gtk_chart_queue_redraw(chart);
}
/*
** change the x-value visibility
*/
void gtk_chart_show_xval(GtkChart * chart, gboolean visible)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set show xval\n") );
chart->show_xval = visible;
//if(chart->type != CHART_TYPE_PIE)
// gtk_chart_queue_redraw(chart);
}
void gtk_chart_show_average(GtkChart * chart, gdouble value, gboolean visible)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set show average %f\n", value) );
chart->average = value;
chart->show_average = visible;
//if(chart->type == CHART_TYPE_LINE)
// chart_recompute(chart);
}
/*
** change the overdrawn visibility
*/
void gtk_chart_show_overdrawn(GtkChart * chart, gboolean visible)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set show overdrawn\n") );
chart->show_over = visible;
//if(chart->type == CHART_TYPE_LINE)
// chart_recompute(chart);
}
/*
** change the minor visibility
*/
void gtk_chart_show_minor(GtkChart * chart, gboolean minor)
{
g_return_if_fail (GTK_IS_CHART (chart));
DB( g_print("\n[gtkchart] set show minor\n") );
chart->minor = minor;
//if(chart->type != CHART_TYPE_PIE)
// gtk_chart_queue_redraw(chart);
}
/* = = = = = = = = = = = = = = = = */
static void
gtk_chart_dispose (GObject *gobject)
{
//GtkChart *chart = GTK_CHART (object);
DB( g_print("\n[gtkchart] dispose\n") );
/* In dispose(), you are supposed to free all types referenced from this
* object which might themselves hold a reference to self. Generally,
* the most simple solution is to unref all members on which you own a
* reference.
*/
/* dispose() might be called multiple times, so we must guard against
* calling g_object_unref() on an invalid GObject by setting the member
* NULL; g_clear_object() does this for us, atomically.
*/
//g_clear_object (&self->priv->an_object);
/* Always chain up to the parent class; there is no need to check if
* the parent class implements the dispose() virtual function: it is
* always guaranteed to do so
*/
G_OBJECT_CLASS (gtk_chart_parent_class)->dispose (gobject);
}
static void
gtk_chart_init (GtkChart * chart)
{
GtkWidget *widget, *vbox, *frame, *overlay, *label;
DB( g_print("\n[gtkchart] init\n") );
chart->surface = NULL;
chart->items = NULL;
chart->title = NULL;
//TODO temp test
chart->colsum = NULL;
chart->collabel = NULL;
chart->pfd = NULL;
chart->abs = FALSE;
chart->dual = FALSE;
chart->usrbarw = 0.0;
chart->show_legend = TRUE;
chart->show_legend_wide = FALSE;
chart->show_mono = FALSE;
chart->hover = -1;
chart->lasthover = -1;
chart->colhover = -1;
chart->lastcolhover = -1;
chart->minor_rate = 1.0;
chart->nb_items = 0;
//TODO
//chart->barw = GTK_CHART_BARW;
gtk_chart_set_color_scheme(chart, CHART_COLMAP_HOMEBANK);
widget = GTK_WIDGET(chart);
gtk_box_set_homogeneous(GTK_BOX(widget), FALSE);
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
hbtk_box_prepend (GTK_BOX (widget), frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_frame_set_child(GTK_FRAME(frame), vbox);
overlay = gtk_overlay_new ();
chart->drawarea = gtk_drawing_area_new();
gtk_widget_set_size_request(chart->drawarea, 100, 100 );
#if DYNAMICS == 1
gtk_widget_set_has_tooltip(chart->drawarea, TRUE);
#endif
gtk_widget_show(chart->drawarea);
gtk_overlay_set_child (GTK_OVERLAY(overlay), chart->drawarea);
hbtk_box_prepend (GTK_BOX (vbox), overlay);
//scrollbar
chart->adjustment = GTK_ADJUSTMENT(gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0));
chart->scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (chart->adjustment));
//5.7 add
gtk_style_context_add_class (gtk_widget_get_style_context (chart->scrollbar), GTK_STYLE_CLASS_BOTTOM);
//gtk_style_context_add_class (gtk_widget_get_style_context (chart->scrollbar), "overlay-indicator");
gtk_box_append (GTK_BOX (vbox), chart->scrollbar);
//gtk_widget_show(chart->scrollbar);
//overlay
label = gtk_label_new(NULL);
chart->breadcrumb = label;
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_label_set_track_visited_links(GTK_LABEL(label), FALSE);
gtk_overlay_add_overlay( GTK_OVERLAY(overlay), label );
gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (overlay), label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_widget_set_margin_top(label, SPACING_MEDIUM*4);
#if MYDEBUG == 1
/*GtkStyle *style;
PangoFontDescription *font_desc;
g_print("draw_area font\n");
style = gtk_widget_get_style(GTK_WIDGET(chart->drawarea));
font_desc = style->font_desc;
g_print("family: %s\n", pango_font_description_get_family(font_desc) );
g_print("size: %d (%d)\n", pango_font_description_get_size (font_desc), pango_font_description_get_size (font_desc )/PANGO_SCALE );
*/
#endif
g_signal_connect( G_OBJECT(chart->drawarea), "configure-event", G_CALLBACK (drawarea_configure_event_callback), chart);
g_signal_connect( G_OBJECT(chart->drawarea), "realize", G_CALLBACK(drawarea_realize_callback), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "draw", G_CALLBACK(drawarea_draw_callback), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "state-flags-changed", G_CALLBACK(drawarea_state_changed_callback), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "style-updated", G_CALLBACK(drawarea_style_updated_callback), chart ) ;
#if DYNAMICS == 1
gtk_widget_add_events(GTK_WIDGET(chart->drawarea),
GDK_EXPOSURE_MASK |
GDK_POINTER_MOTION_MASK |
//GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK
//GDK_BUTTON_RELEASE_MASK
);
g_signal_connect( G_OBJECT(chart->drawarea), "query-tooltip", G_CALLBACK(drawarea_querytooltip_callback), chart );
g_signal_connect( G_OBJECT(chart->drawarea), "motion-notify-event", G_CALLBACK(drawarea_motionnotifyevent_callback), chart );
#endif
g_signal_connect (G_OBJECT(chart->adjustment), "value-changed", G_CALLBACK (colchart_first_changed), chart);
g_signal_connect (G_OBJECT(chart->breadcrumb), "activate-link", G_CALLBACK (drawarea_cb_root_activate_link), chart);
//g_signal_connect( G_OBJECT(chart->drawarea), "map-event", G_CALLBACK(chart_map), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "button-press-event", G_CALLBACK(drawarea_button_press_event), chart );
//g_signal_connect( G_OBJECT(chart->drawarea), "button-release-event", G_CALLBACK(chart_button_release), chart );
}
static void
gtk_chart_finalize (GObject * object)
{
GtkChart *chart = GTK_CHART (object);
DB( g_print("\n[gtkchart] finalize\n") );
chart_clear(chart);
if(chart->pfd)
{
pango_font_description_free (chart->pfd);
chart->pfd = NULL;
}
if (chart->surface)
{
cairo_surface_destroy (chart->surface);
chart->surface = NULL;
}
G_OBJECT_CLASS (gtk_chart_parent_class)->finalize (object);
}
static void
gtk_chart_class_init (GtkChartClass * class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
//GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
DB( g_print("\n[gtkchart] class init\n") );
//gobject_class->get_property = gtk_chart_get_property;
//gobject_class->set_property = gtk_chart_set_property;
gobject_class->dispose = gtk_chart_dispose;
gobject_class->finalize = gtk_chart_finalize;
//widget_class->size_allocate = gtk_chart_size_allocate;
}
static void
gtk_chart_class_intern_init (gpointer klass)
{
gtk_chart_parent_class = g_type_class_peek_parent (klass);
gtk_chart_class_init ((GtkChartClass *) klass);
}
GType
gtk_chart_get_type ()
{
static GType chart_type = 0;
if (!chart_type)
{
static const GTypeInfo chart_info =
{
sizeof (GtkChartClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) gtk_chart_class_intern_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GtkChart),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_chart_init,
NULL
};
chart_type = g_type_register_static (GTK_TYPE_BOX, "GtkChart",
&chart_info, 0);
}
return chart_type;
}
GtkWidget *
gtk_chart_new (gint type)
{
GtkChart *chart;
DB( g_print("\n======================================================\n") );
DB( g_print("\n[gtkchart] new\n") );
chart = g_object_new (GTK_TYPE_CHART, NULL);
chart->type = type;
return GTK_WIDGET(chart);
}
homebank-5.9.7/src/hub-scheduled.h 0000664 0001750 0001750 00000002113 14736461415 016300 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "dsp-mainwindow.h"
#ifndef __HUB_SCHEDULED_H__
#define __HUB_SCHEDULED_H__
void ui_hub_scheduled_postall(GtkWidget *widget, gpointer user_data);
void ui_hub_scheduled_populate(GtkWidget *widget, gpointer user_data);
GtkWidget *ui_hub_scheduled_create(struct hbfile_data *data);
#endif
homebank-5.9.7/src/hb-assign.c 0000644 0001750 0001750 00000034722 14736461407 015444 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-assign.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void
da_asg_free(Assign *item)
{
DB( g_print("da_asg_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->search) );
g_free(item->search);
g_free(item->notes);
g_free(item);
}
}
Assign *
da_asg_malloc(void)
{
DB( g_print("da_asg_malloc\n") );
return g_malloc0(sizeof(Assign));
}
void
da_asg_destroy(void)
{
DB( g_print("da_asg_destroy\n") );
g_hash_table_destroy(GLOBALS->h_rul);
}
void
da_asg_new(void)
{
DB( g_print("da_asg_new\n") );
GLOBALS->h_rul = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_asg_free);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void da_asg_max_key_ghfunc(gpointer key, Assign *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
static gboolean da_asg_name_grfunc(gpointer key, Assign *item, gchar *name)
{
if( name && item->search )
{
if(!strcasecmp(name, item->search))
return TRUE;
}
return FALSE;
}
/**
* da_asg_length:
*
* Return value: the number of elements
*/
guint
da_asg_length(void)
{
return g_hash_table_size(GLOBALS->h_rul);
}
/**
* da_asg_remove:
*
* delete an rul from the GHashTable
*
* Return value: TRUE if the key was found and deleted
*
*/
gboolean
da_asg_remove(guint32 key)
{
DB( g_print("da_asg_remove %d\n", key) );
return g_hash_table_remove(GLOBALS->h_rul, &key);
}
//#1889659: ensure name != null/empty
static gboolean
da_asg_ensure_name(Assign *item)
{
if( item->search == NULL || strlen(item->search) == 0 )
{
gint key = item->key > 0 ? item->key : da_asg_get_max_key() + 1;
g_free(item->search);
item->search = g_strdup_printf("no name %d", key);
return TRUE;
}
return FALSE;
}
/**
* da_asg_insert:
*
* insert an rul into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_asg_insert(Assign *item)
{
guint32 *new_key;
DB( g_print("da_asg_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
//#1889659: ensure name != null/empty
da_asg_ensure_name(item);
g_hash_table_insert(GLOBALS->h_rul, new_key, item);
return TRUE;
}
/**
* da_asg_append:
*
* append a new rul into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_asg_append(Assign *item)
{
Assign *existitem;
guint32 *new_key;
DB( g_print("da_asg_append\n") );
DB( g_print(" -> try append: %s\n", item->search) );
if( item->search != NULL )
{
/* ensure no duplicate */
existitem = da_asg_get_by_name( item->search );
if( existitem == NULL )
{
new_key = g_new0(guint32, 1);
*new_key = da_asg_get_max_key() + 1;
item->key = *new_key;
//added 5.3.3
item->pos = da_asg_length() + 1;
DB( g_print(" -> append id: %d\n", *new_key) );
g_hash_table_insert(GLOBALS->h_rul, new_key, item);
return TRUE;
}
DB( g_print(" -> %s already exist: %d\n", item->search, item->key) );
}
DB( g_print(" -> %s search null: %d\n", item->search, item->key) );
return FALSE;
}
Assign *
da_asg_duplicate(Assign *srcitem)
{
Assign *existitem, *newitem = NULL;
gchar *newsearch;
guint32 *new_key;
DB( g_print("da_asg_duplicate\n") );
newsearch = g_strdup_printf("%s %s", srcitem->search, _("(copy)") );
/* ensure no duplicate */
existitem = da_asg_get_by_name( newsearch );
if( existitem == NULL )
{
newitem = da_asg_malloc();
//raw duplicate the memory segment
memcpy(newitem, srcitem, sizeof(Assign));
newitem->search = NULL;
newitem->notes = NULL;
new_key = g_new0(guint32, 1);
*new_key = da_asg_get_max_key() + 1;
newitem->key = *new_key;
//added 5.3.3
newitem->pos = da_asg_length() + 1;
newitem->search = newsearch;
if( srcitem->notes )
newitem->notes = g_strdup(srcitem->notes);
g_hash_table_insert(GLOBALS->h_rul, new_key, newitem);
}
else
{
g_free(newsearch);
}
return newitem;
}
/**
* da_asg_get_max_key:
*
* Get the biggest key from the GHashTable
*
* Return value: the biggest key value
*
*/
guint32
da_asg_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_rul, (GHFunc)da_asg_max_key_ghfunc, &max_key);
return max_key;
}
/**
* da_asg_get_by_name:
*
* Get an rul structure by its name
*
* Return value: rul * or NULL if not found
*
*/
Assign *
da_asg_get_by_name(gchar *name)
{
DB( g_print("da_asg_get_by_name\n") );
return g_hash_table_find(GLOBALS->h_rul, (GHRFunc)da_asg_name_grfunc, name);
}
/**
* da_asg_get:
*
* Get an rul structure by key
*
* Return value: rul * or NULL if not found
*
*/
Assign *
da_asg_get(guint32 key)
{
DB( g_print("da_asg_get_rul\n") );
return g_hash_table_lookup(GLOBALS->h_rul, &key);
}
void da_asg_consistency(Assign *item)
{
//5.2.4 we drop internal xfer here as it will disapear
//was not possible, but just in case
if( item->paymode == OLDPAYMODE_INTXFER )
item->paymode = PAYMODE_XFER;
}
/* = = = = = = = = = = = = = = = = = = = = */
Assign *da_asg_init_from_transaction(Assign *asg, Transaction *txn)
{
DB( g_print("\n[scheduled] init from txn\n") );
//#2018680
//asg->search = g_strdup_printf("%s %s", _("**PREFILLED**"), txn->memo );
//#2037132 ensure memo is not empty
if( txn->memo != NULL )
asg->search = g_strdup( txn->memo );
da_asg_ensure_name(asg);
asg->flags |= ASGF_PREFILLED;
asg->flags |= (ASGF_DOPAY|ASGF_DOCAT|ASGF_DOMOD);
asg->kcat = txn->kcat;
if(!(txn->flags & OF_INTXFER))
{
asg->kpay = txn->kpay;
asg->paymode = txn->paymode;
}
return asg;
}
void da_asg_update_position(void)
{
GList *lrul, *list;
guint32 newpos = 1;
DB( g_print("da_asg_update_position\n") );
lrul = list = assign_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Assign *item = list->data;
item->pos = newpos++;
list = g_list_next(list);
}
g_list_free(lrul);
}
gchar *assign_get_target_payee(Assign *asgitem)
{
gchar *retval = NULL;
if( asgitem && (asgitem->flags & (ASGF_DOPAY|ASGF_OVWPAY)) )
{
Payee *pay = da_pay_get(asgitem->kpay);
if(pay != NULL)
retval = pay->name;
}
return retval;
}
gchar *assign_get_target_category(Assign *asgitem)
{
gchar *retval = NULL;
if( asgitem && (asgitem->flags & (ASGF_DOCAT|ASGF_OVWCAT)) )
{
Category *cat = da_cat_get(asgitem->kcat);
if(cat != NULL)
retval = cat->fullname;
}
return retval;
}
static gint
assign_glist_pos_compare_func(Assign *a, Assign *b)
{
return a->pos - b->pos;
}
static gint
assign_glist_key_compare_func(Assign *a, Assign *b)
{
return a->key - b->key;
}
GList *assign_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_rul);
switch(column)
{
case HB_GLIST_SORT_POS:
return g_list_sort(list, (GCompareFunc)assign_glist_pos_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)assign_glist_key_compare_func);
break;
}
}
static gboolean misc_text_match(gchar *text, gchar *searchtext, gboolean exact)
{
gboolean match = FALSE;
if(text == NULL)
return FALSE;
//DB( g_print("search %s in %s\n", rul->name, ope->memo) );
if( searchtext != NULL )
{
if( exact == TRUE )
{
if( g_strrstr(text, searchtext) != NULL )
{
DB( g_print("-- found case '%s'\n", searchtext) );
match = TRUE;
}
}
else
{
gchar *word = g_utf8_casefold(text, -1);
gchar *needle = g_utf8_casefold(searchtext, -1);
if( g_strrstr(word, needle) != NULL )
{
DB( g_print("-- found nocase '%s'\n", searchtext) );
match = TRUE;
}
g_free(word);
g_free(needle);
}
}
return match;
}
static gboolean misc_regex_match(gchar *text, gchar *searchtext, gboolean exact)
{
gboolean match = FALSE;
if(text == NULL)
return FALSE;
DB( g_print("-- match RE %s in %s\n", searchtext, text) );
if( searchtext != NULL )
{
match = g_regex_match_simple(searchtext, text,
((exact == TRUE)?0:G_REGEX_CASELESS) | G_REGEX_OPTIMIZE,
G_REGEX_MATCH_NOTEMPTY );
if (match == TRUE) { DB( g_print("-- found pattern '%s'\n", searchtext) ); }
}
return match;
}
//#1710085 assignment based on amount
static gboolean transaction_auto_assign_rule_match(Assign *rul, gchar *text, gdouble amount)
{
gboolean match1, match2;
match1 = TRUE;
match2 = FALSE;
if( rul->flags & ASGF_AMOUNT )
{
if( amount != rul->amount )
match1 = FALSE;
}
if( !(rul->flags & ASGF_REGEX) )
{
if( misc_text_match(text, rul->search, rul->flags & ASGF_EXACT) )
match2 = TRUE;
}
else
{
if( misc_regex_match(text, rul->search, rul->flags & ASGF_EXACT) )
match2 = TRUE;
}
return ((match1==TRUE) && (match2==TRUE)) ? TRUE : FALSE;
}
static GList *transaction_auto_assign_eval_txn(GList *l_rul, Transaction *txn)
{
GList *ret_list = NULL;
GList *list;
gchar *text = NULL;
list = g_list_first(l_rul);
while (list != NULL)
{
Assign *rul = list->data;
text = txn->memo;
if(rul->field == 1) //payee
{
Payee *pay = da_pay_get(txn->kpay);
if(pay)
text = pay->name;
}
if( transaction_auto_assign_rule_match(rul, text, txn->amount) == TRUE )
{
//TODO: perf must use preprend, see glib doc
ret_list = g_list_append(ret_list, rul);
}
list = g_list_next(list);
}
DB( g_print("- %d rule(s) match on '%s'\n", g_list_length (ret_list), text) );
return ret_list;
}
static GList *transaction_auto_assign_eval_split(GList *l_rul, gchar *text, gdouble amount)
{
GList *ret_list = NULL;
GList *list;
list = g_list_first(l_rul);
while (list != NULL)
{
Assign *rul = list->data;
if( rul->field == 0 ) //memo
{
if( transaction_auto_assign_rule_match(rul, text, amount) == TRUE )
{
//TODO: perf must use preprend, see glib doc
ret_list = g_list_append(ret_list, rul);
}
}
list = g_list_next(list);
}
DB( g_print("- %d rule(s) match on '%s'\n", g_list_length (ret_list), text) );
return ret_list;
}
guint transaction_auto_assign(GList *ope_list, guint32 kacc, gboolean lockrecon)
{
GList *l_ope;
GList *l_rul;
GList *l_match, *l_tmp;
guint changes = 0;
DB( g_print("\n[transaction] auto_assign\n") );
l_rul = assign_glist_sorted(HB_GLIST_SORT_POS);
l_ope = g_list_first(ope_list);
while (l_ope != NULL)
{
Transaction *ope = l_ope->data;
gboolean changed = FALSE;
//#1909749 skip reconciled if lock is ON
if( lockrecon && ope->status == TXN_STATUS_RECONCILED )
goto next;
DB( g_print("\n- curr txn '%s' : acc=%d, pay=%d, cat=%d, %s\n", ope->memo, ope->kacc, ope->kpay, ope->kcat, (ope->flags & OF_SPLIT) ? "is_split" : "" ) );
//#1215521: added kacc == 0
if( (kacc == ope->kacc || kacc == 0) )
{
Transaction *child = NULL;
//#2092388 also sync child
if( ope->flags & OF_INTXFER )
{
child = transaction_xfer_child_strong_get(ope);
}
if( !(ope->flags & OF_SPLIT) )
{
l_match = l_tmp = transaction_auto_assign_eval_txn(l_rul, ope);
while( l_tmp != NULL )
{
Assign *rul = l_tmp->data;
if( (ope->kpay == 0 && (rul->flags & ASGF_DOPAY)) || (rul->flags & ASGF_OVWPAY) )
{
if(ope->kpay != rul->kpay) { changed = TRUE; }
ope->kpay = rul->kpay;
if(child != NULL)
child->kpay = rul->kpay;
}
if( (ope->kcat == 0 && (rul->flags & ASGF_DOCAT)) || (rul->flags & ASGF_OVWCAT) )
{
if(ope->kcat != rul->kcat) { changed = TRUE; }
ope->kcat = rul->kcat;
if(child != NULL)
child->kcat = rul->kcat;
}
if( (ope->paymode == 0 && (rul->flags & ASGF_DOMOD)) || (rul->flags & ASGF_OVWMOD) )
{
//ugly hack - don't allow modify intxfer
if( !(ope->flags & OF_INTXFER) )
{
if(ope->paymode != rul->paymode) { changed = TRUE; }
ope->paymode = rul->paymode;
}
}
if( (ope->tags == NULL && (rul->flags & ASGF_DOTAG)) || (rul->flags & ASGF_OVWTAG) )
{
if(tags_equal(rul->tags, ope->tags) == FALSE) { changed = TRUE; }
g_free(ope->tags);
ope->tags = tags_clone(rul->tags);
if(child != NULL)
{
g_free(child->tags);
child->tags = tags_clone(rul->tags);
}
}
l_tmp = g_list_next(l_tmp);
}
g_list_free(l_match);
}
else
{
guint i, nbsplit = da_splits_length(ope->splits);
for(i=0;isplits, i);
DB( g_print("- eval split '%s'\n", split->memo) );
l_match = l_tmp = transaction_auto_assign_eval_split(l_rul, split->memo, split->amount);
while( l_tmp != NULL )
{
Assign *rul = l_tmp->data;
//#1501144: check if user wants to set category in rule
if( (split->kcat == 0 || (rul->flags & ASGF_OVWCAT)) && (rul->flags & ASGF_DOCAT) )
{
if(split->kcat != rul->kcat) { changed = TRUE; }
split->kcat = rul->kcat;
}
l_tmp = g_list_next(l_tmp);
}
g_list_free(l_match);
}
}
if(changed == TRUE)
{
ope->dspflags |= FLAG_TMP_EDITED;
changes++;
if( child != NULL )
{
child->dspflags |= FLAG_TMP_EDITED;
changes++;
}
}
}
next:
l_ope = g_list_next(l_ope);
}
g_list_free(l_rul);
return changes;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG
static void
da_asg_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
{
guint32 *id = key;
Assign *item = value;
DB( g_print(" %d :: %s\n", *id, item->search) );
}
static void
da_asg_debug_list(void)
{
DB( g_print("\n** debug **\n") );
g_hash_table_foreach(GLOBALS->h_rul, da_asg_debug_list_ghfunc, NULL);
DB( g_print("\n** end debug **\n") );
}
#endif
homebank-5.9.7/src/list-report.h 0000664 0001750 0001750 00000004632 15001132656 016044 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LIST_REPORT__H__
#define __LIST_REPORT__H__
enum
{
LST_REPORT_POS, //keep for compatibility with chart
LST_REPORT_KEY,
LST_REPORT_LABEL,
//LST_REPORT_ROW,
LST_REPORT_EXPENSE,
LST_REPORT_INCOME,
LST_REPORT_TOTAL,
LST_REPORT_OVERLABEL,
NUM_LST_REPORT
};
#define LST_REPORT_POS_TOTAL G_MAXINT
// --- time stuff ---
//time maximum column (4 years = 365,25 * 4)
#define LST_REP_COLID_MAX 1461
//special column id
#define LST_REP_COLID_POS LST_REP_COLID_MAX + 10
#define LST_REP_COLID_AVERAGE LST_REP_COLID_MAX + 11
#define LST_REP_COLID_TOTAL LST_REP_COLID_MAX + 12
enum {
LST_REPORT2_POS,
LST_REPORT2_KEY,
LST_REPORT2_LABEL,
LST_REPORT2_ROW,
LST_REPORT2_OVERLABEL,
NUM_LST_REPORT2
};
struct lst_report_data
{
GtkWidget *treeview;
// guint intvl;
// guint n_cols;
// DataCol **cols;
gdouble tot_exp;
gdouble tot_inc;
};
GtkTreeStore *lst_report_new(void);
GtkWidget *lst_report_create(void);
void lst_report_add_columns(GtkTreeView *treeview, GtkTreeModel *model);
gboolean lst_report_get_top_level (GtkTreeModel *liststore, guint32 key, GtkTreeIter *return_iter);
GString *lst_report_to_string(ToStringMode mode, GtkTreeView *treeview, gint src, gchar *title);
gboolean lst_rep_time_get_top_level (GtkTreeModel *liststore, guint32 key, GtkTreeIter *return_iter);
GString *lst_rep_time_to_string(ToStringMode mode, GtkTreeView *treeview, gint src, gchar *title);
GtkWidget *lst_rep_time_createtype(GtkListStore *store);
GtkTreeStore *lst_rep_time_new(void);
GtkWidget *lst_rep_time_create(void);
void lst_rep_time_renewcol(GtkTreeView *treeview, GtkTreeModel *model, DataTable *dt, gboolean avg);
#endif
homebank-5.9.7/src/ui-transaction.c 0000644 0001750 0001750 00000167204 15057111416 016520 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "hbtk-decimalentry.h"
#include "ui-transaction.h"
#include "gtk-dateentry.h"
#include "hbtk-switcher.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-account.h"
#include "ui-txn-split.h"
#include "ui-tag.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
//extern HbKvData CYA_TXN_STATUS[];
extern HbKivData CYA_TXN_STATUSIMG[];
extern gchar *CYA_TXN_TYPE[];
static void ui_popover_tpl_populate(struct deftransaction_data *data, GList *srclist);
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void deftransaction_set_amount_currency(Account *acc, GtkWidget *stamount, GtkWidget *lbcurr)
{
guint curdigits = 2;
gchar *curlabel = "";
if( acc != NULL )
{
Currency *cur = da_cur_get(acc->kcur);
curdigits = (cur != NULL) ? cur->frac_digits : 2;
curlabel = strlen(cur->iso_code) == 3 ? cur->iso_code : cur->symbol;
}
//gtk_spin_button_set_digits (GTK_SPIN_BUTTON(stamount), curdigits);
hbtk_decimal_entry_set_digits(HBTK_DECIMAL_ENTRY(stamount), curdigits);
gtk_label_set_label(GTK_LABEL(lbcurr), curlabel);
}
static void deftransaction_set_amount_xfer(struct deftransaction_data *data)
{
Account *srcacc, *dstacc;
gdouble srcamt, dstamt;
gboolean haswarn = FALSE;
if( data->action != TXN_DLG_ACTION_ADD )
goto end;
DB( g_print("\n[ui-transaction] set xfer amount\n") );
srcacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_acc));
dstacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_accto));
if( srcacc != NULL && dstacc != NULL )
{
//srcamt = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
srcamt = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
//return 0 if convertion fail
dstamt = hb_amount_convert(-srcamt, srcacc->kcur, dstacc->kcur);
if( hb_amount_cmp(dstamt, 0.0) == 0 )
haswarn = TRUE;
else
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_xferamt), dstamt);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_xferamt), dstamt);
}
end:
hb_widget_visible(data->IM_xfernorate, haswarn);
}
static void deftransaction_update(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
gint type, paymode;
gboolean sensitive, visible, xferamtvisible;
Account *srcacc, *dstacc;
gchar *lbto;
DB( g_print("\n[ui-transaction] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_type));
paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
//template: hide date
visible = (data->type == TXN_DLG_TYPE_TPL) ? FALSE : TRUE;
hb_widget_visible(data->LB_date, visible);
hb_widget_visible(data->GR_date, visible);
//xfer: hide split+paymode / show accto
visible = (type == TXN_TYPE_INTXFER) ? FALSE : TRUE;
DB( g_print(" is xfer %d: hide split/pay, show accto\n", !visible) );
hb_widget_visible(data->BT_split, visible);
hb_widget_visible(data->LB_mode , visible);
hb_widget_visible(data->NU_mode , visible);
hb_widget_visible(data->LB_accto, !visible);
hb_widget_visible(data->PO_accto, !visible);
//5.8
if( PREFS->xfer_syncdate == FALSE && data->action == TXN_DLG_ACTION_EDIT && data->type == TXN_DLG_TYPE_TXN )
{
hb_widget_visible(data->LB_dateto, !visible);
hb_widget_visible(data->PO_dateto, !visible);
}
//this code is duplicated into paymode
visible = (paymode == PAYMODE_CHECK) ? TRUE : FALSE;
visible = (type == TXN_TYPE_INTXFER) ? FALSE : visible;
hb_widget_visible(data->CM_cheque, visible);
//disable amount+category if split is set
sensitive = (data->ope->flags & (OF_SPLIT)) ? FALSE : TRUE;
DB( g_print(" is plit %d: disable amt/cat\n", !sensitive) );
gtk_widget_set_sensitive(data->RA_type, sensitive);
gtk_widget_set_sensitive(data->ST_amount, sensitive);
gtk_widget_set_sensitive(data->PO_cat, sensitive);
DB( g_print(" action:%d type:%d\n", data->action, data->type) );
//srcacc = ui_acc_comboboxentry_get(GTK_COMBO_BOX(data->PO_acc));
//dstacc = ui_acc_comboboxentry_get(GTK_COMBO_BOX(data->PO_accto));
srcacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_acc));
dstacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_accto));
//#1676162 update the nb digits of amount
//set digits and currency label
deftransaction_set_amount_currency(srcacc, data->ST_amount, data->LB_curr);
lbto = _("_To:");
xferamtvisible = FALSE;
if( type == TXN_TYPE_INTXFER )
{
//gdouble amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
gdouble amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
DB( g_print(" xfer stuff, amt=%.2f\n", amount) );
//5.8 test
lbto = ( amount <= 0.0 ) ? _("_To:") : _("_From:");
//#1673260 show target amount if != kcur
if(srcacc != NULL && dstacc != NULL)
{
xferamtvisible = (srcacc->kcur == dstacc->kcur) ? FALSE : TRUE;
}
//set digits and currency label
deftransaction_set_amount_currency(dstacc, data->ST_xferamt, data->LB_xfercurr);
deftransaction_set_amount_xfer(data);
}
else
hb_widget_visible(data->IM_xfernorate, FALSE);
DB( g_print(" lblto: '%s'\n", lbto ) );
DB( g_print(" show tgt amt %d\n", xferamtvisible ) );
if( PREFS->xfer_syncdate == FALSE && data->action == TXN_DLG_ACTION_EDIT && data->type == TXN_DLG_TYPE_TXN )
{
gtk_label_set_text_with_mnemonic (GTK_LABEL(data->LB_dateto), lbto);
}
gtk_label_set_text_with_mnemonic (GTK_LABEL(data->LB_accto) , lbto);
hb_widget_visible(data->ST_xferamt , xferamtvisible);
hb_widget_visible(data->LB_xfercurr, xferamtvisible);
// item validation
sensitive = FALSE;
if( (data->type == TXN_DLG_TYPE_TPL) )
{
DB( g_print(" add tpl no check\n") );
sensitive = TRUE;
}
else
{
DB( g_print(" eval src/dst acc\n") );
sensitive = ( (srcacc != NULL) && (srcacc->key != 0) ) ? TRUE : FALSE;
if( (type == TXN_TYPE_INTXFER) )
{
sensitive = FALSE;
//#1858682 still disabled > faulty test a copy/paste acc instead of accto...
//todo
if( (srcacc != NULL) && (dstacc != NULL) )
{
if( (dstacc->key != 0)
&& (dstacc->key != srcacc->key)
//#1673260 xfer
//&& (dstacc->kcur == srcacc->kcur)
) { sensitive = TRUE; }
}
}
}
gtk_dialog_set_response_sensitive(GTK_DIALOG (data->dialog), GTK_RESPONSE_ACCEPT, sensitive);
gtk_dialog_set_response_sensitive(GTK_DIALOG (data->dialog), HB_RESPONSE_ADD , sensitive);
gtk_dialog_set_response_sensitive(GTK_DIALOG (data->dialog), HB_RESPONSE_ADDKEEP, sensitive);
}
static void deftransaction_update_warnsign(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
gboolean warning = FALSE;
gdouble amount;
gint txntype, type;
Category *cat;
DB( g_print("\n[ui-transaction] update warning sign\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#1830707 no warning for xfer
txntype = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
//amount = hb_amount_round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount)), 2);
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
type = 0;
if(amount != 0.0)
type = (amount > 0) ? 1 : -1;
if( txntype != TXN_TYPE_INTXFER )
{
//cat = ui_cat_comboboxentry_get(GTK_COMBO_BOX(data->PO_cat));
cat = ui_cat_entry_popover_get(GTK_BOX(data->PO_cat));
if(cat != NULL && cat->key > 0)
warning = (category_type_get(cat) != type) ? TRUE : FALSE;
gtk_label_set_text(GTK_LABEL(data->LB_warnsign), _("Warning: amount and category sign don't match"));
}
//#2101050+#2114680 xfer sign change
else
{
//#2114680 exclude template
if( (data->type != TXN_DLG_TYPE_TPL) && (data->action == TXN_DLG_ACTION_EDIT) )
warning = (data->txnoldtype == type) ? FALSE : TRUE;
gtk_label_set_text(GTK_LABEL(data->LB_warnsign), _("Warning: amount sign don't match"));
}
DB( g_print("> type=%d oldtype=%d > warn=%d\n", type, data->txnoldtype, warning) );
if(warning)
{
gtk_widget_show_all(data->IB_warnsign);
//#GTK+710888: hack waiting a GTK fix
gtk_widget_queue_resize (data->IB_warnsign);
}
else
gtk_widget_hide(data->IB_warnsign);
deftransaction_update(widget, user_data);
}
static void deftransaction_cb_payee_changed(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Category *cat;
gint paymode;
Payee *pay;
DB( g_print("\n[ui-transaction] update payee\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//pay = ui_pay_comboboxentry_get(GTK_COMBO_BOX(data->PO_pay));
pay = ui_pay_entry_popover_get(GTK_BOX(data->PO_pay));
if( pay != NULL )
{
// only set for empty category
// #1635053 and also paymode unset
// #1817278 and independently
//cat = ui_cat_comboboxentry_get(GTK_COMBO_BOX(data->PO_cat));
cat = ui_cat_entry_popover_get(GTK_BOX(data->PO_cat));
if( (cat == NULL || cat->key == 0) )
{
DB( g_print("set cat to %d\n", pay->kcat) );
g_signal_handlers_block_by_func (G_OBJECT (ui_cat_entry_popover_get_entry(GTK_BOX(data->PO_cat))), G_CALLBACK (deftransaction_update_warnsign), NULL);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), pay->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), pay->kcat);
g_signal_handlers_unblock_by_func (G_OBJECT (ui_cat_entry_popover_get_entry(GTK_BOX(data->PO_cat))), G_CALLBACK (deftransaction_update_warnsign), NULL);
}
paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
if( (paymode == PAYMODE_NONE) )
{
DB( g_print("set paymode to %d\n", pay->paymode) );
kiv_combo_box_set_active(GTK_COMBO_BOX(data->NU_mode), pay->paymode);
}
}
}
static void deftransaction_set_cheque(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Account *acc;
guint cheque;
gint type, paymode;
gchar *cheque_str;
DB( g_print("\n[ui-transaction] set_cheque\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->action != TXN_DLG_ACTION_EDIT )
{
paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
if(paymode == PAYMODE_CHECK)
{
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
if( type == TXN_TYPE_EXPENSE )
{
//acc = ui_acc_comboboxentry_get(GTK_COMBO_BOX(data->PO_acc));
acc = ui_acc_entry_popover_get(GTK_BOX(data->PO_acc));
//#1410166
if(acc != NULL)
{
cheque = ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cheque))==TRUE ? acc->cheque2 : acc->cheque1 );
//#1915660 only when cheque number is set
if( cheque > 0 )
{
cheque_str = g_strdup_printf("%d", cheque + 1);
DB( g_print(" - should fill for acc %d '%s' chequenr='%s'\n", acc->key, acc->name, cheque_str) );
gtk_entry_set_text(GTK_ENTRY(data->ST_number), cheque_str);
g_free(cheque_str);
}
}
}
else
if( type == TXN_TYPE_INCOME )
{
gtk_entry_set_text(GTK_ENTRY(data->ST_number), "");
}
}
}
}
static void deftransaction_cb_accfrom_changed(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Account *srcacc;
DB( g_print("\n[ui-transaction] accfrom change > update accto\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//srcacc = ui_acc_comboboxentry_get(GTK_COMBO_BOX(data->PO_acc));
srcacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_acc));
if( srcacc )
{
//ui_acc_comboboxentry_populate_except(GTK_COMBO_BOX(data->PO_accto), GLOBALS->h_acc, srcacc->key, ACC_LST_INSERT_NORMAL);
ui_acc_entry_popover_populate_except(GTK_BOX(data->PO_accto), GLOBALS->h_acc, srcacc->key, ACC_LST_INSERT_NORMAL);
}
deftransaction_update(widget, user_data);
}
//#1673260
static gboolean deftransaction_cb_dstamount_focusout(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct deftransaction_data *data;
gint type;
gdouble amount;
DB( g_print("\n[ui-transaction] dst amount focus-out-event %d\n", gtk_widget_is_focus(widget)) );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
// when add xfer, dst amount must be positive
if( (type == TXN_TYPE_INTXFER) && (data->action == TXN_DLG_ACTION_ADD) )
{
//amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_xferamt));
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_xferamt));
if( amount < 0 )
{
g_signal_handlers_block_by_func(G_OBJECT(data->ST_xferamt), G_CALLBACK(deftransaction_cb_dstamount_focusout), NULL);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_xferamt), ABS(amount));
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_xferamt), ABS(amount));
g_signal_handlers_unblock_by_func(G_OBJECT(data->ST_xferamt), G_CALLBACK(deftransaction_cb_dstamount_focusout), NULL);
}
}
return FALSE;
}
static gboolean deftransaction_cb_amount_focusout(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct deftransaction_data *data;
gint type;
gdouble amount;
gboolean change;
DB( g_print("\n[ui-transaction] amount focus-out-event %d\n", gtk_widget_is_focus(widget)) );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
//amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
change = FALSE;
if( type == TXN_TYPE_INTXFER )
{
DB( g_print(" is xfer\n") );
// for internal transfer add, amount must be expense by default
if( data->action == TXN_DLG_ACTION_ADD && amount > 0)
change = TRUE;
//#2101050 never force sign here
}
else
{
DB( g_print(" is not xfer\n") );
if( hb_amount_type_match(amount, type) == FALSE )
change = TRUE;
}
if( change == TRUE )
{
g_signal_handlers_block_by_func(G_OBJECT(data->ST_amount), G_CALLBACK(deftransaction_cb_amount_focusout), NULL);
DB( g_print(" force value to %.2f\n", amount * -1) );
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), amount * -1);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), amount * -1);
g_signal_handlers_unblock_by_func(G_OBJECT(data->ST_amount), G_CALLBACK(deftransaction_cb_amount_focusout), NULL);
}
if( type == TXN_TYPE_INTXFER )
{
DB( g_print(" - call set amt xfer\n") );
deftransaction_set_amount_xfer(data);
}
else
{
//#1872329 fill-in cheque if condition match
DB( g_print(" - call set cheque\n") );
deftransaction_set_cheque(widget, NULL);
}
DB( g_print(" - call warnsign\n") );
deftransaction_update_warnsign(widget, NULL);
return FALSE;
}
static void deftransaction_cb_type_toggled(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
gint type;
DB( g_print("\n[ui-transaction] type change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_type));
DB( g_print(" type: %d\n", type ) );
if( type == TXN_TYPE_INTXFER )
{
if( !(data->action == TXN_DLG_ACTION_EDIT) )
deftransaction_cb_accfrom_changed(widget, user_data);
}
//#1882456
ui_cat_entry_popover_sort_type(GTK_BOX(data->PO_cat), type);
deftransaction_cb_amount_focusout(widget, NULL, user_data);
deftransaction_update(widget, user_data);
}
static void deftransaction_set(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Transaction *entry;
gint type;
gchar *tagstr;
DB( g_print("\n[ui-transaction] set\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
entry = data->ope;
type = transaction_get_type(entry);
DB( g_print(" - ope=%p data=%p\n", data->ope, data) );
g_signal_handlers_block_by_func(G_OBJECT(data->RA_type), G_CALLBACK(deftransaction_cb_type_toggled), NULL);
hbtk_switcher_set_active(HBTK_SWITCHER(data->RA_type), type);
g_signal_handlers_unblock_by_func(G_OBJECT(data->RA_type), G_CALLBACK(deftransaction_cb_type_toggled), NULL);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_date), (guint)entry->date);
hbtk_entry_set_text(GTK_ENTRY(data->ST_memo), entry->memo);
g_signal_handlers_block_by_func(G_OBJECT(data->ST_amount), G_CALLBACK(deftransaction_cb_amount_focusout), NULL);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), entry->amount);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), entry->amount);
g_signal_handlers_unblock_by_func(G_OBJECT(data->ST_amount), G_CALLBACK(deftransaction_cb_amount_focusout), NULL);
//#1673260
if( type == TXN_TYPE_INTXFER )
{
g_signal_handlers_block_by_func(G_OBJECT(data->ST_xferamt), G_CALLBACK(deftransaction_cb_dstamount_focusout), NULL);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_xferamt), entry->xferamount);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_xferamt), entry->xferamount);
g_signal_handlers_unblock_by_func(G_OBJECT(data->ST_xferamt), G_CALLBACK(deftransaction_cb_dstamount_focusout), NULL);
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_cheque), (entry->flags & OF_CHEQ2) ? 1 : 0);
hbtk_entry_set_text(GTK_ENTRY(data->ST_number), entry->number);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), entry->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), entry->kcat);
//#1910857 don't trigger autofill when set payee
g_signal_handlers_block_by_func(G_OBJECT(ui_pay_entry_popover_get_entry(GTK_BOX(data->PO_pay))), G_CALLBACK(deftransaction_cb_payee_changed), NULL);
//ui_pay_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_pay), entry->kpay);
ui_pay_entry_popover_set_active(GTK_BOX(data->PO_pay), entry->kpay);
g_signal_handlers_unblock_by_func(G_OBJECT(ui_pay_entry_popover_get_entry(GTK_BOX(data->PO_pay))), G_CALLBACK(deftransaction_cb_payee_changed), NULL);
tagstr = tags_tostring(entry->tags);
hbtk_entry_set_text(GTK_ENTRY(data->ST_tags), tagstr);
g_free(tagstr);
hbtk_switcher_set_active (HBTK_SWITCHER(data->RA_status), entry->status);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_remind), (entry->flags & OF_REMIND) ? 1 : 0);
//as we trigger an event on this
//let's place it at the end to avoid missvalue on the trigger function
//g_signal_handlers_block_by_func (G_OBJECT (data->PO_acc), G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
g_signal_handlers_block_by_func (G_OBJECT (ui_acc_entry_popover_get_entry(GTK_BOX(data->PO_acc))), G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
if( entry->kacc > 0 )
{
//ui_acc_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_acc), entry->kacc);
ui_acc_entry_popover_set_active(GTK_BOX(data->PO_acc), entry->kacc);
}
else //1829007 set first item if only 1 account
{
//#1859077 >=5.3 no default account, as we should pass acckey here
ui_acc_entry_popover_set_single(GTK_BOX(data->PO_acc));
}
//g_signal_handlers_unblock_by_func (G_OBJECT (data->PO_acc), G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
g_signal_handlers_unblock_by_func (G_OBJECT (ui_acc_entry_popover_get_entry(GTK_BOX(data->PO_acc))), G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
//ui_acc_comboboxentry_populate_except(GTK_COMBO_BOX(data->PO_accto), GLOBALS->h_acc, entry->kacc, ACC_LST_INSERT_NORMAL);
ui_acc_entry_popover_populate_except(GTK_BOX(data->PO_accto), GLOBALS->h_acc, entry->kacc, ACC_LST_INSERT_NORMAL);
//ui_acc_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_accto), entry->kxferacc);
ui_acc_entry_popover_set_active(GTK_BOX(data->PO_accto), entry->kxferacc);
kiv_combo_box_set_active(GTK_COMBO_BOX(data->NU_mode), entry->paymode);
}
static void deftransaction_cb_split_clicked(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Transaction *ope;
gdouble amount;
Account *srcacc;
gint type, nbsplit;
guint32 kcur, date;
DB( g_print("\n[ui-transaction] cb split clicked\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ope = data->ope;
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
date = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_date));
//amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
srcacc = ui_acc_entry_popover_get(GTK_BOX(data->PO_acc));
kcur = (srcacc != NULL) ? srcacc->kcur : GLOBALS->kcur;
ui_split_dialog(data->dialog, &ope->splits, type, date, amount, kcur, &deftransaction_set_amount_from_split);
DB( g_print(" - after closed dialog\n") );
/* old stuffs */
//# 1419476 empty category when no split either...
if( (ope->flags & (OF_SPLIT)) )
{
//# 1416624 empty category when split
g_signal_handlers_block_by_func (G_OBJECT (data->PO_cat), G_CALLBACK (deftransaction_update_warnsign), NULL);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), 0);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), 0);
g_signal_handlers_unblock_by_func (G_OBJECT (data->PO_cat), G_CALLBACK (deftransaction_update_warnsign), NULL);
}
//eval split to garantee disabled items
ope->flags &= ~(OF_SPLIT);
nbsplit = da_splits_length(ope->splits);
if(nbsplit > 0)
data->ope->flags |= (OF_SPLIT);
DB( g_print(" - call update\n") );
deftransaction_update(data->dialog, NULL);
}
static void deftransaction_paymode(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
gint type, paymode;
gboolean visible;
DB( g_print("\n[ui-transaction] paymode change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
visible = (paymode == PAYMODE_CHECK) ? TRUE : FALSE;
visible = (type == TXN_TYPE_INTXFER) ? FALSE : visible;
hb_widget_visible(data->CM_cheque, visible);
/* todo: prefill the cheque number ? */
deftransaction_set_cheque(widget, user_data);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void deftransaction_set_amount_from_split(GtkWidget *widget, gdouble amount)
{
struct deftransaction_data *data;
gint type;
DB( g_print("\n[ui-transaction] set_amount_from_split\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("- amount=%.2f\n", amount) );
//#1885413 enable sign invert from split dialog
type = (amount < 0.0) ? TXN_TYPE_EXPENSE : TXN_TYPE_INCOME;
g_signal_handlers_block_by_func(data->RA_type, G_CALLBACK(deftransaction_cb_type_toggled), NULL);
hbtk_switcher_set_active (HBTK_SWITCHER(data->RA_type), type);
g_signal_handlers_unblock_by_func(data->RA_type, G_CALLBACK(deftransaction_cb_type_toggled), NULL);
data->ope->amount = amount;
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), amount);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), amount);
deftransaction_update(widget, NULL);
}
void deftransaction_get(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
Transaction *entry;
gchar *txt;
gint type, active;
DB( g_print("\n[ui-transaction] get\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
entry = data->ope;
DB( g_print(" txn: %p\n", entry) );
//if( entry == NULL )
// return;
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
DB( g_print(" type: %d\n", type) );
entry->flags &= ~(OF_INCOME|OF_INTXFER);
if( type == TXN_TYPE_INCOME)
entry->flags |= OF_INCOME;
if( type == TXN_TYPE_INTXFER)
entry->flags |= OF_INTXFER;
entry->date = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_date));
DB( hb_print_date(entry->date, " date:") );
//#1988594 ensure amount are updated
//entry->amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
entry->amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
DB( g_print(" amount: %f '%s'\n", entry->amount, gtk_entry_get_text(GTK_ENTRY(data->ST_amount))) );
//entry->xferamount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_xferamt));
entry->xferamount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_xferamt));
DB( g_print(" xferamount %f '%s'\n", entry->xferamount, gtk_entry_get_text(GTK_ENTRY(data->ST_xferamt))) );
//entry->kacc = ui_acc_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_acc));
entry->kacc = ui_acc_entry_popover_get_key(GTK_BOX(data->PO_acc));
//entry->kxferacc = ui_acc_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_accto));
entry->kxferacc = ui_acc_entry_popover_get_key(GTK_BOX(data->PO_accto));
DB( g_print(" srcacc: %d\n", entry->kacc) );
DB( g_print(" dstacc: %d\n", entry->kxferacc) );
//free any previous string
if( entry->memo )
{
g_free(entry->memo);
entry->memo = NULL;
}
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_memo));
// ignore if entry is empty
if (txt && *txt)
{
entry->memo = g_strdup(txt);
//#1716182 add into memo autocomplete
if(PREFS->txn_memoacp == TRUE)
{
if( da_transaction_insert_memo(entry->memo, entry->date) )
{
GtkEntryCompletion *completion;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print(" add memo to completion\n") );
completion = gtk_entry_get_completion (GTK_ENTRY(data->ST_memo));
model = gtk_entry_completion_get_model (completion);
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, -1,
0, txt,
-1);
}
}
}
DB( g_print(" memo: '%s'\n", entry->memo) );
//free any previous string
if( entry->number )
{
g_free(entry->number);
entry->number = NULL;
}
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_number));
// ignore if entry is empty
if (txt && *txt)
{
entry->number = g_strdup(txt);
}
DB( g_print(" info: '%s'\n", entry->number) );
entry->paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mode));
//entry->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat));
entry->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(data->PO_cat));
//entry->kpay = ui_pay_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_pay));
entry->kpay = ui_pay_entry_popover_get_key_add_new(GTK_BOX(data->PO_pay));
DB( g_print(" paymode: %d\n", entry->paymode) );
DB( g_print(" cat: %d\n", entry->kcat) );
DB( g_print(" pay: %d\n", entry->kpay) );
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_tags));
DB( g_print(" tags: '%s'\n", txt) );
g_free(entry->tags);
entry->tags = tags_parse(txt);
//entry->status = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_status));
entry->status = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_status));
DB( g_print(" status: '%d'\n", entry->status) );
entry->flags &= ~(OF_REMIND);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_remind));
if(active == 1)
entry->flags |= OF_REMIND;
// consistency checks
//#617936/#1988594 ensure amount sign
if( data->action == TXN_DLG_ACTION_ADD )
{
entry->amount = ABS(entry->amount);
entry->xferamount = ABS(entry->xferamount);
if( (type == TXN_TYPE_EXPENSE) || (type == TXN_TYPE_INTXFER) )
{
entry->amount = -entry->amount;
}
}
//#1615245: moved here, after get combo entry key
//if( entry->paymode != PAYMODE_INTXFER )
if( !(entry->flags & OF_INTXFER) )
{
//#677351: revert kxferacc to 0
entry->kxferacc = 0;
}
else
entry->paymode = PAYMODE_NONE;
/* flags */
//entry->flags = 0;
entry->flags &= ~(OF_SPLIT|OF_ADVXFER|OF_CHEQ2); //remove existing flags
//1859117: keep the split flag
if( da_splits_length (entry->splits) > 0 )
entry->flags |= OF_SPLIT; //Flag that Splits are active
if( data->action == TXN_DLG_ACTION_ADD || data->action == TXN_DLG_ACTION_INHERIT)
entry->dspflags |= FLAG_TMP_ADDED;
if( data->action == TXN_DLG_ACTION_EDIT)
entry->dspflags |= FLAG_TMP_EDITED;
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cheque));
if(active == 1)
entry->flags |= OF_CHEQ2;
//#1673260
if( type == TXN_TYPE_INTXFER )
{
Account *srcacc, *dstacc;
srcacc = da_acc_get(entry->kacc);
dstacc = da_acc_get(entry->kxferacc);
if( srcacc && dstacc )
{
if( srcacc->kcur != dstacc->kcur )
{
DB( g_print(" get OF_ADVXFER\n") );
entry->flags |= OF_ADVXFER;
}
}
}
else
entry->xferamount = 0;
//active = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_amount));
//we keep this in case the user want to force the type
da_transaction_set_flag(entry);
}
static gboolean confirm_handler(struct deftransaction_data *data)
{
DB( g_print("\n[ui-transaction] hide confirm text\n") );
//#1859088 normally dispose remove the event source, so we don't land here after timeout
hb_widget_visible(data->LB_msgadded, FALSE);
data->evtsrcid = 0;
return FALSE;
}
void deftransaction_external_confirm(GtkWidget *dialog, Transaction *ope)
{
struct deftransaction_data *data;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar *txt;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(dialog, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-transaction] display confirm text\n") );
if(data->evtsrcid > 0 )
return;
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, ope->amount, ope->kcur, GLOBALS->minor);
txt = g_strdup_printf(_("Transaction of %s created."), buf);
gtk_label_set_text(GTK_LABEL(data->LB_msgadded), txt);
g_free(txt);
hb_widget_visible(data->LB_msgadded, TRUE);
data->evtsrcid = g_timeout_add(5000, (GSourceFunc) confirm_handler, (gpointer) data);
}
/*
** called from outside (ledger/report detail)
*/
gint deftransaction_external_edit(GtkWindow *parent, Transaction *old_txn, Transaction *new_txn)
{
struct deftransaction_data *data;
GtkWidget *dialog;
gboolean result, accchanged;
Transaction *child;
Account *acc;
DB( g_print("\n------------------------\n") );
DB( g_print("\n[ui-transaction] external edit (from out)\n") );
dialog = create_deftransaction_window(GTK_WINDOW(parent), TXN_DLG_ACTION_EDIT, TXN_DLG_TYPE_TXN, 0);
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(dialog, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" -- test xfer\n") );
//5.7 test if xfer src or dst
// and disable expense or income as well to avoid mistake
data->txnoldtype = 0;
if( old_txn->amount < 0 )
data->txnoldtype = -1;
if( old_txn->amount > 0 )
data->txnoldtype = 1;
child = NULL;
if( old_txn->flags & OF_INTXFER )
{
//use old in case of dst_acc change
child = transaction_xfer_child_strong_get(old_txn);
if( old_txn->amount < 0 )
//disable income
hbtk_switcher_set_nth_sensitive(HBTK_SWITCHER(data->RA_type), 1, FALSE);
if( old_txn->amount > 0 )
//disable expense
hbtk_switcher_set_nth_sensitive(HBTK_SWITCHER(data->RA_type), 0, FALSE);
//#1867979 todate
//#2109861 child can be null
if( PREFS->xfer_syncdate == FALSE && child != NULL )
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_dateto), (guint)child->date);
}
DB( g_print(" xfer old type: %d\n", data->txnoldtype) );
deftransaction_set_transaction(dialog, new_txn);
DB( g_print(" ** dialog run **\n") );
result = gtk_dialog_run (GTK_DIALOG (dialog));
DB( g_print(" ** dialog ended :: result=%d**\n", result) );
accchanged = FALSE;
if(result == GTK_RESPONSE_ACCEPT)
{
deftransaction_get(dialog, NULL);
account_balances_sub(old_txn);
account_balances_add(new_txn);
accchanged = TRUE;
/* ok different case here
* new is intxfer
a) old was not
check for existing child or add it
b) old was
sync (acc change is inside now)
* new is not intxfer
a) old was
manage break intxfer
* always manage account change
*/
acc = da_acc_get(new_txn->kacc);
//#1931816: sort if date changed
if(old_txn->date != new_txn->date)
{
da_transaction_queue_sort(acc->txn_queue);
accchanged = TRUE;
}
//if( new_txn->paymode == PAYMODE_INTXFER )
if( new_txn->flags & OF_INTXFER )
{
// change to an internal xfer
if( !(old_txn->flags & OF_INTXFER) )
{
gint tmpxferresult;
// this call can popup a user dialog to choose
tmpxferresult = transaction_xfer_search_or_add_child(GTK_WINDOW(dialog), FALSE, new_txn, new_txn->kxferacc);
if( tmpxferresult == GTK_RESPONSE_CANCEL )
accchanged = FALSE;
}
else // just sync the existing xfer
{
//#2109861 child can be null
if( child != NULL)
{
//#1584342 was faultly old_txn
transaction_xfer_child_sync(new_txn, child);
//#1867979 todate
if( PREFS->xfer_syncdate == FALSE )
child->date = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_dateto));
}
accchanged = TRUE;
}
}
else
{
//#1250061 : manage ability to break an internal xfer
if(old_txn->flags & OF_INTXFER)
{
gint break_result;
DB( g_print(" - should break internal xfer\n") );
break_result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(parent),
NULL,
_("Do you want to break the internal transfer?\n\n"
"Proceeding will delete the target transaction."),
_("_Break"),
TRUE
);
if(break_result == GTK_RESPONSE_OK)
{
//we must use old_txn to ensure get the child
//#1663789 but we must clean new as well
transaction_xfer_remove_child(old_txn);
transaction_xfer_change_to_normal(new_txn);
accchanged = TRUE;
}
else //force paymode to internal xfer
{
//new_txn->paymode = PAYMODE_INTXFER;
new_txn->flags |= OF_INTXFER;
}
}
}
//#1638035: manage account change
if( old_txn->kacc != new_txn->kacc )
{
//todo: maybe we should restrict this also to same currency account
//=> no pb for normal, and intxfer is restricted by ui (in theory)
transaction_acc_move(new_txn, old_txn->kacc, new_txn->kacc);
accchanged = TRUE;
}
//#1581863 store reconciled date
if( (old_txn->status != new_txn->status) && (new_txn->status == TXN_STATUS_RECONCILED) )
{
if(acc)
acc->rdate = GLOBALS->today;
}
}
//#1883403 we may have changed the split amount but if we cancel it must be restored
else
{
if( old_txn->flags & OF_SPLIT )
new_txn->amount = old_txn->amount;
}
/* update account flag */
if( accchanged == TRUE )
{
DB( g_print(" mark acc as changed\n") );
acc = da_acc_get(new_txn->kacc);
if(acc)
acc->dspflags |= FLAG_ACC_TMP_EDITED;
}
deftransaction_dispose(dialog, NULL);
gtk_window_destroy (GTK_WINDOW(dialog));
return result;
}
void deftransaction_set_transaction(GtkWidget *widget, Transaction *ope)
{
struct deftransaction_data *data;
DB( g_print("\n[ui-transaction] set transaction (from out)\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//if( ope == NULL )
// return;
data->ope = ope;
DB( g_print(" - ope=%p data=%p\n", data->ope, data) );
DB( g_print(" - call set\n") );
deftransaction_set(widget, NULL);
DB( g_print(" - call update\n") );
deftransaction_update(widget, NULL);
DB( g_print(" - call warnsign\n") );
deftransaction_update_warnsign(widget, NULL);
}
// end external call
static void deftransaction_setup(struct deftransaction_data *data)
{
DB( g_print("\n[ui-transaction] setup\n") );
//DB( g_print(" init data\n") );
DB( g_print(" populate\n") );
if( data->showtemplate )
{
ui_popover_tpl_populate (data, GLOBALS->arc_list);
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data->modelfilter));
}
//ui_acc_comboboxentry_populate(GTK_COMBO_BOX(data->PO_acc), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
ui_acc_entry_popover_populate(GTK_BOX(data->PO_acc), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
//5.3 it seems no need to populate @init
//ui_acc_comboboxentry_populate(GTK_COMBO_BOX(data->PO_accto), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
//ui_acc_entry_popover_populate(GTK_COMBO_BOX(data->PO_accto), GLOBALS->h_acc, ACC_LST_INSERT_NORMAL);
//5.3 done in popover
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_cat), GLOBALS->h_cat);
//5.2.7 done in popover
//ui_pay_comboboxentry_populate(GTK_COMBO_BOX(data->PO_pay), GLOBALS->h_pay);
//ui_tag_combobox_populate(GTK_COMBO_BOX_TEXT(data->CY_tags));
//DB( g_print(" set widgets default\n") );
//DB( g_print(" connect widgets signals\n") );
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_popover_tpl_onRowActivated(GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct deftransaction_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
model = gtk_tree_view_get_model(treeview);
if (gtk_tree_model_get_iter(model, &iter, path))
{
Archive *arc;
Transaction *txn;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPTPL_DATAS, &arc, -1);
txn = data->ope;
da_transaction_init_from_template(txn, arc);
DB( g_print(" calls\n") );
deftransaction_set(GTK_WIDGET(treeview), NULL);
deftransaction_paymode(GTK_WIDGET(treeview), NULL);
deftransaction_update(GTK_WIDGET(treeview), NULL);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->MB_template), FALSE);
}
}
static gint
ui_popover_tpl_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
Archive *entry1, *entry2;
gtk_tree_model_get(model, a, LST_DSPTPL_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DSPTPL_DATAS, &entry2, -1);
retval = hb_string_utf8_compare(entry1->memo, entry2->memo);
return retval;
}
static void ui_popover_tpl_populate(struct deftransaction_data *data, GList *srclist)
{
GtkTreeModel *model;
GtkTreeIter iter;
GList *list;
GString *tpltitle;
//insert all glist item into treeview
model = data->model;
gtk_list_store_clear(GTK_LIST_STORE(model));
tpltitle = g_string_sized_new(255);
list = g_list_first(srclist);
while (list != NULL)
{
Archive *item = list->data;
//#1968249 build a non empty label, when memo/payee/category are empty
da_archive_get_display_label(tpltitle, item);
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, -1,
LST_DSPTPL_DATAS, item,
LST_DSPTPL_NAME, tpltitle->str,
-1);
//DB( g_print(" populate_treeview: %d %08x\n", i, list->data) );
list = g_list_next(list);
}
g_string_free(tpltitle, TRUE);
}
static void
ui_popover_tpl_refilter (GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data = user_data;
DB( g_print(" text changed\n") );
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data->modelfilter));
}
static gboolean ui_popover_tpl_func_visible (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct deftransaction_data *data = user_data;
Archive *entry;
gchar *str;
gboolean visible = TRUE;
gboolean showsched;
gboolean showallacc;
showsched = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_showsched));
showallacc = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_showallacc));
gchar *needle = g_ascii_strdown(gtk_entry_get_text(GTK_ENTRY(data->ST_search)), -1);
gtk_tree_model_get (model, iter,
LST_DSPTPL_DATAS, &entry,
LST_DSPTPL_NAME, &str, -1);
if( entry )
{
if( !showallacc && (data->kacc != 0) && (entry->kacc != data->kacc) )
visible = FALSE;
else
{
if( (entry->rec_flags & TF_RECUR) && !showsched)
{
visible = FALSE;
}
else
{
gchar *haystack = g_ascii_strdown(str, -1);
if (str && g_strrstr (haystack, needle) == NULL )
{
visible = FALSE;
}
DB( g_print("filter: '%s' '%s' %d\n", str, needle, visible) );
g_free(haystack);
}
}
}
g_free(needle);
g_free (str);
return visible;
}
static GtkWidget *ui_popover_tpl_create(struct deftransaction_data *data)
{
GtkListStore *store;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *box, *widget, *scrollwin, *treeview;
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
widget = make_search();
data->ST_search = widget;
gtk_box_prepend (GTK_BOX(box), widget);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
store = gtk_list_store_new(NUM_LST_DSPTPL,
G_TYPE_POINTER,
G_TYPE_STRING);
data->model = GTK_TREE_MODEL(store);
//#1865361 txn dialog template list switch sort order on each save
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_popover_tpl_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
data->modelfilter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(data->model), NULL));
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(data->modelfilter), ui_popover_tpl_func_visible, data, NULL);
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(data->modelfilter));
data->LV_arc = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_grab_focus(treeview);
/* column for bug numbers */
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (NULL,
renderer,
"text",
LST_DSPTPL_NAME,
NULL);
//gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
widget = gtk_check_button_new_with_mnemonic(_("Show _scheduled"));
data->CM_showsched = widget;
gtk_box_prepend (GTK_BOX(box), widget);
widget = gtk_check_button_new_with_mnemonic(_("Show _all accounts"));
data->CM_showallacc = widget;
gtk_box_prepend (GTK_BOX(box), widget);
gtk_widget_show_all (box);
//#1796564 hide show all template if no account
gtk_widget_set_visible (data->CM_showallacc, data->kacc == 0 ? FALSE : TRUE);
//signals
g_signal_connect (data->CM_showsched, "toggled", G_CALLBACK (ui_popover_tpl_refilter), data);
g_signal_connect (data->CM_showallacc, "toggled", G_CALLBACK (ui_popover_tpl_refilter), data);
g_signal_connect (data->ST_search, "search-changed", G_CALLBACK (ui_popover_tpl_refilter), data);
return box;
}
static GtkWidget *deftransaction_create_template(struct deftransaction_data *data)
{
GtkWidget *box, *menubutton, *image;
menubutton = gtk_menu_button_new ();
data->MB_template = menubutton;
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_box_prepend (GTK_BOX(box), image);
gtk_container_add(GTK_CONTAINER(menubutton), box);
gtk_widget_set_tooltip_text(menubutton, _("Use a template"));
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_DOWN );
gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
gtk_widget_show_all(menubutton);
GtkWidget *template = ui_popover_tpl_create(data);
GtkWidget *popover = create_popover (menubutton, template, GTK_POS_BOTTOM);
gtk_widget_set_size_request (popover, 2*HB_MINWIDTH_LIST, PHI*HB_MINHEIGHT_LIST);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
g_signal_connect (GTK_TREE_VIEW(data->LV_arc), "row-activated", G_CALLBACK (ui_popover_tpl_onRowActivated), NULL);
return menubutton;
}
static gboolean
deftransaction_getgeometry(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct WinGeometry *wg;
DB( g_print("\n[ui-transaction] get geometry\n") );
//store size
wg = &PREFS->txn_wg;
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, NULL);
DB( g_print(" window: w=%d\n", wg->w) );
return FALSE;
}
//#1681532 free in destroy event
static void deftransaction_cb_destroy(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
DB( g_print("\n[ui-transaction] --destroy cb\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" free data\n") );
g_free(data);
}
//note: this is called from external usage
void deftransaction_dispose(GtkWidget *widget, gpointer user_data)
{
struct deftransaction_data *data;
DB( g_print("\n[ui-transaction] dispose\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#1859088 remove any pending event source (g_timeout_add)
if(data->evtsrcid > 0)
g_source_remove(data->evtsrcid);
deftransaction_getgeometry(data->dialog, NULL, data);
//#1681532 don't free here
//g_free(data);
}
GtkWidget *create_deftransaction_window (GtkWindow *parent, HbTxnDlgAction action, HbTxnDlgType type, guint32 kacc)
{
struct deftransaction_data *data;
struct WinGeometry *wg;
GtkWidget *dialog, *content;
GtkWidget *bar;
GtkWidget *group_grid, *hbox, *label, *widget;
gchar *title;
gint row;
DB( g_print("\n[ui-transaction] new\n") );
data = g_malloc0(sizeof(struct deftransaction_data));
if(!data) return NULL;
dialog = gtk_dialog_new();
gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" - window=%p, inst_data=%p\n", dialog, data) );
data->dialog = dialog;
data->action = action;
data->type = type;
data->kacc = kacc;
// if you add/delete response_id also change into deftransaction_update
if(action == TXN_DLG_ACTION_EDIT)
{
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_REJECT,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
}
else //ADD_INHERIT
{
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Close"), GTK_RESPONSE_REJECT);
if( type == TXN_DLG_TYPE_TXN )
gtk_dialog_add_button(GTK_DIALOG(dialog), _("Add & _Keep"), HB_RESPONSE_ADDKEEP);
if( !(type == TXN_DLG_TYPE_SCH) )
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Add"), HB_RESPONSE_ADD);
else
gtk_dialog_add_button (GTK_DIALOG(dialog), _("_Post"), HB_RESPONSE_ADD);
}
title = NULL;
switch(action)
{
case TXN_DLG_ACTION_NONE:
title = NULL;
break;
case TXN_DLG_ACTION_ADD:
title = _("Add transaction");
if(type == TXN_DLG_TYPE_TPL) { title = _("Add template"); }
else if(type == TXN_DLG_TYPE_SCH) { title = _("Post scheduled"); }
break;
case TXN_DLG_ACTION_INHERIT:
title = _("Inherit transaction");
if(type == TXN_DLG_TYPE_TPL)
title = _("Inherit template");
break;
case TXN_DLG_ACTION_EDIT:
title = _("Edit transaction");
if(type == TXN_DLG_TYPE_TPL)
title = _("Edit template");
break;
}
gtk_window_set_title (GTK_WINDOW(dialog), title);
DB( g_print(" action:%d '%s', dlgtype:%d \n", action, title, type ) );
//gtk_window_set_decorated(GTK_WINDOW(dialog), TRUE);
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
//group main
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);
hb_widget_set_margin(GTK_WIDGET(group_grid), SPACING_LARGE);
gtk_box_prepend (GTK_BOX (content), group_grid);
row=0;
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup (HBTK_SWITCHER(widget), CYA_TXN_TYPE, TRUE);
data->RA_type = widget;
gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
gtk_grid_attach (GTK_GRID (group_grid), widget, 0, row, 5, 1);
gtk_widget_set_margin_bottom(widget, SPACING_MEDIUM);
data->showtemplate = FALSE;
if( (data->type == TXN_DLG_TYPE_TXN) && (da_archive_length() > 0) )
{
if( (data->action != TXN_DLG_ACTION_EDIT) || PREFS->txn_showtemplate )
{
data->showtemplate = TRUE;
widget = deftransaction_create_template(data);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
gtk_widget_set_margin_bottom(widget, SPACING_MEDIUM);
}
}
row++;
label = make_label_widget(_("_Date:"));
data->LB_date = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
data->GR_date = hbox;
//gtk_widget_set_hexpand(hbox, FALSE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
widget = gtk_date_entry_new(label);
data->PO_date = widget;
//gtk_widget_set_halign(widget, GTK_ALIGN_START);
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
//gtk_widget_set_tooltip_text(widget, _("Date accepted here are:\nday,\nday/month or month/day,\nand complete date into your locale"));
gtk_widget_set_tooltip_text(widget, _("- type: d, d/m, m/d a complete date\n- use arrow key + ctrl or shift\n- empty for today"));
gtk_box_prepend (GTK_BOX (hbox), widget);
//5.8 xfer date
if( PREFS->xfer_syncdate == FALSE && data->action == TXN_DLG_ACTION_EDIT && data->type == TXN_DLG_TYPE_TXN )
{
row++;
label = make_label_widget(_("T_o:"));
data->LB_dateto = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = gtk_date_entry_new(label);
gtk_widget_set_halign(widget, GTK_ALIGN_START);
data->PO_dateto = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 3, 1);
}
row++;
label = make_label_widget(_("Amou_nt:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
//gtk_widget_set_hexpand(hbox, FALSE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
//widget = make_amount(label);
widget = hbtk_decimal_entry_new(label);
data->ST_amount = widget;
//gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget), GTK_ENTRY_ICON_PRIMARY, ICONNAME_HB_TOGGLE_SIGN);
//gtk_entry_set_icon_tooltip_text(GTK_ENTRY(widget), GTK_ENTRY_ICON_PRIMARY, _("Toggle amount sign"));
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_SPLIT, _("Transaction splits"));
data->BT_split = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label(NULL, 0, 0.5);
data->LB_curr = label;
gtk_widget_set_margin_start(label, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (hbox), label);
//#1673260
row++;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
//gtk_widget_set_hexpand(hbox, FALSE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 2, row, 3, 1);
//widget = make_amount(label);
widget = hbtk_decimal_entry_new(label);
data->ST_xferamt = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label(NULL, 0, 0.5);
data->LB_xfercurr = label;
//gtk_widget_set_margin_start(label, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (hbox), label);
widget = hbtk_image_new_from_icon_name_16(ICONNAME_WARNING);
data->IM_xfernorate = widget;
gtk_widget_set_tooltip_text (widget, _("No rate available to auto fill"));
gtk_box_prepend (GTK_BOX (hbox), widget);
/*row++;
label = make_label_widget(_("A_ccount:"));
data->LB_accfrom = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = ui_acc_comboboxentry_new(label);
data->PO_acc = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);*/
row++;
label = make_label_widget(_("A_ccount:"));
data->LB_accfrom = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = ui_acc_entry_popover_new(label);
data->PO_acc = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
gtk_widget_set_margin_top(label, SPACING_MEDIUM);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
/*row++;
label = make_label_widget(_("T_o:"));
data->LB_accto = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = ui_acc_comboboxentry_new(label);
data->PO_accto = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);*/
row++;
label = make_label_widget(_("T_o:"));
data->LB_accto = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = ui_acc_entry_popover_new(label);
data->PO_accto = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 3, 1);
row++;
label = make_label_widget(_("Pa_yment:"));
data->LB_mode = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = make_paymode(label);
data->NU_mode = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
widget = gtk_check_button_new_with_mnemonic(_("Book _2"));
data->CM_cheque = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 2, 1);
row++;
label = make_label_widget(_("_Number:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = make_string(label);
data->ST_number = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Payee:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_hexpand(hbox, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
widget = ui_pay_entry_popover_new(label);
data->PO_pay = widget;
gtk_widget_set_hexpand(widget, TRUE);
hbtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
gtk_widget_set_tooltip_text(widget, _("- type some letter for autocompletion\n- type new text to create entry"));
gtk_box_prepend (GTK_BOX (hbox), widget);
gtk_widget_set_margin_top(label, SPACING_MEDIUM);
gtk_widget_set_margin_top(hbox, SPACING_MEDIUM);
row++;
label = make_label_widget(_("Cate_gory:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_hexpand(hbox, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
data->PO_cat = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
//gtk_widget_set_tooltip_text(widget, _("Autocompletion and direct seizure\nis available"));
gtk_widget_set_tooltip_text(widget, _("- type some letter for autocompletion\n- type new text to create entry"));
gtk_box_prepend (GTK_BOX (hbox), widget);
/*
row++;
label = make_label_widget(_("_Status:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = hbtk_combo_box_new_with_data (label, CYA_TXN_STATUS);
gtk_widget_set_halign(widget, GTK_ALIGN_START);
data->CY_status = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 2, 1);
*/
//#1847622 transaction editor: "Status drop" down menu
row++;
label = make_label_widget(_("_Status:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_hexpand(hbox, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
widget = hbtk_switcher_new(GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup_with_data(HBTK_SWITCHER(widget), label, CYA_TXN_STATUSIMG, TRUE);
data->RA_status = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = make_image_toggle_button(ICONNAME_HB_ITEM_REMIND, _("Remind"));
data->CM_remind = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
gtk_widget_set_margin_top(label, SPACING_MEDIUM);
gtk_widget_set_margin_top(hbox, SPACING_MEDIUM);
row++;
label = make_label_widget(_("M_emo:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = make_memo_entry(label);
data->ST_memo = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 4, 1);
row++;
label = make_label_widget(_("_Tags:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(hbox)), GTK_STYLE_CLASS_LINKED);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 4, 1);
widget = make_string(label);
data->ST_tags = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
widget = ui_tag_popover_list(data->ST_tags);
data->CY_tags = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
DB( g_print(" -- showall\n") );
gtk_widget_show_all(group_grid);
//#1831975 visual add confirmation
row++;
label = gtk_label_new(NULL);
data->LB_msgadded = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 4, 1);
//infobar
//row++;
bar = gtk_info_bar_new ();
//gtk_grid_attach (GTK_GRID (group_grid), bar, 1, row, 4, 1);
gtk_box_prepend (GTK_BOX (content), bar);
data->IB_warnsign = bar;
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
label = gtk_label_new (NULL);
data->LB_warnsign = label;
hbtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
//setup, init and show window
deftransaction_setup(data);
wg = &PREFS->txn_wg;
gtk_window_set_default_size(GTK_WINDOW(dialog), wg->w, -1);
DB( g_print(" -- signal\n") );
// connect dialog signals
g_signal_connect (dialog, "destroy", G_CALLBACK (deftransaction_cb_destroy), NULL);
//debug signal not to release
//g_signal_connect (dialog, "configure-event", G_CALLBACK (deftransaction_getgeometry), (gpointer)data);
g_signal_connect (data->RA_type , "changed", G_CALLBACK (deftransaction_cb_type_toggled), NULL);
//5.7 removed
//g_signal_connect (data->PO_date, "changed", G_CALLBACK (deftransaction_cb_date_changed), NULL);
//g_signal_connect (data->PO_acc , "changed", G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
g_signal_connect (ui_acc_entry_popover_get_entry(GTK_BOX(data->PO_acc)), "changed", G_CALLBACK (deftransaction_cb_accfrom_changed), NULL);
//g_signal_connect (data->PO_accto, "changed", G_CALLBACK (deftransaction_update), NULL);
g_signal_connect (ui_acc_entry_popover_get_entry(GTK_BOX(data->PO_accto)), "changed", G_CALLBACK (deftransaction_update), NULL);
g_signal_connect_after (data->ST_amount, "value-changed", G_CALLBACK (deftransaction_cb_amount_focusout), NULL);
g_signal_connect_after (data->ST_amount, "focus-out-event", G_CALLBACK (deftransaction_cb_amount_focusout), NULL);
g_signal_connect (data->BT_split, "clicked", G_CALLBACK (deftransaction_cb_split_clicked), NULL);
//#1673260
g_signal_connect_after (data->ST_xferamt, "value-changed", G_CALLBACK (deftransaction_cb_dstamount_focusout), NULL);
g_signal_connect_after (data->ST_xferamt, "focus-out-event", G_CALLBACK (deftransaction_cb_dstamount_focusout), NULL);
g_signal_connect (data->NU_mode , "changed", G_CALLBACK (deftransaction_paymode), NULL);
g_signal_connect (data->CM_cheque, "toggled", G_CALLBACK (deftransaction_paymode), NULL);
//g_signal_connect (data->PO_pay , "changed", G_CALLBACK (deftransaction_cb_payee_changed), NULL);
g_signal_connect (ui_pay_entry_popover_get_entry(GTK_BOX(data->PO_pay)), "changed", G_CALLBACK (deftransaction_cb_payee_changed), NULL);
//g_signal_connect (data->PO_cat , "changed", G_CALLBACK (deftransaction_update_warnsign), NULL);
g_signal_connect (ui_cat_entry_popover_get_entry(GTK_BOX(data->PO_cat)), "changed", G_CALLBACK (deftransaction_update_warnsign), NULL);
DB( g_print(" -- return\n") );
return dialog;
}
homebank-5.9.7/src/dsp-mainwindow.h 0000644 0001750 0001750 00000010421 15005632540 016510 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_MANWINDOW_GTK_H__
#define __HB_MANWINDOW_GTK_H__
typedef struct _pnlaccgrp PnlAccGrp;
struct _pnlaccgrp
{
gchar *name;
//gshort type;
//gshort pos;
//gboolean expanded;
gboolean showtotal;
gdouble bal_clear;
gdouble bal_recon;
gdouble bal_today;
gdouble bal_future;
GPtrArray *acclist;
};
struct hbfile_data
{
GtkWidget *window;
GtkWidget *dbgchange;
GtkWidget *menubar;
GtkWidget *ME_menufile, *ME_menuedit, *ME_menuview, *ME_menuman, *ME_menutxn, *ME_menurep, *ME_menutool, *ME_menuhelp;
GtkWidget *MI_new, *MI_open, *MI_save, *MI_saveas, *MI_import, *MI_exportqif, *MI_revert, *MI_openbak, *MI_properties, *MI_close, *MI_quit;
GtkWidget *MI_prefs, *MI_showtbar, *MI_showtotchart, *MI_showtimchart, *MI_showbotlist, *MI_eurominor;
GtkWidget *MI_manwal, *MI_manacc, *MI_manpay, *MI_mancat, *MI_mantpl, *MI_manbud, *MI_manbudtable, *MI_manasg, *MI_mancur, *MI_mantag;
GtkWidget *MI_txnadd, *MI_txnshow, *MI_txnshowall, *MI_scheduler, *MI_addscheduled;
GtkWidget *MI_repstat, *MI_reptime, *MI_repbal, *MI_repbudg, *MI_repvehi;
GtkWidget *MI_welcome, *MI_filestats, *MI_anonymise;
GtkWidget *MI_contents, *MI_online, *MI_updates, *MI_relnote, *MI_problem, *MI_translate, *MI_about;
GtkWidget *toolbar;
GtkWidget *BT_new, *BT_open, *BT_save;
GtkWidget *BT_manacc, *BT_manpay, *BT_mancat, *BT_mantpl, *BT_manbud, *BT_manasg;
GtkWidget *BT_txnshow, *BT_txnadd;
GtkWidget *BT_repstat, *BT_reptime, *BT_repbal, *BT_repbudg, *BT_repvehi;
GtkWidget *BT_help, *BT_donate;
GtkWidget *vpaned;
GtkWidget *hpaned;
/* panel: your account */
GtkWidget *LV_acc;
GtkWidget *BT_browse;
GtkWidget *BT_expandall;
GtkWidget *BT_collapseall;
gboolean showall;
GSimpleActionGroup *action_group_acc;
GHashTable *h_accgrp;
PnlAccGrp *totaccgrp;
//hub total
Filter *hubtot_filter;
//DataTable hobtot_dt;
GtkWidget *GR_hubtot;
GtkWidget *LB_hubtot;
GtkWidget *LV_hubtot;
gdouble hubtot_total;
GtkWidget *CY_hubtot_range;
GtkWidget *RE_hubtot_chart;
GSimpleActionGroup *hubtot_action_group;
//hub time
Filter *hubtim_filter;
DataTable *hubtim_dt;
gint hubtim_rows;
gint hubtim_cols;
GtkWidget *GR_hubtim;
GtkWidget *LB_hubtim;
GtkWidget *LV_hubtim;
gdouble hubtim_total;
GtkWidget *CY_hubtim_range;
GtkWidget *RE_hubtim_chart;
GSimpleActionGroup *hubtim_action_group;
//hub scheduled
GtkWidget *GR_upc;
GtkWidget *IM_info;
GtkWidget *LV_upc;
GtkWidget *BT_sched_skip;
GtkWidget *BT_sched_post;
GtkWidget *BT_sched_editpost;
GtkWidget *CY_sched_range;
GtkWidget *TX_selection;
GtkWidget *stack;
// max is 2=HUB_TXN_TYPE_REMIND, so 3 elt
GtkWidget *LV_txn[3];
gchar *wintitle;
Account *acc;
gint busy;
GtkRecentManager *recent_manager;
GtkWidget *recent_menu;
/*
UBYTE accnum;
UBYTE pad0;
struct Account *acc;
ULONG mindate, maxdate;
ULONG change;
ULONG keyvalue;
UBYTE title[140];
UBYTE Filename[108];
UBYTE csvpath[108];
*/
};
enum {
HB_WELCOME_OPENLAST = 2,
HB_WELCOME_CREATENEW,
HB_WELCOME_OPENEXISTING,
HB_WELCOME_OPENSAMPLE,
HB_WELCOME_READMANUAL,
HB_WELCOME_CONFIGPREF,
};
//temporary ?
void ui_wallet_defarchive(Archive *arc);
GtkWidget *ui_wallet_window_new(GtkWidget *do_widget);
void ui_wallet_populate_accounts(GtkWidget *widget, gpointer user_data);
void ui_wallet_open_check(GtkWidget *widget, gchar *filepath);
void ui_wallet_open_internal(GtkWidget *widget, gpointer user_data);
void ui_wallet_update(GtkWidget *widget, gpointer user_data);
gint ui_wallet_action_help_welcome(void);
#endif /* __HB_MANWINDOW_GTK_H__ */
homebank-5.9.7/src/hb-transaction.c 0000644 0001750 0001750 00000115711 15115062432 016466 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-transaction.h"
#include "hb-tag.h"
#include "hb-split.h"
//TODO: move this
#include "ui-dialogs.h"
/****************************************************************************/
/* Debug macro */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
da_transaction_clean(Transaction *item)
{
if(item != NULL)
{
if(item->memo != NULL)
{
g_free(item->memo);
item->memo = NULL;
}
if(item->number != NULL)
{
g_free(item->number);
item->number = NULL;
}
if(item->tags != NULL)
{
g_free(item->tags);
item->tags = NULL;
}
if(item->splits != NULL)
{
da_split_destroy(item->splits);
item->splits = NULL;
item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
}
}
}
void
da_transaction_free(Transaction *item)
{
if(item != NULL)
{
da_transaction_clean(item);
g_free(item);
}
}
Transaction *
da_transaction_malloc(void)
{
return g_malloc0(sizeof(Transaction));
}
Transaction *da_transaction_init(Transaction *txn, guint32 kacc)
{
guint32 date;
DB( g_print("da_transaction_init\n") );
//#1860309 keep the date when init for add/inherit
date = txn->date;
da_transaction_clean(txn);
memset(txn, 0, sizeof(Transaction));
txn->date = date;
//fix: 318733 / 1335285
if( PREFS->heritdate == FALSE )
txn->date = GLOBALS->today;
txn->kacc = kacc;
da_transaction_set_default_template(txn);
return txn;
}
Transaction *da_transaction_init_from_template(Transaction *txn, Archive *arc)
{
guint32 date;
DB( g_print("da_transaction_init_from_template\n") );
//#5.4.2 date must remains when we set a template, as template has no date
date = txn->date;
da_transaction_clean(txn);
txn->date = date;
txn->amount = arc->amount;
//#1258344 keep the current account if tpl is empty
if(arc->kacc)
txn->kacc = arc->kacc;
txn->paymode = arc->paymode;
txn->flags = arc->flags;
txn->status = arc->status;
txn->kpay = arc->kpay;
txn->kcat = arc->kcat;
txn->kxferacc = arc->kxferacc;
//#1673260
txn->xferamount = arc->xferamount;
if(arc->memo != NULL)
txn->memo = g_strdup(arc->memo);
if(arc->number != NULL)
txn->number = g_strdup(arc->number);
txn->tags = tags_clone(arc->tags);
txn->splits = da_splits_clone(arc->splits);
if( da_splits_length (txn->splits) > 0 )
txn->flags |= OF_SPLIT; //Flag that Splits are active
return txn;
}
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));
DB( g_print("da_transaction_clone\n") );
if(new_item)
{
//duplicate the string
new_item->memo = g_strdup(src_item->memo);
new_item->number = g_strdup(src_item->number);
//duplicate tags/splits
//no g_free here to avoid free the src tags (memdup copied the ptr)
new_item->tags = tags_clone(src_item->tags);
new_item->splits = da_splits_clone(src_item->splits);
if( da_splits_length (new_item->splits) > 0 )
new_item->flags |= OF_SPLIT; //Flag that Splits are active
}
return new_item;
}
GList *
da_transaction_new(void)
{
return NULL;
}
guint
da_transaction_length(void)
{
GList *lst_acc, *lnk_acc;
guint count = 0;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
count += g_queue_get_length (acc->txn_queue);
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
return count;
}
static void da_transaction_queue_free_ghfunc(Transaction *item, gpointer data)
{
da_transaction_free (item);
}
void da_transaction_destroy(void)
{
GList *lacc, *list;
lacc = g_hash_table_get_values(GLOBALS->h_acc);
list = g_list_first(lacc);
while (list != NULL)
{
Account *acc = list->data;
g_queue_foreach(acc->txn_queue, (GFunc)da_transaction_queue_free_ghfunc, NULL);
//txn queue is freed into account
list = g_list_next(list);
}
g_list_free(lacc);
}
// used from ledger only
static gint da_transaction_compare_datafunc(Transaction *a, Transaction *b, gpointer data)
{
gint retval = (gint)a->date - b->date;
if(!retval) //#1749457
retval = a->pos - b->pos;
return retval;
}
void da_transaction_queue_sort(GQueue *queue)
{
g_queue_sort(queue, (GCompareDataFunc)da_transaction_compare_datafunc, NULL);
}
static gint da_transaction_compare_func(Transaction *a, Transaction *b)
{
return ((gint)a->date - b->date);
}
GList *da_transaction_sort(GList *list)
{
return( g_list_sort(list, (GCompareFunc)da_transaction_compare_func));
}
gboolean da_transaction_insert_memo(gchar *memo, guint32 date)
{
if( date < (GLOBALS->today - PREFS->txn_memoacp_days) )
return FALSE;
if( memo != NULL )
{
if( g_hash_table_lookup(GLOBALS->h_memo, memo) == NULL )
{
g_hash_table_insert(GLOBALS->h_memo, g_strdup(memo), NULL);
return TRUE;
}
}
return FALSE;
}
gboolean da_transaction_insert_memos(Transaction *txn)
{
gboolean retval = FALSE;
if( txn->date < (GLOBALS->today - PREFS->txn_memoacp_days) )
return FALSE;
da_transaction_insert_memo(txn->memo, txn->date);
if( txn->splits != NULL )
{
for(guint i=0;isplits->len;i++)
{
Split *split = g_ptr_array_index(txn->splits, i);
if( split != NULL )
{
da_transaction_insert_memo(split->memo, txn->date);
}
}
}
return retval;
}
gboolean da_transaction_insert_sorted(Transaction *newitem)
{
Account *acc;
GList *lnk_txn;
acc = da_acc_get(newitem->kacc);
if(!acc)
return FALSE;
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *item = lnk_txn->data;
if(item->date <= newitem->date)
break;
lnk_txn = g_list_previous(lnk_txn);
}
// we're at insert point, insert after txn
g_queue_insert_after(acc->txn_queue, lnk_txn, newitem);
if(PREFS->txn_memoacp == TRUE)
{
DB( g_print(" add memo to completion\n") );
da_transaction_insert_memos(newitem);
}
return TRUE;
}
// nota: this is called only when loading xml file
gboolean da_transaction_prepend(Transaction *item)
{
Account *acc;
acc = da_acc_get(item->kacc);
//#1661279
if(!acc)
return FALSE;
item->kcur = acc->kcur;
g_queue_push_tail(acc->txn_queue, item);
return TRUE;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static guint32
da_transaction_get_max_kxfer(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
guint32 max_key = 0;
DB( g_print("da_transaction_get_max_kxfer\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
if( item->flags & OF_INTXFER )
{
max_key = MAX(max_key, item->kxfer);
}
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
DB( g_print(" max_key : %d \n", max_key) );
return max_key;
}
static void da_transaction_goto_orphan(Transaction *txn)
{
const gchar *oatn = "orphaned transactions";
Account *ori_acc, *acc;
gboolean found;
DB( g_print("\n[transaction] goto orphan\n") );
g_warning("txn consistency: moving to orphan %d '%s' %.2f", txn->date, txn->memo, txn->amount);
acc = da_acc_get_by_name((gchar *)oatn);
if(acc == NULL)
{
acc = da_acc_malloc();
acc->name = g_strdup(oatn);
da_acc_append(acc);
DB( g_print(" - created orphan acc %d\n", acc->key) );
}
ori_acc = da_acc_get(txn->kacc);
if( ori_acc )
{
found = g_queue_remove(ori_acc->txn_queue, txn);
DB( g_print(" - found in origin ? %d\n", found) );
if(found)
{
txn->kacc = acc->key;
da_transaction_insert_sorted (txn);
DB( g_print("moved txn to %d\n", txn->kacc) );
}
}
}
void da_transaction_set_flag(Transaction *item)
{
DB( g_print("\n[transaction] set flag\n") );
DB( g_print(" amnt=%f => %f\n", item->amount, item->xferamount) );
DB( g_print(" kxfer=%d\n", item->kxfer) );
DB( g_print(" in :: of_inc is %s\n", item->flags & OF_INCOME ? "set" : "unset" ) ) ;
//#2002348 no change if zero
if( item->amount != 0.0 )
{
item->flags &= ~(OF_INCOME);
if( item->amount > 0)
item->flags |= (OF_INCOME);
}
DB( g_print(" out :: of_inc is %s\n", item->flags & OF_INCOME ? "set" : "unset" ) );
}
void da_transaction_consistency(Transaction *item)
{
Account *acc;
Category *cat;
Payee *pay;
guint nbsplit;
DB( g_print("\n[transaction] consistency\n") );
DB( g_print(" %d %.2f %s\n", item->date, item->amount, item->memo) );
// ensure date is between range
item->date = CLAMP(item->date, HB_MINDATE, HB_MAXDATE);
// check account exists
acc = da_acc_get(item->kacc);
if(acc == NULL)
{
g_warning("txn consistency: fixed invalid acc %d", item->kacc);
da_transaction_goto_orphan(item);
GLOBALS->changes_count++;
}
// check category exists
cat = da_cat_get(item->kcat);
if(cat == NULL)
{
g_warning("txn consistency: fixed invalid cat %d", item->kcat);
item->kcat = 0;
GLOBALS->changes_count++;
}
//#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 %d for split txn", item->kcat);
item->kcat = 0;
GLOBALS->changes_count++;
}
}
// check payee exists
pay = da_pay_get(item->kpay);
if(pay == NULL)
{
g_warning("txn consistency: fixed invalid pay %d", item->kpay);
item->kpay = 0;
GLOBALS->changes_count++;
}
// 5.3: fix split on intxfer
if( ((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) && (item->splits != NULL) )
{
g_warning("txn consistency: fixed invalid split on xfer");
item->flags &= ~(OF_INTXFER);
item->paymode = PAYMODE_XFER;
item->kxfer = 0;
item->kxferacc = 0;
}
// reset dst acc for non xfer transaction
if( !((item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER)) )
{
if( (item->kxfer != 0) || (item->kxferacc != 0) )
{
g_warning("txn consistency: fixed invalid xfer");
item->kxfer = 0;
item->kxferacc = 0;
}
}
// intxfer: check dst account exists
if( (item->flags & OF_INTXFER) || (item->paymode == OLDPAYMODE_INTXFER) )
{
gint tak = item->kxferacc;
item->kxferacc = ABS(tak); //I crossed negative here one day
acc = da_acc_get(item->kxferacc);
if(acc == NULL)
{
g_warning("txn consistency: fixed invalid dst_acc %d", item->kxferacc);
da_transaction_goto_orphan(item);
item->kxfer = 0;
item->paymode = PAYMODE_XFER;
GLOBALS->changes_count++;
}
}
//#1628678 tags for internal xfer should be checked as well
//#1787826 intxfer should not have split
//#1295877 ensure income flag is correctly set
da_transaction_set_flag(item);
//#1308745 ensure remind flag unset if reconciled
//useless since 5.0
//if( item->flags & OF_VALID )
// item->flags &= ~(OF_REMIND);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* new transfer functions */
Transaction *transaction_xfer_child_new_from_txn(Transaction *stxn)
{
Transaction *child;
Account *acc;
child = da_transaction_clone(stxn);
//#1673260 deal with multi currency amount
if( !(stxn->flags & OF_ADVXFER) )
{
child->amount = -stxn->amount;
//TODO:maybe
//child->xferamount = 0;
}
else
{
child->amount = stxn->xferamount;
child->xferamount = stxn->amount;
}
da_transaction_set_flag(child); // set income/xpense
//#1268026 #1690555 #2067855
if( PREFS->xfer_syncstat == FALSE )
{
if( (child->status == TXN_STATUS_CLEARED) || (child->status == TXN_STATUS_RECONCILED) )
child->status = TXN_STATUS_NONE;
//child->flags &= ~(OF_VALID); // delete reconcile state
}
child->kacc = child->kxferacc;
child->kxferacc = stxn->kacc;
acc = da_acc_get( child->kacc );
if( acc != NULL )
child->kcur = acc->kcur;
return child;
}
static void transaction_xfer_create_child(Transaction *stxn)
{
Transaction *child;
Account *acc;
DB( g_print("\n[transaction] xfer_create_child\n") );
if( stxn->kxferacc > 0 )
{
child = transaction_xfer_child_new_from_txn(stxn);
stxn->flags |= (OF_INTXFER);
child->flags |= (OF_INTXFER);
stxn->dspflags |= (FLAG_TMP_EDITED);
child->dspflags |= (FLAG_TMP_ADDED);
/* update acc flags */
acc = da_acc_get( child->kacc );
if(acc != NULL)
{
acc->dspflags |= FLAG_ACC_TMP_ADDED;
//strong link
guint maxkey = da_transaction_get_max_kxfer();
DB( g_print(" + maxkey is %d\n", maxkey) );
stxn->kxfer = maxkey+1;
child->kxfer = maxkey+1;
DB( g_print(" + strong link to %d\n", stxn->kxfer) );
DB( g_print(" + add transfer, %p to acc %d\n", child, acc->key) );
da_transaction_insert_sorted(child);
account_balances_add (child);
}
}
}
//#1708974 enable different date
//#1987975 only suggest opposite sign amount txn
static gboolean transaction_xfer_child_might(Transaction *stxn, Transaction *dtxn, guchar *matchrate)
{
gboolean retval = FALSE;
gint32 daygap = PREFS->xfer_daygap;
gint rate = 100;
DB( g_print("\n[transaction] xfer_child_might\n") );
//paranoia check
if ( (stxn == dtxn) || (stxn->kacc == dtxn->kacc) )
return FALSE;
DB( g_print(" src: %d %d %d %f %d\n", stxn->kcur, stxn->date, stxn->kacc, stxn->amount, stxn->kxfer ) );
DB( g_print(" dst: %d %d %d %f %d\n", dtxn->kcur, dtxn->date, dtxn->kacc, dtxn->amount, dtxn->kxfer ) );
//keep only normal dtxn
if( dtxn->kxfer != 0 )
return FALSE;
//#1708974 date allow +/- daygap
if( (dtxn->date > (stxn->date + daygap)) && (dtxn->date > (stxn->date - daygap)) )
return FALSE;
daygap = (stxn->date - dtxn->date);
rate -= ABS(daygap);
if( stxn->kcur == dtxn->kcur )
{
//#1987975 only suggest opposite sign amount txn
if( stxn->amount == -dtxn->amount )
{
DB( g_print(" >found same curr + same amt\n") );
retval = TRUE;
}
}
//#2047647 cross currency target txn proposal, with 10% gap
else
{
gdouble minamt = stxn->xferamount * 0.9;
gdouble maxamt = stxn->xferamount * 1.1;
DB( g_print(" >check %.2f [%.2f...%.2f]\n", dtxn->amount, minamt, maxamt) );
if( hb_amount_between(dtxn->amount, minamt, maxamt) == TRUE)
{
rate -= ABS(dtxn->amount - stxn->xferamount);
DB( g_print(" >found !=curr\n") );
retval = TRUE;
}
}
//other rate
if( stxn->kpay != dtxn->kpay)
rate -= 1;
if( stxn->kcat != dtxn->kcat)
rate -= 1;
if( hb_string_compare(stxn->memo, dtxn->memo) != 0 )
rate -= 1;
//TODO: maybe add info & tag here
rate = CLAMP(rate, 0, 100);
*matchrate = rate;
DB( g_print(" >return %d\n\n", retval) );
return retval;
}
static GList *transaction_xfer_child_might_list_get(Transaction *ope, guint32 kdstacc)
{
GList *lst_acc, *lnk_acc;
GList *list, *matchlist = NULL;
DB( g_print("\n[transaction] xfer_child_might_list_get\n") );
DB( g_print(" - kdstacc:%d\n", kdstacc) );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
if( !(acc->flags & AF_CLOSED) && (acc->key != ope->kacc) && ( (acc->key == kdstacc) || kdstacc == 0 ) )
{
list = g_queue_peek_tail_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
// no need to go higher than src txn date - daygap
if(item->date < (ope->date - PREFS->xfer_daygap))
{
DB( g_print(" break date: %d < %d\n", item->date , (ope->date - PREFS->xfer_daygap)) );
break;
}
if( transaction_xfer_child_might(ope, item, &item->matchrate) == TRUE )
{
DB( g_print(" - match %3d: %d %s %f %d=>%d\n", item->matchrate, item->date, item->memo, item->amount, item->kacc, item->kxferacc) );
matchlist = g_list_append(matchlist, item);
}
list = g_list_previous(list);
}
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
return matchlist;
}
gint transaction_xfer_search_or_add_child(GtkWindow *parent, gboolean addmode, Transaction *ope, guint32 kdstacc)
{
GList *matchlist;
gint count;
gint result = GTK_RESPONSE_NONE;
Transaction *child = NULL;
DB( g_print("\n[transaction] xfer_search_or_add_child\n") );
matchlist = transaction_xfer_child_might_list_get(ope, kdstacc);
count = g_list_length(matchlist);
DB( g_print(" matchlist : %d match found\n", count) );
//#2044601 user pref always popup in addmode
//prior if count=0 we were inserting without asking user
if( count == 0 )
{
//we should create the child
DB( g_print(" force create new\n") );
result = HB_RESPONSE_CREATE_NEW;
}
//#2044601 addmode=1 means we add from main/ledger window
DB( g_print(" addmode : %d\n", addmode) );
DB( g_print(" showdialog: %d\n", PREFS->xfer_showdialog) );
if( (result == GTK_RESPONSE_NONE) || (addmode == TRUE && PREFS->xfer_showdialog == TRUE ) )
{
DB( g_print(" popup action txn target\n") );
result = ui_dialog_transaction_xfer_select_child(parent, ope, matchlist, &child);
}
switch( result )
{
case HB_RESPONSE_SELECTION:
//#1827193 in case child is null...
DB( g_print(" link to child %p\n", child) );
if( child != NULL )
transaction_xfer_change_to_child(ope, child);
break;
case HB_RESPONSE_CREATE_NEW:
DB( g_print(" create child\n") );
transaction_xfer_create_child(ope);
break;
//GTK_RESPONSE_CANCEL
default:
//#2044601 addmode no action if user cancel
if( addmode == FALSE )
{
DB( g_print(" change normal txn\n") );
transaction_xfer_change_to_normal(ope);
da_transaction_set_flag(ope);
}
ope->dspflags &= ~(FLAG_TMP_EDITED);
break;
}
g_list_free(matchlist);
DB( g_print(" > return %d\n", result) );
return result;
}
Transaction *transaction_xfer_child_strong_get(Transaction *src)
{
Account *dstacc;
GList *list;
DB( g_print("\n[transaction] xfer_child_strong_get\n") );
dstacc = da_acc_get(src->kxferacc);
if( !dstacc || src->kxfer <= 0 )
return NULL;
DB( g_print(" - search: %d %s %f %d=>%d - %d\n", src->date, src->memo, src->amount, src->kacc, src->kxferacc, src->kxfer) );
list = g_queue_peek_tail_link(dstacc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
//#1252230
//if( item->paymode == PAYMODE_INTXFER
// && item->kacc == src->kxferacc
if( (item->flags & OF_INTXFER)
&& (item->kxfer == src->kxfer)
&& (item != src) )
{
DB( g_print(" - found : %d %s %f %d=>%d - %d\n", item->date, item->memo, item->amount, item->kacc, item->kxferacc, src->kxfer) );
return item;
}
list = g_list_previous(list);
}
DB( g_print(" - not found...\n") );
return NULL;
}
void transaction_xfer_change_to_normal(Transaction *ope)
{
DB( g_print("\n[transaction] xfer_change_to_normal\n") );
//remove xfer flags
ope->flags &= ~(OF_INTXFER|OF_ADVXFER);
ope->kxfer = 0;
ope->kxferacc = 0;
ope->xferamount = 0;
}
//TODO: should be static but used in hb_import
void transaction_xfer_change_to_child(Transaction *stxn, Transaction *child)
{
Account *dstacc;
DB( g_print("\n[transaction] xfer_change_to_child\n") );
stxn->flags |= (OF_INTXFER);
child->flags |= (OF_INTXFER);
stxn->dspflags |= (FLAG_TMP_EDITED);
child->dspflags |= (FLAG_TMP_EDITED);
/* update acc flags */
dstacc = da_acc_get( child->kacc);
if(dstacc != NULL)
dstacc->dspflags |= FLAG_ACC_TMP_EDITED;
//strong link
guint maxkey = da_transaction_get_max_kxfer();
stxn->kxfer = maxkey+1;
child->kxfer = maxkey+1;
stxn->kxferacc = child->kacc;
child->kxferacc = stxn->kacc;
//#1673260 enable different currency
if(stxn->kcur == child->kcur)
{
DB( g_print(" sync amount (==kcur)\n") );
child->amount = -stxn->amount;
}
else
{
DB( g_print(" sync amount (!=kcur)\n") );
//we keep the original child amount
//child->amount = stxn->xferamount;
child->xferamount = stxn->amount;
child->flags |= (OF_ADVXFER);
}
}
void transaction_xfer_child_sync(Transaction *s_txn, Transaction *child)
{
Account *acc;
DB( g_print("\n[transaction] xfer_child_sync\n") );
if( child == NULL )
{
DB( g_print(" - no child found\n") );
return;
}
DB( g_print(" - found do sync\n") );
/* update acc flags */
acc = da_acc_get( child->kacc);
if(acc != NULL)
acc->dspflags |= FLAG_ACC_TMP_EDITED;
account_balances_sub (child);
//# 1708974 enable different date
//# 2065633 with option
if( PREFS->xfer_syncdate == TRUE )
child->date = s_txn->date;
//#2019193 option the sync xfer status
//#2125300 overide if TXN_STATUS_VOID
if( (PREFS->xfer_syncstat == TRUE) || (s_txn->status == TXN_STATUS_VOID) )
{
//#2111359 remind status not synced
child->flags &= ~(OF_REMIND);
if(s_txn->flags & OF_REMIND)
child->flags |= OF_REMIND;
child->status = s_txn->status;
child->dspflags |= FLAG_TMP_EDITED;
}
//#1673260 enable different currency
if( !(child->flags & OF_ADVXFER) )
{
DB( g_print(" sync amount (==kcur)\n") );
child->amount = -s_txn->amount;
}
else
{
DB( g_print(" sync amount (!=kcur)\n") );
child->amount = s_txn->xferamount;
child->xferamount = s_txn->amount;
}
child->dspflags |= FLAG_TMP_EDITED;
//#1295877 flag update to avoid bad column display
child->flags &= ~(OF_INCOME);
if( child->amount > 0 )
child->flags |= (OF_INCOME);
// ensure to have INC to good display payee direction
else if( child->amount == 0 )
{
if( s_txn->amount < 0 )
child->flags |= (OF_INCOME);
}
child->kpay = s_txn->kpay;
child->kcat = s_txn->kcat;
if(child->memo)
g_free(child->memo);
child->memo = g_strdup(s_txn->memo);
if(child->number)
g_free(child->number);
child->number = g_strdup(s_txn->number);
account_balances_add (child);
//#1252230 sync account also
//#1663789 idem after 5.1
//source changed: update child key (move of s_txn is done in external_edit)
if( s_txn->kacc != child->kxferacc )
{
child->kxferacc = s_txn->kacc;
}
//dest changed: move child & update child key
if( s_txn->kxferacc != child->kacc )
{
transaction_acc_move(child, child->kacc, s_txn->kxferacc);
}
//synchronise tags since 5.1
g_free(child->tags);
child->tags = tags_clone (s_txn->tags);
}
void transaction_xfer_remove_child(Transaction *src)
{
Transaction *dst;
DB( g_print("\n[transaction] xfer_remove_child\n") );
dst = transaction_xfer_child_strong_get( src );
if( dst != NULL )
{
Account *acc = da_acc_get(dst->kacc);
if( acc != NULL )
{
DB( g_print("deleting...") );
account_balances_sub(dst);
g_queue_remove(acc->txn_queue, dst);
//#1419304 we keep the deleted txn to a trash stack
//da_transaction_free (dst);
//g_trash_stack_push(&GLOBALS->txn_stk, dst);
GLOBALS->deltxn_list = g_slist_prepend(GLOBALS->deltxn_list, dst);
//#1691992
acc->dspflags |= FLAG_ACC_TMP_EDITED;
}
}
src->kxfer = 0;
src->kxferacc = 0;
}
// still useful for upgrade from < file v0.6 (hb v4.4 kxfer)
Transaction *transaction_old_get_child_transfer(Transaction *src)
{
Account *acc;
GList *list;
DB( g_print("\n[transaction] get_child_transfer\n") );
//DB( g_print(" search: %d %s %f %d=>%d\n", src->date, src->memo, src->amount, src->account, src->kxferacc) );
acc = da_acc_get(src->kxferacc);
if( acc != NULL )
{
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
// no need to go higher than src txn date
if(item->date > src->date)
break;
// keep this one for backward compatibility
if( item->paymode == OLDPAYMODE_INTXFER)
{
if( src->date == item->date &&
src->kacc == item->kxferacc &&
src->kxferacc == item->kacc &&
ABS(src->amount) == ABS(item->amount) )
{
//DB( g_print(" found : %d %s %f %d=>%d\n", item->date, item->memo, item->amount, item->account, item->kxferacc) );
return item;
}
}
list = g_list_next(list);
}
}
DB( g_print(" not found...\n") );
return NULL;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gchar *transaction_get_status_string(Transaction *txn)
{
gchar *retval = "";
if(txn->status == TXN_STATUS_CLEARED)
retval = "c";
else
if(txn->status == TXN_STATUS_RECONCILED)
retval = "R";
else
//#2051307 5.7.4 allow remind
//if(txn->status == TXN_STATUS_REMIND)
if(txn->flags & OF_REMIND)
retval = "?";
else
if(txn->status == TXN_STATUS_VOID)
retval = "v";
return retval;
}
gint transaction_get_type(Transaction *txn)
{
//TRANSFER has priority on INCOME
if(txn->flags & OF_INTXFER)
return TXN_TYPE_INTXFER;
else
if(txn->flags & OF_INCOME)
return TXN_TYPE_INCOME;
return TXN_TYPE_EXPENSE;
}
gboolean transaction_is_balanceable(Transaction *ope)
{
//#1875100/#2061227
if( ope->flags & (OF_ISIMPORT|OF_ISPAST) )
return FALSE;
//#1812598
//if( (ope->status == TXN_STATUS_REMIND) && (PREFS->includeremind == FALSE) )
if( (ope->flags & OF_REMIND) && (PREFS->includeremind == FALSE) )
return FALSE;
if( (ope->status == TXN_STATUS_VOID) )
return FALSE;
return TRUE;
}
void transaction_remove(Transaction *ope)
{
Account *acc;
//controls accounts valid (archive scheduled maybe bad)
acc = da_acc_get(ope->kacc);
if(acc == NULL) return;
account_balances_sub(ope);
if( ope->flags & OF_INTXFER )
{
transaction_xfer_remove_child( ope );
}
g_queue_remove(acc->txn_queue, ope);
acc->dspflags |= FLAG_ACC_TMP_EDITED;
//#1419304 we keep the deleted txn to a trash stack
//da_transaction_free(entry);
//g_trash_stack_push(&GLOBALS->txn_stk, ope);
GLOBALS->deltxn_list = g_slist_prepend(GLOBALS->deltxn_list, ope);
}
void transaction_changed(Transaction *txn, gboolean saverecondate)
{
Account *acc;
if( txn == NULL )
return;
acc = da_acc_get(txn->kacc);
if(acc == NULL)
return;
acc->dspflags |= FLAG_ACC_TMP_EDITED;
//#1581863 store reconciled date
if( saverecondate == TRUE )
acc->rdate = GLOBALS->today;
}
Transaction *transaction_add(GtkWindow *parent, gboolean addmode, Transaction *ope)
{
Transaction *newope;
Account *acc, *dacc;
gboolean do_add;
DB( g_print("\n[transaction] transaction_add\n") );
do_add = TRUE;
//controls accounts valid (archive scheduled maybe bad)
acc = da_acc_get(ope->kacc);
//#1972078 forbid on a closed account
if( (acc == NULL) || (acc->flags & AF_CLOSED) )
return NULL;
DB( g_print(" acc is '%s' %d\n", acc->name, acc->key) );
if(ope->flags & OF_INTXFER)
{
//controls accounts valid (archive scheduled maybe bad)
dacc = da_acc_get(ope->kxferacc);
//#1972078 forbid on a closed account
if( (dacc == NULL) || (dacc->flags & AF_CLOSED) )
return NULL;
}
//affect currency, mandatory to get a valid display in xfer selection dialog
ope->kcur = acc->kcur;
//allocate a new entry and copy from our edited structure
newope = da_transaction_clone(ope);
//let the user select a xfer action if necessary
if(ope->flags & OF_INTXFER)
{
gint response_id;
//#2044601 can return GTK_RESPONSE_CANCEL
response_id = transaction_xfer_search_or_add_child(parent, addmode, newope, newope->kxferacc);
if( response_id == GTK_RESPONSE_CANCEL )
do_add = FALSE;
}
DB( g_print(" do_add = %d\n", do_add) );
//real addition here
if( do_add == TRUE )
{
//todo: a deplacer dans account
/* store a new cheque number into account */
if( (newope->paymode == PAYMODE_CHECK) && (newope->number) && !(newope->flags & OF_INCOME) )
{
guint cheque;
/* get the active account and the corresponding cheque number */
acc = da_acc_get( newope->kacc);
if( acc != NULL )
{
cheque = atol(newope->number);
//DB( g_print(" -> should store cheque number %d to %d", cheque, newope->account) );
if( newope->flags & OF_CHEQ2 )
{
acc->cheque2 = MAX(acc->cheque2, cheque);
}
else
{
acc->cheque1 = MAX(acc->cheque1, cheque);
}
}
}
acc->dspflags |= FLAG_ACC_TMP_ADDED;
DB( g_print(" + add normal %p to acc %d\n", newope, acc->key) );
//da_transaction_append(newope);
da_transaction_insert_sorted(newope);
newope->dspflags |= FLAG_TMP_ADDED;
//#2061227 flag txn to be reviewed if too old
//#2107649 do it before eval balance
if( ( PREFS->safe_pend_recon == TRUE && (acc->rdate > 0 && newope->date < acc->rdate) )
|| ( PREFS->safe_pend_past == TRUE && (newope->date < (GLOBALS->today - PREFS->safe_pend_past_days)) )
)
{
DB( g_print(" flag as in past to approve\n") );
newope->flags |= OF_ISPAST;
acc->nb_pending++;
account_flags_eval(acc);
}
account_balances_add(newope);
if(newope->flags & OF_INTXFER)
{
// delete any splits
da_split_destroy(ope->splits);
ope->splits = NULL;
ope->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
}
//#1581863 store reconciled date
if( newope->status == TXN_STATUS_RECONCILED )
acc->rdate = GLOBALS->today;
}
else
{
DB( g_print(" clean newope and return NULL\n") );
da_transaction_clean(newope);
newope = NULL;
}
return newope;
}
gboolean transaction_acc_move(Transaction *txn, guint32 okacc, guint32 nkacc)
{
Account *oacc, *nacc;
DB( g_print("\n[transaction] acc_move\n") );
if( okacc == nkacc )
return TRUE;
oacc = da_acc_get(okacc);
nacc = da_acc_get(nkacc);
if( oacc && nacc )
{
//#2109854 manage pending
if( txn->flags & (OF_ISPAST | OF_ISIMPORT) )
{
if( oacc->nb_pending > 0 )
oacc->nb_pending--;
account_flags_eval(oacc);
nacc->nb_pending--;
account_flags_eval(nacc);
}
account_balances_sub(txn);
//remove from old
if( g_queue_remove(oacc->txn_queue, txn) )
{
//add to new
g_queue_push_tail(nacc->txn_queue, txn);
txn->kacc = nacc->key;
txn->kcur = nacc->kcur;
nacc->dspflags |= FLAG_ACC_TMP_EDITED;
account_balances_add(txn);
//#1865083 src acc also changed (balance)
oacc->dspflags |= FLAG_ACC_TMP_EDITED;
return TRUE;
}
else
{
//ensure to keep txn into current account
txn->kacc = okacc;
account_balances_add(txn);
}
}
return FALSE;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gboolean transaction_similar_match(Transaction *stxn, Transaction *dtxn, guint32 daygap)
{
gboolean retval = FALSE;
if(stxn == dtxn)
return FALSE;
DB( g_print(" date: %d - %d = %d\n", stxn->date, dtxn->date, stxn->date - dtxn->date) );
if( ( stxn->kcur == dtxn->kcur )
&& ( dtxn->date <= (stxn->date + daygap) )
&& ( dtxn->date >= (stxn->date - daygap) )
&& ( hb_amount_cmp(stxn->amount, dtxn->amount) == 0 )
//5.8 removed memo, later propose option
//&& (hb_string_compare(stxn->memo, dtxn->memo) == 0)
//todo: later propose option category/payee
)
{
retval = TRUE;
}
return retval;
}
void transaction_check_chkcatsign_unmark(Account *acc)
{
GList *lnk_txn;
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *stxn = lnk_txn->data;
stxn->dspflags &= ~(FLAG_TMP_CHKSIGN);
lnk_txn = g_list_previous(lnk_txn);
}
}
gint transaction_check_chkcatsign_mark(Account *acc)
{
GList *lnk_txn;
gint count = 0;
//mark category sign
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
DB( g_print("------\n eval src: %d, '%s', '%s', %.2f\n", txn->date, txn->number, txn->memo, txn->amount) );
if( (txn->flags & OF_INTXFER) == FALSE )
{
txn->dspflags &= ~(FLAG_TMP_CHKSIGN);
if(txn->amount != 0.0)
{
gint type = (txn-> amount > 0) ? 1 : -1;
Category *cat = da_cat_get(txn->kcat);
if( cat != NULL && (category_type_get(cat) != type) )
{
txn->dspflags |= FLAG_TMP_CHKSIGN;
count++;
}
}
}
lnk_txn = g_list_previous(lnk_txn);
}
DB( g_print(" - found: %d\n", count ) );
return count;
}
void transaction_similar_unmark(Account *acc)
{
GList *lnk_txn;
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *stxn = lnk_txn->data;
stxn->dspflags &= ~(FLAG_TMP_DUPSRC|FLAG_TMP_DUPDST);
stxn->dupgid = 0;
//stxn->marker = TXN_MARK_NONE;
lnk_txn = g_list_previous(lnk_txn);
}
}
gint transaction_similar_mark(Account *acc, guint32 daygap)
{
GList *lnk_txn, *list2;
gint nball = 0;
gint nbdup = 0;
gchar tmpgid = 1;
//warning the list must be sorted by date then amount
//ideally (easier to parse) we shoudl get a list sorted by amount, then date
DB( g_print("\n[transaction] check duplicate\n") );
DB( g_print("\n - account:'%s' gap:%d\n", acc->name, daygap) );
#if MYDEBUG == 1
GTimer *t = g_timer_new();
g_print(" - start parse\n");
#endif
/*
llast = g_list_last(old ope list);
DB( g_print("- end last : %f sec\n", g_timer_elapsed(t, NULL)) );
g_timer_reset(t);
ltxn = llast->data;
g_date_clear(&gd, 1);
g_date_set_julian(&gd, ltxn->date);
g_print(" - last julian=%u %02d-%02d-%04d\n", ltxn->date, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
minjulian = ltxn->date - (366*2);
g_date_clear(&gd, 1);
g_date_set_julian(&gd, minjulian);
g_print(" - min julian=%u %02d-%02d-%04d\n", minjulian, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
*/
transaction_similar_unmark(acc);
//mark duplicate
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *stxn = lnk_txn->data;
//if(stxn->date < minjulian)
// break;
DB( g_print("------\n eval src: %d, '%s', '%s', %.2f\n", stxn->date, stxn->number, stxn->memo, stxn->amount) );
list2 = g_list_previous(lnk_txn);
while (list2 != NULL)
{
Transaction *dtxn = list2->data;
DB( g_print(" + with dst: %d, '%s', '%s', %.2f\n", dtxn->date, dtxn->number, dtxn->memo, dtxn->amount) );
if( (stxn->date - dtxn->date) > daygap )
{
DB( g_print(" break %d %d\n", (dtxn->date - daygap) , (stxn->date - daygap)) );
break;
}
//if( dtxn->marker == TXN_MARK_NONE )
if( (dtxn->dspflags & (FLAG_TMP_DUPSRC|FLAG_TMP_DUPDST)) == 0 )
{
if( transaction_similar_match(stxn, dtxn, daygap) )
{
//stxn->marker = TXN_MARK_DUPSRC;
//dtxn->marker = TXN_MARK_DUPDST;
stxn->dspflags |= FLAG_TMP_DUPSRC;
dtxn->dspflags |= FLAG_TMP_DUPDST;
stxn->dupgid = tmpgid;
dtxn->dupgid = tmpgid;
DB( g_print(" = dtxn marker=%d\n", dtxn->dspflags) );
nball++;
}
}
else
{
DB( g_print(" already marked %d\n", dtxn->dspflags) );
}
list2 = g_list_previous(list2);
}
DB( g_print(" = stxn marker=%d\n", stxn->dspflags) );
//if( stxn->marker == TXN_MARK_DUPSRC )
if( stxn->dspflags & FLAG_TMP_DUPSRC )
{
tmpgid++;
nbdup++;
}
lnk_txn = g_list_previous(lnk_txn);
}
DB( g_print(" - end parse : %f sec\n", g_timer_elapsed(t, NULL)) );
DB( g_timer_destroy (t) );
DB( g_print(" - found: %d/%d dup\n", nbdup, nball ) );
return nbdup;
}
guint
transaction_auto_all_from_payee(GList *txnlist)
{
GList *list;
Payee *pay;
guint changes = 0;
DB( g_print("\n[transaction] auto from payee\n") );
DB( g_print(" n_txn=%d\n", g_list_length(txnlist)) );
list = g_list_first(txnlist);
while(list != NULL)
{
Transaction *txn = list->data;
if( txn != NULL )
{
pay = da_pay_get(txn->kpay);
if( pay != NULL )
{
if( txn->kcat == 0 )
{
txn->kcat = pay->kcat;
changes++;
}
if( txn->paymode == PAYMODE_NONE )
{
txn->paymode = pay->paymode;
changes++;
}
}
}
list = g_list_next(list);
}
return changes;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* = = experimental = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
probably add a structure hosted into a glist here
with kind of problem: duplicate, child xfer, orphan xfer
and collect all that with target txn
*/
/*void future_transaction_test_account(Account *acc)
{
GList *lnk_txn, *list2;
gint nball = 0;
gint nbdup = 0;
gint nbxfer = 0;
GPtrArray *array;
//future
gint gapday = 0, i;
//warning the list must be sorted by date then amount
//ideally (easier to parse) we shoudl get a list sorted by amount, then date
DB( g_print("\n[transaction] check duplicate\n") );
DB( g_print("\n - account:'%s'\n", acc->name) );
GTimer *t = g_timer_new();
g_print(" - start parse\n");
llast = g_list_last(old ope list);
DB( g_print("- end last : %f sec\n", g_timer_elapsed(t, NULL)) );
g_timer_reset(t);
ltxn = llast->data;
g_date_clear(&gd, 1);
g_date_set_julian(&gd, ltxn->date);
g_print(" - last julian=%u %02d-%02d-%04d\n", ltxn->date, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
minjulian = ltxn->date - (366*2);
g_date_clear(&gd, 1);
g_date_set_julian(&gd, minjulian);
g_print(" - min julian=%u %02d-%02d-%04d\n", minjulian, g_date_get_day (&gd), g_date_get_month (&gd), g_date_get_year(&gd));
array = g_ptr_array_sized_new (25);
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *stxn = lnk_txn->data;
//if(stxn->date < minjulian)
// break;
DB( g_print("------\n eval src: %d, '%s', '%s', %2.f\n", stxn->date, stxn->number, stxn->memo, stxn->amount) );
stxn->marker = 0;
list2 = g_list_previous(lnk_txn);
while (list2 != NULL)
{
Transaction *dtxn = list2->data;
stxn->marker = 0;
if( (dtxn->date + gapday) < (stxn->date + gapday) )
break;
DB( g_print(" + with dst: %d, '%s', '%s', %2.f\n", dtxn->date, dtxn->number, dtxn->memo, dtxn->amount) );
if( transaction_similar_match(stxn, dtxn, gapday) )
{
g_ptr_array_add (array, stxn);
g_ptr_array_add (array, dtxn);
nbdup++;
DB( g_print(" + dst=1 src=1\n") );
}
nball++;
list2 = g_list_previous(list2);
}
lnk_txn = g_list_previous(lnk_txn);
}
DB( g_print(" - end parse : %f sec\n", g_timer_elapsed(t, NULL)) );
DB( g_timer_destroy (t) );
for(i=0;ilen;i++)
{
Transaction *txn = g_ptr_array_index(array, i);
txn->marker = 1;
}
g_ptr_array_free(array, TRUE);
DB( g_print(" - found: %d/%d dup, %d xfer\n", nbdup, nball, nbxfer ) );
}
//todo: add a limitation, no need to go through all txn
// 1 year in th past, or abolute number ?
gint future_transaction_test_notification(void)
{
GList *lst_acc, *lnk_acc;
DB( g_print("\ntransaction_test_notification\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
transaction_similar_mark(acc);
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
return 0;
}
*/
homebank-5.9.7/src/ui-assist-import.c 0000644 0001750 0001750 00000225544 15120467230 017012 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hub-account.h"
#include "hb-import.h"
#include "ui-assist-import.h"
#include "dsp-mainwindow.h"
#include "list-operation.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern HbKvData CYA_IMPORT_DATEORDER[];
extern HbKvData CYA_IMPORT_OFXNAME[];
extern HbKvData CYA_IMPORT_OFXMEMO[];
static void ui_import_page_filechooser_eval(GtkWidget *widget, gpointer user_data);
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG == 1
static void list_txn_cell_data_function_debug (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *gentxn;
gchar *text;
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &gentxn,
-1);
text = g_strdup_printf("%d %d > %d", gentxn->is_imp_similar, gentxn->is_dst_similar, gentxn->to_import);
g_object_set(renderer,
"text", text,
NULL);
g_free(text);
}
#endif
static void list_txn_cell_data_function_toggle (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *gentxn;
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &gentxn,
-1);
g_object_set(renderer, "active", gentxn->to_import, NULL);
}
static void list_txn_cell_data_function_warning (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *gentxn;
gchar *iconname = NULL;
// get the transaction
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &gentxn,
-1);
//iconname = ( gentxn->julian == 0 ) ? ICONNAME_WARNING : NULL;
//if(iconname == NULL)
iconname = ( gentxn->is_dst_similar || gentxn->is_imp_similar ) ? ICONNAME_HB_ITEM_SIMILAR : NULL;
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void list_txn_cell_data_function_error (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *gentxn;
gchar *iconname = NULL;
// get the transaction
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &gentxn,
-1);
iconname = ( gentxn->julian == 0 ) ? ICONNAME_ERROR : NULL;
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void list_txn_cell_data_function_text (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gint colid = GPOINTER_TO_INT(user_data);
gchar buf[12];
GDate date;
gchar *text = "";
GenTxn *item;
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &item,
-1);
switch(colid)
{
case LST_DSPOPE_DATE: //date
{
gchar *color = NULL;
if(item->julian > 0)
{
g_date_set_julian(&date, item->julian);
//#1794170 %F is ignored under ms windows
//g_date_strftime (buf, 12-1, "%F", &date);
g_date_strftime (buf, 12-1, "%Y-%m-%d", &date);
text = buf;
}
else
{
text = item->date;
color = PREFS->color_warn;
}
g_object_set(renderer,
"foreground", color,
NULL);
}
//g_object_set(renderer, "text", item->date, NULL);
break;
case LST_DSPOPE_MEMO: //memo
text = item->memo;
break;
case LST_DSPOPE_PAYEE: //payee
text = item->payee;
break;
case LST_DSPOPE_CATEGORY: //category
text = item->category;
break;
}
g_object_set(renderer,
"text", text,
//"scale-set", TRUE,
//"scale", item->to_import ? 1.0 : 0.8,
"strikethrough-set", TRUE,
"strikethrough", item->to_import ? FALSE : TRUE,
NULL);
}
/*
** amount cell function
*/
static void list_txn_cell_data_function_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *item;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gdouble amount;
gchar *color;
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &item,
-1);
//#1866456
amount = (item->togamount == TRUE) ? -item->amount : item->amount;
//todo: we could use digit and currency of target account
//hb_strfnum(buf, G_ASCII_DTOSTR_BUF_SIZE-1, item->amount, GLOBALS->kcur, FALSE);
//hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, ope->amount, GLOBALS->minor);
g_ascii_formatd(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, "%.2f", amount);
color = get_normal_color_amount(amount);
g_object_set(renderer,
"foreground", color,
"text", formatd_buf,
NULL);
}
static void list_txn_cell_data_function_info (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenTxn *item;
gtk_tree_model_get(model, iter,
LST_GENTXN_POINTER, &item,
-1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
g_object_set(renderer, "icon-name", get_paymode_icon_name(item->paymode), NULL);
break;
case 2:
g_object_set(renderer, "text", item->number, NULL);
break;
}
}
static void list_txn_importfixed_toggled (GtkCellRendererToggle *cell, gchar *path_str, gpointer userdata)
{
GtkTreeView *treeview;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
GenTxn *gentxn;
g_return_if_fail(GTK_IS_TREE_VIEW(userdata));
treeview = userdata;
model = gtk_tree_view_get_model(treeview);
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_GENTXN_POINTER, &gentxn, -1);
gentxn->to_import ^= 1;
gtk_tree_path_free (path);
//#1993727 no update after toggle
//fake a treeview changed signal
g_signal_emit_by_name(gtk_tree_view_get_selection(treeview), "changed", NULL);
}
static gint list_txn_import_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
GenTxn *gentxn1, *gentxn2;
gtk_tree_model_get(model, a, LST_GENTXN_POINTER, &gentxn1, -1);
gtk_tree_model_get(model, b, LST_GENTXN_POINTER, &gentxn2, -1);
switch(sortcol)
{
case LST_DSPOPE_MEMO:
retval = hb_string_utf8_compare(gentxn1->memo, gentxn2->memo);
break;
case LST_DSPOPE_AMOUNT:
retval = (gentxn1->amount - gentxn2->amount) > 0 ? 1 : -1;
break;
case LST_DSPOPE_PAYEE:
retval = hb_string_utf8_compare(gentxn1->payee, gentxn2->payee);
break;
case LST_DSPOPE_CATEGORY:
retval = hb_string_utf8_compare(gentxn1->category, gentxn2->category);
break;
case LST_DSPOPE_DATE:
default:
retval = gentxn1->julian - gentxn2->julian;
break;
}
return retval;
}
static GtkTreeViewColumn *
list_txn_import_column_text_create(gchar *title, gint sortcolumnid, gpointer user_data)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
renderer = gtk_cell_renderer_text_new ();
/*g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);*/
if( sortcolumnid == LST_DSPOPE_AMOUNT )
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
//#2004631 date and column title alignement
if( sortcolumnid == LST_DSPOPE_AMOUNT )
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
if(sortcolumnid == LST_DSPOPE_AMOUNT )
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_amount, GINT_TO_POINTER(sortcolumnid), NULL);
else
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_text, GINT_TO_POINTER(sortcolumnid), NULL);
return column;
}
static GtkWidget *list_txn_import_create(void)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
/* create list store */
store = gtk_list_store_new(
NUM_LST_GENTXN,
G_TYPE_POINTER
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines|GTK_TREE_VIEW_GRID_LINES_VERTICAL);
// debug/import checkbox
column = gtk_tree_view_column_new();
#if MYDEBUG == 1
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_debug, NULL, NULL);
#endif
renderer = gtk_cell_renderer_toggle_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_toggle, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
g_signal_connect (renderer, "toggled", G_CALLBACK (list_txn_importfixed_toggled), treeview);
// icons
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Import ?"));
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, 16, -1);
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_warning, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// date
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_error, NULL, NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_text, GINT_TO_POINTER(LST_DSPOPE_DATE), NULL);
gtk_tree_view_column_set_title (column, _("Date"));
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_DATE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// memo
column = list_txn_import_column_text_create(_("Memo"), LST_DSPOPE_MEMO, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// amount
column = list_txn_import_column_text_create(_("Amount"), LST_DSPOPE_AMOUNT, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// info
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Pay./Number"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_info, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_text_new ();
/*g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);*/
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_function_info, GINT_TO_POINTER(2), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// payee
column = list_txn_import_column_text_create(_("Payee"), LST_DSPOPE_PAYEE, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// category
column = list_txn_import_column_text_create(_("Category"), LST_DSPOPE_CATEGORY, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// empty
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), list_txn_import_compare_func, NULL, NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_DATE , list_txn_import_compare_func, GINT_TO_POINTER(LST_DSPOPE_DATE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_MEMO , list_txn_import_compare_func, GINT_TO_POINTER(LST_DSPOPE_MEMO), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_AMOUNT , list_txn_import_compare_func, GINT_TO_POINTER(LST_DSPOPE_AMOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_PAYEE , list_txn_import_compare_func, GINT_TO_POINTER(LST_DSPOPE_PAYEE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_CATEGORY, list_txn_import_compare_func, GINT_TO_POINTER(LST_DSPOPE_CATEGORY), NULL);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
return(treeview);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gint ui_genacc_comboboxtext_get_active(GtkWidget *widget)
{
GtkTreeModel *model;
GtkTreeIter iter;
gint key = -1;
g_return_val_if_fail(GTK_IS_COMBO_BOX(widget), key);
if( gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
{
model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
gtk_tree_model_get(model, &iter,
LST_GENACC_KEY, &key,
-1);
}
return key;
}
static void ui_genacc_comboboxtext_set_active(GtkWidget *widget, gint active_key)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gint key;
g_return_if_fail(GTK_IS_COMBO_BOX(widget));
model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gtk_tree_model_get(model, &iter,
LST_GENACC_KEY, &key,
-1);
if(key == active_key)
gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static GtkWidget *ui_genacc_comboboxtext_new(struct import_data *data, GtkWidget *label)
{
GtkListStore *store;
GtkCellRenderer *renderer;
GtkWidget *combobox;
GtkTreeIter iter;
GList *lacc, *list;
store = gtk_list_store_new (NUM_LST_GENACC, G_TYPE_STRING, G_TYPE_INT);
combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(combobox), renderer, "text", LST_GENACC_NAME);
g_object_unref(store);
gtk_list_store_insert_with_values (GTK_LIST_STORE(store), &iter, -1,
LST_GENACC_NAME, _(""),
LST_GENACC_KEY, DST_ACC_GLOBAL,
-1);
gtk_list_store_insert_with_values (GTK_LIST_STORE(store), &iter, -1,
LST_GENACC_NAME, _(""),
LST_GENACC_KEY, DST_ACC_NEW,
-1);
//#2030333 5.7 sort by pos
lacc = list = account_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Account *item = list->data;
if( !(item->flags & AF_CLOSED) )
{
gtk_list_store_insert_with_values (GTK_LIST_STORE(store), &iter, -1,
LST_GENACC_NAME, item->name,
LST_GENACC_KEY, item->key,
-1);
}
list = g_list_next(list);
}
g_list_free(lacc);
gtk_list_store_insert_with_values (GTK_LIST_STORE(store), &iter, -1,
LST_GENACC_NAME, _(""),
LST_GENACC_KEY, DST_ACC_SKIP,
-1);
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), combobox);
return combobox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
enum
{
TARGET_URI_LIST
};
static GtkTargetEntry drop_types[] =
{
{"text/uri-list", 0, TARGET_URI_LIST}
};
static void
list_file_add(GtkWidget *treeview, GenFile *genfile)
{
char *basename;
GtkTreeModel *model;
GtkTreeIter iter;
basename = g_path_get_basename(genfile->filepath);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_GENFILE_POINTER, genfile,
LST_GENFILE_NAME, g_strdup(basename),
-1);
g_free(basename);
}
static void list_file_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x, gint y,
GtkSelectionData *selection_data,
guint info, guint time, GtkWindow *window)
{
struct import_data *data;
gchar **uris, **str;
gchar *newseldata;
gint slen;
if (info != TARGET_URI_LIST)
return;
DB( g_print("\n[ui-treeview] drag_data_received\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
/* On MS-Windows, it looks like `selection_data->data' is not NULL terminated. */
slen = gtk_selection_data_get_length(selection_data);
newseldata = g_new (gchar, slen + 1);
memcpy (newseldata, gtk_selection_data_get_data(selection_data), slen);
newseldata[slen] = 0;
uris = g_uri_list_extract_uris (newseldata);
ImportContext *ictx = &data->ictx;
str = uris;
for (str = uris; *str; str++)
//if( *str )
{
GError *error = NULL;
gchar *path = g_filename_from_uri (*str, NULL, &error);
if (path)
{
GenFile *genfile;
genfile = da_gen_file_append_from_filename(ictx, path);
if(genfile)
list_file_add(data->LV_file, genfile);
}
else
{
g_warning ("Could not convert uri to local path: %s", error->message);
g_error_free (error);
}
g_free (path);
}
g_strfreev (uris);
g_free(newseldata);
ui_import_page_filechooser_eval(widget, NULL);
}
static void
list_file_valid_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GenFile *genfile;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
LST_GENFILE_POINTER, &genfile,
-1);
iconname = (genfile->filetype == FILETYPE_UNKNOWN) ? ICONNAME_HB_FILE_INVALID : ICONNAME_HB_FILE_VALID;
g_object_set(renderer, "icon-name", iconname, NULL);
}
static GtkWidget *
list_file_new(void)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// create list store
store = gtk_list_store_new(NUM_LST_FILE,
G_TYPE_POINTER,
G_TYPE_STRING
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
//column: valid
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Valid"));
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, 16, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_file_valid_cell_data_function, NULL, NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
g_object_set(renderer, "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
// column: name
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (_("Name"),
renderer,
"text",
LST_GENFILE_NAME,
NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_GENFILE_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeviewattribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_acc_listview_compare_func, NULL, NULL);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
gtk_drag_dest_set (GTK_WIDGET (treeview),
GTK_DEST_DEFAULT_ALL,
drop_types,
G_N_ELEMENTS (drop_types),
GDK_ACTION_COPY);
g_signal_connect (G_OBJECT (treeview), "drag-data-received",
G_CALLBACK (list_file_drag_data_received), treeview);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_import_page_filechooser_delete_action(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImportContext *ictx;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
DB( g_print("\n[ui-import] page_filechooser_delete_action\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_file));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
GenFile *genfile;
gtk_tree_model_get(model, &iter, LST_GENFILE_POINTER, &genfile, -1);
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
ictx->gen_lst_file = g_list_remove(ictx->gen_lst_file, genfile);
da_gen_file_free(genfile);
}
ui_import_page_filechooser_eval(widget, NULL);
}
static void ui_import_page_filechooser_add_action(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImportContext *ictx;
GtkWidget *dialog;
GtkFileFilter *filter;
gint res;
DB( g_print("\n[ui-import] page_filechooser_add_action\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
dialog = gtk_file_chooser_dialog_new ("Open File",
GTK_WINDOW(data->assistant),
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
_("_Open"),
GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), PREFS->path_import);
DB( g_print(" set current folder '%s'\n", PREFS->path_import) );
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Known files"));
gtk_file_filter_add_pattern (filter, "*.[Qq][Ii][Ff]");
#ifndef NOOFX
gtk_file_filter_add_pattern (filter, "*.[OoQq][Ff][Xx]");
#endif
gtk_file_filter_add_pattern (filter, "*.[Cc][Ss][Vv]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
//if(data->filetype == FILETYPE_UNKNOWN)
// gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("QIF files"));
gtk_file_filter_add_pattern (filter, "*.[Qq][Ii][Ff]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
//if(data->filetype == FILETYPE_QIF)
// gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
#ifndef NOOFX
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("OFX/QFX files"));
gtk_file_filter_add_pattern (filter, "*.[OoQq][Ff][Xx]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
//if(data->filetype == FILETYPE_OFX)
// gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
#endif
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("CSV files"));
gtk_file_filter_add_pattern (filter, "*.[Cc][Ss][Vv]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
//if(data->filetype == FILETYPE_CSV_HB)
// gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All files"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT)
{
GSList *list;
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
list = gtk_file_chooser_get_filenames(chooser);
while(list)
{
GenFile *genfile;
DB( g_print(" selected '%p'\n", list->data) );
genfile = da_gen_file_append_from_filename(ictx, list->data);
if(genfile)
list_file_add(data->LV_file, genfile);
list = g_slist_next(list);
}
g_slist_free_full (list, g_free);
/* remind folder to preference */
gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(chooser));
DB( g_print(" store folder '%s'\n", folder) );
g_free(PREFS->path_import);
PREFS->path_import = folder;
}
gtk_window_destroy (GTK_WINDOW(dialog));
ui_import_page_filechooser_eval(widget, NULL);
}
static void
ui_import_page_filechooser_visible (GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
DB( g_print("\n[ui-import] page_filechooser_visible\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//open the file add if no file
if( gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_file)), NULL) == 0 )
{
//g_signal_emit_by_name(data->BT_file_add, "clicked", NULL);
ui_import_page_filechooser_add_action(data->BT_file_add, NULL);
}
}
static void ui_import_page_confirmation_fill(struct import_data *data)
{
ImportContext *ictx = &data->ictx;
GList *list;
GString *node;
DB( g_print("\n[ui-import] page_confirmation_fill\n") );
node = g_string_sized_new(255);
list = g_list_first(ictx->gen_lst_acc);
while (list != NULL)
{
GenAcc *genacc = list->data;
gchar *targetname = NULL;
switch( genacc->kacc )
{
case DST_ACC_GLOBAL:
targetname = _("new global account");
break;
case DST_ACC_NEW:
targetname = _("new account");
break;
case DST_ACC_SKIP:
targetname = _("skipped");
break;
default:
{
Account *acc = da_acc_get (genacc->kacc);
if(acc)
targetname = acc->name;
}
break;
}
//line1: title
g_string_append_printf(node, "'%s'\n => '%s'", genacc->name, targetname);
//line2: count
if( genacc->kacc != DST_ACC_SKIP)
{
hb_import_gen_acc_count_txn(ictx, genacc);
g_string_append_printf(node, _(", %d of %d transactions"), genacc->n_txnimp, genacc->n_txnall);
}
g_string_append(node, "\n\n");
list = g_list_next(list);
}
gtk_label_set_markup (GTK_LABEL(data->TX_summary), node->str);
g_string_free(node, TRUE);
}
static gboolean ui_import_page_import_eval(GtkWidget *widget, gpointer user_data)
{
//struct import_data *data;
//ImportContext *ictx;
//gint count;
DB( g_print("\n[ui-import] page_import_eval\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//ictx = &data->ictx;
//count = g_list_length (ictx->gen_lst_acc);
//DB( g_print(" count=%d (max=%d)\n", count, TXN_MAX_ACCOUNT) );
//if( count <= TXN_MAX_ACCOUNT )
return TRUE;
//return FALSE;
}
static void ui_import_page_filechooser_eval(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImportContext *ictx;
GList *list;
gint count = 0;
DB( g_print("\n[ui-import] page_filechooser_eval\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
list = g_list_first(ictx->gen_lst_file);
while (list != NULL)
{
GenFile *genfile = list->data;
if(genfile->filetype != FILETYPE_UNKNOWN)
count++;
list = g_list_next(list);
}
gint index = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
GtkWidget *current_page = gtk_assistant_get_nth_page (GTK_ASSISTANT(data->assistant), index);
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), current_page, (count > 0) ? TRUE : FALSE);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static ImpTxnData *ui_import_page_transaction_data_get(GArray *txndata, guint32 idx)
{
//gint i;
/*g_print(" array @%p, len is %d\n", txndata, txndata->len);
for(i=0;ilen;i++)
g_print(" %d %p\n", i, &g_array_index(txndata, ImpTxnData, i) );
g_print(" get idx=%d - %p\n", idx, &g_array_index (txndata, ImpTxnData, idx) );
*/
if( idx <= txndata->len )
return &g_array_index (txndata, ImpTxnData, idx);
return NULL;
}
//#1993727 no update after toggle
static void ui_import_page_transaction_update_count(struct import_data *data)
{
ImpTxnData *txndata;
ImportContext *ictx;
gchar *label = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gint count;
DB( g_print("\n[ui-import] txn update count\n") );
ictx = &data->ictx;
//get the account, it will be the account into the glist
gint acckey = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant)) - (PAGE_IMPORT);
GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
//count gentxn total and toimport
count = 0;
genacc->n_txnimp = 0;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(txndata->LV_gentxn));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
GenTxn *gentxn;
gtk_tree_model_get(model, &iter,
LST_GENTXN_POINTER, &gentxn,
-1);
count++;
if(gentxn->to_import)
genacc->n_txnimp++;
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
if(genacc->n_txnsimimp || genacc->n_txnsimdst)
label = g_strdup_printf(_("%d transaction(s), %d similar, %d existing, %d selected"), count, genacc->n_txnsimimp, genacc->n_txnsimdst, genacc->n_txnimp);
else
label = g_strdup_printf(_("%d transaction(s), %d selected"), count, genacc->n_txnimp);
gtk_label_set_markup (GTK_LABEL(txndata->LB_txn_title), label);
g_free(label);
}
static void ui_import_page_transaction_cb_fill_same(GtkTreeSelection *treeselection, gpointer user_data)
{
struct import_data *data;
ImpTxnData *txndata;
//ImportContext *ictx;
GtkTreeSelection *selection;
GtkTreeModel *model, *dupmodel;
GtkTreeIter iter, newiter;
GList *tmplist;
GtkWidget *widget;
guint count = 0;
DB( g_print("\n[ui-import] page_transaction_cb_fill_same\n") );
widget = GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection));
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//ictx = &data->ictx;
gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint acckey = pageidx - (PAGE_IMPORT);
//GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
//txndata = &data->txndata[acckey];
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
ui_import_page_transaction_update_count(data);
//update same
dupmodel = gtk_tree_view_get_model(GTK_TREE_VIEW(txndata->LV_duptxn));
gtk_tree_store_clear (GTK_TREE_STORE(dupmodel));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(txndata->LV_gentxn));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
GenTxn *gentxn;
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &gentxn, -1);
if( gentxn->lst_existing != NULL )
{
tmplist = g_list_first(gentxn->lst_existing);
while (tmplist != NULL)
{
Transaction *tmp = tmplist->data;
/* append to our treeview */
//gtk_list_store_append (GTK_LIST_STORE(dupmodel), &newiter);
//gtk_list_store_set (GTK_LIST_STORE(dupmodel), &newiter,
//#1830523/#1840393
count++;
gtk_tree_store_insert_with_values(GTK_TREE_STORE(dupmodel), &newiter, NULL, -1,
MODEL_TXN_POINTER, tmp,
MODEL_TXN_SPLITAMT, tmp->amount,
-1);
//DB( g_print(" - fill: %s %.2f %x\n", item->memo, item->amount, (unsigned int)item->same) );
tmplist = g_list_next(tmplist);
}
}
}
gtk_expander_set_expanded (GTK_EXPANDER(txndata->EX_duptxn), (count > 0) ? TRUE : FALSE);
}
static void ui_import_page_transaction_options_get(struct import_data *data)
{
ImpTxnData *txndata;
ImportContext *ictx;
DB( g_print("\n[ui-import] options_get\n") );
ictx = &data->ictx;
gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint accidx = pageidx - (PAGE_IMPORT);
//GenAcc *genacc = g_list_nth_data(ictx->gen_lst_acc, accidx);
//txndata = &data->txndata[accidx];
txndata = ui_import_page_transaction_data_get(data->txndata, accidx);
ictx->opt_dateorder = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(txndata->CY_txn_dateorder));
ictx->opt_daygap = gtk_spin_button_get_value(GTK_SPIN_BUTTON(txndata->NB_txn_daygap));
ictx->opt_ucfirst = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(txndata->CM_txn_ucfirst));
ictx->opt_togamount = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(txndata->CM_txn_togamount));
ictx->opt_ofxname = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(txndata->CY_txn_ofxname));
ictx->opt_ofxmemo = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(txndata->CY_txn_ofxmemo));
ictx->opt_qifmemo = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(txndata->CM_txn_qifmemo));
ictx->opt_qifswap = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(txndata->CM_txn_qifswap));
DB( g_print(" datefmt = '%s' (%d)\n", CYA_IMPORT_DATEORDER[ictx->opt_dateorder], ictx->opt_dateorder) );
}
static void ui_import_page_transaction_update(struct import_data *data)
{
ImpTxnData *txndata;
ImportContext *ictx;
gboolean sensitive, visible;
gboolean iscomplete;
GtkTreeModel *model;
DB( g_print("\n[ui-import] page_transaction_update\n") );
ictx = &data->ictx;
gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint acckey = pageidx - (PAGE_IMPORT);
//GenAcc *genacc = g_list_nth_data(ictx->gen_lst_acc, acckey);
GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
//txndata = &data->txndata[acckey];
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
DB( g_print(" page idx:%d, genacckey:%d genacc:%p, txndata:%p\n", pageidx, acckey, genacc, txndata) );
if(genacc)
{
DB( g_print(" genacc id=%d name='%s'\n dstacc=%d\n", acckey, genacc->name, genacc->kacc ) );
visible = (genacc->is_unamed == TRUE) && (genacc->filetype != FILETYPE_CSV_HB) ? TRUE: FALSE;
hb_widget_visible (txndata->IM_unamed, visible);
sensitive = (genacc->kacc == DST_ACC_SKIP) ? FALSE : TRUE;
DB( g_print(" sensitive=%d\n", sensitive) );
gtk_widget_set_sensitive(txndata->LV_gentxn, sensitive);
gtk_widget_set_sensitive(txndata->EX_duptxn, sensitive);
//todo: disable option button
gtk_widget_set_sensitive(txndata->GR_misc, sensitive);
gtk_widget_set_sensitive(txndata->GR_date, sensitive);
gtk_widget_set_sensitive(txndata->GR_ofx, sensitive);
gtk_widget_set_sensitive(txndata->GR_qif, sensitive);
gtk_widget_set_sensitive(txndata->GR_select, sensitive);
//todo: display a warning if incorrect date
gchar *msg_icon = NULL, *msg_label = NULL;
iscomplete = (genacc->n_txnbaddate > 0) ? FALSE : TRUE;
iscomplete = (genacc->kacc == DST_ACC_SKIP) ? TRUE : iscomplete;
DB( g_print(" nbbaddates=%d, dstacc=%d\n", genacc->n_txnbaddate, genacc->kacc) );
DB( g_print(" iscomplete=%d\n", iscomplete) );
//show/hide invalid date group
visible = FALSE;
if(genacc->n_txnbaddate > 0)
{
visible = TRUE;
DB( g_print(" invalid date detected\n" ) );
msg_icon = ICONNAME_ERROR;
msg_label =
_("Some date cannot be converted. Please try to change the date order to continue.");
}
gtk_image_set_from_icon_name(GTK_IMAGE(txndata->IM_txn), msg_icon, GTK_ICON_SIZE_BUTTON);
gtk_label_set_text(GTK_LABEL(txndata->LB_txn), msg_label);
hb_widget_visible (txndata->GR_msg, visible);
//show/hide select valid button
visible = (!genacc->n_txnsimimp && !genacc->n_txnsimdst) ? FALSE : TRUE;
hb_widget_visible (txndata->BT_val, visible);
//show/hide bottom duplicate panel
visible = TRUE;
if( genacc->kacc==DST_ACC_GLOBAL || genacc->kacc==DST_ACC_NEW || genacc->kacc==DST_ACC_SKIP)
visible = FALSE;
hb_widget_visible (txndata->EX_duptxn, visible);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(txndata->LV_gentxn));
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), LST_DSPOPE_DATE, GTK_SORT_ASCENDING);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT(data->assistant), pageidx);
gtk_assistant_set_page_complete(GTK_ASSISTANT(data->assistant), page, iscomplete);
}
}
static void ui_import_page_transaction_cb_account_changed(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImpTxnData *txndata;
ImportContext *ictx;
gint dstacc;
DB( g_print("\n[ui-import] cb_account_changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint acckey = pageidx - (PAGE_IMPORT);
//GenAcc *genacc = g_list_nth_data(ictx->gen_lst_acc, accidx);
GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
//txndata = &data->txndata[acckey];
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
//set target acc id
dstacc = ui_genacc_comboboxtext_get_active (txndata->CY_acc);
genacc->kacc = dstacc;
ui_import_page_transaction_options_get(data);
hb_import_option_apply(ictx, genacc);
hb_import_gen_txn_check_duplicate(ictx, genacc);
hb_import_gen_txn_check_target_similar(ictx, genacc);
genacc->is_dupcheck = TRUE;
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(txndata->LV_gentxn));
ui_import_page_transaction_update(data);
}
static void ui_import_page_transaction_cb_option_changed(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImpTxnData *txndata;
ImportContext *ictx;
DB( g_print("\n[ui-import] cb_option_changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint acckey = pageidx - (PAGE_IMPORT);
//GenAcc *genacc = g_list_nth_data(ictx->gen_lst_acc, accidx);
GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
//txndata = &data->txndata[acckey];
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
ui_import_page_transaction_options_get(data);
hb_import_option_apply(ictx, genacc);
//#1866456 check also target similar
if( txndata->CM_txn_togamount == widget )
{
DB( g_print(" should check target similar\n") );
hb_import_gen_txn_check_duplicate(ictx, genacc);
hb_import_gen_txn_check_target_similar(ictx, genacc);
}
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(txndata->LV_gentxn));
ui_import_page_transaction_update(data);
}
static void ui_import_page_transaction_fill(struct import_data *data)
{
ImpTxnData *txndata;
ImportContext *ictx = &data->ictx;
GtkWidget *view;
GtkTreeModel *model;
GtkTreeIter iter;
GList *tmplist;
gchar *label = NULL;
gboolean visible;
//gint nbacc;
DB( g_print("\n[ui-import] page_transaction_fill\n") );
//get the account, it will be the account into the glist
//of pagenum - PAGE_IMPORT
//gint pageidx = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant));
gint acckey = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant)) - (PAGE_IMPORT);
//GenAcc *genacc = g_list_nth_data(ictx->gen_lst_acc, acckey);
GenAcc *genacc = da_gen_acc_get_by_key(ictx->gen_lst_acc, acckey);
//nbacc = g_list_length(ictx->gen_lst_acc);
//txndata = &data->txndata[acckey];
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
DB( g_print(" genacckey:%d genacc:%p, txndata:%p\n", acckey, genacc, txndata) );
if(genacc)
{
gint count;
DB( g_print(" genacc id=%d name='%s'\n dstacc=%d\n", acckey, genacc->name, genacc->kacc ) );
g_signal_handlers_block_by_func(txndata->CY_acc, G_CALLBACK(ui_import_page_transaction_cb_account_changed), NULL);
ui_genacc_comboboxtext_set_active(txndata->CY_acc, genacc->kacc);
g_signal_handlers_unblock_by_func(txndata->CY_acc, G_CALLBACK(ui_import_page_transaction_cb_account_changed), NULL);
g_signal_handlers_block_by_func(txndata->NB_txn_daygap, G_CALLBACK(ui_import_page_transaction_cb_account_changed), NULL);
ictx->opt_daygap = PREFS->dtex_daygap;
DB( g_print(" init %d to dategap\n", ictx->opt_daygap) );
gtk_spin_button_set_value(GTK_SPIN_BUTTON(txndata->NB_txn_daygap), ictx->opt_daygap);
g_signal_handlers_unblock_by_func(txndata->NB_txn_daygap, G_CALLBACK(ui_import_page_transaction_cb_account_changed), NULL);
ui_import_page_transaction_options_get(data);
hb_import_option_apply(ictx, genacc);
if( genacc->is_dupcheck == FALSE )
{
hb_import_gen_txn_check_duplicate(ictx, genacc);
hb_import_gen_txn_check_target_similar(ictx, genacc);
genacc->is_dupcheck = TRUE;
}
view = txndata->LV_gentxn;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
gtk_list_store_clear (GTK_LIST_STORE(model));
count = 0;
genacc->n_txnimp = 0;
tmplist = g_list_first(ictx->gen_lst_txn);
while (tmplist != NULL)
{
GenTxn *item = tmplist->data;
//todo: change this, this should be account
if(item->kacc == genacc->key)
{
// append to our treeview
//gtk_list_store_append (GTK_LIST_STORE(model), &iter);
//gtk_list_store_set (GTK_LIST_STORE(model), &iter,
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, -1,
LST_GENTXN_POINTER, item,
-1);
DB( g_print(" fill: %s, %d, %s %.2f\n", item->account, item->julian, item->memo, item->amount) );
count++;
if(item->to_import)
genacc->n_txnimp++;
}
tmplist = g_list_next(tmplist);
}
//label = g_strdup_printf(_("'%s' - %s"), genacc->name, hb_import_filetype_char_get(genacc));
label = g_strdup_printf(_("Import %s in_to:"), genacc->is_unamed ? _("this file") : _("this account") );
gtk_label_set_markup_with_mnemonic (GTK_LABEL(txndata->LB_acc_title), label);
g_free(label);
//build tooltip
GenFile *genfile = da_gen_file_get (ictx->gen_lst_file, genacc->kfile);
label = g_strdup_printf(_("Name: %s\nNumber: %s\nFile: %s\nEncoding: %s"), genacc->name, genacc->number, genfile->filepath, genfile->encoding);
gtk_widget_set_tooltip_text (GTK_WIDGET(txndata->LB_acc_title), label);
gtk_widget_set_tooltip_text (GTK_WIDGET(txndata->LB_acc_info), label);
g_free(label);
//#2111468 add error log
visible = genfile->n_error > 0 ? TRUE : FALSE;
hb_widget_visible(GTK_WIDGET(txndata->EX_log), visible);
if( genfile->errlog )
{
GtkTextBuffer *buffer;
GtkTextIter iter;
label = g_strdup_printf(_("%d invalid lines were skipped"), genfile->n_error);
gtk_expander_set_label(GTK_EXPANDER(txndata->EX_log), label);
g_free(label);
gtk_expander_set_expanded(GTK_EXPANDER(txndata->EX_log), TRUE);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txndata->TV_log));
gtk_text_buffer_set_text (buffer, "", 0);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
gtk_text_buffer_insert (buffer, &iter, genfile->errlog, -1);
}
//label = g_strdup_printf(_("Account %d of %d"), acckey+1, nbacc);
//gtk_label_set_markup (GTK_LABEL(txndata->LB_acc_count), label);
//g_free(label);
//#1993727 no update after toggle
ui_import_page_transaction_update_count(data);
visible = (genacc->filetype == FILETYPE_OFX) ? FALSE : TRUE;
hb_widget_visible(GTK_WIDGET(txndata->GR_date), visible);
visible = (genacc->filetype == FILETYPE_OFX) ? TRUE : FALSE;
hb_widget_visible(GTK_WIDGET(txndata->GR_ofx), visible);
visible = (genacc->filetype == FILETYPE_QIF) ? TRUE : FALSE;
hb_widget_visible(GTK_WIDGET(txndata->GR_qif), visible);
gtk_stack_set_visible_child_name(GTK_STACK(txndata->ST_stack), visible ? "QIF" : "OFX");
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_import_page_intro_cb_dontshow(GtkWidget *widget, gpointer user_data)
{
PREFS->dtex_nointro = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
}
static GtkWidget *
ui_import_page_intro_create(GtkWidget *assistant, struct import_data *data)
{
GtkWidget *mainbox, *label, *widget;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
gtk_widget_set_halign(mainbox, GTK_ALIGN_CENTER);
gtk_widget_set_valign(mainbox, GTK_ALIGN_CENTER);
label = make_label(_("Import transactions from bank or credit card"), 0, 0);
gimp_label_set_attributes(GTK_LABEL(label),
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
-1);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
label = make_label(
_("With this assistant you will be guided through the process of importing one or several\n" \
"downloaded statements from your bank or credit card, in the following formats:"), 0, 0);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label),
_("Recommended: .OFX or .QFX\n" \
"(Sometimes named Money™ or Quicken™)\n" \
"Supported: .QIF\n" \
"(Common Quicken™ file)\n" \
"Advanced users only: .CSV\n"
"(format is specific to HomeBank, see the documentation)"));
/* supported format */
/*label = make_label(
_("HomeBank can import files in the following formats:\n" \
"- QIF\n" \
"- OFX/QFX (optional at compilation time)\n" \
"- CSV (format is specific to HomeBank, see the documentation)\n" \
), 0.0, 0.0);*/
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
label = make_label(
_("No changes will be made until you click \"Apply\" at the end of this assistant."), 0., 0.0);
gtk_box_prepend (GTK_BOX (mainbox), label);
//SPACING_SMALL
widget = gtk_check_button_new_with_mnemonic (_("Don't show this again"));
data->CM_dsta = widget;
gtk_box_append (GTK_BOX (mainbox), widget);
//SPACING_SMALL
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_dsta), PREFS->dtex_nointro);
gtk_widget_show_all (mainbox);
g_signal_connect (data->CM_dsta, "toggled", G_CALLBACK (ui_import_page_intro_cb_dontshow), data);
return mainbox;
}
static void ui_import_page_filechooser_update(GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
GtkTreeSelection *selection;
gboolean sensitive;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_file));
gint count = gtk_tree_selection_count_selected_rows(selection);
sensitive = (count > 0) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->BT_file_delete, sensitive);
//gtk_widget_set_sensitive(data->BT_merge, sensitive);
//gtk_widget_set_sensitive(data->BT_delete, sensitive);
}
static void ui_import_page_filechooser_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_import_page_filechooser_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static GtkWidget *
ui_import_page_filechooser_create (GtkWidget *assistant, struct import_data *data)
{
GtkWidget *mainbox, *vbox, *hbox, *bbox;
GtkWidget *widget, *label, *scrollwin, *treeview, *tbar;
mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (mainbox), hbox);
widget = hbtk_image_new_from_icon_name_24 (ICONNAME_HB_QUICKTIPS);
gtk_box_prepend (GTK_BOX (hbox), widget);
//SPACING_SMALL
label = make_label(
_("Drag&Drop one or several files to import.\n" \
"You can also use the add/delete buttons of the list.")
, 0., 0.0);
gtk_box_prepend (GTK_BOX (hbox), label);
//SPACING_SMALL
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
//list
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_hexpand(scrollwin, TRUE);
gtk_widget_set_vexpand(scrollwin, TRUE);
treeview = list_file_new();
data->LV_file = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_file_add = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_file_delete = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
gtk_widget_show_all (mainbox);
ui_import_page_filechooser_update(assistant, NULL);
g_signal_connect (G_OBJECT (data->BT_file_add), "clicked", G_CALLBACK (ui_import_page_filechooser_add_action), data);
g_signal_connect (G_OBJECT (data->BT_file_delete), "clicked", G_CALLBACK (ui_import_page_filechooser_delete_action), data);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_file)), "changed", G_CALLBACK (ui_import_page_filechooser_selection), NULL);
return mainbox;
}
static GtkWidget *
ui_import_page_import_create (GtkWidget *assistant, struct import_data *data)
{
GtkWidget *mainbox;
GtkWidget *label, *widget;
gchar *txt;
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
//gtk_widget_set_halign(mainbox, GTK_ALIGN_CENTER);
//gtk_widget_set_valign(mainbox, GTK_ALIGN_CENTER);
widget = hbtk_image_new_from_icon_name_32(ICONNAME_ERROR);
gtk_box_prepend (GTK_BOX (mainbox), widget);
txt = _("There is too much account in the files you choose,\n" \
"please use the back button to select less files.");
label = gtk_label_new(txt);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
gtk_box_prepend (GTK_BOX (mainbox), label);
gtk_widget_show_all (mainbox);
return mainbox;
}
static gboolean
ui_import_page_transaction_cb_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
struct import_data *data;
ImpTxnData *txndata;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
guint action;
//g_return_val_if_fail(GTK_IS_TREE_VIEW(user_data), TRUE);
data = user_data;
gint acckey = gtk_assistant_get_current_page(GTK_ASSISTANT(data->assistant)) - (PAGE_IMPORT);
txndata = ui_import_page_transaction_data_get(data->txndata, acckey);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(txndata->LV_gentxn));
DB( g_print(" comboboxlink '%s' \n", uri) );
//define the action
action = LST_SELECT_UNSET;
if (g_strcmp0 (uri, "all") == 0)
action = LST_SELECT_ALL;
else if (g_strcmp0 (uri, "non") == 0)
action = LST_SELECT_NONE;
else if (g_strcmp0 (uri, "inv") == 0)
action = LST_SELECT_INVERT;
else if (g_strcmp0 (uri, "val") == 0)
action = LST_SELECT_VALID;
//apply the action
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
GenTxn *gentxn;
gtk_tree_model_get(model, &iter,
LST_GENTXN_POINTER, &gentxn,
-1);
switch(action)
{
case LST_SELECT_ALL : gentxn->to_import = TRUE; break;
case LST_SELECT_NONE: gentxn->to_import = FALSE; break;
case LST_SELECT_INVERT: gentxn->to_import ^= TRUE; break;
case LST_SELECT_VALID:
gentxn->to_import = (!gentxn->is_imp_similar && !gentxn->is_dst_similar) ? TRUE : FALSE;
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
gtk_widget_queue_draw(GTK_WIDGET(txndata->LV_gentxn));
ui_import_page_transaction_update_count(data);
return TRUE;
}
static GtkWidget *
ui_import_page_transaction_create (GtkWidget *assistant, gint idx, struct import_data *data)
{
ImpTxnData *txndata;
GtkWidget *table, *box, *group, *stack;
GtkWidget *label, *scrollwin, *treeview, *expander, *widget;
ImpTxnData tmp;
gint row;
//txndata = &data->txndata[idx];
memset(&tmp, 0, sizeof(ImpTxnData));
g_array_insert_vals(data->txndata, idx, &tmp, 1);
txndata = ui_import_page_transaction_data_get(data->txndata, idx);
DB( g_print(" txndat=%p\n", txndata) );
if(!txndata)
return NULL;
table = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
//line 1 left
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
//gtk_widget_set_hexpand(box, TRUE);
gtk_grid_attach (GTK_GRID(table), box, 0, row, 1, 1);
//5.6 info icon
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
txndata->LB_acc_info = widget;
gtk_box_prepend (GTK_BOX (box), widget);
// XXX (type) + accname
label = make_label(NULL, 0.0, 0.5);
txndata->LB_acc_title = label;
//gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_LARGE, -1);
gtk_box_prepend (GTK_BOX (box), label);
widget = ui_genacc_comboboxtext_new(data, label);
//gtk_widget_set_hexpand(widget, TRUE);
txndata->CY_acc = widget;
gtk_box_prepend (GTK_BOX (box), widget);
widget = hbtk_image_new_from_icon_name_16(ICONNAME_WARNING);
txndata->IM_unamed = widget;
gtk_widget_set_tooltip_text (widget, _("Target account identification by name or number failed."));
gtk_box_prepend (GTK_BOX (box), widget);
//line 1 right
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
//gtk_widget_set_hexpand(box, TRUE);
gtk_grid_attach (GTK_GRID(table), box, 1, row, 1, 1);
//csv options
group = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_date = group;
gtk_box_prepend (GTK_BOX(box), group);
label = make_label(_("Date order:"), 0, 0.5);
gtk_box_prepend (GTK_BOX(group), label);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_DATEORDER);
txndata->CY_txn_dateorder = widget;
gtk_box_prepend (GTK_BOX(group), widget);
stack = gtk_stack_new();
gtk_box_prepend (GTK_BOX(box), stack);
txndata->ST_stack= stack;
//qif options
group = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_qif = group;
gtk_stack_add_named(GTK_STACK(stack), group, "QIF");
widget = gtk_check_button_new_with_mnemonic (_("_Import memos"));
txndata->CM_txn_qifmemo = widget;
gtk_box_prepend (GTK_BOX(group), widget);
widget = gtk_check_button_new_with_mnemonic (_("_Swap memos with payees"));
txndata->CM_txn_qifswap = widget;
gtk_box_prepend (GTK_BOX(group), widget);
//ofx options
group = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_ofx = group;
gtk_stack_add_named(GTK_STACK(stack), group, "OFX");
label = make_label(_("OFX _Name:"), 0, 0.5);
gtk_box_prepend (GTK_BOX(group), label);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_OFXNAME);
txndata->CY_txn_ofxname = widget;
gtk_box_prepend (GTK_BOX(group), widget);
label = make_label(_("OFX _Memo:"), 0, 0.5);
gtk_box_prepend (GTK_BOX(group), label);
widget = hbtk_combo_box_new_with_data(label, CYA_IMPORT_OFXMEMO);
txndata->CY_txn_ofxmemo = widget;
gtk_box_prepend (GTK_BOX(group), widget);
// n transaction ...
row++;
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
//gtk_widget_set_hexpand(box, TRUE);
gtk_grid_attach (GTK_GRID(table), box, 0, row, 1, 1);
group = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_select = group;
gtk_box_prepend (GTK_BOX (box), group);
label = make_label (_("Select:"), 0, 0.5);
gtk_box_prepend (GTK_BOX (group), label);
label = make_clicklabel("all", _("All"));
txndata->BT_all= label;
gtk_box_prepend (GTK_BOX (group), label);
label = make_clicklabel("non", _("None"));
txndata->BT_non = label;
gtk_box_prepend (GTK_BOX (group), label);
label = make_clicklabel("inv", _("Invert"));
txndata->BT_inv = label;
gtk_box_prepend (GTK_BOX (group), label);
label = make_clicklabel("val", _("Valid"));
txndata->BT_val = label;
gtk_box_prepend (GTK_BOX (group), label);
label = make_label(NULL, 0.5, 0.5);
txndata->LB_txn_title = label;
hbtk_box_prepend (GTK_BOX (box), label);
// import into
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_grid_attach (GTK_GRID(table), box, 1, row, 1, 1);
group = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_misc = group;
gtk_box_prepend (GTK_BOX (box), group);
widget = gtk_check_button_new_with_mnemonic (_("Sentence _case memo/payee"));
txndata->CM_txn_ucfirst = widget;
gtk_box_prepend (GTK_BOX(group), widget);
widget = gtk_check_button_new_with_mnemonic (_("_Toggle amount"));
txndata->CM_txn_togamount = widget;
gtk_box_prepend (GTK_BOX(group), widget);
// error messages
row++;
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
txndata->GR_msg = box;
//gtk_widget_set_hexpand(box, TRUE);
gtk_grid_attach (GTK_GRID(table), box, 0, row, 2, 1);
widget = gtk_image_new ();
txndata->IM_txn = widget;
gtk_widget_set_valign(widget, GTK_ALIGN_START);
gtk_box_prepend (GTK_BOX (box), widget);
label = make_label(NULL, 0.0, 0.5);
txndata->LB_txn = label;
gtk_box_prepend (GTK_BOX (box), label);
//#2111468 add error log
row++;
expander = gtk_expander_new_with_mnemonic(_("Error log"));
txndata->EX_log = expander;
gtk_widget_set_hexpand(expander, TRUE);
gtk_grid_attach (GTK_GRID (table), expander, 0, row, 2, 1);
label = gtk_text_view_new();
txndata->TV_log = label;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (scrollwin, -1, 128);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), label);
//gtk_widget_set_hexpand (scrollwin, TRUE);
//gtk_widget_set_vexpand (scrollwin, TRUE);
gtk_expander_set_child(GTK_EXPANDER(expander), scrollwin);
//hb_widget_set_margin(GTK_WIDGET(scrollwin), SPACING_MEDIUM);
row++;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = list_txn_import_create();
txndata->LV_gentxn = treeview;
gtk_widget_set_hexpand(scrollwin, TRUE);
gtk_widget_set_vexpand(scrollwin, TRUE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_grid_attach (GTK_GRID(table), scrollwin, 0, row, 2, 1);
//duplicate
row++;
expander = gtk_expander_new (_("Similar transaction in target account (possible duplicate)"));
txndata->EX_duptxn = expander;
//gtk_widget_set_hexpand(expander, TRUE);
gtk_grid_attach (GTK_GRID(table), expander, 0, row, 2, 1);
group = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (group), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (group), SPACING_SMALL);
gtk_expander_set_child (GTK_EXPANDER(expander), group);
row = 0;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_hexpand(scrollwin, TRUE);
treeview = create_list_transaction(LIST_TXN_TYPE_OTHER, PREFS->lst_impope_columns);
txndata->LV_duptxn = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_set_size_request(scrollwin, -1, HB_MINWIDTH_LIST/2);
gtk_grid_attach (GTK_GRID (group), scrollwin, 0, row, 5, 1);
row++;
label = make_label(_("Date _gap:"), 0, 0.5);
gtk_grid_attach (GTK_GRID (group), label, 0, row, 1, 1);
widget = make_numeric(label, 0.0, HB_DATE_MAX_GAP);
txndata->NB_txn_daygap = widget;
gtk_grid_attach (GTK_GRID (group), widget, 1, row, 1, 1);
//TRANSLATORS: there is a spinner on the left of this label, and so you have 0....x days of date tolerance
label = make_label(_("days"), 0, 0.5);
gtk_grid_attach (GTK_GRID (group), label, 2, row, 1, 1);
widget = hbtk_image_new_from_icon_name_16(ICONNAME_HB_QUICKTIPS );
gtk_widget_set_hexpand(widget, FALSE);
gtk_grid_attach (GTK_GRID (group), widget, 3, row, 1, 1);
label = make_label (_(
"The match is done in order: by account, amount and date.\n" \
"A date tolerance of 0 day means an exact match"), 0, 0.5);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
-1);
gtk_widget_set_hexpand(label, TRUE);
gtk_grid_attach (GTK_GRID (group), label, 4, row, 1, 1);
// init ofx/qfx option to move
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(txndata->CY_txn_dateorder), PREFS->dtex_datefmt);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(txndata->CM_txn_ucfirst), PREFS->dtex_ucfirst);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(txndata->CY_txn_ofxname), PREFS->dtex_ofxname);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(txndata->CY_txn_ofxmemo), PREFS->dtex_ofxmemo);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(txndata->CM_txn_qifmemo), PREFS->dtex_qifmemo);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(txndata->CM_txn_qifswap), PREFS->dtex_qifswap);
gtk_widget_show_all (table);
gtk_widget_hide(txndata->GR_qif);
gtk_widget_hide(txndata->GR_ofx);
//#1993727 no update after toggle
g_signal_connect (txndata->BT_all, "activate-link", G_CALLBACK (ui_import_page_transaction_cb_activate_link), data);
g_signal_connect (txndata->BT_non, "activate-link", G_CALLBACK (ui_import_page_transaction_cb_activate_link), data);
g_signal_connect (txndata->BT_inv, "activate-link", G_CALLBACK (ui_import_page_transaction_cb_activate_link), data);
g_signal_connect (txndata->BT_val, "activate-link", G_CALLBACK (ui_import_page_transaction_cb_activate_link), data);
g_signal_connect (txndata->CY_acc , "changed", G_CALLBACK (ui_import_page_transaction_cb_account_changed), data);
g_signal_connect (txndata->CY_txn_dateorder, "changed", G_CALLBACK (ui_import_page_transaction_cb_account_changed), data);
g_signal_connect (txndata->NB_txn_daygap , "value-changed", G_CALLBACK (ui_import_page_transaction_cb_account_changed), data);
g_signal_connect (txndata->CY_txn_ofxname , "changed", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (txndata->CY_txn_ofxmemo , "changed", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (txndata->CM_txn_qifmemo, "toggled", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (txndata->CM_txn_qifswap, "toggled", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (txndata->CM_txn_ucfirst, "toggled", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (txndata->CM_txn_togamount, "toggled", G_CALLBACK (ui_import_page_transaction_cb_option_changed), data);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(txndata->LV_gentxn)), "changed",
G_CALLBACK (ui_import_page_transaction_cb_fill_same), NULL);
return table;
}
static GtkWidget *
ui_import_page_confirmation_create(GtkWidget *assistant, struct import_data *data)
{
GtkWidget *group_grid, *label, *widget, *scrollwin;
gint row = 0;
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);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_NONE);
gtk_widget_set_hexpand(scrollwin, TRUE);
gtk_widget_set_vexpand(scrollwin, TRUE);
widget = gtk_label_new (NULL);
data->TX_summary = widget;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
gtk_grid_attach (GTK_GRID (group_grid), scrollwin, 0, row, 4, 1);
row++;
label = make_label_group(_("Option"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic(_("Import as pending"));
gtk_widget_set_margin_start(widget, SPACING_MEDIUM);
data->CM_set_pending = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
row++;
label = make_label_group(_("Run automation"));
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic(_("1) Enrich with _payee default"));
gtk_widget_set_margin_start(widget, SPACING_MEDIUM);
data->CM_do_auto_payee = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic(_("2) Run automatic _assigment rules"));
gtk_widget_set_margin_start(widget, SPACING_MEDIUM);
data->CM_do_auto_assign = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
row++;
label = make_label(_("Click \"Apply\" to update your accounts."), 0, 0.5);
gtk_widget_set_margin_top(label, SPACING_LARGE);
gtk_widget_set_margin_bottom(label, SPACING_LARGE);
gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
gtk_grid_attach (GTK_GRID (group_grid), label, 3, row, 1, 1);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_do_auto_payee), PREFS->dtex_dodefpayee);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_do_auto_assign), PREFS->dtex_doautoassign);
gtk_widget_show_all (group_grid);
return group_grid;
}
static void
ui_import_assistant_prepare (GtkWidget *widget, GtkWidget *page, gpointer user_data)
{
struct import_data *data;
ImportContext *ictx;
gint current_page, n_pages;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
current_page = gtk_assistant_get_current_page (GTK_ASSISTANT(data->assistant));
n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT(data->assistant));
DB( g_print("\n--------\n[ui-import] prepare \n page %d of %d\n", current_page, n_pages) );
switch( current_page )
{
case PAGE_WELCOME:
DB( g_print("\n #1 intro\n") );
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), page, TRUE);
break;
case PAGE_FILES:
DB( g_print("\n #2 file choose\n") );
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), page, FALSE);
// the page complete is contextual in ui_import_page_filechooser_selection_changed
// check is something valid :: count total rows
ui_import_page_filechooser_eval(widget, user_data);
break;
case PAGE_IMPORT:
DB( g_print("\n #3 real import\n") );
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), page, FALSE);
//todo: more test needed here
//clean any previous txn page
/*for(i=(n_pages-1);i>=PAGE_IMPORT+1;i--)
{
GtkWidget *page = gtk_assistant_get_nth_page (GTK_ASSISTANT(data->assistant), i);
gboolean isacc;
if( page != NULL )
{
isacc = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(page), "pgacc"));
DB( g_print(" %d is acc: %d\n", i, isacc) );
if( isacc )
{
gtk_assistant_remove_page(GTK_ASSISTANT(data->assistant), i);
gtk_widget_destroy (page);
}
}
}*/
hb_import_load_all(&data->ictx);
//add 1 page per account
gint key, nbacc;
nbacc = g_list_length (ictx->gen_lst_acc);
if(data->txndata)
{
g_array_free(data->txndata, TRUE);
data->txndata = NULL;
}
data->txndata = g_array_sized_new(FALSE, TRUE, sizeof(ImpTxnData), nbacc);
//#1820618 patch for glib < 2.58 https://gitlab.gnome.org/GNOME/glib/issues/1374
if( glib_minor_version < 58 )
{
g_array_set_size(data->txndata, nbacc);
}
DB( g_print(" accnb=%d @%p\n", nbacc, data->txndata) );
//debug
//_import_context_debug_acc_list(&data->ictx);
//if(nbacc < TXN_MAX_ACCOUNT)
//{
for(key=1;keygen_lst_acc, key);
DB( g_print(" create page txn for '%s' '%s' at page %d\n", genacc->name, genacc->number, PAGE_IMPORT + key) );
page = ui_import_page_transaction_create (data->assistant, key, data);
//g_object_set_data(G_OBJECT(page), "pgacc", (gpointer)TRUE);
gtk_widget_show_all (page);
gtk_assistant_insert_page (GTK_ASSISTANT (data->assistant), page, PAGE_IMPORT + key);
//gtk_assistant_set_page_title (GTK_ASSISTANT (data->assistant), page, _("Transaction"));
//gtk_assistant_set_page_title (GTK_ASSISTANT (data->assistant), page, genacc->name);
title = g_strdup_printf("%s %d", (!genacc->is_unamed) ? _("Account") : _("Unknown"), key );
gtk_assistant_set_page_title (GTK_ASSISTANT (data->assistant), page, title);
g_free(title);
}
//}
// obsolete ??
if( ui_import_page_import_eval (widget, NULL) )
{
/*if(ictx->nb_new_acc == 0)
{
DB( g_print(" -> jump to Transaction page\n") );
//gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), data->pages[PAGE_ACCOUNT], TRUE);
gtk_assistant_next_page(GTK_ASSISTANT(data->assistant));
gtk_assistant_next_page(GTK_ASSISTANT(data->assistant));
//gtk_assistant_set_current_page (GTK_ASSISTANT(data->assistant), PAGE_TRANSACTION);
}
else
{
DB( g_print(" -> jump to Account page\n") );
//gtk_assistant_set_current_page (GTK_ASSISTANT(data->assistant), PAGE_ACCOUNT);
gtk_assistant_next_page(GTK_ASSISTANT(data->assistant));
}*/
gtk_assistant_next_page(GTK_ASSISTANT(data->assistant));
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), page, TRUE);
}
break;
default:
if(current_page != (n_pages - 1))
{
DB( g_print("\n #4 transaction\n") );
if( current_page == PAGE_IMPORT + 1)
//hack to get rid of back button
gtk_assistant_set_page_type (GTK_ASSISTANT(data->assistant), page, GTK_ASSISTANT_PAGE_INTRO);
ui_import_page_transaction_fill(data);
ui_import_page_transaction_update(data);
}
else
{
DB( g_print("\n #5 confirmation\n") );
ui_import_page_confirmation_fill(data);
gtk_assistant_set_page_complete (GTK_ASSISTANT(data->assistant), page, TRUE);
}
}
}
static void
ui_import_assistant_apply (GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
ImportContext *ictx;
DB( g_print("\n[ui-import] apply\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ictx = &data->ictx;
ictx->set_pending = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_set_pending));
ictx->do_auto_payee = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_do_auto_payee));
ictx->do_auto_assign = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_do_auto_assign));
//#1845388 persist options for lazy people
PREFS->dtex_dodefpayee = ictx->do_auto_payee;
PREFS->dtex_doautoassign = ictx->do_auto_assign;
PREFS->dtex_daygap = ictx->opt_daygap;
DB( g_print(" store %d to daygap\n", PREFS->dtex_daygap) );
hb_import_apply(&data->ictx);
}
static gboolean
ui_import_assistant_dispose(GtkWidget *widget, gpointer user_data)
{
struct import_data *data = user_data;
DB( g_print("\n[ui-import] dispose\n") );
#if MYDEBUG == 1
gpointer data2 = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
g_print(" user_data=%p to be free, data2=%p\n", user_data, data2);
#endif
da_import_context_destroy(&data->ictx);
if(data->txndata)
{
g_array_free(data->txndata, TRUE);
}
// todo: optimize this
//if(data->imp_cnt_trn > 0)
//{
//GLOBALS->changes_count += data->imp_cnt_trn;
//our global list has changed, so update the treeview
account_compute_balances (FALSE);
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
//}
g_free(user_data);
//delete-event TRUE abort/FALSE destroy
return FALSE;
}
static void
ui_import_assistant_close_cancel (GtkWidget *widget, gpointer user_data)
{
struct import_data *data;
GtkWidget *assistant = (GtkWidget *) user_data;
DB( g_print("\n[ui-import] close\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ui_import_assistant_dispose(widget, data);
gtk_window_destroy (GTK_WINDOW(assistant));
}
/* starting point of import */
GtkWidget *ui_import_assistant_new (gchar **paths)
{
struct import_data *data;
GtkWidget *assistant, *page, *page_file;
gint w, h;
DB( g_print("\n[ui-import] new\n") );
data = g_malloc0(sizeof(struct import_data));
if(!data) return NULL;
assistant = gtk_assistant_new ();
data->assistant = assistant;
//store our window private data
g_object_set_data(G_OBJECT(assistant), "inst_data", (gpointer)data);
//DB( g_print("** \n[ui-import] window=%x, inst_data=%x\n", assistant, data) );
gtk_window_set_modal(GTK_WINDOW (assistant), TRUE);
gtk_window_set_transient_for(GTK_WINDOW(assistant), GTK_WINDOW(GLOBALS->mainwindow));
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
gtk_window_set_default_size (GTK_WINDOW(assistant), w * 0.8, h * 0.8);
//gtk_window_set_default_size (GTK_WINDOW(assistant), w - 24, h - 24);
page = ui_import_page_intro_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_INTRO);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Welcome"));
gtk_assistant_set_page_complete (GTK_ASSISTANT(assistant), page, TRUE );
page = ui_import_page_filechooser_create (assistant, data);
page_file = page;
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Select file(s)"));
page = ui_import_page_import_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
//gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_PROGRESS);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Import"));
//3...x transaction page will be added automatically
//page = ui_import_page_transaction_create (assistant, 0, data);
//gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
//hack to hide the back button here
//gtk_assistant_set_page_type (GTK_ASSISTANT(assistant), page, GTK_ASSISTANT_PAGE_INTRO);
//gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Transaction"));
page = ui_import_page_confirmation_create (assistant, data);
gtk_assistant_append_page (GTK_ASSISTANT (assistant), page);
gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, GTK_ASSISTANT_PAGE_CONFIRM);
gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, _("Confirmation"));
//gtk_assistant_set_forward_page_func(GTK_ASSISTANT(assistant), ui_import_assistant_forward_page_func, data, NULL);
//setup
//ui_import_page_filechooser_selection_changed(assistant, data);
DB( g_printf(" check list of paths '%p'\n", paths) );
if( paths != NULL )
{
ImportContext *ictx = &data->ictx;
GenFile *genfile;
gchar **str = paths;
while(*str != NULL)
{
DB( g_printf(" try to append '%s'\n", *str) );
genfile = da_gen_file_append_from_filename(ictx, *str);
if(genfile)
{
list_file_add(data->LV_file, genfile);
}
str++;
}
g_strfreev(paths);
}
//connect all our signals
//g_signal_connect (window, "delete-event", G_CALLBACK (hbfile_dispose), (gpointer)data);
g_signal_connect (G_OBJECT (assistant), "prepare", G_CALLBACK (ui_import_assistant_prepare), NULL);
g_signal_connect (G_OBJECT (page_file), "map", G_CALLBACK (ui_import_page_filechooser_visible), NULL);
g_signal_connect (G_OBJECT (assistant), "cancel", G_CALLBACK (ui_import_assistant_close_cancel), assistant);
g_signal_connect (G_OBJECT (assistant), "close", G_CALLBACK (ui_import_assistant_close_cancel), assistant);
g_signal_connect (G_OBJECT (assistant), "apply", G_CALLBACK (ui_import_assistant_apply), NULL);
#ifdef G_OS_WIN32
hbtk_assistant_hack_button_order(GTK_ASSISTANT(assistant));
#endif
gtk_widget_show (assistant);
if(PREFS->dtex_nointro)
gtk_assistant_set_current_page(GTK_ASSISTANT(assistant), PAGE_FILES);
return assistant;
}
homebank-5.9.7/src/gtk-chart-progress.h 0000644 0001750 0001750 00000012327 15115327776 017323 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __CHARTPROGRESS_H__
#define __CHARTPROGRESS_H__
#include "gtk-chart-colors.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Macro for casting a pointer to a GtkWidget or GtkWidgetClass pointer.
* Macros for testing whether `widget' or `klass' are of type GTK_TYPE_WIDGET.
*/
#define GTK_TYPE_CHARTPROGRESS (ui_chart_progress_get_type ())
#define GTK_CHARTPROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CHARTPROGRESS, ChartProgress))
#define GTK_CHARTPROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CHARTPROGRESS, ChartProgressClass)
#define GTK_IS_CHARTPROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CHARTPROGRESS))
#define GTK_IS_CHARTPROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CHARTPROGRESS))
#define GTK_CHARTPROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CHARTPROGRESS, ChartProgressClass))
typedef struct _ChartProgress ChartProgress;
typedef struct _ChartProgressClass ChartProgressClass;
typedef struct _StackItem StackItem;
typedef struct _HbtkDrawProgContext HbtkDrawProgContext;
typedef gchar (* ChartProgressPrintIntFunc) (gint value, gboolean minor);
typedef gchar (* ChartProgressPrintDoubleFunc) (gdouble value, gboolean minor);
/* = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#define GTK_CHARTPROGRESS_BARW 32
#define CHART_BUFFER_LENGTH 128
#define DEFAULT_DELAY 500 /* Default delay in ms */
#define CHART_MARGIN 12 //standard a4 margin
#define CHART_SPACING 6
struct _StackItem
{
/* data part */
gchar *label;
gdouble spent;
gdouble budget;
gdouble result;
gchar *status;
gint n_child;
/* draw stuffs */
gdouble rate;
gboolean warn;
/* tmp datas */
gdouble rawrate;
};
struct _HbtkDrawProgContext
{
gboolean isprint;
gboolean darktheme;
gint first, visible;
double barw, blkw;
/* drawing datas */
double l, t, b, r, w, h;
double graph_width, graph_height; //graph dimension
/* zones height */
double title_zh;
double subtitle_zh, subtitle_y;
double header_zh, header_y;
double item_zh;
/* column width */
double cat_col_w;
double bud_col_w;
double res_col_w;
double rel_col_w;
};
/* you should access only the entry and list fields directly */
struct _ChartProgress
{
/*< private >*/
GtkBox hbox;
GtkWidget *drawarea;
GtkAdjustment *adjustment;
GtkWidget *scrollbar;
GtkWidget *breadcrumb;
/* data storage */
GtkTreeModel *model;
gint nb_items;
GArray *items;
/*gchar **titles;
gdouble *spent;
gdouble *budget;*/
gchar *title;
gchar *subtitle;
gchar *budget_title;
gchar *result_title;
gboolean minor;
guint32 kcur;
gdouble minor_rate;
gchar *minor_symbol;
/* color datas */
GtkColorScheme color_scheme;
/* buffer surface */
cairo_surface_t *surface;
gint hover, lasthover;
struct _HbtkDrawProgContext context;
PangoFontDescription *pfd;
gint pfd_size;
gchar buffer[CHART_BUFFER_LENGTH];
};
typedef struct
{
ChartProgress *chart;
HbtkDrawProgContext drawctx;
gint num_pages;
} GtkChartProgPrintData;
struct _ChartProgressClass {
GtkBoxClass parent_class;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
};
GType ui_chart_progress_get_type (void);
/* public function */
GtkWidget *ui_chart_progress_new(void);
void gtk_chart_progress_print(ChartProgress *chart, GtkWindow *parent, gchar *dirname, gchar *filename);
void ui_chart_progress_set_color_scheme(ChartProgress * chart, gint colorscheme);
void ui_chart_progress_set_dualdatas(ChartProgress *chart, GtkTreeModel *model, gchar *coltitle1, gchar *coltitle2, gchar *title, gchar *subtitle);
void ui_chart_progress_set_title(ChartProgress * chart, gchar *title);
void ui_chart_progress_set_subtitle(ChartProgress * chart, gchar *subtitle);
void ui_chart_progress_set_barw(ChartProgress * chart, gdouble barw);
void ui_chart_progress_show_minor(ChartProgress * chart, gboolean minor);
void ui_chart_progress_set_minor_prefs(ChartProgress * chart, gdouble rate, gchar *symbol);
void ui_chart_progress_set_currency(ChartProgress * chart, guint32 kcur);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __CHARTPROGRESS_H__ */
homebank-5.9.7/src/rep-time.c 0000644 0001750 0001750 00000166333 15005633746 015314 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "rep-time.h"
#include "list-operation.h"
#include "gtk-chart.h"
#include "gtk-dateentry.h"
#include "dsp-mainwindow.h"
#include "ui-account.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-filter.h"
#include "ui-transaction.h"
#include "ui-tag.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* prototypes */
static GtkWidget *lst_reptime_create(void);
static GString *lst_reptime_to_string(ToStringMode mode, GtkTreeView *treeview, gchar *title);
static void lst_reptime_set_cur(GtkTreeView *treeview, guint32 kcur);
extern HbKvData CYA_REPORT_GRPBY_TREND[];
extern HbKvData CYA_REPORT_INTVL[];
/* = = = = = = = = = = = = = = = = */
static gchar *
reptime_compute_title(gint src, gint intvl)
{
gchar *title;
//TRANSLATORS: example 'Category by Month'
title = g_strdup_printf(_("%s by %s"),
hbtk_get_label(CYA_REPORT_GRPBY_TREND, src),
hbtk_get_label(CYA_REPORT_INTVL, intvl)
);
return title;
}
static void reptime_sensitive(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gboolean visible, sensitive;
gint page;
DB( g_print("\n[rep-time] sensitive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
visible = page == 0 ? TRUE : FALSE;
hb_widget_visible (data->BT_detail, visible);
hb_widget_visible (data->BT_export, visible);
visible = page == 0 ? FALSE : TRUE;
//5.7
//hb_widget_visible(data->BT_print, visible);
hb_widget_visible(data->LB_zoomx, visible);
hb_widget_visible(data->RG_zoomx, visible);
page = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail)), NULL);
sensitive = ((page > 0) && data->detail) ? TRUE : FALSE;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detclip")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "detcsv")), sensitive);
}
static void reptime_update_daterange(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gchar *daterange;
DB( g_print("\n[rep-time] update daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
daterange = filter_daterange_text_get(data->filter);
gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
g_free(daterange);
}
static void reptime_detail(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
guint active = GPOINTER_TO_INT(user_data);
guint tmpintvl;
guint32 from;
GList *list;
GtkTreeModel *model;
GtkTreeIter iter, child;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[rep-time] detail\n") );
//tmpsrc = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_src));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
//reptime_compute_set_filter was already called here
//get our min max date
from = data->filter->mindate;
//to = data->filter->maxdate;
/* clear and detach our model */
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if(data->detail && data->txn_queue)
{
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_detail), NULL); /* Detach model from view */
/* fill in the model */
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
guint i, pos;
if(filter_txn_match(data->filter, ope) == 1)
{
pos = report_interval_get_pos(tmpintvl, from, ope);
if( pos == active )
{
gdouble dtlamt = report_txn_amount_get(data->filter, ope);
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model),
&iter, NULL, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITAMT, dtlamt,
-1);
//#1875801 show split detail
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
for(i=0;isplits, i);
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &child, &iter, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITPTR, split,
-1);
}
}
}
}
list = g_list_next(list);
}
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
g_object_unref(model);
gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
}
}
static void reptime_update(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
GtkTreeModel *model;
gint page;
gint tmpsrc, tmpintvl;
gboolean cumul;
gchar *title;
DB( g_print("\n[rep-time] update\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_report));
//byamount = 0;
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
cumul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cumul));
// ensure not exp & inc for piechart
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
DB( g_print(" page %d\n\n", page) );
//DB( g_print(" tmpintvl %d\n\n", tmpintvl) );
//column = LST_HUBREPTIME_POS;
//DB( g_print(" sort on column %d\n\n", column) );
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), column, GTK_SORT_DESCENDING);
gtk_chart_show_legend(GTK_CHART(data->RE_chart), FALSE, FALSE);
gtk_chart_show_xval(GTK_CHART(data->RE_chart), TRUE);
gtk_chart_show_average(GTK_CHART(data->RE_chart), data->average, cumul);
title = reptime_compute_title(tmpsrc, tmpintvl);
gtk_chart_set_datas_total(GTK_CHART(data->RE_chart), model, LST_HUBREPTIME_TOTAL, LST_HUBREPTIME_TOTAL, title, NULL);
g_free(title);
if(page == 1)
{
DB( g_print(" change chart type to %d\n", data->charttype) );
gtk_chart_set_type (GTK_CHART(data->RE_chart), data->charttype);
gtk_chart_set_showmono(GTK_CHART(data->RE_chart), TRUE);
}
//test 5.8
//as it is not the filter dialog, count
da_flt_count_item(data->filter);
gchar *txt = filter_text_summary_get(data->filter);
ui_label_set_integer(GTK_LABEL(data->TX_fltactive), data->filter->n_active);
gtk_widget_set_tooltip_text(data->TT_fltactive, txt);
g_free(txt);
}
static void reptime_export_result_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct reptime_data *data;
GtkClipboard *clipboard;
GString *node;
gchar *coltitle;
gint tmpintvl;
DB( g_print("\n[rep-time] export result clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
coltitle = hbtk_get_label(CYA_REPORT_INTVL, tmpintvl);
node = lst_reptime_to_string(HB_STRING_CLIPBOARD, GTK_TREE_VIEW(data->LV_report), coltitle);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void reptime_export_result_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct reptime_data *data;
gchar *filename = NULL;
GString *node;
GIOChannel *io;
gchar *name;
gint tmpsrc;
gchar *title;
gint tmpintvl;
DB( g_print("\n[rep-time] export result csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
name = g_strdup_printf("hb-reptime_%s.csv", hbtk_get_label(CYA_REPORT_GRPBY_TREND, tmpsrc) );
if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
title = hbtk_get_label(CYA_REPORT_INTVL, tmpintvl);
node = lst_reptime_to_string(HB_STRING_EXPORT, GTK_TREE_VIEW(data->LV_report), title);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
g_free( filename );
}
g_free(name);
}
static void reptime_export_detail_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct reptime_data *data;
GtkClipboard *clipboard;
GString *node;
guint flags;
DB( g_print("\n[rep-time] export detail clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
flags = LST_TXN_EXP_CLR | LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), TRUE, FALSE, FALSE, flags);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void reptime_export_detail_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct reptime_data *data;
gchar *filepath = NULL;
GString *node;
GIOChannel *io;
gchar *name;
gint tmpsrc;
gboolean hassplit, hasstatus;
DB( g_print("\n[rep-time] export detail csv\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
name = g_strdup_printf("hb-reptime-detail_%s.csv", hbtk_get_label(CYA_REPORT_GRPBY_TREND, tmpsrc) );
filepath = g_build_filename(PREFS->path_export, name, NULL);
//#2019312
//if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filepath, name) == TRUE )
if( ui_dialog_export_csv(GTK_WINDOW(data->window), &filepath, &hassplit, &hasstatus, FALSE) == GTK_RESPONSE_ACCEPT )
{
DB( g_print(" + filename is %s\n", filepath) );
io = g_io_channel_new_file(filepath, "w", NULL);
if(io != NULL)
{
guint flags;
flags = LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
if( hasstatus )
flags |= LST_TXN_EXP_CLR;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), FALSE, hassplit, FALSE, flags);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
}
g_free( filepath );
g_free(name);
}
static void reptime_update_for(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gint tmpsrc, pageid;
Filter *flt;
DB( g_print("\n[rep-time] update for\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
flt = data->filter;
DB( g_print(" src=%d\n", tmpsrc) );
//filter_reset(flt);
//inactive all filters
flt->option[FLT_GRP_CATEGORY] = 0;
flt->option[FLT_GRP_PAYEE] = 0;
flt->option[FLT_GRP_ACCOUNT] = 0;
flt->option[FLT_GRP_TAG] = 0;
switch(tmpsrc)
{
case REPORT_GRPBY_CATEGORY:
pageid = 1;
flt->option[FLT_GRP_CATEGORY] = 1;
da_flt_status_cat_set(flt, 0, TRUE);
break;
case REPORT_GRPBY_PAYEE:
pageid = 2;
flt->option[FLT_GRP_PAYEE] = 1;
da_flt_status_pay_set(flt, 0, TRUE);
break;
case REPORT_GRPBY_TAG:
pageid = 3;
flt->option[FLT_GRP_TAG] = 1;
break;
default: //REPORT_GRPBY_ACCOUNT
pageid = 0;
flt->option[FLT_GRP_ACCOUNT] = 1;
break;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_itemtype), pageid);
//hb_widget_visible(data->CM_all, defvisible);
//hb_widget_visible(data->BT_filter, !monoflt);
}
//reset the filter
static void reptime_filter_setup(struct reptime_data *data)
{
guint32 accnum;
DB( g_print("\n[rep-time] reset filter\n") );
filter_reset(data->filter);
filter_preset_daterange_set(data->filter, PREFS->date_range_rep, data->accnum);
/* 3.4 : make int transfer out of stats */
//TODO: for compatibility with < 5.3, keep this unset, but normally it should be set
//filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
//5.6 set default account
data->filter->option[FLT_GRP_ACCOUNT] = 1;
accnum = data->accnum;
if(!accnum)
{
accnum = da_acc_get_first_key();
}
DB( g_print(" accnum=%d\n", accnum) );
ui_acc_listview_set_active(GTK_TREE_VIEW(data->LV_acc), accnum);
ui_acc_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_acc), data->filter);
}
//beta
#if BETA_FILTER
static void reptime_action_filter(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
//debug
//create_deffilter_window(data->filter, TRUE);
if(ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, TRUE, FALSE) != GTK_RESPONSE_REJECT)
{
reptime_compute(data->window, NULL);
//ui_repdtime_update_date_widget(data->window, NULL);
//ui_repdtime_update_daterange(data->window, NULL);
/*g_signal_handler_block(data->CY_range, data->handler_id[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_ALLDATE);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPDIST_RANGE]);
*/
}
}
#endif
/* = = = = = = = = = = = = = = = = */
static void reptime_compute(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gint tmpintvl, showempty;
guint32 from, to;
gboolean cumul;
gdouble cumulation, average;
GtkTreeModel *model;
GtkTreeIter iter;
GList *list;
gint id;
guint n_result, i;
DB( g_print("----------------\n[rep-time] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//clear all
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
data->txn_queue = NULL;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
gtk_list_store_clear (GTK_LIST_STORE(model));
gtk_chart_set_datas_none(GTK_CHART(data->RE_chart));
//#2019876 return is invalid date range
if( data->filter->maxdate < data->filter->mindate )
return;
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
cumul = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cumul));
//range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
showempty = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_showempty));
data->accnum = 0;
//to remove > 5.0.2
//#1715532 5.0.5: no... but only showall
/*if( (showall == TRUE) && (range == FLT_RANGE_MISC_ALLDATE) )
{
filter_preset_daterange_set(data->filter, data->filter->range, data->accnum);
reptime_update_quickdate(widget, NULL);
}*/
//get our min max date
from = data->filter->mindate;
to = data->filter->maxdate;
//TODO: not necessary until date range change
//free previous txn
data->txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
n_result = report_interval_count(tmpintvl, from, to);
//DB( gint tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src)) );
//DB( g_print(" %s :: n_result=%d\n", hbtk_get_label(CYA_REPORT_GRPBY_TREND, tmpsrc), n_result) );
/* allocate some memory */
data->tmp_income = g_malloc0((n_result+2) * sizeof(gdouble));
data->tmp_expense = g_malloc0((n_result+2) * sizeof(gdouble));
if(data->tmp_income && data->tmp_expense)
{
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
//DB( g_print("** testing '%s', acc=%d, cat=%d, pay=%d\n", ope->memo, ope->kacc, ope->kcat, ope->kpay) );
if( (filter_txn_match(data->filter, ope) == 1) )
{
guint pos;
gdouble amount;
amount = report_txn_amount_get(data->filter, ope);
//#1829603 Multi currencies problem in Trend Time Report
//if( ! ( tmpsrc == REPORT_GRPBY_ACCOUNT && showall == FALSE) )
amount = hb_amount_base(amount, ope->kcur);
pos = report_interval_get_pos(tmpintvl, from, ope);
if( pos <= n_result )
{
//DB( g_print("** pos=%d : add of %.2f\n", pos, amount) );
if(amount < 0)
data->tmp_expense[pos] += amount;
else
data->tmp_income[pos] += amount;
}
else
{
//DB( g_print("** pos=%d : invalid offset\n", pos) );
}
}
list = g_list_next(list);
}
/* clear and detach our 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_report), NULL); /* Detach model from view */
cumulation = 0.0;
/* insert into the treeview */
for(i=0, id=0; itmp_expense[i] + data->tmp_income[i];
gdouble value;
//if( !showempty && total == 0 )
//#2091004 5.8.6 keep lines with exact 0
if( !showempty
&& (hb_amount_cmp(data->tmp_expense[i], 0.0) == 0)
&& (hb_amount_cmp(data->tmp_income[i], 0.0) == 0)
)
continue;
report_interval_snprint_name(intvlname, sizeof(intvlname)-1, tmpintvl, from, i);
//DB( g_print("try to insert item %d\n", i) );
cumulation += total;
value = (cumul == TRUE) ? cumulation : total;
//DB( g_print(" inserting %2d, '%s', %9.2f\n", i, intvlname, value) );
gtk_list_store_insert_with_values (GTK_LIST_STORE(model), &iter, -1,
LST_HUBREPTIME_POS, id++,
LST_HUBREPTIME_KEY, i,
LST_HUBREPTIME_LABEL, intvlname,
LST_HUBREPTIME_EXPENSE, data->tmp_expense[i],
LST_HUBREPTIME_INCOME, data->tmp_income[i],
LST_HUBREPTIME_TOTAL, value,
-1);
}
// set chart and listview currency
//TODO: we should maybe display in native instead of global ?
//maybe to be done while getting active checkbos and store into filter
guint32 kcur = GLOBALS->kcur;
/*if( (showall == FALSE) && (tmpsrc == REPORT_GRPBY_ACCOUNT) )
{
Account *acc;
//selkey = ui_acc_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_acc));
selkey = ui_acc_entry_popover_get_key(GTK_BOX(data->PO_acc));
acc = da_acc_get(selkey);
if( acc != NULL )
{
kcur = acc->kcur;
//fix 5.4.2 crash here
gtk_chart_set_overdrawn(GTK_CHART(data->RE_chart), acc->minimum);
}
}*/
lst_reptime_set_cur(GTK_TREE_VIEW(data->LV_report), kcur);
gtk_chart_set_currency(GTK_CHART(data->RE_chart), kcur);
/* update column 0 title */
GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
if(column)
gtk_tree_view_column_set_title(column, hbtk_get_label(CYA_REPORT_INTVL, tmpintvl) );
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
g_object_unref(model);
//update average
gtk_label_set_text(GTK_LABEL(data->TX_info), "");
if( cumul == TRUE )
{
gchar *info;
gchar buf[128];
average = cumulation / n_result;
data->average = average;
hb_strfmon(buf, 127, average, kcur, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor)) );
info = g_strdup_printf(_("Average: %s"), buf);
gtk_label_set_text(GTK_LABEL(data->TX_info), info);
g_free(info);
}
}
/* free our memory */
g_free(data->tmp_expense);
g_free(data->tmp_income);
reptime_update(widget, user_data);
}
/* = = = = = = = = = = = = = = = = */
static void reptime_for(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
DB( g_print("\n[rep-time] for\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
reptime_update_for(widget, data);
reptime_compute(widget, data);
}
static void reptime_action_filter_reset(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
DB( g_print("\n[rep-time] filter reset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//TODO: to review: clean selection
ui_acc_listview_quick_select(GTK_TREE_VIEW(data->LV_acc), "non");
ui_cat_listview_quick_select(GTK_TREE_VIEW(data->LV_cat), "non");
ui_pay_listview_quick_select(GTK_TREE_VIEW(data->LV_pay), "non");
ui_tag_listview_quick_select(GTK_TREE_VIEW(data->LV_tag), "non");
reptime_filter_setup(data);
g_signal_handler_block(data->CY_range, data->hid[HID_REPTIME_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPTIME_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_src), REPORT_GRPBY_ACCOUNT);
reptime_compute(data->window, NULL);
}
static void
reptime_date_change(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
DB( g_print("\n[rep-time] date change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
// set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->CY_range, data->hid[HID_REPTIME_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPTIME_RANGE]);
reptime_compute(widget, NULL);
reptime_update_daterange(widget, NULL);
}
static void reptime_update_quickdate(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
DB( g_print("\n[rep-time] update quickdate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
g_signal_handler_block(data->PO_mindate, data->hid[HID_REPTIME_MINDATE]);
g_signal_handler_block(data->PO_maxdate, data->hid[HID_REPTIME_MAXDATE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
g_signal_handler_unblock(data->PO_mindate, data->hid[HID_REPTIME_MINDATE]);
g_signal_handler_unblock(data->PO_maxdate, data->hid[HID_REPTIME_MAXDATE]);
}
static void reptime_range_change(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gint range;
DB( g_print("\n[rep-time] range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
//should never happen
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, data->accnum);
}
//#2046032 set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
reptime_update_quickdate(widget, NULL);
reptime_compute(widget, NULL);
reptime_update_daterange(widget, NULL);
}
/* = = = = = = = = = = = = = = = = */
//beta
#if PRIV_FILTER
static void reptime_action_filter(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
//debug
//create_deffilter_window(data->filter, TRUE);
if(ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, TRUE, FALSE) != GTK_RESPONSE_REJECT)
{
reptime_compute(data->window, NULL);
//ui_repdtime_update_date_widget(data->window, NULL);
//ui_repdtime_update_daterange(data->window, NULL);
/*g_signal_handler_block(data->CY_range, data->hid[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_ALLDATE);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPDIST_RANGE]);
*/
}
}
#endif
static void reptime_action_viewlist(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
data->charttype = CHART_TYPE_NONE;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 0);
reptime_sensitive(data->window, NULL);
}
static void reptime_action_viewline(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
data->charttype = CHART_TYPE_LINE;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
reptime_sensitive(data->window, NULL);
reptime_update(data->window, NULL);
}
static void reptime_action_viewcolumn(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
data->charttype = CHART_TYPE_COL;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
reptime_sensitive(data->window, NULL);
reptime_update(data->window, NULL);
}
static void reptime_action_print(GtkWidget *toolbutton, gpointer user_data)
{
struct reptime_data *data = user_data;
gint tmpsrc, tmpintvl, page;
gchar *coltitle, *title, *name;
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
title = reptime_compute_title(tmpsrc, tmpintvl);
name = g_strdup_printf("hb-reptime_%s.csv", hbtk_get_label(CYA_REPORT_GRPBY_TREND, tmpsrc) );
if( page == 0 )
{
GString *node;
coltitle = hbtk_get_label(CYA_REPORT_INTVL, tmpintvl);
node = lst_reptime_to_string(HB_STRING_PRINT, GTK_TREE_VIEW(data->LV_report), coltitle);
hb_print_listview(GTK_WINDOW(data->window), node->str, NULL, title, name, FALSE);
g_string_free(node, TRUE);
}
else
{
gtk_chart_print(GTK_CHART(data->RE_chart), GTK_WINDOW(data->window), PREFS->path_export, name);
}
g_free(name);
}
static void
reptime_cb_acc_changed(GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data)
{
struct reptime_data *data = user_data;
DB( g_print ("\n[rep-time] acc list changed\n") );
ui_acc_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_acc), data->filter);
reptime_compute(GTK_WIDGET(data->LV_acc), NULL);
}
static void
reptime_cb_cat_changed(GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data)
{
struct reptime_data *data = user_data;
DB( g_print ("\n[rep-time] cat list changed\n") );
ui_cat_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_cat), data->filter);
reptime_compute(GTK_WIDGET(data->LV_cat), NULL);
}
static void
reptime_cb_pay_changed(GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data)
{
struct reptime_data *data = user_data;
DB( g_print ("\n[rep-time] pay list changed\n") );
ui_pay_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_pay), data->filter);
reptime_compute(GTK_WIDGET(data->LV_pay), NULL);
}
static void
reptime_cb_tag_changed(GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data)
{
struct reptime_data *data = user_data;
DB( g_print ("\n[rep-time] tag list changed\n") );
ui_tag_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_tag), data->filter);
reptime_compute(GTK_WIDGET(data->LV_tag), NULL);
}
static gboolean
reptime_cb_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
struct reptime_data *data;
gint tmpsrc;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(label), GTK_TYPE_WINDOW)), "inst_data");
tmpsrc = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_src));
switch(tmpsrc)
{
case REPORT_GRPBY_ACCOUNT:
ui_acc_listview_quick_select(GTK_TREE_VIEW(data->LV_acc), uri);
reptime_cb_acc_changed(NULL, NULL, data);
break;
case REPORT_GRPBY_CATEGORY:
ui_cat_listview_quick_select(GTK_TREE_VIEW(data->LV_cat), uri);
reptime_cb_cat_changed(NULL, NULL, data);
break;
case REPORT_GRPBY_PAYEE:
ui_pay_listview_quick_select(GTK_TREE_VIEW(data->LV_pay), uri);
reptime_cb_pay_changed(NULL, NULL, data);
break;
case REPORT_GRPBY_TAG:
ui_tag_listview_quick_select(GTK_TREE_VIEW(data->LV_tag), uri);
reptime_cb_tag_changed(NULL, NULL, data);
break;
}
reptime_compute (GTK_WIDGET(data->window), NULL);
return TRUE;
}
static void reptime_detail_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct reptime_data *data;
Transaction *active_txn;
gboolean result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[rep-time] A detail row has been double-clicked!\n") );
active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_detail));
if(active_txn)
{
Transaction *old_txn, *new_txn;
//#1909749 skip reconciled if lock is ON
if( PREFS->safe_lock_recon == TRUE && active_txn->status == TXN_STATUS_RECONCILED )
return;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path = NULL;
//1936806 keep the selection
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
path = gtk_tree_model_get_path(model, &iter);
}
//#1640885
GLOBALS->changes_count++;
reptime_compute(data->window, NULL);
if( path != NULL )
{
gtk_tree_selection_select_path(treeselection, path);
gtk_tree_path_free(path);
}
}
da_transaction_free (old_txn);
}
}
static void reptime_update_detail(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#2018039
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_detail), PREFS->safe_lock_recon);
if(data->detail)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
guint key;
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_HUBREPTIME_KEY, &key, -1);
DB( g_print(" - active is %d\n", key) );
reptime_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
}
gtk_widget_show(data->GR_detail);
}
else
gtk_widget_hide(data->GR_detail);
}
static void reptime_toggle_detail(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->detail ^= 1;
DB( g_print("\n[rep-time] toggledetail to %d\n", data->detail) );
reptime_update_detail(widget, user_data);
reptime_sensitive(widget, NULL);
}
static void reptime_cb_zoomx_changed(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
gdouble value;
DB( g_print("\n[rep-time] zoomx\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
value = gtk_range_get_value(GTK_RANGE(data->RG_zoomx));
DB( g_print(" + scale is %.2f\n", value) );
gtk_chart_set_barw(GTK_CHART(data->RE_chart), value);
}
static void reptime_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct reptime_data *data;
DB( g_print("\n[rep-time] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
//hbfile_update(data->LV_acc, (gpointer)4);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
gtk_chart_show_minor(GTK_CHART(data->RE_chart), GLOBALS->minor);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart));
}
static void reptime_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
guint key = -1;
DB( g_print("\n[rep-time] selection\n") );
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_HUBREPTIME_KEY, &key, -1);
}
DB( g_print(" - active is %d\n", key) );
reptime_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
reptime_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
/* = = = = = = = = = = = = = = = = */
static const GActionEntry win_actions[] = {
{ "resclip" , reptime_export_result_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "rescsv" , reptime_export_result_csv, NULL, NULL, NULL, {0,0,0} },
{ "detclip" , reptime_export_detail_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "detcsv" , reptime_export_detail_csv, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
static GtkWidget *
reptime_toolbar_create(struct reptime_data *data)
{
GtkWidget *toolbar, *button;
toolbar = gtk_toolbar_new();
button = (GtkWidget *)gtk_radio_tool_button_new(NULL);
data->BT_list = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LIST, "label", _("List"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as list"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_line = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LINE, "label", _("Line"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as lines"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_column = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_COLUMN, "label", _("Column"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as column"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_OPE_SHOW,
"label", _("Detail"),
"tooltip-text", _("Toggle detail"),
NULL);
data->BT_detail = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REFRESH, _("Refresh"), _("Refresh results"));
data->BT_refresh = button;
//export button
button = gtk_menu_button_new();
data->BT_export = button;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(button)), GTK_STYLE_CLASS_FLAT);
GtkWidget *image = hbtk_image_new_from_icon_name_24 (ICONNAME_HB_FILE_EXPORT);
g_object_set (button, "image", image, NULL);
GtkToolItem *toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), button);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(toolitem), -1);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Result to clipboard"), "win.resclip");
g_menu_append (section, _("_Result to CSV") , "win.rescsv");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Detail to clipboard"), "win.detclip");
g_menu_append (section, _("_Detail to CSV") , "win.detcsv");
g_object_unref (section);
GActionGroup *actiongroup = (GActionGroup*)g_simple_action_group_new ();
data->actions = actiongroup;
g_action_map_add_action_entries (G_ACTION_MAP (actiongroup), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (button, "win", G_ACTION_GROUP(actiongroup));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), G_MENU_MODEL (menu));
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_PRINT, _("Print"), _("Print"));
data->BT_print = button;
return toolbar;
}
static void reptime_window_setup(struct reptime_data *data)
{
DB( g_print("\n[rep-time] window setup\n") );
DB( g_print(" init data\n") );
DB( g_print(" populate\n") );
ui_acc_listview_populate(data->LV_acc, ACC_LST_INSERT_REPORT, NULL);
ui_cat_listview_populate(data->LV_cat, CAT_TYPE_ALL, NULL, TRUE);
ui_pay_listview_populate(data->LV_pay, NULL, TRUE);
ui_tag_listview_populate(data->LV_tag, 0);
reptime_filter_setup(data);
reptime_update_for(data->window, data);
DB( g_print(" set widgets default\n") );
//src is account
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_intvl), REPORT_INTVL_MONTH);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor), GLOBALS->minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail))), "minor", (gpointer)data->CM_minor);
gtk_chart_set_smallfont (GTK_CHART(data->RE_chart), PREFS->rep_smallfont);
DB( g_print(" connect widgets signals\n") );
//display signals
data->hid[HID_REPTIME_VIEW] = g_signal_connect (data->CY_intvl, "changed", G_CALLBACK (reptime_compute), (gpointer)data);
g_signal_connect (data->CM_cumul, "toggled", G_CALLBACK (reptime_compute), NULL);
g_signal_connect (data->CM_showempty, "toggled", G_CALLBACK (reptime_compute), NULL);
g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (reptime_toggle_minor), NULL);
g_signal_connect (data->RG_zoomx, "value-changed", G_CALLBACK (reptime_cb_zoomx_changed), NULL);
//filter signals
g_signal_connect (data->BT_reset , "clicked", G_CALLBACK (reptime_action_filter_reset), NULL);
data->hid[HID_REPTIME_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (reptime_date_change), (gpointer)data);
data->hid[HID_REPTIME_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (reptime_date_change), (gpointer)data);
data->hid[HID_REPTIME_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (reptime_range_change), NULL);
//item filter
g_signal_connect (data->CY_src, "changed", G_CALLBACK (reptime_for), (gpointer)data);
g_signal_connect (data->BT_all, "activate-link", G_CALLBACK (reptime_cb_activate_link), NULL);
g_signal_connect (data->BT_non, "activate-link", G_CALLBACK (reptime_cb_activate_link), NULL);
g_signal_connect (data->BT_inv, "activate-link", G_CALLBACK (reptime_cb_activate_link), NULL);
GtkCellRendererToggle *renderer;
renderer = g_object_get_data(G_OBJECT(data->LV_acc), "togrdr_data");
g_signal_connect_after (G_OBJECT(renderer), "toggled", G_CALLBACK (reptime_cb_acc_changed), (gpointer)data);
renderer = g_object_get_data(G_OBJECT(data->LV_cat), "togrdr_data");
g_signal_connect_after (G_OBJECT(renderer), "toggled", G_CALLBACK (reptime_cb_cat_changed), (gpointer)data);
renderer = g_object_get_data(G_OBJECT(data->LV_pay), "togrdr_data");
g_signal_connect_after (G_OBJECT(renderer), "toggled", G_CALLBACK (reptime_cb_pay_changed), (gpointer)data);
renderer = g_object_get_data(G_OBJECT(data->LV_tag), "togrdr_data");
g_signal_connect_after (G_OBJECT(renderer), "toggled", G_CALLBACK (reptime_cb_tag_changed), (gpointer)data);
//toolbar signals
g_signal_connect (G_OBJECT (data->BT_list), "clicked", G_CALLBACK (reptime_action_viewlist), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_line), "clicked", G_CALLBACK (reptime_action_viewline), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_column), "clicked", G_CALLBACK (reptime_action_viewcolumn), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_detail), "clicked", G_CALLBACK (reptime_toggle_detail), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_refresh), "clicked", G_CALLBACK (reptime_compute), (gpointer)data);
//export is a menu
g_signal_connect (G_OBJECT (data->BT_print), "clicked", G_CALLBACK (reptime_action_print), (gpointer)data);
//treeview signals
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), "changed", G_CALLBACK (reptime_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (reptime_detail_onRowActivated), NULL);
}
static gboolean reptime_window_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct reptime_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[rep-time] window mapped\n") );
//setup, init and show window
reptime_window_setup(data);
//trigger update
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
data->mapped_done = TRUE;
return FALSE;
}
static gboolean reptime_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct reptime_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[rep-time] window dispose\n") );
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
da_flt_free(data->filter);
g_free(data);
//store position and size
wg = &PREFS->tme_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
//enable define windows
GLOBALS->define_off--;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
//unref window to our open window list
GLOBALS->openwindows = g_slist_remove(GLOBALS->openwindows, widget);
return FALSE;
}
//allocate our object/memory
static void reptime_window_acquire(struct reptime_data *data)
{
DB( g_print("\n[rep-time] window acquire\n") );
data->txn_queue = g_queue_new ();
data->filter = da_flt_malloc();
data->detail = 0;
}
// the window creation
GtkWidget *reptime_window_new(guint32 accnum)
{
struct reptime_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *hbox, *vbox, *fbox, *bbox, *notebook, *treeview, *treebox, *vpaned, *scrollwin;
GtkWidget *label, *widget, *table;
gint row;
DB( g_print("\n[rep-time] window new\n") );
data = g_malloc0(sizeof(struct reptime_data));
if(!data) return NULL;
data->accnum = accnum;
reptime_window_acquire(data);
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//ref window to our open window list
GLOBALS->openwindows = g_slist_prepend(GLOBALS->openwindows, window);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
gtk_window_set_title (GTK_WINDOW (window), _("Trend Time Report"));
//window contents
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_SMALL);
gtk_window_set_child(GTK_WINDOW(window), mainbox);
//control part
table = gtk_grid_new ();
gtk_widget_set_hexpand (GTK_WIDGET(table), FALSE);
gtk_box_prepend (GTK_BOX (mainbox), table);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
label = make_label_group(_("Display"));
gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
row++;
label = make_label_widget(_("Inter_val:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_INTVL);
data->CY_intvl = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("_Cumulate"));
data->CM_cumul = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show empty line"));
data->CM_showempty = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Zoom X:"));
data->LB_zoomx = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = make_scale(label);
data->RG_zoomx = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//-- filter
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
//5.8 test
row++;
fbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_grid_attach (GTK_GRID (table), fbox, 0, row, 3, 1);
label = make_label_group(_("Filter"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
gtk_box_prepend (GTK_BOX (fbox), label);
// active
label = make_label_widget(_("Active:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (fbox), label);
label = make_label(NULL, 0.0, 0.5);
gtk_widget_set_margin_start(label, SPACING_SMALL);
data->TX_fltactive = label;
gtk_box_prepend (GTK_BOX (fbox), label);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
data->TT_fltactive = fbox;
gtk_box_prepend (GTK_BOX (fbox), widget);
//test button
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (bbox), GTK_STYLE_CLASS_LINKED);
gtk_box_append (GTK_BOX (fbox), bbox);
widget = make_image_button(ICONNAME_HB_CLEAR, _("Clear filter"));
data->BT_reset = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
row++;
//label = make_label_group(_("Date filter"));
label = make_label_group(_("Date"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_DISABLE);
gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
row++;
label = make_label_widget(_("_From:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_mindate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_maxdate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
row++;
label = make_label_group(_("Item"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_By:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_GRPBY_TREND);
data->CY_src = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
treebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_grid_attach (GTK_GRID (table), treebox, 1, row, 2, 1);
label = make_label (_("Select:"), 0, 0.5);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("all", _("All"));
data->BT_all = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("non", _("None"));
data->BT_non = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("inv", _("Invert"));
data->BT_inv = label;
gtk_box_prepend (GTK_BOX (treebox), label);
row++;
notebook = gtk_notebook_new();
data->GR_itemtype = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_grid_attach (GTK_GRID (table), notebook, 1, row, 2, 1);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
data->SW_acc = scrollwin;
gtk_widget_set_margin_bottom (scrollwin, SPACING_LARGE);
treeview = ui_acc_listview_new(TRUE);
data->LV_acc = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
//gtk_widget_set_size_request(widget, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollwin, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
//data->SW_acc = scrollwin;
gtk_widget_set_margin_bottom (scrollwin, SPACING_LARGE);
treeview = ui_cat_listview_new(TRUE, FALSE);
data->LV_cat = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
//gtk_widget_set_size_request(widget, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollwin, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
//data->SW_acc = scrollwin;
gtk_widget_set_margin_bottom (scrollwin, SPACING_LARGE);
treeview = ui_pay_listview_new(TRUE, FALSE);
data->LV_pay = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
//gtk_widget_set_size_request(widget, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollwin, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
//data->SW_acc = scrollwin;
gtk_widget_set_margin_bottom (scrollwin, SPACING_LARGE);
treeview = ui_tag_listview_new(TRUE, FALSE);
data->LV_tag = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
//gtk_widget_set_size_request(widget, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollwin, NULL);
//part: info + report
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
//toolbar
widget = reptime_toolbar_create(data);
data->TB_bar = widget;
gtk_box_prepend (GTK_BOX (vbox), widget);
//infos
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
hb_widget_set_margin(GTK_WIDGET(hbox), SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = make_label(NULL, 0.5, 0.5);
gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
data->TX_daterange = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = gtk_label_new(NULL);
data->TX_info = label;
gtk_box_append (GTK_BOX (hbox), label);
/* report area */
notebook = gtk_notebook_new();
data->GR_result = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
hbtk_box_prepend (GTK_BOX (vbox), notebook);
//page: list
vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vpaned, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = lst_reptime_create();
data->LV_report = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack1 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
//detail
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->GR_detail = scrollwin;
//gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW (scrollwin), GTK_CORNER_TOP_RIGHT);
treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_det_columns);
data->LV_detail = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack2 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
list_txn_set_save_column_width(GTK_TREE_VIEW(treeview), TRUE);
//page: lines
widget = gtk_chart_new(CHART_TYPE_LINE);
data->RE_chart = widget;
//gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.suffix_symbol);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
// connect dialog signals
g_signal_connect (window, "delete-event", G_CALLBACK (reptime_window_dispose), (gpointer)data);
g_signal_connect (window, "map-event" , G_CALLBACK (reptime_window_mapped), NULL);
// setup, init and show window
wg = &PREFS->tme_wg;
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
// toolbar
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_widget_show_all (window);
//hide start widget
hb_widget_visible(data->LB_zoomx, FALSE);
hb_widget_visible(data->RG_zoomx, FALSE);
hb_widget_visible(data->CM_minor, PREFS->euro_active);
hb_widget_visible(data->GR_detail, data->detail);
return window;
}
/* = = = = = = = = = = = = = = = = */
static GString *lst_reptime_to_string(ToStringMode mode, GtkTreeView *treeview, gchar *title)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gchar sep;
node = g_string_new(NULL);
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
// header
g_string_append (node, (title == NULL) ? _("Time slice") : title );
g_string_append_c (node, sep );
g_string_append (node, _("Expense") );
g_string_append_c (node, sep );
g_string_append (node, _("Income") );
g_string_append_c (node, sep );
g_string_append (node, _("Total") );
g_string_append (node, "\n" );
// lines
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gchar *name;
gdouble values[4];
gtk_tree_model_get (model, &iter,
//LST_HUBREPTIME_KEY, i,
LST_HUBREPTIME_LABEL, &name,
LST_HUBREPTIME_EXPENSE, &values[0],
LST_HUBREPTIME_INCOME, &values[1],
LST_HUBREPTIME_TOTAL, &values[2],
-1);
g_string_append (node, name );
for(guint i=0;i<3;i++)
{
g_string_append_c(node, sep);
_format_decimal(node, mode, values[i]);
}
g_string_append_c(node, '\n');
//leak
g_free(name);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
static void lst_reptime_cell_data_function_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint colid = GPOINTER_TO_INT(user_data);
gtk_tree_model_get(model, iter, colid, &value, -1);
//#2091004 we have exact 0.0, do we force display ?
if( (hb_amount_cmp(value, 0.0) != 0) || (colid == LST_HUBREPTIME_TOTAL) )
{
guint32 kcur = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(gtk_tree_view_column_get_tree_view(col)), "kcur_data"));
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, GLOBALS->minor);
g_object_set(renderer,
"foreground", get_normal_color_amount(value),
"text", buf,
NULL);
}
else
{
g_object_set(renderer, "text", "", NULL);
}
}
static GtkTreeViewColumn *lst_reptime_column_create_amount(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_reptime_cell_data_function_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static gint lst_reptime_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
gint pos1, pos2;
gdouble val1, val2;
gtk_tree_model_get(model, a,
LST_HUBREPTIME_POS, &pos1,
sortcol, &val1,
-1);
gtk_tree_model_get(model, b,
LST_HUBREPTIME_POS, &pos2,
sortcol, &val2,
-1);
/*
if(pos1 == -1) return(1);
if(pos2 == -1) return(-1);
*/
if(sortcol == LST_HUBREPTIME_POS)
retval = pos2 - pos1;
else
retval = (ABS(val1) - ABS(val2)) > 0 ? 1 : -1;
DB( g_print(" sort %d=%d or %.2f=%.2f :: %d\n", pos1,pos2, val1, val2, retval) );
return retval;
}
static void lst_reptime_set_cur(GtkTreeView *treeview, guint32 kcur)
{
g_object_set_data(G_OBJECT(treeview), "kcur_data", GUINT_TO_POINTER(kcur));
}
/*
** create our statistic list
*/
static GtkWidget *lst_reptime_create(void)
{
GtkListStore *store;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
/* create list store */
store = gtk_list_store_new(
NUM_LST_HUBREPTIME,
G_TYPE_INT, //pos
G_TYPE_INT, //key
G_TYPE_STRING, //label
G_TYPE_DOUBLE, //exp
G_TYPE_DOUBLE, //inc
G_TYPE_DOUBLE, //total
G_TYPE_INT //flags
);
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
/* column: Label */
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Time slice"));
renderer = gtk_cell_renderer_text_new();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
//gtk_tree_view_column_set_cell_data_func(column, renderer, ope_result_cell_data_function, NULL, NULL);
gtk_tree_view_column_add_attribute(column, renderer, "text", LST_HUBREPTIME_LABEL);
//gtk_tree_view_column_set_sort_column_id (column, LST_HUBREPTIME_NAME);
gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
//gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Expense */
column = lst_reptime_column_create_amount(_("Expense"), LST_HUBREPTIME_EXPENSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Income */
column = lst_reptime_column_create_amount(_("Income"), LST_HUBREPTIME_INCOME);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Total */
column = lst_reptime_column_create_amount(_("Total"), LST_HUBREPTIME_TOTAL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column last: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* sort */
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_HUBREPTIME_POS , lst_reptime_compare_func, GINT_TO_POINTER(LST_HUBREPTIME_POS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_HUBREPTIME_TOTAL, lst_reptime_compare_func, GINT_TO_POINTER(LST_HUBREPTIME_TOTAL), NULL);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
return(view);
}
homebank-5.9.7/src/gtk-dateentry.h 0000644 0001750 0001750 00000005347 14736461415 016360 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __GTK_DATE_ENTRY_H__
#define __GTK_DATE_ENTRY_H__
G_BEGIN_DECLS
#define GTK_TYPE_DATE_ENTRY (gtk_date_entry_get_type ())
#define GTK_DATE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DATE_ENTRY, GtkDateEntry))
#define GTK_DATE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DATE_ENTRY, GtkDateEntryClass)
#define GTK_IS_DATE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DATE_ENTRY))
#define GTK_IS_DATE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DATE_ENTRY))
#define GTK_DATE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DATE_ENTRY, GtkDateEntryClass))
typedef struct _GtkDateEntry GtkDateEntry;
typedef struct _GtkDateEntryClass GtkDateEntryClass;
typedef struct _GtkDateEntryPrivate GtkDateEntryPrivate;
#define HB_MINDATE 693596 //01/01/1900
#define HB_MAXDATE 803533 //31/12/2200
struct _GtkDateEntry
{
GtkBox box;
/*< private >*/
GtkDateEntryPrivate *priv;
};
struct _GtkDateEntryClass
{
GtkBoxClass parent_class;
/* signals */
void (* changed) (GtkDateEntry *dateentry);
/* Padding for future expansion */
void (*_gtk_reserved0) (void);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
};
struct _GtkDateEntryPrivate
{
GtkWidget *entry;
GtkWidget *button;
GtkWidget *arrow;
GtkWidget *popover;
GtkWidget *calendar;
GtkWidget *BT_today;
GDate *date;
guint32 lastdate;
GDate nowdate, mindate, maxdate;
gulong hid_dayselect;
};
GType gtk_date_entry_get_type(void) G_GNUC_CONST;
GtkWidget *gtk_date_entry_new(GtkWidget *label);
void gtk_date_entry_set_error(GtkDateEntry *dateentry, gboolean error);
guint32 gtk_date_entry_get_date(GtkDateEntry * dateentry);
void gtk_date_entry_set_date(GtkDateEntry * dateentry, guint32 julian_days);
GDateWeekday gtk_date_entry_get_weekday(GtkDateEntry *dateentry);
G_END_DECLS
#endif /* __GTK_DATE_ENTRY_H__ */
homebank-5.9.7/src/ui-filter.c 0000644 0001750 0001750 00000136576 15106116350 015465 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-widgets.h"
#include "ui-filter.h"
#include "ui-account.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-tag.h"
#include "hbtk-switcher.h"
#include "gtk-dateentry.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern gchar *CYA_FLT_TYPE[];
extern gchar *CYA_FLT_STATUS[];
extern gchar *CYA_SELECT[];
extern gchar *RA_FILTER_MODE[];
extern HbKivData CYA_TXN_PAYMODE[NUM_PAYMODE_MAX];
/* = = = = = = = = = = = = = = = = = = = = */
static void
ui_flt_manage_cb_range_change(GtkWidget *widget, gpointer user_data);
static guint
_gtkentry_to_filter(GtkEntry *entry, gchar **storage)
{
const gchar *txt;
guint change = 0;
if(!GTK_IS_ENTRY(entry))
return 0;
txt = gtk_entry_get_text(GTK_ENTRY(entry));
if( g_strcmp0(txt, *storage) != 0 )
{
change++;
g_free(*storage);
*storage = g_strdup(txt);
}
return change;
}
/* = = = = = = = = = = = = = = = = = = = = */
static void ui_flt_hub_tag_set(Filter *flt, struct ui_flt_manage_data *data)
{
DB( g_print("(ui_flt_hub_tag) set\n") );
if(data->filter != NULL)
{
GtkTreeModel *model;
//GtkTreeSelection *selection;
GtkTreeIter iter;
gboolean valid;
gint i;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_tag));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_tag));
i=0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Tag *tagitem;
gboolean status;
gtk_tree_model_get (model, &iter,
LST_DEFTAG_DATAS, &tagitem,
-1);
status = da_flt_status_tag_get(flt, tagitem->key);
DB( g_print(" set tag k:%3d = %d (%s)\n", tagitem->key, status, tagitem->name) );
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFTAG_TOGGLE, status, -1);
/* Make iter point to the next row in the list store */
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
}
static gboolean ui_flt_hub_tag_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
DB( g_print("(ui_flt_hub_tag) activate_link\n") );
g_return_val_if_fail(GTK_IS_TREE_VIEW(user_data), TRUE);
ui_tag_listview_quick_select(user_data, uri );
return TRUE;
}
/* = = = = = = = = = = = = = = = = = = = = */
//#1828732 add expand/collapse all for categories in edit filter
static void ui_flt_hub_category_expand_all(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n(ui_flt_hub_category) expand all (data=%p)\n", data) );
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_cat));
}
static void ui_flt_hub_category_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n(ui_flt_hub_category) collapse all (data=%p)\n", data) );
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_cat));
}
static void ui_flt_hub_category_set(Filter *flt, struct ui_flt_manage_data *data)
{
DB( g_print("(ui_flt_hub_category) set\n") );
if(data->filter != NULL)
{
GtkTreeModel *model;
//GtkTreeSelection *selection;
GtkTreeIter iter, child;
gint n_child;
gboolean valid;
gint i;
// category
DB( g_print(" category\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat));
i=0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Category *catitem;
gboolean status;
gtk_tree_model_get (model, &iter,
LST_DEFCAT_DATAS, &catitem,
-1);
status = da_flt_status_cat_get(flt, catitem->key);
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, LST_DEFCAT_TOGGLE, status, -1);
DB( g_print(" set %d to '%s' %d\n", status, catitem->name, catitem->key) );
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
i++;
gtk_tree_model_get (model, &child,
LST_DEFCAT_DATAS, &catitem,
-1);
status = da_flt_status_cat_get(flt, catitem->key);
gtk_tree_store_set (GTK_TREE_STORE (model), &child, LST_DEFCAT_TOGGLE, status, -1);
DB( g_print(" set %d to '%s' %d\n", status, catitem->name, catitem->key) );
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
/* Make iter point to the next row in the list store */
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
}
static gboolean ui_flt_hub_category_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
DB( g_print("(ui_flt_hub_category) activate_link\n") );
g_return_val_if_fail(GTK_IS_TREE_VIEW(user_data), TRUE);
ui_cat_listview_quick_select(user_data, uri );
return TRUE;
}
/* = = = = = = = = = = = = = = = = */
static void ui_flt_hub_payee_set(Filter *flt, struct ui_flt_manage_data *data)
{
DB( g_print("(ui_flt_hub_payee) set\n") );
if(data->filter != NULL)
{
GtkTreeModel *model;
//GtkTreeSelection *selection;
GtkTreeIter iter;
gboolean valid;
gint i;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_pay));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_pay));
i=0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Payee *payitem;
gboolean status;
gtk_tree_model_get (model, &iter,
LST_DEFPAY_DATAS, &payitem,
-1);
status = da_flt_status_pay_get(flt, payitem->key);
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFPAY_TOGGLE, status, -1);
DB( g_print(" set %d to '%s' %d\n", status, payitem->name, payitem->key) );
/* Make iter point to the next row in the list store */
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
}
static gboolean ui_flt_hub_payee_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
DB( g_print("(ui_flt_hub_payee) activate_link\n") );
g_return_val_if_fail(GTK_IS_TREE_VIEW(user_data), TRUE);
ui_pay_listview_quick_select(user_data, uri );
return TRUE;
}
/* = = = = = = = = = = = = = = = = */
static void ui_flt_hub_account_set(Filter *flt, struct ui_flt_manage_data *data)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gint i;
DB( g_print("(ui_flt_hub_account) set\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
i=0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Account *accitem;
gboolean status;
gtk_tree_model_get (model, &iter,
LST_DEFACC_DATAS, &accitem,
-1);
status = da_flt_status_acc_get(flt, accitem->key);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
LST_DEFACC_TOGGLE, status, -1);
/* Make iter point to the next row in the list store */
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static gboolean ui_flt_hub_account_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
DB( g_print("(ui_flt_hub_account) activate_link\n") );
g_return_val_if_fail(GTK_IS_TREE_VIEW(user_data), TRUE);
ui_acc_listview_quick_select(GTK_TREE_VIEW(user_data), uri);
return TRUE;
}
/* = = = = = = = = = = = = = = = = */
static void ui_flt_manage_update_page(gint pageidx, const gchar *pagename, struct ui_flt_manage_data *data)
{
GtkWidget *child;
GValue gvalue = G_VALUE_INIT;
gboolean visible;
g_value_init (&gvalue, G_TYPE_BOOLEAN);
visible = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[pageidx]))) ? FALSE : TRUE;
gtk_widget_set_sensitive(data->RA_matchmode[pageidx], visible);
gtk_widget_set_sensitive(data->GR_page[pageidx], visible);
g_value_set_boolean (&gvalue, visible);
child = gtk_stack_get_child_by_name(GTK_STACK(data->stack), pagename);
gtk_container_child_set_property(GTK_CONTAINER(data->stack), child, "needs-attention", &gvalue);
}
static void ui_flt_manage_update(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_manage_data *data;
gboolean sensitive, visible, v1, v2;
GtkWidget *child;
GValue gvalue = G_VALUE_INIT;
gint range;
DB( g_print("\n[ui_flt_manage] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
g_value_init (&gvalue, G_TYPE_BOOLEAN);
// date
gtk_widget_set_sensitive(data->SW_enabled[FLT_GRP_DATE], FALSE);
ui_flt_manage_update_page(FLT_GRP_DATE, FLT_PAGE_NAME_DAT, data);
sensitive = (range == FLT_RANGE_MISC_CUSTOM) ? TRUE : FALSE;
gtk_widget_set_sensitive(GTK_WIDGET(data->LB_mindate), sensitive);
gtk_widget_set_sensitive(GTK_WIDGET(data->LB_maxdate), sensitive);
gtk_widget_set_sensitive(GTK_WIDGET(data->PO_mindate), sensitive);
gtk_widget_set_sensitive(GTK_WIDGET(data->PO_maxdate), sensitive);
// type
visible = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[FLT_GRP_TYPE]))) ? FALSE : TRUE;
gtk_widget_set_sensitive(data->RA_matchmode[FLT_GRP_TYPE], visible);
gtk_widget_set_sensitive(data->GR_page[FLT_GRP_TYPE], visible);
g_value_set_boolean (&gvalue, visible);
child = gtk_stack_get_child_by_name(GTK_STACK(data->stack), FLT_PAGE_NAME_TYP);
gtk_container_child_set_property(GTK_CONTAINER(data->stack), child, "needs-attention", &gvalue);
// status
visible = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[FLT_GRP_STATUS]))) ? FALSE : TRUE;
gtk_widget_set_sensitive(data->RA_matchmode[FLT_GRP_STATUS], visible);
gtk_widget_set_sensitive(data->GR_page[FLT_GRP_STATUS], visible);
g_value_set_boolean (&gvalue, visible);
child = gtk_stack_get_child_by_name(GTK_STACK(data->stack), FLT_PAGE_NAME_STA);
gtk_container_child_set_property(GTK_CONTAINER(data->stack), child, "needs-attention", &gvalue);
// account
if(data->show_account == TRUE)
ui_flt_manage_update_page(FLT_GRP_ACCOUNT, FLT_PAGE_NAME_ACC, data);
// amount/text
v1 = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[FLT_GRP_AMOUNT]))) ? FALSE : TRUE;
gtk_widget_set_sensitive(data->RA_matchmode[FLT_GRP_AMOUNT], v1);
gtk_widget_set_sensitive(data->GR_page[FLT_GRP_AMOUNT], v1);
v2 = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[FLT_GRP_TEXT]))) ? FALSE : TRUE;
gtk_widget_set_sensitive(data->RA_matchmode[FLT_GRP_TEXT], v2);
gtk_widget_set_sensitive(data->GR_page[FLT_GRP_TEXT], v2);
visible = v1 | v2;
g_value_set_boolean (&gvalue, visible);
child = gtk_stack_get_child_by_name(GTK_STACK(data->stack), FLT_PAGE_NAME_TXT);
gtk_container_child_set_property(GTK_CONTAINER(data->stack), child, "needs-attention", &gvalue);
// payee
ui_flt_manage_update_page(FLT_GRP_PAYEE, FLT_PAGE_NAME_PAY, data);
// category
ui_flt_manage_update_page(FLT_GRP_CATEGORY, FLT_PAGE_NAME_CAT, data);
// tag
ui_flt_manage_update_page(FLT_GRP_TAG, FLT_PAGE_NAME_TAG, data);
// payment
ui_flt_manage_update_page(FLT_GRP_PAYMODE, FLT_PAGE_NAME_PMT, data);
//v1 = (!gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[FLT_GRP_PAYMODE]))) ? FALSE : TRUE;
//gtk_widget_set_sensitive(data->GR_page[FLT_GRP_PAYMODE], v1);
}
static void
ui_flt_manage_get_option(struct ui_flt_manage_data *data, gint index)
{
gint newoption = gtk_switch_get_active(GTK_SWITCH(data->SW_enabled[index]));
//option should be set: 0=off, 1=include, 2=exclude
if( newoption == 1 )
{
if( hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_matchmode[index])) == 1)
newoption++;
}
data->filter->option[index] = newoption;
}
static void ui_flt_manage_get(struct ui_flt_manage_data *data)
{
Filter *flt = data->filter;
gchar *olddigest, *newdigest;
guint i, length;
gboolean active;
DB( g_print("\n[ui_flt_manage] get\n") );
if(flt != NULL)
{
//TODO: 5.8 we should count changes into flt->nbchanges
length = offsetof(Filter, exact);
DB( g_print(" length: %d\n", length) );
//use a checksum for non-pointer data
olddigest = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *)flt, length);
DB( g_print(" option\n") );
ui_flt_manage_get_option(data, FLT_GRP_DATE);
ui_flt_manage_get_option(data, FLT_GRP_TYPE);
ui_flt_manage_get_option(data, FLT_GRP_STATUS);
ui_flt_manage_get_option(data, FLT_GRP_PAYEE);
ui_flt_manage_get_option(data, FLT_GRP_CATEGORY);
ui_flt_manage_get_option(data, FLT_GRP_TAG);
if(data->show_account == TRUE)
ui_flt_manage_get_option(data, FLT_GRP_ACCOUNT);
ui_flt_manage_get_option(data, FLT_GRP_TEXT);
ui_flt_manage_get_option(data, FLT_GRP_AMOUNT);
ui_flt_manage_get_option(data, FLT_GRP_PAYMODE);
//date
DB( g_print(" date\n") );
//5.8 date off means show all date
if( data->filter->option[FLT_GRP_DATE] == 0 )
{
data->filter->option[FLT_GRP_DATE] = 1;
data->filter->range = FLT_RANGE_MISC_ALLDATE;
}
flt->range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
flt->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
flt->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
//type
DB( g_print(" type\n") );
flt->typ_nexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_typnexp));
flt->typ_ninc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_typninc));
flt->typ_xexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_typxexp));
flt->typ_xinc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_typxinc));
//status
DB( g_print(" status\n") );
flt->sta_non = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_stanon));
flt->sta_clr = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_staclr));
flt->sta_rec = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_starec));
flt->forceadd = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forceadd));
flt->forcechg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forcechg));
flt->forceremind = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forceremind));
flt->forcevoid = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_forcevoid));
//paymode
DB( g_print(" paymode\n") );
for(i=0;iCM_paymode[i] ))
continue;
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(data->CM_paymode[i]), "uid"));
flt->paymode[uid] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]));
}
//amount
flt->minamount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_minamount));
flt->maxamount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_maxamount));
//5.8 we compute new checksum here to detect changes
newdigest = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *)flt, length);
DB( g_print(" checksum: '%s'\n", olddigest) );
DB( g_print(" checksum: '%s'\n", newdigest) );
if (strcmp(olddigest, newdigest) )
{
flt->nbchanges++;
DB( g_print(" > checksum differs\n") );
}
DB( g_print(" changes: %d (post checksum)\n", flt->nbchanges) );
g_free (olddigest);
g_free (newdigest);
// data below need to detect/count change on their own
//text:memo
//text:info
flt->nbchanges += _gtkentry_to_filter(GTK_ENTRY(data->ST_memo), &flt->memo);
flt->nbchanges += _gtkentry_to_filter(GTK_ENTRY(data->ST_number), &flt->number);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_exact));
if( flt->exact != active )
{
flt->nbchanges++;
flt->exact = active;
}
DB( g_print(" changes: %d (post memo/info)\n", flt->nbchanges) );
// account
if(data->show_account == TRUE)
{
flt->nbchanges += ui_acc_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_acc), flt);
DB( g_print(" changes: %d (post acc)\n", flt->nbchanges) );
}
// payee
flt->nbchanges += ui_pay_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_pay), flt);
DB( g_print(" changes: %d (post pay)\n", flt->nbchanges) );
// category
flt->nbchanges += ui_cat_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_cat), flt);
DB( g_print(" changes: %d (post cat)\n", flt->nbchanges) );
// tag
flt->nbchanges += ui_tag_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_tag), flt);
DB( g_print(" changes: %d (post tag)\n", flt->nbchanges) );
// active tab
g_strlcpy(flt->last_tab, gtk_stack_get_visible_child_name(GTK_STACK(data->stack)), 8);
DB( g_print(" page is '%s'\n", flt->last_tab) );
}
}
static void ui_flt_manage_set_option(struct ui_flt_manage_data *data, gint index)
{
//option: 0=off, 1=include, 2=exclude
gint tmpoption = data->filter->option[index];
gtk_switch_set_active(GTK_SWITCH(data->SW_enabled[index]), tmpoption);
if( tmpoption == 2 )
{
hbtk_switcher_set_active (HBTK_SWITCHER(data->RA_matchmode[index]), 1);
}
}
static void ui_flt_manage_set(struct ui_flt_manage_data *data)
{
Filter *flt = data->filter;
DB( g_print("\n[ui_flt_manage] set\n") );
if(flt != NULL)
{
gint i;
DB( g_print(" options\n") );
ui_flt_manage_set_option(data, FLT_GRP_DATE);
ui_flt_manage_set_option(data, FLT_GRP_TYPE);
ui_flt_manage_set_option(data, FLT_GRP_STATUS);
ui_flt_manage_set_option(data, FLT_GRP_PAYEE);
ui_flt_manage_set_option(data, FLT_GRP_CATEGORY);
ui_flt_manage_set_option(data, FLT_GRP_TAG);
if(data->show_account == TRUE)
ui_flt_manage_set_option(data, FLT_GRP_ACCOUNT);
ui_flt_manage_set_option(data, FLT_GRP_TEXT);
ui_flt_manage_set_option(data, FLT_GRP_AMOUNT);
ui_flt_manage_set_option(data, FLT_GRP_PAYMODE);
//DB( g_print(" setdate %d to %x\n", flt->mindate, data->PO_mindate) );
//DB( g_print(" setdate %d to %x\n", 0, data->PO_mindate) );
//date
DB( g_print(" date\n") );
//g_signal_handler_block(data->CY_range, data->handler_id[HID_REPDIST_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), flt->range);
//g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPDIST_RANGE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), flt->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), flt->maxdate);
//type
DB( g_print(" type\n") );
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_typnexp), flt->typ_nexp);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_typninc), flt->typ_ninc);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_typxexp), flt->typ_xexp);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_typxinc), flt->typ_xinc);
//status
DB( g_print(" status/type\n") );
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_stanon), flt->sta_non);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_staclr), flt->sta_clr);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_starec), flt->sta_rec);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forceadd), flt->forceadd);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forcechg), flt->forcechg);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forceremind), flt->forceremind);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_forcevoid), flt->forcevoid);
//paymode
DB( g_print(" paymode\n") );
for(i=0;iCM_paymode[i] ))
continue;
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(data->CM_paymode[i]), "uid"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]), flt->paymode[uid]);
}
//amount
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_minamount), flt->minamount);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_maxamount), flt->maxamount);
//text
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_exact), flt->exact);
gtk_entry_set_text(GTK_ENTRY(data->ST_number), (flt->number != NULL) ? flt->number : "");
gtk_entry_set_text(GTK_ENTRY(data->ST_memo), (flt->memo != NULL) ? flt->memo : "");
//account
if(data->show_account == TRUE)
{
DB( g_print(" account\n") );
ui_flt_hub_account_set(flt, data);
}
// payee
ui_flt_hub_payee_set(flt, data);
// category
ui_flt_hub_category_set(flt, data);
// tag
ui_flt_hub_tag_set(flt, data);
}
}
static void ui_flt_manage_setup(struct ui_flt_manage_data *data)
{
DB( g_print("\n[ui_flt_manage] setup\n") );
if(data->show_account == TRUE && data->LV_acc != NULL)
{
gint insert_type = data->txnmode ? ACC_LST_INSERT_NORMAL : ACC_LST_INSERT_REPORT;
//gtk_tree_selection_set_mode(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc))), GTK_SELECTION_MULTIPLE);
ui_acc_listview_populate(data->LV_acc, insert_type, NULL);
//populate_view_acc(data->LV_acc, GLOBALS->acc_list, FALSE);
}
if(data->LV_pay)
{
//gtk_tree_selection_set_mode(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_pay))), GTK_SELECTION_MULTIPLE);
ui_pay_listview_populate(data->LV_pay, NULL, TRUE);
//populate_view_pay(data->LV_pay, GLOBALS->pay_list, FALSE);
}
if(data->LV_cat)
{
//gtk_tree_selection_set_mode(GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat))), GTK_SELECTION_MULTIPLE);
//populate_view_cat(data->LV_cat, GLOBALS->cat_list, FALSE);
ui_cat_listview_populate(data->LV_cat, CAT_TYPE_ALL, NULL, TRUE);
gtk_tree_view_expand_all (GTK_TREE_VIEW(data->LV_cat));
}
if(data->LV_tag)
{
ui_tag_listview_populate(data->LV_tag, 0);
}
}
static gboolean
ui_flt_page_paymode_activate_link (GtkWidget *label,
const gchar *uri,
gpointer user_data)
{
struct ui_flt_manage_data *data;
gint i;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(label, GTK_TYPE_WINDOW)), "inst_data");
for(i=0;iCM_paymode[i] ))
continue;
if (g_strcmp0 (uri, "all") == 0)
{
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]), TRUE);
}
else
if (g_strcmp0 (uri, "non") == 0)
{
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]), FALSE);
}
else
if (g_strcmp0 (uri, "inv") == 0)
{
gboolean act = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_paymode[i]), !act);
}
}
return TRUE;
}
static void
_checkdate_valid(struct ui_flt_manage_data *data)
{
gboolean valid = (data->filter->mindate <= data->filter->maxdate) ? TRUE : FALSE;
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), !valid);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), !valid);
//disable use if invalid date
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), GTK_RESPONSE_ACCEPT, valid);
}
static void
ui_flt_manage_cb_date_change(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_manage_data *data;
DB( g_print("\n[ui_flt_manage] min/max/date change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
g_signal_handlers_block_by_func(data->CY_range, G_CALLBACK(ui_flt_manage_cb_range_change), NULL);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handlers_unblock_by_func(data->CY_range, G_CALLBACK(ui_flt_manage_cb_range_change), NULL);
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
//5.8 check for error
_checkdate_valid(data);
}
static void
ui_flt_manage_cb_range_change(GtkWidget *widget, gpointer user_data)
{
struct ui_flt_manage_data *data;
gint range;
//DB( g_print("\n[repdist] range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, 0);
g_signal_handlers_block_by_func(data->PO_mindate, G_CALLBACK(ui_flt_manage_cb_date_change), NULL);
g_signal_handlers_block_by_func(data->PO_maxdate, G_CALLBACK(ui_flt_manage_cb_date_change), NULL);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
g_signal_handlers_unblock_by_func(data->PO_mindate, G_CALLBACK(ui_flt_manage_cb_date_change), NULL);
g_signal_handlers_unblock_by_func(data->PO_maxdate, G_CALLBACK(ui_flt_manage_cb_date_change), NULL);
//#2046032 set min/max date for both widget
_checkdate_valid(data);
}
ui_flt_manage_update(widget, NULL);
}
/* = = = = = = = = = = = = = = = = */
static GtkWidget *ui_flt_page_misc_header(gchar *title, gint index, struct ui_flt_manage_data *data)
{
GtkWidget *grid, *label, *widget;
gint row;
//header
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
row = 0;
label = make_label_group(title);
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 2, 1);
row++;
widget = gtk_switch_new();
//gtk_widget_set_halign(widget, GTK_ALIGN_START);
data->SW_enabled[index] = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 1, row, 1, 1);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), RA_FILTER_MODE, FALSE);
data->RA_matchmode[index] = widget;
//gtk_widget_set_halign(widget, GTK_ALIGN_CENTER);
gtk_grid_attach (GTK_GRID (grid), widget, 2, row, 1, 1);
return grid;
}
static GtkWidget *ui_flt_page_widget_toolbar(struct ui_flt_list_data *data)
{
GtkWidget *treebox, *label;
treebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_widget_set_margin_bottom(treebox, SPACING_SMALL);
label = make_label (_("Select:"), 0, 0.5);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("all", _("All"));
data->bt_all = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("non", _("None"));
data->bt_non = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("inv", _("Invert"));
data->bt_inv = label;
gtk_box_prepend (GTK_BOX (treebox), label);
return treebox;
}
static GtkWidget *ui_flt_page_list_generic (gchar *title, GtkWidget *treeview, struct ui_flt_list_data *data)
{
GtkWidget *vbox, *treebox, *scrollwin;
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
data->gr_criteria = vbox;
treebox = ui_flt_page_widget_toolbar(data);
gtk_box_prepend (GTK_BOX (vbox), treebox);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(treeview), GTK_TREE_VIEW_GRID_LINES_NONE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
return vbox;
}
static GtkWidget *ui_flt_page_account (struct ui_flt_manage_data *data)
{
struct ui_flt_list_data tmp;
GtkWidget *part, *grid;
GtkWidget *treeview;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Account"), FLT_GRP_ACCOUNT, data);
gtk_box_prepend (GTK_BOX (part), grid);
treeview = ui_acc_listview_new(TRUE);
grid = ui_flt_page_list_generic(_("Account"), treeview, &tmp);
hbtk_box_prepend (GTK_BOX (part), grid);
//data->SW_enabled[FLT_GRP_ACCOUNT] = tmp.sw_enabled;
data->GR_page[FLT_GRP_ACCOUNT] = tmp.gr_criteria;
//data->RA_matchmode[FLT_GRP_ACCOUNT] = tmp.ra_mode;
data->LV_acc = treeview;
g_signal_connect (tmp.bt_all, "activate-link", G_CALLBACK (ui_flt_hub_account_activate_link), treeview);
g_signal_connect (tmp.bt_non, "activate-link", G_CALLBACK (ui_flt_hub_account_activate_link), treeview);
g_signal_connect (tmp.bt_inv, "activate-link", G_CALLBACK (ui_flt_hub_account_activate_link), treeview);
return(part);
}
static GtkWidget *ui_flt_page_category (struct ui_flt_manage_data *data)
{
struct ui_flt_list_data tmp;
GtkWidget *part, *grid, *widget, *tbar, *bbox;
GtkWidget *treeview;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Category"), FLT_GRP_CATEGORY, data);
gtk_box_prepend (GTK_BOX (part), grid);
treeview = ui_cat_listview_new(TRUE, FALSE);
grid = ui_flt_page_list_generic(_("Category"), treeview, &tmp);
hbtk_box_prepend (GTK_BOX (part), grid);
//data->SW_enabled[FLT_GRP_CATEGORY] = tmp.sw_enabled;
data->GR_page[FLT_GRP_CATEGORY] = tmp.gr_criteria;
//data->RA_matchmode[FLT_GRP_CATEGORY] = tmp.ra_mode;
data->LV_cat = treeview;
g_signal_connect (tmp.bt_all, "activate-link", G_CALLBACK (ui_flt_hub_category_activate_link), treeview);
g_signal_connect (tmp.bt_non, "activate-link", G_CALLBACK (ui_flt_hub_category_activate_link), treeview);
g_signal_connect (tmp.bt_inv, "activate-link", G_CALLBACK (ui_flt_hub_category_activate_link), treeview);
// expand/colapse
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (grid), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
g_signal_connect (G_OBJECT (data->BT_expand), "clicked", G_CALLBACK (ui_flt_hub_category_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapse), "clicked", G_CALLBACK (ui_flt_hub_category_collapse_all), NULL);
return(part);
}
static GtkWidget *ui_flt_page_payee (struct ui_flt_manage_data *data)
{
struct ui_flt_list_data tmp;
GtkWidget *part, *grid;
GtkWidget *treeview;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Payee"), FLT_GRP_PAYEE, data);
gtk_box_prepend (GTK_BOX (part), grid);
treeview = ui_pay_listview_new(TRUE, FALSE);
grid = ui_flt_page_list_generic(_("Payee"), treeview, &tmp);
hbtk_box_prepend (GTK_BOX (part), grid);
//data->SW_enabled[FLT_GRP_PAYEE] = tmp.sw_enabled;
data->GR_page[FLT_GRP_PAYEE] = tmp.gr_criteria;
//data->RA_matchmode[FLT_GRP_PAYEE] = tmp.ra_mode;
data->LV_pay = treeview;
g_signal_connect (tmp.bt_all, "activate-link", G_CALLBACK (ui_flt_hub_payee_activate_link), treeview);
g_signal_connect (tmp.bt_non, "activate-link", G_CALLBACK (ui_flt_hub_payee_activate_link), treeview);
g_signal_connect (tmp.bt_inv, "activate-link", G_CALLBACK (ui_flt_hub_payee_activate_link), treeview);
return part;
}
static GtkWidget *ui_flt_page_tag (struct ui_flt_manage_data *data)
{
struct ui_flt_list_data tmp;
GtkWidget *part, *grid;
GtkWidget *treeview;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Tag"), FLT_GRP_TAG, data);
gtk_box_prepend (GTK_BOX (part), grid);
treeview = ui_tag_listview_new(TRUE, FALSE);
grid = ui_flt_page_list_generic(_("Tag"), treeview, &tmp);
hbtk_box_prepend (GTK_BOX (part), grid);
//data->SW_enabled[FLT_GRP_TAG] = tmp.sw_enabled;
data->GR_page[FLT_GRP_TAG] = tmp.gr_criteria;
//data->RA_matchmode[FLT_GRP_TAG] = tmp.ra_mode;
data->LV_tag = treeview;
g_signal_connect (tmp.bt_all, "activate-link", G_CALLBACK (ui_flt_hub_tag_activate_link), treeview);
g_signal_connect (tmp.bt_non, "activate-link", G_CALLBACK (ui_flt_hub_tag_activate_link), treeview);
g_signal_connect (tmp.bt_inv, "activate-link", G_CALLBACK (ui_flt_hub_tag_activate_link), treeview);
return part;
}
static GtkWidget *ui_flt_page_date(struct ui_flt_manage_data *data)
{
GtkWidget *part, *grid, *label, *widget;
gint row;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Date"), FLT_GRP_DATE, data);
gtk_box_prepend (GTK_BOX (part), grid);
// criteria
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
data->GR_page[FLT_GRP_DATE] = grid;
gtk_box_prepend (GTK_BOX (part), grid);
row = 0;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_NONE);
gtk_grid_attach (GTK_GRID (grid), data->CY_range, 1, row, 1, 1);
gtk_widget_set_margin_bottom(label, SPACING_MEDIUM);
gtk_widget_set_margin_bottom(data->CY_range, SPACING_MEDIUM);
row++;
label = make_label_widget(_("_From:"));
data->LB_mindate = label;
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
widget = gtk_date_entry_new(label);
data->PO_mindate = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 1, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
data->LB_maxdate = label;
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
widget = gtk_date_entry_new(label);
data->PO_maxdate = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 1, row, 1, 1);
return part;
}
static GtkWidget *ui_flt_page_amounttext(struct ui_flt_manage_data *data)
{
GtkWidget *part, *grid, *hbox, *widget, *label;
gint row;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
// header
grid = ui_flt_page_misc_header(_("Amount"), FLT_GRP_AMOUNT, data);
gtk_box_prepend (GTK_BOX (part), grid);
// criteria
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
data->GR_page[FLT_GRP_AMOUNT] = grid;
gtk_box_prepend (GTK_BOX (part), grid);
row = 0;
label = make_label_widget(_("_From:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
data->ST_minamount = make_amount(label);
gtk_grid_attach (GTK_GRID (grid), data->ST_minamount, 1, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
data->ST_maxamount = make_amount(label);
gtk_grid_attach (GTK_GRID (grid), data->ST_maxamount, 1, row, 1, 1);
//#2051758 idiot-proof input help
row++;
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
//gtk_widget_set_hexpand (hbox, TRUE);
gtk_grid_attach (GTK_GRID (grid), hbox, 1, row, 2, 1);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label_widget(_("Input From -30 To -15 to filter on expense"));
gtk_box_prepend (GTK_BOX (hbox), label);
// header
grid = ui_flt_page_misc_header(_("Text"), FLT_GRP_TEXT, data);
gtk_box_prepend (GTK_BOX (part), grid);
// criteria
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
data->GR_page[FLT_GRP_TEXT] = grid;
gtk_box_prepend (GTK_BOX (part), grid);
row = 0;
label = make_label_widget(_("_Memo:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
data->ST_memo = make_string(label);
gtk_widget_set_hexpand (data->ST_memo, TRUE);
gtk_grid_attach (GTK_GRID (grid), data->ST_memo, 1, row, 1, 1);
row++;
label = make_label_widget(_("_Number:"));
//----------------------------------------- l, r, t, b
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
data->ST_number = make_string(label);
gtk_widget_set_hexpand (data->ST_number, TRUE);
gtk_grid_attach (GTK_GRID (grid), data->ST_number, 1, row, 1, 1);
row++;
data->CM_exact = gtk_check_button_new_with_mnemonic (_("Case _sensitive"));
gtk_grid_attach (GTK_GRID (grid), data->CM_exact, 1, row, 1, 1);
return part;
}
static GtkWidget *ui_flt_page_payment(struct ui_flt_manage_data *data)
{
struct ui_flt_list_data tmp;
GtkWidget *part, *grid, *widget, *image, *vbox2, *tbar;
HbKivData *tmpkv, *kvdata = CYA_TXN_PAYMODE;
gint i, row;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
// header
grid = ui_flt_page_misc_header(_("Payment"), FLT_GRP_PAYMODE, data);
gtk_box_prepend (GTK_BOX (part), grid);
vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
data->GR_page[FLT_GRP_PAYMODE] = vbox2;
gtk_box_prepend (GTK_BOX (part), vbox2);
tbar = ui_flt_page_widget_toolbar(&tmp);
hbtk_box_prepend (GTK_BOX (vbox2), tbar);
GtkWidget *frame = gtk_frame_new(NULL);
gtk_box_prepend (GTK_BOX (vbox2), frame);
grid = gtk_grid_new ();
//gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_frame_set_child(GTK_FRAME(frame), grid);
hb_widget_set_margin(grid, 4);
gtk_style_context_add_class (gtk_widget_get_style_context (frame), GTK_STYLE_CLASS_VIEW);
for(i=0;iname == NULL )
break;
row = i;
image = hbtk_image_new_from_icon_name_16( tmpkv->iconname);
gtk_grid_attach (GTK_GRID (grid), image, 0, row, 1, 1);
widget = gtk_check_button_new_with_mnemonic(_(tmpkv->name));
data->CM_paymode[i] = widget;
g_object_set_data(G_OBJECT(widget), "uid", GUINT_TO_POINTER(tmpkv->key));
gtk_grid_attach (GTK_GRID (grid), data->CM_paymode[i], 1, row, 1, 1);
}
g_signal_connect (tmp.bt_all, "activate-link", G_CALLBACK (ui_flt_page_paymode_activate_link), NULL);
g_signal_connect (tmp.bt_non, "activate-link", G_CALLBACK (ui_flt_page_paymode_activate_link), NULL);
g_signal_connect (tmp.bt_inv, "activate-link", G_CALLBACK (ui_flt_page_paymode_activate_link), NULL);
return part;
}
static GtkWidget *ui_flt_page_type(struct ui_flt_manage_data *data)
{
GtkWidget *part, *grid, *widget;
gint row;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
//header
grid = ui_flt_page_misc_header(_("Type"), FLT_GRP_TYPE, data);
gtk_box_prepend (GTK_BOX (part), grid);
// criteria
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
data->GR_page[FLT_GRP_TYPE] = grid;
gtk_box_prepend (GTK_BOX (part), grid);
row = 0;
widget = gtk_toggle_button_new_with_label(_("Expense"));
data->CM_typnexp = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
row++;
widget = gtk_toggle_button_new_with_label(_("Income"));
data->CM_typninc = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
row++;
widget = gtk_toggle_button_new_with_label(_("Expense Transfer"));
data->CM_typxexp = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
row++;
widget = gtk_toggle_button_new_with_label(_("Income Transfer"));
data->CM_typxinc = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
return part;
}
static GtkWidget *ui_flt_page_status(struct ui_flt_manage_data *data)
{
GtkWidget *part, *grid, *widget;
gint row;
part = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_LARGE);
// header
grid = ui_flt_page_misc_header(_("Status"), FLT_GRP_STATUS, data);
gtk_box_prepend (GTK_BOX (part), grid);
// criteria
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
data->GR_page[FLT_GRP_STATUS] = grid;
gtk_box_prepend (GTK_BOX (part), grid);
row = 0;;
widget = gtk_toggle_button_new_with_label(_("None"));
data->CM_stanon = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
row++;
widget = gtk_toggle_button_new_with_label(_("Cleared"));
data->CM_staclr = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
row++;
widget = gtk_toggle_button_new_with_label(_("Reconciled"));
data->CM_starec = widget;
gtk_grid_attach (GTK_GRID (grid), widget, 0, row, 1, 1);
return part;
}
gint ui_flt_manage_dialog_new(GtkWindow *parentwindow, Filter *filter, gboolean show_account, gboolean txnmode)
{
struct ui_flt_manage_data *data;
GtkWidget *dialog, *content, *mainbox, *sidebar, *stack, *grid, *page, *label, *widget;
gchar *wintitle;
gint w, h, dw, dh;
DB( g_print("\n\n------------------------\n") );
DB( g_print("\n[ui-filter] new\n") );
data = g_malloc0(sizeof(struct ui_flt_manage_data));
DB( g_print(" key;%d name: '%s'\n", filter->key, filter->name) );
DB( g_print(" show_account:%d, txnmode:%d\n", show_account, txnmode) );
data->filter = filter;
data->saveable = filter->key > 0 ? TRUE : FALSE;
data->show_account = show_account;
data->txnmode = txnmode;
data->dialog = dialog = gtk_dialog_new_with_buttons (NULL,
GTK_WINDOW (parentwindow),
0, //no flags
NULL, //no buttons
NULL);
if(!txnmode)
{
widget = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Reset"), HB_RESPONSE_FLT_RESET);
gtk_widget_set_margin_end(widget, SPACING_LARGE);
}
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Cancel"), GTK_RESPONSE_REJECT);
if( data->saveable )
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Save & Use"), HB_RESPONSE_FLT_SAVE_USE);
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Use"), GTK_RESPONSE_ACCEPT);
//window title
wintitle = g_strdup_printf("%s - %s", _("Edit filter"), data->saveable ? filter->name : _("default") );
gtk_window_set_title(GTK_WINDOW(dialog), wintitle );
g_free(wintitle);
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(parentwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 2:3
dw = (dh * 2) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, -1);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" - window=%p, inst_data=%p\n", dialog, data) );
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hbtk_box_prepend (GTK_BOX (content), mainbox);
sidebar = gtk_stack_sidebar_new ();
gtk_widget_set_margin_bottom(sidebar, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), sidebar);
stack = gtk_stack_new ();
gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
//gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE);
gtk_stack_sidebar_set_stack (GTK_STACK_SIDEBAR (sidebar), GTK_STACK (stack));
hb_widget_set_margin(GTK_WIDGET(stack), SPACING_LARGE);
data->stack = stack;
hbtk_box_prepend (GTK_BOX (mainbox), stack);
//TODO: needs to keep this until we enable from/to into ledger
page = ui_flt_page_date(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_DAT, _("Date"));
page = ui_flt_page_type(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_TYP, _("Type"));
page = ui_flt_page_status(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_STA, _("Status"));
if(show_account == TRUE)
{
page = ui_flt_page_account(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_ACC, _("Account"));
}
page = ui_flt_page_payee(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_PAY, _("Payee"));
page = ui_flt_page_category(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_CAT, _("Category"));
page = ui_flt_page_tag(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_TAG, _("Tag"));
page = ui_flt_page_payment(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_PMT, _("Payment"));
page = ui_flt_page_amounttext(data);
gtk_stack_add_titled (GTK_STACK (stack), page, FLT_PAGE_NAME_TXT, _("Amount/Text"));
//#xxxxxxx
widget = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
gtk_widget_set_margin_top(widget, SPACING_LARGE);
gtk_widget_set_margin_bottom(widget, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (mainbox), widget);
// force display
grid = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
data->GR_force = grid;
hb_widget_set_margin(grid, SPACING_LARGE);
gtk_widget_set_valign(grid, GTK_ALIGN_END);
gtk_box_prepend (GTK_BOX (mainbox), grid);
label = make_label_group(_("Always show"));
gtk_box_prepend (GTK_BOX (grid), label);
widget = gtk_check_button_new_with_mnemonic (_("Remind"));
data->CM_forceremind = widget;
gtk_box_prepend (GTK_BOX (grid), widget);
widget = gtk_check_button_new_with_mnemonic (_("Void"));
data->CM_forcevoid = widget;
gtk_box_prepend (GTK_BOX (grid), widget);
widget = gtk_check_button_new_with_mnemonic (_("Added"));
data->CM_forceadd = widget;
gtk_box_prepend (GTK_BOX (grid), widget);
widget = gtk_check_button_new_with_mnemonic (_("Edited"));
data->CM_forcechg = widget;
gtk_box_prepend (GTK_BOX (grid), widget);
//setup, init and show window
ui_flt_manage_setup(data);
ui_flt_manage_set(data);
/* signal connect */
//g_signal_connect (data->SW_enabled[FLT_GRP_DATE] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_TYPE] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_STATUS] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_PAYEE] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_CATEGORY], "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_TAG] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
if(show_account == TRUE)
g_signal_connect (data->SW_enabled[FLT_GRP_ACCOUNT] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_TEXT] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_AMOUNT] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->SW_enabled[FLT_GRP_PAYMODE] , "notify::active", G_CALLBACK (ui_flt_manage_update), NULL);
g_signal_connect (data->CY_range , "changed", G_CALLBACK (ui_flt_manage_cb_range_change), NULL);
g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (ui_flt_manage_cb_date_change), NULL);
g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (ui_flt_manage_cb_date_change), NULL);
gtk_widget_show_all (dialog);
//gtk_widget_grab_focus(sidebar);
ui_flt_manage_update(dialog, NULL);
if(!txnmode)
{
hb_widget_visible (data->GR_force, FALSE);
}
if( *data->filter->last_tab != '\0' )
gtk_stack_set_visible_child_name (GTK_STACK(data->stack), data->filter->last_tab);
DB( g_print(" set page '%s'\n", data->filter->last_tab) );
//wait for the user
gint retval; // = 55;
//while( result == 55 )
//{
retval = gtk_dialog_run (GTK_DIALOG (dialog));
switch (retval)
{
case HB_RESPONSE_FLT_SAVE_USE:
case GTK_RESPONSE_ACCEPT:
//do_application_specific_something ();
ui_flt_manage_get(data);
da_flt_count_item(filter);
break;
//case 55: reset will be treated in calling window
}
//}
// cleanup and destroy
//ui_flt_manage_cleanup(&data, result);
DB( g_print(" destroy\n") );
gtk_window_destroy (GTK_WINDOW(dialog));
DB( g_print(" free\n") );
g_free(data);
DB( g_print(" end dialog filter all ok\n") );
return retval;
}
homebank-5.9.7/src/homebank.h 0000644 0001750 0001750 00000015056 15121556100 015341 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_H__
#define __HOMEBANK_H__
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include /* floor */
#include /* gettext */
#include
#include /* atoi, atof, atol */
#include /* memset, memcpy, strcasestr, strcmp, strcpy */
//#include
#include
#include
#include
#include "hb-types.h"
#include "enums.h"
#include "icon-names.h"
#include "hb-preferences.h"
#include "hb-account.h"
#include "hb-archive.h"
#include "hb-assign.h"
#include "hb-category.h"
#include "hb-filter.h"
#include "hb-payee.h"
#include "hb-tag.h"
#include "hb-transaction.h"
#include "hb-currency.h"
#include "hb-encoding.h"
#include "hb-export.h"
#include "hb-group.h"
#include "hb-hbfile.h"
#include "hb-import.h"
#include "hb-misc.h"
#include "hb-report.h"
#define _(str) gettext (str)
#define gettext_noop(str) (str)
#define N_(str) gettext_noop (str)
/* = = = = = = = = = = = = = = = = */
#define HB_PRIV_FUNC FALSE
#define HB_PRIV_FORCE_ENUS FALSE
#define HB_UNSTABLE FALSE
#define HB_UNSTABLE_SHOW FALSE //show user RC header
#define HOMEBANK_MAJOR 5
#define HOMEBANK_MINOR 9
#define HOMEBANK_MICRO 7
#define HB_VERSION "5.9.7"
#define HB_VERSION_NUM (HOMEBANK_MAJOR*10000) + (HOMEBANK_MINOR*100) + HOMEBANK_MICRO
#define FILE_VERSION 1.6
#if HB_UNSTABLE == FALSE
#define PROGNAME "HomeBank"
#define HB_DATA_PATH "homebank"
#else
#define PROGNAME "HomeBank " HB_VERSION " (unstable)"
#define HB_DATA_PATH "homebank_unstable"
#endif
#ifdef G_OS_WIN32
#define GETTEXT_PACKAGE "homebank"
#define LOCALE_DIR "locale"
#define PIXMAPS_DIR "images"
#define HELP_DIR "help"
#define PACKAGE_VERSION HB_VERSION
#define PACKAGE "homebank"
#define VERSION HB_VERSION
//#define PORTABLE_APP
//#define NOOFX
#define ENABLE_NLS 1
#endif
/* container spacing */
#define SPACING_TINY 3
#define SPACING_SMALL 6
#define SPACING_MEDIUM 12
#define SPACING_LARGE 18
#define SPACING_POPOVER 10
#define HB_DATE_MAX_GAP 7
// those 2 line are duplicated into dateentry
#define HB_MINDATE 693596 //01/01/1900
#define HB_MAXDATE 803533 //31/12/2200
/* widget minimum width */
#define HB_MINWIDTH_LIST 161
#define HB_MINHEIGHT_LIST 260
#define HB_MINWIDTH_SEARCH 240
#define HB_MINWIDTH_COLUMN 48
/* miscellaneous */
#define PHI 1.61803399
#define HB_NUMBER_SAMPLE 1234567.89
/* hbfile/account/import update flags */
enum
{
UF_TITLE = 1 << 0, //1
UF_SENSITIVE = 1 << 1, //2
UF_VISUAL = 1 << 2, //4
UF_REFRESHALL = 1 << 3, //8
UF_TXNLIST = 1 << 4, //16
// = 1 << 5
};
typedef enum
{
FILETYPE_UNKNOWN,
FILETYPE_HOMEBANK,
FILETYPE_OFX,
FILETYPE_QIF,
FILETYPE_CSV_HB,
// FILETYPE_AMIGA_HB,
NUM_FILETYPE
} HbFileType;
/* ---- icon size as defined into gtkiconfactory.c ---- */
/* GTK_ICON_SIZE_MENU 16
* GTK_ICON_SIZE_BUTTON 20
* GTK_ICON_SIZE_SMALL_TOOLBAR 18
* GTK_ICON_SIZE_LARGE_TOOLBAR 24 (default for toolbar)
* GTK_ICON_SIZE_DND 32
* GTK_ICON_SIZE_DIALOG 48
*/
/*
** Global application datas
*/
struct HomeBank
{
// hbfile storage
GHashTable *h_cur; //currencies
GHashTable *h_grp; //groups
GHashTable *h_acc; //accounts
GHashTable *h_pay; //payees
GHashTable *h_cat; //categories
GHashTable *h_rul; //assign rules
GHashTable *h_tag; //tags
GHashTable *h_flt; //filters
GtkListStore *fltmodel;
GHashTable *h_memo; //memo/description
GList *arc_list; //scheduled/template
//#1419304 we keep the deleted txn to a stack trash
//GTrashStack *txn_stk;
GSList *openwindows; //added 5.5.1
GSList *deltxn_list;
// hbfile (wallet saved properties)
gchar *owner;
gshort auto_smode;
gshort auto_weekday;
gshort auto_nbmonths;
gshort auto_nbdays;
gdouble lifen_earnbyh;
guint32 vehicle_category;
guint32 kcur; // base currency
// hbfile (unsaved properties)
guint changes_count;
gboolean hbfile_is_new;
gboolean hbfile_is_bak;
gchar *xhb_filepath;
gboolean xhb_hasrevert; //file has backup (*.xhb~) used for revert menu sensitivity
guint64 xhb_timemodified;
gboolean xhb_obsoletecurr;
// really global stuffs
gboolean first_run;
guint32 today; //today's date
gint define_off; //>0 when a stat, account window is opened
gboolean minor;
GtkApplication *application;
GtkWidget *mainwindow; //should be global to access attached window data
GtkCssProvider *provider;
GtkIconTheme *icontheme;
//GdkPixbuf *lst_pixbuf[NUM_LST_PIXBUF];
//gint lst_pixbuf_maxwidth;
GDBusProxy *settings_portal;
ColorScheme color_scheme;
gboolean theme_is_dark;
};
gchar *homebank_filepath_with_extention(gchar *path, gchar *extension);
gchar *homebank_filename_without_extention(gchar *path);
void homebank_file_ensure_xhb(gchar *filename);
void homebank_backup_current_file(void);
gboolean homebank_util_url_show (const gchar *url);
gchar *homebank_lastopenedfiles_load(void);
gboolean homebank_lastopenedfiles_save(void);
void homebank_window_set_icon_from_file(GtkWindow *window, gchar *filename);
const gchar *homebank_app_get_config_dir (void);
const gchar *homebank_app_get_images_dir (void);
const gchar *homebank_app_get_pixmaps_dir (void);
const gchar *homebank_app_get_locale_dir (void);
const gchar *homebank_app_get_help_dir (void);
const gchar *homebank_app_get_datas_dir (void);
guint32 homebank_app_date_get_julian(void);
GtkWindow *homebank_app_find_window(gint needle_key);
/* - - - - obsolete/future things - - - - */
/*
typedef struct _budget Budget;
struct _budget
{
guint key;
gushort flags;
guint cat_key;
guint year;
gdouble value[13];
};
*/
/*
struct _investment
{
guint date;
gdouble buy_amount;
gdouble curr_amount;
gdouble commission;
guint number;
guint account;
gchar *name;
gchar *symbol;
gchar *note;
};
*/
#endif
homebank-5.9.7/src/hb-misc.h 0000644 0001750 0001750 00000010257 15016603252 015101 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_MISC__H__
#define __HB_MISC__H__
//amount sign
enum {
HB_AMT_SIGN_OFF,
HB_AMT_SIGN_EXP,
HB_AMT_SIGN_INC
};
//date min/max bound
typedef enum {
HB_DATE_BOUND_FIRST,
HB_DATE_BOUND_LAST,
} HbDateBound;
double hb_amount_round(const double x, unsigned int n);
gdouble hb_amount_base(gdouble value, guint32 kcur);
gdouble hb_amount_convert(gdouble value, guint32 skcur, guint32 dkcur);
gdouble hb_amount_to_euro(gdouble amount);
gboolean hb_amount_type_match(gdouble amount, gint type);
gint hb_amount_cmp(gdouble val1, gdouble val2);
gboolean hb_amount_between(gdouble val, gdouble min, gdouble max);
gint hb_amount_forced_sign(const gchar *text);
gdouble hb_rate(gdouble value, gdouble total);
gchar *hb_str_rate(gchar *outstr, gint outlen, gdouble rate);
gchar *hb_str_formatd(gchar *outstr, gint outlen, gchar *buf1, Currency *cur, gboolean showsymbol);
void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor);
void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor);
void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor);
void hb_strfmongc(gchar *outstr, gint outlen, gdouble value);
void _format_decimal(GString *node, ToStringMode mode, gdouble value);
void hb_strlifeenergy(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor);
gint hb_filename_type_get_by_extension(gchar *filepath);
gchar *hb_filename_new_for_backup(gchar *filename);
GPtrArray *hb_filename_backup_list(gchar *filename);
gchar *hb_filename_backup_get_filtername(gchar *filename);
gchar *hb_filename_new_with_extension(gchar *filename, const gchar *extension);
gchar *get_normal_color_amount(gdouble value);
gchar *get_minimum_color_amount(gdouble value, gdouble minvalue);
void hb_label_set_amount(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor);
void hb_label_set_colvalue(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor);
//void get_period_minmax(guint month, guint year, guint32 *mindate, guint32 *maxdate);
//void get_range_minmax(guint32 refdate, gint range, guint32 *mindate, guint32 *maxdate);
gint hb_string_ascii_compare(gchar *s1, gchar *s2);
gint hb_string_compare(gchar *s1, gchar *s2);
gint hb_string_utf8_strstr(gchar *haystack, gchar *needle, gboolean exact);
gint hb_string_utf8_compare(gchar *s1, gchar *s2);
gchar *hb_string_dup_raw_amount_clean(const gchar *string, gint digits);
void hb_string_strip_utf8_bom(gchar *str);
void hb_string_strip_crlf(gchar *str);
gboolean hb_string_has_leading_trailing(gchar *str);
void hb_string_replace_char(gchar oc, gchar nc, gchar *str);
void hb_string_remove_char(gchar c, gchar *str);
gchar *hb_string_copy_jsonpair(gchar *dst, gchar *str);
void hb_string_inline(gchar *str);
gchar *hb_strdup_nobrackets (const gchar *str);
gchar *hb_sprint_date(gchar *outstr, guint32 julian);
guint32 hb_date_get_jbound(guint32 jdate, HbDateBound bound);
guint32 hb_date_get_julian(gchar *string, gint datefmt);
gboolean hb_string_isdate(gchar *str);
gboolean hb_string_isdigit(gchar *str);
gboolean hb_string_isprint(gchar *str);
void hb_print_date(guint32 jdate, gchar *label);
void hex_dump(gchar *ptr, guint length);
#if( (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 68) )
guint g_string_replace (GString *string,
const gchar *find,
const gchar *replace,
guint limit);
#endif
#endif
homebank-5.9.7/src/hb-hbfile.h 0000644 0001750 0001750 00000003045 14736461415 015407 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_HBFILE_H__
#define __HB_HBFILE_H__
GQueue *hbfile_transaction_get_partial(guint32 minjulian, guint32 maxjulian);
GQueue *hbfile_transaction_get_partial_budget(guint32 minjulian, guint32 maxjulian);
gboolean hbfile_file_isbackup(gchar *filepath);
gboolean hbfile_file_hasrevert(gchar *filepath);
guint64 hbfile_file_get_time_modified(gchar *filepath);
void hbfile_file_default(void);
void hbfile_change_owner(gchar *owner);
void hbfile_change_filepath(gchar *filepath);
void hbfile_cleanup(gboolean file_clear);
void hbfile_setup(gboolean file_clear);
void hbfile_sanity_tags(void);
void hbfile_sanity_check(void);
void hbfile_anonymize(void);
void hbfile_change_basecurrency(guint32 key);
void hbfile_replace_basecurrency(Currency4217 *curfmt);
#endif
homebank-5.9.7/src/hb-import-ofx.c 0000664 0001750 0001750 00000026722 14736461407 016267 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-import.h"
#ifndef NOOFX
#include
#endif
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
#ifndef NOOFX
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* ofx_proc_account_cb:
*
* The ofx_proc_account_cb event is always generated first, to allow the application to create accounts
* or ask the user to match an existing account before the ofx_proc_statement and ofx_proc_transaction
* event are received. An OfxAccountData is passed to this event.
*
*/
static LibofxProcStatementCallback
ofx_proc_account_cb(const struct OfxAccountData data, ImportContext *ctx)
{
GenAcc *genacc;
Account *dst_acc;
DB( g_print("** ofx_proc_account_cb()\n") );
if(data.account_id_valid==true)
{
DB( g_print(" account_id: %s\n", data.account_id) );
DB( g_print(" account_name: %s\n", data.account_name) );
}
//if(data.account_number_valid==true)
//{
DB( g_print(" account_number: %s\n", data.account_number) );
//}
if(data.account_type_valid==true)
{
DB( g_print(" account_type: %d\n", data.account_type) );
/*
enum:
OFX_CHECKING A standard checking account
OFX_SAVINGS A standard savings account
OFX_MONEYMRKT A money market account
OFX_CREDITLINE A line of credit
OFX_CMA Cash Management Account
OFX_CREDITCARD A credit card account
OFX_INVESTMENT An investment account
*/
}
if(data.currency_valid==true)
{
DB( g_print(" currency: %s\n", data.currency) );
}
//todo: normally should check for validity here
// in every case we create an account here
DB( g_print(" -> create generic account: '%s':'%s'\n", data.account_id, data.account_name) );
genacc = hb_import_gen_acc_get_next (ctx, FILETYPE_OFX, (gchar *)data.account_name, (gchar *)data.account_id);
ctx->curr_acc_isnew = TRUE;
dst_acc = hb_import_acc_find_existing((gchar *)data.account_name, (gchar *)data.account_id );
if( dst_acc != NULL )
{
genacc->kacc = dst_acc->key;
ctx->curr_acc_isnew = FALSE;
if(dst_acc->type == ACC_TYPE_CREDITCARD)
genacc->is_ccard = TRUE;
}
ctx->curr_acc = genacc;
DB( fputs("\n",stdout) );
return 0;
}
/**
* ofx_proc_statement_cb:
*
* The ofx_proc_statement_cb event is sent after all ofx_proc_transaction events have been sent.
* An OfxStatementData is passed to this event.
*
*/
static LibofxProcStatementCallback
ofx_proc_statement_cb(const struct OfxStatementData data, ImportContext *ctx)
{
DB( g_print("** ofx_proc_statement_cb()\n") );
#if MYDEBUG == 1
if(data.ledger_balance_date_valid==true)
{
struct tm temp_tm;
temp_tm = *localtime(&(data.ledger_balance_date));
g_print("ledger_balance_date : %d%s%d%s%d%s", temp_tm.tm_mday, "/", temp_tm.tm_mon+1, "/", temp_tm.tm_year+1900, "\n");
}
#endif
if(data.ledger_balance_valid==true)
{
if( ctx->curr_acc != NULL && ctx->curr_acc_isnew == TRUE )
{
ctx->curr_acc->initial = data.ledger_balance;
}
DB( g_print("ledger_balance: $%.2f%s",data.ledger_balance,"\n") );
}
return 0;
}
/**
* ofx_proc_statement_cb:
*
* An ofx_proc_transaction_cb event is generated for every transaction in the ofx response,
* after ofx_proc_statement (and possibly ofx_proc_security is generated.
* An OfxTransactionData structure is passed to this event.
*
*/
static LibofxProcStatementCallback
ofx_proc_transaction_cb(const struct OfxTransactionData data, ImportContext *ctx)
{
struct tm *temp_tm;
GDate date;
GenTxn *gentxn;
guint row = 0;
DB( g_print("** ofx_proc_transaction_cb()\n") );
gentxn = da_gen_txn_malloc();
// date
gentxn->julian = 0;
if(data.date_posted_valid && (data.date_posted != 0))
{
temp_tm = localtime(&data.date_posted);
if( temp_tm != 0)
{
g_date_set_dmy(&date, temp_tm->tm_mday, temp_tm->tm_mon+1, temp_tm->tm_year+1900);
gentxn->julian = g_date_get_julian(&date);
}
}
else if (data.date_initiated_valid && (data.date_initiated != 0))
{
temp_tm = localtime(&data.date_initiated);
if( temp_tm != 0)
{
g_date_set_dmy(&date, temp_tm->tm_mday, temp_tm->tm_mon+1, temp_tm->tm_year+1900);
gentxn->julian = g_date_get_julian(&date);
}
}
// amount
if(data.amount_valid==true)
{
gentxn->amount = data.amount;
}
// 5.5.1 add fitid
if(data.fi_id_valid==true)
{
gentxn->fitid = g_strdup(data.fi_id);
}
// check number :: The check number is most likely an integer and can probably be converted properly with atoi().
//However the spec allows for up to 12 digits, so it is not garanteed to work
if(data.check_number_valid==true)
{
gentxn->rawnumber = g_strdup(data.check_number);
}
//todo: reference_number ?Might present in addition to or instead of a check_number. Not necessarily a number
// ofx:name = Can be the name of the payee or the description of the transaction
if(data.name_valid==true)
{
gentxn->rawpayee = g_strdup(data.name);
}
//memo ( new for v4.2) #319202 Extra information not included in name
DB( g_print(" -> memo is='%d'\n", data.memo_valid) );
if(data.memo_valid==true)
{
gentxn->rawmemo = g_strdup(data.memo);
}
// payment
if(data.transactiontype_valid==true)
{
switch(data.transactiontype)
{
//#740373
case OFX_CREDIT:
if(gentxn->amount < 0)
gentxn->amount *= -1;
break;
case OFX_DEBIT:
if(gentxn->amount > 0)
gentxn->amount *= -1;
break;
case OFX_INT:
gentxn->paymode = PAYMODE_XFER;
break;
case OFX_DIV:
gentxn->paymode = PAYMODE_XFER;
break;
case OFX_FEE:
gentxn->paymode = PAYMODE_FEE;
break;
case OFX_SRVCHG:
gentxn->paymode = PAYMODE_XFER;
break;
case OFX_DEP:
gentxn->paymode = PAYMODE_DEPOSIT;
break;
case OFX_ATM:
gentxn->paymode = PAYMODE_CASH;
break;
case OFX_POS:
if(ctx->curr_acc && ctx->curr_acc->is_ccard == TRUE)
gentxn->paymode = PAYMODE_CCARD;
else
gentxn->paymode = PAYMODE_DCARD;
break;
case OFX_XFER:
gentxn->paymode = PAYMODE_XFER;
break;
case OFX_CHECK:
gentxn->paymode = PAYMODE_CHECK;
break;
case OFX_PAYMENT:
gentxn->paymode = PAYMODE_EPAYMENT;
break;
case OFX_CASH:
gentxn->paymode = PAYMODE_CASH;
break;
case OFX_DIRECTDEP:
gentxn->paymode = PAYMODE_DEPOSIT;
break;
case OFX_DIRECTDEBIT:
//1854953: directdebit not adding in 4.6
//gentxn->paymode = PAYMODE_XFER;
gentxn->paymode = PAYMODE_DIRECTDEBIT;
break;
case OFX_REPEATPMT:
gentxn->paymode = PAYMODE_REPEATPMT;
break;
case OFX_OTHER:
break;
default :
break;
}
}
if( ctx->curr_acc )
{
//5.8 #2063416 same date txn
gentxn->row = row++;
gentxn->account = g_strdup(ctx->curr_acc->name);
#if MYDEBUG == 1
if(gentxn->rawnumber)
g_print(" len number %d %ld\n", (int)strlen(gentxn->rawnumber) , g_utf8_strlen(gentxn->rawnumber, -1));
if(gentxn->rawmemo)
g_print(" len memo %d %ld\n", (int)strlen(gentxn->rawmemo) , g_utf8_strlen(gentxn->rawmemo, -1));
if(gentxn->rawpayee)
g_print(" len name %d %ld\n", (int)strlen(gentxn->rawpayee), g_utf8_strlen(gentxn->rawpayee, -1));
#endif
//#1842935 workaround for libofx truncate bug that can leave invalid UTF-8 string
//NAME = A-32 (96 allowed)
#if( (GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION >= 52) )
if( gentxn->rawpayee && g_utf8_strlen(gentxn->rawpayee, -1) > 32 )
{
gchar *oldtxt = gentxn->rawpayee;
DB( g_print(" ensure UTF-8 for truncated NAME='%s'\n", oldtxt) );
gentxn->rawpayee = g_utf8_make_valid(oldtxt, -1);
g_free(oldtxt);
}
#endif
//TODO: maybe MEMO = A-255
/* ensure utf-8 here, has under windows, libofx not always return utf-8 as it should */
#ifndef G_OS_UNIX
DB( g_print(" ensure UTF-8\n") );
gentxn->rawnumber = homebank_utf8_ensure(gentxn->rawnumber);
gentxn->rawmemo = homebank_utf8_ensure(gentxn->rawmemo);
gentxn->rawpayee = homebank_utf8_ensure(gentxn->rawpayee);
#endif
da_gen_txn_append(ctx, gentxn);
DB( g_print(" insert gentxn: acc=%s\n", gentxn->account) );
if( ctx->curr_acc_isnew == TRUE )
{
DB( g_print(" sub amount from initial\n") );
ctx->curr_acc->initial -= data.amount;
}
}
else
{
da_gen_txn_free(gentxn);
DB( g_print(" no account, insert txn skipped\n") );
}
return 0;
}
static LibofxProcStatusCallback
ofx_proc_status_cb(const struct OfxStatusData data, ImportContext *ctx)
{
DB( g_print("** ofx_proc_status_cb()\n") );
if(data.ofx_element_name_valid==true){
DB( g_print(" Ofx entity this status is relevent to: '%s'\n", data.ofx_element_name) );
}
if(data.severity_valid==true){
DB( g_print(" Severity: ") );
switch(data.severity){
case INFO : DB( g_print("INFO\n") );
break;
case WARN : DB( g_print("WARN\n") );
break;
case ERROR : DB( g_print("ERROR\n") );
break;
default: DB( g_print("WRITEME: Unknown status severity!\n") );
}
}
if(data.code_valid==true){
DB( g_print(" Code: %d, name: %s\n Description: %s\n", data.code, data.name, data.description) );
}
if(data.server_message_valid==true){
DB( g_print(" Server Message: %s\n", data.server_message) );
}
DB( g_print("\n") );
return 0;
}
GList *homebank_ofx_import(ImportContext *ictx, GenFile *genfile)
{
/*extern int ofx_PARSER_msg;
extern int ofx_DEBUG_msg;
extern int ofx_WARNING_msg;
extern int ofx_ERROR_msg;
extern int ofx_INFO_msg;
extern int ofx_STATUS_msg;*/
DB( g_print("\n[import] ofx import (libofx=%s) \n", LIBOFX_VERSION_RELEASE_STRING) );
/*ofx_PARSER_msg = false;
ofx_DEBUG_msg = false;
ofx_WARNING_msg = false;
ofx_ERROR_msg = false;
ofx_INFO_msg = false;
ofx_STATUS_msg = false;*/
LibofxContextPtr libofx_context = libofx_get_new_context();
ofx_set_status_cb (libofx_context, (LibofxProcStatusCallback) ofx_proc_status_cb , ictx);
ofx_set_statement_cb (libofx_context, (LibofxProcStatementCallback) ofx_proc_statement_cb , ictx);
ofx_set_account_cb (libofx_context, (LibofxProcAccountCallback) ofx_proc_account_cb , ictx);
ofx_set_transaction_cb(libofx_context, (LibofxProcTransactionCallback)ofx_proc_transaction_cb, ictx);
#ifdef G_OS_WIN32
//#932959: windows don't like utf8 path, so convert
gchar *filepath = g_win32_locale_filename_from_utf8(genfile->filepath);
libofx_proc_file(libofx_context, filepath, AUTODETECT);
g_free(filepath);
#else
libofx_proc_file(libofx_context, genfile->filepath, AUTODETECT);
#endif
libofx_free_context(libofx_context);
DB( g_print("ofx nb txn=%d\n", g_list_length(ictx->gen_lst_txn) ));
return ictx->gen_lst_txn;
}
#endif
homebank-5.9.7/src/ui-assign.c 0000644 0001750 0001750 00000167512 15021366150 015457 0 ustar franam franam /* HomeBank -- Free, easy, personal ruleing for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "ui-assign.h"
#include "hbtk-switcher.h"
#include "ui-category.h"
#include "ui-payee.h"
#include "ui-tag.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern gchar *CYA_ASG_FIELD[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_asg_listview_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFASG_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFASG_TOGGLE, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
}
static void ui_asg_listview_sort_force(GtkTreeSortable *sortable, gpointer user_data)
{
gint sort_column_id;
GtkSortType order;
DB( g_print("\n[ui-asg-listview] sort force\n") );
gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &order);
DB( g_print(" id %d\n order %d\n", sort_column_id, order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), sort_column_id, order);
}
/*
static gint
ui_asg_listview_compare_default_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
Assign *item1, *item2;
gtk_tree_model_get(model, a, LST_DEFASG_DATAS, &item1, -1);
gtk_tree_model_get(model, b, LST_DEFASG_DATAS, &item2, -1);
return item1->pos - item2->pos;
}
*/
static gint
ui_asg_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
Assign *item1, *item2;
gint retval = 0;
gtk_tree_model_get(model, a, LST_DEFASG_DATAS, &item1, -1);
gtk_tree_model_get(model, b, LST_DEFASG_DATAS, &item2, -1);
switch (sortcol)
{
case LST_DEFASG_SORT_POS:
retval = item1->pos - item2->pos;
break;
case LST_DEFASG_SORT_SEARCH:
retval = hb_string_utf8_compare(item1->search, item2->search);
break;
case LST_DEFASG_SORT_PAYEE:
{
gchar *name1 = assign_get_target_payee(item1);
gchar *name2 = assign_get_target_payee(item2);
retval = hb_string_utf8_compare(name1, name2);
}
break;
case LST_DEFASG_SORT_CATEGORY:
{
gchar *name1 = assign_get_target_category(item1);
gchar *name2 = assign_get_target_category(item2);
retval = hb_string_utf8_compare(name1, name2);
}
break;
case LST_DEFASG_SORT_PAYMENT:
retval = item1->paymode - item2->paymode;
break;
case LST_DEFASG_SORT_TAGS:
{
gchar *t1 = tags_tostring(item1->tags);
gchar *t2 = tags_tostring(item2->tags);
retval = hb_string_utf8_compare(t1, t2);
g_free(t2);
g_free(t1);
}
break;
case LST_DEFASG_SORT_NOTES:
retval = hb_string_utf8_compare(item1->notes, item2->notes);
break;
default:
g_return_val_if_reached(0);
}
if( retval == 0 )
retval = item1->pos - item2->pos;
return retval;
}
static void
ui_asg_listview_cell_data_func_icons (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
iconname = ( asgitem->flags & ASGF_PREFILLED ) ? ICONNAME_HB_ITEM_PREFILLED : NULL;
break;
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
ui_asg_listview_cell_data_function_pos (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar buffer[256];
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
g_snprintf(buffer, 256-1, "%d", asgitem->pos);
g_object_set(renderer, "text", buffer, NULL);
}
static void
ui_asg_listview_cell_data_func_searchicons (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
iconname = ( asgitem->flags & ASGF_EXACT ) ? ICONNAME_HB_TEXT_CASE : NULL;
break;
case 2:
iconname = ( asgitem->flags & ASGF_REGEX ) ? ICONNAME_HB_TEXT_REGEX : NULL;
break;
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
ui_asg_listview_cell_data_function_search (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar *name;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
if(asgitem->search == NULL)
name = _("(none)"); // can never occurs also
else
name = asgitem->search;
g_object_set(renderer, "text", name, NULL);
}
static void
ui_asg_listview_cell_data_function_payee (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar *text;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
text = assign_get_target_payee(asgitem);
g_object_set(renderer, "text", text, NULL);
}
static void
ui_asg_listview_cell_data_function_category (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gchar *text;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
text = assign_get_target_category(asgitem);
g_object_set(renderer, "text", text, NULL);
}
static void
ui_asg_listview_cell_data_function_notes (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
g_object_set(renderer, "text", asgitem->notes != NULL ? asgitem->notes : "", NULL);
}
static void
ui_asg_listview_cell_data_function_payment (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
g_object_set(renderer, "icon-name", get_paymode_icon_name(asgitem->paymode), NULL);
}
static void
ui_asg_listview_cell_data_function_tags (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Assign *asgitem;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &asgitem, -1);
if(asgitem->tags != NULL)
{
gchar *text = tags_tostring(asgitem->tags);
g_object_set(renderer, "text", text, NULL);
g_free(text);
}
else
g_object_set(renderer, "text", NULL, NULL);
}
#if MYDEBUG == 1
static void
ui_asg_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Payee *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
/* = = = = = = = = = = = = = = = = */
/**
* rul_list_add:
*
* Add a single element (useful for dynamics add)
*
* Return value: --
*
*/
void
ui_asg_listview_add(GtkTreeView *treeview, Assign *item)
{
DB( g_print("\n[ui-asg-listview] add\n") );
if( item->search != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
model = gtk_tree_view_get_model(treeview);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFASG_TOGGLE, FALSE,
LST_DEFASG_DATAS, item,
-1);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter);
//5.6 add scroll to active item
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_view_scroll_to_cell(treeview, path, NULL, FALSE, 0, 0);
gtk_tree_path_free(path);
}
}
guint32
ui_asg_listview_get_selected_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui-asg-listview] get selected key\n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Assign *item;
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &item, -1);
if( item!= NULL )
return item->key;
}
return 0;
}
void
ui_asg_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui-asg-listview] remove selected\n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
}
/*
static gint ui_asg_glist_compare_func(Assign *a, Assign *b)
{
return 0; //((gint)a->pos - b->pos);
}
*/
void ui_asg_listview_populate(GtkWidget *view, gchar *needle)
{
GtkTreeModel *model;
GtkTreeIter iter;
GList *lrul, *list;
gboolean hastext = FALSE;
gboolean insert = TRUE;
DB( g_print("\n[ui-asg-listview] populate\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
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(view), NULL); /* Detach model from view */
if( needle != NULL )
hastext = (strlen(needle) >= 2) ? TRUE : FALSE;
/* populate */
//g_hash_table_foreach(GLOBALS->h_rul, (GHFunc)ui_asg_listview_populate_ghfunc, model);
//lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
lrul = list = assign_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Assign *item = list->data;
if(hastext)
insert = hb_string_utf8_strstr(item->search, needle, FALSE);
if( insert == TRUE )
{
DB( g_print(" populate: k%d p%d '%s'\n", item->key, item->pos, item->notes) );
gtk_list_store_insert_with_values (GTK_LIST_STORE(model), &iter, -1,
LST_DEFASG_TOGGLE , FALSE,
LST_DEFASG_DATAS, item,
-1);
}
list = g_list_next(list);
}
g_list_free(lrul);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
}
/*
static gboolean ui_asg_listview_search_equal_func (GtkTreeModel *model,
gint column,
const gchar *key,
GtkTreeIter *iter,
gpointer search_data)
{
gboolean retval = TRUE;
gchar *normalized_string;
gchar *normalized_key;
gchar *case_normalized_string = NULL;
gchar *case_normalized_key = NULL;
Assign *item;
//gtk_tree_model_get_value (model, iter, column, &value);
gtk_tree_model_get(model, iter, LST_DEFASG_DATAS, &item, -1);
if(item != NULL)
{
normalized_string = g_utf8_normalize (item->search, -1, G_NORMALIZE_ALL);
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
if (normalized_string && normalized_key)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
case_normalized_key = g_utf8_casefold (normalized_key, -1);
if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
retval = FALSE;
}
g_free (normalized_key);
g_free (normalized_string);
g_free (case_normalized_key);
g_free (case_normalized_string);
}
return retval;
}
*/
static GtkTreeViewColumn *
ui_asg_listview_column_text_create(gchar *title, gint sortcolumnid, GtkTreeCellDataFunc func, gpointer user_data)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
DB( g_print("\n[ui-asg-listview] text create\n") );
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
//gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
//gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
//gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, func, user_data, NULL);
return column;
}
/*
enum
{
TARGET_GTK_TREE_MODEL_ROW
};
static GtkTargetEntry row_targets[] = {
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,
TARGET_GTK_TREE_MODEL_ROW }
};
static void
data_received (GtkWidget *widget,
GdkDragContext *context,
gint x, gint y,
GtkSelectionData *selda,
guint info, guint time,
gpointer dada)
{
g_print(" drag data received\n");
}
*/
GtkWidget *
ui_asg_listview_new(gboolean withtoggle)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("\n[ui-asg-listview] new\n") );
// create list store
store = gtk_list_store_new(NUM_LST_DEFASG,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer,
"active", LST_DEFASG_TOGGLE,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
g_signal_connect (renderer, "toggled",
G_CALLBACK (ui_asg_listview_toggled_cb), store);
}
// column: icons
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_func_icons, GINT_TO_POINTER(1), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column: position
renderer = gtk_cell_renderer_text_new ();
//#2004631 date and column title alignement
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, "#");
gtk_tree_view_column_set_sort_column_id (column, LST_DEFASG_SORT_POS);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_function_pos, GINT_TO_POINTER(LST_DEFASG_DATAS), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Search
column = ui_asg_listview_column_text_create(_("Search"), LST_DEFASG_SORT_SEARCH, ui_asg_listview_cell_data_function_search, NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_cell_renderer_set_fixed_size(renderer, 16, -1);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_func_searchicons, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_cell_renderer_set_fixed_size(renderer, 16, -1);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_func_searchicons, GINT_TO_POINTER(2), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Notes
column = ui_asg_listview_column_text_create(_("Notes"), LST_DEFASG_SORT_NOTES, ui_asg_listview_cell_data_function_notes, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Payee
column = ui_asg_listview_column_text_create(_("Payee"), LST_DEFASG_SORT_PAYEE, ui_asg_listview_cell_data_function_payee, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Category
column = ui_asg_listview_column_text_create(_("Category"), LST_DEFASG_SORT_CATEGORY, ui_asg_listview_cell_data_function_category, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Payment
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set(renderer, "xalign", 0.0, NULL);
column = gtk_tree_view_column_new();
//TRANSLATORS: abbreviation for payment
gtk_tree_view_column_set_title(column, _("Pay."));
gtk_tree_view_column_set_sort_column_id (column, LST_DEFASG_SORT_PAYMENT);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_asg_listview_cell_data_function_payment, 0, NULL);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column : Tags
column = ui_asg_listview_column_text_create(_("Tags"), LST_DEFASG_SORT_TAGS, ui_asg_listview_cell_data_function_tags, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column : empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeviewattribute
//gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
//5.6 drag n drop is no more possible easily when sort
//gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
/*
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
GDK_BUTTON1_MASK,
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview),
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
g_signal_connect (treeview, "drag-data-received", G_CALLBACK (data_received), NULL);
*/
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_asg_listview_compare_default_func, NULL, NULL);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
//sortable
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_POS, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_POS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_SEARCH, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_SEARCH), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_PAYEE, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_PAYEE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_CATEGORY, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_CATEGORY), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_PAYMENT, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_PAYMENT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_TAGS, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_TAGS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_NOTES, ui_asg_listview_compare_func, GINT_TO_POINTER(LST_DEFASG_SORT_NOTES), NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFASG_SORT_POS, GTK_SORT_ASCENDING);
//#1897810 add quicksearch
//gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_asg_listview_search_equal_func, NULL, NULL);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_asg_dialog_update(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_dialog_data *data;
gboolean sensitive;
//ignore event triggered from inactive radiobutton
//if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) == FALSE )
// return;
DB( g_print("\n[ui-asg-dialog] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_amount));
gtk_widget_set_sensitive(data->ST_amount, sensitive);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_pay));
gtk_widget_set_sensitive(data->LB_pay, sensitive);
gtk_widget_set_sensitive(data->PO_pay, sensitive);
gtk_widget_set_sensitive(data->CM_payovw, sensitive);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_cat));
gtk_widget_set_sensitive(data->LB_cat, sensitive);
gtk_widget_set_sensitive(data->PO_cat, sensitive);
gtk_widget_set_sensitive(data->CM_catovw, sensitive);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_mod));
gtk_widget_set_sensitive(data->LB_mod, sensitive);
gtk_widget_set_sensitive(data->NU_mod, sensitive);
gtk_widget_set_sensitive(data->CM_modovw, sensitive);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_tags));
gtk_widget_set_sensitive(data->LB_tags, sensitive);
gtk_widget_set_sensitive(data->ST_tags, sensitive);
gtk_widget_set_sensitive(data->CY_tags, sensitive);
gtk_widget_set_sensitive(data->CM_tagsovw, sensitive);
}
/*
** rename the selected assign to our treeview and temp GList
*/
static void ui_asg_dialog_rename(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_dialog_data *data;
gboolean error;
gchar *txt;
GString *errstr;
Assign *item;
DB( g_print("\n[ui-asg-dialog] rename\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
errstr = g_string_new(NULL);
error = FALSE;
gtk_label_set_text(GTK_LABEL(data->LB_wrntxt), "");
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET(data->ST_search)), GTK_STYLE_CLASS_ERROR);
item = data->asgitem;
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
if( txt == NULL || *txt == '\0' )
{
//#2042035
g_string_append_printf(errstr, _("Search cannot be empty"));
error = TRUE;
goto end;
}
if( strcmp(txt, item->search) )
{
//#2042035
Assign *existitem = da_asg_get_by_name(txt);
if( existitem != NULL )
{
g_string_append_printf(errstr, _("This search text already exists at position %d"), existitem->pos);
error = TRUE;
}
}
//#1842897 lead/trail visible if detected
if( txt != NULL && hb_string_has_leading_trailing(txt) == TRUE )
{
gchar *wrntxt;
gchar **split;
split = g_strsplit(txt, " ", -1);
wrntxt = g_strjoinv("\xE2\x90\xA3", split);
if( errstr->len > 0 )
g_string_append(errstr, "\n");
g_string_append(errstr, wrntxt);
g_free(wrntxt);
g_strfreev(split);
}
end:
gtk_label_set_text(GTK_LABEL(data->LB_wrntxt), errstr->str);
if( errstr->len > 0 )
gtk_widget_show(data->GR_wrntxt);
else
gtk_widget_hide(data->GR_wrntxt);
g_string_free(errstr, TRUE);
if( error == TRUE )
{
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(data->ST_search)), GTK_STYLE_CLASS_ERROR);
}
//5.7.2 disable OK
gtk_dialog_set_response_sensitive(GTK_DIALOG (data->dialog), GTK_RESPONSE_ACCEPT, !error);
}
static void ui_asg_dialog_get(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_dialog_data *data;
Assign *item;
gint active;
gchar *txt;
DB( g_print("\n[ui-asg-dialog] get\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
item = data->asgitem;
if(item != NULL)
{
data->change++;
item->field = hbtk_switcher_get_active (HBTK_SWITCHER(data->CY_field));
//#2042035
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
if (txt && *txt)
{
hbtk_entry_replace_text(GTK_ENTRY(data->ST_search), &item->search);
}
item->flags = 0;
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_exact));
if(active == 1) item->flags |= ASGF_EXACT;
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_re));
if(active == 1) item->flags |= ASGF_REGEX;
//#1710085 assignment based on amount
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_amount));
if(active == 1) item->flags |= ASGF_AMOUNT;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_pay));
if(active == 1) item->flags |= ASGF_DOPAY;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_payovw));
if(active == 1) item->flags |= ASGF_OVWPAY;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_cat));
if(active == 1) item->flags |= ASGF_DOCAT;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_catovw));
if(active == 1) item->flags |= ASGF_OVWCAT;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_mod));
if(active == 1) item->flags |= ASGF_DOMOD;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_modovw));
if(active == 1) item->flags |= ASGF_OVWMOD;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_tags));
if(active == 1) item->flags |= ASGF_DOTAG;
active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(data->CM_tagsovw));
if(active == 1) item->flags |= ASGF_OVWTAG;
item->amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
//item->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat));
item->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(data->PO_cat));
//item->kpay = ui_pay_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_pay));
item->kpay = ui_pay_entry_popover_get_key_add_new(GTK_BOX(data->PO_pay));
item->paymode = kiv_combo_box_get_active(GTK_COMBO_BOX(data->NU_mod));
gchar *txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_tags));
DB( g_print(" tags: '%s'\n", txt) );
g_free(item->tags);
item->tags = tags_parse(txt);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_notes), &item->notes);
}
}
/*
** set widgets contents from the selected assign
*/
static void ui_asg_dialog_set(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_dialog_data *data;
Assign *item;
gint active;
DB( g_print("\n[ui-asg-dialog] set\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
item = data->asgitem;
DB( g_print(" -> set rul id=%d\n", item->key) );
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), item->search);
hbtk_switcher_set_active (HBTK_SWITCHER(data->CY_field), item->field);
hbtk_entry_set_text(GTK_ENTRY(data->ST_notes), item->notes);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_exact), (item->flags & ASGF_EXACT) ? 1 : 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_re), (item->flags & ASGF_REGEX) ? 1 : 0);
//#1710085 assignment based on amount
//g_signal_handlers_block_by_func(G_OBJECT(data->CM_amount), G_CALLBACK(ui_asg_manage_update_amount), NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_amount), (item->flags & ASGF_AMOUNT ? 1 : 0));
//g_signal_handlers_unblock_by_func(G_OBJECT(data->CM_amount), G_CALLBACK(ui_asg_manage_update_amount), NULL);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), item->amount);
active = (item->flags & ASGF_DOPAY) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_pay), active);
active = (item->flags & ASGF_OVWPAY) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_payovw), active);
//ui_pay_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_pay), item->kpay);
ui_pay_entry_popover_set_active(GTK_BOX(data->PO_pay), item->kpay);
active = (item->flags & ASGF_DOCAT) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_cat), active);
active = (item->flags & ASGF_OVWCAT) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_catovw), active);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), item->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), item->kcat);
active = (item->flags & ASGF_DOMOD) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_mod), active);
active = (item->flags & ASGF_OVWMOD) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_modovw), active);
kiv_combo_box_set_active(GTK_COMBO_BOX(data->NU_mod), item->paymode);
active = (item->flags & ASGF_DOTAG) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_tags), active);
active = (item->flags & ASGF_OVWTAG) ? 1 : 0;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(data->CM_tagsovw), active);
gchar *tagstr = tags_tostring(item->tags);
hbtk_entry_set_text(GTK_ENTRY(data->ST_tags), tagstr);
g_free(tagstr);
hbtk_entry_set_text(GTK_ENTRY(data->ST_notes), item->notes);
//}
}
static void ui_asg_dialog_cb_destroy_cb_destroy(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_dialog_data *data;
DB( g_print("\n[ui-asg-dialog] destroy cb\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" free data\n") );
g_free(data);
}
static GtkWidget *ui_asg_dialog_new(GtkWindow *parent, Assign *item)
{
struct ui_asg_dialog_data *data;
GtkWidget *dialog, *content;
GtkWidget *content_grid, *group_grid;
GtkWidget *label, *entry1;
GtkWidget *wbox, *bbox;
GtkWidget *widget;
gint crow, row;
gint w, h, dw, dh;
DB( g_print("\n[ui-asg-dialog] new\n") );
data = g_malloc0(sizeof(struct ui_asg_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Assignment"),
GTK_WINDOW(parent),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
data->asgitem = item;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 4:3
dw = (dh * 4) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" window=%p, inst_data=%p\n", dialog, data) );
//window contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
/* right area */
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX(content), content_grid);
// group :: Rule
crow = 0;
group_grid = gtk_grid_new ();
data->GR_condition = group_grid;
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);
row = 0;
label = make_label_group(_("Condition"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 3, 1);
row++;
//label = make_label_widget(_("Con_tains:"));
label = make_label_widget(_("_Search:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(bbox)), GTK_STYLE_CLASS_LINKED);
gtk_grid_attach (GTK_GRID (group_grid), bbox, 2, row, 2, 1);
entry1 = make_string(label);
data->ST_search = entry1;
gtk_widget_set_hexpand(entry1, TRUE);
hbtk_box_prepend (GTK_BOX(bbox), entry1);
row++;
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
data->GR_wrntxt = bbox;
gtk_grid_attach (GTK_GRID (group_grid), bbox, 2, row, 2, 1);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_WARNING);
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_label(NULL, 0.0, 0.5);
data->LB_wrntxt = widget;
hbtk_box_prepend (GTK_BOX(bbox), widget);
row++;
label = make_label_widget(_("_In:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_ASG_FIELD, FALSE);
data->CY_field = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Case _sensitive"));
data->CM_exact = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("_Regular expression"));
data->CM_re = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
label = make_label_widget(_("Amou_nt:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (group_grid), bbox, 2, row, 2, 1);
widget = gtk_check_button_new_with_mnemonic(_("_AND"));
data->CM_amount = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_amount(label);
data->ST_amount = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
// group :: Assignments
crow++;
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);
row = 0;
//label = make_label_group(_("Assign payee"));
label = make_label_group(_("Assignments"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 3, 1);
//payee
row++;
widget = gtk_check_button_new();
data->CM_pay = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_widget (_("_Payee:"));
data->LB_pay = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
//widget = ui_pay_comboboxentry_new(label);
widget = ui_pay_entry_popover_new(label);
data->PO_pay = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
widget = gtk_check_button_new_with_mnemonic(_("Overwrite"));
data->CM_payovw = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
//category
row++;
widget = gtk_check_button_new();
data->CM_cat = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_widget (_("_Category:"));
data->LB_cat = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
data->PO_cat = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
widget = gtk_check_button_new_with_mnemonic(_("Overwrite"));
data->CM_catovw = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
//payment
row++;
widget = gtk_check_button_new();
data->CM_mod = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_widget (_("Pay_ment:"));
data->LB_mod = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
widget = make_paymode (label);
data->NU_mod = widget;
gtk_widget_set_halign(widget, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group_grid), widget, 3, row, 1, 1);
widget = gtk_check_button_new_with_mnemonic(_("Overwrite"));
data->CM_modovw = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
//tags
row++;
widget = gtk_check_button_new();
data->CM_tags = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
label = make_label_widget (_("_Tags:"));
data->LB_tags = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 2, row, 1, 1);
wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(wbox)), GTK_STYLE_CLASS_LINKED);
gtk_grid_attach (GTK_GRID (group_grid), wbox, 3, row, 1, 1);
widget = make_string(label);
data->ST_tags = widget;
hbtk_box_prepend (GTK_BOX (wbox), widget);
widget = ui_tag_popover_list(data->ST_tags);
data->CY_tags = widget;
gtk_box_prepend (GTK_BOX (wbox), widget);
widget = gtk_check_button_new_with_mnemonic(_("Overwrite"));
data->CM_tagsovw = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 4, row, 1, 1);
//misc
crow++;
group_grid = gtk_grid_new ();
data->GR_misc = group_grid;
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);
gtk_widget_set_margin_top(group_grid, SPACING_MEDIUM);
row = 0;
label = make_label_widget(_("Notes:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
entry1 = make_string(label);
data->ST_notes = entry1;
gtk_widget_set_hexpand(entry1, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), entry1, 2, row, 2, 1);
// connect dialog signals
g_signal_connect (dialog, "destroy", G_CALLBACK (ui_asg_dialog_cb_destroy_cb_destroy), NULL);
//g_signal_connect (dialog, "map-event", G_CALLBACK (ui_asg_manage_mapped), &dialog);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
gtk_widget_hide(data->GR_wrntxt);
g_signal_connect (G_OBJECT (data->ST_search), "changed", G_CALLBACK (ui_asg_dialog_rename), NULL);
g_signal_connect (data->CM_amount, "toggled", G_CALLBACK (ui_asg_dialog_update), NULL);
g_signal_connect (G_OBJECT (data->CM_pay), "toggled", G_CALLBACK (ui_asg_dialog_update), NULL);
g_signal_connect (G_OBJECT (data->CM_cat), "toggled", G_CALLBACK (ui_asg_dialog_update), NULL);
g_signal_connect (G_OBJECT (data->CM_mod), "toggled", G_CALLBACK (ui_asg_dialog_update), NULL);
g_signal_connect (G_OBJECT (data->CM_tags), "toggled", G_CALLBACK (ui_asg_dialog_update), NULL);
return dialog;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_asg_manage_refilter(struct ui_asg_manage_data *data)
{
gchar *needle;
DB( g_print("[ui-asg-manage] refilter\n") );
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
ui_asg_listview_populate(data->LV_rul, needle);
}
/*
** update the widgets status and contents from action/selection value
*/
static void ui_asg_manage_update(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean selected, sensitive, canup, candw, canto;
DB( g_print("\n[ui-asg-manage] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//if true there is a selected node
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul)), &model, &iter);
DB( g_print(" selected = %d\n", selected) );
sensitive = (selected == TRUE) ? TRUE : FALSE;
//gtk_widget_set_sensitive(data->BT_mod, sensitive);
gtk_widget_set_sensitive(data->BT_rem, sensitive);
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_dup, sensitive);
//#1999243/2000629 rewrite up/down/to button sensitivity
canup = candw = canto = selected;
DB( g_print(" is_sortable= %d\n", GTK_IS_TREE_SORTABLE(model)) );
if( selected == TRUE )
{
GtkTreeIter *tmpIter;
gint sort_column_id;
GtkSortType sort_order;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_rul));
sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
sort_order = GTK_SORT_DESCENDING;
gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &sort_column_id, &sort_order);
DB( g_print(" sort is colid=%d order=%d (ok is %d %d)\n", sort_column_id, sort_order, LST_DEFASG_SORT_POS, GTK_SORT_ASCENDING) );
if( !((sort_column_id == LST_DEFASG_SORT_POS) && (sort_order == GTK_SORT_ASCENDING)) )
{
canup = candw = FALSE;
DB( g_print(" sort is not by position ASC\n") );
goto end;
}
tmpIter = gtk_tree_iter_copy(&iter);
canup = gtk_tree_model_iter_previous(model, tmpIter);
gtk_tree_iter_free(tmpIter);
tmpIter = gtk_tree_iter_copy(&iter);
candw = gtk_tree_model_iter_next(model, tmpIter);
gtk_tree_iter_free(tmpIter);
}
end:
DB( g_print(" can up=%d dw=%d to=%d\n", canup, candw, canto) );
gtk_widget_set_sensitive(data->BT_up , canup);
gtk_widget_set_sensitive(data->BT_down, candw);
gtk_widget_set_sensitive(data->BT_move, canto);
}
/*
static gboolean ui_asg_manage_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
{
ui_asg_manage_get(widget, user_data);
return FALSE;
}
*/
static void ui_asg_manage_popmove(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Assign *curitem;
guint32 curpos, newpos;
GList *lrul, *list;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] moveto apply (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_poppos));
newpos = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->ST_poppos));
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &curitem, -1);
curpos = curitem->pos;
if( curpos == newpos )
goto end;
lrul = list = assign_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Assign *item = list->data;
if( item != curitem )
{
//move is before
if( newpos < curpos )
{
if( item->pos >= newpos && item->pos < curpos )
item->pos++;
}
//move is after
else
{
if( item->pos > curpos && item->pos <= newpos )
item->pos--;
}
}
list = g_list_next(list);
}
g_list_free(lrul);
curitem->pos = newpos;
//#1999243 add change
data->change++;
ui_asg_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
end:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->MB_moveto), FALSE);
}
//#1999243 set maximum position when popover open
static void ui_asg_manage_cb_move_to(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
gint maxpos;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] moveto init (data=%p)\n", data) );
maxpos = da_asg_length();
gtk_spin_button_set_range(GTK_SPIN_BUTTON(data->ST_poppos), 1.0, (gdouble)maxpos);
}
static void ui_asg_manage_cb_move_updown(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkDirectionType direction = GPOINTER_TO_INT(user_data);
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean hasprvnxt;
Assign *curitem, *prvnxtitem;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] up/down (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &curitem, -1);
hasprvnxt = FALSE;
if( direction == GTK_DIR_UP )
hasprvnxt = gtk_tree_model_iter_previous(model, &iter);
else if( direction == GTK_DIR_DOWN )
hasprvnxt = gtk_tree_model_iter_next(model, &iter);
if( hasprvnxt == TRUE )
{
gushort tmp = curitem->pos;
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &prvnxtitem, -1);
//swap position
curitem->pos = prvnxtitem->pos;
prvnxtitem->pos = tmp;
//#1999243 add change
data->change++;
ui_asg_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
}
}
static void ui_asg_manage_edit(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Assign *item;
GtkWidget *dialog;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] edit (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &item, -1);
dialog = ui_asg_dialog_new(GTK_WINDOW(data->dialog), item);
ui_asg_dialog_set(dialog, NULL);
ui_asg_dialog_update(dialog, NULL);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
ui_asg_dialog_get(dialog, NULL);
data->change++;
}
// cleanup and destroy
//ui_asg_dialog_cleanup(dialog);
gtk_window_destroy (GTK_WINDOW(dialog));
ui_asg_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
}
static void ui_asg_manage_dup(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Assign *item, *newitem;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] dup (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFASG_DATAS, &item, -1);
newitem = da_asg_duplicate(item);
if( newitem )
{
ui_asg_listview_add(GTK_TREE_VIEW(data->LV_rul), newitem);
ui_asg_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
else
{
gchar *newsearch = g_strdup_printf("%s %s", item->search, _("(copy)") );
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot duplicate this Assignment,\n"
"'%s' already exists."),
newsearch
);
g_free(newsearch);
}
}
}
/*
** add an empty new assign to our temp GList and treeview
*/
static void ui_asg_manage_add(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
GtkWidget *dialog;
Assign *item;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] add (data=%p)\n", data) );
item = da_asg_malloc();
item->search = g_strdup_printf( _("(rule %d)"), da_asg_length()+1);
dialog = ui_asg_dialog_new(GTK_WINDOW(data->dialog), item);
ui_asg_dialog_set(dialog, NULL);
ui_asg_dialog_update(dialog, NULL);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
ui_asg_dialog_get(dialog, NULL);
da_asg_append(item);
ui_asg_listview_add(GTK_TREE_VIEW(data->LV_rul), item);
data->change++;
}
else
da_asg_free(item);
// cleanup and destroy
//ui_asg_dialog_cleanup(dialog);
gtk_window_destroy (GTK_WINDOW(dialog));
}
/*
** delete the selected assign to our treeview and temp GList
*/
static void ui_asg_manage_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data;
guint32 key;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-asg-manage] delete) data=%p\n", data) );
key = ui_asg_listview_get_selected_key(GTK_TREE_VIEW(data->LV_rul));
if( key > 0 )
{
Assign *item = da_asg_get(key);
gchar *title = NULL;
gchar *secondtext;
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->search);
secondtext = _("If you delete an assignment, it will be permanently lost.");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
da_asg_remove(key);
ui_asg_listview_remove_selected(GTK_TREE_VIEW(data->LV_rul));
data->change++;
}
da_asg_update_position();
}
}
static gboolean ui_asg_manage_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct ui_asg_manage_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
DB( g_printf("\n[ui-asg-manage] cb key press\n") );
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else
if (keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_rul);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void
ui_asg_manage_cb_row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
DB( g_printf("\n[ui-asg-manage] row activated\n") );
ui_asg_manage_edit(GTK_WIDGET(treeview), userdata);
}
static void
ui_asg_manage_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_printf("\n[ui-asg-manage] selection\n") );
ui_asg_manage_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void
ui_asg_manage_sort_changed(GtkTreeSortable *sortable, gpointer user_data)
{
struct ui_asg_manage_data *data = user_data;
DB( g_printf("\n[ui-asg-manage] sort changed\n") );
ui_asg_manage_update(data->dialog, NULL);
}
static void
ui_asg_manage_search_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct ui_asg_manage_data *data = user_data;
DB( g_printf("\n[ui-asg-manage] search_changed_cb\n") );
ui_asg_manage_refilter(data);
}
static gboolean
ui_asg_manage_cleanup(struct ui_asg_manage_data *data, gint result)
{
//GtkTreeModel *tree_model;
gboolean doupdate = FALSE;
DB( g_print("\n[ui-asg-manage] cleanup data=%p\n", data) );
//tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_rul));
//data->change += ui_asg_manage_persist_position(tree_model);
GLOBALS->changes_count += data->change;
return doupdate;
}
static void ui_asg_manage_setup(struct ui_asg_manage_data *data)
{
DB( g_print("\n[ui-asg-manage] setup\n") );
DB( g_print(" init data\n") );
//init GList
data->tmp_list = NULL; //hb-glist_clone_list(GLOBALS->rul_list, sizeof(struct _Assign));
data->change = 0;
DB( g_print(" populate\n") );
da_asg_update_position();
ui_asg_listview_populate(data->LV_rul, NULL);
//5.5 done in popover
//ui_pay_comboboxentry_populate(GTK_COMBO_BOX(data->PO_pay), GLOBALS->h_pay);
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_cat), GLOBALS->h_cat);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
//gtk_tree_view_set_search_entry(GTK_TREE_VIEW(data->LV_rul), GTK_ENTRY(data->ST_search));
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(data->LV_rul), FALSE);
g_signal_connect (data->ST_search, "search-changed", G_CALLBACK (ui_asg_manage_search_changed_cb), data);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_rul)), "changed", G_CALLBACK (ui_asg_manage_selection), NULL);
g_signal_connect (data->LV_rul, "row-activated", G_CALLBACK (ui_asg_manage_cb_row_activated), NULL);
g_signal_connect (gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_rul)), "sort-column-changed", G_CALLBACK (ui_asg_manage_sort_changed), data);
g_signal_connect (G_OBJECT (data->BT_add), "clicked", G_CALLBACK (ui_asg_manage_add), NULL);
g_signal_connect (G_OBJECT (data->BT_rem), "clicked", G_CALLBACK (ui_asg_manage_delete), NULL);
g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_asg_manage_edit), NULL);
g_signal_connect (G_OBJECT (data->BT_dup), "clicked", G_CALLBACK (ui_asg_manage_dup), NULL);
g_signal_connect (G_OBJECT (data->BT_up ), "clicked", G_CALLBACK (ui_asg_manage_cb_move_updown), GUINT_TO_POINTER(GTK_DIR_UP));
g_signal_connect (G_OBJECT (data->BT_down), "clicked", G_CALLBACK (ui_asg_manage_cb_move_updown), GUINT_TO_POINTER(GTK_DIR_DOWN));
g_signal_connect (G_OBJECT (data->BT_move), "clicked", G_CALLBACK (ui_asg_manage_cb_move_to), NULL);
// popover signals
g_signal_connect (G_OBJECT (data->ST_poppos), "activate", G_CALLBACK (ui_asg_manage_popmove), NULL);
g_signal_connect (G_OBJECT (data->BT_popmove), "clicked", G_CALLBACK (ui_asg_manage_popmove), NULL);
}
static gboolean
ui_asg_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_asg_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[ui-asg-manage] mapped\n") );
ui_asg_manage_setup(data);
ui_asg_manage_update(data->LV_rul, NULL);
gtk_widget_grab_focus(GTK_WIDGET(data->LV_rul));
data->mapped_done = TRUE;
return FALSE;
}
static GtkWidget *ui_asg_popover_move_after_new(struct ui_asg_manage_data *data)
{
GtkWidget *box, *menubutton, *label, *widget, *image;
GtkWidget *pop_content;
DB( g_print("\n[ui-asg-manage] create popmove\n") );
menubutton = gtk_menu_button_new ();
data->MB_moveto = menubutton;
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
image = gtk_image_new_from_icon_name (ICONNAME_LIST_MOVE_AFTER, GTK_ICON_SIZE_BUTTON);
gtk_box_prepend (GTK_BOX(box), image);
gtk_container_add(GTK_CONTAINER(menubutton), box);
gtk_widget_set_tooltip_text(menubutton, _("Move to..."));
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_DOWN );
gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
//gtk_widget_set_hexpand (menubutton, TRUE);
gtk_widget_show_all(menubutton);
pop_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
widget = make_label_group(_("Move rule"));
gtk_box_prepend(GTK_BOX(pop_content), widget);
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
hbtk_box_prepend (GTK_BOX(pop_content), widget);
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX(pop_content), box);
label = make_label_widget(_("_To:"));
gtk_box_prepend(GTK_BOX(box), label);
widget = make_numeric(label, 1, 99);
data->ST_poppos = widget;
gtk_entry_set_width_chars(GTK_ENTRY(widget), 10);
gtk_box_prepend(GTK_BOX(box), widget);
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
hbtk_box_prepend (GTK_BOX(pop_content), widget);
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append(GTK_BOX(pop_content), box);
widget = gtk_button_new_with_mnemonic(_("Move"));
data->BT_popmove = widget;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_SUGGESTED_ACTION);
gtk_box_append(GTK_BOX(box), widget);
gtk_widget_show_all(pop_content);
GtkWidget *popover = create_popover (menubutton, pop_content, GTK_POS_TOP);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
return menubutton;
}
GtkWidget *ui_asg_manage_dialog (void)
{
struct ui_asg_manage_data *data;
GtkWidget *dialog, *content_area, *bbox, *hbox, *vbox, *tbar;
GtkWidget *box, *treeview, *scrollwin;
GtkWidget *widget, *content;
gint w, h, dw, dh;
DB( g_print("\n[ui-asg-manage] dialog\n") );
data = g_malloc0(sizeof(struct ui_asg_manage_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Assignments"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" dialog=%p, inst_data=%p\n", dialog, data) );
//dialog content
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
content = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(content), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), content);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (content), hbox);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hbtk_box_prepend (GTK_BOX (hbox), box);
widget = make_search ();
data->ST_search = widget;
gtk_widget_set_size_request(widget, HB_MINWIDTH_SEARCH, -1);
gtk_widget_set_halign(widget, GTK_ALIGN_END);
gtk_box_prepend (GTK_BOX (hbox), widget);
// list + toolbar
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (content), vbox);
// listview
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = ui_asg_listview_new(FALSE);
data->LV_rul = treeview;
gtk_widget_set_size_request(treeview, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_rem = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
data->BT_edit = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DUPLICATE, _("Duplicate"));
data->BT_dup = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_MOVE_UP, _("Move up"));
data->BT_up = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_MOVE_DOWN, _("Move down"));
data->BT_down = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = ui_asg_popover_move_after_new(data);
data->BT_move = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_asg_manage_mapped), &dialog);
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_asg_manage_cb_on_key_press), (gpointer)data);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all(content);
gtk_widget_show (dialog);
// wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
ui_asg_manage_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/ui-category.h 0000644 0001750 0001750 00000006163 14736461415 016023 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_CATEGORY_GTK_H__
#define __HB_CATEGORY_GTK_H__
enum
{
LST_DEFCAT_TOGGLE,
LST_DEFCAT_DATAS,
NUM_LST_DEFCAT
};
#define LST_DEFCAT_SORT_NAME 1
#define LST_DEFCAT_SORT_USETXN 2
#define LST_DEFCAT_SORT_USECFG 3
//liststore
enum
{
STO_CAT_DATA,
STO_CAT_FULLNAME,
NUM_STO_CAT
};
enum
{
CAT_TYPE_ALL,
CAT_TYPE_EXPENSE,
CAT_TYPE_INCOME
};
/*
enum
{
LST_CMBCAT_DATAS,
LST_CMBCAT_FULLNAME,
LST_CMBCAT_SORTNAME,
LST_CMBCAT_NAME,
LST_CMBCAT_SUBCAT,
NUM_LST_CMBCAT
};
*/
//TODO: only used into vehicle
void ui_cat_entry_popover_clear(GtkBox *box);
void ui_cat_entry_popover_sort_type(GtkBox *box, guint type);
void ui_cat_entry_popover_add(GtkBox *box, Category *item);
GtkWidget *ui_cat_entry_popover_get_entry(GtkBox *box);
Category *ui_cat_entry_popover_get(GtkBox *box);
guint32 ui_cat_entry_popover_get_key_add_new(GtkBox *box);
guint32 ui_cat_entry_popover_get_key(GtkBox *box);
void ui_cat_entry_popover_set_active(GtkBox *box, guint32 key);
GtkWidget *ui_cat_entry_popover_new(GtkWidget *label);
/* = = = = = = = = = = */
guint ui_cat_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter);
void ui_cat_listview_quick_select(GtkTreeView *treeview, const gchar *uri);
void ui_cat_listview_add(GtkTreeView *treeview, Category *item, GtkTreeIter *parent);
Category *ui_cat_listview_get_selected(GtkTreeView *treeview);
Category *ui_cat_listview_get_selected_parent(GtkTreeView *treeview, GtkTreeIter *parent);
void ui_cat_listview_remove_selected(GtkTreeView *treeview);
void ui_cat_listview_populate(GtkWidget *view, gint type, gchar *needle, gboolean showhidden);
GtkWidget *ui_cat_listview_new(gboolean withtoggle, gboolean withcount);
/* = = = = = = = = = = */
struct ui_cat_manage_dialog_data
{
GList *tmp_list;
gboolean usagefilled;
gint change;
GtkWidget *dialog;
GActionGroup * actions;
gboolean mapped_done;
GtkWidget *BT_showhidden;
GtkWidget *BT_showusage;
GtkWidget *ST_search;
GtkWidget *LV_cat;
GtkWidget *RE_addreveal;
GtkWidget *ST_name1, *ST_name2;
//GtkWidget *CM_type;
GtkWidget *RA_type;
GtkWidget *BT_add;
GtkWidget *BT_edit;
GtkWidget *BT_merge;
GtkWidget *BT_delete;
GtkWidget *BT_hide;
GtkWidget *BT_expand;
GtkWidget *BT_collapse;
GtkWidget *LA_category;
};
struct catPopContext
{
GtkTreeModel *model;
guint except_key;
gint type;
};
GtkWidget *ui_cat_manage_dialog (void);
#endif
homebank-5.9.7/src/hbtk-decimalentry.h 0000664 0001750 0001750 00000005516 14774301055 017177 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2023 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HBTK_DECIMAL_ENTRY_H__
#define __HBTK_DECIMAL_ENTRY_H__
G_BEGIN_DECLS
#define HBTK_TYPE_DECIMAL_ENTRY (hbtk_decimal_entry_get_type ())
#define HBTK_DECIMAL_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HBTK_TYPE_DECIMAL_ENTRY, HbtkDecimalEntry))
#define HBTK_DECIMAL_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HBTK_TYPE_DECIMAL_ENTRY, HbtkDecimalEntryClass)
#define HBTK_IS_DECIMAL_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HBTK_TYPE_DECIMAL_ENTRY))
#define HBTK_IS_DECIMAL_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HBTK_TYPE_DECIMAL_ENTRY))
#define HBTK_DECIMAL_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), HBTK_TYPE_DECIMAL_ENTRY, HbtkDecimalEntryClass))
typedef struct _HbtkDecimalEntry HbtkDecimalEntry;
typedef struct _HbtkDecimalEntryClass HbtkDecimalEntryClass;
typedef struct _HbtkDecimalEntryPrivate HbtkDecimalEntryPrivate;
typedef enum {
OPERATION_OFF,
OPERATION_ADD,
OPERATION_SUB,
OPERATION_MUL,
OPERATION_DIV
} HbtkOperation;
struct _HbtkDecimalEntry
{
GtkBox box;
/*< private >*/
HbtkDecimalEntryPrivate *priv;
};
struct _HbtkDecimalEntryClass
{
GtkBoxClass parent_class;
/* signals */
void (* changed) (HbtkDecimalEntry *dateentry);
/* Padding for future expansion */
void (*_gtk_reserved0) (void);
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
};
struct _HbtkDecimalEntryPrivate
{
GtkWidget *entry;
gboolean forcedsign;
gboolean valid;
gdouble value;
guint digits : 10;
gulong hid_insert;
gulong hid_changed;
};
GType hbtk_decimal_entry_get_type(void) G_GNUC_CONST;
GtkWidget *hbtk_decimal_entry_new(GtkWidget *label);
gdouble hbtk_decimal_entry_get_value (HbtkDecimalEntry *decimalentry);
gboolean hbtk_decimal_entry_get_forcedsign (HbtkDecimalEntry *decimalentry);
void hbtk_decimal_entry_set_value (HbtkDecimalEntry *decimalentry, gdouble value);
void hbtk_decimal_entry_set_digits (HbtkDecimalEntry *decimalentry, guint value);
G_END_DECLS
#endif /* __HBTK_DECIMAL_ENTRY_H__ */
homebank-5.9.7/src/ui-widgets.h 0000644 0001750 0001750 00000016375 15032526067 015655 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_WIDGETS_GTK_H__
#define __HB_WIDGETS_GTK_H__
typedef struct _hbtk_kv_data HbKvData;
typedef struct _hbtk_kiv_data HbKivData;
struct _hbtk_kv_data {
guint32 key;
const gchar *name;
};
struct _hbtk_kiv_data {
guint32 key;
const gchar *iconname;
const gchar *name;
};
#define HBTK_IS_SEPARATOR -66
typedef enum {
DATE_RANGE_FLAG_NONE = 0,
DATE_RANGE_FLAG_BUDGET_MODE = 1 << 1,
DATE_RANGE_FLAG_CUSTOM_HIDDEN = 1 << 8,
DATE_RANGE_FLAG_CUSTOM_DISABLE = 1 << 9
} HbDateRangeFlags;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* GTK4 transitional anticipation */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if( (GTK_MAJOR_VERSION < 4) )
void gtk_window_set_child (GtkWindow* window, GtkWidget* child);
void gtk_popover_set_child (GtkPopover* popover, GtkWidget* child);
void gtk_frame_set_child (GtkFrame* frame, GtkWidget* child);
void gtk_overlay_set_child (GtkOverlay* overlay, GtkWidget* child);
void gtk_scrolled_window_set_child (GtkScrolledWindow* scrolled_window, GtkWidget* child);
void gtk_revealer_set_child (GtkRevealer* revealer, GtkWidget* child);
void gtk_expander_set_child (GtkExpander* expander, GtkWidget* child);
void gtk_box_prepend (GtkBox* box, GtkWidget* child);
void gtk_box_append (GtkBox* box, GtkWidget* child);
void gtk_box_prependfe (GtkBox* box, GtkWidget* child);
void gtk_window_destroy (GtkWindow* window);
#endif
GtkWidget *hbtk_image_new_from_icon_name_16(const gchar *icon_name);
GtkWidget *hbtk_image_new_from_icon_name_24(const gchar *icon_name);
GtkWidget *hbtk_image_new_from_icon_name_32(const gchar *icon_name);
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void hbtk_box_prepend (GtkBox* box, GtkWidget* child);
GtkWidget *make_label(gchar *str, gfloat xalign, gfloat yalign);
GtkWidget *make_clicklabel(gchar *id, gchar *str);
GtkWidget *make_label_group(gchar *str);
GtkWidget *make_label_left(char *str);
GtkWidget *make_label_widget(gchar *str);
GtkWidget *make_text(gfloat xalign);
GtkWidget *make_search(void);
GtkWidget *make_string(GtkWidget *label);
GtkWidget *hbtk_menubar_add_menu(GtkWidget *menubar, gchar *label, GtkWidget **menuitem_ptr);
GtkWidget *hbtk_menu_add_menuitem(GtkWidget *menu, gchar *label);
GtkWidget *hbtk_toolbar_add_toolbutton(GtkToolbar *toolbar, gchar *icon_name, gchar *label, gchar *tooltip_text);
GtkWidget *make_image_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_image_toggle_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_image_radio_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_image_button2(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_image_toggle_button2(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_tb(void);
GtkWidget *make_tb_separator(void);
GtkWidget *make_tb_image_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_tb_image_toggle_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_tb_image_radio_button(gchar *icon_name, gchar *tooltip_text);
GtkWidget *make_memo_entry(GtkWidget *label);
GtkWidget *make_string_maxlength(GtkWidget *label, guint max_length);
GtkWidget *make_entry_numeric(GtkWidget *label, gint min, gint max);
GtkWidget *make_amount(GtkWidget *label);
GtkWidget *make_amount_pos(GtkWidget *label);
GtkWidget *make_exchange_rate(GtkWidget *label);
GtkWidget *make_numeric(GtkWidget *label, gdouble min, gdouble max);
GtkWidget *make_scrolled_window_ns(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy);
GtkWidget *make_scrolled_window(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy);
GtkWidget *make_scale(GtkWidget *label);
GtkWidget *make_long(GtkWidget *label);
gchar *hb_get_scheduled_unit(gint unit);
guint32 hbtk_monthyear_getmin(GtkSpinButton *spin);
guint32 hbtk_monthyear_getmax(GtkSpinButton *spin);
void hbtk_monthyear_set(GtkSpinButton *spin, guint32 julian);
GtkWidget *make_monthyear(GtkWidget *label);
//GtkWidget *make_year(GtkWidget *label);
GtkWidget *make_daterange(GtkWidget *label, HbDateRangeFlags flags);
GtkWidget *create_popover (GtkWidget *parent, GtkWidget *child, GtkPositionType pos);
void ui_label_set_integer(GtkLabel *label, gint value);
void hbtk_listview_redraw_selected_row(GtkTreeView *treeview);
gboolean hbtk_tree_store_get_top_level(GtkTreeModel *model, gint column_id, guint32 key, GtkTreeIter *return_iter);
void hbtk_tree_store_remove_iter_with_child(GtkTreeModel *model, GtkTreeIter *iter);
GtkTreeViewColumn *hbtk_treeview_get_column_by_id(GtkTreeView *treeview, gint search_id);
gchar *hbtk_get_label(HbKvData *kvdata, guint32 key);
guint32 hbtk_combo_box_get_active_id (GtkComboBox *combobox);
void hbtk_combo_box_set_active_id (GtkComboBox *combobox, guint32 active_id);
void hbtk_combo_box_text_append (GtkComboBox *combobox, guint32 key, gchar *text);
GtkWidget *hbtk_combo_box_new (GtkWidget *label);
GtkWidget *hbtk_combo_box_new_with_data (GtkWidget *label, HbKvData *kvdata);
GtkWidget *hbtk_combo_box_new_with_array (GtkWidget *label, gchar **items);
#ifdef G_OS_WIN32
void hbtk_assistant_hack_button_order(GtkAssistant *assistant);
#endif
void gimp_label_set_attributes (GtkLabel *label, ...);
void hb_window_run_pending(void);
void hb_widget_set_margins(GtkWidget *widget, gint top, gint right, gint bottom, gint left);
void hb_widget_set_margin(GtkWidget *widget, gint margin);
void hb_widget_visible(GtkWidget *widget, gboolean visible);
void hbtk_entry_tag_name_append(GtkEntry *entry, gchar *tagname);
void hbtk_entry_set_text(GtkEntry *entry, gchar *text);
gboolean hbtk_entry_replace_text(GtkEntry *entry, gchar **storage);
/*
guint make_popaccount_populate(GtkComboBox *combobox, GList *srclist);
GtkWidget *make_popaccount(GtkWidget *label);
guint make_poppayee_populate(GtkComboBox *combobox, GList *srclist);
GtkWidget *make_poppayee(GtkWidget *label);
guint make_poparchive_populate(GtkComboBox *combobox, GList *srclist);
GtkWidget *make_poparchive(GtkWidget *label);
guint make_popcategory_populate(GtkComboBox *combobox, GList *srclist);
GtkWidget *make_popcategory(GtkWidget *label);
*/
gint hb_clicklabel_to_int(const gchar *uri);
const gchar *get_paymode_icon_name(guint32 key);
const gchar *get_grpflag_icon_name(guint32 key);
guint32 kiv_combo_box_get_active (GtkComboBox *combo_box);
void kiv_combo_box_set_active (GtkComboBox *combo_box, guint32 active_key);
void paymode_list_get_order(GtkTreeView *treeview);
GtkWidget *make_fltgrpflag(GtkWidget *label);
GtkWidget *make_paymode_list(void);
GtkWidget *make_paymode(GtkWidget *label);
#endif
homebank-5.9.7/src/icon-names.h 0000664 0001750 0001750 00000016625 14736464104 015630 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* -------- named icons (Standard Icon Name) -------- */
//obsolete, as since since gtk3.10 : no more icons for dialogs and menu
/*
#define ICONNAME_SAVE_AS "document-save-as" //obsolete
#define ICONNAME_REVERT "document-revert" //obsolete
#define ICONNAME_PROPERTIES "document-properties" //obsolete
#define ICONNAME_CLOSE "window-close" //obsolete
#define ICONNAME_QUIT "application-exit" //obsolete
#define ICONNAME_HELP "help-browser" //obsolete
#define ICONNAME_ABOUT "help-about" //obsolete
#define ICONNAME_PREFERENCES "preferences-system" //obsolete
*/
//#define ICONNAME_FIND "edit-find" //unused
//#define ICONNAME_CLEAR "edit-clear" //unused
//#define ICONNAME_HB_SCHED_SKIP "media-skip-forward"
//#define ICONNAME_HB_SCHED_POST "media-playback-start"
//in 5.2 no themeable icon to keep a consistent iconset
#define ICONNAME_WARNING "dialog-warning"
#define ICONNAME_ERROR "dialog-error"
#define ICONNAME_INFO "dialog-information"
#define ICONNAME_FOLDER "folder-symbolic"
#define ICONNAME_EMBLEM_OK "emblem-ok-symbolic"
#define ICONNAME_EMBLEM_SYSTEM "emblem-system-symbolic"
#define ICONNAME_WINDOW_CLOSE "window-close-symbolic"
#define ICONNAME_LIST_ADD "list-add-symbolic"
#define ICONNAME_LIST_EDIT "document-edit-symbolic"
#define ICONNAME_LIST_DUPLICATE "list-duplicate-symbolic"
#define ICONNAME_LIST_DELETE "list-remove-symbolic"
#define ICONNAME_LIST_DELETE_ALL "list-remove-all-symbolic"
#define ICONNAME_LIST_MOVE_UP "hb-go-up-symbolic"
#define ICONNAME_LIST_MOVE_DOWN "hb-go-down-symbolic"
#define ICONNAME_LIST_MOVE_AFTER "list-move-after-symbolic"
#define ICONNAME_SYSTEM_SEARCH "system-search-symbolic"
// custom or gnome not found
#define ICONNAME_HB_BUTTON_MENU "open-menu-symbolic"
#define ICONNAME_HB_BUTTON_BROWSER "open-in-browser-symbolic"
#define ICONNAME_HB_BUTTON_COLLAPSE "list-collapse-all-symbolic"
#define ICONNAME_HB_BUTTON_EXPAND "list-expand-all-symbolic"
#define ICONNAME_HB_BUTTON_SPLIT "edit-split-symbolic"
#define ICONNAME_HB_BUTTON_DELETE "edit-delete-symbolic"
#define ICONNAME_HB_TOGGLE_SIGN "toggle-sign-symbolic"
#define ICONNAME_HB_LIST_MERGE "list-merge-symbolic"
#define ICONNAME_HB_BUTTON_HIDE "eye-not-looking-symbolic"
#define ICONNAME_HB_BUTTON_USAGE "data-usage-symbolic"
#define ICONNAME_HB_TEXT_CASE "text-casesensitive-symbolic"
#define ICONNAME_HB_TEXT_REGEX "text-regularexpression-symbolic"
/* -------- named icons (Custom to homebank) -------- */
#define ICONNAME_HB_CURRENCY "hb-currency"
#define ICONNAME_HB_ACCOUNT "hb-account"
#define ICONNAME_HB_ARCHIVE "hb-archive"
#define ICONNAME_HB_ASSIGN "hb-assign"
#define ICONNAME_HB_BUDGET "hb-budget"
#define ICONNAME_HB_CATEGORY "hb-category"
#define ICONNAME_HB_PAYEE "hb-payee"
#define ICONNAME_HB_OPE_SHOW "hb-ope-show" //? "view-register
#define ICONNAME_HB_OPE_FUTURE "hb-ope-future"
#define ICONNAME_HB_REP_STATS "hb-rep-stats"
#define ICONNAME_HB_REP_TIME "hb-rep-time"
#define ICONNAME_HB_REP_BALANCE "hb-rep-balance"
#define ICONNAME_HB_REP_BUDGET "hb-rep-budget"
#define ICONNAME_HB_REP_CAR "hb-rep-vehicle"
#define ICONNAME_HB_HELP "hb-help"
#define ICONNAME_HB_DONATE "hb-donate"
#define ICONNAME_HB_VIEW_LIST "hb-view-list" //"view-list-text"
#define ICONNAME_HB_VIEW_BAR "hb-view-bar" //"view-chart-bar"
#define ICONNAME_HB_VIEW_COLUMN "hb-view-column" //"view-chart-column"
#define ICONNAME_HB_VIEW_LINE "hb-view-line" //"view-chart-line"
#define ICONNAME_HB_VIEW_PROGRESS "hb-view-progress" //"view-chart-progress"
#define ICONNAME_HB_VIEW_PIE "hb-view-pie" //"view-chart-pie"
#define ICONNAME_HB_VIEW_DONUT "hb-view-donut" //"view-chart-donut"
#define ICONNAME_HB_VIEW_STACK "hb-view-stack" //"view-chart-stack"
#define ICONNAME_HB_VIEW_STACK100 "hb-view-stack100" //"view-chart-stack100"
#define ICONNAME_HB_SHOW_LEGEND "hb-legend" //"view-legend"
#define ICONNAME_HB_SHOW_RATE "hb-rate" // obsolete ?
#define ICONNAME_HB_REFRESH "hb-view-refresh" //"view-refresh"
#define ICONNAME_HB_FILTER "hb-filter" //"edit-filter"
#define ICONNAME_HB_CLEAR "hb-clear" //"edit-clear"
#define ICONNAME_HB_LIFEENERGY "hb-life-energy"
#define ICONNAME_CHANGES_PREVENT "hb-changes-prevent"
#define ICONNAME_CHANGES_ALLOW "hb-changes-allow"
#define ICONNAME_HB_QUICKTIPS "hb-quicktips" //quick help tips
#define ICONNAME_HB_FILE_NEW "hb-document-new" //document-new
#define ICONNAME_HB_FILE_OPEN "hb-document-open" //document-open
#define ICONNAME_HB_FILE_SAVE "hb-document-save" //document-save
#define ICONNAME_HB_FILE_IMPORT "hb-file-import" //document-import
#define ICONNAME_HB_FILE_EXPORT "hb-file-export" //document-export
#define ICONNAME_HB_FILE_VALID "hb-file-valid"
#define ICONNAME_HB_FILE_INVALID "hb-file-invalid"
#define ICONNAME_HB_PRINT "hb-document-print"
#define ICONNAME_HB_OPE_MOVUP "hb-go-up"
#define ICONNAME_HB_OPE_MOVDW "hb-go-down"
#define ICONNAME_HB_OPE_ADD "hb-ope-add" //? "edit-add"
#define ICONNAME_HB_OPE_HERIT "hb-ope-herit" //? "edit-clone"
#define ICONNAME_HB_OPE_EDIT "hb-ope-edit" //
#define ICONNAME_HB_OPE_MULTIEDIT "hb-ope-multiedit" //
#define ICONNAME_HB_OPE_CLEARED "hb-ope-cleared"
#define ICONNAME_HB_OPE_RECONCILED "hb-ope-reconciled"
#define ICONNAME_HB_OPE_DELETE "hb-ope-delete" //? "edit-delete"
//#define ICONNAME_CONVERT "hb-ope-convert" // obsolete ?
//#define ICONNAME_HB_ASSIGN_RUN "hb-assign-run" // obsolete ?
/* -- status ope icons */
#define ICONNAME_HB_ITEM_CLEAR "hb-item-clear"
#define ICONNAME_HB_ITEM_RECON "hb-item-recon"
#define ICONNAME_HB_ITEM_RECONLOCK "hb-item-reconlock"
#define ICONNAME_HB_ITEM_VOID "hb-item-void"
/* -- listview icons -- */
//#define ICONNAME_HB_ITEM_NONE "hb-item-none"
#define ICONNAME_HB_ITEM_CLOSED "hb-item-closed"
#define ICONNAME_HB_ITEM_ADDED "hb-item-added" //"hb-ope-new"
#define ICONNAME_HB_ITEM_EDITED "hb-item-edited"
#define ICONNAME_HB_ITEM_AUTO "hb-item-auto" //"hb-ope-auto" //?
#define ICONNAME_HB_ITEM_BUDGET "hb-item-budget" //"hb-ope-budget" //?
#define ICONNAME_HB_ITEM_FORCED "hb-item-forced" //"hb-ope-forced" //?
#define ICONNAME_HB_ITEM_REMIND "hb-item-remind"
#define ICONNAME_HB_ITEM_SIMILAR "hb-item-similar"
#define ICONNAME_HB_ITEM_PREFILLED "hb-item-prefilled"
#define ICONNAME_HB_ITEM_FUTURE "hb-item-future"
//5.9
#define ICONNAME_HB_ITEM_IMPORT "hb-item-import"
#define ICONNAME_HB_ITEM_PAST "hb-item-pending"
#define ICONNAME_HB_PM_INTXFER "hb-pm-intransfer"
homebank-5.9.7/src/rep-balance.c 0000644 0001750 0001750 00000136542 15005633616 015736 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "rep-balance.h"
#include "list-operation.h"
#include "gtk-chart.h"
#include "gtk-dateentry.h"
#include "ui-account.h"
#include "dsp-mainwindow.h"
#include "ui-transaction.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* prototypes */
static GtkWidget *lst_repbal_create(void);
static void lst_repbal_set_cur(GtkTreeView *treeview, guint32 kcur);
static GString *lst_repbal_to_string(ToStringMode mode, GtkTreeView *treeview, gchar *title);
//extern gchar *CYA_REPORT_MODE[];
extern HbKvData CYA_REPORT_INTVL[];
/* = = = = = = = = = = = = = = = = */
static gchar *
repbalance_compute_title(gint intvl)
{
gchar *title;
//TRANSLATORS: example 'Balance by Month'
title = g_strdup_printf(_("Balance by %s"), hbtk_get_label(CYA_REPORT_INTVL, intvl) );
return title;
}
static void repbalance_sensitive(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gboolean visible;
gint page;
DB( g_print(" \n[rep-bal] sensitive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
visible = page == 0 ? TRUE : FALSE;
hb_widget_visible (data->BT_detail, visible);
//sensitive = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), NULL, NULL);
//gtk_action_set_sensitive(action, sensitive);
visible = page == 0 ? FALSE : TRUE;
//5.7
//hb_widget_visible (data->BT_print, visible);
hb_widget_visible(data->LB_zoomx, visible);
hb_widget_visible(data->RG_zoomx, visible);
}
static void repbalance_update_daterange(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gchar *daterange;
DB( g_print("\n[rep-bal] update daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
daterange = filter_daterange_text_get(data->filter);
gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
g_free(daterange);
}
static void repbalance_update_info(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gchar *info;
gchar buf[128];
DB( g_print("\n[rep-bal] update info\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
gboolean minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
hb_strfmon(buf, 127, data->minimum, data->usrkcur, minor);
////TRANSLATORS: count of transaction in overdrawn / count of total transaction under overdrawn amount threshold
info = g_strdup_printf(_("%d/%d under %s"), data->nbovrdraft, data->nbope, buf);
gtk_label_set_text(GTK_LABEL(data->TX_info), info);
g_free(info);
}
static void repbalance_detail(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
guint active = GPOINTER_TO_INT(user_data);
guint tmpintvl;
GList *list;
GtkTreeModel *model;
GtkTreeIter iter, child;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[rep-bal] detail\n") );
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
/* clear and detach our model */
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if(data->detail && data->txn_queue)
{
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_detail), NULL); /* Detach model from view */
/* fill in the model */
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
guint i, pos;
if( (ope->date >= data->filter->mindate) && (ope->date <= data->filter->maxdate) )
{
//#1907699 date is wrong
//pos = report_interval_get_pos(tmpintvl, data->jbasedate, ope);
//#1970020 but with #1958001 fix, it is
pos = report_interval_get_pos(tmpintvl, data->filter->mindate, ope);
DB( g_print(" get '%s', pos=%d act=%d\n", ope->memo, pos, active) );
//filter here
//if( pos == active && (ope->kacc == acckey || selectall) )
if( pos == active )
{
DB( g_print(" insert\n") );
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model),
&iter, NULL, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITAMT, ope->amount,
-1);
//#1875801 show split detail
if( ope->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(ope->splits);
for(i=0;isplits, i);
gtk_tree_store_insert_with_values (GTK_TREE_STORE(model), &child, &iter, -1,
MODEL_TXN_POINTER, ope,
MODEL_TXN_SPLITPTR, split,
-1);
}
}
}
}
list = g_list_next(list);
}
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
g_object_unref(model);
gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
}
}
static guint32
_data_compute_currency(struct repbalance_data *data)
{
GList *lst_acc, *lnk_acc;
guint32 lastcurr = GLOBALS->kcur;
guint count = 0;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *accitem = lnk_acc->data;
if( da_flt_status_acc_get(data->filter, accitem->key) == TRUE )
{
if( count == 0 )
lastcurr = accitem->kcur;
else if( accitem->kcur != lastcurr )
{
lastcurr = GLOBALS->kcur;
goto end;
}
count++;
}
lnk_acc = g_list_next(lnk_acc);
}
end:
g_list_free(lst_acc);
return lastcurr;
}
static void
_data_collect_txn(struct repbalance_data *data)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
gboolean inclxfer;
guint usrnbacc;
DB( g_print("\n- - - - - - - -\n[rep-bal] collect txn\n") );
//clear all
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
data->txn_queue = NULL;
data->firstbalance = 0.0;
//#2019876 return is invalid date range
if( data->filter->maxdate < data->filter->mindate )
return;
//grab user selection
inclxfer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_inclxfer));
//as it is not the filter dialog, count
//todo, we could count into filter other func
da_flt_count_item(data->filter);
usrnbacc = data->filter->n_item[FLT_GRP_ACCOUNT];
//todo: find another way to define mono/multi currency
data->usrkcur = _data_compute_currency(data);
if(usrnbacc == 1)
inclxfer = TRUE;
//5.8 fake filter
filter_preset_type_set(data->filter, FLT_TYPE_ALL, FLT_OFF);
if( inclxfer == FALSE )
filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
DB( g_print(" usr: n_acc=%d, kcur=%d, incxfer=%d\n", usrnbacc, data->usrkcur, inclxfer) );
if( (usrnbacc == 0) )
goto end;
//collect
DB( g_print(" -- collect txn\n") );
data->txn_queue = g_queue_new ();
//grab data from user selected account
//+ compute sum of initialbalance
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
gdouble amount = 0.0;
DB( g_print(" eval acc=%d '%s' >> fltincl=%d\n", acc->key, acc->name, da_flt_status_acc_get(data->filter, acc->key) ) );
// avoid unselected and noreport (maybe already filtered into list)
if( (acc->flags & (AF_NOREPORT)) || (da_flt_status_acc_get(data->filter, acc->key) == FALSE) )
goto next_acc;
// set minimum (only used when 1 account is selected)
data->minimum = acc->minimum;
// if more than 1 cur get amount as base currency
if( (data->usrkcur != acc->kcur) )
amount = hb_amount_base(acc->initial, acc->kcur);
else
amount = acc->initial;
data->firstbalance += amount;
DB( g_print(" - stored initial %.2f for account %d:%s\n", amount, acc->key, acc->name) );
//collect every txn for account
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
//5.5 forgot to filter...
//#1886123 include remind based on user prefs
if( !transaction_is_balanceable(txn) )
goto next_txn;
//#2045514 xclude xfer from selected account
//if( usrnbacc > 1 && da_flt_status_acc_get(data->filter, txn->kacc) != 0 && data->tmp_acckeys[txn->kxferacc] != 0 )
if( (inclxfer == FALSE)
&& (da_flt_status_acc_get(data->filter, txn->kacc) == TRUE )
&& (da_flt_status_acc_get(data->filter, txn->kxferacc) == TRUE)
)
goto next_txn;
//#2104162 compute 1stbalance here
// if more than 1 cur get amount as base currency
if( (data->usrkcur != txn->kcur) )
amount = hb_amount_base(txn->amount, txn->kcur);
else
amount = txn->amount;
// cumulate pre-date range balance
if( (txn->date < data->filter->mindate) )
data->firstbalance += amount;
g_queue_push_head (data->txn_queue, txn);
next_txn:
lnk_txn = g_list_next(lnk_txn);
}
next_acc:
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
end:
DB( g_print(" - first balance %.2f\n", data->firstbalance) );
}
static void repbalance_compute(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gint tmpintvl;
gboolean showempty;
guint32 i;
GtkTreeModel *model;
GtkTreeIter iter;
//Account *acc;
gint usrnbacc;
DB( g_print("\n- - - - - - - -\n[rep-bal] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//clear all
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
gtk_list_store_clear (GTK_LIST_STORE(model));
gtk_chart_set_datas_none(GTK_CHART(data->RE_chart));
//reset our data
data->nbope = 0;
data->nbovrdraft = 0;
repbalance_update_info(widget, user_data);
//test 5.8
da_flt_count_item(data->filter);
usrnbacc = data->filter->n_item[FLT_GRP_ACCOUNT];
gchar *txt = filter_text_summary_get(data->filter);
ui_label_set_integer(GTK_LABEL(data->TX_fltactive), data->filter->n_active);
gtk_widget_set_tooltip_text(data->TT_fltactive, txt);
g_free(txt);
//compute
if( (!data->txn_queue) || g_queue_get_length(data->txn_queue) == 0)
goto end;
DB( g_print(" -- compute\n") );
//grab user selection
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
showempty = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_showempty));
// grab nb result and allocate memory
//#1958001
data->n_result = report_interval_count(tmpintvl, data->filter->mindate, data->filter->maxdate);
data->tmp_income = g_malloc0((data->n_result+2) * sizeof(gdouble));
data->tmp_expense = g_malloc0((data->n_result+2) * sizeof(gdouble));
DB( g_print(" %d days in selection\n", data->filter->maxdate - data->filter->mindate));
DB( hb_print_date(data->filter->mindate, "min") );
DB( hb_print_date(data->filter->maxdate, "max") );
if(data->tmp_income && data->tmp_expense)
{
GList *list;
gdouble amount;
/* sort by date & compute the balance */
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
if( (da_flt_status_acc_get(data->filter, ope->kacc) == TRUE) )
{
// if more than 1 cur get amount as base currency
if( (data->usrkcur != ope->kcur) )
amount = hb_amount_base(ope->amount, ope->kcur);
else
amount = ope->amount;
//check: this should be useless as filtered in collect_txn
if( (ope->date >= data->filter->mindate) && (ope->date <= data->filter->maxdate) )
{
gint pos = report_interval_get_pos(tmpintvl, data->filter->mindate, ope);
//deal with transactions
if(amount < 0)
data->tmp_expense[pos] += amount;
else
data->tmp_income[pos] += amount;
}
}
list = g_list_next(list);
}
}
// set currency
lst_repbal_set_cur(GTK_TREE_VIEW(data->LV_report), data->usrkcur);
gtk_chart_set_currency(GTK_CHART(data->RE_chart), data->usrkcur);
//populate
DB( g_print(" -- populate\n") );
// ref and detach model for speed
g_object_ref(model);
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), NULL);
DB( g_print(" inserting %d results\n", data->n_result) );
gdouble balance = data->firstbalance;
//#1958001
for(i=0;in_result;i++)
{
gboolean is_ovrdraft = FALSE;
gchar intvlname[64];
balance += data->tmp_expense[i];
balance += data->tmp_income[i];
if( !showempty && (data->tmp_expense[i] == 0 && data->tmp_income[i] == 0) )
continue;
if(usrnbacc == 1)
is_ovrdraft = (balance < data->minimum) ? TRUE : FALSE;
//#1907699 date is wrong
report_interval_snprint_name(intvlname, sizeof(intvlname)-1, tmpintvl, data->filter->mindate, i);
//report_interval_snprint_name(intvlname, sizeof(intvlname)-1, tmpintvl, jmindate, i);
DB( g_print(" %3d: %s %f %f\n", i, intvlname, data->tmp_expense[i], data->tmp_income[i]) );
/* column 0: pos (gint) */
/* not used: column 1: key (gint) */
/* column 2: name (gchar) */
/* column x: values (double) */
gtk_list_store_insert_with_values (GTK_LIST_STORE(model),
&iter, -1,
LST_OVER_POS, i,
LST_OVER_KEY, i,
LST_OVER_LABEL, intvlname,
LST_OVER_EXPENSE, data->tmp_expense[i],
LST_OVER_INCOME , data->tmp_income[i],
LST_OVER_TOTAL, balance,
LST_OVER_FLAGS, (is_ovrdraft == TRUE) ? REPORT_FLAG_OVER : 0,
-1);
data->nbope++;
if(is_ovrdraft == TRUE)
data->nbovrdraft++;
}
gboolean visible = (usrnbacc > 1) ? FALSE : TRUE;
gtk_widget_set_visible (GTK_WIDGET(data->CM_inclxfer), !visible);
gtk_widget_set_visible (GTK_WIDGET(data->TX_info), visible);
gtk_chart_show_overdrawn(GTK_CHART(data->RE_chart), visible);
if( visible == TRUE )
{
repbalance_update_info(widget, NULL);
gtk_chart_set_overdrawn(GTK_CHART(data->RE_chart), data->minimum);
}
//5.8.6 update column 0 title
GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
if(column)
gtk_tree_view_column_set_title(column, hbtk_get_label(CYA_REPORT_INTVL, tmpintvl) );
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
g_object_unref(model);
/* update bar chart */
gchar *title = repbalance_compute_title(tmpintvl);
gtk_chart_set_datas_total(GTK_CHART(data->RE_chart), model, LST_OVER_TOTAL, LST_OVER_TOTAL, title, NULL);
//gtk_chart_set_line_datas(GTK_CHART(data->RE_chart), model, LST_OVER_TOTAL, LST_OVER_DATE);
g_free(title);
end:
g_free(data->tmp_expense);
g_free(data->tmp_income);
data->tmp_expense = NULL;
data->tmp_income = NULL;
}
//reset the filter
static void repbalance_filter_setup(struct repbalance_data *data)
{
guint32 accnum;
DB( g_print("\n[rep-bal] filter setup\n") );
filter_reset(data->filter);
filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
data->filter->option[FLT_GRP_ACCOUNT] = 1;
//5.6 set default account
accnum = data->accnum;
if(!accnum)
{
accnum = da_acc_get_first_key();
}
DB( g_print(" accnum=%d\n", accnum) );
ui_acc_listview_set_active(GTK_TREE_VIEW(data->LV_acc), accnum);
ui_acc_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_acc), data->filter);
}
/* = = = = = = = = = = = = = = = = */
//beta
#if PRIV_FILTER
static void repbalance_action_filter(GtkWidget *toolbutton, gpointer user_data)
{
struct repbalance_data *data = user_data;
//debug
//create_deffilter_window(data->filter, TRUE);
if(ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, TRUE, FALSE) != GTK_RESPONSE_REJECT)
{
//repbalance_compute(data->window, NULL);
//ui_repdtime_update_date_widget(data->window, NULL);
//ui_repdtime_update_daterange(data->window, NULL);
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_ALLDATE);
}
}
#endif
static void repbalance_action_viewlist(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 0);
repbalance_sensitive(data->window, NULL);
}
/*static void repbalance_action_mode_changed(GtkWidget *toolbutton, gpointer user_data)
{
struct repbalance_data *data = user_data;
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(data->BT_list), TRUE);
repbalance_action_viewlist(toolbutton, data);
}
*/
static void repbalance_action_viewline(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data = user_data;
gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
repbalance_sensitive(data->window, NULL);
}
static void repbalance_action_print(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data = user_data;
gint tmpintvl, page;
gchar *title, *name;
tmpintvl = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_intvl));
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
name = g_strdup_printf("hb-repbalance_%s.csv", hbtk_get_label(CYA_REPORT_INTVL, tmpintvl) );
if( page == 0 )
{
GString *node;
title = repbalance_compute_title(tmpintvl);
node = lst_repbal_to_string(HB_STRING_PRINT, GTK_TREE_VIEW(data->LV_report), title);
hb_print_listview(GTK_WINDOW(data->window), node->str, NULL, title, name, FALSE);
g_string_free(node, TRUE);
g_free(title);
}
else
{
gtk_chart_print(GTK_CHART(data->RE_chart), GTK_WINDOW(data->window), NULL, name);
}
}
static void repbalance_cb_filter_changed(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
DB( g_print("\n[rep-bal] cb filter changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
_data_collect_txn(data);
repbalance_compute(data->window, NULL);
}
static void repbalance_action_filter_reset(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
DB( g_print("\n[rep-bal] filter reset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//TODO: to review: clean selection
ui_acc_listview_quick_select(GTK_TREE_VIEW(data->LV_acc), "non");
repbalance_filter_setup(data);
g_signal_handler_block(data->CY_range, data->hid[HID_REPBALANCE_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPBALANCE_RANGE]);
repbalance_cb_filter_changed(widget, user_data);
}
static void repbalance_date_change(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
DB( g_print("\n[rep-bal] date minmax change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
// set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->CY_range, data->hid[HID_REPBALANCE_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->hid[HID_REPBALANCE_RANGE]);
repbalance_update_daterange(widget, NULL);
repbalance_cb_filter_changed(widget, user_data);
}
static void repbalance_range_change(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gint range;
DB( g_print("\n[rep-bal] date range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
//should never happen
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, 0);
}
//#2046032 set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->PO_mindate, data->hid[HID_REPBALANCE_MINDATE]);
g_signal_handler_block(data->PO_maxdate, data->hid[HID_REPBALANCE_MAXDATE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
g_signal_handler_unblock(data->PO_mindate, data->hid[HID_REPBALANCE_MINDATE]);
g_signal_handler_unblock(data->PO_maxdate, data->hid[HID_REPBALANCE_MAXDATE]);
repbalance_update_daterange(widget, NULL);
repbalance_cb_filter_changed(widget, user_data);
}
static void
repbalance_cb_acc_changed(GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data)
{
struct repbalance_data *data = user_data;
ui_acc_listview_toggle_to_filter(GTK_TREE_VIEW(data->LV_acc), data->filter);
repbalance_cb_filter_changed(data->window, user_data);
}
static gboolean
repbalance_cb_acc_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
struct repbalance_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(label), GTK_TYPE_WINDOW)), "inst_data");
ui_acc_listview_quick_select(GTK_TREE_VIEW(data->LV_acc), uri);
repbalance_cb_acc_changed(NULL, NULL, data);
return TRUE;
}
static void
repbalance_detail_onRowActivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
struct repbalance_data *data;
Transaction *active_txn;
gboolean result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print ("\n[rep-bal] A detail row has been double-clicked!\n") );
active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_detail));
if(active_txn)
{
Transaction *old_txn, *new_txn;
//#1909749 skip reconciled if lock is ON
if( PREFS->safe_lock_recon == TRUE && active_txn->status == TXN_STATUS_RECONCILED )
return;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path = NULL;
//1936806 keep the selection
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
path = gtk_tree_model_get_path(model, &iter);
}
//#1640885
GLOBALS->changes_count++;
repbalance_compute (data->window, NULL);
if( path != NULL )
{
gtk_tree_selection_select_path(treeselection, path);
gtk_tree_path_free(path);
}
}
da_transaction_free (old_txn);
}
}
static void repbalance_update_detail(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#2018039
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_detail), PREFS->safe_lock_recon);
if(data->detail)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
guint key;
treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_OVER_KEY, &key, -1);
DB( g_print(" - active is %d\n", key) );
repbalance_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
}
gtk_widget_show(data->GR_detail);
}
else
gtk_widget_hide(data->GR_detail);
}
static void repbalance_toggle_detail(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->detail ^= 1;
DB( g_print("\n[rep-bal] toggledetail to %d\n", (int)data->detail) );
repbalance_update_detail(widget, user_data);
}
static void repbalance_cb_zoomx_changed(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
gdouble value;
DB( g_print("(\n[rep-bal] zoomx\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
value = gtk_range_get_value(GTK_RANGE(data->RG_zoomx));
DB( g_print(" + scale is %.2f\n", value) );
gtk_chart_set_barw(GTK_CHART(data->RE_chart), value);
}
static void repbalance_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct repbalance_data *data;
DB( g_print("\n[rep-bal] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
repbalance_update_info(widget,NULL);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
gtk_chart_show_minor(GTK_CHART(data->RE_chart), GLOBALS->minor);
gtk_chart_queue_redraw(GTK_CHART(data->RE_chart));
}
static void repbalance_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
guint key = -1;
DB( g_print("\n[rep-bal] selection\n") );
if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_OVER_KEY, &key, -1);
}
DB( g_print(" - active is %d\n", key) );
repbalance_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
}
/* = = = = = = = = = = = = = = = = */
static GtkWidget *
repbalance_toolbar_create(struct repbalance_data *data)
{
GtkWidget *toolbar, *button;
toolbar = gtk_toolbar_new();
button = (GtkWidget *)gtk_radio_tool_button_new(NULL);
data->BT_list = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LIST, "label", _("List"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as list"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = (GtkWidget *)gtk_radio_tool_button_new_from_widget(GTK_RADIO_TOOL_BUTTON(button));
data->BT_line = button;
g_object_set (button, "icon-name", ICONNAME_HB_VIEW_LINE, "label", _("Line"), NULL);
gtk_widget_set_tooltip_text(button, _("View results as lines"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
button = gtk_widget_new(GTK_TYPE_TOGGLE_TOOL_BUTTON,
"icon-name", ICONNAME_HB_OPE_SHOW,
"label", _("Detail"),
"tooltip-text", _("Toggle detail"),
NULL);
data->BT_detail = button;
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REFRESH, _("Refresh"), _("Refresh results"));
data->BT_refresh = button;
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_PRINT, _("Print"), _("Print"));
data->BT_print = button;
return toolbar;
}
static void repbalance_window_setup(struct repbalance_data *data)
{
DB( g_print("\n[rep-bal] setup\n") );
DB( g_print(" init data\n") );
DB( g_print(" populate\n") );
ui_acc_listview_populate(data->LV_acc, ACC_LST_INSERT_REPORT, NULL);
repbalance_filter_setup(data);
DB( g_print(" set widgets default\n") );
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
gtk_chart_show_legend(GTK_CHART(data->RE_chart), FALSE, FALSE);
gtk_chart_show_xval(GTK_CHART(data->RE_chart), TRUE);
gtk_chart_set_smallfont (GTK_CHART(data->RE_chart), PREFS->rep_smallfont);
DB( g_print(" connect widgets signals\n") );
//display signals
g_signal_connect (data->CY_intvl, "changed", G_CALLBACK (repbalance_compute), NULL);
g_signal_connect (data->CM_showempty,"toggled", G_CALLBACK (repbalance_compute), NULL);
g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repbalance_toggle_minor), NULL);
g_signal_connect (data->RG_zoomx, "value-changed", G_CALLBACK (repbalance_cb_zoomx_changed), NULL);
//filter signals
#if PRIV_FILTER
g_signal_connect (G_OBJECT (data->BT_filter), "clicked", G_CALLBACK (repbalance_action_filter), (gpointer)data);
#endif
g_signal_connect (data->BT_reset , "clicked", G_CALLBACK (repbalance_action_filter_reset), NULL);
data->hid[HID_REPBALANCE_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repbalance_range_change), NULL);
data->hid[HID_REPBALANCE_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repbalance_date_change), (gpointer)data);
data->hid[HID_REPBALANCE_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repbalance_date_change), (gpointer)data);
g_signal_connect (data->BT_all, "activate-link", G_CALLBACK (repbalance_cb_acc_activate_link), NULL);
g_signal_connect (data->BT_non, "activate-link", G_CALLBACK (repbalance_cb_acc_activate_link), NULL);
g_signal_connect (data->BT_inv, "activate-link", G_CALLBACK (repbalance_cb_acc_activate_link), NULL);
g_signal_connect (data->CM_inclxfer, "toggled", G_CALLBACK (repbalance_cb_filter_changed), NULL);
//item filter
GtkCellRendererToggle *renderer = g_object_get_data(G_OBJECT(data->LV_acc), "togrdr_data");
g_signal_connect_after (G_OBJECT(renderer), "toggled", G_CALLBACK (repbalance_cb_acc_changed), (gpointer)data);
//toolbar signals
g_signal_connect (data->BT_list, "clicked", G_CALLBACK (repbalance_action_viewlist), (gpointer)data);
g_signal_connect (data->BT_line, "clicked", G_CALLBACK (repbalance_action_viewline), (gpointer)data);
g_signal_connect (data->BT_detail, "clicked", G_CALLBACK (repbalance_toggle_detail), (gpointer)data);
g_signal_connect (data->BT_refresh, "clicked", G_CALLBACK (repbalance_cb_filter_changed), (gpointer)data);
g_signal_connect (data->BT_print, "clicked", G_CALLBACK (repbalance_action_print), (gpointer)data);
//treeview signals
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), "changed", G_CALLBACK (repbalance_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (repbalance_detail_onRowActivated), NULL);
}
static gboolean repbalance_window_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repbalance_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[rep-bal] window mapped\n") );
//setup, init and show window
repbalance_window_setup(data);
//trigger update
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
data->mapped_done = TRUE;
return FALSE;
}
static gboolean repbalance_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repbalance_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[rep-bal] window dispose\n") );
if(data->txn_queue != NULL)
g_queue_free (data->txn_queue);
da_flt_free(data->filter);
g_free(data);
//store position and size
wg = &PREFS->ove_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
//enable define windows
GLOBALS->define_off--;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
//unref window to our open window list
GLOBALS->openwindows = g_slist_remove(GLOBALS->openwindows, widget);
return FALSE;
}
//allocate our object/memory
static void repbalance_window_acquire(struct repbalance_data *data)
{
DB( g_print("\n[rep-bal] acquire\n") );
data->filter = da_flt_malloc();
data->detail = 0;
}
// the window creation
GtkWidget *repbalance_window_new(guint accnum)
{
struct repbalance_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *hbox, *vbox, *fbox, *bbox, *notebook, *vpaned, *scrollwin;
GtkWidget *label, *widget, *table, *treebox, *treeview;
gint row;
DB( g_print("\n[rep-bal] window new\n") );
data = g_malloc0(sizeof(struct repbalance_data));
if(!data) return NULL;
data->accnum = accnum;
repbalance_window_acquire (data);
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//ref window to our open window list
GLOBALS->openwindows = g_slist_prepend(GLOBALS->openwindows, window);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
gtk_window_set_title (GTK_WINDOW (window), _("Balance report"));
//window contents
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_SMALL);
gtk_window_set_child(GTK_WINDOW(window), mainbox);
//control part
table = gtk_grid_new ();
gtk_widget_set_hexpand (GTK_WIDGET(table), FALSE);
gtk_box_prepend (GTK_BOX (mainbox), table);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
//label = make_label_group(_("Display"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
//row++;
fbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_grid_attach (GTK_GRID (table), fbox, 0, row, 3, 1);
label = make_label_group(_("Display"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
gtk_box_prepend (GTK_BOX (fbox), label);
//5.5
row++;
label = make_label_widget(_("Inter_val:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_REPORT_INTVL);
data->CY_intvl = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Show empty line"));
data->CM_showempty = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Zoom X:"));
data->LB_zoomx = label;
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = make_scale(label);
data->RG_zoomx = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//-- filter
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
//5.8 test
row++;
fbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_grid_attach (GTK_GRID (table), fbox, 0, row, 3, 1);
label = make_label_group(_("Filter"));
//gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
gtk_box_prepend (GTK_BOX (fbox), label);
// active
label = make_label_widget(_("Active:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (fbox), label);
label = make_label(NULL, 0.0, 0.5);
gtk_widget_set_margin_start(label, SPACING_SMALL);
data->TX_fltactive = label;
gtk_box_prepend (GTK_BOX (fbox), label);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
data->TT_fltactive = fbox;
gtk_box_prepend (GTK_BOX (fbox), widget);
//test button
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (bbox), GTK_STYLE_CLASS_LINKED);
gtk_box_append (GTK_BOX (fbox), bbox);
widget = make_image_button(ICONNAME_HB_CLEAR, _("Clear filter"));
data->BT_reset = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
row++;
//label = make_label_group(_("Date filter"));
label = make_label_group(_("Date"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_DISABLE);
gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
row++;
label = make_label_widget(_("_From:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_mindate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_maxdate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
//row++;
//widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
//gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
row++;
//label = make_label_group(_("Account filter"));
label = make_label_group(_("Account"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
treebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_grid_attach (GTK_GRID (table), treebox, 1, row, 2, 1);
label = make_label (_("Select:"), 0, 0.5);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("all", _("All"));
data->BT_all = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("non", _("None"));
data->BT_non = label;
gtk_box_prepend (GTK_BOX (treebox), label);
label = make_clicklabel("inv", _("Invert"));
data->BT_inv = label;
gtk_box_prepend (GTK_BOX (treebox), label);
row++;
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
data->SW_acc = scrollwin;
treeview = ui_acc_listview_new(TRUE);
data->LV_acc = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
gtk_widget_set_size_request(treeview, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_grid_attach (GTK_GRID (table), scrollwin, 1, row, 2, 1);
//#2083175 option for xfer
row++;
widget = gtk_check_button_new_with_mnemonic (_("Include _transfer"));
data->CM_inclxfer = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//part: info + report
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
//toolbar
widget = repbalance_toolbar_create(data);
data->TB_bar = widget;
gtk_box_prepend (GTK_BOX (vbox), widget);
//infos
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_widget_set_margin_bottom (hbox, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = make_label(NULL, 0.5, 0.5);
gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
data->TX_daterange = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = gtk_label_new(NULL);
data->TX_info = label;
gtk_box_append (GTK_BOX (hbox), label);
/* report area */
notebook = gtk_notebook_new();
data->GR_result = notebook;
gtk_widget_show(notebook);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
hbtk_box_prepend (GTK_BOX (vbox), notebook);
//page: list
vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vpaned, NULL);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = lst_repbal_create();
data->LV_report = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack1 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
//detail
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
data->GR_detail = scrollwin;
//gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW (scrollwin), GTK_CORNER_TOP_RIGHT);
treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_det_columns);
data->LV_detail = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_paned_pack2 (GTK_PANED(vpaned), scrollwin, TRUE, TRUE);
list_txn_set_save_column_width(GTK_TREE_VIEW(treeview), TRUE);
//page: lines
widget = gtk_chart_new(CHART_TYPE_LINE);
data->RE_chart = widget;
//gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.suffix_symbol);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
// connect dialog signals
g_signal_connect (window, "delete-event", G_CALLBACK (repbalance_window_dispose), (gpointer)data);
g_signal_connect (window, "map-event" , G_CALLBACK (repbalance_window_mapped), NULL);
// setup, init and show window
wg = &PREFS->ove_wg;
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
// toolbar
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_widget_show_all (window);
//hide start widget
hb_widget_visible(data->LB_zoomx, FALSE);
hb_widget_visible(data->RG_zoomx, FALSE);
hb_widget_visible(data->CM_minor, PREFS->euro_active);
hb_widget_visible(data->CM_inclxfer, FALSE);
hb_widget_visible(data->GR_detail, data->detail);
return window;
}
/* = = = = = = = = = = = = = = = = */
static GString *lst_repbal_to_string(ToStringMode mode, GtkTreeView *treeview, gchar *title)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gchar sep;
node = g_string_new(NULL);
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
// header
g_string_append (node, _("Date") );
g_string_append_c (node, sep );
g_string_append (node, _("Expense") );
g_string_append_c (node, sep );
g_string_append (node, _("Income") );
g_string_append_c (node, sep );
g_string_append (node, _("Balance") );
g_string_append (node, "\n" );
// lines
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gchar *intvlname;
gdouble values[4];
gtk_tree_model_get (model, &iter,
LST_OVER_LABEL, &intvlname,
LST_OVER_EXPENSE, &values[0],
LST_OVER_INCOME, &values[1],
LST_OVER_TOTAL, &values[2],
-1);
g_string_append (node, intvlname );
for(guint i=0;i<3;i++)
{
g_string_append_c(node, sep);
_format_decimal(node, mode, values[i]);
}
g_string_append_c(node, '\n');
//leak
g_free(intvlname);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
static void lst_repbal_cell_data_function_date (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gchar *datestr;
gint flags;
gchar *color;
gint weight;
gtk_tree_model_get(model, iter,
LST_OVER_LABEL, &datestr,
LST_OVER_FLAGS, &flags,
-1);
color = NULL;
weight = PANGO_WEIGHT_NORMAL;
if( flags & REPORT_FLAG_OVER )
{
if(PREFS->custom_colors == TRUE)
color = PREFS->color_warn;
weight = PANGO_WEIGHT_BOLD;
}
g_object_set(renderer,
"weight", weight,
"foreground", color,
"text", datestr,
NULL);
//leak
g_free(datestr);
}
static void lst_repbal_cell_cell_data_function_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint flags;
gchar *color;
//gint weight;
guint32 kcur = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(gtk_tree_view_column_get_tree_view(col)), "kcur_data"));
//get datas
gtk_tree_model_get(model, iter,
LST_OVER_FLAGS, &flags,
GPOINTER_TO_INT(user_data), &value,
-1);
//fix: 400483
if( value == 0.0 )
g_object_set(renderer, "text", NULL, NULL);
else
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, GLOBALS->minor);
color = NULL;
//weight = PANGO_WEIGHT_NORMAL;
if(value != 0.0 && PREFS->custom_colors == TRUE)
color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
if( flags & REPORT_FLAG_OVER )
{
if(PREFS->custom_colors == TRUE)
color = PREFS->color_warn;
//weight = PANGO_WEIGHT_BOLD;
}
g_object_set(renderer,
//"weight", weight,
"foreground", color,
"text", buf,
NULL);
}
}
static GtkTreeViewColumn *lst_repbal_column_amount_create(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbal_cell_cell_data_function_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static void lst_repbal_set_cur(GtkTreeView *treeview, guint32 kcur)
{
g_object_set_data(G_OBJECT(treeview), "kcur_data", GUINT_TO_POINTER(kcur));
}
/*
** create our statistic list
*/
static GtkWidget *lst_repbal_create(void)
{
GtkListStore *store;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
/* create list store */
store = gtk_list_store_new(
NUM_LST_OVER,
G_TYPE_INT, //pos
G_TYPE_INT, //key
G_TYPE_STRING, //label
G_TYPE_DOUBLE, //exp
G_TYPE_DOUBLE, //inc
G_TYPE_DOUBLE, //total
G_TYPE_INT //flags
);
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
/* column: Label */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Date"));
renderer = gtk_cell_renderer_text_new();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_repbal_cell_data_function_date, NULL, NULL);
//gtk_tree_view_column_add_attribute(column, renderer, "text", LST_OVER_DATE);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Expense */
column = lst_repbal_column_amount_create(_("Expense"), LST_OVER_EXPENSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Income */
column = lst_repbal_column_amount_create(_("Income"), LST_OVER_INCOME);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Total/Balance */
column = lst_repbal_column_amount_create(_("Balance"), LST_OVER_TOTAL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column last: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
return(view);
}
homebank-5.9.7/src/hub-reptotal.h 0000664 0001750 0001750 00000003050 14736461415 016173 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HUB_SPENDING_H__
#define __HUB_SPENDING_H__
enum {
HUB_TOT_VIEW_NONE,
HUB_TOT_VIEW_TOPCAT,
HUB_TOT_VIEW_TOPPAY,
HUB_TOT_VIEW_TOPACC,
HUB_TOT_VIEW_ACCBAL,
//5.7.3
HUB_TOT_VIEW_GRPBAL
};
/* list top spending */
enum
{
LST_TOPSPEND_POS, //fake for pie
LST_TOPSPEND_KEY, //fake for pie
LST_TOPSPEND_NAME,
LST_TOPSPEND_AMOUNT,
LST_TOPSPEND_RATE,
NUM_LST_TOPSPEND
};
void ui_hub_reptotal_update(GtkWidget *widget, gpointer user_data);
void ui_hub_reptotal_clear(GtkWidget *widget, gpointer user_data);
void ui_hub_reptotal_populate(GtkWidget *widget, gpointer user_data);
void ui_hub_reptotal_setup(struct hbfile_data *data);
void ui_hub_reptotal_dispose(struct hbfile_data *data);
GtkWidget *ui_hub_reptotal_create(struct hbfile_data *data);
#endif
homebank-5.9.7/src/ui-txn-split.c 0000664 0001750 0001750 00000115372 15005623670 016141 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty ofdeftransaction_amountchanged
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "ui-transaction.h"
#include "ui-archive.h"
#include "gtk-dateentry.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-account.h"
#include "hbtk-decimalentry.h"
#include "ui-txn-split.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
#define GTK_RESPONSE_SPLIT_REM 10888
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void list_split_cell_data_func_number (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GtkTreePath *path;
gint *indices;
gchar num[16];
path = gtk_tree_model_get_path(model, iter);
indices = gtk_tree_path_get_indices(path);
//num = gtk_tree_path_to_string(path);
g_snprintf(num, 15, "%d", 1 + *indices);
gtk_tree_path_free(path);
g_object_set(renderer, "text", num, NULL);
}
static void list_split_cell_data_func_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar *color;
guint32 kcur;
gtk_tree_model_get(model, iter, 0, &split, -1);
kcur = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), "kcur_data"));
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, split->amount, kcur, FALSE);
color = get_normal_color_amount(split->amount);
g_object_set(renderer,
"foreground", color,
"text", buf,
NULL);
}
static void list_split_cell_data_func_memo (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
gtk_tree_model_get(model, iter, 0, &split, -1);
g_object_set(renderer, "text", split->memo, NULL);
}
static void list_split_cell_data_func_category (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
Category *cat;
gtk_tree_model_get(model, iter, 0, &split, -1);
cat = da_cat_get(split->kcat);
if( cat != NULL )
{
g_object_set(renderer, "text", cat->fullname, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void list_split_populate(GtkWidget *treeview, GPtrArray *splits)
{
GtkTreeModel *model;
GtkTreeIter iter;
Split *split;
gint count, i;
DB( g_print("\n[list_split] populate\n") );
count = da_splits_length (splits);
if( count <= 0 )
return;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
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(treeview), NULL); /* Detach model from view */
/* populate */
for(i=0 ; i < count ; i++)
{
split = da_splits_get(splits, i);
DB( g_print(" append split %d : %d, %.2f, '%s'\n", i, split->kcat, split->amount, split->memo) );
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
0, split,
-1);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), model); /* Re-attach model to view */
g_object_unref(model);
}
static GtkWidget *
list_split_new(guint32 kcur)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("\n[ui_split_listview] new\n") );
// create list store
store = gtk_list_store_new(1,
G_TYPE_POINTER
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
//column 0: line number
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes("#", renderer, NULL);
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_split_cell_data_func_number, NULL, NULL);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column 1: category
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 20,
NULL);
column = gtk_tree_view_column_new_with_attributes(_("Category"), renderer, NULL);
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
//gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
//gtk_tree_view_column_set_fixed_width( column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_split_cell_data_func_category, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column 2: memo
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 20,
NULL);
column = gtk_tree_view_column_new_with_attributes(_("Memo"), renderer, NULL);
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
//gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
//gtk_tree_view_column_set_fixed_width( column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_split_cell_data_func_memo, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column 3: amount
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes(_("Amount"), renderer, NULL);
g_object_set_data(G_OBJECT(column), "kcur_data", GINT_TO_POINTER(kcur));
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
//gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
//gtk_tree_view_column_set_fixed_width( column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_split_cell_data_func_amount, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// column empty
//column = gtk_tree_view_column_new();
//gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeviewattribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_acc_listview_compare_func, NULL, NULL);
//gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*static gboolean ui_split_dialog_cb_output_amount(GtkSpinButton *spin, gpointer data)
{
GtkAdjustment *adjustment;
gchar *text;
gdouble value;
//gpointer position;
DB( g_print("\n[ui_split_dialog] amount output\n") );
adjustment = gtk_spin_button_get_adjustment (spin);
value = gtk_adjustment_get_value (adjustment);
if( value == 0.0 )
{
gtk_entry_set_text (GTK_ENTRY (spin), "");
}
else
{
text = g_strdup_printf ("%+.*f", gtk_spin_button_get_digits(spin), value);
DB( g_print(" output '%s'\n", text) );
gtk_entry_set_text (GTK_ENTRY (spin), text);
//gtk_editable_delete_text (GTK_EDITABLE(spin), 0, -1);
//gtk_editable_insert_text (GTK_EDITABLE(spin), text, -1, &position);
g_free (text);
}
return TRUE;
}*/
static void ui_split_dialog_filter_text_handler (GtkEntry *entry,
const gchar *text,
gint length,
gint *position,
gpointer data)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint i, count=0;
gchar *result = g_new0 (gchar, length+1);
for (i=0; i < length; i++)
{
if (text[i]=='|')
continue;
result[count++] = text[i];
}
if (count > 0) {
g_signal_handlers_block_by_func (G_OBJECT (editable),
G_CALLBACK (ui_split_dialog_filter_text_handler),
data);
gtk_editable_insert_text (editable, result, count, position);
g_signal_handlers_unblock_by_func (G_OBJECT (editable),
G_CALLBACK (ui_split_dialog_filter_text_handler),
data);
}
g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
g_free (result);
}
static void ui_split_dialog_cb_eval_order(struct ui_split_dialog_data *data)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
guint i;
DB( g_print("\n[ui_split_dialog] eval order\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_split));
i=1; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Split *split;
gtk_tree_model_get (model, &iter, 0, &split, -1);
split->pos = i;
DB( g_print(" split pos: %d '%s' %.2f\n", i, split->memo, split->amount) );
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
da_splits_sort(data->tmp_splits);
}
/*
static gboolean ui_split_dialog_cb_amount_focus_out (GtkEditable *spin_button, GdkEvent *event, gpointer user_data)
{
struct ui_split_dialog_data *data;
const gchar *txt;
DB( g_print("\n[ui_split_dialog] cb amount focus out\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(spin_button), GTK_TYPE_WINDOW)), "inst_data");
//force store
txt = gtk_entry_get_text(GTK_ENTRY(spin_button));
data->amountsign = hb_amount_forced_sign(txt);
DB( g_print(" txt='%s'\n amt=%.8f\n sign=%d\n", txt, gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_button)), data->amountsign) );
return FALSE;
}
*/
static void ui_split_dialog_update(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
gboolean tmpval;
guint count;
DB( g_print("\n[ui_split_dialog] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
count = da_splits_length (data->tmp_splits);
//btn: edit/rem
tmpval = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split)), NULL, NULL);
gtk_widget_set_sensitive (data->BT_edit, (data->isedited) ? FALSE : tmpval);
gtk_widget_set_sensitive (data->BT_dup, (data->isedited) ? FALSE : tmpval);
gtk_widget_set_sensitive (data->BT_rem, (data->isedited) ? FALSE : tmpval);
//btn: remall
tmpval = (count > 1) ? TRUE : FALSE;
gtk_widget_set_sensitive (data->BT_remall, (data->isedited) ? FALSE : tmpval);
//btn: add/apply
/*amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
tmpval = hb_amount_round(amount, 2) != 0.0 ? TRUE : FALSE;
gtk_widget_set_sensitive (data->BT_apply, tmpval);
*/
//btn: add
tmpval = ( count >= TXN_MAX_SPLIT ) ? FALSE : TRUE;
gtk_widget_set_sensitive (data->BT_add, tmpval);
if( data->isedited )
tmpval = TRUE;
gtk_widget_set_sensitive (data->PO_cat, tmpval);
gtk_widget_set_sensitive (data->ST_memo, tmpval);
gtk_widget_set_sensitive (data->ST_amount, tmpval);
//btn: show/hide
gtk_widget_set_sensitive (data->LV_split, !data->isedited);
hb_widget_visible (data->BT_add, !data->isedited);
hb_widget_visible (data->IM_edit, data->isedited);
hb_widget_visible (data->BT_apply, data->isedited);
hb_widget_visible (data->BT_cancel, data->isedited);
}
static void ui_split_dialog_edit_end(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
DB( g_print("\n[ui_split_dialog] edit_end\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), 0);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), 0);
if( data->mode == SPLIT_MODE_EMPTY )
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), 0.0);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), 0.0);
gtk_entry_set_text(GTK_ENTRY(data->ST_memo), "");
gtk_widget_grab_focus(data->ST_amount);
data->isedited = FALSE;
}
static void ui_split_dialog_edit_start(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui_split_dialog] edit_start\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_split));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Split *split;
gchar *txt;
gtk_tree_model_get(model, &iter, 0, &split, -1);
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), split->kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), split->kcat);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), split->amount);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), split->amount);
txt = (split->memo != NULL) ? split->memo : "";
gtk_entry_set_text(GTK_ENTRY(data->ST_memo), txt);
data->isedited = TRUE;
ui_split_dialog_update (data->dialog, user_data);
}
}
static void ui_split_dialog_cancel_cb(GtkWidget *widget, gpointer user_data)
{
//struct ui_split_dialog_data *data;
DB( g_print("\n[ui_split_dialog] cancel\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
ui_split_dialog_edit_end(widget, user_data);
ui_split_dialog_update (widget, user_data);
}
static void ui_split_dialog_apply_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("--------\n[ui_split_dialog] apply\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Split *split;
gdouble amount;
gtk_tree_model_get(model, &iter, 0, &split, -1);
DB( g_print(" update spin\n") );
//gtk_spin_button_update (GTK_SPIN_BUTTON(data->ST_amount));
//amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
if(amount)
{
//split->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat));
split->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(data->PO_cat));
g_free(split->memo);
split->memo = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_memo)));
//split->amount = amount;
//#1910819 must round frac digit
split->amount = hb_amount_round(amount, data->cur->frac_digits);
}
}
ui_split_dialog_edit_end(widget, user_data);
ui_split_dialog_compute (widget, data);
ui_split_dialog_update (widget, user_data);
}
static void ui_split_dialog_deleteall_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
gint result;
DB( g_print("\n[ui_split_dialog] deleteall_cb\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
NULL,
_("Do you want to delete all split lines"),
_("_Delete"),
TRUE
);
if(result == GTK_RESPONSE_OK)
{
gtk_list_store_clear (GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW(data->LV_split))));
da_split_destroy(data->tmp_splits);
data->tmp_splits = da_split_new ();
ui_split_dialog_compute (widget, data);
ui_split_dialog_update (widget, user_data);
}
}
static void ui_split_dialog_cb_duplicate(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui_split_dialog] duplicate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Split *src_split, *new_split;
gtk_tree_model_get(model, &iter, 0, &src_split, -1);
new_split = da_split_duplicate(src_split);
if( new_split )
{
//ui_split_listview_add(GTK_TREE_VIEW(data->LV_split), new_split);
da_splits_append (data->tmp_splits, new_split);
//model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_split));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
0, new_split,
-1);
}
}
ui_split_dialog_compute (widget, data);
ui_split_dialog_update (widget, user_data);
}
static void ui_split_dialog_delete_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui_split_dialog] delete_cb\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Split *split;
gtk_tree_model_get(model, &iter, 0, &split, -1);
//todo: not implemented yet
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
da_splits_delete(data->tmp_splits, split);
}
ui_split_dialog_compute (widget, data);
ui_split_dialog_update (widget, user_data);
}
static void ui_split_dialog_add_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
Split *split;
guint count;
gdouble amount;
DB( g_print("--------\n[ui_split_dialog] add\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
count = da_splits_length (data->tmp_splits);
DB( g_print(" n_split: %d (of %d)\n", count, TXN_MAX_SPLIT) );
if( count <= TXN_MAX_SPLIT )
{
split = da_split_malloc ();
//5.4.4
DB( g_print(" update spin %d\n", data->amountsign) );
//gtk_spin_button_update (GTK_SPIN_BUTTON(data->ST_amount));
//amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_amount));
amount = hbtk_decimal_entry_get_value(HBTK_DECIMAL_ENTRY(data->ST_amount));
if(amount)
{
//force sign apply
if( hbtk_decimal_entry_get_forcedsign(HBTK_DECIMAL_ENTRY(data->ST_amount)) == FALSE )
{
DB( g_print(" raw amt=%.2f\n", amount) );
switch( data->amountsign )
{
case HB_AMT_SIGN_EXP:
if( amount > 0)
amount *= -1;
break;
case HB_AMT_SIGN_INC:
if( amount < 0)
amount *= -1;
break;
default:
if( hb_amount_type_match(amount, data->txntype) == FALSE )
amount *= -1;
break;
}
}
DB( g_print(" final amt=%.2f\n", amount) );
//split->amount = amount;
//#1910819 must round frac digit
split->amount = hb_amount_round(amount, data->cur->frac_digits);
//split->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->PO_cat));
split->kcat = ui_cat_entry_popover_get_key_add_new(GTK_BOX(data->PO_cat));
split->memo = g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_memo)));
//#1977686 add into memo autocomplete
if(PREFS->txn_memoacp == TRUE)
{
if( da_transaction_insert_memo(split->memo, data->date) )
{
GtkEntryCompletion *completion;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print(" add memo to completion\n") );
completion = gtk_entry_get_completion (GTK_ENTRY(data->ST_memo));
model = gtk_entry_completion_get_model (completion);
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, -1,
0, split->memo,
-1);
}
}
DB( g_print(" append split : %d, %.2f, %s\n", split->kcat, split->amount, split->memo) );
da_splits_append (data->tmp_splits, split);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_split));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
0, split,
-1);
ui_split_dialog_compute (widget, data);
}
// 0 amount not allowed into splits
else
{
da_split_free(split);
}
}
else
{
g_warning("split error: limit of %d reached", TXN_MAX_SPLIT);
}
ui_split_dialog_edit_end(widget, user_data);
ui_split_dialog_update (widget, user_data);
}
static void ui_split_dialog_cb_amount_activate(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
DB( g_print("\n[ui_split_dialog] cb amount activate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
//we trigger the focus-out-event on spinbutton, with grab the add button
//because we also do things before the legacy spinbutton fucntion
//gtk_widget_grab_focus(data->BT_add);
if( data->isedited == TRUE )
ui_split_dialog_apply_cb(widget, NULL);
else
ui_split_dialog_add_cb(widget, NULL);
}
static void ui_split_rowactivated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data)
{
DB( g_print("\n[ui_split_dialog] rowactivated\n") );
ui_split_dialog_edit_start(GTK_WIDGET(treeview), NULL);
}
static void ui_split_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_print("\n[ui_split_dialog] selection\n") );
ui_split_dialog_update (GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), user_data);
}
void ui_split_dialog_compute(GtkWidget *widget, gpointer user_data)
{
struct ui_split_dialog_data *data;
gint i, count, nbvalid;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gboolean sensitive;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
DB( g_print("\n[ui_split_dialog] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
nbvalid = 0;
data->sumsplit = 0.0;
data->remsplit = 0.0;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_split));
i=0; valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Split *split;
gtk_tree_model_get (model, &iter,
0, &split,
-1);
data->sumsplit += split->amount;
if( hb_amount_round(split->amount, data->cur->frac_digits) != 0.0 )
nbvalid++;
/* Make iter point to the next row in the list store */
i++; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
count = i;
DB( g_print(" n_count=%d, n_valid=%d\n", count, nbvalid ) );
data->remsplit = data->amount - data->sumsplit;
//validation: 2 split min, if 0 split
//Accept button is not disabled to enable empty the splits
sensitive = FALSE;
if( (count == 0) || nbvalid >= 2 )
sensitive = TRUE;
gtk_widget_hide(data->IB_inflimit);
gtk_widget_hide(data->IB_wrnsum);
gtk_widget_hide(data->IB_errtype);
if( count >= TXN_MAX_SPLIT )
{
gtk_widget_show_all(data->IB_inflimit);
//#GTK+710888: hack waiting a GTK fix
gtk_widget_queue_resize (data->IB_inflimit);
}
if( data->mode == SPLIT_MODE_AMOUNT )
{
if( hb_amount_round(data->remsplit, data->cur->frac_digits) == 0.0 )
{
g_sprintf(buf, "----");
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), 0);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), 0.0);
}
else
{
//g_snprintf(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->cur->format, data->remsplit);
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->remsplit, data->cur->key, FALSE);
//#1845841 bring back checkpoint with initial amount + init remainder
//revert, because block any edition/inherit
//sensitive = (count > 1) ? FALSE : sensitive;
//but keep prefill remainder
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_amount), data->remsplit);
hbtk_decimal_entry_set_value(HBTK_DECIMAL_ENTRY(data->ST_amount), data->remsplit);
gtk_widget_show_all(data->IB_wrnsum);
//#GTK+710888: hack waiting a GTK fix
gtk_widget_queue_resize (data->IB_wrnsum);
}
gtk_label_set_label(GTK_LABEL(data->LB_remain), buf);
//g_snprintf(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->cur->format, data->amount);
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->amount, data->cur->key, FALSE);
gtk_label_set_label(GTK_LABEL(data->LB_txnamount), buf);
}
//g_snprintf(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->cur->format, data->sumsplit);
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, data->sumsplit, data->cur->key, FALSE);
gtk_label_set_text(GTK_LABEL(data->LB_sumsplit), buf);
//if split sum sign do not match
if( hb_amount_type_match(data->sumsplit, data->txntype) == FALSE )
{
//#1885413 enable sign invert from split dialog
//sensitive = FALSE;
gtk_widget_show_all(data->IB_errtype);
//#GTK+710888: hack waiting a GTK fix
gtk_widget_queue_resize (data->IB_errtype);
}
gtk_dialog_set_response_sensitive(GTK_DIALOG(data->dialog), GTK_RESPONSE_ACCEPT, sensitive);
}
static void ui_split_dialog_setup(struct ui_split_dialog_data *data)
{
guint count;
DB( g_print("\n[ui_split_dialog] set\n") );
count = da_splits_length(data->tmp_splits);
data->nbsplit = count > 1 ? count-1 : 0;
DB( g_print(" n_count = %d\n", count) );
list_split_populate (data->LV_split, data->tmp_splits);
data->isedited = FALSE;
//gtk_spin_button_set_digits (GTK_SPIN_BUTTON(data->ST_amount), data->cur->frac_digits);
hbtk_decimal_entry_set_digits(HBTK_DECIMAL_ENTRY(data->ST_amount), data->cur->frac_digits);
//5.5 done in popover
//ui_cat_comboboxentry_populate(GTK_COMBO_BOX(data->PO_cat), GLOBALS->h_cat);
ui_split_dialog_compute(data->dialog, data);
ui_split_dialog_update (data->dialog, data);
}
GtkWidget *ui_split_view_dialog (GtkWidget *parent, Transaction *ope)
{
GtkWidget *dialog, *content, *table, *scrollwin, *treeview;
gint w, h, dw, dh, row;
DB( g_print("\n[ui_split_dialog] new view only\n") );
if( ope->splits == NULL )
return NULL;
dialog = gtk_dialog_new_with_buttons (_("Transaction splits"),
GTK_WINDOW(parent),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
//store our dialog private data
DB( g_print(" window=%p\n", dialog) );
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(parent), &w, &h);
dh = (h/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" parent w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
table = gtk_grid_new ();
hb_widget_set_margin(GTK_WIDGET(table), SPACING_SMALL);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_TINY);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_TINY);
hbtk_box_prepend (GTK_BOX (content), table);
row = 0;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request(scrollwin, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
treeview = list_split_new(ope->kcur);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_grid_attach (GTK_GRID (table), scrollwin, 0, row, 4, 1);
//setup
list_split_populate(treeview, ope->splits);
gtk_widget_show_all (dialog);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_window_destroy (GTK_WINDOW(dialog));
return NULL;
}
GtkWidget *ui_split_dialog (GtkWidget *parent, GPtrArray **src_splits, gint txntype, guint32 date, gdouble amount, guint32 kcur, void (update_callbackFunction(GtkWidget*, gdouble)))
{
struct ui_split_dialog_data *data;
GtkWidget *dialog, *content, *table, *box, *scrollwin, *bar;
GtkWidget *label, *widget, *treeview;
gint row;
DB( g_print("\n[ui_split_dialog] new\n") );
data = g_malloc0(sizeof(struct ui_split_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Transaction splits"),
GTK_WINDOW(parent),
0,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
NULL);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" window=%p, inst_data=%p\n", dialog, data) );
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &dialog);
data->dialog = dialog;
gtk_dialog_add_button(GTK_DIALOG(dialog), _("_OK"), GTK_RESPONSE_ACCEPT);
data->date = date;
data->cur = da_cur_get (kcur);
DB( g_print(" kcur: %d %d %s\n", data->cur->key, data->cur->frac_digits, data->cur->format) );
//todo: init should move
//clone splits or create new
data->src_splits = *src_splits;
data->txntype = txntype;
data->mode = (hb_amount_round(amount, data->cur->frac_digits) != 0.0) ? SPLIT_MODE_AMOUNT : SPLIT_MODE_EMPTY;
data->amount = amount;
data->sumsplit = amount;
DB( g_print(" amount : %f\n", data->amount) );
DB( g_print(" txntype: %s\n", data->txntype == TXN_TYPE_EXPENSE ? "expense" : "income" ));
DB( g_print(" mode : %s\n", data->mode == SPLIT_MODE_AMOUNT ? "amount" : "empty" ));
if( *src_splits != NULL )
data->tmp_splits = da_splits_clone(*src_splits);
else
data->tmp_splits = da_split_new();
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
table = gtk_grid_new ();
hb_widget_set_margin(GTK_WIDGET(table), SPACING_LARGE);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_TINY);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_TINY);
hbtk_box_prepend (GTK_BOX (content), table);
row = 0;
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
//gtk_widget_set_size_request(scrollwin, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
//gtk_widget_set_hexpand (scrollwin, TRUE);
treeview = list_split_new(kcur);
data->LV_split = treeview;
gtk_widget_set_vexpand (treeview, TRUE);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollwin), data->LV_split);
gtk_grid_attach (GTK_GRID (table), scrollwin, 0, row, 4, 1);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_TINY);
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
gtk_grid_attach (GTK_GRID (table), box, 4, row, 1, 1);
widget = make_image_button(ICONNAME_LIST_DELETE_ALL, _("Delete all"));
data->BT_remall = widget;
gtk_box_append (GTK_BOX (box), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_rem = widget;
gtk_box_append (GTK_BOX(box), widget);
widget = make_image_button(ICONNAME_LIST_DUPLICATE, _("Duplicate"));
data->BT_dup = widget;
gtk_box_append (GTK_BOX(box), widget);
widget = make_image_button(ICONNAME_HB_OPE_EDIT, _("Edit"));
data->BT_edit = widget;
gtk_box_append (GTK_BOX(box), widget);
row++;
label = gtk_label_new(_("Category"));
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_grid_attach (GTK_GRID (table), label, 0, row, 1, 1);
label = gtk_label_new(_("Memo"));
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
//5.7.1
gchar *typelabel = _("Amount");
if( txntype == TXN_TYPE_EXPENSE ) typelabel = _("Expense");
else
if( txntype == TXN_TYPE_INCOME ) typelabel = _("Income");
label = gtk_label_new(typelabel);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_grid_attach (GTK_GRID (table), label, 2, row, 1, 1);
row++;
//widget = ui_cat_comboboxentry_new(NULL);
widget = ui_cat_entry_popover_new(NULL);
data->PO_cat = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 1, 1);
//1977686
//widget = make_string(NULL);
widget = make_memo_entry(NULL);
data->ST_memo= widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (table), widget, 1, row, 1, 1);
//widget = make_amount(NULL);
widget = hbtk_decimal_entry_new(NULL);
data->ST_amount = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_TINY);
gtk_grid_attach (GTK_GRID (table), box, 3, row, 1, 1);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_QUICKTIPS);
gtk_widget_set_tooltip_text(widget, _("Prefix with -/+ to force the sign"));
gtk_box_prepend (GTK_BOX (box), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_OPE_EDIT);
data->IM_edit = widget;
hbtk_box_prepend (GTK_BOX(box), widget);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend (GTK_BOX(box), widget);
widget = make_image_button(ICONNAME_EMBLEM_OK, _("Apply"));
data->BT_apply = widget;
gtk_box_prepend (GTK_BOX(box), widget);
widget = make_image_button(ICONNAME_WINDOW_CLOSE, _("Cancel"));
data->BT_cancel = widget;
gtk_box_prepend (GTK_BOX(box), widget);
if( data->mode == SPLIT_MODE_AMOUNT )
{
row++;
label = gtk_label_new(_("Transaction amount:"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = gtk_label_new(NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_widget_set_margin_end (widget, SPACING_SMALL);
data->LB_txnamount = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
label = gtk_label_new(_("Unassigned:"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = gtk_label_new(NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_widget_set_margin_end (widget, SPACING_SMALL);
data->LB_remain = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_grid_attach (GTK_GRID (table), widget, 1, row, 2, 1);
}
row++;
label = gtk_label_new(_("Sum of splits:"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
widget = gtk_label_new(NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_widget_set_margin_end (widget, SPACING_SMALL);
data->LB_sumsplit = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
bar = gtk_info_bar_new ();
data->IB_inflimit = bar;
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
label = gtk_label_new (_("Number of splits limit is reached"));
hbtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
gtk_grid_attach (GTK_GRID (table), bar, 0, row, 4, 1);
row++;
bar = gtk_info_bar_new ();
data->IB_errtype = bar;
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
label = gtk_label_new (_("Warning: sum of splits and transaction type don't match"));
hbtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
gtk_grid_attach (GTK_GRID (table), bar, 0, row, 4, 1);
row++;
bar = gtk_info_bar_new ();
data->IB_wrnsum = bar;
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_ERROR);
label = gtk_label_new (_("Warning: sum of splits and transaction amount don't match"));
hbtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
gtk_grid_attach (GTK_GRID (table), bar, 0, row, 4, 1);
//connect all our signals
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_split)), "changed", G_CALLBACK (ui_split_selection), data);
g_signal_connect (GTK_TREE_VIEW(data->LV_split), "row-activated", G_CALLBACK (ui_split_rowactivated), data);
g_signal_connect (data->BT_edit , "clicked", G_CALLBACK (ui_split_dialog_edit_start), NULL);
g_signal_connect (data->BT_dup , "clicked", G_CALLBACK (ui_split_dialog_cb_duplicate), NULL);
g_signal_connect (data->BT_rem , "clicked", G_CALLBACK (ui_split_dialog_delete_cb), NULL);
g_signal_connect (data->BT_remall, "clicked", G_CALLBACK (ui_split_dialog_deleteall_cb), NULL);
g_signal_connect (data->ST_memo , "insert-text", G_CALLBACK(ui_split_dialog_filter_text_handler), data);
//g_signal_connect_after (data->ST_amount, "focus-out-event", G_CALLBACK (ui_split_dialog_cb_amount_focus_out), data);
g_signal_connect_after (data->ST_amount, "activate", G_CALLBACK (ui_split_dialog_cb_amount_activate), NULL);
g_signal_connect (data->BT_add , "clicked", G_CALLBACK (ui_split_dialog_add_cb), NULL);
g_signal_connect (data->BT_apply , "clicked", G_CALLBACK (ui_split_dialog_apply_cb), NULL);
g_signal_connect (data->BT_cancel, "clicked", G_CALLBACK (ui_split_dialog_cancel_cb), NULL);
//gtk_window_set_default_size(GTK_WINDOW(dialog), 480, -1);
gtk_widget_show_all (dialog);
//setup, init and show dialog
ui_split_dialog_setup(data);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
switch (result)
{
// sum split and alter txn amount
case GTK_RESPONSE_ACCEPT:
if( da_splits_length(data->tmp_splits) )
{
ui_split_dialog_cb_eval_order(data);
// here we swap src_splits <> tmp_splits
*src_splits = data->tmp_splits;
data->tmp_splits = data->src_splits;
update_callbackFunction(parent, data->sumsplit);
}
else
{
//delete split and revert back original amount
da_split_destroy(*src_splits);
*src_splits = NULL;
update_callbackFunction(parent, data->amount);
}
break;
/*case GTK_RESPONSE_SPLIT_REM:
da_split_destroy(*src_splits);
*src_splits = NULL;
update_callbackFunction(parent, data->sumsplit);
break;
*/
default:
//do_nothing_since_dialog_was_cancelled ();
break;
}
// debug
/*#if MYDEBUG == 1
{
guint i;
for(i=0;iope_splits[i];
if(data->ope_splits[i] == NULL)
break;
g_print(" split %d : %d, %.2f, %s\n", i, split->kcat, split->amount, split->memo);
}
}
#endif*/
// cleanup and destroy
//GLOBALS->changes_count += data->change;
gtk_window_destroy (GTK_WINDOW(dialog));
da_split_destroy (data->tmp_splits);
g_free(data);
return NULL;
}
homebank-5.9.7/src/ui-assist-start.h 0000644 0001750 0001750 00000002775 14736461415 016654 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_UI_ASSIST_START_GTK_H__
#define __HB_UI_ASSIST_START_GTK_H__
#define PAGE_WELCOME 0
#define PAGE_GENERAL 1
#define PAGE_CURRENCIES 2
#define PAGE_CATEGORIES 3
#define PAGE_ACCOUNTS 4
struct assist_start_data
{
GtkWidget *dialog;
//GtkWidget *pages[NUM_PAGE];
GtkWidget *ST_owner;
GtkWidget *LB_cur_base, *BT_cur_change;;
GtkWidget *CM_cur_add, *LB_cur_others, *BT_cur_add;
GtkWidget *GR_file;
GtkWidget *TX_file;
GtkWidget *TX_preview;
GtkWidget *ok_image, *ko_image;
GtkWidget *CM_load;
GtkWidget *CM_acc_add;
GtkWidget *GR_acc;
GtkWidget *ST_name;
GtkWidget *CY_type;
Currency4217 *curfmt;
gchar *pathfilename;
GPtrArray *cur_arr;
};
GtkWidget *ui_newfile_assitant_new(void);
#endif
homebank-5.9.7/src/Makefile.am 0000644 0001750 0001750 00000005060 14774301713 015445 0 ustar franam franam
common_defines = \
-DSHARE_DIR=\""$(pkgdatadir)"\" \
-DDATA_DIR=\""$(datadir)"\"
bin_PROGRAMS = homebank
HOMEBANK_CORE =
USER_INTERFACE =
homebank_SOURCES = \
dsp-account.c \
dsp-account.h \
dsp-mainwindow.c \
dsp-mainwindow.h \
enums.h \
hb-types.h \
gtk-chart.c \
gtk-chart.h \
gtk-chart-colors.c \
gtk-chart-colors.h \
gtk-chart-progress.c \
gtk-chart-progress.h \
gtk-dateentry.c \
gtk-dateentry.h \
hb-account.c \
hb-account.h \
hb-archive.c \
hb-archive.h \
hb-assign.c \
hb-assign.h \
hb-category.c \
hb-category.h \
hb-currency.c \
hb-currency.h \
hb-encoding.c \
hb-encoding.h \
hb-export.c \
hb-export.h \
hb-filter.c \
hb-filter.h \
hb-hbfile.c \
hb-hbfile.h \
hb-import.c \
hb-import.h \
hb-import-ofx.c \
hb-import-qif.c \
hb-import-csv.c \
hb-misc.c \
hb-misc.h \
hb-payee.c \
hb-payee.h \
hb-group.c \
hb-group.h \
hb-preferences.c \
hb-preferences.h \
hb-pref-data.c \
hb-pref-data.h \
hb-report.c \
hb-report.h \
hb-tag.c \
hb-tag.h \
hb-split.c \
hb-split.h \
hbtk-decimalentry.c \
hbtk-decimalentry.h \
hb-transaction.c \
hb-transaction.h \
hb-xml.c \
hb-xml.h \
hbtk-switcher.c \
hbtk-switcher.h \
homebank.c \
homebank.h \
icon-names.h \
hub-account.c \
hub-account.h \
hub-reptime.c \
hub-reptime.h \
hub-reptotal.c \
hub-reptotal.h \
hub-scheduled.c \
hub-scheduled.h \
hub-transaction.c \
hub-transaction.h \
language.c \
language.h \
list-account.c \
list-account.h \
list-operation.c \
list-operation.h \
list-report.c \
list-report.h \
list-scheduled.c \
list-scheduled.h \
rep-balance.c \
rep-balance.h \
rep-budget.c \
rep-budget.h \
rep-stats.c \
rep-stats.h \
rep-time.c \
rep-time.h \
rep-vehicle.c \
rep-vehicle.h \
ui-account.c \
ui-account.h \
ui-archive.c \
ui-archive.h \
ui-assign.c \
ui-assign.h \
ui-assist-import.c \
ui-assist-import.h \
ui-assist-start.c \
ui-assist-start.h \
ui-budget.c \
ui-budget.h \
ui-budget-tabview.c \
ui-budget-tabview.h \
ui-category.c \
ui-category.h \
ui-currency.c \
ui-currency.h \
ui-dialogs.c \
ui-dialogs.h \
ui-filter.c \
ui-filter.h \
ui-flt-widget.c \
ui-flt-widget.h \
ui-hbfile.c \
ui-hbfile.h \
ui-group.c \
ui-group.h \
ui-payee.c \
ui-payee.h \
ui-pref.c \
ui-pref.h \
ui-tag.c \
ui-tag.h \
ui-transaction.c \
ui-transaction.h \
ui-txn-multi.c \
ui-txn-multi.h \
ui-txn-split.c \
ui-txn-split.h \
ui-widgets-data.c \
ui-widgets.c \
ui-widgets.h
homebank_LDADD = $(DEPS_LIBS) \
$(LIBSOUP_LIBS)
AM_CPPFLAGS = \
$(DEPS_CFLAGS) \
$(LIBSOUP_CFLAGS) \
$(common_defines)
homebank-5.9.7/src/hb-payee.c 0000644 0001750 0001750 00000031620 15120541712 015237 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-payee.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
//Payee *
//da_pay_clone
void
da_pay_free(Payee *item)
{
DB( g_print("da_pay_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->name) );
g_free(item->name);
g_free(item->notes);
g_free(item);
}
}
Payee *
da_pay_malloc(void)
{
DB( g_print("da_pay_malloc\n") );
return g_malloc0(sizeof(Payee));
}
void
da_pay_destroy(void)
{
DB( g_print("da_pay_destroy\n") );
g_hash_table_destroy(GLOBALS->h_pay);
}
void
da_pay_new(void)
{
Payee *item;
DB( g_print("da_pay_new\n") );
GLOBALS->h_pay = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_pay_free);
// insert our 'no payee'
item = da_pay_malloc();
item->name = g_strdup("");
da_pay_insert(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* da_pay_length:
*
* Return value: the number of elements
*/
guint
da_pay_length(void)
{
return g_hash_table_size(GLOBALS->h_pay);
}
static void
da_pay_max_key_ghfunc(gpointer key, Payee *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
/**
* da_pay_get_max_key:
*
* Get the biggest key from the GHashTable
*
* Return value: the biggest key value
*
*/
guint32
da_pay_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_pay, (GHFunc)da_pay_max_key_ghfunc, &max_key);
return max_key;
}
/**
* da_pay_delete:
*
* delete an payee from the GHashTable
*
* Return value: TRUE if the key was found and deleted
*
*/
gboolean
da_pay_delete(guint32 key)
{
DB( g_print("da_pay_delete %d\n", key) );
return g_hash_table_remove(GLOBALS->h_pay, &key);
}
//#1889659: ensure name != null/empty
static gboolean
da_pay_ensure_name(Payee *item)
{
// (no payee) have name=""
if( item->key > 0 )
{
if( item->name == NULL || strlen(item->name) == 0 )
{
g_free(item->name);
item->name = g_strdup_printf("no name %d", item->key);
return TRUE;
}
}
return FALSE;
}
static void
da_pay_rename(Payee *item, gchar *newname)
{
DB( g_print("- renaming '%s' => '%s'\n", item->name, newname) );
g_free(item->name);
item->name = g_strdup(newname);
//#1889659: ensure name != null/empty
da_pay_ensure_name(item);
}
/**
* da_pay_insert:
*
* insert an payee into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_pay_insert(Payee *item)
{
guint32 *new_key;
DB( g_print("da_pay_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
//#1889659: ensure name != null/empty
da_pay_ensure_name(item);
g_hash_table_insert(GLOBALS->h_pay, new_key, item);
return TRUE;
}
/**
* da_pay_append:
*
* append a new payee into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_pay_append(Payee *item)
{
Payee *existitem;
DB( g_print("da_pay_append\n") );
existitem = da_pay_get_by_name( item->name );
if( existitem == NULL )
{
item->key = da_pay_get_max_key() + 1;
da_pay_insert(item);
return TRUE;
}
DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
return FALSE;
}
/**
* da_pay_append_if_new:
*
* append a new payee into the GHashTable
*
* Return value: existing or new payee
*
*/
Payee *
da_pay_append_if_new(gchar *rawname)
{
Payee *retval = NULL;
retval = da_pay_get_by_name(rawname);
if(retval == NULL)
{
retval = da_pay_malloc();
retval->key = da_pay_get_max_key() + 1;
retval->name = g_strdup(rawname);
g_strstrip(retval->name);
da_pay_insert(retval);
}
return retval;
}
static gboolean
da_pay_name_grfunc(gpointer key, Payee *item, gchar *name)
{
if( name && item->name )
{
if(!strcasecmp(name, item->name))
return TRUE;
}
return FALSE;
}
/**
* da_pay_get_by_name:
*
* Get an payee structure by its name
*
* Return value: Payee * or NULL if not found
*
*/
Payee *
da_pay_get_by_name(gchar *rawname)
{
Payee *retval = NULL;
gchar *stripname;
DB( g_print("da_pay_get_by_name\n") );
if( rawname )
{
stripname = g_strdup(rawname);
g_strstrip(stripname);
if( strlen(stripname) == 0 )
retval = da_pay_get(0);
else
retval = g_hash_table_find(GLOBALS->h_pay, (GHRFunc)da_pay_name_grfunc, stripname);
g_free(stripname);
}
return retval;
}
/**
* da_pay_get:
*
* Get a payee structure by key
*
* Return value: Payee * or NULL if not found
*
*/
Payee *
da_pay_get(guint32 key)
{
//DB( g_print("da_pay_get\n") );
return g_hash_table_lookup(GLOBALS->h_pay, &key);
}
gchar *da_pay_get_name(Payee *item)
{
return (item->key == 0) ? _("(no payee)") : item->name;
}
void da_pay_consistency(Payee *item)
{
g_strstrip(item->name);
//5.2.4 we drop internal xfer here as it will disapear
//was faulty possible
if( item->paymode == OLDPAYMODE_INTXFER )
item->paymode = PAYMODE_XFER;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG
static void
da_pay_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
{
guint32 *id = key;
Payee *item = value;
DB( g_print(" %d :: %s\n", *id, item->name) );
}
static void
da_pay_debug_list(void)
{
DB( g_print("\n** debug **\n") );
g_hash_table_foreach(GLOBALS->h_pay, da_pay_debug_list_ghfunc, NULL);
DB( g_print("\n** end debug **\n") );
}
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gint
payee_delete_unused(void)
{
GList *lpay, *list;
gint count = 0;
lpay = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *entry = list->data;
if(entry->nb_use_all <= 0 && entry->key > 0)
{
da_pay_delete (entry->key);
count++;
}
list = g_list_next(list);
}
g_list_free(lpay);
return count;
}
void
payee_fill_usage(void)
{
GList *lpay;
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *lrul, *list;
DB( g_print("[da_pay] fill usage\n") );
lpay = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *entry = list->data;
entry->nb_use_all = 0;
entry->nb_use_txn = 0;
list = g_list_next(list);
}
g_list_free(lpay);
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
Payee *pay = da_pay_get (txn->kpay);
if(pay)
{
pay->nb_use_all++;
pay->nb_use_txn++;
}
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
Payee *pay = da_pay_get (entry->kpay);
if(pay)
pay->nb_use_all++;
list = g_list_next(list);
}
lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *entry = list->data;
Payee *pay = da_pay_get (entry->kpay);
if(pay)
pay->nb_use_all++;
list = g_list_next(list);
}
g_list_free(lrul);
}
void
payee_move(guint32 key1, guint32 key2)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *lrul, *list;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
if(txn->kpay == key1)
{
txn->kpay = key2;
txn->dspflags |= FLAG_TMP_EDITED;
}
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
if(entry->kpay == key1)
{
entry->kpay = key2;
}
list = g_list_next(list);
}
lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *entry = list->data;
if(entry->kpay == key1)
{
entry->kpay = key2;
}
list = g_list_next(list);
}
g_list_free(lrul);
}
gboolean
payee_rename(Payee *item, const gchar *newname)
{
Payee *existitem;
gchar *stripname;
gboolean retval = FALSE;
stripname = g_strdup(newname);
g_strstrip(stripname);
existitem = da_pay_get_by_name(stripname);
if( existitem != NULL && existitem->key != item->key)
{
DB( g_print("- error, same name already exist with other key %d <> %d\n", existitem->key, item->key) );
}
else
{
DB( g_print("- renaming\n") );
da_pay_rename (item, stripname);
retval = TRUE;
}
g_free(stripname);
return retval;
}
static gint
payee_glist_name_compare_func(Payee *a, Payee *b)
{
return hb_string_utf8_compare(a->name, b->name);
}
static gint
payee_glist_key_compare_func(Payee *a, Payee *b)
{
return a->key - b->key;
}
GList *
payee_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_pay);
switch(column)
{
case HB_GLIST_SORT_NAME:
return g_list_sort(list, (GCompareFunc)payee_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)payee_glist_key_compare_func);
break;
}
}
gboolean
payee_load_csv(gchar *filename, gchar **error)
{
gboolean retval;
GIOChannel *io;
gchar *tmpstr;
gint io_stat;
gchar **str_array;
const gchar *encoding;
gint nbcol;
encoding = homebank_file_getencoding(filename);
DB( g_print(" -> encoding should be %s\n", encoding) );
retval = TRUE;
*error = NULL;
io = g_io_channel_new_file(filename, "r", NULL);
if(io != NULL)
{
if( encoding != NULL )
{
g_io_channel_set_encoding(io, encoding, NULL);
}
for(;;)
{
io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, NULL);
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_NORMAL)
{
if( tmpstr != NULL)
{
DB( g_print("\n + strip\n") );
hb_string_strip_crlf(tmpstr);
DB( g_print(" + split '%s'\n", tmpstr) );
str_array = g_strsplit (tmpstr, ";", 2);
// payee;category : later paymode?
nbcol = g_strv_length (str_array);
if( nbcol > 2 )
{
*error = _("invalid CSV format");
retval = FALSE;
DB( g_print(" + error %s\n", *error) );
}
else
{
Payee *pay = NULL;
Category *cat = NULL;
if( nbcol >= 1 )
{
DB( g_print(" add pay:'%s' ?\n", str_array[0]) );
pay = da_pay_append_if_new(str_array[0]);
DB( g_print(" pay: %p\n", pay) );
if( pay != NULL )
{
GLOBALS->changes_count++;
}
}
if( nbcol == 2 )
{
DB( g_print(" add cat:'%s'\n", str_array[1]) );
cat = da_cat_append_ifnew_by_fullname(str_array[1]);
DB( g_print(" cat: %p %p\n", cat, pay) );
if( cat != NULL )
{
if( pay != NULL)
{
DB( g_print(" set default cat to %d\n", cat->key) );
pay->kcat = cat->key;
}
GLOBALS->changes_count++;
}
}
}
g_strfreev (str_array);
}
g_free(tmpstr);
}
}
g_io_channel_unref (io);
}
return retval;
}
void
payee_save_csv(gchar *filename)
{
GIOChannel *io;
GList *lpay, *list;
gchar *outstr;
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
lpay = list = payee_glist_sorted(HB_GLIST_SORT_NAME);
while (list != NULL)
{
Payee *item = list->data;
gchar *fullcatname;
if(item->key != 0)
{
fullcatname = NULL;
if( item->kcat > 0 )
{
Category *cat = da_cat_get(item->kcat);
if( cat != NULL )
{
fullcatname = cat->fullname;
}
}
if( fullcatname != NULL )
outstr = g_strdup_printf("%s;%s\n", item->name, fullcatname);
else
outstr = g_strdup_printf("%s;\n", item->name);
DB( g_print(" + export %s %s\n", item->name, fullcatname) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
//#1999250 don't free here...
//g_free(fullcatname);
}
list = g_list_next(list);
}
g_list_free(lpay);
g_io_channel_unref (io);
}
DB( g_print(" export ok\n") );
}
homebank-5.9.7/src/ui-flt-widget.h 0000664 0001750 0001750 00000003042 14736461415 016247 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_FILTER_WIDGET_GTK_H__
#define __HB_FILTER_WIDGET_GTK_H__
/* list display account */
enum
{
LST_FAVFLT_KEY,
LST_FAVFLT_NAME,
NUM_LST_FAVFLT
};
struct ui_flt_popover_data
{
Filter *filter;
GSimpleActionGroup *actions;
GtkWindow *parent;
GtkWidget *box;
GtkWidget *combobox;
GtkWidget *menubutton;
};
/* = = = = = = = = = = */
GtkListStore *lst_lst_favfilter_model_new(void);
void
ui_flt_popover_hub_save (GtkWidget *widget, gpointer user_data);
void ui_flt_manage_header_sensitive(GtkWidget *widget, gpointer user_data);
GtkWidget *ui_flt_popover_hub_get_combobox(GtkBox *box, gpointer user_data);
Filter *ui_flt_popover_hub_get(GtkBox *box, gpointer user_data);
GtkWidget *create_popover_widget(GtkWindow *parent, Filter *filter);
#endif homebank-5.9.7/src/hub-account.c 0000664 0001750 0001750 00000043340 14736461407 015777 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hub-account.h"
#include "dsp-mainwindow.h"
#include "list-account.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern HbKvData CYA_ACC_TYPE[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void da_accgrp_free(PnlAccGrp *item)
{
DB( g_print("da_accgrp_free\n") );
DB( g_print(" free '%s'\n", item->name) );
if(item->name)
g_free(item->name);
if(item->acclist)
g_ptr_array_free (item->acclist, TRUE);
g_free(item);
}
static PnlAccGrp *da_accgrp_malloc(void)
{
DB( g_print("da_accgrp_malloc\n") );
return g_malloc0(sizeof(PnlAccGrp));
}
static void da_accgrp_destroy(GHashTable *h_group)
{
GHashTableIter grp_iter;
gpointer key, value;
DB( g_print("\n[hub-account] groups free\n") );
if(h_group == NULL)
return;
g_hash_table_iter_init (&grp_iter, h_group);
while (g_hash_table_iter_next (&grp_iter, &key, &value))
{
PnlAccGrp *group = value;
da_accgrp_free(group);
}
g_hash_table_destroy (h_group);
}
static GHashTable *da_accgrp_new(void)
{
DB( g_print("\n[hub-account] groups new\n") );
return g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GHashTable *ui_hub_account_groups_get(GtkTreeView *treeview, gint groupby, gboolean showall)
{
GHashTable *h_group;
GList *lacc, *elt;
gchar *groupname;
gint nballoc;
DB( g_print("\n[hub-account] groups get\n") );
nballoc = da_acc_length ();
DB( g_print(" %d accounts\n", nballoc) );
h_group = da_accgrp_new();
lacc = g_hash_table_get_values(GLOBALS->h_acc);
elt = g_list_first(lacc);
while (elt != NULL)
{
Account *acc = elt->data;
PnlAccGrp *group;
//#1674045 ony rely on nosummary
//if( showall || !(acc->flags & (AF_CLOSED|AF_NOSUMMARY)) )
if( showall || !(acc->flags & AF_NOSUMMARY) )
{
switch( groupby )
{
case DSPACC_GROUP_BY_BANK:
{
groupname = _("(no institution)");
if( (acc->bankname != NULL) && strlen(acc->bankname) > 0 )
groupname = acc->bankname;
}
break;
case DSPACC_GROUP_BY_GROUP:
{
Group *grp = da_grp_get(acc->kgrp);
groupname = _("(no group)");
if( grp != NULL && grp->key > 0 )
groupname = grp->name;
}
break;
default:
//pre 5.1.3 historical by type display
groupname = hbtk_get_label(CYA_ACC_TYPE, acc->type);
break;
}
//#1820853 groupname could be NULL
if( groupname != NULL )
{
if( g_hash_table_contains(h_group, groupname) == FALSE )
{
group = da_accgrp_malloc();
group->name = g_strdup(groupname);
group->acclist = g_ptr_array_sized_new(nballoc);
g_hash_table_insert(h_group, g_strdup(groupname), group );
}
group = g_hash_table_lookup(h_group, groupname);
if( group != NULL )
{
g_ptr_array_add(group->acclist, (gpointer)acc);
}
}
}
elt = g_list_next(elt);
}
g_list_free(lacc);
return h_group;
}
static void ui_hub_account_groups_compute(GHashTable *h_accgrp, PnlAccGrp *totaccgrp)
{
GHashTableIter grp_iter;
gpointer key, value;
PnlAccGrp *gt;
guint j;
DB( g_print("\n[hub-account] groups compute\n") );
if( !h_accgrp || !totaccgrp)
return;
gt = totaccgrp;
gt->bal_recon = 0;
gt->bal_clear = 0;
gt->bal_today = 0;
gt->bal_future = 0;
g_hash_table_iter_init (&grp_iter, h_accgrp);
while (g_hash_table_iter_next (&grp_iter, &key, &value))
{
PnlAccGrp *g = value;
if(!g)
continue;
DB( g_print(" g '%s'\n", g->name) );
g->showtotal = TRUE;
g->bal_recon = 0;
g->bal_clear = 0;
g->bal_today = 0;
g->bal_future = 0;
for(j=0;jacclist->len;j++)
{
Account *acc = g_ptr_array_index(g->acclist, j);
if(acc)
{
//#1896441 outflow summary
if( (acc->flags & AF_OUTFLOWSUM) == FALSE )
{
g->bal_recon += hb_amount_base(acc->bal_recon, acc->kcur);
g->bal_clear += hb_amount_base(acc->bal_clear, acc->kcur);
g->bal_today += hb_amount_base(acc->bal_today, acc->kcur);
g->bal_future += hb_amount_base(acc->bal_future, acc->kcur);
//#1950234 show subtotal when single with foreign currency
if( (g->acclist->len == 1) && (acc->kcur == GLOBALS->kcur) )
{
g->showtotal = FALSE;
}
}
else
{
DB( g_print(" '%s' is outflow\n", acc->name) );
}
}
}
DB( g_print(" + total :: %.2f %.2f %.2f %.2f - showsub:%d\n", g->bal_recon, g->bal_clear, g->bal_today, g->bal_future, g->showtotal) );
//sum for grand total
gt->bal_recon += g->bal_recon;
gt->bal_clear += g->bal_clear;
gt->bal_today += g->bal_today;
gt->bal_future += g->bal_future;
}
}
void ui_hub_account_populate(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
GtkTreeIter iter1, child_iter;
Account *acc;
guint j, nbtype;
GHashTable *h_group;
GHashTableIter grp_iter;
gpointer key, value;
DB( g_print("\n[hub-account] populate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
// clear previous
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
gtk_tree_store_clear (GTK_TREE_STORE(model));
if(data->totaccgrp != NULL)
da_accgrp_free(data->totaccgrp);
if(data->h_accgrp != NULL)
da_accgrp_destroy(data->h_accgrp);
h_group = ui_hub_account_groups_get(GTK_TREE_VIEW(data->LV_acc), PREFS->pnl_acc_show_by, data->showall);
data->h_accgrp = h_group;
data->totaccgrp = da_accgrp_malloc();
ui_hub_account_groups_compute(data->h_accgrp, data->totaccgrp);
DB( g_print("\n\n populate listview, %d group(s)\n", g_hash_table_size(h_group)) );
nbtype = 0;
g_hash_table_iter_init (&grp_iter, h_group);
while (g_hash_table_iter_next (&grp_iter, &key, &value))
{
PnlAccGrp *group = value;
gint position;
if(group != NULL)
{
nbtype++;
//1: Header: Bank, Cash, ...
DB( g_print(" g '%s'\n", (gchar *)key) );
//#1663399 keep group 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].name != NULL && t < 32)
{
if( !strcmp(CYA_ACC_TYPE[t].name, 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_DATAS, group,
-1);
//2: Accounts for real
for(j=0;jacclist->len;j++)
{
acc = g_ptr_array_index(group->acclist, j);
DB( g_print(" + '%s' :: %.2f %.2f %.2f %.2f\n", acc->name, acc->bal_recon, acc->bal_clear, 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_DATATYPE, DSPACC_TYPE_NORMAL,
LST_DSPACC_DATAS, acc,
-1);
}
// insert group total line
//if(group->acclist->len > 1)
//#1950234 show subtotal when single with foreign currency
if( group->showtotal == TRUE )
{
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_DATAS, group,
-1);
}
}
}
// Grand total
if( nbtype > 1 )
{
gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
LST_DSPACC_DATATYPE, DSPACC_TYPE_TOTAL,
LST_DSPACC_DATAS, data->totaccgrp,
-1);
}
ui_hub_account_groups_compute(data->h_accgrp, data->totaccgrp);
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
}
//this func should only recompute balance of acc groups
void ui_hub_account_compute(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
DB( g_print("\n[hub-account] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ui_hub_account_groups_compute(data->h_accgrp, data->totaccgrp);
gtk_widget_queue_draw (data->LV_acc);
}
static void ui_hub_account_browse(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");
if( account_has_website(data->acc) )
{
homebank_util_url_show(data->acc->website);
}
}
static void ui_hub_account_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_hub_account_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));
}
/* Callback function for the undo action */
/*static void
activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
}*/
static void
ui_hub_account_clipboard (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GtkClipboard *clipboard;
GString *node;
//g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
node = lst_accview_to_string(GTK_TREE_VIEW(data->LV_acc), TRUE);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
static void
ui_hub_account_print (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GString *node;
//g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
node = lst_accview_to_string(GTK_TREE_VIEW(data->LV_acc), HB_STRING_PRINT);
hb_print_listview(GTK_WINDOW(data->window), node->str, NULL, _("Your accounts"), NULL, FALSE);
g_string_free(node, TRUE);
}
static void
ui_hub_account_activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_boolean (old_state),
g_variant_get_boolean (new_state)) );
data->showall = g_variant_get_boolean (new_state);
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
}
static void
ui_hub_account_activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
//struct hbfile_data *data = user_data;
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_string (old_state, NULL),
g_variant_get_string (new_state, NULL)) );
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_TYPE;
if( !strcmp("bank", g_variant_get_string(new_state, NULL)) )
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_BANK;
else
if( !strcmp("group", g_variant_get_string(new_state, NULL)) )
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_GROUP;
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
}
static const GActionEntry actions[] = {
// name, function(), type, state,
{ "groupby" , ui_hub_account_activate_radio , "s", "'type'", NULL, {0,0,0} },
{ "showall" , ui_hub_account_activate_toggle , NULL, "false" , NULL, {0,0,0} },
{ "clipboard" , ui_hub_account_clipboard , NULL, NULL , NULL, {0,0,0} },
{ "print" , ui_hub_account_print , NULL, NULL , NULL, {0,0,0} },
// { "paste", activate_action, NULL, NULL, NULL, {0,0,0} },
};
void ui_hub_account_setup(struct hbfile_data *data)
{
GAction *action;
GVariant *new_state;
if( !G_IS_SIMPLE_ACTION_GROUP(data->action_group_acc) )
return;
action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "showall");
if( action )
{
new_state = g_variant_new_boolean (data->showall);
g_simple_action_set_state (G_SIMPLE_ACTION(action), new_state);
}
action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "groupby");
if( action )
{
const gchar *value = "type";
if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_BANK )
value = "bank";
else
if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_GROUP )
value = "group";
new_state = g_variant_new_string (value);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
}
}
void ui_hub_account_dispose(struct hbfile_data *data)
{
DB( g_print("\n[hub-account] dispose\n") );
if(data->h_accgrp != NULL)
da_accgrp_destroy(data->h_accgrp);
if(data->totaccgrp != NULL)
da_accgrp_free(data->totaccgrp);
data->h_accgrp = NULL;
data->totaccgrp = NULL;
}
GtkWidget *ui_hub_account_create(struct hbfile_data *data)
{
GtkWidget *hub, *label, *widget, *scrollwin, *treeview, *tbar, *bbox, *image;
DB( g_print("\n[hub-account] create\n") );
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margins(GTK_WIDGET(hub), 0, SPACING_SMALL, SPACING_SMALL, SPACING_SMALL);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (hub), scrollwin);
treeview = (GtkWidget *)lst_accview_new();
data->LV_acc = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (hub), tbar);
label = make_label_group(_("Your accounts"));
gtk_box_prepend (GTK_BOX (tbar), label);
//gmenu test (see test folder into gtk)
GMenu *menu, *section;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Copy to clipboard"), "actions.clipboard");
g_menu_append (section, _("Print..."), "actions.print");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section (menu, _("Group by"), G_MENU_MODEL(section));
g_menu_append (section, _("type") , "actions.groupby::type");
g_menu_append (section, _("group") , "actions.groupby::group");
g_menu_append (section, _("institution"), "actions.groupby::bank");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Show all"), "actions.showall");
g_object_unref (section);
GSimpleActionGroup *group = g_simple_action_group_new ();
data->action_group_acc = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = gtk_menu_button_new();
gtk_box_prepend (GTK_BOX (bbox), widget);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
image = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
g_object_set (widget, "image", image, NULL);
gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expandall = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapseall = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_BROWSER, _("Browse Website"));
data->BT_browse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
g_signal_connect (G_OBJECT (data->BT_expandall ), "clicked" , G_CALLBACK (ui_hub_account_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapseall), "clicked" , G_CALLBACK (ui_hub_account_collapse_all), NULL);
g_signal_connect (G_OBJECT (data->BT_browse ), "clicked" , G_CALLBACK (ui_hub_account_browse), NULL);
return hub;
}
homebank-5.9.7/src/ui-tag.h 0000664 0001750 0001750 00000004355 14736461415 014764 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_TAG_GTK_H__
#define __HB_TAG_GTK_H__
enum
{
LST_DEFTAG_TOGGLE,
LST_DEFTAG_DATAS,
NUM_LST_DEFTAG
};
#define LST_DEFTAG_SORT_USETXN 2
#define LST_DEFTAG_SORT_USECFG 3
#define LST_DEFTAG_SORT_NAME 4
struct ui_tag_manage_dialog_data
{
GtkWidget *dialog;
GActionGroup * actions;
gboolean mapped_done;
GtkWidget *BT_showusage;
GtkWidget *RE_addreveal;
GtkWidget *ST_name;
GtkWidget *LV_tag;
GtkWidget *BT_add;
GtkWidget *BT_edit;
GtkWidget *BT_merge;
GtkWidget *BT_delete;
gboolean usagefilled;
gint change;
};
struct ui_tag_dialog_data
{
GtkWidget *dialog;
};
/* = = = = = = = = = = */
guint32 ui_tag_combobox_get_key(GtkComboBox *combobox);
void ui_tag_combobox_populate_except(GtkComboBoxText *combobox, guint except_key);
void ui_tag_combobox_populate(GtkComboBoxText *combobox);
GtkWidget *ui_tag_combobox_new(GtkWidget *label);
GtkWidget *
ui_tag_popover_list(GtkWidget *entry);
/* = = = = = = = = = = */
guint ui_tag_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter);
void ui_tag_listview_quick_select(GtkTreeView *treeview, const gchar *uri);
void ui_tag_listview_add(GtkTreeView *treeview, Tag *item);
guint32 ui_tag_listview_get_selected_key(GtkTreeView *treeview);
void ui_tag_listview_remove_selected(GtkTreeView *treeview);
void ui_tag_listview_populate(GtkWidget *view, gint insert_type);
GtkWidget *ui_tag_listview_new(gboolean withtoggle, gboolean withcount);
GtkWidget *ui_tag_manage_dialog (void);
#endif
homebank-5.9.7/src/ui-archive.c 0000644 0001750 0001750 00000106544 15005624436 015620 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-archive.h"
#include "ui-account.h"
#include "ui-category.h"
#include "ui-payee.h"
#include "ui-txn-split.h"
#include "ui-tag.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "list-scheduled.h"
#include "gtk-dateentry.h"
#include "hbtk-switcher.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern char *CYA_ARC_FREQ[];
extern HbKvData CYA_ARC_ORDINAL[];
extern HbKvData CYA_ARC_WEEKDAY[];
extern HbKvData CYA_ARC_WEEKEND[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_arc_listview_select_by_pointer(GtkTreeView *treeview, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
gboolean valid;
Archive *arc = user_data;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Archive *tmp_arc;
gtk_tree_model_get (model, &iter, LST_DSPUPC_DATAS, &tmp_arc, -1);
if( arc == tmp_arc )
{
gtk_tree_selection_select_iter (selection, &iter);
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static void
ui_arc_manage_update(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
Archive *arc;
gboolean selected, recurring, sensitive;
DB( g_print("\n[ui-scheduled] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter);
DB( g_print(" toolbutton sensitive\n") );
sensitive = selected;
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_rem, sensitive);
gtk_widget_set_sensitive(data->BT_dup, sensitive);
gtk_widget_set_sensitive(data->BT_schedule, sensitive);
gtk_widget_set_sensitive(data->IM_wrnwe, sensitive);
gtk_widget_set_sensitive(data->SW_recurrent, sensitive);
DB( g_print(" scheduled popover sensitive\n") );
recurring = FALSE;
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arc, -1);
if( arc->rec_flags & TF_RECUR )
recurring = TRUE;
}
gtk_widget_set_sensitive(data->GR_recurrent, recurring);
gtk_widget_set_sensitive(data->SW_recurrent, TRUE);
gtk_widget_set_sensitive(data->LB_next, recurring);
gtk_widget_set_sensitive(data->PO_next, recurring);
if(recurring == TRUE)
{
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_relative));
DB( g_print(" relative = %d (recu=%d cm=%d)\n", sensitive, recurring, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_relative))) );
gtk_widget_set_sensitive(data->LB_relative, sensitive);
gtk_widget_set_sensitive(data->CY_ordinal, sensitive);
gtk_widget_set_sensitive(data->CY_weekday, sensitive);
//gint freq = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_rec_freq));
gint freq = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_rec_freq));
sensitive = (freq == AUTO_FREQ_MONTH) ? TRUE : FALSE;
hb_widget_visible(data->CM_relative, sensitive);
hb_widget_visible(data->LB_relative, sensitive);
hb_widget_visible(data->CY_ordinal, sensitive);
hb_widget_visible(data->CY_weekday, sensitive);
//update freq label
//#2108847 not translated
gchar *txt = _(ui_arc_listview_get_freq_label(freq));
gtk_label_set_text(GTK_LABEL(data->LB_rec_every2), txt);
sensitive = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_relative));
gtk_widget_set_sensitive(data->LB_weekend, sensitive);
gtk_widget_set_sensitive(data->CY_weekend, sensitive);
sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_limit));
gtk_widget_set_sensitive(data->NB_limit, sensitive);
gtk_widget_set_sensitive(data->LB_posts, sensitive);
}
DB( g_print(" row changed\n") );
if(selected)
{
GtkTreePath *path;
// redraw the row to display/hide the icon
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_model_row_changed(model, path, &iter);
gtk_tree_path_free (path);
// gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_arc));
//gtk_widget_queue_draw (GTK_WIDGET(data->LV_arc));
}
}
static void
ui_arc_manage_cb_check_weekend(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
gboolean visible;
gint tmpwe;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//update weekend info
tmpwe = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_weekend));
visible = FALSE;
if( tmpwe==ARC_WEEKEND_BEFORE || tmpwe==ARC_WEEKEND_AFTER )
{
guint32 tmpdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_next));
if( tmpdate != scheduled_get_txn_real_postdate(tmpdate, tmpwe) )
visible = TRUE;
}
DB( g_print(" warn weekend %d\n", visible) );
hb_widget_visible(data->IM_wrnwe, visible);
}
static void
ui_arc_manage_cb_relative_changed(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
gint ordinal, nextwd, every;
GDate date;
guint32 nextdate = GLOBALS->today;
DB( g_print("\n[ui-scheduled] cb relative changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ordinal = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_ordinal));
nextwd = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_weekday));
every = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->NB_rec_every));
g_date_set_julian(&date, GLOBALS->today);
nextdate = scheduled_date_get_next_relative(&date, ordinal, nextwd, every);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_next), nextdate);
ui_arc_manage_update(widget, user_data);
}
static void
ui_arc_manage_cb_schedule_changed(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
Archive *arcitem;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean selected, sensitive;
DB( g_print("\n[ui-scheduled] cb schedule changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
sensitive = FALSE;
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter);
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arcitem, -1);
arcitem->rec_flags &= ~(TF_RECUR);
sensitive = gtk_switch_get_active(GTK_SWITCH(data->SW_recurrent)) ? TRUE : FALSE;
if(sensitive)
arcitem->rec_flags |= TF_RECUR;
}
ui_arc_manage_update(widget, user_data);
}
static void
ui_arc_manage_populate_listview(struct ui_arc_manage_data *data)
{
GtkTreeModel *model;
GtkTreeIter iter;
GList *list;
gchar *needle;
gboolean hastext;
gint i, typsch, typtpl;
DB( g_print("\n[ui-scheduled] populate listview\n") );
typsch = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_typsch));
typtpl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_typtpl));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_arc));
hastext = (gtk_entry_get_text_length (GTK_ENTRY(data->ST_search)) >= 2) ? TRUE : FALSE;
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
DB( g_print(" typsch=%d / typtpl=%d\n", typsch, typtpl) );
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_arc), NULL); /* Detach model from view */
i=0;
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
gboolean insert = FALSE;
if( (typsch) && (item->rec_flags & TF_RECUR) )
insert = TRUE;
if( (typtpl) && !(item->rec_flags & TF_RECUR) )
insert = TRUE;
if( insert )
{
gboolean qinsert = TRUE;
if(hastext)
{
qinsert = filter_tpl_search_match(needle, item);
}
if( qinsert )
{
gtk_list_store_insert_with_values (GTK_LIST_STORE(model), &iter, -1,
LST_DSPUPC_DATAS, item, //data struct
// LST_DEFARC_OLDPOS, i, //oldpos
-1);
}
}
//DB( g_print(" populate_treeview: %d %08x\n", i, list->data) );
i++; list = g_list_next(list);
}
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_arc), model); /* Re-attach model to view */
g_object_unref(model);
// gtk_tree_view_expand_all (GTK_TREE_VIEW(data->LV_arc));
}
static void
ui_arc_manage_cb_add_clicked(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
Archive *item;
gint typsch, typtpl;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-scheduled] cb add\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_arc));
GtkWidget *dialog;
Transaction *new_txn = da_transaction_malloc();
gboolean result;
dialog = create_deftransaction_window(GTK_WINDOW(data->dialog), TXN_DLG_ACTION_ADD, TXN_DLG_TYPE_TPL, 0);
deftransaction_set_transaction(dialog, new_txn);
result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == HB_RESPONSE_ADD)
{
deftransaction_get(dialog, NULL);
item = da_archive_malloc();
//item->memo = g_strdup_printf(_("(template %d)"), g_list_length(GLOBALS->arc_list) + 1);
da_archive_init_from_transaction(item, new_txn, FALSE);
typsch = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_typsch));
typtpl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_typtpl));
if( typsch && !typtpl )
item->rec_flags |= TF_RECUR;
item->rec_every = 1;
item->rec_freq = AUTO_FREQ_MONTH;
item->nextdate = GLOBALS->today;
//GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
da_archive_append_new(item);
DB( g_print(" kacc: '%d'\n", item->kacc) );
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DSPUPC_DATAS, item,
// LST_DEFARC_OLDPOS, 0,
-1);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &iter);
data->change++;
}
deftransaction_dispose(dialog, NULL);
gtk_window_destroy (GTK_WINDOW(dialog));
da_transaction_free(new_txn);
}
static void
ui_arc_manage_cb_edit_clicked(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean selected;
Archive *arcitem;
GtkWidget *dialog;
Transaction *new_txn = da_transaction_malloc();
gboolean result;
DB( g_print("\n[ui-scheduled] cb edit\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter);
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arcitem, -1);
dialog = create_deftransaction_window(GTK_WINDOW(data->dialog), TXN_DLG_ACTION_EDIT, TXN_DLG_TYPE_TPL, 0);
da_transaction_init_from_template(new_txn, arcitem);
deftransaction_set_transaction(dialog, new_txn);
result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
deftransaction_get(dialog, NULL);
da_archive_init_from_transaction(arcitem, new_txn, FALSE);
//this redraw the row
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &iter);
data->change++;
}
deftransaction_dispose(dialog, NULL);
gtk_window_destroy (GTK_WINDOW(dialog));
da_transaction_free(new_txn);
}
}
static void
ui_arc_manage_cb_dup_clicked(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Archive *arcitem, *newitem;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-scheduled] dup (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arcitem, -1);
newitem = da_archive_clone(arcitem);
if( newitem )
{
if( da_archive_append(newitem) )
{
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DSPUPC_DATAS, newitem,
// LST_DEFARC_OLDPOS, 0,
-1);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &iter);
}
}
}
}
static void
ui_arc_manage_cb_delete_clicked(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Archive *item;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-scheduled] cb delete (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gchar *title;
gchar *secondtext;
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &item, -1);
//5.7.4 check if template is used
if( !(item->rec_flags & TF_RECUR) )
{
if( template_is_account_used(item) == TRUE )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_INFO,
_("Template delete"),
_("This template is used as an account template and cannot be deleted.")
);
return;
}
}
//#1940103 as memo can be null, use (no memo) instead
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->memo != NULL ? item->memo : _("(no memo)") );
secondtext = _("If you delete a scheduled/template, it will be permanently lost.");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
GLOBALS->arc_list = g_list_remove(GLOBALS->arc_list, item);
data->change++;
}
//DB( g_print(" delete =%08x (pos=%d)\n", entry, g_list_index(data->tmp_list, entry) ) );
}
}
static void
ui_arc_manage_set(GtkWidget *widget, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
Archive *item;
DB( g_print("\n[ui-scheduled] set popover\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &item, -1);
g_signal_handlers_block_by_func (G_OBJECT (data->PO_next), G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
g_signal_handlers_block_by_func (G_OBJECT (data->CY_weekend), G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
g_signal_handlers_block_by_func (G_OBJECT (data->SW_recurrent ), G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
g_signal_handlers_block_by_func (G_OBJECT (data->CM_limit), G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
gtk_switch_set_active(GTK_SWITCH(data->SW_recurrent), (item->rec_flags & TF_RECUR) ? 1 : 0);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_rec_every), item->rec_every);
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_rec_freq), item->rec_freq);
hbtk_switcher_set_active(HBTK_SWITCHER(data->RA_rec_freq), item->rec_freq);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_next), item->nextdate);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_limit), (item->rec_flags & TF_LIMIT) ? 1 : 0);
DB( g_print(" nb_limit = %d %g\n", item->limit, (gdouble)item->limit) );
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->NB_limit), (gdouble)item->limit);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_weekend), item->weekend);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_relative), (item->rec_flags & TF_RELATIVE) ? 1 : 0);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_ordinal), item->rec_ordinal);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_weekday), item->rec_weekday);
g_signal_handlers_unblock_by_func (G_OBJECT (data->CM_limit), G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
g_signal_handlers_unblock_by_func (G_OBJECT (data->SW_recurrent ), G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
g_signal_handlers_unblock_by_func (G_OBJECT (data->CY_weekend ), G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
g_signal_handlers_unblock_by_func (G_OBJECT (data->PO_next ), G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
}
}
static void
ui_arc_manage_getlast(struct ui_arc_manage_data *data)
{
GtkTreeModel *model;
GtkTreeIter iter;
Archive *item;
gboolean active;
DB( g_print("\n[ui-scheduled] getlast\n") );
if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &item, -1);
//#1863484: reset flag to enable remove auto and limit :)
item->rec_flags &= ~(TF_RECUR|TF_LIMIT|TF_RELATIVE);
active = gtk_switch_get_active(GTK_SWITCH(data->SW_recurrent));
if(active == 1) item->rec_flags |= TF_RECUR;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->NB_rec_every));
item->rec_every = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->NB_rec_every));
//item->rec_freq = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_rec_freq));
item->rec_freq = hbtk_switcher_get_active(HBTK_SWITCHER(data->RA_rec_freq));
item->nextdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_next));
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_limit));
if(active == 1) item->rec_flags |= TF_LIMIT;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->NB_limit));
item->limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->NB_limit));
item->weekend = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_weekend));
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_relative));
if(active == 1) item->rec_flags |= TF_RELATIVE;
item->rec_ordinal = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_ordinal));
item->rec_weekday = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_weekday));
//#1906953 add skip weekend
scheduled_nextdate_weekend_adjust(item);
data->change++;
}
}
static void
ui_arc_manage_cb_popover_closed(GtkWidget *popover, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
gboolean selected;
DB( g_print("\n[ui-scheduled] cb popover closed\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(popover, GTK_TYPE_WINDOW)), "inst_data");
data = user_data;
/* redraw the row to display/hide the icon */
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter);
if(selected)
{
ui_arc_manage_getlast(data);
path = gtk_tree_model_get_path(model, &iter);
#if MYDEBUG == 1
gchar *spath = gtk_tree_path_to_string(path);
g_print(" selected '%s'\n", spath);
g_free(spath);
#endif
gtk_tree_model_row_changed(model, path, &iter);
gtk_tree_path_free (path);
}
}
static gboolean ui_arc_manage_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct ui_arc_manage_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else
if (keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_arc);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void
ui_arc_manage_cb_selection_changed(GtkTreeSelection *treeselection, gpointer user_data)
{
struct ui_arc_manage_data *data;
GtkWidget *treeview;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean selected;
Archive *arcitem;
DB( g_print("\n[ui-scheduled] selection\n") );
treeview = (GtkWidget *)gtk_tree_selection_get_tree_view (treeselection);
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(treeview, GTK_TYPE_WINDOW)), "inst_data");
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), &model, &iter);
DB( g_print(" a row is selected = %d\n", selected) );
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DSPUPC_DATAS, &arcitem, -1);
ui_arc_manage_set(treeview, NULL);
}
ui_arc_manage_cb_check_weekend(GTK_WIDGET(treeview), NULL);
ui_arc_manage_update(GTK_WIDGET(treeview), NULL);
}
static void
ui_arc_manage_cb_row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
ui_arc_manage_cb_edit_clicked(GTK_WIDGET(treeview), userdata);
}
static void
ui_arc_manage_cb_flttype_changed (GtkToggleButton *button, gpointer user_data)
{
ui_arc_manage_populate_listview(user_data);
//g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
}
static gboolean
ui_arc_manage_cleanup(struct ui_arc_manage_data *data, gint result)
{
gboolean doupdate = FALSE;
DB( g_print("\n[ui-scheduled] cleanup\n") );
da_archive_glist_sorted(HB_GLIST_SORT_NAME);
GLOBALS->changes_count += data->change;
return doupdate;
}
static void
ui_arc_manage_setup(struct ui_arc_manage_data *data)
{
DB( g_print("\n[ui-scheduled] setup\n") );
DB( g_print(" init data\n") );
//init GList
data->tmp_list = NULL; //hb-glist_clone_list(GLOBALS->arc_list, sizeof(struct _Archive));
data->change = 0;
DB( g_print(" set widgets default\n") );
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_rec_freq), AUTO_FREQ_MONTH);
hbtk_switcher_set_active(HBTK_SWITCHER(data->RA_rec_freq), AUTO_FREQ_MONTH);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->BT_typsch), TRUE);
DB( g_print(" populate\n") );
ui_arc_manage_populate_listview(data);
DB( g_print(" connect widgets signals\n") );
g_signal_connect (data->BT_typsch, "toggled", G_CALLBACK (ui_arc_manage_cb_flttype_changed), data);
g_signal_connect (data->BT_typtpl, "toggled", G_CALLBACK (ui_arc_manage_cb_flttype_changed), data);
//#1999297 seems to crash because of internal gtk search
//gtk_tree_view_set_search_entry(GTK_TREE_VIEW(data->LV_arc), GTK_ENTRY(data->ST_search));
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(data->LV_arc), FALSE);
g_signal_connect (data->ST_search, "search-changed", G_CALLBACK (ui_arc_manage_cb_flttype_changed), data);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_arc)), "changed", G_CALLBACK (ui_arc_manage_cb_selection_changed), NULL);
g_signal_connect (data->LV_arc, "row-activated", G_CALLBACK (ui_arc_manage_cb_row_activated), NULL);
g_signal_connect (data->BT_add , "clicked", G_CALLBACK (ui_arc_manage_cb_add_clicked), NULL);
g_signal_connect (data->BT_rem , "clicked", G_CALLBACK (ui_arc_manage_cb_delete_clicked), NULL);
g_signal_connect (data->BT_edit, "clicked", G_CALLBACK (ui_arc_manage_cb_edit_clicked), NULL);
g_signal_connect (data->BT_dup , "clicked", G_CALLBACK (ui_arc_manage_cb_dup_clicked), NULL);
//popover
g_signal_connect (data->PO_recurrent, "closed", G_CALLBACK (ui_arc_manage_cb_popover_closed), data);
g_signal_connect (data->SW_recurrent, "notify::active", G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
g_signal_connect (data->PO_next, "changed", G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
g_signal_connect (data->RA_rec_freq, "changed", G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
g_signal_connect (data->CM_relative, "toggled", G_CALLBACK (ui_arc_manage_cb_relative_changed), NULL);
g_signal_connect (data->CY_ordinal, "changed", G_CALLBACK (ui_arc_manage_cb_relative_changed), NULL);
g_signal_connect (data->CY_weekday, "changed", G_CALLBACK (ui_arc_manage_cb_relative_changed), NULL);
g_signal_connect (data->CY_weekend, "changed", G_CALLBACK (ui_arc_manage_cb_check_weekend), NULL);
g_signal_connect (data->CM_limit, "toggled", G_CALLBACK (ui_arc_manage_cb_schedule_changed), NULL);
if(data->ext_arc != NULL)
ui_arc_listview_select_by_pointer(GTK_TREE_VIEW(data->LV_arc), data->ext_arc);
}
static gboolean
ui_arc_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_arc_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[ui-scheduled] mapped\n") );
ui_arc_manage_setup(data);
ui_arc_manage_update(data->LV_arc, NULL);
//gtk_widget_grab_focus(GTK_WIDGET(data->LV_arc));
data->mapped_done = TRUE;
return FALSE;
}
static GtkWidget *
ui_arc_manage_create_scheduling(struct ui_arc_manage_data *data)
{
GtkWidget *content, *group_grid, *hbox, *vbox, *expander, *label, *widget;
gint row;
content = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (content), hbox);
//on/off switch
widget = gtk_switch_new();
data->SW_recurrent = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = gtk_date_entry_new(NULL);
data->PO_next = widget;
gtk_box_append (GTK_BOX (hbox), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_WARNING);
gtk_widget_set_tooltip_text(widget, _("The post date will be shifted outside of the weekend"));
data->IM_wrnwe = widget;
gtk_box_append (GTK_BOX (hbox), widget);
label = gtk_label_new_with_mnemonic (_("Next _date:"));
data->LB_next = label;
gtk_box_append (GTK_BOX (hbox), label);
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
data->GR_recurrent = vbox;
gtk_box_prepend (GTK_BOX (content), vbox);
label = make_label_group(_("Recurrence pattern"));
gtk_box_prepend (GTK_BOX (vbox), label);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
widget = hbtk_switcher_new(GTK_ORIENTATION_VERTICAL);
data->RA_rec_freq = widget;
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_ARC_FREQ, FALSE);
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
gtk_box_prepend (GTK_BOX (hbox), widget);
// group :: Scheduled insertion
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_box_prepend (GTK_BOX (hbox), group_grid);
row = 0;
label = make_label_widget(_("Ever_y:"));
data->LB_rec_every = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 1, 1);
widget = make_numeric(label, 1, 100);
data->NB_rec_every = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = gtk_label_new(NULL);
data->LB_rec_every2 = label;
//gtk_widget_set_hexpand(label, TRUE);
gtk_box_prepend (GTK_BOX (hbox), label);
row++;
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_halign(hbox, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 0, row, 1, 1);
widget = gtk_check_button_new();
data->CM_relative = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
label = make_label_widget(_("The"));
data->LB_relative = label;
gtk_box_prepend (GTK_BOX (hbox), label);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_halign(hbox, GTK_ALIGN_START);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_ARC_ORDINAL);
data->CY_ordinal = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_combo_box_new_with_data(label, CYA_ARC_WEEKDAY);
data->CY_weekday = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
row++;
expander = gtk_expander_new_with_mnemonic(_("More options"));
data->EX_options = expander;
gtk_grid_attach (GTK_GRID (group_grid), expander, 0, row, 2, 1);
//gtk_box_prepend (GTK_BOX (content), expander);
// group :: Scheduled insertion
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);
hb_widget_set_margin(GTK_WIDGET(group_grid), SPACING_SMALL);
gtk_expander_set_child (GTK_EXPANDER(expander), group_grid);
row++;
label = make_label_widget(_("Week end:"));
data->LB_weekend = label;
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_ARC_WEEKEND);
gtk_widget_set_hexpand(widget, FALSE);
data->CY_weekend = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
row++;
label = make_label_widget(_("_Stop after:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, row, 1, 1);
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (group_grid), hbox, 1, row, 1, 1);
widget = gtk_check_button_new();
data->CM_limit = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = make_numeric(label, 1, 366);
data->NB_limit = widget;
hbtk_box_prepend (GTK_BOX (hbox), widget);
label = gtk_label_new_with_mnemonic (_("posts"));
data->LB_posts = label;
gtk_box_prepend (GTK_BOX (hbox), label);
gtk_widget_show_all(content);
return content;
}
GtkWidget *
ui_arc_manage_dialog (Archive *ext_arc)
{
struct ui_arc_manage_data *data;
GtkWidget *dialog, *content_area, *bbox, *hbox, *vbox, *tbar;
GtkWidget *box, *treeview, *scrollwin;
GtkWidget *widget, *content, *menubutton, *image, *label;
gint w, h, dw, dh;
DB( g_print("\n[ui-scheduled] dialog\n") );
data = g_malloc0(sizeof(struct ui_arc_manage_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage scheduled/template transactions"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
data->ext_arc = ext_arc;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
//dialog content
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
content = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(content), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), content);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (content), hbox);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (hbox), box);
widget = gtk_toggle_button_new_with_label(_("Scheduled"));
data->BT_typsch = widget;
gtk_box_prepend (GTK_BOX (box), widget);
widget = gtk_toggle_button_new_with_label(_("Template"));
data->BT_typtpl = widget;
gtk_box_prepend (GTK_BOX (box), widget);
widget = make_search ();
data->ST_search = widget;
gtk_widget_set_size_request(widget, HB_MINWIDTH_SEARCH, -1);
gtk_widget_set_halign(widget, GTK_ALIGN_END);
gtk_box_prepend (GTK_BOX (hbox), widget);
// list + toolbar
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (content), vbox);
// listview
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
//#1970509 enable hscrollbar
//gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
treeview = (GtkWidget *)ui_arc_listview_widget_new();
data->LV_arc = treeview;
gtk_widget_set_size_request(treeview, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_rem = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
//widget = gtk_button_new_with_mnemonic(_("_Edit"));
widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
data->BT_edit = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DUPLICATE, _("Duplicate"));
data->BT_dup = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
//schedule button
menubutton = gtk_menu_button_new ();
data->BT_schedule = menubutton;
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_DOWN );
gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
//gtk_widget_set_hexpand (menubutton, TRUE);
gtk_widget_show_all(menubutton);
gtk_box_prepend(GTK_BOX(bbox), menubutton);
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
label = gtk_label_new_with_mnemonic (_("_Schedule"));
gtk_box_prepend (GTK_BOX(box), label);
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_box_prepend (GTK_BOX(box), image);
gtk_container_add(GTK_CONTAINER(menubutton), box);
GtkWidget *template = ui_arc_manage_create_scheduling(data);
GtkWidget *popover = create_popover (menubutton, template, GTK_POS_TOP);
data->PO_recurrent = popover;
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_arc_manage_mapped), &dialog);
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_arc_manage_cb_on_key_press), (gpointer)data);
ui_arc_listview_widget_columns_order_load(GTK_TREE_VIEW(data->LV_arc));
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all(content);
gtk_widget_show (dialog);
// wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
ui_arc_listview_widget_columns_order_save(GTK_TREE_VIEW(data->LV_arc));
// cleanup and destroy
ui_arc_manage_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-import-qif.c 0000644 0001750 0001750 00000027702 14736461407 016247 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
//#include "ui-assist-import.h"
#include "hb-import.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static void
hb_qif_parser_parse(ImportContext *ctx, GenFile *genfile);
/* = = = = = = = = = = = = = = = = */
GList *homebank_qif_import(ImportContext *ictx, GenFile *genfile)
{
DB( g_print("\n[import] homebank QIF\n") );
hb_qif_parser_parse(ictx, genfile);
return ictx->gen_lst_txn;;
}
/* = = = = = = = = = = = = = = = = */
gdouble
hb_qif_parser_get_amount(gchar *string)
{
gdouble amount;
gint l, i;
gchar *new_str, *p;
gint ndcount = 0;
gchar dc;
//DB( g_print("\n[qif] hb_qif_parser_get_amount\n") );
amount = 0.0;
dc = '?';
//TODO: we should use here ==> hb_string_dup_raw_amount_clean(const gchar *string, gint digits)
l = strlen(string) - 1;
// the first non-digit is a grouping, or a decimal separator
// if the non-digit is after a 3 digit serie, it might be a grouping
for(i=l;i>=0;i--)
{
//DB( g_print(" %d :: %c :: ds='%c' ndcount=%d\n", i, string[i], dc, ndcount) );
if( string[i] == '-' || string[i] == '+' ) continue;
if( g_ascii_isdigit( string[i] ))
{
ndcount++;
}
else
{
if( (ndcount != 3) && (string[i] == '.' || string[i]==',') )
{
dc = string[i];
}
ndcount = 0;
}
}
//DB( g_print(" s='%s' :: ds='%c'\n", string, dc) );
new_str = g_malloc (l+3); //#1214077
p = new_str;
for(i=0;i<=l;i++)
{
if( g_ascii_isdigit( string[i] ) || string[i] == '-' )
{
*p++ = string[i];
}
else
if( string[i] == dc )
*p++ = '.';
}
*p++ = '\0';
amount = g_ascii_strtod(new_str, NULL);
//DB( g_print(" -> amount was='%s' => to='%s' double='%f'\n", string, new_str, amount) );
g_free(new_str);
return amount;
}
/* O if m-d-y (american)
1 if d-m-y (european) */
/* obsolete 4.5
static gint
hb_qif_parser_guess_datefmt(ImportContext *ctx)
{
gboolean retval = TRUE;
GList *qiflist;
gboolean r, valid;
gint d, m, y;
DB( g_print("(qif) get_datetype\n") );
qiflist = g_list_first(ctx->gen_lst_txn);
while (qiflist != NULL)
{
GenTxn *item = qiflist->data;
r = hb_qif_parser_get_dmy(item->date, &d, &m, &y);
valid = g_date_valid_dmy(d, m, y);
DB( g_print(" -> date: %s :: %d %d %d :: %d\n", item->date, d, m, y, valid ) );
if(valid == FALSE)
{
retval = FALSE;
break;
}
qiflist = g_list_next(qiflist);
}
return retval;
}
*/
static gint
hb_qif_parser_get_block_type(gchar *qif_line)
{
gchar **typestr;
gint type = QIF_NONE;
DB( g_print("--------\n[qif] block type\n") );
//DB( g_print(" -> str: %s type: %d\n", qif_line, type) );
if(g_str_has_prefix(qif_line, "!Account") || g_str_has_prefix(qif_line, "!account"))
{
type = QIF_ACCOUNT;
}
else
{
typestr = g_strsplit(qif_line, ":", 2);
if( g_strv_length(typestr) == 2 )
{
gchar *qif_line = g_utf8_casefold(typestr[1], -1);
//DB( g_print(" -> str[1]: %s\n", typestr[1]) );
if( g_str_has_prefix(qif_line, "bank") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "cash") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "ccard") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "invst") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "oth a") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "oth l") )
{
type = QIF_TRANSACTION;
}
else
if( g_str_has_prefix(qif_line, "security") )
{
type = QIF_SECURITY;
}
else
if( g_str_has_prefix(qif_line, "prices") )
{
type = QIF_PRICES;
}
g_free(qif_line);
}
g_strfreev(typestr);
}
//DB( g_print(" -> return type: %d\n", type) );
return type;
}
static void
hb_qif_parser_parse(ImportContext *ctx, GenFile *genfile)
{
GIOChannel *io;
GenTxn tran = { 0 };
DB( g_print("\n[qif] hb_qif_parser_parse\n") );
io = g_io_channel_new_file(genfile->filepath, "r", NULL);
if(io != NULL)
{
gchar *qif_line;
GError *err = NULL;
gint io_stat;
gint type = QIF_NONE;
gchar *value = NULL;
GenAcc tmpgenacc = { 0 };
GenAcc *genacc;
guint row = 0;
DB( g_print(" -> encoding should be %s\n", genfile->encoding) );
if( genfile->encoding != NULL )
{
g_io_channel_set_encoding(io, genfile->encoding, NULL);
}
DB( g_print(" -> encoding is %s\n", g_io_channel_get_encoding(io)) );
// within a single qif file, if there is no accoutn data
// then txn are related to a single account
genacc = NULL;
for(;;)
{
io_stat = g_io_channel_read_line(io, &qif_line, NULL, NULL, &err);
if( io_stat == G_IO_STATUS_EOF )
break;
if( io_stat == G_IO_STATUS_ERROR )
{
DB (g_print(" + ERROR %s\n",err->message));
break;
}
if( io_stat == G_IO_STATUS_NORMAL )
{
hb_string_strip_crlf(qif_line);
//DB (g_print("** new QIF line: '%s' **\n", qif_line));
//start qif parsing
if(g_str_has_prefix(qif_line, "!")) /* !Type: or !Option: or !Account otherwise ignore */
{
type = hb_qif_parser_get_block_type(qif_line);
DB ( g_print("-> ---- QIF block: '%s' (type = %d) ----\n", qif_line, type) );
}
value = &qif_line[1];
if( type == QIF_ACCOUNT )
{
switch(qif_line[0])
{
case 'N': // Name
{
g_strstrip(value);
tmpgenacc.name = g_strdup(value);
DB ( g_print(" name: '%s'\n", value) );
break;
}
case 'T': // Type of account
{
DB ( g_print(" type: '%s'\n", value) );
// added for 5.0.1
if( g_ascii_strcasecmp("CCard", value) == 0 )
{
tmpgenacc.is_ccard = TRUE;
}
break;
}
/*
case 'D': // Description
{
DB ( g_print(" description: '%s'\n", value) );
break;
}
case 'L': // Credit limit (only for credit card accounts)
if(g_str_has_prefix(qif_line, "L"))
{
DB ( g_print(" credit limit: '%s'\n", value) );
break;
}
case '$': // Statement balance amount
{
DB ( g_print(" balance: '%s'\n", value) );
break;
}*/
case '^': // end
{
Account *dst_acc;
genacc = hb_import_gen_acc_get_next (ctx, FILETYPE_QIF, tmpgenacc.name, NULL);
// number is null for QIF because it is not a QIF account field into specification
dst_acc = hb_import_acc_find_existing(tmpgenacc.name, NULL );
if( dst_acc != NULL )
{
DB( g_print(" - set dst_acc to %d\n", dst_acc->key) );
genacc->kacc = dst_acc->key;
}
genacc->is_ccard = tmpgenacc.is_ccard;
g_free(tmpgenacc.name);
tmpgenacc.name = NULL;
tmpgenacc.is_ccard = FALSE;
DB ( g_print(" ----------------\n") );
break;
}
}
}
if( type == QIF_TRANSACTION )
{
switch(qif_line[0])
{
case 'D': //date
{
gchar *ptr;
// US Quicken seems to be using the ' to indicate post-2000 two-digit years
//(such as 01/01'00 for Jan 1 2000)
ptr = g_strrstr (value, "\'");
if(ptr != NULL) { *ptr = '/'; }
ptr = g_strrstr (value, " ");
if(ptr != NULL) { *ptr = '0'; }
g_free(tran.date);
tran.date = g_strdup(value);
break;
}
case 'T': // amount
{
tran.amount = hb_qif_parser_get_amount(value);
break;
}
case 'C': // cleared status
{
tran.reconciled = FALSE;
if(g_str_has_prefix(value, "X") || g_str_has_prefix(value, "R") )
{
tran.reconciled = TRUE;
}
tran.cleared = FALSE;
if(g_str_has_prefix(value, "*") || g_str_has_prefix(value, "c") )
{
tran.cleared = TRUE;
}
break;
}
case 'N': // check num or reference number
{
if(*value != '\0')
{
g_free(tran.number);
g_strstrip(value);
tran.number = g_strdup(value);
}
break;
}
case 'P': // payee
{
if(*value != '\0')
{
g_free(tran.payee);
g_strstrip(value);
tran.rawpayee = g_strdup(value);
}
break;
}
case 'M': // memo
{
if(*value != '\0')
{
g_free(tran.memo);
tran.rawmemo = g_strdup(value);
}
break;
}
case 'L': // category
{
// LCategory of transaction
// L[Transfer account name]
// LCategory of transaction/Class of transaction
// L[Transfer account]/Class of transaction
// this is managed at insertion
if(*value != '\0')
{
g_free(tran.category);
g_strstrip(value);
tran.category = g_strdup(value);
}
break;
}
case 'S':
case 'E':
case '$':
{
if(tran.nb_splits < TXN_MAX_SPLIT)
{
switch(qif_line[0])
{
case 'S': // split category
{
GenSplit *s = &tran.splits[tran.nb_splits];
if(*value != '\0')
{
g_free(s->category);
g_strstrip(value);
s->category = g_strdup(value);
}
break;
}
case 'E': // split memo
{
GenSplit *s = &tran.splits[tran.nb_splits];
if(*value != '\0')
{
g_free(s->memo);
s->memo = g_strdup(value);
}
break;
}
case '$': // split amount
{
GenSplit *s = &tran.splits[tran.nb_splits];
s->amount = hb_qif_parser_get_amount(value);
// $ line normally end a split
#if MYDEBUG == 1
g_print(" -> new split added: [%d] S=%s, E=%s, $=%.2f\n", tran.nb_splits, s->category, s->memo, s->amount);
#endif
tran.nb_splits++;
break;
}
}
}
// end split
break;
}
case '^': // end of line
{
GenTxn *newitem;
//fix: 380550
if( tran.date )
{
//5.8 #2063416 same date txn
tran.row = row++;
//ensure we have an account
//todo: check this
if(genacc == NULL)
{
genacc = hb_import_gen_acc_get_next (ctx, FILETYPE_QIF, NULL, NULL);
}
tran.account = g_strdup(genacc->name);
DB ( g_print(" -> store qif txn: dat:'%s' amt:%.2f pay:'%s' mem:'%s' cat:'%s' acc:'%s' nbsplit:%d\n", tran.date, tran.amount, tran.payee, tran.memo, tran.category, tran.account, tran.nb_splits) );
newitem = da_gen_txn_malloc();
da_gen_txn_move(&tran, newitem);
da_gen_txn_append(ctx, newitem);
}
//unvalid tran
tran.date = 0;
//todo: should clear mem alloc here
tran.nb_splits = 0;
break;
}
}
// end of switch
}
// end QIF_TRANSACTION
}
// end of stat normal
g_free(qif_line);
}
// end of for loop
g_io_channel_unref (io);
}
}
homebank-5.9.7/src/hb-group.c 0000664 0001750 0001750 00000011721 14736461407 015310 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-group.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void da_grp_free(Group *item)
{
DB( g_print("da_group_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->name) );
g_free(item->name);
g_free(item);
}
}
Group *da_grp_malloc(void)
{
DB( g_print("da_group_malloc\n") );
return g_malloc0(sizeof(Group));
}
void da_grp_destroy(void)
{
DB( g_print("da_group_destroy\n") );
g_hash_table_destroy(GLOBALS->h_grp);
}
void da_grp_new(void)
{
Group *item;
DB( g_print("da_group_new\n") );
GLOBALS->h_grp = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_grp_free);
// insert our 'no group'
item = da_grp_malloc();
item->name = g_strdup("");
da_grp_insert(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint
da_grp_length(void)
{
return g_hash_table_size(GLOBALS->h_grp);
}
static void
da_grp_max_key_ghfunc(gpointer key, Group *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
guint32
da_grp_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_grp, (GHFunc)da_grp_max_key_ghfunc, &max_key);
return max_key;
}
gboolean
da_grp_remove(guint32 key)
{
DB( g_print("da_grp_remove %d\n", key) );
return g_hash_table_remove(GLOBALS->h_grp, &key);
}
gboolean
da_grp_insert(Group *item)
{
guint32 *new_key;
DB( g_print("da_grp_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
g_hash_table_insert(GLOBALS->h_grp, new_key, item);
return TRUE;
}
gboolean
da_grp_append(Group *item)
{
Group *existitem;
DB( g_print("da_grp_append\n") );
existitem = da_grp_get_by_name( item->name);
if( existitem == NULL )
{
item->key = da_grp_get_max_key() + 1;
da_grp_insert(item);
return TRUE;
}
DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
return FALSE;
}
Group *
da_grp_get_by_name(gchar *rawname)
{
Group *retval = NULL;
gchar *stripname;
GHashTableIter iter;
gpointer key, value;
DB( g_print("da_grp_get_by_name\n") );
if( rawname )
{
stripname = g_strdup(rawname);
g_strstrip(stripname);
if( strlen(stripname) > 0 )
{
g_hash_table_iter_init (&iter, GLOBALS->h_grp);
while (g_hash_table_iter_next (&iter, &key, &value))
{
Group *item = value;
//if( item->type != type )
// continue;
if( stripname && item->name )
{
if(!strcasecmp(stripname, item->name))
{
retval = item;
break;
}
}
}
}
g_free(stripname);
}
return retval;
}
Group *
da_grp_get(guint32 key)
{
//DB( g_print("da_grp_get\n") );
return g_hash_table_lookup(GLOBALS->h_grp, &key);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void group_delete_unused(void)
{
guint32 i, max_key;
gboolean *used;
GList *lst_acc, *lnk_acc;
max_key = da_grp_get_max_key();
used = g_malloc0((max_key + 1) * sizeof(gboolean));
if( used )
{
// collect usage
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
if(acc)
used[acc->kgrp] = TRUE;
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
//clean unused
for(i=1;iname) );
da_grp_remove(i);
}
}
}
g_free(used);
}
}
static gint
group_glist_name_compare_func(Group *a, Group *b)
{
return hb_string_utf8_compare(a->name, b->name);
}
static gint
group_glist_key_compare_func(Group *a, Group *b)
{
return a->key - b->key;
}
GList *group_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_grp);
switch(column)
{
case HB_GLIST_SORT_NAME:
return g_list_sort(list, (GCompareFunc)group_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)group_glist_key_compare_func);
break;
}
}
homebank-5.9.7/src/hb-xml.c 0000644 0001750 0001750 00000205532 15057111223 014740 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-transaction.h"
#include "hb-xml.h"
#include "ui-dialogs.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
// v0.1 to v0.2 : we must change account reference by making a +1 to its index references
static void homebank_upgrade_to_v02(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
GHashTable *h_old_acc;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v02\n") );
//keep old hashtable with us
h_old_acc = GLOBALS->h_acc;
da_acc_new();
lst_acc = g_hash_table_get_values(h_old_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
acc->key++;
acc->pos++;
da_acc_insert (acc);
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
entry->kacc++;
entry->kxferacc++;
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
//we loose some small memory here
g_hash_table_steal_all(h_old_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
entry->kacc++;
entry->kxferacc++;
list = g_list_next(list);
}
}
// v0.2 to v0.3 : we must assume categories exists : bugs 303886, 303738
static void homebank_upgrade_to_v03(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v03\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
da_transaction_consistency(entry);
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
da_archive_consistency(entry);
list = g_list_next(list);
}
}
static void homebank_upgrade_to_v04(void)
{
DB( g_print("\n[hb-xml] homebank_upgrade_to_v04\n") );
da_archive_glist_sorted(HB_GLIST_SORT_NAME);
}
// v0.4 to v0.5 :
// we must assume kxferacc exists in archives for internal xfer : bug 528923
// if not, delete automation from the archive
static void homebank_upgrade_to_v05(void)
{
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v05\n") );
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
da_archive_consistency(entry);
list = g_list_next(list);
}
}
// v0.5 to v0.6 : we must change kxferacc to 0 on non Xfer transactions
//#677351
static void homebank_upgrade_to_v06(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v06\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
da_transaction_consistency(entry);
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
da_archive_consistency(entry);
list = g_list_next(list);
}
}
// v0.7 AF_BUDGET deleted instead of AF_NOBUDGET
static void homebank_upgrade_to_v07(void)
{
GList *lacc, *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v07\n") );
lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc = list->data;
if( acc->flags & AF_OLDBUDGET ) // budget include
{
acc->flags &= ~(AF_OLDBUDGET);
}
else
{
acc->flags |= AF_NOBUDGET;
}
list = g_list_next(list);
}
g_list_free(lacc);
}
static void homebank_upgrade_to_v08(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v08\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
da_transaction_consistency(entry);
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void homebank_upgrade_to_v10(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v10\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
entry->status = TXN_STATUS_NONE;
if(entry->flags & OLDF_VALID)
entry->status = TXN_STATUS_RECONCILED;
else
if(entry->flags & OLDF_REMIND)
entry->status = TXN_OLDSTATUS_REMIND;
//remove those flags
entry->flags &= ~(OLDF_VALID|OLDF_REMIND);
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void homebank_upgrade_to_v11(void)
{
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v11\n") );
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
entry->status = TXN_STATUS_NONE;
if(entry->flags & OLDF_VALID)
entry->status = TXN_STATUS_RECONCILED;
else
if(entry->flags & OLDF_REMIND)
entry->status = TXN_OLDSTATUS_REMIND;
//remove those flags
entry->flags &= ~(OLDF_VALID|OLDF_REMIND);
list = g_list_next(list);
}
}
// v0.6 to v0.7 : assign a default currency
static void homebank_upgrade_to_v12(void)
{
DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") );
// set a base currency to the hbfile if not
DB( g_print("GLOBALS->kcur %d\n", GLOBALS->kcur) );
ui_dialog_upgrade_choose_currency();
}
static void homebank_upgrade_to_v12_7(void)
{
GList *lst_acc, *lnk_acc;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v12\n") );
//#1674045 exclude closed account from everywhere to
//keep continuity for user that don't want to change this
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
if( acc->flags & AF_CLOSED )
{
if( !(acc->flags & AF_NOSUMMARY) )
acc->flags |= AF_NOSUMMARY;
if( !(acc->flags & AF_NOBUDGET) )
acc->flags |= AF_NOBUDGET;
if( !(acc->flags & AF_NOREPORT) )
acc->flags |= AF_NOREPORT;
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void homebank_upgrade_to_v13(void)
{
GList *list;
guint32 newkey;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v13\n") );
//#1008629 assign a key to each archive
newkey = 1;
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
item->key = newkey++;
list = g_list_next(list);
}
}
static void homebank_upgrade_to_v14(void)
{
GList *lst_acc, *lnk_acc, *lasg;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v14\n") );
//internal xfer no more a payment => goto a flags
//update every txn/arc
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
if( item->paymode == OLDPAYMODE_INTXFER )
{
item->flags |= OF_INTXFER;
item->paymode = PAYMODE_NONE;
}
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
if( item->paymode == OLDPAYMODE_INTXFER )
{
item->flags |= OF_INTXFER;
item->paymode = PAYMODE_NONE;
}
list = g_list_next(list);
}
//assignment now have position+name, so initiate it
lasg = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *item = list->data;
item->pos = item->key;
list = g_list_next(list);
}
g_list_free(lasg);
}
// migrate 5.9.5
static void homebank_upgrade_to_v14_595(void)
{
DB( g_print("\n[hb-xml] homebank_upgrade_to_v14_595\n") );
//#2121309 fix potential bad position
da_acc_pos_sanitize();
}
// migrate 5.9.2
static void homebank_upgrade_to_v14_592(void)
{
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v14_592\n") );
//##2112135 fix limit bad flag
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
if( item->rec_flags & (TF_LIMIT) && ((item->limit >= 366) || item->nextdate >= HB_MAXDATE) )
{
DB( g_print(" fix arc limit %d\n", item->limit) );
item->rec_flags &= ~(TF_LIMIT | TF_RECUR);
item->limit = 0;
item->nextdate = GLOBALS->today;
}
list = g_list_next(list);
}
}
// migrate 5.9
static void homebank_upgrade_to_v14_59(void)
{
GList *lst_acc, *lnk_acc;
GList *list;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v14_59\n") );
//#chnage template/scheduled
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
//#move arc flags
if( item->flags & (OLDF_AUTO|OLDF_LIMIT))
{
DB( g_print(" move tpl flags to rec_flags\n") );
item->rec_flags = 0;
if(item->flags & OLDF_AUTO)
item->rec_flags |= TF_RECUR;
if(item->flags & OLDF_LIMIT)
item->rec_flags |= TF_LIMIT;
item->flags &= ~(OLDF_AUTO|OLDF_LIMIT);
}
//#clean arc flags OLDF_ADDED|OLDF_CHANGED
if( item->flags & (OLDF_ADDED|OLDF_CHANGED))
{
DB( g_print(" clean tpl flags\n") );
item->flags &= ~(OLDF_ADDED|OLDF_CHANGED);
}
list = g_list_next(list);
}
//#remind status move to flag
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
//also remove tpl/sch flags
item->flags &= ~(OF_REMIND|OLDF_AUTO|OLDF_LIMIT);
if( item->status == TXN_OLDSTATUS_REMIND )
{
DB( g_print(" move remind status\n") );
item->flags |= OF_REMIND;
item->status = 0;
}
if( item->status == TXN_OLDSTATUS_VOID )
{
DB( g_print(" update void status\n") );
item->status = TXN_STATUS_VOID;
}
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void homebank_upgrade_to_v14_12(void)
{
gint oldsmode, smode;
DB( g_print("\n[hb-xml] homebank_upgrade_to_v14_12\n") );
//convert old smode to new one
oldsmode = GLOBALS->auto_smode;
smode = 0;
switch(oldsmode)
{
case 0:
smode = ARC_POSTMODE_PAYOUT;
break;
case 1:
{
if(GLOBALS->auto_nbdays == 0)
smode = ARC_POSTMODE_DUEDATE;
else
smode = ARC_POSTMODE_ADVANCE;
}
break;
}
DB( g_print("migrated smode: %d to %d\n", oldsmode, smode) );
GLOBALS->auto_smode = smode;
}
// lower v0.6 : we must assume categories/payee exists
// and strong link to xfer
// #632496
static void homebank_upgrade_lower_v06(void)
{
GList *lst_acc, *lnk_acc;
Category *cat;
Payee *pay;
GList *lrul, *list;
DB( g_print("\n[hb-xml] homebank_upgrade_lower_v06\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *entry = list->data;
//also strong link internal xfer
if(entry->paymode == OLDPAYMODE_INTXFER && entry->kxfer == 0)
{
Transaction *child = transaction_old_get_child_transfer(entry);
if(child != NULL)
{
transaction_xfer_change_to_child(entry, child);
}
}
da_transaction_consistency(entry);
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *entry = list->data;
cat = da_cat_get(entry->kcat);
if(cat == NULL)
{
DB( g_print(" !! fixing cat for rul: %d is unknown\n", entry->kcat) );
entry->kcat = 0;
}
pay = da_pay_get(entry->kpay);
if(pay == NULL)
{
DB( g_print(" !! fixing pay for rul: %d is unknown\n", entry->kpay) );
entry->kpay = 0;
}
list = g_list_next(list);
}
g_list_free(lrul);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
** misc xml attributes methods
*/
static void hb_xml_append_txt(GString *gstring, gchar *attrname, gchar *value)
{
if(value != NULL && *value != 0)
{
gchar *escaped = g_markup_escape_text(value, -1);
g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped);
g_free(escaped);
}
}
static void
append_escaped_text (GString *str,
const gchar *text,
gssize length)
{
const gchar *p;
const gchar *end;
gunichar c;
p = text;
end = text + length;
while (p < end)
{
const gchar *next;
next = g_utf8_next_char (p);
switch (*p)
{
case '&':
g_string_append (str, "&");
break;
case '<':
g_string_append (str, "<");
break;
case '>':
g_string_append (str, ">");
break;
case '\'':
g_string_append (str, "'");
break;
case '"':
g_string_append (str, """);
break;
default:
c = g_utf8_get_char (p);
if ((0x1 <= c && c <= 0x8) ||
(0xa <= c && c <= 0xd) || //changed here from b<->c to a<->d
(0xe <= c && c <= 0x1f) ||
(0x7f <= c && c <= 0x84) ||
(0x86 <= c && c <= 0x9f))
g_string_append_printf (str, "%x;", c);
else
g_string_append_len (str, p, next - p);
break;
}
p = next;
}
}
// we override g_markup_escape_text from glib to encode \n (LF) & \r (CR)
static void hb_xml_append_txt_crlf(GString *gstring, gchar *attrname, gchar *value)
{
if(value != NULL && *value != 0)
{
gssize length;
GString *escaped;
//gchar *escaped = g_markup_escape_text(value, -1);
length = strlen (value);
escaped = g_string_sized_new (length);
append_escaped_text (escaped, value, length);
g_string_append_printf(gstring, " %s=\"%s\"", attrname, escaped->str);
g_string_free (escaped, TRUE);
}
}
static void hb_xml_append_int0(GString *gstring, gchar *attrname, guint32 value)
{
g_string_append_printf(gstring, " %s=\"%d\"", attrname, value);
}
static void hb_xml_append_int(GString *gstring, gchar *attrname, guint32 value)
{
if(value != 0)
{
hb_xml_append_int0(gstring, attrname, value);
}
}
static void hb_xml_append_amt(GString *gstring, gchar *attrname, gdouble amount)
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
//we must use this, as fprintf use locale decimal settings and not '.'
g_ascii_dtostr (buf, sizeof (buf), amount);
g_string_append_printf(gstring, " %s=\"%s\"", attrname, buf);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void filter_group_import_keys(Filter *flt, gint group, gchar *text)
{
gchar **str_array;
gboolean is_set;
gint len, i;
DB( g_print(" import keys '%s'\n", text) );
str_array = g_strsplit (text, ",", -1);
len = g_strv_length( str_array );
for(i=0;i '%s'", is_set ? "set" : "**skip**") );
}
g_strfreev(str_array);
}
static void filter_group_import(Filter *flt, gint group, const gchar *text)
{
gchar **str_array = NULL;
gchar **bol_array = NULL;
gint i, len;
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] group import '%s'\n", flt->name == NULL ? "noname" : flt->name ) );
DB( g_print(" '%d' > '%s'\n", group, text) );
str_array = g_strsplit (text, "|", 2);
if( g_strv_length( str_array ) != 2 )
goto end;
flt->option[group] = atoi(str_array[0]);
switch(group)
{
//0:option 1:range 2:min 3:max
case FLT_GRP_DATE:
bol_array = g_strsplit (str_array[1], ",", 3);
len = g_strv_length( bol_array );
flt->range = atoi(bol_array[0]);
if(len >= 2 && flt->range == FLT_RANGE_MISC_CUSTOM)
{
flt->mindate = atoi(bol_array[1]);
flt->maxdate = atoi(bol_array[2]);
}
g_strfreev(bol_array);
break;
case FLT_GRP_ACCOUNT:
flt->option[group] = atoi(str_array[0]);
filter_group_import_keys(flt, FLT_GRP_ACCOUNT, str_array[1]);
break;
case FLT_GRP_PAYEE:
flt->option[group] = atoi(str_array[0]);
filter_group_import_keys(flt, FLT_GRP_PAYEE, str_array[1]);
break;
case FLT_GRP_CATEGORY:
flt->option[group] = atoi(str_array[0]);
filter_group_import_keys(flt, FLT_GRP_CATEGORY, str_array[1]);
break;
case FLT_GRP_TAG:
flt->option[group] = atoi(str_array[0]);
filter_group_import_keys(flt, FLT_GRP_TAG, str_array[1]);
break;
case FLT_GRP_STATUS:
flt->option[group] = atoi(str_array[0]);
bol_array = g_strsplit (str_array[1], ",", -1);
if( g_strv_length( bol_array ) == 3 )
{
flt->sta_non = atoi(bol_array[0]);
flt->sta_clr = atoi(bol_array[1]);
flt->sta_rec = atoi(bol_array[2]);
}
g_strfreev(bol_array);
break;
case FLT_GRP_TYPE:
flt->option[group] = atoi(str_array[0]);
bol_array = g_strsplit (str_array[1], ",", -1);
if( g_strv_length( bol_array ) == 4 )
{
flt->typ_nexp = atoi(bol_array[0]);
flt->typ_ninc = atoi(bol_array[1]);
flt->typ_xexp = atoi(bol_array[2]);
flt->typ_xinc = atoi(bol_array[3]);
}
g_strfreev(bol_array);
break;
case FLT_GRP_PAYMODE:
flt->option[group] = atoi(str_array[0]);
bol_array = g_strsplit (str_array[1], ",", -1);
len = g_strv_length( bol_array );
if( len < NUM_PAYMODE_MAX )
{
for(i=0;ipaymode[id] = TRUE;
}
}
break;
case FLT_GRP_AMOUNT:
flt->option[group] = atoi(str_array[0]);
bol_array = g_strsplit (str_array[1], ",", -1);
if( g_strv_length( bol_array ) == 2 )
{
flt->minamount = g_ascii_strtod(bol_array[0], NULL);
flt->maxamount = g_ascii_strtod(bol_array[1], NULL);
}
break;
case FLT_GRP_TEXT:
flt->option[group] = atoi(str_array[0]);
bol_array = g_strsplit (str_array[1], "¤", -1);
if( g_strv_length( bol_array ) == 3 )
{
flt->exact = atoi(bol_array[0]);
flt->memo = g_strdup(bol_array[1]);
flt->number = g_strdup(bol_array[2]);
}
break;
}
end:
g_strfreev(str_array);
}
static gchar *filter_group_export(Filter *flt, gint group)
{
gchar *retval = NULL;
GString *node;
guint i;
DB( g_print("\n[filter] group export '%s'\n", flt->name == NULL ? "noname" : flt->name )) ;
g_return_val_if_fail( flt != NULL, NULL );
switch(group)
{
case FLT_GRP_DATE:
if(flt->option[group] > 0)
{
//TODO: maybe always keep 4 values here
if(flt->range == FLT_RANGE_MISC_CUSTOM)
retval = g_strdup_printf("%d|%d,%d,%d", flt->option[group], FLT_RANGE_MISC_CUSTOM, flt->mindate, flt->maxdate);
else
retval = g_strdup_printf("%d|%d", flt->option[group], flt->range);
DB( g_printf(" date > '%s'\n", retval) );
}
break;
case FLT_GRP_ACCOUNT:
if(flt->option[group] > 0)
{
node = g_string_sized_new(flt->gbacc->len);
g_string_append_printf(node, "%d|", flt->option[group]);
DB( g_printf("acc len:%d\n", flt->gbacc->len) );
for(i=0;igbacc->len;i++)
{
if( da_flt_status_acc_get(flt, i) == TRUE )
g_string_append_printf(node, "%d,", i);
}
g_string_erase(node, node->len-1, 1);
retval = g_string_free(node, FALSE);
DB( g_printf(" acc > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_PAYEE:
if(flt->option[group] > 0)
{
node = g_string_sized_new(flt->gbpay->len);
g_string_append_printf(node, "%d|", flt->option[group]);
DB( g_printf("pay len:%d\n", flt->gbpay->len) );
for(i=0;igbpay->len;i++)
{
if( da_flt_status_pay_get(flt, i) == TRUE )
g_string_append_printf(node, "%d,", i);
}
g_string_erase(node, node->len-1, 1);
retval = g_string_free(node, FALSE);
DB( g_printf(" pay > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_CATEGORY:
if(flt->option[group] > 0)
{
node = g_string_sized_new(flt->gbcat->len);
g_string_append_printf(node, "%d|", flt->option[group]);
DB( g_printf("cat len:%d\n", flt->gbcat->len) );
for(i=0;igbcat->len;i++)
{
if( da_flt_status_cat_get(flt, i) == TRUE )
g_string_append_printf(node, "%d,", i);
}
g_string_erase(node, node->len-1, 1);
retval = g_string_free(node, FALSE);
DB( g_printf(" cat > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_TAG:
if(flt->option[group] > 0)
{
node = g_string_sized_new(flt->gbtag->len);
g_string_append_printf(node, "%d|", flt->option[group]);
DB( g_printf("tag len:%d\n", flt->gbtag->len) );
for(i=0;igbtag->len;i++)
{
if( da_flt_status_tag_get(flt, i) == TRUE )
g_string_append_printf(node, "%d,", i);
}
g_string_erase(node, node->len-1, 1);
retval = g_string_free(node, FALSE);
DB( g_printf(" tag > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_STATUS:
if(flt->option[group] > 0)
{
node = g_string_sized_new(30);
g_string_append_printf(node, "%d|", flt->option[group]);
g_string_append_printf(node, "%d,", flt->sta_non);
g_string_append_printf(node, "%d,", flt->sta_clr);
g_string_append_printf(node, "%d" , flt->sta_rec);
retval = g_string_free(node, FALSE);
DB( g_printf(" sta > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_TYPE:
if(flt->option[group] > 0)
{
node = g_string_sized_new(30);
g_string_append_printf(node, "%d|", flt->option[group]);
g_string_append_printf(node, "%d,", flt->typ_nexp);
g_string_append_printf(node, "%d,", flt->typ_ninc);
g_string_append_printf(node, "%d,", flt->typ_xexp);
g_string_append_printf(node, "%d" , flt->typ_xinc);
retval = g_string_free(node, FALSE);
DB( g_printf(" typ > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_PAYMODE:
if(flt->option[group] > 0)
{
node = g_string_sized_new(30);
g_string_append_printf(node, "%d|", flt->option[group]);
for(i=0;ipaymode[i] == TRUE )
g_string_append_printf(node, "%d,", i);
}
g_string_erase(node, node->len-1, 1);
retval = g_string_free(node, FALSE);
DB( g_printf(" pay > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_AMOUNT:
if(flt->option[group] > 0)
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
node = g_string_sized_new(30);
g_string_append_printf(node, "%d|", flt->option[group]);
//we must use this, as fprintf use locale decimal settings and not '.'
g_ascii_dtostr (buf, sizeof (buf), flt->minamount);
g_string_append(node, buf);
g_string_append(node, ",");
g_ascii_dtostr (buf, sizeof (buf), flt->maxamount);
g_string_append(node, buf);
retval = g_string_free(node, FALSE);
DB( g_printf(" amt > '%s'\n", retval) );
node = NULL;
}
break;
case FLT_GRP_TEXT:
if(flt->option[group] > 0)
{
node = g_string_sized_new(30);
g_string_append_printf(node, "%d|", flt->option[group]);
g_string_append_printf(node, "%d¤%s¤%s", flt->exact, flt->memo, flt->number);
retval = g_string_free(node, FALSE);
DB( g_printf(" txt > '%s'\n", retval) );
node = NULL;
}
break;
}
return retval;
}
static void hb_xml_append_fltgroup(GString *gstring, gchar *attrname, Filter *flt, gint group)
{
gchar *tmpstr;
DB( g_printf("[xml] append fltgrp for '%s' %d\n", attrname, group) );
tmpstr = filter_group_export(flt, group);
if(tmpstr)
{
DB( g_printf(" > '%s'\n", tmpstr) );
hb_xml_append_txt(gstring, attrname, tmpstr);
g_free(tmpstr);
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void homebank_load_xml_acc(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Account *entry = da_acc_malloc();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "pos" )) { entry->pos = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "type" )) { entry->type = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "curr" )) { entry->kcur = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->name = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "number" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->number = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "bankname")) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->bankname = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "initial" )) { entry->initial = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "minimum" )) { entry->minimum = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "maximum" )) { entry->maximum = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "cheque1" )) { entry->cheque1 = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "cheque2" )) { entry->cheque2 = atoi(attribute_values[i]); }
//5.7
else if(!strcmp (attribute_names[i], "website" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->website = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "notes" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "tpl" )) { entry->karc = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "grp" )) { entry->kgrp = atoi(attribute_values[i]); }
//5.5
else if(!strcmp (attribute_names[i], "ccday" )) { entry->cccday = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "rdate" )) { entry->rdate = atoi(attribute_values[i]); }
}
//all attribute loaded: append
da_acc_insert(entry);
}
static void homebank_load_xml_asg(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Assign *entry = da_asg_malloc();
gint exact = 0;
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "pos" )) { entry->pos = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "field" )) { entry->field = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->search = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "notes" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
//#1999879 assignment by amount do not save
else if(!strcmp (attribute_names[i], "amount" )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); }
// prior v08
else if(!strcmp (attribute_names[i], "exact" )) { exact = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "tags" ))
{
if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 )
{
entry->tags = tags_parse(attribute_values[i]);
}
}
}
/* in v08 exact moved to flag */
if( ctx->file_version <= 0.7)
{
entry->flags = (ASGF_DOCAT|ASGF_DOPAY);
if( exact > 0 )
entry->flags |= ASGF_EXACT;
}
//all attribute loaded: append
//#1892828 append change the pos...
//da_asg_append(entry);
da_asg_insert(entry);
}
static void homebank_load_xml_pay(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Payee *entry = da_pay_malloc();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "category")) { entry->kcat = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "notes" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->notes = g_strdup(attribute_values[i]); }
}
//all attribute loaded: append
da_pay_insert(entry);
}
static void homebank_load_xml_prop(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
if(!strcmp (attribute_names[i], "title" )) { g_free(GLOBALS->owner); GLOBALS->owner = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "curr" )) { GLOBALS->kcur = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "car_category")) { GLOBALS->vehicle_category = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "auto_smode" )) { GLOBALS->auto_smode = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "auto_weekday")) { GLOBALS->auto_weekday = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "auto_nbmonths")) { GLOBALS->auto_nbmonths = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "auto_nbdays" )) { GLOBALS->auto_nbdays = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "earnbyh" )) { GLOBALS->lifen_earnbyh = g_ascii_strtod(attribute_values[i], NULL); }
}
}
static void homebank_load_xml_cat(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Category *entry = da_cat_malloc();
gboolean budget;
gint i, j;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "parent")) { entry->parent = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
budget = FALSE;
for(j=0;j<=12;j++)
{
gchar *tmpname;
tmpname = g_strdup_printf ("b%d", j);
if(!(strcmp (attribute_names[i], tmpname))) { entry->budget[j] = g_ascii_strtod(attribute_values[i], NULL); }
g_free(tmpname);
if(entry->budget[j]) budget = TRUE;
}
if(budget == TRUE)
entry->flags |= GF_BUDGET;
}
//all attribute loaded: append
da_cat_insert( entry);
}
static void homebank_load_xml_cur(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Currency *entry = da_cur_malloc ();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "iso" )) { entry->iso_code = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "symb" )) { entry->symbol = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "syprf" )) { entry->sym_prefix = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "dchar" )) { entry->decimal_char = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "gchar" )) { entry->grouping_char = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "frac" )) { entry->frac_digits = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "rate" )) { entry->rate = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "mdate ")) { entry->mdate = atoi(attribute_values[i]); }
}
//all attribute loaded: append
da_cur_insert (entry);
}
static void homebank_load_xml_grp(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Group *entry = da_grp_malloc();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
//else if(!strcmp (attribute_names[i], "type")) { entry->type = atoi(attribute_values[i]); }
//else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
}
//all attribute loaded: append
da_grp_insert(entry);
}
static void homebank_load_xml_flt(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Filter *entry = da_flt_malloc();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "dat" )) { filter_group_import(entry, FLT_GRP_DATE, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "acc" )) { filter_group_import(entry, FLT_GRP_ACCOUNT, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "pay" )) { filter_group_import(entry, FLT_GRP_PAYEE, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "cat" )) { filter_group_import(entry, FLT_GRP_CATEGORY, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "tag" )) { filter_group_import(entry, FLT_GRP_TAG, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "txt" )) { filter_group_import(entry, FLT_GRP_TEXT, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "amt" )) { filter_group_import(entry, FLT_GRP_AMOUNT, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "mod" )) { filter_group_import(entry, FLT_GRP_PAYMODE, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "sta" )) { filter_group_import(entry, FLT_GRP_STATUS, attribute_values[i]); }
else if(!strcmp (attribute_names[i], "typ" )) { filter_group_import(entry, FLT_GRP_TYPE, attribute_values[i]); }
}
//5.8 force alldate if off
if( entry->option[FLT_GRP_DATE] == 0 )
{
entry->option[FLT_GRP_DATE] = 1;
entry->range = FLT_RANGE_MISC_ALLDATE;
}
//all attribute loaded: append
da_flt_insert(entry);
}
static void homebank_load_xml_tag(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Tag *entry = da_tag_malloc();
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
//else if(!strcmp (attribute_names[i], "flags")) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "name" )) { entry->name = g_strdup(attribute_values[i]); }
}
//all attribute loaded: append
da_tag_insert(entry);
}
static void homebank_load_xml_fav(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Archive *entry = da_archive_malloc();
gchar *scat = NULL;
gchar *samt = NULL;
gchar *smem = NULL;
gboolean split = FALSE;
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "key" )) { entry->key = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "amount" )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "account" )) { entry->kacc = atoi(attribute_values[i]); }
//#1673260
else if(!strcmp (attribute_names[i], "damt" )) { entry->xferamount = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "grpflg" )) { entry->grpflg = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "st" )) { entry->status = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "category" )) { entry->kcat = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "wording" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "info" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->number = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "tags" ))
{
if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 )
{
entry->tags = tags_parse(attribute_values[i]);
}
}
else if(!strcmp (attribute_names[i], "recflg" )) { entry->rec_flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "nextdate" )) { entry->nextdate = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "every" )) { entry->rec_every = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "unit" )) { entry->rec_freq = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "limit" )) { entry->limit = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "weekend" )) { entry->weekend = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "gap" )) { entry->daygap = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "ordn" )) { entry->rec_ordinal = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "wkdy" )) { entry->rec_weekday = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "scat" )) { scat = (gchar *)attribute_values[i]; split = TRUE; }
else if(!strcmp (attribute_names[i], "samt" )) { samt = (gchar *)attribute_values[i]; split = TRUE; }
else if(!strcmp (attribute_names[i], "smem" )) { smem = (gchar *)attribute_values[i]; split = TRUE; }
}
if(split == TRUE)
{
entry->splits = da_split_new ();
if (da_splits_parse(entry->splits, scat, samt, smem) > 0)
{
entry->flags |= OF_SPLIT; //Flag that Splits are active
}
}
//all attribute loaded: append
//GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, entry);
da_archive_append(entry);
}
static void homebank_load_xml_ope(ParseContext *ctx, const gchar **attribute_names, const gchar **attribute_values)
{
Transaction *entry = da_transaction_malloc();
gchar *scat = NULL;
gchar *samt = NULL;
gchar *smem = NULL;
gboolean split = FALSE;
gint i;
for (i = 0; attribute_names[i] != NULL; i++)
{
//DB( g_print(" att='%s' val='%s'\n", attribute_names[i], attribute_values[i]) );
if(!strcmp (attribute_names[i], "date" )) { entry->date = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "amount" )) { entry->amount = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "account" )) { entry->kacc = atoi(attribute_values[i]); }
//#1673260
else if(!strcmp (attribute_names[i], "damt" )) { entry->xferamount = g_ascii_strtod(attribute_values[i], NULL); }
else if(!strcmp (attribute_names[i], "dst_account")) { entry->kxferacc = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "paymode" )) { entry->paymode = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "grpflg" )) { entry->grpflg = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "st" )) { entry->status = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "flags" )) { entry->flags = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "payee" )) { entry->kpay = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "category" )) { entry->kcat = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "wording" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->memo = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "info" )) { if(strcmp(attribute_values[i],"(null)") && attribute_values[i] != NULL) entry->number = g_strdup(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "tags" ))
{
if(attribute_values[i] != NULL && strlen(attribute_values[i]) > 0 && strcmp(attribute_values[i],"(null)") != 0 )
{
entry->tags = tags_parse(attribute_values[i]);
}
}
else if(!strcmp (attribute_names[i], "kxfer" )) { entry->kxfer = atoi(attribute_values[i]); }
else if(!strcmp (attribute_names[i], "scat" )) { scat = (gchar *)attribute_values[i]; split = TRUE; }
else if(!strcmp (attribute_names[i], "samt" )) { samt = (gchar *)attribute_values[i]; split = TRUE; }
else if(!strcmp (attribute_names[i], "smem" )) { smem = (gchar *)attribute_values[i]; split = TRUE; }
}
//bugfix 303886
//if(entry->kcat < 0)
// entry->kcat = 0;
if(split == TRUE)
{
entry->splits = da_split_new ();
if (da_splits_parse(entry->splits, scat, samt, smem) > 0)
{
entry->flags |= OF_SPLIT; //Flag that Splits are active
}
}
//all attribute loaded: append
// for perf reason we use prepend here, the list will be reversed later
da_transaction_prepend(entry);
}
static void
start_element_handler (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
ParseContext *ctx = user_data;
//GtkUIManager *self = ctx->self;
//DB( g_print("** start element: '%s'\n", element_name) );
switch(element_name[0])
{
case 'a':
{
if(!strcmp (element_name, "account")) //account
{
homebank_load_xml_acc(ctx, attribute_names, attribute_values);
}
else if(!strcmp (element_name, "asg")) //assign
{
homebank_load_xml_asg(ctx, attribute_names, attribute_values);
}
}
break;
case 'p':
{
if(!strcmp (element_name, "pay"))
{
homebank_load_xml_pay(ctx, attribute_names, attribute_values);
}
else if(!strcmp (element_name, "properties"))
{
homebank_load_xml_prop(ctx, attribute_names, attribute_values);
}
}
break;
case 'g':
{
if(!strcmp (element_name, "grp"))
{
homebank_load_xml_grp(ctx, attribute_names, attribute_values);
}
}
break;
case 'c':
{
if(!strcmp (element_name, "cat"))
{
homebank_load_xml_cat(ctx, attribute_names, attribute_values);
}
else if(!strcmp (element_name, "cur"))
{
homebank_load_xml_cur(ctx, attribute_names, attribute_values);
}
}
break;
//TODO: < 5.2 misstyped here, should be tag without a s
//commented > 5.2 useless not loaded, but no side effect
case 't':
{
if(!strcmp (element_name, "tag"))
{
homebank_load_xml_tag(ctx, attribute_names, attribute_values);
}
}
break;
case 'f':
{
if(!strcmp (element_name, "fav"))
{
homebank_load_xml_fav(ctx, attribute_names, attribute_values);
}
else if(!strcmp (element_name, "flt"))
{
homebank_load_xml_flt(ctx, attribute_names, attribute_values);
}
}
break;
case 'o':
{
if(!strcmp (element_name, "ope"))
{
homebank_load_xml_ope(ctx, attribute_names, attribute_values);
}
}
break;
}
}
/*
static void
end_element_handler (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
ParseContext *ctx = user_data;
//DB( g_print("-- end element: %s\n", element_name) );
}
*/
static GMarkupParser hb_parser = {
start_element_handler,
NULL, //end_element_handler,
NULL, //text_handler,
NULL,
NULL //cleanup
};
static gboolean hb_xml_get_version(ParseContext *ctx, gchar *buffer)
{
gchar *v_buffer;
ctx->file_version = 0.0;
ctx->data_version = 0;
/* v3.4 add :: prevent load of future file version */
v_buffer = g_strstr_len(buffer, 50, "file_version = g_ascii_strtod(v_buffer+13, NULL); /* a little hacky, but works ! */
if( ctx->file_version == 0.0 )
ctx->file_version = 0.1;
else if( ctx->file_version == 5.0 ) //was a mistake
ctx->file_version = 1.0;
v_buffer = g_strstr_len(buffer+13, 50, "d=");
if( v_buffer )
{
//TODO: beware here of we display all the file...
DB( g_print(" d=%.25s)\n\n", v_buffer) );
ctx->data_version = atoi(v_buffer+3);
}
return TRUE;
}
/*
** XML load homebank file: hbfile
*/
gint homebank_load_xml(gchar *filename)
{
gint retval;
gchar *buffer;
gsize length;
GError *error = NULL;
ParseContext ctx;
GMarkupParseContext *context;
gboolean rc, dosanity;
DB( g_print("\n[hb-xml] homebank_load_xml\n") );
retval = XML_OK;
if (!g_file_get_contents (filename, &buffer, &length, &error))
{
if(error)
{
g_warning("unable to load file %s: %s", filename, error->message);
g_error_free(error);
retval = XML_IO_ERROR;
}
}
else
{
if( hb_xml_get_version(&ctx, buffer) == FALSE )
return XML_FILE_ERROR;
if( ctx.file_version > FILE_VERSION )
return XML_VERSION_ERROR;
DB( g_print("- file ok : v=%.1f data_v=%06d\n", ctx.file_version, ctx.data_version) );
/* 1st: validate the file is well in utf-8 */
buffer = homebank_utf8_ensure(buffer);
/* then process the buffer */
context = g_markup_parse_context_new (&hb_parser, 0, &ctx, NULL);
error = NULL;
rc = g_markup_parse_context_parse (context, buffer, length, &error);
if( error )
{
g_print("failed: %s\n", error->message);
g_error_free (error);
}
if( rc == FALSE )
{
error = NULL;
g_markup_parse_context_end_parse(context, &error);
if(error)
{
g_print("failed: %s\n", error->message);
g_error_free (error);
}
}
g_markup_parse_context_free (context);
g_free (buffer);
/* file upgrade / bugfix */
dosanity = FALSE;
// group a test for very old version
if( ctx.file_version <= 1.0 )
{
if( ctx.file_version <= 0.1 )
homebank_upgrade_to_v02();
if( ctx.file_version <= 0.2 )
homebank_upgrade_to_v03();
if( ctx.file_version <= 0.3 )
homebank_upgrade_to_v04();
if( ctx.file_version <= 0.4 )
homebank_upgrade_to_v05();
if( ctx.file_version <= 0.5 )
{
homebank_upgrade_to_v06();
homebank_upgrade_lower_v06();
}
if( ctx.file_version <= 0.6 )
{
homebank_upgrade_to_v07();
hbfile_sanity_check();
}
if( ctx.file_version <= 0.7 ) // <= 4.5
{
homebank_upgrade_to_v08();
}
if( ctx.file_version <= 0.8 ) // <= 4.6
{
dosanity = TRUE;
}
if( ctx.file_version <= 0.9 ) // <= 4.6.3 - 2014-08-09
{
homebank_upgrade_to_v10();
dosanity = TRUE;
}
if( ctx.file_version <= 1.0 ) // <= 5.0.0
{
homebank_upgrade_to_v11();
dosanity = TRUE;
}
}
//starting 5.0.4 data upgrade is done without changing file_version
//file version is changed only when the structure change
//don't start number below with 0 to avoid octal interpretation
if( ctx.data_version <= 50005 ) // <= 5.0.5
{
dosanity = TRUE;
}
if( ctx.file_version <= 1.1 ) // <= 5.1.0
{
homebank_upgrade_to_v12();
dosanity = TRUE;
}
if( ctx.data_version <= 50106 ) // < 5.1.6
{
homebank_upgrade_to_v12_7();
}
if( ctx.file_version < 1.3 ) // <= 5.2
{
homebank_upgrade_to_v13();
dosanity = TRUE;
}
if( ctx.data_version <= 50203 )
{
//fix payee defaut payment to int xfer from 5.1
dosanity = TRUE;
}
if( ctx.file_version < 1.4 ) // <= 5.3
{
homebank_upgrade_to_v14();
dosanity = TRUE;
}
if( ctx.data_version < 50402 )
//fix income txn flag that may be incorrect (multiple edit)
dosanity = TRUE;
if( ctx.data_version < 50600 )
homebank_upgrade_to_v14_12();
if( ctx.data_version < 50604 )
//#2018414 tag name replace any space by -
dosanity = TRUE;
if( ctx.data_version < 50900 )
homebank_upgrade_to_v14_59();
if( ctx.data_version == 50900 || ctx.data_version == 50901 )
//fix arc bad limit
homebank_upgrade_to_v14_592();
if( ctx.data_version < 50904 )
homebank_upgrade_to_v14_595();
// next ?
// sanity check at last
if( dosanity == TRUE )
hbfile_sanity_check();
}
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*
** XML properties save
*/
static GError *
homebank_save_xml_prop(GIOChannel *io)
{
gchar *title;
GString *node;
GError *error = NULL;
title = GLOBALS->owner == NULL ? "" : GLOBALS->owner;
node = g_string_sized_new(255);
g_string_assign(node, "kcur);
hb_xml_append_int(node, "car_category", GLOBALS->vehicle_category);
hb_xml_append_int0(node, "auto_smode", GLOBALS->auto_smode);
hb_xml_append_int(node, "auto_weekday", GLOBALS->auto_weekday);
hb_xml_append_int(node, "auto_nbmonths", GLOBALS->auto_nbmonths);
hb_xml_append_int(node, "auto_nbdays", GLOBALS->auto_nbdays);
hb_xml_append_amt(node, "earnbyh", GLOBALS->lifen_earnbyh);
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
g_string_free(node, TRUE);
return error;
}
/*
** XML currency save
*/
static GError *
homebank_save_xml_cur(GIOChannel *io)
{
GList *list;
gchar *tmpstr;
char buf1[G_ASCII_DTOSTR_BUF_SIZE];
GError *error = NULL;
list = g_hash_table_get_values(GLOBALS->h_cur);
while (list != NULL)
{
Currency *item = list->data;
tmpstr = g_markup_printf_escaped(
"\n",
item->key,
item->flags,
item->iso_code,
item->name,
item->symbol,
item->sym_prefix,
item->decimal_char,
item->grouping_char,
item->frac_digits,
g_ascii_dtostr (buf1, sizeof (buf1), item->rate),
item->mdate
);
g_io_channel_write_chars(io, tmpstr, -1, NULL, &error);
g_free(tmpstr);
if(error)
goto curfail;
list = g_list_next(list);
}
curfail:
g_list_free(list);
return error;
}
/*
** XML account save
*/
static GError *
homebank_save_xml_acc(GIOChannel *io)
{
GList *lacc, *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
lacc = list = account_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Account *item = list->data;
item->dspflags &= ~(FLAG_ACC_TMP_ADDED|FLAG_ACC_TMP_EDITED); //delete flag
g_string_assign(node, "key);
hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_int(node, "pos", item->pos);
hb_xml_append_int(node, "type", item->type);
hb_xml_append_int(node, "curr", item->kcur);
hb_xml_append_txt(node, "name", item->name);
hb_xml_append_txt(node, "number", item->number);
hb_xml_append_txt(node, "bankname", item->bankname);
hb_xml_append_amt(node, "initial", item->initial);
hb_xml_append_amt(node, "minimum", item->minimum);
hb_xml_append_amt(node, "maximum", item->maximum);
hb_xml_append_int(node, "cheque1", item->cheque1);
hb_xml_append_int(node, "cheque2", item->cheque2);
hb_xml_append_txt(node, "website", item->website);
hb_xml_append_txt_crlf(node, "notes", item->notes);
hb_xml_append_int(node, "tpl", item->karc);
hb_xml_append_int(node, "grp", item->kgrp);
//5.5
hb_xml_append_int(node, "ccday", item->cccday);
hb_xml_append_int(node, "rdate", item->rdate);
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto accfail;
list = g_list_next(list);
}
accfail:
g_list_free(lacc);
g_string_free(node, TRUE);
return error;
}
/*
** XML payee save
*/
static GError *
homebank_save_xml_pay(GIOChannel *io)
{
GList *lpay, *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
lpay = list = payee_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Payee *item = list->data;
if(item->key != 0)
{
g_string_assign(node, "key);
hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_txt(node, "name", item->name);
hb_xml_append_int(node, "category", item->kcat);
hb_xml_append_int(node, "paymode" , item->paymode);
hb_xml_append_txt_crlf(node, "notes", item->notes);
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto payfail;
}
list = g_list_next(list);
}
payfail:
g_list_free(lpay);
g_string_free(node, TRUE);
return error;
}
/*
** XML category save
*/
static GError *
homebank_save_xml_cat(GIOChannel *io)
{
GList *lcat, *list;
GString *node;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
GError *error = NULL;
node = g_string_sized_new(255);
lcat = list = category_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Category *item = list->data;
guint i;
if(item->key != 0)
{
g_string_assign(node, "key);
hb_xml_append_int(node, "parent", item->parent);
hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_txt(node, "name", item->name);
for(i=0;i<=12;i++)
{
if(item->budget[i] != 0)
{
g_string_append_printf(node," b%d=\"%s\"", i, g_ascii_dtostr (buf, sizeof (buf), item->budget[i]));
}
}
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto catfail;
}
list = g_list_next(list);
}
catfail:
g_list_free(lcat);
g_string_free(node, TRUE);
return error;
}
/*
** XML grp save
*/
static GError *
homebank_save_xml_grp(GIOChannel *io)
{
GList *lgrp, *list;
gchar *tmpstr;
GError *error = NULL;
lgrp = list = group_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Group *item = list->data;
if(item->key != 0)
{
//tmpstr = g_markup_printf_escaped("\n",
tmpstr = g_markup_printf_escaped("\n",
item->key,
//item->type,
item->name
);
g_io_channel_write_chars(io, tmpstr, -1, NULL, &error);
g_free(tmpstr);
if(error)
goto grpfail;
}
list = g_list_next(list);
}
grpfail:
g_list_free(lgrp);
return error;
}
/*
** XML filter save
*/
static GError *
homebank_save_xml_flt(GIOChannel *io)
{
GList *lflt, *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
lflt = list = filter_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Filter *item = list->data;
if(item->key != 0)
{
g_string_assign(node, "key);
//hb_xml_append_int(node, "parent", item->parent);
//hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_fltgroup(node, "dat", item, FLT_GRP_DATE);
hb_xml_append_fltgroup(node, "acc", item, FLT_GRP_ACCOUNT);
hb_xml_append_fltgroup(node, "pay", item, FLT_GRP_PAYEE);
hb_xml_append_fltgroup(node, "cat", item, FLT_GRP_CATEGORY);
hb_xml_append_fltgroup(node, "tag", item, FLT_GRP_TAG);
hb_xml_append_fltgroup(node, "txt", item, FLT_GRP_TEXT);
hb_xml_append_fltgroup(node, "amt", item, FLT_GRP_AMOUNT);
hb_xml_append_fltgroup(node, "mod", item, FLT_GRP_PAYMODE);
hb_xml_append_fltgroup(node, "sta", item, FLT_GRP_STATUS);
hb_xml_append_fltgroup(node, "typ", item, FLT_GRP_TYPE);
hb_xml_append_txt(node, "name", item->name);
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto fltfail;
}
list = g_list_next(list);
}
fltfail:
g_list_free(lflt);
g_string_free(node, TRUE);
return error;
}
/*
** XML tag save
*/
static GError *
homebank_save_xml_tag(GIOChannel *io)
{
GList *ltag, *list;
gchar *tmpstr;
GError *error = NULL;
ltag = list = tag_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Tag *item = list->data;
if(item->key != 0)
{
tmpstr = g_markup_printf_escaped("\n",
item->key,
item->name
);
g_io_channel_write_chars(io, tmpstr, -1, NULL, &error);
g_free(tmpstr);
if(error)
goto tagfail;
}
list = g_list_next(list);
}
tagfail:
g_list_free(ltag);
return error;
}
/*
** XML assign save
*/
static GError *
homebank_save_xml_asg(GIOChannel *io)
{
GList *lasg, *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
lasg = list = assign_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Assign *item = list->data;
gchar *tagstr = tags_tostring(item->tags);
//#2018680
item->flags &= ~(ASGF_PREFILLED); //delete flag
g_string_assign(node, "key);
hb_xml_append_int(node, "flags" , item->flags);
hb_xml_append_int(node, "pos" , item->pos);
hb_xml_append_int(node, "field" , item->field);
hb_xml_append_txt(node, "name" , item->search);
hb_xml_append_txt(node, "notes" , item->notes);
hb_xml_append_int(node, "payee" , item->kpay);
hb_xml_append_int(node, "category", item->kcat);
hb_xml_append_int(node, "paymode" , item->paymode);
//#1999879 assignment by amount do not save
hb_xml_append_amt(node, "amount", item->amount);
hb_xml_append_txt(node, "tags", tagstr);
g_string_append(node, "/>\n");
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto asgfail;
list = g_list_next(list);
}
asgfail:
g_list_free(lasg);
g_string_free(node, TRUE);
return error;
}
/*
** XML archive save
*/
static GError *
homebank_save_xml_fav(GIOChannel *io)
{
GList *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
list = da_archive_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Archive *item = list->data;
gchar *tagstr = tags_tostring(item->tags);
g_string_assign(node, "key);
hb_xml_append_amt(node, "amount", item->amount);
hb_xml_append_int(node, "account", item->kacc);
//#1673260
if( item->flags & OF_ADVXFER )
hb_xml_append_amt(node, "damt", item->xferamount);
hb_xml_append_int(node, "dst_account", item->kxferacc);
hb_xml_append_int(node, "paymode", item->paymode);
hb_xml_append_int(node, "grpflg", item->grpflg);
hb_xml_append_int(node, "st", item->status);
hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_int(node, "payee", item->kpay);
hb_xml_append_int(node, "category", item->kcat);
hb_xml_append_txt(node, "wording", item->memo);
hb_xml_append_txt(node, "info", item->number);
hb_xml_append_txt(node, "tags", tagstr);
hb_xml_append_int(node, "recflg", item->rec_flags);
hb_xml_append_int(node, "nextdate", item->nextdate);
hb_xml_append_int(node, "every", item->rec_every);
hb_xml_append_int(node, "unit", item->rec_freq);
if(item->rec_flags & TF_LIMIT)
{
hb_xml_append_int(node, "limit", item->limit);
}
hb_xml_append_int(node, "weekend", item->weekend);
hb_xml_append_int(node, "gap", item->daygap);
if(item->rec_flags & TF_RELATIVE)
{
hb_xml_append_int(node, "ordn", item->rec_ordinal);
hb_xml_append_int(node, "wkdy", item->rec_weekday);
}
if(da_splits_length(item->splits) > 0)
{
gchar *cats, *amounts, *memos;
da_splits_tostring(item->splits, &cats, &amounts, &memos);
g_string_append_printf(node, " scat=\"%s\"", cats);
g_string_append_printf(node, " samt=\"%s\"", amounts);
//fix #1173910
gchar *escaped = g_markup_escape_text(memos, -1);
g_string_append_printf(node, " smem=\"%s\"", escaped);
g_free(escaped);
g_free(cats);
g_free(amounts);
g_free(memos);
}
g_string_append(node, "/>\n");
g_free(tagstr);
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto favfail;
list = g_list_next(list);
}
favfail:
//no list free here it is already the global list
g_string_free(node, TRUE);
return error;
}
/*
** XML transaction save
*/
static GError *
homebank_save_xml_ope(GIOChannel *io)
{
GList *lst_acc, *lnk_acc;
GList *list;
GString *node;
GError *error = NULL;
node = g_string_sized_new(255);
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *item = list->data;
gchar *tagstr = tags_tostring(item->tags);
item->dspflags = 0;
g_string_assign(node, "date);
hb_xml_append_amt(node, "amount", item->amount);
hb_xml_append_int(node, "account", item->kacc);
//#1673260
if( item->flags & OF_ADVXFER )
hb_xml_append_amt(node, "damt", item->xferamount);
hb_xml_append_int(node, "dst_account", item->kxferacc);
hb_xml_append_int(node, "paymode", item->paymode);
hb_xml_append_int(node, "grpflg", item->grpflg);
hb_xml_append_int(node, "st", item->status);
hb_xml_append_int(node, "flags", item->flags);
hb_xml_append_int(node, "payee", item->kpay);
hb_xml_append_int(node, "category", item->kcat);
hb_xml_append_txt(node, "wording", item->memo);
hb_xml_append_txt(node, "info", item->number);
hb_xml_append_txt(node, "tags", tagstr);
hb_xml_append_int(node, "kxfer", item->kxfer);
if(da_splits_length(item->splits) > 0)
{
gchar *cats, *amounts, *memos;
da_splits_tostring(item->splits, &cats, &amounts, &memos);
g_string_append_printf(node, " scat=\"%s\"", cats);
g_string_append_printf(node, " samt=\"%s\"", amounts);
//fix #1173910
gchar *escaped = g_markup_escape_text(memos, -1);
g_string_append_printf(node, " smem=\"%s\"", escaped);
g_free(escaped);
g_free(cats);
g_free(amounts);
g_free(memos);
}
g_string_append(node, "/>\n");
g_free(tagstr);
g_io_channel_write_chars(io, node->str, -1, NULL, &error);
if(error)
goto opefail;
list = g_list_next(list);
}
lnk_acc = g_list_next(lnk_acc);
}
opefail:
g_list_free(lst_acc);
g_string_free(node, TRUE);
return error;
}
static GError *
homebank_save_xml_ver(GIOChannel *io)
{
GError *error = NULL;
char buf1[G_ASCII_DTOSTR_BUF_SIZE];
gchar *outstr;
g_ascii_dtostr (buf1, sizeof (buf1), FILE_VERSION);
outstr = g_strdup_printf("\n", buf1, HB_VERSION_NUM);
g_io_channel_write_chars(io, outstr, -1, NULL, &error);
g_free(outstr);
return error;
}
/*
** XML save homebank file: hbfile
*/
gint homebank_save_xml(gchar *filename)
{
GIOChannel *io;
GError *error = NULL;
gint retval = XML_IO_ERROR;
//The default encoding for the external file is UTF-8.
io = g_io_channel_new_file(filename, "w", &error);
if(error) goto failure;
//#2069152 handle windows Controlled Folder Access (CFA) write access
if( !(g_io_channel_get_flags(io) & G_IO_FLAG_IS_WRITABLE) )
{
retval = XML_NOT_WRITABLE;
goto failure;
}
g_io_channel_write_chars(io, "\n", -1, NULL, &error);
if(error) goto failure;
error = homebank_save_xml_ver(io);
if(error) goto failure;
error = homebank_save_xml_prop(io);
if(error) goto failure;
error = homebank_save_xml_cur(io);
if(error) goto failure;
error = homebank_save_xml_grp(io);
if(error) goto failure;
error = homebank_save_xml_acc(io);
if(error) goto failure;
error = homebank_save_xml_pay(io);
if(error) goto failure;
error = homebank_save_xml_cat(io);
if(error) goto failure;
error = homebank_save_xml_tag(io);
if(error) goto failure;
error = homebank_save_xml_asg(io);
if(error) goto failure;
error = homebank_save_xml_fav(io);
if(error) goto failure;
error = homebank_save_xml_ope(io);
if(error) goto failure;
error = homebank_save_xml_flt(io);
if(error) goto failure;
g_io_channel_write_chars(io, "\n", -1, NULL, &error);
if(error) goto failure;
retval = XML_OK;
failure:
if(error)
{
g_warning("unable to save file %s: %s", filename, error->message);
//TODO: later: propagate up
g_error_free(error);
}
g_io_channel_unref (io);
return retval;
}
homebank-5.9.7/src/ui-category.c 0000644 0001750 0001750 00000217376 15005624330 016013 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#include "ui-category.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern gchar *CYA_CAT_TYPE[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkWidget *
container_get_nth(GtkBox *container, gint nth)
{
GList *lchild, *list;
GtkWidget *child;
if(!GTK_IS_CONTAINER(container))
return NULL;
lchild = list = gtk_container_get_children (GTK_CONTAINER(container));
child = g_list_nth_data (list, nth);
g_list_free(lchild);
return child;
}
GtkWidget *
ui_cat_entry_popover_get_entry(GtkBox *box)
{
return container_get_nth(box, 0);
}
Category *
ui_cat_entry_popover_get(GtkBox *box)
{
GtkWidget *entry;
gchar *name;
Category *item = NULL;
DB( g_print ("ui_cat_entry_popover_get()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (entry));
item = da_cat_get_by_fullname(name);
}
return item;
}
guint32
ui_cat_entry_popover_get_key_add_new(GtkBox *box)
{
Category *item;
GtkWidget *entry;
GtkTreeModel *store;
DB( g_print ("ui_cat_entry_popover_get_key_add_new()\n") );
/* automatic add */
//todo: check prefs + ask the user here 1st time
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
gchar *name = (gchar *)gtk_entry_get_text(GTK_ENTRY (entry));
item = da_cat_get_by_fullname(name);
if(item != NULL)
return item->key;
item = da_cat_append_ifnew_by_fullname(name);
if( item != NULL )
{
store = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
if( store )
gtk_list_store_insert_with_values(GTK_LIST_STORE(store), NULL, -1,
STO_CAT_DATA, item,
STO_CAT_FULLNAME, item->fullname,
-1);
return item->key;
}
}
return 0;
}
guint32
ui_cat_entry_popover_get_key(GtkBox *box)
{
Category *item = ui_cat_entry_popover_get(box);
return ((item != NULL) ? item->key : 0);
}
void
ui_cat_entry_popover_set_active(GtkBox *box, guint32 key)
{
GtkWidget *entry;
DB( g_print ("[cat entry popover] setactive\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
Category *item = da_cat_get(key);
hbtk_entry_set_text(GTK_ENTRY(entry), item != NULL ? item->fullname : "");
}
}
void
ui_cat_entry_popover_add(GtkBox *box, Category *item)
{
GtkWidget *entry;
DB( g_print ("[cat entry popover] add\n") );
DB( g_print (" -> try to add: '%s'\n", item->name) );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
if( item->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
STO_CAT_DATA, item,
STO_CAT_FULLNAME, item->fullname,
-1);
}
}
}
static void
ui_cat_entry_popover_cb_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkEntry *entry = user_data;
if( GTK_IS_ENTRY(entry) )
{
treeselection = gtk_tree_view_get_selection(tree_view);
if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
{
Category *item;
gtk_tree_model_get(model, &iter, STO_CAT_DATA, &item, -1);
if(item)
gtk_entry_set_text(GTK_ENTRY(user_data), item->fullname);
}
}
}
static void
ui_cat_entry_popover_text_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Category *item;
gchar *markup;
gtk_tree_model_get(model, iter,
STO_CAT_DATA, &item,
-1);
markup = (item->key == 0) ? _("(no category)") : item->typename;
g_object_set(renderer, "markup", markup, NULL);
}
static void
ui_cat_entry_popover_populate(GtkListStore *store)
{
GHashTableIter hiter;
gpointer key, value;
g_hash_table_iter_init (&hiter, GLOBALS->h_cat);
while (g_hash_table_iter_next (&hiter, &key, &value))
{
Category *item = value;
//#1826360 wish: archive payee/category to lighten the lists
if( !(item->flags & GF_HIDDEN) )
{
gtk_list_store_insert_with_values(GTK_LIST_STORE(store), NULL, -1,
STO_CAT_DATA, item,
STO_CAT_FULLNAME, item->fullname,
-1);
}
}
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
static void
ui_cat_entry_popover_function (GtkEditable *editable, gpointer user_data)
{
DB( g_print("text changed to %s\n", gtk_entry_get_text(GTK_ENTRY(editable)) ) );
}
static void
ui_cat_entry_popover_cb_toggled (GtkToggleButton *togglebutton, gpointer user_data)
{
GtkWidget *entry = user_data;
GtkAllocation allocation;
GtkPopover *popover;
if(GTK_IS_ENTRY(entry))
{
gtk_widget_get_allocation (entry, &allocation);
popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(togglebutton));
if(GTK_IS_POPOVER(popover))
{
gtk_widget_set_size_request (GTK_WIDGET(popover), allocation.width + (2*SPACING_POPOVER), -1);
DB( g_print("should set width to %d\n", allocation.width + (2*SPACING_POPOVER)) );
}
}
}
void
ui_cat_entry_popover_clear(GtkBox *box)
{
GtkWidget *entry;
GtkTreeModel *store;
DB( g_print ("[cat entry popover] clear\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
store = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
gtk_list_store_clear (GTK_LIST_STORE(store));
}
}
void
ui_cat_entry_popover_sort_type(GtkBox *box, guint type)
{
GtkWidget *entry;
GtkTreeModel *store;
DB( g_print ("[cat entry popover] sort type\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
store = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
g_object_set_data(G_OBJECT(store), "type", GUINT_TO_POINTER(type));
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
}
static gint
ui_cat_entry_popover_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
Category *cat1, *cat2;
Category *pcat1, *pcat2;
guint type;
gtk_tree_model_get(model, a, STO_CAT_DATA, &cat1, -1);
gtk_tree_model_get(model, b, STO_CAT_DATA, &cat2, -1);
if(cat1->key == 0)
return -1;
if(cat2->key == 0)
return 1;
//#2042484 exp/inc sort should be done on lvl1 only
pcat1 = cat1->parent == 0 ? cat1 : da_cat_get(cat1->parent);
pcat2 = cat2->parent == 0 ? cat2 : da_cat_get(cat2->parent);
//#1882456
type = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(model), "type"));
switch(type)
{
case TXN_TYPE_INCOME:
// inc first
retval = (pcat2->flags & GF_INCOME) - (pcat1->flags & GF_INCOME);
break;
default:
retval = (pcat1->flags & GF_INCOME) - (pcat2->flags & GF_INCOME);
}
if( !retval )
retval = hb_string_utf8_compare(cat1->fullname, cat2->fullname);
return retval;
}
static gboolean
ui_cat_entry_popover_completion_func (GtkEntryCompletion *completion,
const gchar *key,
GtkTreeIter *iter,
gpointer user_data)
{
Category *item = NULL;
gchar *normalized_string;
gchar *case_normalized_string;
gboolean ret = FALSE;
GtkTreeModel *model;
model = gtk_entry_completion_get_model (completion);
gtk_tree_model_get (model, iter,
STO_CAT_DATA, &item,
-1);
if (item != NULL)
{
normalized_string = g_utf8_normalize (item->fullname, -1, G_NORMALIZE_ALL);
if (normalized_string != NULL)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
//g_print("match '%s' for '%s' ?\n", key, case_normalized_string);
//if (!strncmp (key, case_normalized_string, strlen (key)))
if (g_strstr_len (case_normalized_string, strlen (case_normalized_string), key ))
{
ret = TRUE;
// g_print(" ==> yes !\n");
}
g_free (case_normalized_string);
}
g_free (normalized_string);
}
return ret;
}
static void
ui_cat_entry_popover_destroy( GtkWidget *widget, gpointer user_data )
{
DB( g_print ("[cat entry popover] destroy\n") );
}
GtkWidget *
ui_cat_entry_popover_new(GtkWidget *label)
{
GtkWidget *mainbox, *box, *entry, *menubutton, *image, *popover, *scrollwin, *treeview;
GtkListStore *store;
GtkEntryCompletion *completion;
DB( g_print ("[pay entry popover] new\n") );
mainbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(mainbox)), GTK_STYLE_CLASS_LINKED);
entry = gtk_entry_new();
hbtk_box_prepend (GTK_BOX(mainbox), entry);
menubutton = gtk_menu_button_new ();
//data->MB_template = menubutton;
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_button_set_image(GTK_BUTTON(menubutton), image);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_LEFT );
//gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
gtk_box_prepend(GTK_BOX(mainbox), menubutton);
completion = gtk_entry_completion_new ();
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_object_unref(completion);
store = gtk_list_store_new (NUM_STO_CAT,
G_TYPE_POINTER,
G_TYPE_STRING
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_cat_entry_popover_compare_func, NULL, NULL);
ui_cat_entry_popover_populate(store);
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
gtk_entry_completion_set_match_func(completion, ui_cat_entry_popover_completion_func, NULL, NULL);
g_object_unref(store);
gtk_entry_completion_set_text_column (completion, STO_CAT_FULLNAME);
gtk_widget_show_all(mainbox);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
//gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_show_all(box);
//gtk_widget_set_can_focus(GTK_WIDGET(treeview), FALSE);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new_with_attributes (NULL,
renderer,
"text",
STO_CAT_FULLNAME,
NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_entry_popover_text_cell_data_function, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_BROWSE);
//popover = create_popover (menubutton, box, GTK_POS_BOTTOM);
popover = create_popover (menubutton, box, GTK_POS_LEFT);
//gtk_widget_set_size_request (popover, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
gtk_widget_set_vexpand(popover, TRUE);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
// connect our dispose function
g_signal_connect (entry, "destroy", G_CALLBACK (ui_cat_entry_popover_destroy), NULL);
g_signal_connect_after (entry , "changed", G_CALLBACK (ui_cat_entry_popover_function), NULL);
g_signal_connect (menubutton, "toggled", G_CALLBACK (ui_cat_entry_popover_cb_toggled), entry);
g_signal_connect (treeview, "row-activated", G_CALLBACK (ui_cat_entry_popover_cb_row_activated), entry);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_popover_popdown), popover);
#else
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_widget_hide), popover);
#endif
//g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK (ui_cat_entry_popover_cb_selection), entry);
//g_signal_connect_swapped(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK(gtk_popover_popdown), popover);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), entry);
//gtk_widget_set_size_request(comboboxentry, HB_MINWIDTH_LIST, -1);
return mainbox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint
ui_cat_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter)
{
GtkTreeModel *model;
//GtkTreeSelection *selection;
GtkTreeIter iter, child;
guint n_child, change = 0;
gboolean valid;
gboolean toggled;
DB( g_print("(ui_acc_listview) toggle_to_filter\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Category *catitem;
gtk_tree_model_get (model, &iter,
LST_DEFCAT_TOGGLE, &toggled,
LST_DEFCAT_DATAS , &catitem,
-1);
DB( g_print(" cat k:%3d = %d (%s)\n", catitem->key, toggled, catitem->name) );
change += da_flt_status_cat_set(filter, catitem->key, toggled);
//catitem->flt_select = toggled;
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
gtk_tree_model_get (model, &child,
LST_DEFCAT_TOGGLE, &toggled,
LST_DEFCAT_DATAS, &catitem,
-1);
DB( g_print(" subcat k:%3d = %d (%s)\n", catitem->key, toggled, catitem->name) );
change += da_flt_status_cat_set(filter, catitem->key, toggled);
//catitem->flt_select = toggled;
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
return change;
}
static void
ui_cat_listview_fixed_toggled (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter, child;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
gint n_child;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFCAT_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, LST_DEFCAT_TOGGLE, fixed, -1);
/* propagate to child */
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
gtk_tree_store_set (GTK_TREE_STORE (model), &child, LST_DEFCAT_TOGGLE, fixed, -1);
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
/* clean up */
gtk_tree_path_free (path);
}
void
ui_cat_listview_quick_select(GtkTreeView *treeview, const gchar *uri)
{
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
gboolean toggle;
gint n_child, qselect = hb_clicklabel_to_int(uri);
DB( g_print("(ui_acc_listview) quick select\n") );
DB( g_print(" comboboxlink '%s' %d\n", uri, qselect) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
switch(qselect)
{
case HB_LIST_QUICK_SELECT_ALL:
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, LST_DEFCAT_TOGGLE, TRUE, -1);
break;
case HB_LIST_QUICK_SELECT_NONE:
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, LST_DEFCAT_TOGGLE, FALSE, -1);
break;
case HB_LIST_QUICK_SELECT_INVERT:
gtk_tree_model_get (model, &iter, LST_DEFCAT_TOGGLE, &toggle, -1);
toggle ^= 1;
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, LST_DEFCAT_TOGGLE, toggle, -1);
break;
}
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
switch(qselect)
{
case HB_LIST_QUICK_SELECT_ALL:
gtk_tree_store_set (GTK_TREE_STORE (model), &child, LST_DEFCAT_TOGGLE, TRUE, -1);
break;
case HB_LIST_QUICK_SELECT_NONE:
gtk_tree_store_set (GTK_TREE_STORE (model), &child, LST_DEFCAT_TOGGLE, FALSE, -1);
break;
case HB_LIST_QUICK_SELECT_INVERT:
gtk_tree_model_get (model, &child, LST_DEFCAT_TOGGLE, &toggle, -1);
toggle ^= 1;
gtk_tree_store_set (GTK_TREE_STORE (model), &child, LST_DEFCAT_TOGGLE, toggle, -1);
break;
}
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static gint
ui_cat_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
Category *entry1, *entry2;
gint retval = 0;
gtk_tree_model_get(model, a, LST_DEFCAT_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DEFCAT_DATAS, &entry2, -1);
switch (sortcol)
{
case LST_DEFCAT_SORT_NAME:
retval = (entry1->flags & GF_INCOME) - (entry2->flags & GF_INCOME);
if(!retval)
{
retval = hb_string_utf8_compare(entry1->name, entry2->name);
}
break;
case LST_DEFCAT_SORT_USETXN:
retval = entry1->nb_use_txn - entry2->nb_use_txn;
break;
case LST_DEFCAT_SORT_USECFG:
retval = (entry1->nb_use_all - entry1->nb_use_txn) - (entry2->nb_use_all - entry2->nb_use_txn);
break;
default:
g_return_val_if_reached(0);
}
return retval;
}
/*
** draw some text from the stored data structure
*/
static void
ui_cat_listview_text_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Category *item;
gchar *markup;
gtk_tree_model_get(model, iter,
LST_DEFCAT_DATAS, &item,
-1);
markup = (item->key == 0) ? _("(no category)") : item->typename;
g_object_set(renderer, "markup", markup, NULL);
}
static void
ui_cat_listview_count_txn_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Category *entry;
gchar buffer[32];
guint16 use;
guint16 usecat;
buffer[0] = '\0';
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
use = entry->nb_use_txn;
usecat = entry->nb_use_txncat;
if(use > 0)
{
if( !(entry->flags & GF_SUB) && (usecat != use) )
g_snprintf(buffer, 32-1, "%d (%d)", use, usecat);
else
g_snprintf(buffer, 32-1, "%d", use);
}
g_object_set(renderer, "text", buffer, NULL);
}
static void
ui_cat_listview_count_cfg_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Category *entry;
gchar buffer[32];
gint use;
gint usecat;
buffer[0] = '\0';
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
use = entry->nb_use_all - entry->nb_use_txn;
usecat = entry->nb_use_allcat - entry->nb_use_txncat;
if(use > 0 || usecat > 0)
{
if( !(entry->flags & GF_SUB) && (usecat != use) )
g_snprintf(buffer, 32-1, "%d (%d)", use, usecat);
else
g_snprintf(buffer, 32-1, "%d", use);
}
g_object_set(renderer, "text", buffer, NULL);
}
static void
ui_cat_listview_icon_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Category *entry;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &entry, -1);
if( entry->flags & GF_HIDDEN )
iconname = ICONNAME_HB_BUTTON_HIDE;
g_object_set(renderer, "icon-name", iconname, NULL);
}
#if MYDEBUG == 1
static void
ui_cat_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Category *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
/* = = = = = = = = = = = = = = = = */
void
ui_cat_listview_add(GtkTreeView *treeview, Category *item, GtkTreeIter *parent)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
DB( g_print ("ui_cat_listview_add()\n") );
if( item->name != NULL )
{
model = gtk_tree_view_get_model(treeview);
gtk_tree_store_append (GTK_TREE_STORE(model), &iter, parent);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter,
LST_DEFCAT_TOGGLE, FALSE,
LST_DEFCAT_DATAS, item,
//LST_DEFCAT_NAME, item->name,
-1);
//select the added line
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_view_expand_to_path (treeview, path);
gtk_tree_path_free(path);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(treeview), &iter);
}
}
Category *
ui_cat_listview_get_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print ("ui_cat_listview_get_selected()\n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Category *item;
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &item, -1);
if( item->key != 0 )
return item;
}
return NULL;
}
Category *
ui_cat_listview_get_selected_parent(GtkTreeView *treeview, GtkTreeIter *return_iter)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
Category *item;
DB( g_print ("ui_cat_listview_get_selected_parent()\n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
path = gtk_tree_model_get_path(model, &iter);
DB( g_print ("path depth = %d\n", gtk_tree_path_get_depth(path)) );
if(gtk_tree_path_get_depth(path) > 1)
{
if( gtk_tree_path_up(path) )
{
DB( g_print ("up ok\n") );
if(gtk_tree_model_get_iter(model, &iter, path))
{
DB( g_print ("iter ok\n") );
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &item, -1);
if( item->key != 0 )
{
*return_iter = iter;
return item;
}
}
}
}
else
{
DB( g_print ("path <=1\n") );
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &item, -1);
if( item->key != 0 )
{
*return_iter = iter;
return item;
}
}
}
return NULL;
}
void
ui_cat_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter, child;
gint n_child;
DB( g_print("ui_cat_listview_remove_selected() \n") );
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
//remove any children
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
n_child--;
gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
//After being removed, iter is set to the next valid row at that level,
//or invalidated if it previously pointed to the last one.
//gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
}
}
static void
ui_cat_listview_sort_force(GtkTreeSortable *sortable, gpointer user_data)
{
gint sort_column_id;
GtkSortType order;
DB( g_print("ui_cat_listview_sort_force()\n") );
gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &order);
DB( g_print(" - id %d order %d\n", sort_column_id, order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), sort_column_id, order);
}
struct CatListContext
{
GtkTreeModel *model;
guint except_key;
gint type;
gchar *needle;
gushort *catmatch;
gboolean showhidden;
};
static gboolean
ui_cat_listview_get_top_level (GtkTreeModel *liststore, guint32 key, GtkTreeIter *return_iter)
{
GtkTreeIter iter;
gboolean valid;
Category *item;
//DB( g_print("ui_cat_listview_get_top_level() \n") );
if( liststore != NULL )
{
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter);
while (valid)
{
gtk_tree_model_get (liststore, &iter, LST_DEFCAT_DATAS, &item, -1);
if(item->key == key)
{
*return_iter = iter;
return TRUE;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter);
}
}
return FALSE;
}
static void
ui_cat_listview_populate_cat_ghfunc(gpointer key, gpointer value, struct CatListContext *ctx)
{
GtkTreeIter toplevel;
Category *item = value;
gint item_type;
gboolean matchtype = FALSE;
gboolean matchneedle = TRUE;
gboolean matchhidden = TRUE;
//only cat (lvl1)
if( item->parent != 0 )
return;
item_type = (item->flags & GF_INCOME) ? CAT_TYPE_INCOME : CAT_TYPE_EXPENSE;
if( (ctx->type == CAT_TYPE_ALL)
|| (ctx->type == item_type)
//#1740368 add mixed cat as well
|| (item->flags & GF_MIXED)
|| item->key == 0
)
matchtype = TRUE;
//disable search non matched
if( (ctx->catmatch != NULL) && ctx->catmatch[item->key] == 0 )
matchneedle = FALSE;
if( (ctx->showhidden == FALSE) && (item->flags & GF_HIDDEN) )
matchhidden = FALSE;
if( matchtype && matchneedle && matchhidden )
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(ctx->model), &toplevel, NULL, -1,
LST_DEFCAT_TOGGLE, FALSE,
LST_DEFCAT_DATAS, item,
//LST_DEFCAT_NAME, item->name,
-1);
}
}
static void
ui_cat_listview_populate_subcat_ghfunc(gpointer key, gpointer value, struct CatListContext *ctx)
{
GtkTreeIter toplevel, child;
Category *item = value;
gboolean hasparent;
gboolean matchneedle = TRUE;
gboolean matchhidden = TRUE;
//only subcat (lvl2)
if( item->parent == 0 )
return;
hasparent = ui_cat_listview_get_top_level(ctx->model, item->parent, &toplevel);
if( hasparent == FALSE )
return;
//#1740368 no need to filter on type, always insert all childs
//disable search non matched
if( (ctx->catmatch != NULL) && ctx->catmatch[item->key] == 0 )
matchneedle = FALSE;
if( (ctx->showhidden == FALSE) && (item->flags & GF_HIDDEN) )
matchhidden = FALSE;
if( matchneedle && matchhidden )
{
gtk_tree_store_insert_with_values (GTK_TREE_STORE(ctx->model), &child, &toplevel, -1,
LST_DEFCAT_TOGGLE, FALSE,
LST_DEFCAT_DATAS, item,
//LST_DEFCAT_NAME, item->name,
-1);
}
}
void
ui_cat_listview_populate(GtkWidget *view, gint type, gchar *needle, gboolean showhidden)
{
GtkTreeModel *model;
struct CatListContext ctx = { 0 };
gboolean hastext = FALSE;
guint32 maxcat;
gushort *catmatch = NULL;
GList *lcat, *list;
DB( g_print("ui_cat_listview_populate() \n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
gtk_tree_store_clear (GTK_TREE_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(view), NULL); /* Detach model from view */
if( needle != NULL)
{
hastext = (strlen(needle) >= 2) ? TRUE : FALSE;
DB( g_print(" search: '%s' %s\n", needle, hastext ? "on":"off") );
}
if( hastext )
{
maxcat = da_cat_get_max_key();
catmatch = g_malloc0((maxcat+2) * sizeof(gushort));
if( catmatch != NULL )
{
// mark cat/subcat that match
lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
Category *item = list->data;
gboolean match = hb_string_utf8_strstr(item->name, needle, FALSE);
if(match)
{
catmatch[item->key]++;
if(item->parent != 0)
catmatch[item->parent]++;
DB( g_print(" match %4d:%4d '%s'\n", item->parent, item->key, item->name) );
}
list = g_list_next(list);
}
g_list_free(lcat);
}
}
/* clear and populate */
ctx.model = model;
ctx.type = type;
ctx.needle = needle;
ctx.catmatch = catmatch;
ctx.showhidden = showhidden;
/* we have to do this in 2 times to ensure toplevel (cat) will be added before childs */
/* populate cat 1st */
g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)ui_cat_listview_populate_cat_ghfunc, &ctx);
g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)ui_cat_listview_populate_subcat_ghfunc, &ctx);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
gtk_tree_view_expand_all (GTK_TREE_VIEW(view));
if( hastext )
{
g_free(catmatch);
}
}
static gboolean
ui_cat_listview_search_equal_func (GtkTreeModel *model,
gint column,
const gchar *key,
GtkTreeIter *iter,
gpointer search_data)
{
gboolean retval = TRUE;
gchar *normalized_string;
gchar *normalized_key;
gchar *case_normalized_string = NULL;
gchar *case_normalized_key = NULL;
Category *item;
//gtk_tree_model_get_value (model, iter, column, &value);
gtk_tree_model_get(model, iter, LST_DEFCAT_DATAS, &item, -1);
if(item != NULL)
{
normalized_string = g_utf8_normalize (item->name, -1, G_NORMALIZE_ALL);
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
if (normalized_string && normalized_key)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
case_normalized_key = g_utf8_casefold (normalized_key, -1);
if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
retval = FALSE;
}
g_free (normalized_key);
g_free (normalized_string);
g_free (case_normalized_key);
g_free (case_normalized_string);
}
return retval;
}
GtkWidget *
ui_cat_listview_new(gboolean withtoggle, gboolean withcount)
{
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("ui_cat_listview_new() \n") );
store = gtk_tree_store_new(
NUM_LST_DEFCAT,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
//G_TYPE_STRING
);
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW (treeview), TRUE);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column: hide icon
//#1826360 wish: archive payee/category to lighten the lists
if( withtoggle == FALSE )
{
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_listview_icon_cell_data_function, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
// column: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer, "active", LST_DEFCAT_TOGGLE, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE);
g_signal_connect (G_OBJECT(renderer), "toggled",
G_CALLBACK (ui_cat_listview_fixed_toggled), store);
g_object_set_data(G_OBJECT(treeview), "togrdr_data", renderer);
}
// column: usage
if( withcount == TRUE )
{
column = gtk_tree_view_column_new();
//TRANSLATORS: 'txn' is abbrevation for transaction
gtk_tree_view_column_set_title(column, _("# txn"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_listview_count_txn_cell_data_function, GINT_TO_POINTER(LST_DEFCAT_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFCAT_SORT_USETXN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by degfault hide this column
gtk_tree_view_column_set_visible(column, FALSE);
column = gtk_tree_view_column_new();
//TRANSLATORS: 'cfg' is abbrevation for configuration
gtk_tree_view_column_set_title(column, _("# cfg"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_listview_count_cfg_cell_data_function, GINT_TO_POINTER(LST_DEFCAT_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFCAT_SORT_USECFG);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by degfault hide this column
gtk_tree_view_column_set_visible(column, FALSE);
}
// column: name
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);
if( withtoggle == FALSE )
{
g_object_set(renderer,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
}
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Category"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_cat_listview_text_cell_data_function, GINT_TO_POINTER(LST_DEFCAT_DATAS), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
//gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFCAT_SORT_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
if( withtoggle == TRUE )
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_cat_listview_search_equal_func, NULL, NULL);
// treeview attribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), withcount);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCAT_SORT_NAME, ui_cat_listview_compare_func, GINT_TO_POINTER(LST_DEFCAT_SORT_NAME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCAT_SORT_USETXN, ui_cat_listview_compare_func, GINT_TO_POINTER(LST_DEFCAT_SORT_USETXN), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFCAT_SORT_USECFG, ui_cat_listview_compare_func, GINT_TO_POINTER(LST_DEFCAT_SORT_USECFG), NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFCAT_SORT_NAME, GTK_SORT_ASCENDING);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* ui_cat_manage_filter_text_handler
*
* filter to entry to avoid seizure of ':' char
*
*/
static void
ui_cat_manage_filter_text_handler (GtkEntry *entry,
const gchar *text,
gint length,
gint *position,
gpointer data)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint i, count=0;
gchar *result = g_new0 (gchar, length+1);
for (i=0; i < length; i++)
{
if (text[i]==':')
continue;
result[count++] = text[i];
}
if (count > 0) {
g_signal_handlers_block_by_func (G_OBJECT (editable),
G_CALLBACK (ui_cat_manage_filter_text_handler),
data);
gtk_editable_insert_text (editable, result, count, position);
g_signal_handlers_unblock_by_func (G_OBJECT (editable),
G_CALLBACK (ui_cat_manage_filter_text_handler),
data);
}
g_signal_stop_emission_by_name (G_OBJECT (editable), "insert_text");
g_free (result);
}
static void
ui_cat_manage_dialog_refilter(struct ui_cat_manage_dialog_data *data)
{
gint type;
gboolean showhidden;
gchar *needle;
DB( g_print("\n[ui-cat-manage] refilter\n") );
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type)) == 1 ? CAT_TYPE_INCOME : CAT_TYPE_EXPENSE;
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
showhidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showhidden));
ui_cat_listview_populate(data->LV_cat, type, needle, showhidden);
gtk_tree_view_expand_all (GTK_TREE_VIEW(data->LV_cat));
}
static void
ui_cat_manage_dialog_delete_unused(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data = user_data;
gboolean result;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-cat-manage] delete unused\n") );
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
_("Delete unused categories"),
_("Are you sure you want to permanently\ndelete unused categories?"),
_("_Delete"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
GtkTreeModel *model;
//#1996275 fill usage before delete !
if( data->usagefilled == FALSE )
{
category_fill_usage();
data->usagefilled = TRUE;
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
gtk_tree_store_clear (GTK_TREE_STORE(model));
//#1917075
data->change += category_delete_unused();
ui_cat_manage_dialog_refilter (data);
}
}
static void
ui_cat_manage_dialog_load_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data = user_data;
gchar *filename = NULL;
gchar *error;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-cat-manage] load csv\n") );
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
if(!category_load_csv(filename, &error))
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("File format error"),
_("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
);
}
g_free( filename );
ui_cat_manage_dialog_refilter(data);
}
}
static void
ui_cat_manage_dialog_save_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data = user_data;
gchar *filename = NULL;
gchar *error;
DB( g_print("\n[ui-cat-manage] save csv\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
category_save_csv(filename, &error);
g_free( filename );
}
}
static void
ui_cat_manage_dialog_cb_show_usage (GtkToggleButton *button, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
gboolean showusage;
GtkTreeViewColumn *column;
DB( g_print("\n[ui-cat-manage] show usage\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW)), "inst_data");
if( data->usagefilled == FALSE )
{
category_fill_usage();
data->usagefilled = TRUE;
}
showusage = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showusage));
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_cat), LST_DEFCAT_SORT_USETXN);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_cat), LST_DEFCAT_SORT_USECFG);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
}
static void
ui_cat_manage_dialog_cb_show_hidden (GtkToggleButton *button, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
DB( g_print("\n[ui-cat-manage] show hidden\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW)), "inst_data");
ui_cat_manage_dialog_refilter(data);
}
/**
* ui_cat_manage_dialog_add:
*
* add an empty new category/subcategory
*
*/
static void
ui_cat_manage_dialog_add(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
gboolean isadded, subcat = GPOINTER_TO_INT(user_data);
const gchar *name;
//GtkTreeModel *model;
GtkWidget *tmpwidget;
Category *item, *paritem;
gint type;
DB( g_print("\n[ui-cat-manage] add\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(data=%p) is subcat=%d\n", data, subcat) );
tmpwidget = (subcat == FALSE ? data->ST_name1 : data->ST_name2);
name = gtk_entry_get_text(GTK_ENTRY(tmpwidget));
//model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
item = da_cat_malloc();
item->name = g_strdup(name);
g_strstrip(item->name);
isadded = FALSE;
if( strlen(item->name) > 0 )
{
/* if cat use new id */
if(subcat == FALSE)
{
type = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_type));
if(type == 1)
item->flags |= GF_INCOME;
isadded = da_cat_append(item);
if( isadded == TRUE )
{
DB( g_print(" => add cat: %p %d, %s type=%d\n", item, subcat, item->name, type) );
ui_cat_listview_add(GTK_TREE_VIEW(data->LV_cat), item, NULL);
data->change++;
}
}
/* if subcat use parent id & gf_income */
else
{
GtkTreeIter parent_iter;
paritem = ui_cat_listview_get_selected_parent(GTK_TREE_VIEW(data->LV_cat), &parent_iter);
if(paritem)
{
DB( g_print(" => selitem parent: %d, %s\n", paritem->key, paritem->name) );
item->parent = paritem->key;
item->flags |= (paritem->flags & GF_INCOME);
item->flags |= GF_SUB;
isadded = da_cat_append(item);
if( isadded == TRUE )
{
DB( g_print(" => add subcat: %p %d, %s\n", item, subcat, item->name) );
ui_cat_listview_add(GTK_TREE_VIEW(data->LV_cat), item, &parent_iter);
data->change++;
}
}
}
}
//#2051349 warn user and free lack
if( isadded == FALSE )
{
DB( g_print(" existing item\n") );
da_cat_free(item);
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Duplicate category name. Try another name.") );
}
gtk_entry_set_text(GTK_ENTRY(tmpwidget),"");
}
static void
ui_cat_manage_dialog_edit_entry_cb(GtkEditable *editable, gpointer user_data)
{
GtkDialog *window = user_data;
const gchar *buffer;
DB( g_print("\n[ui-cat-manage] edit cb\n") );
buffer = gtk_entry_get_text(GTK_ENTRY(editable));
gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT, strlen(buffer) > 0 ? TRUE : FALSE);
}
static void
ui_cat_manage_dialog_edit(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
GtkWidget *dialog, *content_area, *grid;
GtkWidget *label, *w_name, *w_type = NULL, *w_child;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
gint row, n_child;
DB( g_print("\n[ui-cat-manage] edit\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Category *item;
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &item, -1);
n_child = gtk_tree_model_iter_n_children(model, &iter);
dialog = gtk_dialog_new_with_buttons (_("Edit Category"),
GTK_WINDOW (data->dialog),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
grid = gtk_grid_new();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(grid), SPACING_LARGE);
hbtk_box_prepend (GTK_BOX (content_area), grid);
// group :: General
row = 0;
label = make_label_widget(_("_Name:"));
gtk_grid_attach (GTK_GRID (grid), label, 1, row, 1, 1);
w_name = gtk_entry_new();
//gtk_widget_set_size_request (w_name, HB_MINWIDTH_LIST, -1);
gtk_widget_set_hexpand(w_name, TRUE);
gtk_entry_set_text(GTK_ENTRY(w_name), item->name);
gtk_entry_set_activates_default (GTK_ENTRY(w_name), TRUE);
gtk_widget_grab_focus (w_name);
gtk_grid_attach (GTK_GRID (grid), w_name, 2, row, 1, 1);
// group :: Type
row++;
label = make_label_group(_("Change Type"));
gtk_widget_set_margin_top(label, SPACING_LARGE);
gtk_grid_attach (GTK_GRID (grid), label, 0, row, 3, 1);
row++;
w_type = gtk_check_button_new_with_mnemonic(_("_Income"));
gtk_grid_attach (GTK_GRID (grid), w_type, 1, row, 2, 1);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_type), item->flags & GF_INCOME ? TRUE : FALSE);
row++;
w_child = gtk_check_button_new_with_mnemonic(_("Propagate to _children"));
gtk_grid_attach (GTK_GRID (grid), w_child, 1, row, 2, 1);
g_signal_connect (G_OBJECT (w_name), "changed", G_CALLBACK (ui_cat_manage_dialog_edit_entry_cb), dialog);
gtk_widget_show_all(grid);
if( (item->flags & GF_SUB) || n_child == 0 )
{
gtk_widget_hide(w_child);
}
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
const gchar *name;
// 1: manage renaming
name = gtk_entry_get_text(GTK_ENTRY(w_name));
// ignore if item is empty
if (name && *name)
{
if( category_rename(item, name) )
{
//to redraw the active entry
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_cat));
data->change++;
}
else
{
Category *parent;
gchar *fromname, *toname = NULL;
fromname = item->fullname;
if( item->parent == 0)
toname = g_strdup(name);
else
{
parent = da_cat_get(item->parent);
if( parent )
{
toname = g_strdup_printf("%s:%s", parent->name, name);
}
}
ui_dialog_msg_infoerror(GTK_WINDOW(dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot rename this Category,\n"
"from '%s' to '%s',\n"
"this name already exists."),
fromname,
toname
);
g_free(toname);
}
}
// 2: manage flag change
gboolean isIncome = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w_type));
gboolean doChild = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w_child));
data->change += category_change_type(item, isIncome, doChild);
ui_cat_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
static void
ui_cat_manage_dialog_merge_entry_cb(GtkEditable *editable, gpointer user_data)
{
GtkDialog *window = user_data;
const gchar *buffer;
DB( g_print("\n[ui-cat-manage] merge cb\n") );
buffer = gtk_entry_get_text(GTK_ENTRY(editable));
gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_OK, strlen(buffer) > 0 ? TRUE : FALSE);
}
static void
ui_cat_manage_dialog_merge(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox;
GtkWidget *getwidget, *cb_subcat, *togglebutton;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui-cat-manage] merge\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Category *srccat;
gchar *title;
gchar *secondtext;
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &srccat, -1);
title = g_strdup_printf (
_("Merge category '%s'"), srccat->name);
dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
title,
NULL
);
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Merge"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
content = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
secondtext = _("Transactions assigned to this category,\n"
"will be moved to the category selected below.");
g_object_set(GTK_MESSAGE_DIALOG (dialog), "secondary-text", secondtext, NULL);
g_free(title);
//getwidget = ui_cat_comboboxentry_new(NULL);
getwidget = ui_cat_entry_popover_new(NULL);
gtk_box_prepend (GTK_BOX (mainvbox), getwidget);
cb_subcat = gtk_check_button_new_with_mnemonic(_("Include _subcategories"));
gtk_box_prepend (GTK_BOX (mainvbox), cb_subcat);
//#2079801 warn budget
secondtext = g_strdup_printf (
_("_Delete the category '%s' (and any budget)"), srccat->name);
togglebutton = gtk_check_button_new_with_mnemonic(secondtext);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), TRUE);
g_free(secondtext);
gtk_box_prepend (GTK_BOX (mainvbox), togglebutton);
//setup
//gtk_combo_box_set_active(GTK_COMBO_BOX(getwidget), oldpos);
g_signal_connect (G_OBJECT (ui_cat_entry_popover_get_entry(GTK_BOX(getwidget))), "changed", G_CALLBACK (ui_cat_manage_dialog_merge_entry_cb), dialog);
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);
//5.5 done in popover, let's keep the src as well
//ui_cat_comboboxentry_populate_except(GTK_COMBO_BOX(getwidget), GLOBALS->h_cat, srccat->key);
gtk_widget_grab_focus (getwidget);
gtk_widget_show_all(mainvbox);
hb_widget_visible(cb_subcat, (srccat->flags & GF_SUB) ? FALSE : TRUE);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_OK)
{
GtkTreeModel *model;
Category *newcat, *parent;
gboolean dosubcat;
guint dstcatkey;
//dstcatkey = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(getwidget));
dstcatkey = ui_cat_entry_popover_get_key_add_new(GTK_BOX(getwidget));
dosubcat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_subcat));
//do nothing if src = dst...
if( srccat->key != dstcatkey )
{
DB( g_print(" -> move cat to %d (subcat=%d)\n", dstcatkey, dosubcat) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_cat));
gtk_tree_store_clear (GTK_TREE_STORE(model));
category_move(srccat->key, dstcatkey, dosubcat);
newcat = da_cat_get (dstcatkey);
//#1771720: update count
//TODO: this is imperfect here, as if subcat, we don't count ?
newcat->nb_use_all += srccat->nb_use_all;
newcat->nb_use_txn += srccat->nb_use_txn;
srccat->nb_use_all = 0;
srccat->nb_use_txn = 0;
//keep the income type with us
parent = da_cat_get(srccat->parent);
if(parent != NULL && (parent->flags & GF_INCOME))
newcat->flags |= GF_INCOME;
//add the new category into listview
if(newcat)
ui_cat_listview_add(GTK_TREE_VIEW(data->LV_cat), newcat, NULL);
// delete the old category
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)) )
{
DB( g_print(" -> delete %d '%s'\n", srccat->key, srccat->name ) );
ui_cat_listview_remove_selected(GTK_TREE_VIEW(data->LV_cat));
da_cat_delete(srccat->key);
//#2079801 later delete budget
}
data->change++;
ui_cat_manage_dialog_refilter(data);
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
/*
** delete the selected category to our treeview and temp GList
*/
static void
ui_cat_manage_dialog_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
Category *item;
gint result;
DB( g_print("\n[ui-cat-manage] delete\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
item = ui_cat_listview_get_selected(GTK_TREE_VIEW(data->LV_cat));
if( item != NULL && item->key != 0 )
{
gchar *title = NULL;
gchar *secondtext = NULL;
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->name);
if( item->nb_use_all > 0 )
{
secondtext = _("This category is used.\n"
"Any transaction using that category will be set to (no category)");
}
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
ui_cat_listview_remove_selected(GTK_TREE_VIEW(data->LV_cat));
category_move(item->key, 0, TRUE);
da_cat_delete(item->key);
data->change++;
}
}
}
static void
ui_cat_manage_dialog_expand_all(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
DB( g_print("\n[ui-cat-manage] expand all\n") );
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_cat));
}
static void
ui_cat_manage_dialog_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
DB( g_print("\n[ui-cat-manage] collapse all\n") );
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_cat));
}
//#1826360 wish: archive payee/category to lighten the lists
static void
ui_cat_manage_dialog_hide(GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter, child;
Category *item;
gboolean showhidden;
gint n_child;
DB( g_print("\n[ui-cat-manage] hide\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
showhidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showhidden));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat));
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
//manage children
n_child = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
while(n_child > 0)
{
gtk_tree_model_get(model, &child, LST_DEFCAT_DATAS, &item, -1);
if( item != NULL )
{
item->flags ^= GF_HIDDEN;
data->change++;
}
n_child--;
gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
gtk_tree_model_get(model, &iter, LST_DEFCAT_DATAS, &item, -1);
if( item != NULL )
{
item->flags ^= GF_HIDDEN;
data->change++;
}
}
if( showhidden )
//refresh
gtk_widget_queue_draw(data->LV_cat);
else
ui_cat_listview_remove_selected(GTK_TREE_VIEW(data->LV_cat));
}
static void
ui_cat_manage_dialog_update(GtkWidget *treeview, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
gchar *category = NULL;
gboolean selected, sensitive;
gboolean haschild = FALSE;
DB( g_print("\n[ui-cat-manage] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
//window = gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW);
//DB( g_print("(defcategory) widget=%08lx, window=%08lx, inst_data=%08lx\n", treeview, window, data) );
//if true there is a selected node
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)), &model, &iter);
if (selected)
{
gchar *tree_path_str;
Category *item;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
LST_DEFCAT_DATAS, &item,
-1);
if( item->key == 0 )
selected = FALSE;
haschild = gtk_tree_model_iter_has_child(GTK_TREE_MODEL(model), &iter);
DB( g_print(" => has child=%d\n", haschild) );
path = gtk_tree_model_get_path(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)), &iter);
tree_path_str = gtk_tree_path_to_string(path);
DB( g_print(" => select is=%s, depth=%d (id=%d, %s) flags=%d\n",
tree_path_str,
gtk_tree_path_get_depth(path),
item->key,
item->name,
item->flags
) );
g_free(tree_path_str);
//get parent if subcategory selected
DB( g_print(" => get parent for title\n") );
if(gtk_tree_path_get_depth(path) != 1)
gtk_tree_path_up(path);
if(gtk_tree_model_get_iter(model, &iter, path))
{
Category *tmpitem;
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
LST_DEFCAT_DATAS, &tmpitem,
-1);
if(tmpitem->key > 0)
category = tmpitem->name;
DB( g_print(" => parent is %s\n", category) );
}
gtk_tree_path_free(path);
}
DB( g_print(" selected = %d\n", selected) );
gtk_label_set_text(GTK_LABEL(data->LA_category), category);
sensitive = (selected == TRUE) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->ST_name2, sensitive);
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_merge, sensitive);
gtk_widget_set_sensitive(data->BT_hide, sensitive);
//avoid deleting top categories
sensitive = (haschild == TRUE) ? FALSE : sensitive;
gtk_widget_set_sensitive(data->BT_delete, sensitive);
}
static gboolean
ui_cat_manage_dialog_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else
if (keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_cat);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void
ui_cat_manage_dialog_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_print("\n[ui-cat-manage] selection\n") );
ui_cat_manage_dialog_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void
ui_cat_manage_dialog_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("\n[ui-cat-manage] onRowActivated\n") );
//TODO: check if not none, should be done into edit
model = gtk_tree_view_get_model(treeview);
gtk_tree_model_get_iter_first(model, &iter);
if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter) == FALSE)
{
ui_cat_manage_dialog_edit(GTK_WIDGET(treeview), NULL);
}
}
static gboolean
ui_cat_manage_dialog_cleanup(struct ui_cat_manage_dialog_data *data, gint result)
{
gboolean doupdate = FALSE;
DB( g_print("\n[ui-cat-manage] cleanup\n") );
if(result == GTK_RESPONSE_ACCEPT)
{
//do_application_specific_something ();
DB( g_print(" accept\n") );
GLOBALS->changes_count += data->change;
}
DB( g_print(" free tmp_list\n") );
//da_category_destroy(data->tmp_list);
return doupdate;
}
static void
ui_cat_manage_type_changed_cb (GtkToggleButton *button, gpointer user_data)
{
ui_cat_manage_dialog_refilter(user_data);
//g_print(" toggle type=%d\n", gtk_toggle_button_get_active(button));
}
static void
ui_cat_manage_search_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data = user_data;
DB( g_printf("\n[ui-cat-manage] search_changed_cb\n") );
ui_cat_manage_dialog_refilter(data);
}
static void
ui_cat_manage_dialog_setup(struct ui_cat_manage_dialog_data *data)
{
DB( g_print("\n[ui-cat-manage] setup\n") );
DB( g_print(" init data\n") );
//init GList
data->tmp_list = NULL; //data->tmp_list = hb-glist_clone_list(GLOBALS->cat_list, sizeof(struct _Group));
data->change = 0;
data->usagefilled = FALSE;
//#2051419 show hidden by default
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->BT_showhidden), TRUE);
DB( g_print(" populate\n") );
//debug
//da_cat_debug_list();
ui_cat_manage_dialog_refilter(data);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
g_signal_connect (G_OBJECT (data->BT_showhidden), "toggled", G_CALLBACK (ui_cat_manage_dialog_cb_show_hidden), NULL);
g_signal_connect (G_OBJECT (data->BT_showusage) , "toggled", G_CALLBACK (ui_cat_manage_dialog_cb_show_usage), NULL);
g_signal_connect (G_OBJECT (data->RA_type), "changed", G_CALLBACK (ui_cat_manage_type_changed_cb), data);
g_object_bind_property (data->BT_add, "active", data->RE_addreveal, "reveal-child", G_BINDING_BIDIRECTIONAL);
gtk_tree_view_set_search_entry(GTK_TREE_VIEW(data->LV_cat), GTK_ENTRY(data->ST_search));
g_signal_connect (G_OBJECT (data->ST_search), "search-changed", G_CALLBACK (ui_cat_manage_search_changed_cb), data);
g_signal_connect (G_OBJECT (data->ST_name1), "activate", G_CALLBACK (ui_cat_manage_dialog_add), GINT_TO_POINTER(FALSE));
g_signal_connect (G_OBJECT (data->ST_name2), "activate", G_CALLBACK (ui_cat_manage_dialog_add), GINT_TO_POINTER(TRUE));
g_signal_connect(G_OBJECT(data->ST_name1), "insert-text", G_CALLBACK(ui_cat_manage_filter_text_handler), NULL);
g_signal_connect(G_OBJECT(data->ST_name2), "insert-text", G_CALLBACK(ui_cat_manage_filter_text_handler), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_cat)), "changed", G_CALLBACK (ui_cat_manage_dialog_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_cat), "row-activated", G_CALLBACK (ui_cat_manage_dialog_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_cat_manage_dialog_edit), NULL);
g_signal_connect (G_OBJECT (data->BT_merge), "clicked", G_CALLBACK (ui_cat_manage_dialog_merge), NULL);
g_signal_connect (G_OBJECT (data->BT_delete), "clicked", G_CALLBACK (ui_cat_manage_dialog_delete), NULL);
g_signal_connect (G_OBJECT (data->BT_hide), "clicked", G_CALLBACK (ui_cat_manage_dialog_hide), NULL);
g_signal_connect (G_OBJECT (data->BT_expand), "clicked", G_CALLBACK (ui_cat_manage_dialog_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapse), "clicked", G_CALLBACK (ui_cat_manage_dialog_collapse_all), NULL);
}
static gboolean
ui_cat_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_cat_manage_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[ui-cat-manage] mapped\n") );
ui_cat_manage_dialog_setup(data);
ui_cat_manage_dialog_update(data->LV_cat, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static const GActionEntry win_actions[] = {
{ "imp" , ui_cat_manage_dialog_load_csv, NULL, NULL, NULL, {0,0,0} },
{ "exp" , ui_cat_manage_dialog_save_csv, NULL, NULL, NULL, {0,0,0} },
{ "del" , ui_cat_manage_dialog_delete_unused, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
GtkWidget *ui_cat_manage_dialog (void)
{
struct ui_cat_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox, *bbox, *table, *hbox, *vbox, *label, *scrollwin, *treeview;
GtkWidget *widget, *image, *tbar, *revealer;
gint w, h, dw, dh, row;
DB( g_print("\n[ui-cat-manage] new\n") );
data = g_malloc0(sizeof(struct ui_cat_manage_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Categories"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
data->change = 0;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 2:3
dw = (dh * 2) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our window private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" dialog=%p, inst_data=%p\n", dialog, data) );
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_LARGE);
//our table
table = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainvbox), table);
//filter part
row = 0;
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), bbox, 0, row, 2, 1);
widget = make_image_toggle_button(ICONNAME_HB_BUTTON_HIDE, _("Show Hidden") );
data->BT_showhidden = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_toggle_button(ICONNAME_HB_BUTTON_USAGE, _("Show Usage") );
data->BT_showusage = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(widget), CYA_CAT_TYPE, TRUE);
data->RA_type = widget;
//gtk_widget_set_halign (bbox, GTK_ALIGN_CENTER);
gtk_box_prepend (GTK_BOX (bbox), widget);
//menubutton
widget = gtk_menu_button_new();
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_box_append (GTK_BOX (bbox), widget);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Import CSV..."), "win.imp");
g_menu_append (section, _("E_xport CSV..."), "win.exp");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Delete unused..."), "win.del");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (widget, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
widget = make_search();
data->ST_search = widget;
gtk_box_append(GTK_BOX (bbox), widget);
// list + toolbar
row++;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_grid_attach (GTK_GRID (table), vbox, 0, row, 2, 1);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
treeview = ui_cat_listview_new(FALSE, TRUE);
data->LV_cat = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
hbtk_box_prepend (GTK_BOX(vbox), scrollwin);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_toggle_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_delete = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
data->BT_edit = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_HB_LIST_MERGE, _("Move/Merge"));
data->BT_merge = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_HIDE, _("Show/Hide"));
data->BT_hide = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend (GTK_BOX (bbox), widget);
// subcategory + add button
row++;
revealer = gtk_revealer_new ();
data->RE_addreveal = revealer;
gtk_grid_attach (GTK_GRID (table), revealer, 0, row, 2, 1);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
gtk_revealer_set_child (GTK_REVEALER(revealer), vbox);
widget = gtk_entry_new ();
data->ST_name1 = widget;
gtk_entry_set_placeholder_text(GTK_ENTRY(data->ST_name1), _("new category") );
hbtk_box_prepend (GTK_BOX (vbox), widget);
row++;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_box_prepend (GTK_BOX (vbox), hbox);
data->LA_category = gtk_label_new(NULL);
gtk_box_prepend (GTK_BOX (hbox), data->LA_category);
label = gtk_label_new(":");
gtk_box_prepend (GTK_BOX (hbox), label);
data->ST_name2 = gtk_entry_new ();
gtk_entry_set_placeholder_text(GTK_ENTRY(data->ST_name2), _("new subcategory") );
hbtk_box_prepend (GTK_BOX (hbox), data->ST_name2);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_cat_manage_mapped), &dialog);
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_cat_manage_dialog_cb_on_key_press), (gpointer)data);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
ui_cat_manage_dialog_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-hbfile.c 0000644 0001750 0001750 00000032656 14736461407 015415 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-hbfile.h"
#include "hb-archive.h"
#include "hb-transaction.h"
#include "ui-flt-widget.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gboolean hbfile_file_isbackup(gchar *filepath)
{
gboolean retval = FALSE;
if( filepath == NULL )
return FALSE;
if( g_str_has_suffix(filepath, "xhb~") || g_str_has_suffix(filepath, "bak") )
retval = TRUE;
return retval;
}
gboolean hbfile_file_hasrevert(gchar *filepath)
{
gchar *bakfilepath;
bakfilepath = hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~");
DB( g_print(" test bak exists '%s'\n", bakfilepath) );
GLOBALS->xhb_hasrevert = g_file_test(bakfilepath, G_FILE_TEST_EXISTS);
g_free(bakfilepath);
//todo check here if need to return something
return GLOBALS->xhb_hasrevert;
}
//#1750161
guint64 hbfile_file_get_time_modified(gchar *filepath)
{
guint64 retval = 0ULL;
GFile *gfile;
GFileInfo *gfileinfo;
DB( g_print("\n[hbfile] get time modified\n") );
gfile = g_file_new_for_path(filepath);
gfileinfo = g_file_query_info (gfile, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
if( gfileinfo )
{
retval = g_file_info_get_attribute_uint64 (gfileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
DB( g_print(" '%s' last access = %lu\n", filepath, retval) );
//add 5.6.2 if file opened more than 24h, currencies are obsolete
GDateTime *dtf = g_file_info_get_modification_date_time(gfileinfo);
gchar *dts = g_date_time_format_iso8601(dtf);
GDateTime *dtn = g_date_time_new_now_local();
GTimeSpan ts = g_date_time_difference(dtn, dtf);
DB( g_print(" modif datetime='%s' since %ld, %ld hours\n", dts, ts, ts/G_TIME_SPAN_HOUR) );
GLOBALS->xhb_obsoletecurr = ((ts/G_TIME_SPAN_HOUR) > 24) ? TRUE : FALSE;
g_free(dts);
g_date_time_unref(dtf);
g_date_time_unref(dtn);
g_object_unref(gfileinfo);
}
g_object_unref(gfile);
return retval;
}
void hbfile_file_default(void)
{
DB( g_print("\n[hbfile] default\n") );
//todo: maybe translate this also
hbfile_change_filepath(g_build_filename(PREFS->path_hbfile, "untitled.xhb", NULL));
GLOBALS->hbfile_is_new = TRUE;
GLOBALS->hbfile_is_bak = FALSE;
GLOBALS->xhb_timemodified = 0ULL;
DB( g_print("- path_hbfile is '%s'\n", PREFS->path_hbfile) );
DB( g_print("- xhb_filepath is '%s'\n", GLOBALS->xhb_filepath) );
}
/*
static gint hbfile_file_load_xhb(gchar *filepath)
{
}
static void hbfile_file_load_backup_xhb(void)
{
//todo: get from dialog.c, and split between dilaog.c/hbfile.c
}
*/
void hbfile_replace_basecurrency(Currency4217 *curfmt)
{
Currency *item;
guint32 oldkcur;
DB( g_print("\n[hbfile] replace base currency\n") );
oldkcur = GLOBALS->kcur;
da_cur_delete(oldkcur);
item = currency_add_from_user(curfmt);
GLOBALS->kcur = item->key;
DB( g_print(" %d ==> %d %s\n", oldkcur, GLOBALS->kcur, item->iso_code) );
}
void hbfile_change_basecurrency(guint32 key)
{
GList *list;
//guint32 oldkcur;
// set every rate to 0
list = g_hash_table_get_values(GLOBALS->h_cur);
while (list != NULL)
{
Currency *entry = list->data;
if(entry->key != GLOBALS->kcur)
{
entry->rate = 0.0;
entry->mdate = 0;
}
list = g_list_next(list);
}
g_list_free(list);
//oldkcur = GLOBALS->kcur;
GLOBALS->kcur = key;
//#1851103: absolutely no reason to do that
// update account with old base currency
/*list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc = list->data;
if( acc->kcur == oldkcur )
acc->kcur = key;
list = g_list_next(list);
}
g_list_free(list);
*/
GLOBALS->changes_count++;
}
static GQueue *hbfile_transaction_get_partial_internal(guint32 minjulian, guint32 maxjulian, gushort exclusionflags)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GQueue *txn_queue;
txn_queue = g_queue_new ();
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
if( (acc->flags & exclusionflags) )
goto next_acc;
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
if( txn->date < minjulian ) //no need to go below mindate
break;
//#1886123 include remind based on user prefs
if( !transaction_is_balanceable(txn) )
goto prev_txn;
if( (txn->date >= minjulian) && (txn->date <= maxjulian) )
{
g_queue_push_head (txn_queue, txn);
}
prev_txn:
lnk_txn = g_list_previous(lnk_txn);
}
next_acc:
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
return txn_queue;
}
GQueue *hbfile_transaction_get_partial(guint32 minjulian, guint32 maxjulian)
{
//#1674045 only rely on nosummary
//return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_CLOSED|AF_NOREPORT));
return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_NOREPORT));
}
GQueue *hbfile_transaction_get_partial_budget(guint32 minjulian, guint32 maxjulian)
{
//#1674045 only rely on nosummary
//return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_CLOSED|AF_NOREPORT|AF_NOBUDGET));
return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_NOREPORT|AF_NOBUDGET));
}
void hbfile_sanity_check(void)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *lxxx, *list;
DB( g_print("\n[hbfile] !! full sanity check !! \n") );
DB( g_print(" - transaction\n") );
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
da_transaction_consistency(txn);
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
DB( g_print(" - scheduled/template\n") );
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
da_archive_consistency(entry);
list = g_list_next(list);
}
DB( g_print(" - account\n") );
lxxx = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *item = list->data;
da_acc_consistency(item);
list = g_list_next(list);
}
g_list_free(lxxx);
DB( g_print(" - payee\n") );
lxxx = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *item = list->data;
da_pay_consistency(item);
list = g_list_next(list);
}
g_list_free(lxxx);
DB( g_print(" - category\n") );
lxxx = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
Category *item = list->data;
da_cat_consistency(item);
list = g_list_next(list);
}
g_list_free(lxxx);
DB( g_print(" - assignments\n") );
lxxx = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *item = list->data;
da_asg_consistency(item);
list = g_list_next(list);
}
g_list_free(lxxx);
//#2018414 replace any space by -
DB( g_print(" - tags\n") );
lxxx = list = g_hash_table_get_values(GLOBALS->h_tag);
while (list != NULL)
{
Tag *item = list->data;
da_tag_consistency(item);
list = g_list_next(list);
}
g_list_free(lxxx);
}
void hbfile_anonymize(void)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *lxxx, *list;
guint cnt;
DB( g_print("\n[hbfile] anonymize\n") );
// owner
hbfile_change_owner(g_strdup("An0nym0us"));
GLOBALS->changes_count++;
GLOBALS->hbfile_is_new = TRUE;
// filename
hbfile_change_filepath(g_build_filename(PREFS->path_hbfile, "anonymized.xhb", NULL));
// accounts
lxxx = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
//#2026641
da_acc_anonymize(list->data);
GLOBALS->changes_count++;
list = g_list_next(list);
}
g_list_free(lxxx);
//payees
lxxx = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *item = list->data;
if(item->key != 0)
{
g_free(item->name);
item->name = g_strdup_printf("payee %d", item->key);
GLOBALS->changes_count++;
}
list = g_list_next(list);
}
g_list_free(lxxx);
//categories
//lxxx = list = g_hash_table_get_values(GLOBALS->h_cat);
lxxx = list = category_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Category *item = list->data;
if(item->key != 0)
{
//#2026641
da_cat_anonymize(item);
GLOBALS->changes_count++;
}
list = g_list_next(list);
}
g_list_free(lxxx);
//tags
lxxx = list = g_hash_table_get_values(GLOBALS->h_tag);
while (list != NULL)
{
Tag *item = list->data;
if(item->key != 0)
{
g_free(item->name);
item->name = g_strdup_printf("tag %d", item->key);
GLOBALS->changes_count++;
}
list = g_list_next(list);
}
g_list_free(lxxx);
//assigns
lxxx = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *item = list->data;
if(item->key != 0)
{
g_free(item->search);
item->search = g_strdup_printf("assign %d", item->key);
//#2026641 assignment notes
g_free(item->notes);
item->notes = NULL;
GLOBALS->changes_count++;
}
list = g_list_next(list);
}
g_list_free(lxxx);
//archives
cnt = 0;
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *item = list->data;
g_free(item->memo);
item->memo = g_strdup_printf("archive %d", cnt++);
GLOBALS->changes_count++;
//#2026641 splits memo as well
if(item->flags & OF_SPLIT)
{
cnt = da_splits_anonymize(item->splits);
GLOBALS->changes_count += cnt;
}
list = g_list_next(list);
}
//transaction
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *item = lnk_txn->data;
g_free(item->number);
item->number = NULL;
g_free(item->memo);
item->memo = g_strdup_printf("memo %d", item->date);
GLOBALS->changes_count++;
if(item->flags & OF_SPLIT)
{
//#2026641
cnt = da_splits_anonymize(item->splits);
GLOBALS->changes_count += cnt;
}
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
void hbfile_change_owner(gchar *owner)
{
g_free(GLOBALS->owner);
GLOBALS->owner = (owner != NULL) ? owner : NULL;
}
void hbfile_change_filepath(gchar *filepath)
{
g_free(GLOBALS->xhb_filepath);
GLOBALS->xhb_filepath = (filepath != NULL) ? filepath : NULL;
}
void hbfile_cleanup(gboolean file_clear)
{
GSList *list;
//Transaction *txn;
DB( g_print("\n[hbfile] cleanup\n") );
DB( g_print("- file clear is %d\n", file_clear) );
// Free data storage
/*txn = g_trash_stack_pop(&GLOBALS->txn_stk);
while( txn != NULL )
{
da_transaction_free (txn);
txn = g_trash_stack_pop(&GLOBALS->txn_stk);
}*/
list = GLOBALS->deltxn_list;
while(list != NULL)
{
da_transaction_free (list->data);
list = g_slist_next(list);
}
g_slist_free(GLOBALS->deltxn_list);
g_slist_free(GLOBALS->openwindows);
da_transaction_destroy();
da_archive_destroy(GLOBALS->arc_list);
g_hash_table_destroy(GLOBALS->h_memo);
da_flt_destroy();
g_object_unref(GLOBALS->fltmodel);
da_asg_destroy();
da_tag_destroy();
da_cat_destroy();
da_pay_destroy();
da_acc_destroy();
da_grp_destroy();
da_cur_destroy();
hbfile_change_owner(NULL);
if(file_clear)
hbfile_change_filepath(NULL);
}
void hbfile_setup(gboolean file_clear)
{
DB( g_print("\n[hbfile] setup\n") );
DB( g_print("- file clear is %d\n", file_clear) );
// Allocate data storage
da_cur_new();
da_grp_new();
da_acc_new();
//txn queue is alloc/free into account
da_pay_new();
da_cat_new();
da_tag_new();
da_asg_new();
GLOBALS->fltmodel = lst_lst_favfilter_model_new();
da_flt_new();
GLOBALS->h_memo = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
GLOBALS->arc_list = NULL;
//5.5.1 list of opened windows
GLOBALS->openwindows = NULL;
//#1419304 we keep the deleted txn to a trash stack
//GLOBALS->txn_stk = NULL;
GLOBALS->deltxn_list = NULL;
if(file_clear == TRUE)
{
hbfile_file_default();
}
else
{
GLOBALS->hbfile_is_new = FALSE;
}
hbfile_change_owner(g_strdup(_("Unknown")));
GLOBALS->kcur = 1;
GLOBALS->vehicle_category = 0;
GLOBALS->auto_smode = ARC_POSTMODE_DUEDATE;
GLOBALS->auto_weekday = 1;
GLOBALS->auto_nbmonths = 1;
GLOBALS->auto_nbdays = 0;
GLOBALS->changes_count = 0;
GLOBALS->xhb_hasrevert = FALSE;
}
homebank-5.9.7/src/hb-filter.c 0000644 0001750 0001750 00000112344 15121277361 015433 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-filter.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
//DB out of bounds
#define DBOOB(x);
//#define DBOOB(x) (x);
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = */
static void da_flt_clean(Filter *flt)
{
if(flt != NULL)
{
g_free(flt->memo);
flt->memo = NULL;
g_free(flt->number);
flt->number = NULL;
g_free(flt->name);
flt->name = NULL;
if(flt->gbtag != NULL)
{
g_array_free(flt->gbtag, TRUE);
flt->gbtag = NULL;
}
if(flt->gbcat != NULL)
{
g_array_free(flt->gbcat, TRUE);
flt->gbcat = NULL;
}
if(flt->gbpay != NULL)
{
g_array_free(flt->gbpay, TRUE);
flt->gbpay = NULL;
}
if(flt->gbacc != NULL)
{
g_array_free(flt->gbacc, TRUE);
flt->gbacc = NULL;
}
}
}
static void da_flt_init(Filter *flt)
{
DB( g_print("da_flt_init\n") );
//5.8 init range to min/max glib bound
flt->range = FLT_RANGE_MISC_ALLDATE;
flt->mindate = HB_MINDATE; //1;
flt->maxdate = HB_MAXDATE; //G_MAXUINT32;
//always allocate 1 elt minimum
// /!\ each array still ->len = 0
flt->gbacc = g_array_sized_new(FALSE, TRUE, sizeof(gchar), 1);
flt->gbpay = g_array_sized_new(FALSE, TRUE, sizeof(gchar), 1);
flt->gbcat = g_array_sized_new(FALSE, TRUE, sizeof(gchar), 1);
flt->gbtag = g_array_sized_new(FALSE, TRUE, sizeof(gchar), 1);
}
void da_flt_free(Filter *flt)
{
DB( g_print("da_flt_free\n") );
if(flt != NULL)
{
da_flt_clean(flt);
g_free(flt);
}
}
Filter *da_flt_malloc(void)
{
Filter *flt;
DB( g_print("da_flt_malloc\n") );
flt = g_malloc0(sizeof(Filter));
da_flt_init(flt);
return flt;
}
static guint count_garray(GArray *array)
{
guint count, i;
for(i=0,count=0 ; i < array->len ; i++)
{
if( array->data[i] == 1 )
count++;
}
return count;
}
void da_flt_count_item(Filter *flt)
{
guint i, count;
flt->n_active = 0;
for(i=0;ioption[i] != 0 )
flt->n_active++;
}
flt->n_item[FLT_GRP_ACCOUNT] = count_garray(flt->gbacc);
flt->n_item[FLT_GRP_PAYEE] = count_garray(flt->gbpay);
flt->n_item[FLT_GRP_CATEGORY] = count_garray(flt->gbcat);
flt->n_item[FLT_GRP_TAG] = count_garray(flt->gbtag);
for(i=0, count=0;ipaymode[i] == TRUE )
count++;
}
flt->n_item[FLT_GRP_PAYMODE] = count;
}
void da_flt_copy(Filter *src, Filter *dst)
{
DB( g_print("da_flt_copy\n") );
DB( g_print(" %p (%s) to %p (%s)\n", src, src->name, dst, dst->name) );
if(!src || !dst)
return;
DB( g_print(" %d acc\n", src->gbacc->len) );
DB( g_print(" %d pay\n", src->gbpay->len) );
DB( g_print(" %d cat\n", src->gbcat->len) );
//clean any previous extra memory
da_flt_clean(dst);
//raw duplicate the memory segment
memcpy(dst, src, sizeof(Filter));
//duplicate extra memory
dst->name = g_strdup(src->name);
dst->number = g_strdup(src->number);
dst->memo = g_strdup(src->memo);
dst->gbacc = g_array_copy(src->gbacc);
dst->gbpay = g_array_copy(src->gbpay);
dst->gbcat = g_array_copy(src->gbcat);
dst->gbtag = g_array_copy(src->gbtag);
DB( g_print(" %d acc\n", dst->gbacc->len) );
DB( g_print(" %d pay\n", dst->gbpay->len) );
DB( g_print(" %d cat\n", dst->gbcat->len) );
//#2065929 update from prefs
dst->forceremind = PREFS->showremind;
dst->forcevoid = PREFS->showvoid;
DB( g_print(" copied\n\n") );
da_flt_count_item(dst);
}
void
da_flt_destroy(void)
{
DB( g_print("da_flt_destroy\n") );
g_hash_table_destroy(GLOBALS->h_flt);
}
void
da_flt_new(void)
{
//Filter *item;
DB( g_print("da_flt_new\n") );
GLOBALS->h_flt = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_flt_free);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint
da_flt_length(void)
{
return g_hash_table_size(GLOBALS->h_flt);
}
static void da_flt_max_key_ghfunc(gpointer key, Filter *item, guint32 *max_key)
{
*max_key = MAX(*max_key, item->key);
}
guint32
da_flt_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_flt, (GHFunc)da_flt_max_key_ghfunc, &max_key);
return max_key;
}
gboolean
da_flt_remove(guint32 key)
{
DB( g_print("da_flt_remove %d\n", key) );
return g_hash_table_remove(GLOBALS->h_flt, &key);
}
gboolean
da_flt_insert(Filter *item)
{
guint32 *new_key;
GtkTreeIter iter;
DB( g_print("da_flt_insert\n") );
new_key = g_new0(guint32, 1);
*new_key = item->key;
g_hash_table_insert(GLOBALS->h_flt, new_key, item);
//limit to 20 char
if( strlen(item->name) > 20 )
{
gchar *truncname = g_strdup_printf("%.20s...", item->name);
gtk_list_store_insert_with_values(GTK_LIST_STORE(GLOBALS->fltmodel), &iter, 0,
0, item->key,
1, truncname,
-1);
g_free(truncname);
}
else
{
//add to model
gtk_list_store_insert_with_values(GTK_LIST_STORE(GLOBALS->fltmodel), &iter, 0,
0, item->key,
1, item->name,
-1);
}
return TRUE;
}
gboolean
da_flt_append(Filter *item)
{
Filter *existitem;
DB( g_print("da_flt_append\n") );
existitem = da_flt_get_by_name( item->name );
if( existitem == NULL )
{
item->key = da_flt_get_max_key() + 1;
da_flt_insert(item);
return TRUE;
}
DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
return FALSE;
}
static gboolean da_flt_name_grfunc(gpointer key, Filter *item, gchar *name)
{
if( name && item->name )
{
if(!strcasecmp(name, item->name))
return TRUE;
}
return FALSE;
}
Filter *
da_flt_get_by_name(gchar *rawname)
{
Filter *retval = NULL;
gchar *stripname;
DB( g_print("da_flt_get_by_name\n") );
if( rawname )
{
stripname = g_strdup(rawname);
g_strstrip(stripname);
if( strlen(stripname) > 0 )
retval = g_hash_table_find(GLOBALS->h_flt, (GHRFunc)da_flt_name_grfunc, stripname);
g_free(stripname);
}
return retval;
}
Filter *
da_flt_get(guint32 key)
{
//DB( g_print("da_flt_get\n") );
return g_hash_table_lookup(GLOBALS->h_flt, &key);
}
void da_flt_consistency(Filter *item)
{
g_strstrip(item->name);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gint
filter_glist_key_compare_func(Filter *a, Filter *b)
{
return a->key - b->key;
}
GList *filter_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_flt);
switch(column)
{
default:
return g_list_sort(list, (GCompareFunc)filter_glist_key_compare_func);
break;
}
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG == 1
static void my_debug_garray(gchar *type, GArray *array)
{
guint i;
g_print(" debug '%s' len=%d\n", type, array->len);
for(i=0 ; i < array->len ; i++)
{
g_print("%02d ", array->data[i]);
}
g_print("\n");
}
#endif
static guint da_flt_item_set(GArray *array, guint32 key, gboolean status)
{
guint change = 0;
if(key < array->len)
{
gchar *sel = &g_array_index(array, gchar, key);
change += (*sel != status) ? 1 : 0;
*sel = status;
DB( g_print(" >update [%d]=>%d\n", key, status) );
}
else
if( status == TRUE )
{
DB( g_print(" >insert [%d]=>%d\n", key, status) );
g_array_insert_vals(array, key, &status, 1);
change++;
}
else
{
DB( g_print(" >nop: status off\n") );
}
return change;
}
guint da_flt_status_tag_set(Filter *flt, guint32 ktag, gboolean status)
{
DB( g_print(" set tag %d to %d\n", ktag, status) );
return da_flt_item_set(flt->gbtag, ktag, status);
}
guint da_flt_status_cat_set(Filter *flt, guint32 kcat, gboolean status)
{
DB( g_print(" set cat %d\n", kcat) );
return da_flt_item_set(flt->gbcat, kcat, status);
}
guint da_flt_status_pay_set(Filter *flt, guint32 kpay, gboolean status)
{
DB( g_print(" set pay %d\n", kpay) );
return da_flt_item_set(flt->gbpay, kpay, status);
}
guint da_flt_status_acc_set(Filter *flt, guint32 kacc, gboolean status)
{
DB( g_print(" set acc %d\n", kacc) );
return da_flt_item_set(flt->gbacc, kacc, status);
}
gboolean da_flt_status_tag_get(Filter *flt, guint32 ktag)
{
if(flt->gbtag == NULL)
return FALSE;
if(ktag < flt->gbtag->len)
return flt->gbtag->data[ktag];
DBOOB( g_warning("filter get tag out of bounds %d of %d", ktag, flt->gbtag->len) );
return FALSE;
}
gboolean da_flt_status_cat_get(Filter *flt, guint32 kcat)
{
if(flt->gbcat == NULL)
return FALSE;
if(kcat < flt->gbcat->len)
return flt->gbcat->data[kcat];
DBOOB( g_warning("filter get cat out of bounds %d of %d", kcat, flt->gbcat->len) );
return FALSE;
}
gboolean da_flt_status_pay_get(Filter *flt, guint32 kpay)
{
if(flt->gbpay == NULL)
return FALSE;
if(kpay < flt->gbpay->len)
return flt->gbpay->data[kpay];
DBOOB( g_warning("filter get pay out of bounds %d of %d", kpay, flt->gbpay->len) );
return FALSE;
}
gboolean da_flt_status_acc_get(Filter *flt, guint32 kacc)
{
if(flt->gbacc == NULL)
return FALSE;
if(kacc < flt->gbacc->len)
return flt->gbacc->data[kacc];
DBOOB( g_warning("filter get acc out of bounds %d of %d", kacc, flt->gbacc->len) );
return FALSE;
}
/* TODO: check this : user in rep_time only */
void filter_status_acc_clear_except(Filter *flt, guint32 selkey)
{
guint i;
DB( g_print("[filter] acc clear %d\n", flt->gbacc->len) );
for(i=0;igbacc->len;i++)
{
flt->gbacc->data[i] = 0;
}
da_flt_status_acc_set(flt, selkey, TRUE);
}
void filter_status_pay_clear_except(Filter *flt, guint32 selkey)
{
guint i;
DB( g_print("[filter] pay clear %d\n", flt->gbpay->len) );
for(i=0;igbpay->len;i++)
{
flt->gbpay->data[i] = 0;
}
da_flt_status_pay_set(flt, selkey, TRUE);
}
void filter_status_cat_clear_except(Filter *flt, guint32 selkey)
{
guint i;
DB( g_print("[filter] cat clear %d\n", flt->gbcat->len) );
for(i=0;igbcat->len;i++)
{
flt->gbcat->data[i] = 0;
}
da_flt_status_cat_set(flt, selkey, TRUE);
//todo
//#1824561 don't forget subcat
}
/* = = = = = = = = = = = = = = = = */
void filter_reset(Filter *flt)
{
gint i;
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] default reset all %p\n", flt) );
flt->key = 0;
for(i=0;ioption[i] = 0;
}
flt->option[FLT_GRP_DATE] = 1;
//5.4.2: useless, as it is always changed after all
//flt->range = FLT_RANGE_LAST12MONTHS;
//filter_preset_daterange_set(flt, flt->range, 0);
flt->range = FLT_RANGE_LAST_30DAYS;
flt->mindate = GLOBALS->today-30;
flt->maxdate = GLOBALS->today;
for(i=0;ipaymode[i] = TRUE;
//reinit the array acc/pay/cat/tag + text
da_flt_clean(flt);
da_flt_init(flt);
//unsaved
flt->nbchanges = 0;
flt->nbdaysfuture = 0;
flt->type = FLT_TYPE_ALL;
flt->status = FLT_STATUS_ALL;
flt->forceremind = PREFS->showremind;
flt->forcevoid = PREFS->showvoid;
*flt->last_tab = '\0';
}
static void filter_set_date_bounds(Filter *flt, guint32 kacc)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] set date bounds %p\n", flt) );
flt->mindate = 0;
flt->maxdate = 0;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
//#1674045 only rely on nosummary
//if( !(acc->flags & AF_CLOSED) )
{
Transaction *txn;
DB( g_print(" collect date for '%s'\n", acc->name) );
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
if(lnk_txn) {
txn = lnk_txn->data;
if( (kacc == 0) || (txn->kacc == kacc) )
{
if( flt->mindate == 0 )
flt->mindate = txn->date;
else
flt->mindate = MIN(flt->mindate, txn->date);
}
}
lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
if(lnk_txn) {
txn = lnk_txn->data;
if( (kacc == 0) || (txn->kacc == kacc) )
{
if( flt->maxdate == 0 )
flt->maxdate = txn->date;
else
flt->maxdate = MAX(flt->maxdate, txn->date);
}
}
}
lnk_acc = g_list_next(lnk_acc);
}
if( flt->mindate == 0 )
//changed 5.3
//flt->mindate = HB_MINDATE;
flt->mindate = GLOBALS->today - 365;
if( flt->maxdate == 0 )
//changed 5.3
//flt->maxdate = HB_MAXDATE;
flt->maxdate = GLOBALS->today + flt->nbdaysfuture;
g_list_free(lst_acc);
}
gboolean filter_preset_daterange_future_enable(Filter *flt, gint range)
{
gboolean retval = FALSE;
g_return_val_if_fail( flt != NULL, FALSE );
DB( g_print("\n[filter] range future enabled\n") );
DB( g_print(" fltrang=%d range=%d\n", flt->range, range) );
switch( range )
{
case FLT_RANGE_THIS_DAY:
case FLT_RANGE_THIS_WEEK:
case FLT_RANGE_THIS_FORTNIGHT:
case FLT_RANGE_THIS_MONTH:
case FLT_RANGE_THIS_QUARTER:
case FLT_RANGE_THIS_YEAR:
case FLT_RANGE_LAST_30DAYS:
case FLT_RANGE_LAST_60DAYS:
case FLT_RANGE_LAST_90DAYS:
case FLT_RANGE_LAST_12MONTHS:
case FLT_RANGE_LAST_6MONTHS:
retval = TRUE;
break;
}
//TODO: custom date
if( range == FLT_RANGE_MISC_ALLDATE )
{
GDate *date1, *date2;
DB( g_print(" eval alldate\n") );
date1 = g_date_new_julian(GLOBALS->today);
date2 = g_date_new_julian(flt->maxdate);
if( (flt->maxdate > GLOBALS->today)
&& (g_date_get_year(date2) == g_date_get_year(date1))
&& (g_date_get_month(date2) == g_date_get_month(date1)) )
{
retval = TRUE;
}
g_date_free(date2);
g_date_free(date1);
}
DB( hb_print_date(flt->maxdate , " maxdate ") );
DB( g_print(" return: %s\n", retval==TRUE ? "yes" : "no") );
return retval;
}
//5.7 used only in rep_stats
guint32 filter_get_maxdate_forecast(Filter *flt)
{
guint32 retval;
retval = flt->maxdate;
DB( g_print("\n[filter] get maxdate to forecast\n") );
//if( filter_preset_daterange_future_enable(filter, filter->range) )
{
GDate *post_date = g_date_new();
g_date_set_time_t(post_date, time(NULL));
g_date_add_months(post_date, PREFS->rep_forecat_nbmonth);
g_date_set_day(post_date, g_date_get_days_in_month(g_date_get_month(post_date), g_date_get_year(post_date)));
retval = g_date_get_julian(post_date);
g_date_free(post_date);
}
DB( hb_print_date(retval, "retval:") );
return retval;
}
//used only in ledger
void filter_preset_daterange_add_futuregap(Filter *flt, gboolean usrfuture)
{
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] range add future gap\n") );
DB( g_print(" pref=%d usrfuture=%d\n", PREFS->date_future_nbdays, usrfuture) );
flt->nbdaysfuture = 0;
if( PREFS->date_future_nbdays == 0 || !usrfuture )
return;
//#1840998 if future active and visible: we should always maxdate to today + nbdays
if( filter_preset_daterange_future_enable(flt, flt->range) )
{
guint32 jforcedmax = GLOBALS->today + PREFS->date_future_nbdays;
if( flt->maxdate < jforcedmax )
flt->nbdaysfuture = jforcedmax - flt->maxdate;
//else
// flt->nbdaysfuture = nbdays;
DB( g_print(" today=%d, tmpmax=%d, nbdays=%d\n final=%d\n", GLOBALS->today, jforcedmax, PREFS->date_future_nbdays, flt->nbdaysfuture) );
}
}
void filter_preset_daterange_set(Filter *flt, gint range, guint32 kacc)
{
GDate *tmpdate;
guint32 jtoday, jfiscal;
guint16 month, year, yfiscal, qnum;
GDateWeekday wday;
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] daterange set %p %d\n", flt, range) );
flt->range = range;
jtoday = GLOBALS->today;
tmpdate = g_date_new_julian(jtoday);
month = g_date_get_month(tmpdate);
year = g_date_get_year(tmpdate);
DB( hb_print_date(jtoday , "today ") );
qnum = 0;
yfiscal = year;
if( range == FLT_RANGE_LAST_QUARTER || range == FLT_RANGE_THIS_QUARTER ||range == FLT_RANGE_NEXT_QUARTER ||
//#2000834
range == FLT_RANGE_LAST_YEAR || range == FLT_RANGE_THIS_YEAR || range == FLT_RANGE_NEXT_YEAR ||
//5.9 year to date
//#2107704 missed range == ...
range == FLT_RANGE_TODATE_YEAR
)
{
g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, year);
jfiscal = g_date_get_julian(tmpdate);
DB( hb_print_date(jfiscal, "fiscal") );
yfiscal = (jtoday >= jfiscal) ? year : year-1;
if( range == FLT_RANGE_LAST_QUARTER || range == FLT_RANGE_THIS_QUARTER ||range == FLT_RANGE_NEXT_QUARTER )
{
g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
while( (qnum < 5) && (g_date_get_julian(tmpdate) < jtoday) )
{
qnum++;
g_date_add_months (tmpdate, 3);
}
DB( g_print(" qnum: %d\n", qnum ) );
}
}
switch( range )
{
case FLT_RANGE_LAST_DAY:
flt->mindate = flt->maxdate = jtoday - 1;
break;
case FLT_RANGE_THIS_DAY:
flt->mindate = flt->maxdate = jtoday;
break;
case FLT_RANGE_NEXT_DAY:
flt->mindate = flt->maxdate = jtoday + 1;
break;
case FLT_RANGE_LAST_WEEK:
case FLT_RANGE_THIS_WEEK:
case FLT_RANGE_NEXT_WEEK:
//ISO 8601 from must be monday, to slice in correct weekœ
wday = g_date_get_weekday(tmpdate);
g_date_subtract_days (tmpdate, wday-G_DATE_MONDAY);
if( range == FLT_RANGE_LAST_WEEK )
g_date_subtract_days(tmpdate, 7);
else
if( range == FLT_RANGE_NEXT_WEEK )
g_date_add_days(tmpdate, 7);
flt->mindate = g_date_get_julian(tmpdate);
g_date_add_days(tmpdate, 7);
flt->maxdate = g_date_get_julian(tmpdate) - 1;
break;
case FLT_RANGE_LAST_FORTNIGHT:
case FLT_RANGE_THIS_FORTNIGHT:
case FLT_RANGE_NEXT_FORTNIGHT:
//ISO 8601 from must be monday, to slice in correct week
wday = g_date_get_weekday(tmpdate);
g_date_subtract_days (tmpdate, wday - G_DATE_MONDAY);
if( range == FLT_RANGE_LAST_FORTNIGHT )
g_date_subtract_days(tmpdate, 14);
else
if( range == FLT_RANGE_NEXT_FORTNIGHT )
g_date_add_days(tmpdate, 14);
flt->mindate = g_date_get_julian(tmpdate);
g_date_add_days(tmpdate, 14);
flt->maxdate = g_date_get_julian(tmpdate) - 1;
break;
case FLT_RANGE_LAST_MONTH:
case FLT_RANGE_THIS_MONTH:
case FLT_RANGE_NEXT_MONTH:
g_date_set_dmy(tmpdate, 1, month, year);
if( range == FLT_RANGE_LAST_MONTH )
g_date_subtract_months(tmpdate, 1);
else
if( range == FLT_RANGE_NEXT_MONTH )
g_date_add_months(tmpdate, 1);
flt->mindate = g_date_get_julian(tmpdate);
month = g_date_get_month(tmpdate);
year = g_date_get_year(tmpdate);
g_date_add_days(tmpdate, g_date_get_days_in_month(month, year));
flt->maxdate = g_date_get_julian(tmpdate) - 1;
break;
case FLT_RANGE_LAST_QUARTER:
case FLT_RANGE_THIS_QUARTER:
case FLT_RANGE_NEXT_QUARTER:
g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
if( range == FLT_RANGE_LAST_QUARTER )
g_date_subtract_months(tmpdate, 3);
else
if( range == FLT_RANGE_NEXT_QUARTER )
g_date_add_months(tmpdate, 3);
g_date_add_months(tmpdate, 3 * (qnum-1) );
flt->mindate = g_date_get_julian(tmpdate);
g_date_add_months(tmpdate, 3);
flt->maxdate = g_date_get_julian(tmpdate) - 1;
break;
case FLT_RANGE_LAST_YEAR:
case FLT_RANGE_THIS_YEAR:
case FLT_RANGE_NEXT_YEAR:
g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
if( range == FLT_RANGE_LAST_YEAR )
g_date_subtract_years(tmpdate, 1);
else
if( range == FLT_RANGE_NEXT_YEAR )
g_date_add_years(tmpdate, 1);
flt->mindate = g_date_get_julian(tmpdate);
g_date_add_years (tmpdate, 1);
flt->maxdate = g_date_get_julian(tmpdate) - 1;
break;
case FLT_RANGE_LAST_30DAYS:
flt->mindate = jtoday - 30;
flt->maxdate = jtoday;
break;
case FLT_RANGE_LAST_60DAYS:
flt->mindate = jtoday - 60;
flt->maxdate = jtoday;
break;
case FLT_RANGE_LAST_90DAYS:
flt->mindate = jtoday - 90;
flt->maxdate = jtoday;
break;
case FLT_RANGE_LAST_12MONTHS:
g_date_set_julian (tmpdate, jtoday);
//5.7.3 set 1st day of month
g_date_subtract_months(tmpdate, 11);
g_date_set_day(tmpdate, 1);
flt->mindate = g_date_get_julian(tmpdate);
flt->maxdate = jtoday;
break;
case FLT_RANGE_LAST_6MONTHS:
g_date_set_julian (tmpdate, jtoday);
//5.7.3 set 1st day of month
g_date_subtract_months(tmpdate, 5);
g_date_set_day(tmpdate, 1);
flt->mindate = g_date_get_julian(tmpdate);
flt->maxdate = jtoday;
break;
// case FLT_RANGE_MISC_CUSTOM:
//nothing to do
case FLT_RANGE_MISC_ALLDATE:
filter_set_date_bounds(flt, kacc);
break;
case FLT_RANGE_MISC_30DAYS:
flt->mindate = jtoday - 30;
flt->maxdate = jtoday + 30;
break;
case FLT_RANGE_TODATE_YEAR:
g_date_set_dmy(tmpdate, PREFS->fisc_year_day, PREFS->fisc_year_month, yfiscal);
flt->mindate = g_date_get_julian(tmpdate);
flt->maxdate = jtoday;
break;
case FLT_RANGE_TODATE_MONTH:
g_date_set_dmy(tmpdate, 1, month, year);
flt->mindate = g_date_get_julian(tmpdate);
flt->maxdate = jtoday;
break;
case FLT_RANGE_TODATE_ALL:
filter_set_date_bounds(flt, kacc);
flt->maxdate = jtoday;
break;
}
g_date_free(tmpdate);
DB( hb_print_date(flt->mindate , " min ") );
DB( hb_print_date(flt->maxdate , " max ") );
}
void filter_preset_type_set(Filter *flt, gint type, gint mode)
{
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] preset type set\n") );
flt->option[FLT_GRP_TYPE] = FLT_OFF;
flt->type = type;
flt->typ_nexp = FALSE;
flt->typ_ninc = FALSE;
flt->typ_xexp = FALSE;
flt->typ_xinc = FALSE;
if( type != FLT_TYPE_ALL )
{
//FLT_INCLUDE / FLT_EXCLUDE
flt->option[FLT_GRP_TYPE] = mode;
switch(type)
{
case FLT_TYPE_EXPENSE:
flt->typ_nexp = TRUE;
break;
case FLT_TYPE_INCOME:
flt->typ_ninc = TRUE;
break;
case FLT_TYPE_INTXFER:
flt->typ_xexp = TRUE;
flt->typ_xinc = TRUE;
break;
case FLT_TYPE_NOTXFER:
flt->typ_xexp = TRUE;
flt->typ_xinc = TRUE;
flt->option[FLT_GRP_TYPE] = FLT_EXCLUDE;
break;
}
}
}
/* = = = = = = = = = = = = = = = = */
void filter_preset_status_set(Filter *flt, gint status)
{
g_return_if_fail( flt != NULL );
DB( g_print("\n[filter] preset status set\n") );
/* any status */
//#1991459 dont reset type
//flt->option[FLT_GRP_TYPE] = 0;
flt->option[FLT_GRP_STATUS] = 0;
flt->option[FLT_GRP_CATEGORY] = 0;
//#1991459 dont reset type
//flt->type = FLT_TYPE_ALL;
flt->status = status;
flt->sta_non = FALSE;
flt->sta_clr = FALSE;
flt->sta_rec = FALSE;
//#1602835 fautly set
//flt->forceadd = TRUE;
//flt->forcechg = TRUE;
//#1860356 keep widget active_id
//#1873324 ledger status quick filter do not reset
//note: status revert to UNRECONCILED here is normal if PREFS->hidereconciled=TRUE
//flt->rawstatus = status;
if( status != FLT_STATUS_ALL )
{
switch( status )
{
case FLT_STATUS_UNCATEGORIZED:
//#1991459 dont reset type: here to hide xfer txn
//flt->option[FLT_GRP_TYPE] = 2;
//flt->type = FLT_TYPE_INTXFER;
flt->option[FLT_GRP_CATEGORY] = 1;
filter_status_cat_clear_except(flt, 0);
DB( my_debug_garray("cat", flt->gbcat) );
break;
case FLT_STATUS_UNRECONCILED:
flt->option[FLT_GRP_STATUS] = 1;
flt->sta_non = TRUE;
flt->sta_clr = TRUE;
break;
case FLT_STATUS_UNCLEARED:
flt->option[FLT_GRP_STATUS] = 1;
flt->sta_non = TRUE;
break;
case FLT_STATUS_RECONCILED:
flt->option[FLT_GRP_STATUS] = 1;
flt->sta_rec = TRUE;
break;
case FLT_STATUS_CLEARED:
flt->option[FLT_GRP_STATUS] = 1;
flt->sta_clr = TRUE;
break;
}
}
}
gchar *filter_daterange_text_get(Filter *flt)
{
gchar *retval = NULL;
g_return_val_if_fail( flt != NULL, NULL );
DB( g_print("\n[filter] daterange text get\n") );
if( flt->mindate <= flt->maxdate )
{
gchar buffer1[128];
gchar buffer2[128];
gchar buffer3[128];
GDate *date;
date = g_date_new_julian(flt->mindate);
g_date_strftime (buffer1, 128-1, PREFS->date_format, date);
g_date_set_julian(date, flt->maxdate);
g_date_strftime (buffer2, 128-1, PREFS->date_format, date);
if( flt->nbdaysfuture > 0 )
{
g_date_set_julian(date, flt->maxdate + flt->nbdaysfuture);
g_date_strftime (buffer3, 128-1, PREFS->date_format, date);
retval = g_strdup_printf("%s — %s %s", buffer1, buffer2, buffer3);
}
else
retval = g_strdup_printf("%s — %s", buffer1, buffer2);
g_date_free(date);
}
else
retval = g_strdup(_("Invalid date range!"));
//return g_strdup_printf(_("from %s to %s — "), buffer1, buffer2);
return retval;
}
gchar *filter_text_summary_get(Filter *flt)
{
GString *node;
node = g_string_sized_new(128);
if( flt->option[FLT_GRP_TYPE] )
{
g_string_append_printf(node, "%c%s: ",
flt->option[FLT_GRP_TYPE] == FLT_INCLUDE ? '+' : '-',
_("Type"));
if(flt->typ_nexp)
///TRANSLATORS: n-exp > normal espense
{ g_string_append(node, _("n-exp")); g_string_append(node, " "); }
if(flt->typ_ninc)
///TRANSLATORS: n-inc > normal income
{ g_string_append(node, _("n-inc")); g_string_append(node, " "); }
if(flt->typ_xexp)
///TRANSLATORS: x-exp > transfer espense
{ g_string_append(node, _("x-exp")); g_string_append(node, " "); }
if(flt->typ_xinc)
///TRANSLATORS: x-inc > transfer income
{ g_string_append(node, _("x-inc")); g_string_append(node, " "); }
g_string_append(node, "\n");
}
if( flt->option[FLT_GRP_STATUS] )
{
g_string_append_printf(node, "%c%s: ",
flt->option[FLT_GRP_STATUS] == FLT_INCLUDE ? '+' : '-',
_("Status"));
if(flt->sta_non)
{ g_string_append(node, _("none")); g_string_append(node, " "); }
if(flt->sta_clr)
{ g_string_append(node, _("cleared")); g_string_append(node, " "); }
if(flt->sta_rec)
{ g_string_append(node, _("reconciled")); g_string_append(node, " "); }
g_string_append(node, "\n");
}
if( flt->option[FLT_GRP_ACCOUNT] )
{
DB( my_debug_garray("acc", flt->gbacc) );
g_string_append_printf(node, "%c%s: %d\n",
flt->option[FLT_GRP_ACCOUNT] == FLT_INCLUDE ? '+' : '-',
_("Account"), flt->n_item[FLT_GRP_ACCOUNT]);
}
if( flt->option[FLT_GRP_PAYEE] )
{
DB( my_debug_garray("pay", flt->gbpay) );
g_string_append_printf(node, "%c%s: %d\n",
flt->option[FLT_GRP_PAYEE] == FLT_INCLUDE ? '+' : '-',
_("Payee"), flt->n_item[FLT_GRP_PAYEE]);
}
if( flt->option[FLT_GRP_CATEGORY] )
{
g_string_append_printf(node, "%c%s: %d\n",
flt->option[FLT_GRP_CATEGORY] == FLT_INCLUDE ? '+' : '-',
_("Category"), flt->n_item[FLT_GRP_CATEGORY]);
}
if( flt->option[FLT_GRP_TAG] )
{
g_string_append_printf(node, "%c%s: %d\n",
flt->option[FLT_GRP_TAG] == FLT_INCLUDE ? '+' : '-',
_("Tag"), flt->n_item[FLT_GRP_TAG]);
}
if( flt->option[FLT_GRP_PAYMODE] )
{
g_string_append_printf(node, "%c%s: %d\n",
flt->option[FLT_GRP_PAYMODE] == FLT_INCLUDE ? '+' : '-',
_("Payment"), flt->n_item[FLT_GRP_PAYMODE]);
}
if( flt->option[FLT_GRP_AMOUNT] )
{
g_string_append_printf(node, "%c%s: [%.2f | +%.2f]\n",
flt->option[FLT_GRP_AMOUNT] == FLT_INCLUDE ? '+' : '-',
_("Amount"), flt->minamount, flt->maxamount);
}
if( flt->option[FLT_GRP_TEXT] )
{
g_string_append_printf(node, "%c%s: '%s', '%s'\n",
flt->option[FLT_GRP_TEXT] == FLT_INCLUDE ? '+' : '-',
_("Text"), flt->memo, flt->number);
}
//remove last \n
if( node->len > 5 )
g_string_erase(node, node->len-1, 1);
return g_string_free(node, FALSE);
}
/* = = = = = = = = = = = = = = = = */
/* used for quicksearch text into transaction */
gboolean filter_tpl_search_match(gchar *needle, Archive *arc)
{
gboolean retval = FALSE;
Payee *payitem;
DB( g_print("\n[filter] tpl search match\n") );
//#1668036 always try match on txn memo first
if(arc->memo)
{
retval |= hb_string_utf8_strstr(arc->memo, needle, FALSE);
}
if(retval) goto end;
//#1509485
if(arc->flags & OF_SPLIT)
{
guint count, i;
Split *split;
count = da_splits_length(arc->splits);
for(i=0;isplits, i);
tmpinsert = hb_string_utf8_strstr(split->memo, needle, FALSE);
retval |= tmpinsert;
if( tmpinsert )
break;
}
}
if(retval) goto end;
if(arc->number)
{
retval |= hb_string_utf8_strstr(arc->number, needle, FALSE);
}
if(retval) goto end;
payitem = da_pay_get(arc->kpay);
if(payitem)
{
retval |= hb_string_utf8_strstr(payitem->name, needle, FALSE);
}
if(retval) goto end;
//#1741339 add quicksearch for amount
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
//2127104 was hb_strfnum
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, arc->amount, GLOBALS->kcur, FALSE);
retval |= hb_string_utf8_strstr(formatd_buf, needle, FALSE);
}
end:
return retval;
}
static gboolean filter_txn_tag_match(Filter *flt, guint32 *tags)
{
guint count, i;
gboolean retval = FALSE;
g_return_val_if_fail( flt != NULL, FALSE );
if(flt->gbtag == NULL)
return FALSE;
count = tags_count(tags);
//if no tag in txn just check (no tag) is set
if( count == 0 )
{
retval = da_flt_status_tag_get(flt, 0);
goto end;
}
DB( g_print("\n[filter] tnx tag match\n") );
//debug loop */
#if MYDEBUG == 1
g_print(" dbg gbtag %d elt\n", flt->gbtag->len);
for(i=0;igbtag->len;i++)
{
g_print("[%d]=%d ", i, flt->gbtag->data[i]);
}
g_print("\n\n");
#endif
/* loop on any tags */
DB( g_print(" loop txn tags %d\n", count) );
for(i=0;imemo)
{
retval |= hb_string_utf8_strstr(txn->memo, needle, FALSE);
}
if(retval) goto end;
//#1509485
if(txn->flags & OF_SPLIT)
{
guint count, i;
Split *split;
count = da_splits_length(txn->splits);
for(i=0;isplits, i);
tmpinsert = hb_string_utf8_strstr(split->memo, needle, FALSE);
retval |= tmpinsert;
if( tmpinsert )
break;
}
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_NUMBER)
{
if(txn->number)
{
retval |= hb_string_utf8_strstr(txn->number, needle, FALSE);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_PAYEE)
{
payitem = da_pay_get(txn->kpay);
if(payitem)
{
retval |= hb_string_utf8_strstr(payitem->name, needle, FALSE);
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_CATEGORY)
{
//#1509485
if(txn->flags & OF_SPLIT)
{
guint count, i;
Split *split;
count = da_splits_length(txn->splits);
for(i=0;isplits, i);
catitem = da_cat_get(split->kcat);
if(catitem)
{
tmpinsert = hb_string_utf8_strstr(catitem->fullname, needle, FALSE);
retval |= tmpinsert;
}
if( tmpinsert )
break;
}
}
else
{
catitem = da_cat_get(txn->kcat);
if(catitem)
{
retval |= hb_string_utf8_strstr(catitem->fullname, needle, FALSE);
}
}
if(retval) goto end;
}
if(flags & FLT_QSEARCH_TAGS)
{
//TODO: chnage this
tags = tags_tostring(txn->tags);
if(tags)
{
retval |= hb_string_utf8_strstr(tags, needle, FALSE);
}
g_free(tags);
if(retval) goto end;
}
//#1741339 add quicksearch for amount
if(flags & FLT_QSEARCH_AMOUNT)
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
//2127104 was hb_strfnum
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, txn->amount, txn->kcur, FALSE);
DB( g_print(" needle='%s' txnamt='%s'\n", needle, formatd_buf) );
retval |= hb_string_utf8_strstr(formatd_buf, needle, FALSE);
}
end:
return retval;
}
gint filter_acc_match(Filter *flt, Account *acc)
{
gboolean status;
gint insert = 1;
g_return_val_if_fail( flt != NULL, 1 );
/* account */
if(flt->option[FLT_GRP_ACCOUNT])
{
status = da_flt_status_acc_get(flt, acc->key);
insert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_ACCOUNT] == 2) insert ^= 1;
}
return(insert);
}
gint filter_txn_match(Filter *flt, Transaction *txn)
{
gboolean status;
gint insert;
//DB( g_print("\n[filter] txn match\n") );
insert = 1;
/*** start filtering ***/
/* date */
if(flt->option[FLT_GRP_DATE])
{
insert = ( (txn->date >= flt->mindate) && (txn->date <= (flt->maxdate + flt->nbdaysfuture) ) ) ? 1 : 0;
if(flt->option[FLT_GRP_DATE] == 2) insert ^= 1;
}
if(!insert) goto end;
/* account */
if(flt->option[FLT_GRP_ACCOUNT])
{
status = da_flt_status_acc_get(flt, txn->kacc);
insert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_ACCOUNT] == 2) insert ^= 1;
}
if(!insert) goto end;
/* payee */
if(flt->option[FLT_GRP_PAYEE])
{
status = da_flt_status_pay_get(flt, txn->kpay);
insert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_PAYEE] == 2) insert ^= 1;
}
if(!insert) goto end;
/* category */
if(flt->option[FLT_GRP_CATEGORY])
{
if(txn->flags & OF_SPLIT)
{
guint count, i;
Split *split;
insert = 0; //fix: 1151259
count = da_splits_length(txn->splits);
for(i=0;isplits, i);
status = da_flt_status_cat_get(flt, split->kcat);
tmpinsert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_CATEGORY] == 2) tmpinsert ^= 1;
insert |= tmpinsert;
}
}
else
{
status = da_flt_status_cat_get(flt, txn->kcat);
insert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_CATEGORY] == 2) insert ^= 1;
}
}
if(!insert) goto end;
/* tag */
if(flt->option[FLT_GRP_TAG])
{
status = filter_txn_tag_match(flt, txn->tags);
insert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_TAG] == 2) insert ^= 1;
}
if(!insert) goto end;
/* type */
if(flt->option[FLT_GRP_TYPE])
{
gint ntyp1, ntyp2, xtyp1, xtyp2;
ntyp1 = ( (flt->typ_nexp == TRUE) && !(txn->flags & (OF_INCOME)) ) ? 1 : 0;
ntyp2 = ( (flt->typ_ninc == TRUE) && (txn->flags & (OF_INCOME)) ) ? 1 : 0;
if( (txn->flags & (OF_INTXFER)) )
{
xtyp1 = ( (flt->typ_xexp == TRUE) && !(txn->flags & (OF_INCOME)) ) ? 1 : 0;
xtyp2 = ( (flt->typ_xinc == TRUE) && (txn->flags & (OF_INCOME)) ) ? 1 : 0;
}
else
{
xtyp1 = xtyp2 = 0;
}
insert = ntyp1 || ntyp2 || xtyp1 || xtyp2;
if(flt->option[FLT_GRP_TYPE] == 2) insert ^= 1;
}
if(!insert) goto end;
/* status */
if(flt->option[FLT_GRP_STATUS])
{
gint sta1 = ( (flt->sta_non == TRUE) && (txn->status == TXN_STATUS_NONE) ) ? 1 : 0;
gint sta2 = ( (flt->sta_clr == TRUE) && (txn->status == TXN_STATUS_CLEARED) ) ? 1 : 0;
gint sta3 = ( (flt->sta_rec == TRUE) && (txn->status == TXN_STATUS_RECONCILED) ) ? 1 : 0;
insert = sta1 || sta2 || sta3;
if(flt->option[FLT_GRP_STATUS] == 2) insert ^= 1;
}
if(!insert) goto end;
/* paymode */
//#2059709 ignore paymode for xfer
if( !(txn->flags & (OF_INTXFER)) )
{
if(flt->option[FLT_GRP_PAYMODE])
{
insert = ( flt->paymode[txn->paymode] == TRUE) ? 1 : 0;
if(flt->option[FLT_GRP_PAYMODE] == 2) insert ^= 1;
}
if(!insert) goto end;
}
/* amount */
if(flt->option[FLT_GRP_AMOUNT])
{
insert = ( (txn->amount >= flt->minamount) && (txn->amount <= flt->maxamount) ) ? 1 : 0;
if(flt->option[FLT_GRP_AMOUNT] == 2) insert ^= 1;
}
if(!insert) goto end;
/* info/memo */
if(flt->option[FLT_GRP_TEXT])
{
gint insert1, insert2;
insert1 = insert2 = 0;
//2068664 test for empty string as well
if( flt->number && (*flt->number != 0) )
{
if(txn->number)
{
insert1 = hb_string_utf8_strstr(txn->number, flt->number, flt->exact);
}
}
else
insert1 = 1;
//2068664 test for empty string as well
if( flt->memo && (*flt->memo != 0) )
{
//#1668036 always try match on txn memo first
if(txn->memo)
{
insert2 = hb_string_utf8_strstr(txn->memo, flt->memo, flt->exact);
}
if( (insert2 == 0) && (txn->flags & OF_SPLIT) )
{
guint count, i;
Split *split;
count = da_splits_length(txn->splits);
for(i=0;isplits, i);
tmpinsert = hb_string_utf8_strstr(split->memo, flt->memo, flt->exact);
insert2 |= tmpinsert;
if( tmpinsert )
break;
}
}
}
else
insert2 = 1;
insert = insert1 && insert2 ? 1 : 0;
if(flt->option[FLT_GRP_TEXT] == 2) insert ^= 1;
}
//if(!insert) goto end;
end:
/* force display */
if(!insert)
{
if( ((flt->forceadd == TRUE) && (txn->dspflags & FLAG_TMP_ADDED))
|| ((flt->forcechg == TRUE) && (txn->dspflags & FLAG_TMP_EDITED))
)
insert = 1;
}
//5.9 always show pending
if( txn->flags & (OF_ISIMPORT|OF_ISPAST) )
{
insert = TRUE;
}
//#1999186 pref void/remind to false do not work
//if( txn->status == TXN_STATUS_REMIND )
if( txn->flags & OF_REMIND )
{
insert = flt->forceremind;
}
if( txn->status == TXN_STATUS_VOID )
{
insert = flt->forcevoid;
}
// DB( g_print(" %d :: %d :: %d\n", flt->mindate, txn->date, flt->maxdate) );
// DB( g_print(" [%d] %s => %d (%d)\n", txn->account, txn->memo, insert, count) );
return(insert);
}
homebank-5.9.7/src/ui-account.c 0000644 0001750 0001750 00000173550 15120542371 015627 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-account.h"
#include "ui-account.h"
#include "ui-currency.h"
#include "ui-dialogs.h"
#include "ui-group.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern HbKvData CYA_ACC_TYPE[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static GtkWidget *
container_get_nth(GtkBox *container, gint nth)
{
GList *lchild, *list;
GtkWidget *child;
if(!GTK_IS_CONTAINER(container))
return NULL;
lchild = list = gtk_container_get_children (GTK_CONTAINER(container));
child = g_list_nth_data (list, nth);
g_list_free(lchild);
return child;
}
GtkTreeModel *
ui_acc_entry_popover_get_model(GtkBox *box)
{
GtkWidget *entry = container_get_nth(box, 0);
if( GTK_IS_ENTRY(entry) )
{
return gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
}
return NULL;
}
GtkWidget *
ui_acc_entry_popover_get_entry(GtkBox *box)
{
return container_get_nth(box, 0);
}
Account
*ui_acc_entry_popover_get(GtkBox *box)
{
GtkWidget *entry;
gchar *name;
Account *item = NULL;
DB( g_print ("ui_acc_entry_popover_get()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
name = (gchar *)gtk_entry_get_text(GTK_ENTRY (entry));
item = da_acc_get_by_name(name);
}
return item;
}
guint32
ui_acc_entry_popover_get_key(GtkBox *box)
{
Account *item = ui_acc_entry_popover_get(box);
return ((item != NULL) ? item->key : 0);
}
//#1859077 preset account if single
void
ui_acc_entry_popover_set_single(GtkBox *box)
{
GtkWidget *entry;
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print ("ui_acc_popover_set_single()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
model = gtk_entry_completion_get_model(gtk_entry_get_completion(GTK_ENTRY(entry)));
if(model != NULL && gtk_tree_model_iter_n_children(model, NULL) == 1)
{
if( gtk_tree_model_get_iter_first(model, &iter) == TRUE )
{
gchar *item;
gtk_tree_model_get(model, &iter, 0, &item, -1);
hbtk_entry_set_text(GTK_ENTRY(entry), item != NULL ? item : "");
g_free(item);
}
}
}
}
void
ui_acc_entry_popover_set_active(GtkBox *box, guint32 key)
{
GtkWidget *entry;
DB( g_print ("ui_acc_popover_set_active()\n") );
entry = container_get_nth(box, 0);
if( entry != NULL && GTK_IS_ENTRY(entry) )
{
Account *item = da_acc_get(key);
gchar *txt = "";
//#1972078 forbid set a closed account
if( (item != NULL) && !(item->flags & AF_CLOSED) )
{
txt = item->name;
}
hbtk_entry_set_text(GTK_ENTRY(entry), txt);
}
}
static gboolean ui_acc_manage_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct ui_acc_manage_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else
if(keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_acc);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void
ui_acc_entry_popover_cb_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkEntry *entry = user_data;
if( GTK_IS_ENTRY(entry) )
{
treeselection = gtk_tree_view_get_selection(tree_view);
if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
{
gchar *item;
gtk_tree_model_get(model, &iter, 0, &item, -1);
gtk_entry_set_text(GTK_ENTRY(user_data), item);
g_free(item);
}
}
}
static void
ui_acc_entry_popover_populate_ghfunc(gpointer key, gpointer value, struct accPopContext *ctx)
{
GtkTreeIter iter;
Account *acc = value;
if( (acc->flags & AF_CLOSED) ) return;
if( (ctx->insert_type == ACC_LST_INSERT_REPORT) && (acc->flags & AF_NOREPORT) ) return;
if( (acc->key == ctx->except_key) ) return;
//#1673260 this was for xfer with same currency only
//if( (ctx->kcur > 0 ) && (acc->kcur != ctx->kcur) ) return;
DB( g_print (" -> append model '%s'\n", acc->name) );
gtk_list_store_append (GTK_LIST_STORE(ctx->model), &iter);
gtk_list_store_set (GTK_LIST_STORE(ctx->model), &iter,
0, acc->name,
1, acc->pos,
-1);
}
void
ui_acc_entry_popover_populate(GtkBox *box, GHashTable *hash, gint insert_type)
{
ui_acc_entry_popover_populate_except(box, hash, 0, insert_type);
}
void
ui_acc_entry_popover_populate_except(GtkBox *box, GHashTable *hash, guint except_key, gint insert_type)
{
GtkTreeModel *model;
struct accPopContext ctx;
DB( g_print ("ui_acc_entry_popover_populate\n") );
DB( g_print (" -> except is %d\n", except_key) );
model = ui_acc_entry_popover_get_model(GTK_BOX(box));
/* keep our model alive and detach from popover and completion */
g_object_ref(model);
/* clear and populate */
ctx.model = model;
ctx.except_key = except_key;
ctx.insert_type = insert_type;
//#1673260 xfer same currency
//ctx.kcur = 0;
//Account *acc = da_acc_get(except_key);
//if(acc != NULL)
//ctx.kcur = acc->kcur;
gtk_list_store_clear (GTK_LIST_STORE(model));
//5.3 empty the entry
gtk_entry_set_text(GTK_ENTRY (ui_acc_entry_popover_get_entry(GTK_BOX (box))), "");
g_hash_table_foreach(hash, (GHFunc)ui_acc_entry_popover_populate_ghfunc, &ctx);
/* reatach our model */
g_object_unref(model);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
}
static void
ui_acc_entry_popover_function (GtkEditable *editable, gpointer user_data)
{
DB( g_print("text changed to %s\n", gtk_entry_get_text(GTK_ENTRY(editable)) ) );
}
static void
ui_acc_entry_popover_cb_toggled (GtkToggleButton *togglebutton, gpointer user_data)
{
GtkWidget *entry = user_data;
GtkAllocation allocation;
GtkPopover *popover;
DB( g_print ("[acc entry popover] open\n") );
if(GTK_IS_ENTRY(entry))
{
gtk_widget_get_allocation (entry, &allocation);
popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(togglebutton));
if(GTK_IS_POPOVER(popover))
{
gtk_widget_set_size_request (GTK_WIDGET(popover), allocation.width + (2*SPACING_POPOVER), -1);
DB( g_print("should set width to %d\n", allocation.width + (2*SPACING_POPOVER)) );
}
}
}
static gint
ui_acc_entry_popover_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint pos1, pos2;
gtk_tree_model_get(model, a, 1, &pos1, -1);
gtk_tree_model_get(model, b, 1, &pos2, -1);
return (pos1 - pos2);
}
static gboolean
ui_acc_entry_popover_completion_func (GtkEntryCompletion *completion,
const gchar *key,
GtkTreeIter *iter,
gpointer user_data)
{
gchar *name = NULL;
gchar *normalized_string;
gchar *case_normalized_string;
gboolean ret = FALSE;
GtkTreeModel *model;
model = gtk_entry_completion_get_model (completion);
gtk_tree_model_get (model, iter,
0, &name,
-1);
if (name != NULL)
{
normalized_string = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
if (normalized_string != NULL)
{
case_normalized_string = g_utf8_casefold (normalized_string, -1);
//g_print("match '%s' for '%s' ?\n", key, case_normalized_string);
//if (!strncmp (key, case_normalized_string, strlen (key)))
if (g_strstr_len (case_normalized_string, strlen (case_normalized_string), key ))
{
ret = TRUE;
// g_print(" ==> yes !\n");
}
g_free (case_normalized_string);
}
g_free (normalized_string);
}
return ret;
}
static void
ui_acc_entry_popover_destroy( GtkWidget *widget, gpointer user_data )
{
DB( g_print ("[acc entry popover] destroy\n") );
}
GtkWidget *
ui_acc_entry_popover_new(GtkWidget *label)
{
GtkWidget *mainbox, *box, *entry, *menubutton, *image, *popover, *scrollwin, *treeview;
GtkListStore *store;
GtkEntryCompletion *completion;
DB( g_print ("[acc entry popover] new\n") );
mainbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(mainbox)), GTK_STYLE_CLASS_LINKED);
entry = gtk_entry_new();
hbtk_box_prepend (GTK_BOX(mainbox), entry);
menubutton = gtk_menu_button_new ();
//data->MB_template = menubutton;
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_button_set_image(GTK_BUTTON(menubutton), image);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_LEFT );
//gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
gtk_box_prepend(GTK_BOX(mainbox), menubutton);
completion = gtk_entry_completion_new ();
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_object_unref(completion);
store = gtk_list_store_new (2,
G_TYPE_STRING,
G_TYPE_INT
);
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), ui_acc_entry_popover_compare_func, NULL, NULL);
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
gtk_entry_completion_set_match_func(completion, ui_acc_entry_popover_completion_func, NULL, NULL);
g_object_unref(store);
gtk_entry_completion_set_text_column (completion, 0);
gtk_widget_show_all(mainbox);
//popover
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_show_all(box);
//gtk_widget_set_can_focus(GTK_WIDGET(treeview), FALSE);
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new_with_attributes (NULL,
renderer,
"text",
0,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
//gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_BROWSE);
//popover = create_popover (menubutton, box, GTK_POS_BOTTOM);
popover = create_popover (menubutton, box, GTK_POS_LEFT);
//gtk_widget_set_size_request (popover, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
gtk_widget_set_vexpand(popover, TRUE);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
// connect our dispose function
g_signal_connect (entry, "destroy", G_CALLBACK (ui_acc_entry_popover_destroy), NULL);
g_signal_connect_after (entry , "changed", G_CALLBACK (ui_acc_entry_popover_function), NULL);
g_signal_connect (menubutton, "toggled", G_CALLBACK (ui_acc_entry_popover_cb_toggled), entry);
g_signal_connect (treeview, "row-activated", G_CALLBACK (ui_acc_entry_popover_cb_row_activated), entry);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_popover_popdown), popover);
#else
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_widget_hide), popover);
#endif
//g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK (ui_acc_entry_popover_cb_selection), entry);
//g_signal_connect_swapped(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK(gtk_popover_popdown), popover);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), entry);
return mainbox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint ui_acc_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
guint change = 0;
DB( g_print("[ui_acc_listview] toggle_to_filter\n") );
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Account *item;
gboolean toggled;
gtk_tree_model_get (model, &iter,
LST_DEFACC_TOGGLE, &toggled,
LST_DEFACC_DATAS , &item,
-1);
DB( g_print(" acc k:%3d = %d (%s)\n", item->key, toggled, item->name) );
change += da_flt_status_acc_set(filter, item->key, toggled);
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
return change;
}
static void
ui_acc_listview_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeView *treeview = (GtkTreeView *)data;
GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(treeview));
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFACC_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFACC_TOGGLE, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
}
static void ui_acc_listview_sort_force(GtkTreeSortable *sortable, gpointer user_data)
{
gint sort_column_id;
GtkSortType order;
DB( g_print("\n[ui-cacc-listview] sort force\n") );
gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &order);
DB( g_print(" id %d\n order %d\n", sort_column_id, order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), sort_column_id, order);
}
static gint
ui_acc_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
Account *accitem1, *accitem2;
gtk_tree_model_get(model, a, LST_DEFACC_DATAS, &accitem1, -1);
gtk_tree_model_get(model, b, LST_DEFACC_DATAS, &accitem2, -1);
switch (sortcol)
{
case LST_DEFACC_SORT_POS:
retval = accitem1->pos - accitem2->pos;
break;
case LST_DEFACC_SORT_NAME:
retval = hb_string_utf8_compare(accitem1->name, accitem2->name);
break;
}
return retval;
}
static void
ui_acc_listview_icon_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Account *entry;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter, LST_DEFACC_DATAS, &entry, -1);
if( entry->flags & AF_CLOSED )
iconname = ICONNAME_HB_ITEM_CLOSED;
else
{
if( !(entry->flags & AF_NOBUDGET) )
iconname = ICONNAME_HB_ITEM_BUDGET;
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
ui_acc_listview_cell_data_function_pos (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Account *accitem;
gchar buffer[256];
gtk_tree_model_get(model, iter, LST_DEFACC_DATAS, &accitem, -1);
g_snprintf(buffer, 256-1, "%d", accitem->pos);
g_object_set(renderer, "text", buffer, NULL);
}
static void
ui_acc_listview_name_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Account *entry;
gchar *name;
gtk_tree_model_get(model, iter, LST_DEFACC_DATAS, &entry, -1);
if(entry->name == NULL)
name = _("(none)"); // can never occurs !
else
name = entry->name;
g_object_set(renderer, "text", name, NULL);
}
#if MYDEBUG == 1
static void
ui_acc_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Account *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DEFACC_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
/* = = = = = = = = = = = = = = = = */
void
ui_acc_listview_set_active(GtkTreeView *treeview, guint32 key)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Account *accitem;
gtk_tree_model_get (model, &iter,
LST_DEFACC_DATAS, &accitem,
-1);
if( accitem->key == key )
{
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
LST_DEFACC_TOGGLE, TRUE, -1);
break;
}
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
/**
* acc_list_add:
*
* Add a single element (useful for dynamics add)
*
* Return value: --
*
*/
void
ui_acc_listview_add(GtkTreeView *treeview, Account *item)
{
if( item->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(treeview);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFACC_TOGGLE, FALSE,
LST_DEFACC_DATAS, item,
-1);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter);
}
}
guint32
ui_acc_listview_get_selected_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Account *item;
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &item, -1);
if( item!= NULL )
return item->key;
}
return 0;
}
void
ui_acc_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
}
void
ui_acc_listview_quick_select(GtkTreeView *treeview, const gchar *uri)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gboolean toggle;
gint qselect = hb_clicklabel_to_int(uri);
DB( g_print("[ui_acc_listview] quick select\n") );
DB( g_print(" comboboxlink '%s' %d\n", uri, qselect) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
switch(qselect)
{
case HB_LIST_QUICK_SELECT_ALL:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFACC_TOGGLE, TRUE, -1);
break;
case HB_LIST_QUICK_SELECT_NONE:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFACC_TOGGLE, FALSE, -1);
break;
case HB_LIST_QUICK_SELECT_INVERT:
gtk_tree_model_get (model, &iter, LST_DEFACC_TOGGLE, &toggle, -1);
toggle ^= 1;
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFACC_TOGGLE, toggle, -1);
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
void ui_acc_listview_populate(GtkWidget *view, gint insert_type, gchar *needle)
{
GtkTreeModel *model;
GtkTreeIter iter;
GList *lacc, *list;
gboolean hastext = FALSE;
gboolean insert = TRUE;
DB( g_print("[ui_acc_listview] populate\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
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(view), NULL); /* Detach model from view */
if( needle != NULL )
hastext = (strlen(needle) >= 2) ? TRUE : FALSE;
/* populate */
//g_hash_table_foreach(GLOBALS->h_acc, (GHFunc)ui_acc_listview_populate_ghfunc, model);
list = g_hash_table_get_values(GLOBALS->h_acc);
//lacc = list = g_list_sort(list, (GCompareFunc)ui_acc_glist_compare_func);
lacc = list = account_glist_sorted(HB_GLIST_SORT_POS);
while (list != NULL)
{
Account *item = list->data;
if( insert_type == ACC_LST_INSERT_REPORT )
{
//#1674045 ony rely on nosummary
//if( (item->flags & AF_CLOSED) ) goto next1;
if( (item->flags & AF_NOREPORT) ) goto next1;
}
if(hastext)
insert = hb_string_utf8_strstr(item->name, needle, FALSE);
else
insert = TRUE;
if( insert == TRUE )
{
DB( g_print(" populate acc k=%d\n", item->key) );
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFACC_TOGGLE , FALSE,
LST_DEFACC_DATAS, item,
-1);
}
next1:
list = g_list_next(list);
}
g_list_free(lacc);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
}
GtkWidget *
ui_acc_listview_new(gboolean withtoggle)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print("[ui_acc_listview] new\n") );
// create list store
store = gtk_list_store_new(NUM_LST_DEFACC,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_acc_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column 1: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer,
"active", LST_DEFACC_TOGGLE,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE);
g_signal_connect (renderer, "toggled",
G_CALLBACK (ui_acc_listview_toggled_cb), treeview);
g_object_set_data(G_OBJECT(treeview), "togrdr_data", renderer);
}
//column: icons
if( withtoggle == FALSE )
{
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_acc_listview_icon_cell_data_function, GINT_TO_POINTER(LST_DEFACC_DATAS), NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
//2028464 sort by pos/name
// column: position
if( withtoggle == FALSE )
{
renderer = gtk_cell_renderer_text_new ();
//#2004631 date and column title alignement
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, "#");
gtk_tree_view_column_set_sort_column_id (column, LST_DEFACC_SORT_POS);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_acc_listview_cell_data_function_pos, GINT_TO_POINTER(LST_DEFACC_DATAS), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
// column: name
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Account"));
gtk_tree_view_column_set_sort_column_id (column, LST_DEFACC_SORT_NAME);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);
if( withtoggle == FALSE )
{
g_object_set(renderer,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
}
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_acc_listview_name_cell_data_function, GINT_TO_POINTER(LST_DEFACC_DATAS), NULL);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeviewattribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), !withtoggle);
//5.7 no more dragndrop
//gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
//sortable
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFACC_SORT_POS, ui_acc_listview_compare_func, GINT_TO_POINTER(LST_DEFACC_SORT_POS), NULL);
if( withtoggle == FALSE )
{
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFACC_SORT_NAME, ui_acc_listview_compare_func, GINT_TO_POINTER(LST_DEFACC_SORT_NAME), NULL);
}
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFACC_SORT_POS, GTK_SORT_ASCENDING);
//gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview), ui_acc_listview_search_equal_func, NULL, NULL);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_acc_manage_dialog_refilter(struct ui_acc_manage_data *data)
{
gchar *needle;
DB( g_print("[ui-acc] refilter\n") );
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
ui_acc_listview_populate(data->LV_acc, ACC_LST_INSERT_NORMAL, needle);
}
static void ui_acc_manage_getlast(struct ui_acc_manage_data *data)
{
gboolean active;
gdouble value;
Account *item;
DB( g_print("\n[ui-acc] getlast\n") );
DB( g_print(" -> for account id=%d\n", data->lastkey) );
item = da_acc_get(data->lastkey);
if(item != NULL)
{
gchar *olddigest, *newdigest;
guint length;
//data->change++;
length = offsetof(Account, rdate);
olddigest = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *)item, length);
item->type = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
account_set_currency(item, ui_cur_combobox_get_key(GTK_COMBO_BOX(data->CY_curr)) );
hbtk_entry_replace_text(GTK_ENTRY(data->ST_institution), &item->bankname);
hbtk_entry_replace_text(GTK_ENTRY(data->ST_number), &item->number);
item->kgrp = ui_grp_comboboxentry_get_key_add_new(GTK_COMBO_BOX(data->ST_group));
hbtk_entry_replace_text(GTK_ENTRY(data->ST_website), &item->website);
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data->TB_notes));
GtkTextIter siter, eiter;
gtk_text_buffer_get_iter_at_offset (buffer, &siter, 0);
gtk_text_buffer_get_end_iter(buffer, &eiter);
gchar *newnotes = gtk_text_buffer_get_text(buffer, &siter, &eiter, FALSE);
gint tmpcmp = hb_string_ascii_compare(item->notes, newnotes);
if( tmpcmp != 0 )
{
g_free(item->notes);
item->notes = g_strdup(newnotes);
}
item->flags &= ~(AF_CLOSED);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_closed));
if(active) item->flags |= AF_CLOSED;
item->flags &= ~(AF_NOBUDGET);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_nobudget));
if(active) item->flags |= AF_NOBUDGET;
item->flags &= ~(AF_NOSUMMARY);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_nosummary));
if(active) item->flags |= AF_NOSUMMARY;
item->flags &= ~(AF_NOREPORT);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_noreport));
if(active) item->flags |= AF_NOREPORT;
//#1896441 outflow summary
item->flags &= ~(AF_OUTFLOWSUM);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_outflowsum));
if(active) item->flags |= AF_OUTFLOWSUM;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_initial));
value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_initial));
item->initial = value;
//gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_warning));
//value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_warning));
//item->warning = value;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_minimum));
value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_minimum));
item->minimum = value;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_maximum));
value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->ST_maximum));
item->maximum = value;
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_cheque1));
item->cheque1 = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->ST_cheque1));
gtk_spin_button_update(GTK_SPIN_BUTTON(data->ST_cheque2));
item->cheque2 = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->ST_cheque2));
item->karc= hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_template));
//active_id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_template));
//item->karc = atoi(active_id);
newdigest = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *)item, length);
DB( g_print(" checksum: '%s'\n", olddigest) );
DB( g_print(" checksum: '%s'\n", newdigest) );
if (strcmp(olddigest, newdigest) )
{
data->change++;
DB( g_print(" > checksum differs\n") );
}
g_free (olddigest);
g_free (newdigest);
}
}
//#1743254 set frac digits as well
static void ui_acc_manage_changed_curr_cb(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
guint32 key;
Currency *cur;
DB( g_print("\n[ui-acc] changed_curr_cb\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
key = ui_cur_combobox_get_key(GTK_COMBO_BOX(data->CY_curr));
cur = da_cur_get (key);
if( cur != NULL )
{
DB( g_print("- set digits to '%s' %d\n", cur->name, cur->frac_digits) );
gtk_spin_button_set_digits (GTK_SPIN_BUTTON(data->ST_initial), cur->frac_digits);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON(data->ST_minimum), cur->frac_digits);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON(data->ST_maximum), cur->frac_digits);
}
}
/*
** set widgets contents from the selected account
*/
static void ui_acc_manage_set(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
Account *item;
//gchar idbuffer[12];
DB( g_print("\n[ui-acc] set\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &item, -1);
DB( g_print(" -> set acc id=%d\n", item->key) );
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_type), item->type );
ui_cur_combobox_set_active(GTK_COMBO_BOX(data->CY_curr), item->kcur);
if(item->bankname != NULL)
gtk_entry_set_text(GTK_ENTRY(data->ST_institution), item->bankname);
else
gtk_entry_set_text(GTK_ENTRY(data->ST_institution), "");
if(item->number != NULL)
gtk_entry_set_text(GTK_ENTRY(data->ST_number), item->number);
else
gtk_entry_set_text(GTK_ENTRY(data->ST_number), "");
ui_grp_comboboxentry_set_active(GTK_COMBO_BOX(data->ST_group), item->kgrp);
if(item->website != NULL)
gtk_entry_set_text(GTK_ENTRY(data->ST_website), item->website);
else
gtk_entry_set_text(GTK_ENTRY(data->ST_website), "");
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data->TB_notes));
GtkTextIter iter;
gtk_text_buffer_set_text (buffer, "", 0);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
if(item->notes != NULL)
gtk_text_buffer_insert (buffer, &iter, item->notes, -1);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_nobudget), item->flags & AF_NOBUDGET);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_nosummary), item->flags & AF_NOSUMMARY);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_noreport), item->flags & AF_NOREPORT);
//#1896441 outflow summary
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_outflowsum), item->flags & AF_OUTFLOWSUM);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_closed), item->flags & AF_CLOSED);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_initial), item->initial);
//gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_warning), item->warning);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_minimum), item->minimum);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_maximum), item->maximum);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_cheque1), item->cheque1);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ST_cheque2), item->cheque2);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_template), item->karc);
//g_snprintf(idbuffer, 11, "%d", item->karc);
//gtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_template), idbuffer);
}
}
/*
static gboolean ui_acc_manage_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
{
ui_acc_manage_get(widget, user_data);
return FALSE;
}
*/
/*
** update the widgets status and contents from action/selection value
*/
static void ui_acc_manage_update(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean selected, sensitive, canup, candw;
guint32 key;
DB( g_print("\n[ui-acc] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//window = gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW);
//DB( g_print("(defpayee) widget=%08lx, window=%08lx, inst_data=%08lx\n", treeview, window, data) );
key = ui_acc_listview_get_selected_key(GTK_TREE_VIEW(data->LV_acc));
//if true there is a selected node
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), &model, &iter);
DB( g_print(" -> selected = %d action = %d key = %d\n", selected, data->action, key) );
sensitive = (selected == TRUE) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->notebook, sensitive);
sensitive = (selected == TRUE && data->action == 0) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_rem, sensitive);
canup = candw = selected;
if( selected == TRUE )
{
GtkTreeIter *tmpIter;
gint sort_column_id;
GtkSortType sort_order;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
DB( g_print(" model is %p %d\n", model, GTK_IS_LIST_STORE(model)) );
sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
sort_order = GTK_SORT_DESCENDING;
gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model), &sort_column_id, &sort_order);
DB( g_print(" sort is colid=%d order=%d (ok is %d %d)\n", sort_column_id, sort_order, LST_DEFACC_SORT_POS, GTK_SORT_ASCENDING) );
if( !((sort_column_id == LST_DEFACC_SORT_POS) && (sort_order == GTK_SORT_ASCENDING)) )
{
canup = candw = FALSE;
DB( g_print(" sort is not by position ASC\n") );
goto next;
}
tmpIter = gtk_tree_iter_copy(&iter);
canup = gtk_tree_model_iter_previous(model, tmpIter);
gtk_tree_iter_free(tmpIter);
tmpIter = gtk_tree_iter_copy(&iter);
candw = gtk_tree_model_iter_next(model, tmpIter);
gtk_tree_iter_free(tmpIter);
}
next:
DB( g_print(" can up=%d dw=%d\n", canup, candw) );
gtk_widget_set_sensitive(data->BT_up , canup);
gtk_widget_set_sensitive(data->BT_down, candw);
if(selected)
{
if(key != data->lastkey)
{
DB( g_print(" -> should first do a get for account %d\n", data->lastkey) );
ui_acc_manage_getlast(data);
}
ui_acc_manage_set(widget, NULL);
}
data->lastkey = key;
}
static void ui_acc_manage_cb_move_updown(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
GtkDirectionType direction = GPOINTER_TO_INT(user_data);
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean hasprvnxt;
Account *curitem, *prvnxtitem;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] up/down (data=%p)\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &curitem, -1);
hasprvnxt = FALSE;
if( direction == GTK_DIR_UP )
hasprvnxt = gtk_tree_model_iter_previous(model, &iter);
else if( direction == GTK_DIR_DOWN )
hasprvnxt = gtk_tree_model_iter_next(model, &iter);
if( hasprvnxt == TRUE )
{
gushort tmp = curitem->pos;
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &prvnxtitem, -1);
//swap position
curitem->pos = prvnxtitem->pos;
prvnxtitem->pos = tmp;
//#1999243 add change
data->change++;
ui_acc_listview_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
}
}
/*
** add an empty new account to our temp GList and treeview
*/
static void ui_acc_manage_add(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
Account *item;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] add data=%p\n", data) );
gchar *name = dialog_get_name(_("Account name"), NULL, GTK_WINDOW(data->dialog));
if(name != NULL)
{
if(account_exists(name))
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot add an account '%s',\n"
"this name already exists."),
name
);
}
else
{
item = da_acc_malloc();
item->name = name; //g_strdup_printf( _("(account %d)"), da_acc_length()+1);
item->kcur = GLOBALS->kcur;
g_strstrip(item->name);
if( strlen(item->name) > 0 )
{
if( da_acc_append(item) )
{
ui_acc_listview_add(GTK_TREE_VIEW(data->LV_acc), item);
data->change++;
}
}
}
}
}
/*
** delete the selected account to our treeview and temp GList
*/
static void ui_acc_manage_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
guint32 key;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] remove data=%p\n", data) );
key = ui_acc_listview_get_selected_key(GTK_TREE_VIEW(data->LV_acc));
if( key > 0 )
{
Account *item = da_acc_get(key);
guint usagecode = account_is_used(key);
if( usagecode != ACC_USAGE_NONE )
{
gchar *title, *reason;
title = g_strdup_printf (
_("Cannot delete account '%s'"), item->name);
switch(usagecode)
{
case ACC_USAGE_TXN : reason = _("It has transaction"); break;
case ACC_USAGE_TXN_XFER: reason = _("It is target of xfer transaction"); break;
case ACC_USAGE_ARC : reason = _("It has scheduled/template"); break;
case ACC_USAGE_ARC_XFER: reason = _("It is target of xfer scheduled/template"); break;
default: reason = ""; break;
}
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
title,
reason
//_("This account contains transactions and/or is part of internal transfers.")
);
g_free(title);
}
else
{
gchar *title;
gchar *secondtext;
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->name);
secondtext = _("If you delete an account, it will be permanently lost.");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
da_acc_delete(key);
ui_acc_listview_remove_selected(GTK_TREE_VIEW(data->LV_acc));
data->change++;
}
}
}
}
/*
** rename the selected account to our treeview and temp GList
*/
static void ui_acc_manage_rename(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
Account *item;
guint32 key;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] rename data=%p\n", data) );
key = ui_acc_listview_get_selected_key(GTK_TREE_VIEW(data->LV_acc));
if( key > 0 )
{
item = da_acc_get(key);
gchar *name = dialog_get_name(_("Account name"), item->name, GTK_WINDOW(data->dialog));
if(name != NULL)
{
if(account_rename(item, name))
{
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
data->change++;
}
else
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot rename this Account,\n"
"from '%s' to '%s',\n"
"this name already exists."),
item->name,
name
);
}
}
}
}
static void ui_acc_manage_toggled_nobudget(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
Account *accitem;
gboolean selected, active;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] toggled_nobudget data=%p\n", data) );
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), &model, &iter);
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &accitem, -1);
accitem->flags &= ~(AF_NOBUDGET);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_nobudget));
if(active) accitem->flags |= AF_NOBUDGET;
/* redraw the row to display/hide the icon */
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_model_row_changed(model, path, &iter);
gtk_tree_path_free (path);
// gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_arc));
//gtk_widget_queue_draw (GTK_WIDGET(data->LV_arc));
}
}
static void ui_acc_manage_toggled_closed(GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
Account *accitem;
gboolean selected, active;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-acc] toggled_closed data=%p\n", data) );
selected = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), &model, &iter);
if(selected)
{
gtk_tree_model_get(model, &iter, LST_DEFACC_DATAS, &accitem, -1);
accitem->flags &= ~(AF_CLOSED);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_closed));
if(active) accitem->flags |= AF_CLOSED;
/* redraw the row to display/hide the icon */
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_model_row_changed(model, path, &iter);
gtk_tree_path_free (path);
// gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_arc));
//gtk_widget_queue_draw (GTK_WIDGET(data->LV_arc));
}
}
static void ui_acc_manage_rowactivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
//struct account_data *data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
ui_acc_manage_rename(GTK_WIDGET(treeview), NULL);
}
/*
**
*/
static void ui_acc_manage_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_acc_manage_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void
ui_acc_manage_sort_changed(GtkTreeSortable *sortable, gpointer user_data)
{
struct ui_acc_manage_data *data = user_data;
DB( g_printf("\n[ui-acc] sort changed\n") );
ui_acc_manage_update(data->dialog, NULL);
}
//gint ui_acc_manage_list_sort(struct _Account *a, struct _Account *b) { return( a->acc_Id - b->acc_Id); }
/*
**
*/
static gboolean ui_acc_manage_cleanup(struct ui_acc_manage_data *data, gint result)
{
guint32 key;
gboolean doupdate = FALSE;
DB( g_print("\n[ui-acc] cleanup %p\n", data) );
key = ui_acc_listview_get_selected_key(GTK_TREE_VIEW(data->LV_acc));
if(key > 0)
{
data->lastkey = key;
DB( g_print(" -> should first do a get for account %d\n", data->lastkey) );
ui_acc_manage_getlast(data);
}
DB( g_print(" changes: %d\n", data->change) );
GLOBALS->changes_count += data->change;
group_delete_unused();
return doupdate;
}
static void
ui_acc_manage_search_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct ui_acc_manage_data *data = user_data;
DB( g_printf("\n[ui-acc] search_changed_cb\n") );
ui_acc_manage_dialog_refilter(data);
}
/*
**
*/
static void ui_acc_manage_setup(struct ui_acc_manage_data *data)
{
GList *tmplist;
GString *tpltitle;
DB( g_print("\n[ui-acc] setup\n") );
DB( g_print(" init data\n") );
//init GList
data->tmp_list = NULL; //hb-glist_clone_list(GLOBALS->acc_list, sizeof(struct _Account));
data->action = 0;
data->change = 0;
data->lastkey = 0;
DB( g_print(" populate\n") );
ui_acc_listview_populate(data->LV_acc, ACC_LST_INSERT_NORMAL, NULL);
ui_cur_combobox_populate(GTK_COMBO_BOX(data->CY_curr), GLOBALS->h_cur);
//populate_view_acc(data->LV_acc, GLOBALS->acc_list, TRUE);
ui_grp_comboboxentry_populate(GTK_COMBO_BOX(data->ST_group), GLOBALS->h_grp);
//TODO: move to ui-archive
DB( g_print(" populate tpl start\n") );
//setting wrap causes O(n^2) performance regression because after
//every insert the drop-down list size is re-computed.
//gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(data->CY_template), 0);
//populate template
hbtk_combo_box_text_append(GTK_COMBO_BOX(data->CY_template), 0, _("(none)"));
tpltitle = g_string_sized_new(255);
tmplist = g_list_first(GLOBALS->arc_list);
while (tmplist != NULL)
{
Archive *item = tmplist->data;
if( !(item->rec_flags & TF_RECUR) )
{
da_archive_get_display_label(tpltitle, item);
hbtk_combo_box_text_append(GTK_COMBO_BOX(data->CY_template), item->key, tpltitle->str);
}
tmplist = g_list_next(tmplist);
}
g_string_free(tpltitle, TRUE);
//gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_template), 0);
//gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(data->CY_template), 1);
DB( g_print(" populate tpl end\n") );
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), "changed", G_CALLBACK (ui_acc_manage_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_acc), "row-activated", G_CALLBACK (ui_acc_manage_rowactivated), GINT_TO_POINTER(2));
g_signal_connect (gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc)), "sort-column-changed", G_CALLBACK (ui_acc_manage_sort_changed), data);
gtk_tree_view_set_search_entry(GTK_TREE_VIEW(data->LV_acc), GTK_ENTRY(data->ST_search));
g_signal_connect (G_OBJECT (data->ST_search), "search-changed", G_CALLBACK (ui_acc_manage_search_changed_cb), data);
g_signal_connect (data->CY_curr , "changed", G_CALLBACK (ui_acc_manage_changed_curr_cb), NULL);
g_signal_connect (data->CM_closed, "toggled", G_CALLBACK (ui_acc_manage_toggled_closed), NULL);
g_signal_connect (data->CM_nobudget, "toggled", G_CALLBACK (ui_acc_manage_toggled_nobudget), NULL);
g_signal_connect (G_OBJECT (data->BT_add) , "clicked", G_CALLBACK (ui_acc_manage_add), NULL);
g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_acc_manage_rename), NULL);
g_signal_connect (G_OBJECT (data->BT_rem) , "clicked", G_CALLBACK (ui_acc_manage_delete), NULL);
g_signal_connect (G_OBJECT (data->BT_up ), "clicked", G_CALLBACK (ui_acc_manage_cb_move_updown), GUINT_TO_POINTER(GTK_DIR_UP));
g_signal_connect (G_OBJECT (data->BT_down), "clicked", G_CALLBACK (ui_acc_manage_cb_move_updown), GUINT_TO_POINTER(GTK_DIR_DOWN));
}
static gboolean
ui_acc_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_acc_manage_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[ui-acc] mapped\n") );
ui_acc_manage_setup(data);
ui_acc_manage_update(data->LV_acc, NULL);
data->mapped_done = TRUE;
return FALSE;
}
/*
**
*/
GtkWidget *ui_acc_manage_dialog (void)
{
struct ui_acc_manage_data *data;
GtkWidget *dialog, *content, *mainbox, *vbox, *scrollwin, *notebook;
GtkWidget *content_grid, *group_grid, *tbar, *bbox;
GtkWidget *label, *widget, *treeview, *hpaned;
gint w, h, dw, dh, row;
DB( g_print("\n[ui-acc] new\n") );
data = g_malloc0(sizeof(struct ui_acc_manage_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Accounts"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 4:3
dw = (dh * 4) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print(" dialog=%p, inst_data=%p\n", dialog, data) );
//window contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainbox);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_LARGE);
hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
hbtk_box_prepend (GTK_BOX (mainbox), hpaned);
// set paned position
//w = w/PHI;
//gtk_paned_set_position(GTK_PANED(hpaned), w - (w/PHI));
/* left area */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_end(vbox, SPACING_SMALL);
gtk_paned_pack1 (GTK_PANED(hpaned), vbox, FALSE, FALSE);
widget = make_search ();
data->ST_search = widget;
gtk_widget_set_size_request(widget, HB_MINWIDTH_SEARCH, -1);
gtk_widget_set_halign(widget, GTK_ALIGN_END);
hb_widget_set_margins(GTK_WIDGET(widget), 0, 0, SPACING_MEDIUM, 0);
gtk_box_prepend (GTK_BOX (vbox), widget);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
treeview = ui_acc_listview_new(FALSE);
data->LV_acc = treeview;
gtk_widget_set_size_request(treeview, HB_MINWIDTH_LIST, -1);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_ADD, _("Add"));
data->BT_add = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
data->BT_rem = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_EDIT, _("Rename"));
data->BT_edit = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_LIST_MOVE_UP, _("Move up"));
data->BT_up = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_MOVE_DOWN, _("Move down"));
data->BT_down = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
/* right area */
notebook = gtk_notebook_new();
data->notebook = notebook;
gtk_widget_set_margin_start(notebook, SPACING_SMALL);
gtk_paned_pack2 (GTK_PANED(hpaned), notebook, FALSE, FALSE);
/* page :: General */
content_grid = gtk_grid_new();
gtk_orientable_set_orientation(GTK_ORIENTABLE(content_grid), GTK_ORIENTATION_VERTICAL);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
label = gtk_label_new(_("General"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content_grid, label);
// group :: Account
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, 0, 1, 1);
//label = make_label_group(_("Account"));
//gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 0;
label = make_label_widget(_("_Type:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new_with_data(label, CYA_ACC_TYPE);
data->CY_type = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Group:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
//widget = make_string(label);
widget = ui_grp_comboboxentry_new(label);
data->ST_group = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
label = make_label_widget(_("_Institution:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_institution = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
label = make_label_widget(_("N_umber:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_number = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
label = make_label_widget(_("Start _balance:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_amount(label);
data->ST_initial = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("_Currency:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = ui_cur_combobox_new(label);
data->CY_curr = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("this account was _closed"));
data->CM_closed = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
//5.7
row++;
label = make_label_widget(_("Website:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_string(label);
data->ST_website = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
row++;
label = make_label_widget(_("Notes:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = gtk_text_view_new ();
//#1697171 add wrap
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request (scrollwin, -1, 48);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), widget);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
data->TB_notes = widget;
gtk_grid_attach (GTK_GRID (group_grid), scrollwin, 2, row, 2, 1);
/* page :: Behaviour */
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
label = gtk_label_new(_("Behaviour"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content_grid, label);
// group :: miscelleaneous
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, 1, 1, 1);
label = make_label_group(_("Automation"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 2, 1);
row = 1;
label = make_label_widget(_("Default _Template:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = hbtk_combo_box_new(label);
data->CY_template = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 2, 1);
/* test not working
GValue gvalue = G_VALUE_INIT;
g_value_init (&gvalue, G_TYPE_BOOLEAN);
g_value_set_boolean (&gvalue, TRUE);
g_object_set_property(data->CY_template, "appears-as-list", &gvalue);
*/
// group :: Report exclusion
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, 2, 1, 1);
label = make_label_group(_("Report exclusion"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 2, 1);
row = 1;
widget = gtk_check_button_new_with_mnemonic (_("exclude from account _summary"));
data->CM_nosummary = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
//#1896441 outflow summary
row++;
widget = gtk_check_button_new_with_mnemonic (_("outflow into summary"));
data->CM_outflowsum= widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("exclude from the _budget"));
data->CM_nobudget = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("exclude from any _reports"));
data->CM_noreport = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 1, row, 1, 1);
/* page :: Misc. */
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
label = gtk_label_new(_("Misc."));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content_grid, label);
// group :: Current check number
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, 3, 1, 1);
label = make_label_group(_("Current check number"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_widget(_("Checkbook _1:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_long (label);
data->ST_cheque1 = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Checkbook _2:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_long (label);
data->ST_cheque2 = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
// group :: Institution
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, 0, 1, 1);
label = make_label_group(_("Balance limits"));
gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row++;
label = make_label_widget(_("_Overdraft at:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_amount(label);
data->ST_minimum = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
row++;
label = make_label_widget(_("Max_imum:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_amount(label);
data->ST_maximum = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//TODO: warning/absolute minimum balance
/*
label = make_label_widget(_("_Warning at:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = make_amount(label);
data->ST_warning = widget;
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
*/
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_acc_manage_mapped), &dialog);
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_acc_manage_cb_on_key_press), (gpointer)data);
// setup, init and show dialog
//moved to mapped-event
//g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), "changed", G_CALLBACK (ui_acc_manage_selection), NULL);
//g_signal_connect (GTK_TREE_VIEW(data->LV_acc), "row-activated", G_CALLBACK (ui_acc_manage_rowactivated), GINT_TO_POINTER(2));
//g_signal_connect (data->CY_curr , "changed", G_CALLBACK (ui_acc_manage_changed_curr_cb), NULL);
//g_signal_connect (data->CM_closed, "toggled", G_CALLBACK (ui_acc_manage_toggled_closed), NULL);
//g_signal_connect (data->CM_nobudget, "toggled", G_CALLBACK (ui_acc_manage_toggled_nobudget), NULL);
//g_signal_connect (G_OBJECT (data->BT_add) , "clicked", G_CALLBACK (ui_acc_manage_add), NULL);
//g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_acc_manage_rename), NULL);
//g_signal_connect (G_OBJECT (data->BT_rem) , "clicked", G_CALLBACK (ui_acc_manage_delete), NULL);
//setup, init and show dialog
//ui_acc_manage_setup(data);
//ui_acc_manage_update(data->LV_acc, NULL);
//avoid focus on search
gtk_window_set_focus(GTK_WINDOW(dialog), data->BT_add);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup & destroy
ui_acc_manage_cleanup(data, result);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-archive.h 0000644 0001750 0001750 00000010077 14766237776 015620 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ARCHIVE_H__
#define __HB_ARCHIVE_H__
#include "hb-types.h"
struct _archive
{
guint32 key;
gdouble amount;
guint32 kacc;
guchar paymode;
guchar grpflg;
gushort flags;
guint32 kpay;
guint32 kcat;
gchar *memo;
//guint32 date;
//gushort pos;
gushort status;
gchar *number; //info < 5.8
guint32 *tags;
//guint32 kxfer; //strong link xfer key
guint32 kxferacc;
gdouble xferamount; //xfer target alount
GPtrArray *splits;
//recurrence :: https://learn.microsoft.com/en-us/graph/outlook-schedule-recurring-events
gushort rec_flags; //flags
guchar rec_freq; //0-3
guchar rec_every; //100
guchar rec_ordinal; //0-5
guchar rec_weekday; //1-10
guint32 nextdate;
guchar daygap;
guchar weekend; //0 - 3
gushort limit; //366
/* unsaved datas */
gushort dspflags;
};
// saved flags -- data
//gushort is 2 bytes / 16 bits
#define TF_RECUR (1<< 0)
#define TF_LIMIT (1<< 1)
#define TF_RELATIVE (1<< 2)
enum
{
ARC_POSTMODE_DUEDATE,
ARC_POSTMODE_PAYOUT,
ARC_POSTMODE_ADVANCE
};
/*
** scheduled unit
*/
enum {
AUTO_FREQ_DAY,
AUTO_FREQ_WEEK,
AUTO_FREQ_MONTH,
//AUTO_UNIT_QUARTER,
AUTO_FREQ_YEAR
};
//5.9
enum {
AUTO_ORDINAL_FIRST = 1,
AUTO_ORDINAL_SECOND,
AUTO_ORDINAL_THIRD,
AUTO_ORDINAL_FOURTH,
AUTO_ORDINAL_LAST,
};
//5.9
enum {
AUTO_WEEKDAY_MONDAY = 1,
AUTO_WEEKDAY_TUESDAY,
AUTO_WEEKDAY_WEDNESDAY,
AUTO_WEEKDAY_THURSDAY,
AUTO_WEEKDAY_FRIDAY,
AUTO_WEEKDAY_SATURDAY,
AUTO_WEEKDAY_SUNDAY,
//----
AUTO_WEEKDAY_DAY,
AUTO_WEEKDAY_WEEKDAY,
AUTO_WEEKDAY_WEEKENDDAY,
};
enum {
ARC_WEEKEND_POSSIBLE,
ARC_WEEKEND_BEFORE,
ARC_WEEKEND_AFTER,
ARC_WEEKEND_SKIP
};
enum
{
FLT_SCHEDULED_THISMONTH = 1,
FLT_SCHEDULED_NEXTMONTH,
FLT_SCHEDULED_NEXT30DAYS,
FLT_SCHEDULED_NEXT60DAYS,
FLT_SCHEDULED_NEXT90DAYS,
FLT_SCHEDULED_ALLDATE,
//added 5.7
FLT_SCHEDULED_MAXPOSTDATE
};
Archive *da_archive_malloc(void);
Archive *da_archive_clone(Archive *src_item);
guint archive_add_get_nbdays(void);
void da_archive_free(Archive *item);
void da_archive_destroy(GList *list);
guint da_archive_length(void);
void da_archive_stats(gint *nbtpl, gint *nbsch);
gboolean da_archive_append(Archive *item);
gboolean da_archive_append_new(Archive *item);
guint32 da_archive_get_max_key(void);
Archive *da_archive_get(guint32 key);
void da_archive_get_display_label(GString *tpltitle, Archive *item);
void da_archive_consistency(Archive *item);
Archive *da_archive_init_from_transaction(Archive *arc, Transaction *txn, gboolean fromledger);
GList *da_archive_glist_sorted(gint column);
gboolean template_is_account_used(Archive *arc);
void scheduled_nextdate_weekend_adjust(Archive *arc);
guint32 scheduled_date_get_next_post(GDate *date, Archive *arc, guint32 nextdate);
guint32 scheduled_date_get_next_relative(GDate *date, guint ordinal, guint weekday, guint every);
gboolean scheduled_is_postable(Archive *arc);
guint32 scheduled_get_txn_real_postdate(guint32 postdate, gint weekend);
guint32 scheduled_get_latepost_count(GDate *date, Archive *arc, guint32 jrefdate);
guint32 scheduled_date_advance(Archive *arc);
void scheduled_date_get_show_minmax(gint select, guint32 *mindate, guint32 *maxdate);
guint32 scheduled_date_get_post_max(guint32 start, gint auto_smode, gint auto_nbdays, gint auto_weekday, gint nbmonth);
gint scheduled_post_all_pending(void);
#endif
homebank-5.9.7/src/hb-assign.h 0000644 0001750 0001750 00000004520 14736461415 015441 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ASSIGN_H__
#define __HB_ASSIGN_H__
#include "hb-types.h"
struct _assign
{
guint32 key;
gushort flags;
gushort field; //0:memo / 1:payee
gchar *search;
gchar *notes;
guint32 kpay; //dst payee
guint32 kcat; //dst category
gushort paymode; //dst_payment mode
guint32 pos; //position
gdouble amount; //5.6 #1710085 assignment based on amount
guint32 *tags; //5.8 #1674016
};
#define ASGF_EXACT (1<<0)
#define ASGF_DOPAY (1<<1)
#define ASGF_DOCAT (1<<2)
#define ASGF_DOMOD (1<<3)
#define ASGF_DOTAG (1<<4) //5.8
#define ASGF_PREFILLED (1<<5)
#define ASGF_AMOUNT (1<<7) //5.6
#define ASGF_REGEX (1<<8)
#define ASGF_OVWPAY (1<<9)
#define ASGF_OVWCAT (1<<10)
#define ASGF_OVWMOD (1<<11)
#define ASGF_OVWTAG (1<<12) //5.8
void
da_asg_free(Assign *item);
Assign *da_asg_malloc(void);
void da_asg_destroy(void);
void da_asg_new(void);
guint da_asg_length(void);
gboolean da_asg_create_none(void);
gboolean da_asg_remove(guint32 key);
gboolean da_asg_insert(Assign *asg);
gboolean da_asg_append(Assign *asg);
Assign *da_asg_duplicate(Assign *srcitem);
guint32 da_asg_get_max_key(void);
Assign *da_asg_get_by_name(gchar *name);
Assign *da_asg_get(guint32 key);
void da_asg_consistency(Assign *item);
Assign *da_asg_init_from_transaction(Assign *asg, Transaction *txn);
void da_asg_update_position(void);
gchar *assign_get_target_payee(Assign *asgitem);
gchar *assign_get_target_category(Assign *asgitem);
GList *assign_glist_sorted(gint column);
guint transaction_auto_assign(GList *ope_list, guint32 key, gboolean lockrecon);
#endif
homebank-5.9.7/src/hb-category.c 0000644 0001750 0001750 00000065063 15120541633 015763 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-category.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
Category *
da_cat_clone(Category *src_item)
{
Category *new_item = g_memdup(src_item, sizeof(Category));
DB( g_print("da_cat_clone\n") );
if(new_item)
{
//duplicate the string
new_item->name = g_strdup(src_item->name);
new_item->fullname = g_strdup(src_item->fullname);
new_item->typename = g_strdup(src_item->typename);
}
return new_item;
}
void
da_cat_free(Category *item)
{
DB( g_print("da_cat_free\n") );
if(item != NULL)
{
DB( g_print(" => %d, %s\n", item->key, item->name) );
g_free(item->name);
g_free(item->fullname);
g_free(item->typename);
g_free(item);
}
}
Category *
da_cat_malloc(void)
{
DB( g_print("da_cat_malloc\n") );
return g_malloc0(sizeof(Category));
}
void
da_cat_destroy(void)
{
DB( g_print("da_cat_destroy\n") );
g_hash_table_destroy(GLOBALS->h_cat);
}
void
da_cat_new(void)
{
Category *item;
DB( g_print("da_cat_new\n") );
GLOBALS->h_cat = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_cat_free);
// insert our 'no category'
item = da_cat_malloc();
item->key = 0;
item->name = g_strdup("");
item->fullname = g_strdup("");
item->typename = g_strdup("");
da_cat_insert(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/**
* da_cat_length:
*
* Return value: the number of elements
*/
guint
da_cat_length(void)
{
return g_hash_table_size(GLOBALS->h_cat);
}
static void
da_cat_max_key_ghfunc(gpointer key, Category *cat, guint32 *max_key)
{
*max_key = MAX(*max_key, cat->key);
}
/**
* da_cat_get_max_key:
*
* Get the biggest key from the GHashTable
*
* Return value: the biggest key value
*
*/
guint32
da_cat_get_max_key(void)
{
guint32 max_key = 0;
g_hash_table_foreach(GLOBALS->h_cat, (GHFunc)da_cat_max_key_ghfunc, &max_key);
return max_key;
}
static gboolean
da_cat_remove_grfunc(gpointer key, Category *cat, guint32 *remkey)
{
if(cat->key == *remkey || cat->parent == *remkey)
return TRUE;
return FALSE;
}
/**
* da_cat_delete:
*
* delete a category from the GHashTable
*
* Return value: TRUE if the key was found and deleted
*
*/
guint
da_cat_delete(guint32 key)
{
DB( g_print("\nda_cat_delete %d\n", key) );
return g_hash_table_foreach_remove(GLOBALS->h_cat, (GHRFunc)da_cat_remove_grfunc, &key);
}
static void
da_cat_build_typename(Category *item)
{
gchar *newname = NULL;
gchar type;
type = category_get_type_char(item);
if(item->key == 0)
newname = g_strdup(item->name);
else
{
if( item->parent == 0 )
newname = g_markup_printf_escaped("%s [%c]", item->name, type);
//string = g_strdup_printf("%s [%c]", name, type);
else
newname = g_markup_printf_escaped(" %c %s", type, item->name);
//string = g_strdup_printf("%c %s", type, name);
}
if( newname )
{
g_free(item->typename);
item->typename = newname;
DB( g_print("- updated %d:'%s' typename='%s'\n", item->key, item->name, item->typename) );
}
}
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) );
}
//#1889659: ensure name != null/empty
static gboolean
da_cat_ensure_name(Category *item)
{
// (no category) have name=""
if( item->key > 0 )
{
if( item->name == NULL || strlen(item->name) == 0 )
{
g_free(item->name);
item->name = g_strdup_printf("no name %d", item->key);
return TRUE;
}
}
return FALSE;
}
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);
//#1889659: ensure name != null/empty
da_cat_ensure_name(item);
da_cat_build_fullname(item);
da_cat_build_typename(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:
*
* insert a category into the GHashTable
*
* Return value: TRUE if inserted
*
*/
gboolean
da_cat_insert(Category *item)
{
guint32 *new_key;
DB( g_print("\nda_cat_insert\n") );
DB( g_print("- '%s'\n", item->name) );
new_key = g_new0(guint32, 1);
*new_key = item->key;
//#1889659: ensure name != null/empty
da_cat_ensure_name(item);
da_cat_build_fullname(item);
da_cat_build_typename(item);
g_hash_table_insert(GLOBALS->h_cat, new_key, item);
return TRUE;
}
/**
* da_cat_append:
*
* append a category into the GHashTable
*
* Return value: TRUE if inserted
*
*/
// used only to add cat/subcat from ui_category with the 2 inputs
gboolean
da_cat_append(Category *cat)
{
Category *existitem;
DB( g_print("\nda_cat_append\n") );
if( !cat->fullname )
da_cat_build_fullname(cat);
if( !cat->typename )
da_cat_build_typename(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) );
return FALSE;
}
/* fullname i.e. car:refuel */
struct fullcatcontext
{
guint32 parent;
gchar *name;
};
static gboolean
da_cat_fullname_grfunc(gpointer key, Category *item, struct fullcatcontext *ctx)
{
//DB( g_print("'%s' == '%s'\n", ctx->name, item->name) );
if( item->parent == ctx->parent )
{
if( ctx->name && item->name )
if(!strcasecmp(ctx->name, item->name))
return TRUE;
}
return FALSE;
}
static Category *da_cat_get_by_name_find_internal(guint32 parent, gchar *name)
{
struct fullcatcontext ctx;
ctx.parent = parent;
ctx.name = name;
DB( g_print("- searching %s %d '%s'\n", (parent == 0) ? "lv1cat" : "lv2cat", parent, name) );
return g_hash_table_find(GLOBALS->h_cat, (GHRFunc)da_cat_fullname_grfunc, &ctx);
}
static gchar **da_cat_get_by_fullname_split_clean(gchar *rawfullname, guint *outlen)
{
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)) );
if( outlen != NULL )
*outlen = len;
if(len >= 1)
{
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;
}
}
if(valid == TRUE)
return partstr;
DB( g_print("- is invalid\n") );
g_strfreev(partstr);
return NULL;
}
Category *
da_cat_get_by_fullname(gchar *rawfullname)
{
gchar **partstr;
Category *parent = NULL;
Category *retval = NULL;
guint len;
DB( g_print("\nda_cat_get_by_fullname\n") );
if( rawfullname )
{
if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
{
if( len >= 1 )
{
parent = da_cat_get_by_name_find_internal(0, partstr[0]);
retval = parent;
}
if( len == 2 && parent != NULL )
{
retval = da_cat_get_by_name_find_internal(parent->key, partstr[1]);
}
g_strfreev(partstr);
}
}
return retval;
}
/**
* da_cat_append_ifnew_by_fullname:
*
* append a category if it is new by fullname
*
* Return value:
*
*/
Category *
da_cat_append_ifnew_by_fullname(gchar *rawfullname)
{
gchar **partstr;
Category *parent = NULL;
Category *newcat = NULL;
Category *retval = NULL;
guint len;
DB( g_print("\nda_cat_append_ifnew_by_fullname\n") );
if( rawfullname )
{
if( (partstr = da_cat_get_by_fullname_split_clean(rawfullname, &len)) != NULL )
{
if( len >= 1 )
{
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;
}
/* if we have a subcategory - xxx:xxx */
if( len == 2 && parent != NULL )
{
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;
}
g_strfreev(partstr);
}
}
return retval;
}
/**
* da_cat_get:
*
* Get a category structure by key
*
* Return value: Category * or NULL if not found
*
*/
Category *
da_cat_get(guint32 key)
{
//DB( g_print("da_cat_get\n") );
return g_hash_table_lookup(GLOBALS->h_cat, &key);
}
gchar *da_cat_get_name(Category *item)
{
gchar *name = NULL;
if(item != NULL)
{
name = item->key == 0 ? _("(no category)") : item->name;
}
return name;
}
gchar *da_cat_get_fullname(Category *item)
{
gchar *name = NULL;
if(item != NULL)
{
name = item->key == 0 ? _("(no category)") : item->fullname;
}
return name;
}
void da_cat_consistency(Category *item)
{
//gboolean isIncome;
if((item->flags & GF_SUB) && item->key > 0)
{
//check for existing parent
if( da_cat_get(item->parent) == NULL )
{
Category *parent = da_cat_append_ifnew_by_fullname ("orphaned");
item->parent = parent->key;
da_cat_build_fullname(item);
g_warning("category consistency: fixed missing parent %d", item->parent);
}
}
// ensure type equal for categories and its children
/* no since #1740368
if(!(item->flags & GF_SUB) && item->key > 0)
{
isIncome = (item->flags & GF_INCOME) ? TRUE : FALSE;
if( category_change_type(item, isIncome) > 0 )
{
g_warning("category consistency: fixed type for child");
GLOBALS->changes_count++;
}
}*/
if( item->name != NULL )
g_strstrip(item->name);
else
{
if( da_cat_ensure_name(item) )
{
da_cat_build_fullname(item);
g_warning("category consistency: fixed null name");
GLOBALS->changes_count++;
}
}
}
//#2026641
void da_cat_anonymize(Category *item)
{
g_free(item->name);
item->name = g_strdup_printf("category %d", item->key);
da_cat_build_fullname(item);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG
static void
da_cat_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
{
guint32 *id = key;
Category *cat = value;
DB( g_print(" %d :: %s (parent=%d\n", *id, cat->name, cat->parent) );
}
static void
da_cat_debug_list(void)
{
DB( g_print("\n** debug **\n") );
g_hash_table_foreach(GLOBALS->h_cat, da_cat_debug_list_ghfunc, NULL);
DB( g_print("\n** end debug **\n") );
}
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gboolean
category_key_budget_active(guint32 key)
{
Category *catitem = da_cat_get(key);
if( catitem == NULL )
return FALSE;
if( catitem->flags & (GF_BUDGET|GF_FORCED) )
return TRUE;
//#2121405 eval also parent budget
if( catitem->parent > 0 )
{
catitem = da_cat_get(catitem->parent);
if( catitem != NULL )
{
if( catitem->flags & (GF_BUDGET|GF_FORCED) )
return TRUE;
}
}
return FALSE;
}
gboolean
category_key_unbudgeted(guint32 key)
{
Category *catitem = da_cat_get(key);
if( catitem == NULL )
return FALSE;
//#2101100 exclude subcat from unbudgeted
if( PREFS->budg_unexclsub == TRUE )
{
if( catitem->parent > 0 )
{
catitem = da_cat_get(catitem->parent);
if( catitem == NULL )
return FALSE;
}
}
return (catitem->flags & (GF_BUDGET|GF_FORCED)) ? FALSE : TRUE;
}
guint32
category_report_id(guint32 key, gboolean subcat)
{
guint32 retval = 0;
if(subcat == FALSE)
{
Category *catitem = da_cat_get(key);
if(catitem)
retval = (catitem->flags & GF_SUB) ? catitem->parent : catitem->key;
}
else
{
retval = key;
}
//DB( g_print("- cat '%s' reportid = %d\n", catitem->name, retval) );
return retval;
}
gint
category_delete_unused(void)
{
GList *lcat, *list;
gint count = 0;
lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
Category *entry = list->data;
if(entry->nb_use_all <= 0 && entry->key > 0)
{
//da_cat_delete (entry->key);
g_hash_table_remove(GLOBALS->h_cat, &entry->key);
count++;
}
list = g_list_next(list);
}
g_list_free(lcat);
return count;
}
static void
category_fill_usage_count(guint32 kcat, gboolean txn)
{
Category *parent, *cat = da_cat_get (kcat);
if(cat)
{
if(txn == TRUE)
{
cat->nb_use_txn++;
cat->nb_use_txncat++;
}
cat->nb_use_all++;
cat->nb_use_allcat++;
if( cat->parent > 0 )
{
parent = da_cat_get(cat->parent);
if( parent )
{
if(txn == TRUE)
parent->nb_use_txn++;
parent->nb_use_all++;
}
}
}
}
void
category_fill_usage(void)
{
GList *lcat;
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GList *lpay, *lrul, *list;
guint i, nbsplit;
lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
Category *entry = list->data;
entry->nb_use_all = 0;
entry->nb_use_allcat = 0;
entry->nb_use_txn = 0;
entry->nb_use_txncat = 0;
list = g_list_next(list);
}
g_list_free(lcat);
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
//#1689308 count split as well
if( txn->flags & OF_SPLIT )
{
nbsplit = da_splits_length(txn->splits);
for(i=0;isplits, i);
category_fill_usage_count(split->kcat, TRUE);
}
}
else
category_fill_usage_count(txn->kcat, TRUE);
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
lpay = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *entry = list->data;
category_fill_usage_count(entry->kcat, FALSE);
list = g_list_next(list);
}
g_list_free(lpay);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *entry = list->data;
//#1689308 count split as well
if( entry->flags & OF_SPLIT )
{
nbsplit = da_splits_length(entry->splits);
for(i=0;isplits, i);
category_fill_usage_count(split->kcat, FALSE);
}
}
else
category_fill_usage_count(entry->kcat, FALSE);
list = g_list_next(list);
}
lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *entry = list->data;
category_fill_usage_count(entry->kcat, FALSE);
list = g_list_next(list);
}
g_list_free(lrul);
}
//#1875070 if srckey is a cat: any subcat should match as well
static gboolean
category_move_match(guint32 eltkey, guint32 srckey, gboolean dosubcat)
{
Category *cat = da_cat_get(eltkey);
if(cat)
{
if(!dosubcat)
{
if(cat->key == srckey)
return TRUE;
}
else
{
if(cat->key == srckey || cat->parent == srckey)
return TRUE;
}
}
return FALSE;
}
void
category_move(guint32 srckey, guint32 newkey, gboolean dosubcat)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn, *lpay;
GList *lrul, *list;
guint i, nbsplit;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txnitem = lnk_txn->data;
//#1875070
//if(txn->kcat == srckey)
if( category_move_match(txnitem->kcat, srckey, dosubcat) )
{
txnitem->kcat = newkey;
txnitem->dspflags |= FLAG_TMP_EDITED;
}
// move split category #1340142
nbsplit = da_splits_length(txnitem->splits);
for(i=0;isplits, i);
//#1875070
//if( split->kcat == srckey )
if( category_move_match(split->kcat, srckey, dosubcat) )
{
split->kcat = newkey;
txnitem->dspflags |= FLAG_TMP_EDITED;
}
}
lnk_txn = g_list_next(lnk_txn);
}
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *arcitem = list->data;
//#1875070
//if(entry->kcat == srckey)
if( category_move_match(arcitem->kcat, srckey, dosubcat) )
{
arcitem->kcat = newkey;
}
//#2081379 handle split as well
nbsplit = da_splits_length(arcitem->splits);
for(i=0;isplits, i);
//#1875070
//if( split->kcat == srckey )
if( category_move_match(split->kcat, srckey, dosubcat) )
{
split->kcat = newkey;
arcitem->dspflags |= FLAG_TMP_EDITED;
}
}
list = g_list_next(list);
}
//fix 5.4.2 missed payee here
lpay = list = g_hash_table_get_values(GLOBALS->h_pay);
while (list != NULL)
{
Payee *payitem = list->data;
if( category_move_match(payitem->kcat, srckey, dosubcat) )
{
payitem->kcat = newkey;
}
list = g_list_next(list);
}
g_list_free(lpay);
lrul = list = g_hash_table_get_values(GLOBALS->h_rul);
while (list != NULL)
{
Assign *asgitem = list->data;
//#1875070
//if(entry->kcat == srckey)
if( category_move_match(asgitem->kcat, srckey, dosubcat) )
{
asgitem->kcat = newkey;
}
list = g_list_next(list);
}
g_list_free(lrul);
}
gboolean
category_rename(Category *item, const gchar *newname)
{
Category *parent, *existitem;
gchar *fullname = NULL;
gchar *stripname;
gboolean retval = FALSE;
DB( g_print("\n(category) rename\n") );
stripname = g_strdup(newname);
g_strstrip(stripname);
if( item->parent == 0)
fullname = g_strdup(stripname);
else
{
parent = da_cat_get(item->parent);
if( parent )
{
fullname = g_strdup_printf("%s:%s", parent->name, stripname);
}
}
DB( g_print(" - search: %s\n", fullname) );
existitem = da_cat_get_by_fullname( fullname );
if( existitem != NULL && existitem->key != item->key)
{
DB( g_print("- error, same name already exist with other key %d <> %d\n", existitem->key, item->key) );
}
else
{
DB( g_print("- renaming\n") );
da_cat_rename (item, stripname);
retval = TRUE;
}
g_free(fullname);
g_free(stripname);
return retval;
}
static gint
category_glist_name_compare_func(Category *c1, Category *c2)
{
gint retval = 0;
if( c1 != NULL && c2 != NULL )
{
retval = hb_string_utf8_compare(c1->fullname, c2->fullname);
}
return retval;
}
static gint
category_glist_key_compare_func(Category *a, Category *b)
{
gint ka, kb, retval = 0;
if(a->parent == 0 && b->parent == a->key)
retval = -1;
else
if(b->parent == 0 && a->parent == b->key)
retval = 1;
else
{
ka = a->parent != 0 ? a->parent : a->key;
kb = b->parent != 0 ? b->parent : b->key;
retval = ka - kb;
}
#if MYDEBUG == 1
gchar *str;
if(retval < 0)
str = "a < b";
else
if(retval ==0)
str = "a = b";
else
if(retval > 0)
str = "a > b";
DB( g_print("compare a=%2d:%2d to b=%2d:%2d :: %d [%s]\n", a->key, a->parent, b->key, b->parent, retval, str ) );
#endif
return retval;
}
GList *
category_glist_sorted(gint column)
{
GList *list = g_hash_table_get_values(GLOBALS->h_cat);
switch(column)
{
case HB_GLIST_SORT_NAME:
return g_list_sort(list, (GCompareFunc)category_glist_name_compare_func);
break;
//case HB_GLIST_SORT_KEY:
default:
return g_list_sort(list, (GCompareFunc)category_glist_key_compare_func);
break;
}
}
gboolean
category_load_csv(gchar *filename, gchar **error)
{
gboolean retval;
GIOChannel *io;
gchar *tmpstr;
gint io_stat;
gchar **str_array;
gchar *lastcatname = NULL;
gchar *fullcatname;
GError *err = NULL;
Category *item;
gboolean isIncome;
const gchar *encoding;
encoding = homebank_file_getencoding(filename);
DB( g_print(" -> encoding should be %s\n", encoding) );
retval = TRUE;
*error = NULL;
io = g_io_channel_new_file(filename, "r", NULL);
if(io != NULL)
{
if( encoding != NULL )
{
g_io_channel_set_encoding(io, encoding, NULL);
}
for(;;)
{
if( *error != NULL )
break;
io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, &err);
DB( g_print(" + iostat %d\n", io_stat) );
if( io_stat == G_IO_STATUS_ERROR )
{
DB (g_print(" + ERROR %s\n",err->message));
break;
}
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_NORMAL)
{
if( tmpstr != NULL )
{
DB( g_print(" + strip %s\n", tmpstr) );
hb_string_strip_crlf(tmpstr);
DB( g_print(" + split\n") );
str_array = g_strsplit (tmpstr, ";", 3);
// type; sign; name
if( g_strv_length (str_array) != 3 )
{
*error = _("invalid CSV format");
retval = FALSE;
DB( g_print(" + error %s\n", *error) );
}
else
{
DB( g_print(" + read %s : %s : %s\n", str_array[0], str_array[1], str_array[2]) );
fullcatname = NULL;
if( g_str_has_prefix(str_array[0], "1") )
{
fullcatname = g_strdup(str_array[2]);
g_free(lastcatname);
lastcatname = g_strdup(str_array[2]);
}
else
if( g_str_has_prefix(str_array[0], "2") )
{
fullcatname = g_strdup_printf("%s:%s", lastcatname, str_array[2]);
}
item = da_cat_append_ifnew_by_fullname(fullcatname);
DB( g_print(" + item %p\n", item) );
if( item != NULL)
{
isIncome = g_str_has_prefix(str_array[1], "+") ? TRUE : FALSE;
category_change_type(item, isIncome, FALSE);
}
g_free(fullcatname);
g_strfreev (str_array);
}
}
}
g_free(tmpstr);
}
g_io_channel_unref (io);
}
g_free(lastcatname);
return retval;
}
gboolean
category_save_csv(gchar *filename, gchar **error)
{
gboolean retval = FALSE;
GIOChannel *io;
gchar *outstr;
GList *lcat, *list;
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
lcat = list = category_glist_sorted(HB_GLIST_SORT_NAME);
while (list != NULL)
{
Category *item = list->data;
if(item->key != 0)
{
gchar lvel, type;
//#1740368 changes here
type = category_get_type_char(item);
lvel = ( item->parent == 0 ) ? '1' : '2';
outstr = g_strdup_printf("%c;%c;%s\n", lvel, type, item->name);
DB( g_print(" + export %s\n", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
}
list = g_list_next(list);
}
retval = TRUE;
g_list_free(lcat);
g_io_channel_unref (io);
}
return retval;
}
// only used to warn in txn dialog
gint
category_type_get(Category *item)
{
if( (item->flags & (GF_INCOME)) )
return 1;
return -1;
}
gint
category_root_type_get(guint32 key)
{
Category *item;
if(key == 0)
return 0;
item = da_cat_get(key);
if(item == NULL)
return 0;
if(item->parent > 0)
item = da_cat_get(item->parent);
return category_type_get(item);
}
gchar
category_get_type_char(Category *item)
{
return (item->flags & GF_INCOME) ? '+' : '-';
}
static gint
category_change_type_eval(Category *item, gboolean isIncome)
{
gboolean flaginc;
gint retval = 0;
flaginc = (item->flags & (GF_INCOME)) ? TRUE : FALSE;
if( flaginc != isIncome )
retval = 1;
DB( g_print(" change:%d\n", retval) );
return retval;
}
gint
category_change_type(Category *item, gboolean isIncome, gboolean doChild)
{
GList *lcat, *list;
gint changes = 0;
DB( g_print("\n[category] category_change_type\n") );
DB( g_print(" set '%s' inc=%d dochild=%d\n", item->fullname, isIncome, doChild) );
//flag reset & set income
changes += category_change_type_eval(item, isIncome);
item->flags &= ~(GF_INCOME);
if(isIncome == TRUE)
item->flags |= GF_INCOME;
//if item is a subcat we override by its parent for below child processing
if( item->parent != 0 )
item = da_cat_get(item->parent);
if( item && !(item->flags & GF_SUB) )
{
item->flags &= ~(GF_MIXED);
lcat = list = g_hash_table_get_values(GLOBALS->h_cat);
while (list != NULL)
{
Category *child = list->data;
if(child->parent == item->key)
{
// propagate to child
if( doChild )
{
changes += category_change_type_eval(child, isIncome);
DB( g_print(" set child '%s' %d\n", child->fullname, isIncome) );
child->flags &= ~(GF_INCOME); //delete flag
if(isIncome == TRUE)
child->flags |= GF_INCOME;
}
da_cat_build_typename(child);
// set mixed if child has != sign
if((item->flags & GF_INCOME) != (child->flags & GF_INCOME) )
item->flags |= GF_MIXED;
}
list = g_list_next(list);
}
g_list_free(lcat);
}
da_cat_build_typename(item);
return changes;
}
/**
* category_find_preset:
*
* find a user language compatible file for category preset
*
* Return value: a pathname to the file or NULL
*
*/
gchar *
category_find_preset(gchar **lang)
{
gchar **langs;
gchar *filename;
gboolean exists;
guint i;
DB( g_print("** category_find_preset **\n") );
langs = (gchar **)g_get_language_names ();
DB( g_print(" -> %d languages detected\n", g_strv_length(langs)) );
for(i=0;i %d '%s'\n", i, langs[i]) );
filename = g_strdup_printf("hb-categories-%s.csv", langs[i]);
gchar *pathfilename = g_build_filename(homebank_app_get_datas_dir(), filename, NULL);
exists = g_file_test(pathfilename, G_FILE_TEST_EXISTS);
DB( g_print(" -> '%s' exists=%d\n", pathfilename, exists) );
if(exists)
{
g_free(filename);
*lang = langs[i];
return pathfilename;
}
g_free(filename);
g_free(pathfilename);
}
DB( g_print("return NULL\n") );
*lang = NULL;
return NULL;
}
homebank-5.9.7/src/hb-preferences.h 0000644 0001750 0001750 00000014320 14775773122 016461 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_PREFERENCES_H__
#define __HB_PREFERENCES_H__
#include "hb-currency.h"
#include "list-account.h"
#include "list-scheduled.h"
#define DEFAULT_FORMAT_DATE "%x"
#define MAX_FRAC_DIGIT 6
//Tango dark
#define DEFAULT_EXP_COLOR "#ce5c00" //Orange
#define DEFAULT_INC_COLOR "#4e9a36" //Chameleon
#define DEFAULT_WARN_COLOR "#a40000" //Scarlett Red
#define DEFAULT_FUTURE_BG_COLOR "#204a87"
/*
** Preference datas
*/
struct WinGeometry
{
gint l, t, w, h, s;
};
typedef enum
{
HB_PREF_WINGEO_NONE,
HB_PREF_WINGEO_DEFAULT,
HB_PREF_WINGEO_SCREEN,
HB_PREF_WINGEO_NETBOOK, //1024x600
HB_PREF_WINGEO_HD, //1366x768
HB_PREF_WINGEO_HDPLUS, //1600x900
HB_PREF_WINGEO_FHD, //1920x1080
} HbPrefWinGeoPreset;
struct Preferences
{
//--general
gboolean showsplash;
gboolean showwelcome;
gboolean loadlast;
gboolean appendscheduled;
gboolean do_update_currency;
//top spending
//gint date_range_wal;
gint rep_maxspenditems;
//--interface
gboolean custom_colors;
gboolean custom_bg_future;
gshort grid_lines;
gboolean rep_smallfont;
gshort toolbar_style;
gboolean gtk_darktheme;
gchar *icontheme;
gboolean icon_symbolic;
gboolean gtk_override;
gshort gtk_fontsize;
gboolean color_use_palette;
gchar *color_exp;
gchar *color_inc;
gchar *color_warn;
gchar *color_bg_future;
//locale
gchar *language;
gchar *date_format;
gshort fisc_year_day;
gshort fisc_year_month;
gboolean vehicle_unit_ismile; // true if unit is mile, default Km
gboolean vehicle_unit_isgal; // true if unit is gallon, default Liter
//transactions
//--general
gboolean showremind;
gboolean showvoid;
gboolean includeremind;
//--safety
gboolean safe_lock_recon;
gboolean safe_pend_recon;
gboolean safe_pend_past;
gshort safe_pend_past_days;
gshort padx;
//-- ledger
gint date_range_txn;
gint date_future_nbdays;
gboolean hidereconciled;
//-- dialog
gboolean heritdate;
gboolean txn_memoacp;
gshort txn_memoacp_days;
gboolean txn_showtemplate;
gboolean txn_showconfirm;
//--transfer
gboolean xfer_showdialog;
gshort xfer_daygap;
gboolean xfer_syncdate;
gboolean xfer_syncstat;
//--paymode
gint lst_paymode[NUM_PAYMODE_KEY+1];
//import/export
gint dtex_datefmt;
gint dtex_daygap;
gint dtex_ofxname;
gint dtex_ofxmemo;
gboolean dtex_qifmemo;
gboolean dtex_qifswap;
gboolean dtex_ucfirst;
gint dtex_csvsep;
//report options
gint date_range_rep;
gint report_color_scheme;
gboolean stat_byamount;
gboolean stat_showrate;
gboolean stat_showdetail;
gboolean stat_includexfer;
gboolean budg_showdetail;
gboolean budg_unexclsub;
//5.7
gboolean rep_forcast;
gint rep_forecat_nbmonth;
//backup option
gboolean bak_is_automatic;
gshort bak_max_num_copies;
//folders
gchar *path_hbfile;
gchar *path_hbbak;
gchar *path_import;
gchar *path_export;
gchar *path_attach;
//currency api
gchar *api_rate_url;
gchar *api_rate_key;
//euro zone
gboolean euro_active;
gint euro_country;
gboolean euro_mceii;
gdouble euro_value;
Currency minor_cur;
//---- others data (not in pref dialog) -----
gboolean dtex_nointro;
gboolean dtex_dodefpayee;
gboolean dtex_doautoassign;
gchar IntCurrSymbol[8];
gint lst_impope_columns[NUM_LST_DSPOPE+1];
//register list column
gint lst_ope_columns[NUM_LST_DSPOPE+1];
gint lst_ope_col_width[NUM_LST_DSPOPE+1];
gint lst_ope_sort_id; // -- implicit --
gint lst_ope_sort_order; // -- implicit --
//detail list column
gint lst_det_columns[NUM_LST_DSPOPE+1];
gint lst_det_col_width[NUM_LST_DSPOPE+1];
/* windows/dialogs size an position */
struct WinGeometry wal_wg;
struct WinGeometry acc_wg;
struct WinGeometry sta_wg;
struct WinGeometry tme_wg;
struct WinGeometry ove_wg;
struct WinGeometry bud_wg;
struct WinGeometry cst_wg;
struct WinGeometry txn_wg;
struct WinGeometry dbud_wg;
// main window stuffs
gboolean wal_toolbar;
gboolean wal_totchart;
gboolean wal_timchart;
gboolean wal_upcoming;
gint wal_vpaned;
gint wal_hpaned;
//home panel
gshort pnl_acc_col_acc_width;
gint lst_acc_columns[NUM_LST_COL_DSPACC+1];
gshort pnl_acc_show_by;
//hub total/time
gshort hub_tot_view;
gshort hub_tot_range;
gshort hub_tot_raw;
gshort hub_tim_view;
gshort hub_tim_range;
gshort hub_tim_raw;
//5.8 scheduled column order
gint lst_sch_columns[NUM_COL_SCH_UID+1];
gshort pnl_upc_col_pay_show;
gshort pnl_upc_col_pay_width;
gshort pnl_upc_col_cat_show;
gshort pnl_upc_col_cat_width;
gshort pnl_upc_col_mem_show;
gshort pnl_upc_col_mem_width;
gint pnl_upc_range;
gchar *pnl_list_tab;
//vehiclecost units (mile/gal or km/liters)
gchar *vehicle_unit_dist0;
gchar *vehicle_unit_dist1;
gchar *vehicle_unit_vol;
gchar *vehicle_unit_100;
gchar *vehicle_unit_distbyvol;
//unsaved
gushort lastlvl1, lastlvl2;
};
gint homebank_pref_list_column_get(gint *cols_id, gint uid, gint maxcol);
void homebank_pref_setdefault_lst_ope_columns(void);
void homebank_pref_setdefault_lst_det_columns(void);
void homebank_pref_setdefault_lst_sch_columns(void);
void homebank_pref_setdefault_win(void);
void homebank_prefs_set_default(void);
void homebank_pref_free(void);
void homebank_pref_createformat(void);
void homebank_pref_init_measurement_units(void);
void homebank_pref_icon_symbolic(gboolean active);
void homebank_pref_apply_scheme(void);
void homebank_pref_apply(void);
gboolean homebank_pref_load(void);
gboolean homebank_pref_save(void);
void homebank_pref_setdefault(void);
#endif
homebank-5.9.7/src/ui-assign.h 0000644 0001750 0001750 00000005662 14736461415 015475 0 ustar franam franam /* HomeBank -- Free, easy, personal rulounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ASSIGN_GTK_H__
#define __HB_ASSIGN_GTK_H__
enum {
LST_DEFASG_SORT_POS = 1,
LST_DEFASG_SORT_SEARCH,
LST_DEFASG_SORT_PAYEE,
LST_DEFASG_SORT_CATEGORY,
LST_DEFASG_SORT_PAYMENT,
LST_DEFASG_SORT_TAGS,
LST_DEFASG_SORT_NOTES
};
enum
{
LST_DEFASG_TOGGLE,
LST_DEFASG_DATAS,
NUM_LST_DEFASG
};
struct ui_asg_dialog_data
{
//GList *tmp_list;
Assign *asgitem;
gint change;
GtkWidget *dialog;
GtkWidget *GR_condition;
GtkWidget *CY_field;
GtkWidget *ST_search;
GtkWidget *GR_wrntxt, *LB_wrntxt;
GtkWidget *CM_exact;
GtkWidget *CM_re;
GtkWidget *CM_amount, *LB_amount, *ST_amount;
GtkWidget *CM_pay, *CM_payovw;
GtkWidget *LB_pay, *PO_pay;
GtkWidget *CM_cat, *CM_catovw;
GtkWidget *LB_cat, *PO_cat;
GtkWidget *CM_mod, *CM_modovw;
GtkWidget *LB_mod, *NU_mod;
GtkWidget *CM_tags, *CM_tagsovw;
GtkWidget *LB_tags, *ST_tags, *CY_tags;
GtkWidget *GR_misc;
GtkWidget *ST_notes;
};
struct ui_asg_manage_data
{
GList *tmp_list;
gint change;
GtkWidget *dialog;
gboolean mapped_done;
GtkWidget *ST_search;
GtkWidget *LV_rul;
GtkWidget *BT_add, *BT_rem, *BT_edit, *BT_dup, *BT_up, *BT_down, *BT_move;
GtkWidget *MB_moveto, *ST_poppos, *BT_popmove;
};
struct rulPopContext
{
GtkTreeModel *model;
guint except_key;
};
gchar *ui_asg_comboboxentry_get_name(GtkComboBox *entry_box);
guint32 ui_asg_comboboxentry_get_key(GtkComboBox *entry_box);
gboolean ui_asg_comboboxentry_set_active(GtkComboBox *entry_box, guint32 key);
void ui_asg_comboboxentry_add(GtkComboBox *entry_box, Assign *asg);
void ui_asg_comboboxentry_populate(GtkComboBox *entry_box, GHashTable *hash);
void ui_asg_comboboxentry_populate_except(GtkComboBox *entry_box, GHashTable *hash, guint except_key);
GtkWidget *ui_asg_comboboxentry_new(GtkWidget *label);
/* = = = = = = = = = = */
void ui_asg_listview_add(GtkTreeView *treeview, Assign *item);
guint32 ui_asg_listview_get_selected_key(GtkTreeView *treeview);
void ui_asg_listview_remove_selected(GtkTreeView *treeview);
void ui_asg_listview_populate(GtkWidget *view, gchar *needle);
GtkWidget *ui_asg_listview_new(gboolean withtoggle);
/* = = = = = = = = = = */
GtkWidget *ui_asg_manage_dialog (void);
#endif
homebank-5.9.7/src/hb-filter.h 0000644 0001750 0001750 00000013515 15121277341 015436 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_FILTER_H__
#define __HB_FILTER_H__
/*
** filter options
*/
enum
{
FLT_OFF,
FLT_INCLUDE,
FLT_EXCLUDE
};
enum
{
FLT_GRP_DATE,
FLT_GRP_CATEGORY,
FLT_GRP_PAYEE,
FLT_GRP_ACCOUNT,
FLT_GRP_TAG,
FLT_GRP_TEXT,
FLT_GRP_AMOUNT,
FLT_GRP_PAYMODE,
FLT_GRP_STATUS,
FLT_GRP_TYPE,
FLT_GRP_MAX
};
enum
{
OLD56_FLT_RANGE_THISMONTH = 0,
OLD56_FLT_RANGE_LASTMONTH = 1,
OLD56_FLT_RANGE_THISQUARTER = 2,
OLD56_FLT_RANGE_LASTQUARTER = 3,
OLD56_FLT_RANGE_THISYEAR = 4,
OLD56_FLT_RANGE_LASTYEAR = 5, //was not existing on 4.5
// 6 separator
OLD56_FLT_RANGE_LAST30DAYS = 7,
OLD56_FLT_RANGE_LAST60DAYS = 8,
OLD56_FLT_RANGE_LAST90DAYS = 9,
OLD56_FLT_RANGE_LAST12MONTHS = 10,
// 11 separator
OLD56_FLT_RANGE_OTHER = 12,
// 13 separator
OLD56_FLT_RANGE_ALLDATE = 14
};
enum
{
FLT_RANGE_UNSET = 0,
FLT_RANGE_MISC_CUSTOM,
FLT_RANGE_MISC_ALLDATE,
FLT_RANGE_MISC_30DAYS,
FLT_RANGE_LAST_DAY = 20,
FLT_RANGE_LAST_WEEK,
FLT_RANGE_LAST_FORTNIGHT,
FLT_RANGE_LAST_MONTH,
FLT_RANGE_LAST_QUARTER,
FLT_RANGE_LAST_YEAR,
FLT_RANGE_LAST_30DAYS,
FLT_RANGE_LAST_60DAYS,
FLT_RANGE_LAST_90DAYS,
FLT_RANGE_LAST_12MONTHS,
FLT_RANGE_LAST_6MONTHS,
FLT_RANGE_THIS_DAY = 40,
FLT_RANGE_THIS_WEEK,
FLT_RANGE_THIS_FORTNIGHT,
FLT_RANGE_THIS_MONTH,
FLT_RANGE_THIS_QUARTER,
FLT_RANGE_THIS_YEAR,
FLT_RANGE_NEXT_DAY = 60,
FLT_RANGE_NEXT_WEEK,
FLT_RANGE_NEXT_FORTNIGHT,
FLT_RANGE_NEXT_MONTH,
FLT_RANGE_NEXT_QUARTER,
FLT_RANGE_NEXT_YEAR,
FLT_RANGE_TODATE_YEAR = 80,
FLT_RANGE_TODATE_MONTH,
FLT_RANGE_TODATE_ALL,
FLT_RANGE_MAX
};
enum
{
FLT_TYPE_ALL = 0,
// 1 separator
FLT_TYPE_EXPENSE = 2,
FLT_TYPE_INCOME = 3,
FLT_TYPE_INTXFER = 4,
FLT_TYPE_NOTXFER = 5,
};
enum
{
FLT_STATUS_ALL = 0,
// 1 separator
FLT_STATUS_CLEARED = 2,
FLT_STATUS_UNCLEARED = 3,
FLT_STATUS_RECONCILED = 4,
FLT_STATUS_UNRECONCILED = 5,
// 6 separator
FLT_STATUS_UNCATEGORIZED = 7,
//5.9
FLT_STATUS_UNAPPROVED = 8,
};
enum
{
FLT_QSEARCH_MEMO = 1<<0,
FLT_QSEARCH_NUMBER = 1<<1,
FLT_QSEARCH_PAYEE = 1<<2,
FLT_QSEARCH_CATEGORY = 1<<3,
FLT_QSEARCH_TAGS = 1<<4,
FLT_QSEARCH_AMOUNT = 1<<5
};
struct _filter
{
guint32 key;
//gushort flags;
gchar *name;
gshort option[FLT_GRP_MAX];
gint range;
guint32 mindate, maxdate;
//gint rawtype, rawstatus;
//gboolean typ_exp, typ_inc, typ_xfr; //5.6
gboolean typ_nexp, typ_ninc, typ_xexp, typ_xinc; //5.8
gboolean sta_non, sta_clr, sta_rec; //5.6
gboolean paymode[NUM_PAYMODE_MAX];
gdouble minamount, maxamount;
gboolean exact;
//pointer here
gchar *number; //old info < 5.8
gchar *memo;
GArray *gbacc;
GArray *gbpay;
GArray *gbcat;
GArray *gbtag;
/* unsaved datas */
gshort n_active;
gshort n_item[FLT_GRP_MAX];
gint type; //register combobox type
gint status;
gint nbchanges;
gint nbdaysfuture;
gboolean forceadd;
gboolean forcechg;
gboolean forceremind;
gboolean forcevoid;
gchar last_tab[8]; /* keep last active tab */
};
Filter *da_flt_malloc(void);
void da_flt_free(Filter *flt);
void da_flt_copy(Filter *src, Filter *dst);
void da_flt_destroy(void);
void da_flt_new(void);
void da_flt_count_item(Filter *flt);
guint da_flt_length(void);
gboolean da_flt_create_none(void);
gboolean da_flt_remove(guint32 key);
gboolean da_flt_insert(Filter *item);
gboolean da_flt_append(Filter *item);
guint32 da_flt_get_max_key(void);
Filter *da_flt_get_by_name(gchar *name);
Filter *da_flt_get_by_imp_name(gchar *name);
Filter *da_flt_get(guint32 key);
void da_flt_consistency(Filter *item);
guint da_flt_status_acc_set(Filter *flt, guint32 kacc, gboolean status);
guint da_flt_status_pay_set(Filter *flt, guint32 kpay, gboolean status);
guint da_flt_status_cat_set(Filter *flt, guint32 kcat, gboolean status);
guint da_flt_status_tag_set(Filter *flt, guint32 ktag, gboolean status);
gboolean da_flt_status_acc_get(Filter *flt, guint32 kacc);
gboolean da_flt_status_pay_get(Filter *flt, guint32 kpay);
gboolean da_flt_status_cat_get(Filter *flt, guint32 kcat);
gboolean da_flt_status_tag_get(Filter *flt, guint32 ktag);
GList *filter_glist_sorted(gint column);
void filter_status_acc_clear_except(Filter *flt, guint32 selkey);
void filter_status_pay_clear_except(Filter *flt, guint32 selkey);
void filter_status_cat_clear_except(Filter *flt, guint32 selkey);
void filter_reset(Filter *flt);
void filter_preset_daterange_set(Filter *flt, gint range, guint32 kacc);
void filter_preset_type_set(Filter *flt, gint type, gint mode);
gboolean filter_preset_daterange_future_enable(Filter *flt, gint range);
guint32 filter_get_maxdate_forecast(Filter *filter);
void filter_preset_daterange_add_futuregap(Filter *filter, gboolean usrfuture);
void filter_set_tag_by_id(Filter *flt, guint32 key);
void filter_preset_status_set(Filter *flt, gint value);
gchar *filter_daterange_text_get(Filter *flt);
gchar *filter_text_summary_get(Filter *flt);
gboolean filter_txn_search_match(gchar *needle, Transaction *txn, gint flags);
gboolean filter_tpl_search_match(gchar *needle, Archive *arc);
gint filter_acc_match(Filter *flt, Account *acc);
gint filter_txn_match(Filter *flt, Transaction *ope);
#endif
homebank-5.9.7/src/ui-budget.h 0000644 0001750 0001750 00000003033 14736461415 015451 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_BUDGET_GTK_H__
#define __HB_BUDGET_GTK_H__
enum
{
COL_NAME = 0,
COL_OLDINDEX,
NUM_COLS
};
#define FIELD_TYPE 15
struct ui_bud_manage_data
{
GList *tmp_list;
gint change;
Category *lastcatitem;
GtkWidget *dialog;
GActionGroup * actions;
gboolean mapped_done;
GtkWidget *LV_cat;
GtkWidget *BT_expand;
GtkWidget *BT_collapse;
GtkWidget *RA_type;
GtkWidget *label_budget;
GtkWidget *CM_type[2];
GtkWidget *label[13]; //0 index is for All (not displayed)
GtkWidget *spinner[13]; //0 index is for All
GtkWidget *label_options;
GtkWidget *CM_force;
GtkWidget *TX_totexp, *TX_totinc, *TX_totbal;
GtkWidget *BT_clear;
Category *cat;
gdouble totexp, totinc;
};
GtkWidget *ui_bud_manage_dialog (void);
#endif
homebank-5.9.7/src/ui-archive.h 0000644 0001750 0001750 00000003220 14766240166 015617 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ARCHIVE_GTK_H__
#define __HB_ARCHIVE_GTK_H__
enum {
HID_ARC_MEMO,
HID_ARC_VALID,
HID_ARC_REMIND,
HID_ARC_MAX
};
struct ui_arc_manage_data
{
GtkWidget *dialog;
gboolean mapped_done;
GList *tmp_list;
gint change;
Archive *ext_arc;
GtkWidget *BT_typsch, *BT_typtpl;
GtkWidget *ST_search;
GtkWidget *LV_arc;
GtkWidget *BT_add, *BT_rem, *BT_edit, *BT_dup, *BT_schedule;
//recurrent popover
GtkWidget *PO_recurrent, *GR_recurrent;
GtkWidget *SW_recurrent;
GtkWidget *RA_rec_freq;
GtkWidget *LB_next, *PO_next;
GtkWidget *IM_wrnwe;
GtkWidget *LB_rec_every, *NB_rec_every, *LB_rec_every2;
GtkWidget *CM_relative, *LB_relative, *CY_ordinal, *CY_weekday;
GtkWidget *LB_weekend, *CY_weekend;
GtkWidget *EX_options;
GtkWidget *CM_limit;
GtkWidget *NB_limit;
GtkWidget *LB_posts;
};
GtkWidget *ui_arc_manage_dialog (Archive *ext_arc);
#endif
homebank-5.9.7/src/gtk-chart-colors.c 0000664 0001750 0001750 00000027032 14736461407 016752 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "gtk-chart-colors.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
char *chart_colors[] =
{
"HomeBank",
"Money",
"SAP",
"Quicken",
"Office 2010",
"Office 2013",
"Analytics",
"YNAB",
"Quicken 2017",
"Mint",
"Material",
"Nord",
NULL
};
struct rgbcol nord_colors[] =
{
{ 191, 97, 106 },
{ 235, 203, 139 },
{ 163, 190, 140 },
{ 94, 129, 172 },
{ 180, 142, 173 },
{ 193, 158, 102 }, /* added*/
{ 208, 135, 112 },
{ 129, 161, 193 },
{ 238, 187, 119 }, /* added */
{ 143, 188, 187 },
{ 150, 150, 150 } //grey/other
};
int nord_nbcolors = G_N_ELEMENTS(nord_colors);
struct rgbcol material_colors[] =
{
{ 244, 67, 54 },
{ 63, 81, 181 },
{ 0, 150, 136 },
{ 255, 235, 59 },
{ 233, 30, 99 },
{ 33, 150, 243 },
{ 76, 175, 80 },
{ 255, 193, 7 },
{ 156, 39, 176 },
{ 3, 169, 244 },
{ 139, 195, 74 },
{ 255, 152, 0 },
{ 103, 58, 183 },
{ 0, 188, 212 },
{ 205, 220, 57 },
{ 255, 87, 34 },
//{ 121, 85, 72 },
//{ 158, 158, 158 },
//{ 96, 125, 139 },
};
int material_nbcolors = G_N_ELEMENTS(material_colors);
struct rgbcol mint_colors[] =
{
{ 255, 207, 64 },
{ 255, 171, 64 },
{ 69, 202, 230 },
{ 22, 126, 230 },
{ 241, 132, 212 },
{ 242, 72, 72 },
{ 166, 237, 94 },
{ 10, 198, 117 },
{ 24, 158, 124 },
{ 188, 146, 251 },
{ 122, 71, 215 },
{ 255, 255, 139 },
{ 159, 159, 159 } //grey/other
};
int mint_nbcolors = G_N_ELEMENTS(mint_colors);
struct rgbcol quicken2017_colors[] =
{
{ 64, 191, 117 },
{ 119, 131, 255 },
{ 214, 137, 251 },
{ 255, 156, 78 },
{ 28, 168, 221 },
{ 255, 116, 162 },
{ 78, 223, 225 },
{ 85, 120, 195 },
{ 182, 198, 71 },
{ 255, 85, 108 },
{ 113, 113, 113 } //grey/other
};
int quicken2017_nbcolors = G_N_ELEMENTS(quicken2017_colors);
struct rgbcol ynab_colors[] =
{
{ 234, 84, 57 },
{ 243, 173, 81 },
{ 235, 229, 152 },
{ 116, 169, 230 },
{ 225, 248, 129 },
{ 139, 161, 87 },
{ 145, 197, 180 },
{ 0, 157, 174 },
{ 203, 219, 60 },
{ 228, 211, 84 },
{ 128, 133, 233 },
{ 247, 163, 92 },
{ 164, 192, 208 },
{ 124, 181, 236 },
{ 199, 246, 190 },
{ 105, 106, 105 } //grey/other
};
int ynab_nbcolors = G_N_ELEMENTS(ynab_colors);
struct rgbcol money_colors[] =
{
{ 255, 193, 96 },
{ 92, 131, 180 },
{ 165, 88, 124 },
{ 108, 124, 101 },
{ 230, 121, 99 },
{ 91, 160, 154 },
{ 207, 93, 96 },
{ 70, 136, 106 },
{ 245, 163, 97 },
{ 158, 153, 88 },
{ 255, 140, 90 },
{ 122, 151, 173 },
{ 84, 142, 128 },
{ 185, 201, 149 },
{ 165, 99, 103 },
{ 77, 140, 172 },
{ 251, 228, 128 },
{ 73, 99, 149 },
{ 192, 80, 77 },
{ 139, 180, 103 },
{ 132, 165, 214 },
{ 221, 216, 115 },
{ 77, 103, 137 },
{ 165, 181, 156 }
//grey/other
};
int money_nbcolors = G_N_ELEMENTS(money_colors);
struct rgbcol quicken_colors[] =
{
{ 226, 73, 13 },
{ 223, 180, 6 },
{ 124, 179, 0 },
{ 44, 108, 182 },
{ 184, 81, 186 },
{ 165, 165, 165 },
{ 122, 122, 122 },
{ 137, 42, 40 },
{ 70, 161, 100 },
{ 220, 106, 0 },
{ 113, 113, 113 } //grey/other
};
int quicken_nbcolors = G_N_ELEMENTS(quicken_colors);
struct rgbcol analytics_colors[] =
{
{ 5, 141, 199 }, //line color
{ 80, 180, 50 },
{ 237, 86, 27 },
{ 237, 239, 0 },
{ 36, 203, 229 },
{ 100, 229, 114 },
{ 255, 150, 85 },
{ 255, 242, 99 },
{ 106, 249, 196 },
{ 178, 222, 255 },
{ 204, 204, 204 } //grey/other
};
int analytics_nbcolors = G_N_ELEMENTS(analytics_colors);
struct rgbcol office2010_colors[] =
{
{ 60, 100, 149 },
{ 150, 60, 59 },
{ 120, 147, 68 },
{ 99, 75, 123 },
{ 61, 133, 157 },
{ 196, 115, 49 },
{ 73, 120, 176 },
{ 179, 74, 71 },
{ 144, 178, 84 },
{ 117, 93, 153 },
{ 73, 161, 185 },
{ 232, 140, 65 },
{ 126, 155, 199 },
{ 202, 126, 126 },
{ 174, 197, 129 },
{ 156, 137, 182 },
{ 123, 185, 206 },
{ 248, 170, 121 }
//grey/other
};
int office2010_nbcolors = G_N_ELEMENTS(office2010_colors);
struct rgbcol office2013_colors[] =
{
{ 91, 155, 213 },
{ 237, 125, 49 },
{ 165, 165, 165 },
{ 255, 192, 0 },
{ 68, 114, 196 },
{ 112, 173, 71 },
{ 37, 94, 145 },
{ 158, 72, 14 },
{ 99, 99, 99 },
{ 153, 115, 0 },
{ 38, 68, 120 },
{ 67, 104, 43 },
{ 124, 175, 221 },
{ 241, 151, 90 },
{ 183, 183, 183 },
{ 255, 205, 51 },
{ 105, 142, 208 },
{ 140, 193, 104 }
//grey/other
};
int office2013_nbcolors = G_N_ELEMENTS(office2013_colors);
struct rgbcol sap_colors[] =
{
{ 107, 148, 181 },
{ 239, 205, 120 },
{ 160, 117, 146 },
{ 107, 181, 144 },
{ 237, 164, 112 },
{ 107, 106, 161 },
{ 183, 213, 104 },
{ 214, 128, 118 },
{ 135, 115, 161 },
{ 218, 217, 86 },
{ 207, 111, 122 },
{ 85, 168, 161 },
{ 253, 213, 65 },
{ 146, 98, 148 },
{ 115, 192, 59 },
{ 205, 81, 96 },
{ 53, 180, 201 },
{ 248, 175, 103 },
{ 186, 97, 125 },
{ 117, 202, 249 },
{ 244, 131, 35 },
{ 178, 45, 110 },
{ 87, 229, 151 },
{ 204, 171, 68 },
{ 172, 110, 145 },
{ 61, 132, 137 },
{ 224, 117, 79 },
{ 117, 84, 148 },
{ 155, 206, 158 },
{ 255, 133, 100 },
{ 60, 98, 153 },
{ 128, 197, 122 }
};
int sap_nbcolors = G_N_ELEMENTS(sap_colors);
struct rgbcol homebank_colors[] =
{
{ 72, 118, 176 },
{ 180, 198, 230 },
{ 227, 126, 35 },
{ 238, 186, 123 },
{ 97, 158, 58 },
{ 175, 222, 142 },
{ 184, 43, 44 },
{ 231, 151, 149 },
{ 136, 103, 185 },
{ 190, 174, 210 },
{ 127, 87, 77 },
{ 184, 155, 147 },
{ 202, 118, 190 },
{ 230, 181, 208 },
{ 126, 126, 126 },
{ 198, 198, 198 },
{ 187, 188, 56 },
{ 218, 218, 144 },
{ 109, 189, 205 },
{ 176, 217, 228 },
{ 237, 212, 0 },
{ 255, 239, 101 },
{ 207, 93, 96 },
{ 234, 186, 187 },
{ 193, 124, 17 },
{ 240, 181, 90 },
{ 186, 189, 182 },
{ 225, 227, 223 },
{ 115, 210, 22 },
{ 175, 240, 112 },
{ 255, 140, 90 },
{ 255, 191, 165 }
};
int homebank_nbcolors = G_N_ELEMENTS(homebank_colors);
struct rgbcol global_colors[] =
{
{ 0, 0, 0}, // black
{255, 255, 255}, // white
{239, 239, 239}, // grey1 THTEXT 0.05
{ 68, 68, 68}, // text THTEXT 0.78
{ 51, 51, 51}, // xyline THTEXT 0.8
/* { 255, 0, 0}, // fake
{ 255, 255, 0}, // fake
{ 255, 0, 255}, // fake
{ 0, 255, 0}, // fake
{ 0, 0, 255}, // fake
*/
{255, 255, 255}, // theme base (bg)
{ 46, 52, 54}, // theme fg
};
/*
struct rgbcol global_colors[] =
{
{ 0, 0, 0}, // black
{255, 255, 255}, // white
{238, 238, 238}, // #top/bottom lines
{204, 204, 204}, // #dotted lines
{102, 102, 102}, // #x-axis, scale text
{153, 153, 153}, // # ??
{ 0, 119, 204}, // #line color
//new
{239, 239, 239}, // intermediate lines
{ 68, 68, 68}, // text
{ 51, 51, 51}, // x/y axis
};*/
void chart_color_global_default(void)
{
struct rgbcol *tcol;
// set base color (adwaita)
tcol = &global_colors[THBASE];
tcol->r = 255;
tcol->g = 255;
tcol->b = 255;
// set text(bg) color (adwaita)
tcol = &global_colors[THTEXT];
tcol->r = 46;
tcol->g = 52;
tcol->b = 54;
}
void cairo_user_set_rgbcol(cairo_t *cr, struct rgbcol *col)
{
cairo_set_source_rgb(cr, COLTOCAIRO(col->r), COLTOCAIRO(col->g), COLTOCAIRO(col->b));
}
void cairo_user_set_rgbacol(cairo_t *cr, struct rgbcol *col, double alpha)
{
cairo_set_source_rgba(cr, COLTOCAIRO(col->r), COLTOCAIRO(col->g), COLTOCAIRO(col->b), alpha);
}
void cairo_user_set_rgbacol_over(cairo_t *cr, struct rgbcol *col, gboolean over, double alpha)
{
if( over )
cairo_set_source_rgba(cr, COLTOCAIROOVER(col->r), COLTOCAIROOVER(col->g), COLTOCAIROOVER(col->b), alpha);
else
cairo_set_source_rgba(cr, COLTOCAIRO(col->r), COLTOCAIRO(col->g), COLTOCAIRO(col->b), alpha);
}
void cairo_user_set_rgbcol_over(cairo_t *cr, struct rgbcol *col, gboolean over)
{
if( over )
cairo_set_source_rgb(cr, COLTOCAIROOVER(col->r), COLTOCAIROOVER(col->g), COLTOCAIROOVER(col->b));
else
cairo_set_source_rgb(cr, COLTOCAIRO(col->r), COLTOCAIRO(col->g), COLTOCAIRO(col->b));
}
void colorsheme_col8_to_rgba(struct rgbcol *col8, GdkRGBA *rgba)
{
DB( g_print(" set %d %d %d\n", col8->r, col8->g, col8->b) );
rgba->red = COLTOCAIRO((double)col8->r);
rgba->green = COLTOCAIRO((double)col8->g);
rgba->blue = COLTOCAIRO((double)col8->b);
rgba->alpha = 1.0;
DB( g_print(" set %f %f %f\n", rgba->red, rgba->green, rgba->blue) );
}
void colorscheme_init(GtkColorScheme *scheme, gint index)
{
DB( g_print("\n[chart] scheme init\n") );
scheme->cs_blue = 0;
switch(index)
{
default:
case CHART_COLMAP_HOMEBANK:
scheme->colors = homebank_colors;
scheme->nb_cols = homebank_nbcolors;
scheme->cs_green = 4;
scheme->cs_red = 6;
scheme->cs_orange = 2;
break;
case CHART_COLMAP_MSMONEY:
scheme->colors = money_colors;
scheme->nb_cols = money_nbcolors;
scheme->cs_blue = 17;
scheme->cs_green = 19;
scheme->cs_red = 18;
scheme->cs_orange = 8;
break;
case CHART_COLMAP_QUICKEN:
scheme->colors = quicken_colors;
scheme->nb_cols = quicken_nbcolors;
scheme->cs_blue = 3;
scheme->cs_green = 2;
scheme->cs_red = 0;
scheme->cs_orange = 9;
break;
case CHART_COLMAP_ANALYTICS:
scheme->colors = analytics_colors;
scheme->nb_cols = analytics_nbcolors;
scheme->cs_green = 1;
scheme->cs_red = 2;
scheme->cs_orange = 6;
break;
case CHART_COLMAP_OFFICE2010:
scheme->colors = office2010_colors;
scheme->nb_cols = office2010_nbcolors;
scheme->cs_green = 2;
scheme->cs_red = 1;
scheme->cs_orange = 5;
break;
case CHART_COLMAP_OFFICE2013:
scheme->colors = office2013_colors;
scheme->nb_cols = office2013_nbcolors;
scheme->cs_green = 5;
scheme->cs_red = 1;
scheme->cs_orange = 1;
break;
case CHART_COLMAP_SAP:
scheme->colors = sap_colors;
scheme->nb_cols = sap_nbcolors;
scheme->cs_green = 14;
scheme->cs_red = 15;
scheme->cs_orange = 20;
break;
case CHART_COLMAP_YNAB:
scheme->colors = ynab_colors;
scheme->nb_cols = ynab_nbcolors;
scheme->cs_blue = 3;
scheme->cs_green = 5;
scheme->cs_red = 0;
scheme->cs_orange = 1;
break;
case CHART_COLMAP_QUICKEN2017:
scheme->colors = quicken2017_colors;
scheme->nb_cols = quicken2017_nbcolors;
scheme->cs_blue = 7;
scheme->cs_green = 0;
scheme->cs_red = 9;
scheme->cs_orange = 3;
break;
case CHART_COLMAP_MINT:
scheme->colors = mint_colors;
scheme->nb_cols = mint_nbcolors;
scheme->cs_blue = 3;
scheme->cs_green = 7;
scheme->cs_red = 5;
scheme->cs_orange = 1;
break;
case CHART_COLMAP_MATERIAL:
scheme->colors = material_colors;
scheme->nb_cols = material_nbcolors;
scheme->cs_blue = 5;
scheme->cs_green = 6;
scheme->cs_red = 0;
scheme->cs_orange = 7;
break;
case CHART_COLMAP_NORD:
scheme->colors = nord_colors;
scheme->nb_cols = nord_nbcolors;
scheme->cs_blue = 3;
scheme->cs_green = 2;
scheme->cs_red = 0;
scheme->cs_orange = 6;
break;
}
}
homebank-5.9.7/src/rep-vehicle.h 0000644 0001750 0001750 00000003733 14736461415 015776 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_REPVEHICLE_H__
#define __HOMEBANK_REPVEHICLE_H__
enum {
HID_REPVEHICLE_MINDATE,
HID_REPVEHICLE_MAXDATE,
HID_REPVEHICLE_RANGE,
HID_REPVEHICLE_VEHICLE,
MAX_REPVEHICLE_HID
};
enum {
CAR_RES_METER = 1,
CAR_RES_FUEL,
CAR_RES_FUELCOST,
CAR_RES_OTHERCOST,
CAR_RES_TOTALCOST,
MAX_CAR_RES
};
struct repvehicle_data
{
GQueue *txn_queue;
GList *vehicle_list;
Filter *filter;
guint total_dist;
gdouble total_fuel;
gdouble total_fuelcost;
gdouble total_misccost;
GtkWidget *window;
GActionGroup *actions;
gboolean mapped_done;
GtkWidget *TB_bar;
GtkWidget *BT_refresh;
GtkWidget *BT_export;
//GtkWidget *TX_info;
GtkWidget *CM_minor;
GtkWidget *LV_report;
GtkWidget *PO_cat;
GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *CY_range;
GtkWidget *GR_result;
GtkWidget *LA_avera[MAX_CAR_RES];
GtkWidget *LA_total[MAX_CAR_RES];
gulong handler_id[MAX_REPVEHICLE_HID];
};
//extern gchar *CYA_FLT_SELECT[];
/* list stat */
enum
{
LST_CAR_DATE,
LST_CAR_MEMO,
LST_CAR_METER,
LST_CAR_FUEL,
LST_CAR_PRICE,
LST_CAR_AMOUNT,
LST_CAR_DIST,
LST_CAR_100KM,
LST_CAR_DISTBYVOL,
LST_CAR_PARTIAL,
NUM_LST_CAR
};
GtkWidget *repvehicle_window_new(void);
#endif
homebank-5.9.7/src/hb-export.c 0000644 0001750 0001750 00000050665 14736464657 015517 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-export.h"
#include "list-operation.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = */
static void hb_export_qif_elt_txn(GIOChannel *io, Account *acc, gboolean allxfer)
{
GString *elt;
GList *list;
GDate *date;
char amountbuf[G_ASCII_DTOSTR_BUF_SIZE];
gchar *sbuf;
gint count, i;
elt = g_string_sized_new(255);
date = g_date_new ();
list = g_queue_peek_head_link(acc->txn_queue);
while (list != NULL)
{
Transaction *txn = list->data;
Payee *payee;
Category *cat;
//#1915609 qif export of multiple account double xfer line
if( (allxfer == FALSE) && (txn->flags & OF_INTXFER) && txn->amount > 0 )
goto nexttxn;
g_date_set_julian (date, txn->date);
//#1270876
switch(PREFS->dtex_datefmt)
{
case PRF_DATEFMT_MDY: //"m-d-y"
g_string_append_printf (elt, "D%02d/%02d/%04d\n",
g_date_get_month(date),
g_date_get_day(date),
g_date_get_year(date)
);
break;
case PRF_DATEFMT_DMY: //"d-m-y"
g_string_append_printf (elt, "D%02d/%02d/%04d\n",
g_date_get_day(date),
g_date_get_month(date),
g_date_get_year(date)
);
break;
case PRF_DATEFMT_YMD: //"y-m-d"
g_string_append_printf (elt, "D%04d/%02d/%02d\n",
g_date_get_year(date),
g_date_get_month(date),
g_date_get_day(date)
);
break;
}
//g_ascii_dtostr (amountbuf, sizeof (amountbuf), txn->amount);
g_ascii_formatd (amountbuf, sizeof (amountbuf), "%.2f", txn->amount);
g_string_append_printf (elt, "T%s\n", amountbuf);
//#2051307 to prevent ? v to be exported as qif
sbuf = "";
if( txn->status == TXN_STATUS_CLEARED || txn->status == TXN_STATUS_RECONCILED)
transaction_get_status_string(txn);
g_string_append_printf (elt, "C%s\n", sbuf);
if( txn->paymode == PAYMODE_CHECK)
g_string_append_printf (elt, "N%s\n", txn->number);
//Ppayee
payee = da_pay_get(txn->kpay);
if(payee)
g_string_append_printf (elt, "P%s\n", payee->name);
// Mmemo
g_string_append_printf (elt, "M%s\n", txn->memo);
// LCategory of transaction
// L[Transfer account name]
// LCategory of transaction/Class of transaction
// L[Transfer account]/Class of transaction
if( (txn->flags & OF_INTXFER) && (txn->kacc == acc->key) )
{
//#579260
Account *dstacc = da_acc_get(txn->kxferacc);
if(dstacc)
g_string_append_printf (elt, "L[%s]\n", dstacc->name);
}
else
{
cat = da_cat_get(txn->kcat);
if(cat)
{
g_string_append_printf (elt, "L%s\n", cat->fullname);
}
}
// splits
count = da_splits_length(txn->splits);
for(i=0;isplits, i);
cat = da_cat_get(s->kcat);
if(cat)
{
g_string_append_printf (elt, "S%s\n", cat->fullname);
}
g_string_append_printf (elt, "E%s\n", s->memo);
g_ascii_formatd (amountbuf, sizeof (amountbuf), "%.2f", s->amount);
g_string_append_printf (elt, "$%s\n", amountbuf);
}
g_string_append (elt, "^\n");
nexttxn:
list = g_list_next(list);
}
g_io_channel_write_chars(io, elt->str, -1, NULL, NULL);
g_string_free(elt, TRUE);
g_date_free(date);
}
static void hb_export_qif_elt_acc(GIOChannel *io, Account *acc)
{
GString *elt;
gchar *type;
elt = g_string_sized_new(255);
// account export
//#987144 fixed account type
switch(acc->type)
{
case ACC_TYPE_BANK : type = "Bank"; break;
case ACC_TYPE_CASH : type = "Cash"; break;
case ACC_TYPE_ASSET : type = "Oth A"; break;
case ACC_TYPE_CREDITCARD : type = "CCard"; break;
case ACC_TYPE_LIABILITY : type = "Oth L"; break;
default : type = "Bank"; break;
}
g_string_assign(elt, "!Account\n");
g_string_append_printf (elt, "N%s\n", acc->name);
g_string_append_printf (elt, "T%s\n", type);
g_string_append (elt, "^\n");
g_string_append_printf (elt, "!Type:%s\n", type);
g_io_channel_write_chars(io, elt->str, -1, NULL, NULL);
g_string_free(elt, TRUE);
}
void hb_export_qif_account_single(gchar *filename, Account *acc)
{
GIOChannel *io;
io = g_io_channel_new_file(filename, "w", NULL);
if(io == NULL)
{
g_message("file error on: %s", filename);
//retval = XML_IO_ERROR;
}
else
{
hb_export_qif_elt_acc(io, acc);
hb_export_qif_elt_txn(io, acc, TRUE);
g_io_channel_unref (io);
}
}
void hb_export_qif_account_all(gchar *filename)
{
GIOChannel *io;
GList *lacc, *list;
io = g_io_channel_new_file(filename, "w", NULL);
if(io == NULL)
{
g_message("file error on: %s", filename);
//retval = XML_IO_ERROR;
}
else
{
//5.5.1 save accounts in order
//lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
lacc = list = account_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Account *item = list->data;
hb_export_qif_elt_acc(io, item);
hb_export_qif_elt_txn(io, item, FALSE);
list = g_list_next(list);
}
g_list_free(lacc);
g_io_channel_unref (io);
}
}
/* = = = = = = = = = = = = = = = = = = = = */
#define HELPDRAW 0
#define HEX_R(xcol) (((xcol>>24) & 0xFF)/255)
#define HEX_G(xcol) (((xcol>>16) & 0xFF)/255)
#define HEX_B(xcol) (((xcol>> 8) & 0xFF)/255)
#if HELPDRAW == 1
static void hb_pdf_draw_help_rect(cairo_t *cr, gint32 xcol, double x, double y, double w, double h)
{
cairo_save (cr);
cairo_set_line_width(cr, 1.0);
cairo_set_source_rgba(cr, HEX_R(xcol), HEX_G(xcol), HEX_B(xcol), 1.0); //alpha is unused for now
cairo_rectangle (cr, x, y, w, h);
cairo_stroke(cr);
cairo_restore(cr);
}
#endif
// references
// https://www.blurb.com/blog/choosing-a-font-for-print-6-things-you-should-know/
// https://plumgroveinc.com/choosing-a-font-for-print-2/
#define HB_PRINT_FONT_HEAD_POINT 5
#define HB_PRINT_SPACING 6
typedef struct
{
gboolean statement;
gdouble font_size;
gchar *tabtext;
gchar *title;
gchar **lines;
gint header_height;
gint numpagerow;
gint numpagecol;
gint num_columns;
gint *col_width;
gint8 *col_align; //0 if right, 1 if left
gint8 *leftcols; //-1 terminated index of col left aligned
gint lines_per_page;
gint num_lines;
gint num_pages;
} PrintData;
static gint
hb_print_listview_get_idx_for_pagecol(PrintData *data, gint width, gint pagecol)
{
gint col, availw, numbreak;
//DB( g_print(" get col for pagerow %d\n", pagecol) );
availw = width;
numbreak = 1;
for(col=0 ; col < data->num_columns ; col++)
{
//DB( g_print(" ++ col=%d, curw=%d width=%d, numbrk=%d\n", col, currw, width, numbreak) );
if( numbreak >= pagecol )
break;
availw -= data->col_width[col];
// new page column ?
if( availw < data->col_width[col+1] )
{
//DB( g_print(" ++ --break--\n") );
numbreak++;
availw = width;
}
}
//DB( g_print(" return %d\n", col) );
return col;
}
static void
hb_print_listview_end_print (GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data)
{
PrintData *data = (PrintData *)user_data;
g_free(data->col_width);
g_free(data->col_align);
g_strfreev (data->lines);
g_free (data);
}
static void
hb_print_listview_begin_print (GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data)
{
PrintData *data = (PrintData *)user_data;
int i, j, count;
double width, height;
gchar **columns;
PangoLayout *layout;
PangoFontDescription *desc;
gint text_width, text_height, line_height;
DB( g_print("\n-- begin print --\n") );
width = gtk_print_context_get_width (context);
height = gtk_print_context_get_height (context);
line_height = data->font_size + 3;
layout = gtk_print_context_create_pango_layout (context);
desc = pango_font_description_from_string ("Helvetica");
//compute header height
pango_font_description_set_size (desc, (data->font_size + HB_PRINT_FONT_HEAD_POINT) * PANGO_SCALE);
pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
pango_layout_set_text (layout, data->title, -1);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
//1 line space + column title + spacer
data->header_height = text_height + (data->font_size * 2);
height -= data->header_height + (2 * HB_PRINT_SPACING);
data->lines = g_strsplit (data->tabtext, "\n", 0);
//todo: test if line > 1
//get number of column from title
columns = g_strsplit (data->lines[0], "\t", 0);
data->num_columns = g_strv_length(columns);
//debug
/*for(i=0;inum_columns;i++)
{
DB( g_print(" %02d: %s\n", i, columns[i]) );
}*/
g_strfreev (columns);
//alloc memory
data->col_width = g_malloc0 (sizeof(gint)*(data->num_columns + 1));
data->col_align = g_malloc0 (sizeof(gint8)*(data->num_columns + 1));
pango_font_description_set_size (desc, data->font_size * PANGO_SCALE);
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
DB( g_print(" - compute column width - \n") );
//TODO: maybe for text column we should remove outliers here
i = 0;
count = 0;
while (data->lines[i] != NULL)
{
DB( g_print(" eval line %03d: '%s'\n", i, data->lines[i]) );
//skip empty lines
if( strlen(data->lines[i]) > 1 )
{
columns = g_strsplit (data->lines[i], "\t", 0);
j = 0;
while (columns[j] != NULL)
{
pango_layout_set_text (layout, columns[j], -1);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
//DB( g_print(" %d : '%s' %d %d\n", j, columns[j], text_width, text_width / PANGO_SCALE ) );
//add a width
text_width += HB_PRINT_SPACING;
data->col_width[j] = MAX(data->col_width[j], text_width);
j++;
}
g_strfreev (columns);
count++;
}
else
{
DB( g_print(" skipped\n") );
}
i++;
}
g_object_unref (layout);
data->num_lines = count;
data->lines_per_page = floor (height / line_height);
data->numpagerow = (data->num_lines - 1) / data->lines_per_page + 1;
DB( g_print(" num_lines: %d\n", data->num_lines) );
DB( g_print(" lines_per_page: %d\n", data->lines_per_page) );
DB( g_print(" numpagerow: %d\n", data->numpagerow) );
DB( g_print(" num_colums: %d\n", data->num_columns) );
DB( g_print(" width: %f\n", width) );
//todo: clamp columns for statement
// (account) date info payee memo amount clr balance
//taken from 5.7.2
if( data->statement == TRUE )
{
gdouble tmp = width - data->col_width[0] - data->col_width[4] - data->col_width[5] - data->col_width[6];
DB( g_print("\n - statement on - \n") );
DB( g_print(" substract: %d %d %d %d\n", data->col_width[0] , data->col_width[4] , data->col_width[5] , data->col_width[6]) );
DB( g_print(" tmp %f\n", tmp) );
data->col_width[1] = tmp / 4; //info
data->col_width[2] = tmp / 4; //payee
data->col_width[3] = 2*tmp / 4; //memo
}
DB( g_print("\n - compute numpagecol - \n") );
data->numpagecol = 1;
gint availw = width;
for(i=0;inum_columns;i++)
{
availw -= data->col_width[i];
DB( g_print(" colw[%d]=%d, availw=%d\n", i, data->col_width[i], availw) );
// new page column ?
if( availw < data->col_width[i+1] )
{
DB( g_print(" --break--\n") );
data->numpagecol++;
availw = width;
}
}
DB( g_print("\n - assign column alignment - \n") );
//column 0 is left by default
data->col_align[0] = 1;
//affect left align columns
if( data->leftcols != NULL )
{
for(i=0;i<10;i++)
{
gint index = data->leftcols[i];
if( index == -1 )
break;
data->col_align[index] = 1;
DB( g_print(" column %d i left align\n", index) );
}
}
DB( g_print(" numpagecol: %d\n", data->numpagecol) );
data->num_pages = data->numpagerow * data->numpagecol;
DB( g_print(" num_pages:%d\n", data->num_pages) );
gtk_print_operation_set_n_pages (operation, data->num_pages);
}
static void hb_print_listview_draw_line(PrintData *data, gchar *line, gint firstcol, gint lastcol, gint y, cairo_t *cr, PangoLayout *layout)
{
gchar **columns;
gint text_width, text_height;
gint nbcol, j, x;
columns = g_strsplit (line, "\t", 0);
//#xxxxxxxx restrict to real column
nbcol = g_strv_length(columns);
DB( g_print(" draw line col %d to col %d, real is %d\n", firstcol, lastcol, nbcol ));
lastcol = MIN(lastcol, nbcol);
x = 0;
//for(j=0;jnum_columns;j++)
for(j=firstcol ; jcol_width[j] - HB_PRINT_SPACING) * PANGO_SCALE);
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
//do align: 0=right, 1=left
if( data->col_align[j] == 0 )
{
pango_layout_get_pixel_size (layout, &text_width, &text_height);
cairo_move_to(cr, x + data->col_width[j] - text_width - HB_PRINT_SPACING, y);
}
else
cairo_move_to(cr, x, y);
pango_cairo_show_layout (cr, layout);
x += data->col_width[j];
}
else
g_warning(" null print column %d", j);
}
g_strfreev (columns);
}
//print is done from left to right
//page 1&2 will be the first column to fit, then page 3&4 the other columns
static void
hb_print_listview_draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, gpointer user_data)
{
PrintData *data = (PrintData *)user_data;
cairo_t *cr;
PangoLayout *layout;
gint text_width, text_height;
gdouble width, height;
gint line, i, y;
gint pagecol;
PangoFontDescription *desc;
gchar *page_str;
GDate date;
gchar buffer[256];
double tmpval;
#if MYDEBUG == 1
gint pagerow = page_nr%data->numpagerow;
#endif
tmpval = (double)(page_nr+1)/(double)data->numpagerow;
pagecol = ceil(tmpval);
DB( g_print("\n-- draw page %d, pagerow=%d pagecol=%d (tmp=%f)\n", page_nr, pagerow, pagecol, tmpval) );
cr = gtk_print_context_get_cairo_context (context);
width = gtk_print_context_get_width (context);
height = gtk_print_context_get_height (context);
//helpdraw
#if HELPDRAW == 1
hb_pdf_draw_help_rect(cr, 0x0000FF00, 0, 0, width, 0 + data->header_height);
hb_pdf_draw_help_rect(cr, 0x00FFFF00, 0, 0 + data->header_height, width, height - (data->header_height + 9 + (2* HB_PRINT_SPACING)));
hb_pdf_draw_help_rect(cr, 0x00FF0000, 0, height - 9 - HB_PRINT_SPACING, width, 9 + HB_PRINT_SPACING);
#endif
/*
cairo_rectangle (cr, 0, 0, width, data->header_height);
cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
cairo_fill_preserve (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_set_line_width (cr, 1);
cairo_stroke (cr);
*/
//header
layout = gtk_print_context_create_pango_layout (context);
desc = pango_font_description_from_string ("Helvetica");
pango_font_description_set_size (desc, (data->font_size + HB_PRINT_FONT_HEAD_POINT) * PANGO_SCALE);
pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
pango_layout_set_text (layout, data->title, -1);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
if (text_width > width)
{
pango_layout_set_width (layout, width);
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_START);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
}
//left
cairo_move_to (cr, 0, 0);
//center
//cairo_move_to (cr, (width - text_width) / 2, 0);
pango_cairo_show_layout (cr, layout);
g_object_unref (layout);
layout = gtk_print_context_create_pango_layout (context);
desc = pango_font_description_from_string ("Helvetica");
pango_font_description_set_size (desc, data->font_size * PANGO_SCALE);
pango_layout_set_font_description (layout, desc);
gint firstcol = hb_print_listview_get_idx_for_pagecol(data, width, pagecol);
gint lastcol = hb_print_listview_get_idx_for_pagecol(data, width, pagecol+1);
//line header
y = data->header_height - data->font_size;
pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
pango_layout_set_font_description (layout, desc);
hb_print_listview_draw_line(data, data->lines[0], firstcol, lastcol, y, cr, layout);
//helpdraw
#if HELPDRAW == 1
gint x = 0;
gchar **columns = g_strsplit (data->lines[0], "\t", 0);
for(gint j=firstcol ; jcol_width[j] - HB_PRINT_SPACING, height - (data->header_height + 9 + (2* HB_PRINT_SPACING)) );
x += data->col_width[j];
}
}
g_strfreev (columns);
#endif
y = data->header_height + HB_PRINT_SPACING;
pango_font_description_set_weight (desc, PANGO_WEIGHT_NORMAL);
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
//lines
line = (page_nr%data->numpagerow) * data->lines_per_page;
if( line == 0 ) //skip title line
line++;
DB( g_print(" print lines from %d to %d (max)\n", line, line+data->lines_per_page) );
DB( g_print(" print cols from %d\n", firstcol) );
for (i = 0; i < data->lines_per_page && line < data->num_lines; i++)
{
hb_print_listview_draw_line(data, data->lines[line], firstcol, lastcol, y, cr, layout);
y += data->font_size + 3;
line++;
}
g_object_unref (layout);
//footer
layout = gtk_print_context_create_pango_layout (context);
desc = pango_font_description_from_string ("Helvetica");
pango_font_description_set_size (desc, 9 * PANGO_SCALE);
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
y = height - 9;
//left: date
g_date_set_julian (&date, GLOBALS->today);
g_date_strftime (buffer, 256-1, "%a %x", &date);
pango_layout_set_text (layout, buffer, -1);
pango_layout_set_width (layout, -1);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
cairo_move_to (cr, 0, y);
pango_cairo_show_layout (cr, layout);
//right: page
page_str = g_strdup_printf ("page %d/%d", page_nr + 1, data->num_pages);
pango_layout_set_text (layout, page_str, -1);
g_free (page_str);
pango_layout_set_width (layout, -1);
pango_layout_get_pixel_size (layout, &text_width, &text_height);
cairo_move_to (cr, width - text_width - 4, y);
pango_cairo_show_layout (cr, layout);
g_object_unref (layout);
}
//note: statement is hardcoded for account print only
void
hb_print_listview(GtkWindow *parent, gchar *tabtext, gint8 *leftcols, gchar *title, gchar *filepath, gboolean statement)
{
GtkPrintOperation *operation;
GtkPrintSettings *settings;
PrintData *data;
GError *error = NULL;
data = g_new0 (PrintData, 1);
data->statement = statement;
data->font_size = 12.0;
data->tabtext = tabtext;
data->title = title;
data->leftcols = leftcols;
DB( g_print("tabtext debug:\n%s\n", tabtext) );
operation = gtk_print_operation_new ();
g_signal_connect (G_OBJECT (operation), "begin-print", G_CALLBACK (hb_print_listview_begin_print), data);
g_signal_connect (G_OBJECT (operation), "draw-page" , G_CALLBACK (hb_print_listview_draw_page), data);
g_signal_connect (G_OBJECT (operation), "end-print" , G_CALLBACK (hb_print_listview_end_print), data);
gtk_print_operation_set_use_full_page (operation, FALSE);
gtk_print_operation_set_unit (operation, GTK_UNIT_POINTS);
gtk_print_operation_set_embed_page_setup (operation, TRUE);
settings = gtk_print_settings_new ();
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, filepath);
gtk_print_operation_set_print_settings (operation, settings);
gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW (parent), &error);
g_object_unref (operation);
g_object_unref (settings);
if (error)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", error->message);
g_error_free (error);
g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
gtk_widget_show (dialog);
}
}
homebank-5.9.7/src/list-operation.h 0000644 0001750 0001750 00000005246 14736461415 016545 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LIST_OPERATION__H__
#define __LIST_OPERATION__H__
enum {
LIST_TXN_TYPE_BOOK = 0,
LIST_TXN_TYPE_DETAIL,
LIST_TXN_TYPE_OTHER,
LIST_TXN_TYPE_XFERSOURCE,
LIST_TXN_TYPE_XFERTARGET
};
enum {
MODEL_TXN_POINTER = 0,
MODEL_TXN_SPLITAMT,
MODEL_TXN_SPLITPTR
};
enum {
LST_TXN_EXP_ACC = 1 << 0, //detail/print
LST_TXN_EXP_PMT = 1 << 1, //!print
LST_TXN_EXP_CLR = 1 << 2,
LST_TXN_EXP_CAT = 1 << 3,
LST_TXN_EXP_TAG = 1 << 4,
LST_TXN_EXP_BAL = 1 << 5
};
struct list_txn_data
{
GtkWidget *treeview;
GtkTreeViewColumn *tvc_balance;
gint list_type;
gboolean showall;
gboolean lockreconciled;
gboolean warnnocategory;
gboolean tvc_is_visible;
gboolean save_column_width;
gboolean life_energy;
};
GtkWidget *create_list_transaction(gint type, gboolean *pref_columns);
GtkWidget *create_list_import_transaction(gboolean enable_choose);
void list_txn_set_warn_nocategory(GtkTreeView *treeview, gboolean warn);
void list_txn_set_columns(GtkTreeView *treeview, gint *col_id);
void list_txn_get_columns(GtkTreeView *treeview);
gboolean list_txn_column_id_isvisible(GtkTreeView *treeview, gint sort_id);
void list_txn_set_column_acc_visible(GtkTreeView *treeview, gboolean visible);
void list_txn_set_life_energy(GtkTreeView *treeview, gboolean life_energy);
Transaction *list_txn_get_surround_transaction(GtkTreeView *treeview, Transaction **prev, Transaction **next);
Transaction *list_txn_get_active_transaction(GtkTreeView *treeview);
GString *list_txn_to_string(GtkTreeView *treeview, gboolean isclipboard, gboolean hassplit, gboolean selectonly, guint flags);
void list_txn_set_lockreconciled(GtkTreeView *treeview, gboolean lockreconciled);
void list_txn_set_save_column_width(GtkTreeView *treeview, gboolean save_column_width);
void list_txn_sort_force(GtkTreeSortable *sortable, gpointer user_data);
guint list_txn_get_quicksearch_column_mask(GtkTreeView *treeview);
#endif
homebank-5.9.7/src/hb-report.h 0000644 0001750 0001750 00000010064 14736461415 015470 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_REPORT_H__
#define __HB_REPORT_H__
typedef enum
{
REPORT_GRPBY_NONE,
REPORT_GRPBY_CATEGORY,
//REPORT_GRPBY_SUBCATEGORY,
REPORT_GRPBY_PAYEE,
REPORT_GRPBY_ACCOUNT,
REPORT_GRPBY_TAG,
REPORT_GRPBY_MONTH,
REPORT_GRPBY_YEAR,
REPORT_GRPBY_ACCGROUP, //5.7.3
REPORT_GRPBY_TYPE //5.8
} HbReportGrpBy;
typedef enum {
REPORT_TYPE_NONE,
REPORT_TYPE_ALL,
REPORT_TYPE_EXPENSE,
REPORT_TYPE_INCOME,
REPORT_TYPE_TOTAL
} HbReportType;
typedef enum
{
REPORT_INTVL_NONE,
REPORT_INTVL_DAY,
REPORT_INTVL_WEEK,
REPORT_INTVL_FORTNIGHT,
REPORT_INTVL_MONTH,
REPORT_INTVL_QUARTER,
REPORT_INTVL_HALFYEAR,
REPORT_INTVL_YEAR
} HbReportIntvl;
typedef enum
{
REPORT_RESULT_TOTAL,
REPORT_RESULT_CUMUL,
REPORT_RESULT_BALANCE
} HbReportResult;
//5.8 compute option flags
typedef enum {
REPORT_COMP_FLG_NONE = 0,
REPORT_COMP_FLG_CATSIGN = 1 << 1,
REPORT_COMP_FLG_SPENDING = 1 << 2,
REPORT_COMP_FLG_REVENUE = 1 << 3,
REPORT_COMP_FLG_BALANCE = 1 << 8,
REPORT_COMP_FLG_FORECAST = 1 << 9,
} HbReportCompFlag;
typedef struct _datatable DataTable;
typedef struct _datarow DataRow;
typedef struct _datacol DataCol;
typedef struct _carcost CarCost;
struct _carcost
{
guint32 kparent;
guint32 kcat;
guint32 date;
gchar *memo;
gdouble amount;
gboolean partial;
guint meter;
gdouble fuel;
guint dist;
};
CarCost *da_vehiclecost_malloc(void);
void da_vehiclecost_free(CarCost *item);
void da_vehiclecost_destroy(GList *list);
enum {
REPORT_MODE_TOTAL,
REPORT_MODE_TREND
};
void da_datatable_free(DataTable *dt);
gdouble da_datarow_get_cell_sum(DataRow *dr, guint32 index);
DataTable *report_compute(gint grpby, gint intvl, Filter *flt, GQueue *txn_queue, gint flags);
DataCol *report_data_get_col(DataTable *dt, guint32 idx);
DataRow *report_data_get_row(DataTable *dt, guint32 row);
guint report_items_get_key(gint tmpgrpby, guint jfrom, Transaction *ope);
gint report_interval_get_pos(gint intvl, guint jfrom, Transaction *ope);
gint report_interval_count(gint intvl, guint32 jfrom, guint32 jto);
guint32 report_interval_snprint_name(gchar *s, gint slen, gint intvl, guint32 jfrom, gint idx);
gdouble report_txn_amount_get(Filter *flt, Transaction *txn);
struct _datarow
{
guint32 nbcols;
guint32 pos; //used for sort
gchar *label; //row label
gshort flags; //See below
gshort pad1;
gchar *xlabel; //short label
gchar *misclabel; //host top label: year, today, etc
gdouble *colexp; //array for each row column
gdouble *colinc; //array for each row column
gdouble rowexp; //row total expense
gdouble rowinc; //row total income
};
struct _datacol
{
gchar *label; //long label
gshort flags; //See below
gshort pad1;
gchar *xlabel; //short label
gchar *misclabel; //host top label: year, today, etc
};
#define RF_NEWYEAR (1<<1)
#define RF_FORECAST (1<<2)
struct _datatable
{
guint32 nbkeys; //maximum key value for items (row)
guint32 nbrows; //nb of items (length): cat/subcat/pay/acc/...
guint32 nbcols; //nb of intervals: d, w, m, q, hy, y
guint32 maxpostdate;
guint flags;
guint grpby;
guint intvl;
guint32 *keyindex; //array of correspondance key => index in rows
guint32 *keylist;
DataRow **rows; //array of _datarow struct per key of item
DataRow *totrow; //for trend
DataCol **cols; //array of datacol
};
#endif
homebank-5.9.7/src/hb-tag.h 0000644 0001750 0001750 00000003707 14736461415 014736 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_TAG_H__
#define __HB_TAG_H__
#include "hb-types.h"
struct _tag
{
guint32 key;
gchar *name;
/* unsaved datas */
//gboolean flt_select;
guint16 nb_use_txn;
guint16 nb_use_all;
//guint usage_count;
};
void da_tag_free(Tag *item);
Tag *da_tag_malloc(void);
void da_tag_destroy(void);
void da_tag_new(void);
guint da_tag_length(void);
gboolean da_tag_create_none(void);
gboolean da_tag_delete(guint32 key);
gboolean da_tag_insert(Tag *acc);
gboolean da_tag_append(Tag *acc);
Tag *da_tag_append_if_new(gchar *rawname);
guint32 da_tag_get_max_key(void);
Tag *da_tag_get_by_name(gchar *name);
Tag *da_tag_get(guint32 key);
void da_tag_consistency(Tag *item);
gboolean tags_equal(guint32 *stags, guint32 *dtags);
guint tags_count(guint32 *tags);
guint32 *tags_clone(guint32 *tags);
guint32 *tags_parse(const gchar *tagstring);
gchar *tags_tostring(guint32 *tags);
gint tags_delete_unused(void);
void tags_fill_usage(void);
void tag_move(guint32 key1, guint32 key2);
gboolean tag_rename(Tag *item, const gchar *newname);
GList *tag_glist_sorted(gint column);
gboolean tag_load_csv(gchar *filename, gchar **error);
void tag_save_csv(gchar *filename);
#endif
homebank-5.9.7/src/gtk-chart.h 0000644 0001750 0001750 00000016346 14736461415 015463 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __GTK_CHART_H__
#define __GTK_CHART_H__
#include "gtk-chart-colors.h"
G_BEGIN_DECLS
#define GTK_TYPE_CHART (gtk_chart_get_type ())
#define GTK_CHART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CHART, GtkChart))
#define GTK_CHART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CHART, GtkChartClass)
#define GTK_IS_CHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CHART))
#define GTK_IS_CHART_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CHART))
#define GTK_CHART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CHART, GtkChartClass))
typedef struct _GtkChart GtkChart;
typedef struct _GtkChartClass GtkChartClass;
//typedef struct _GtkChartPrivate GtkChartPrivate;
typedef struct _ChartItem ChartItem;
typedef struct _HbtkDrawContext HbtkDrawContext;
typedef gchar (* GtkChartPrintIntFunc) (gint value, gboolean minor);
typedef gchar (* GtkChartPrintDoubleFunc) (gdouble value, gboolean minor);
/* = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* phi value */
#define PHI 1.61803399
/* default zoomx for charts */
#define GTK_CHART_BARW 41 //24
//TODO: #2038623 maybe go lower to 3 pixels for lines
#define GTK_CHART_MINBARW 8 //4
#define GTK_CHART_MAXBARW 41 //128
#define GTK_CHART_SPANBARW GTK_CHART_MAXBARW+1
#define GTK_CHART_MINRADIUS 64
#define CHART_BUFFER_LENGTH 128
// char draw options
#define CHART_PARAM_PIE_DONUT TRUE
#define CHART_PARAM_PIE_LINE FALSE
#define CHART_PARAM_PIE_MARK FALSE
// 5.5
//#define CHART_PARAM_PIE_HOLEVALUE 0.5
#define CHART_PARAM_PIE_HOLEVALUE 0.61803399
/* new stuff */
#define CHART_MARGIN 12 //standard a4 margin is 18
#define CHART_SPACING 6
#define CHART_LINE_SPACING 1.25
//#define PROP_SHOW_MINOR 6
//#define PROP_SHOW_LEGEND 7
#define ROUNDHALF(x) floor(x *2) / 2
enum
{
CHART_TYPE_NONE,
CHART_TYPE_COL,
CHART_TYPE_PIE,
CHART_TYPE_LINE,
CHART_TYPE_STACK,
CHART_TYPE_STACK100,
CHART_TYPE_MAX
};
enum
{
LST_LEGEND_FAKE,
LST_LEGEND_COLOR,
LST_LEGEND_TITLE,
LST_LEGEND_AMOUNT,
LST_LEGEND_RATE,
NUM_LST_LEGEND
};
struct _ChartItem
{
/* data part */
gchar *label;
//gchar *xlabel;
//gchar *misclabel;
//gshort flags;
//gshort pad1;
gdouble serie1;
gdouble serie2;
gdouble rate;
gint n_child;
/* draw stuffs */
//gchar *legend;
double angle2; /* rate for pie */
double height; /* for column */
};
struct _HbtkDrawContext
{
gboolean isprint;
gint visible;
double range, max;
double min;
double unit;
gint div;
double barw, blkw;
// double posbarh, negbarh;
/* drawing datas */
double font_h;
int l, t, w, h;
// int b, r;
cairo_rectangle_t graph;
cairo_rectangle_t legend;
double scale_w;
// double scale_x, scale_y, scale_h;
// double item_x, item_y, item_w;
/* legend dimension */
double legend_font_h;
double legend_label_w;
double legend_value_w;
double legend_rate_w;
/* zones height */
double title_zh;
double subtitle_zh, subtitle_y;
// double header_zh, header_y;
// double item_zh;
double ox, oy;
// gint lastx, lasty, ;
// gint lastpress_x, lastpress_y;
// guint timer_tag;
gint rayon, mark;
// gint left, top;
};
struct _GtkChart
{
//own widget here
/*< private >*/
//GtkChartPrivate *priv;
/* all below should be in priv normally */
GtkBox hbox;
GtkWidget *drawarea;
GtkAdjustment *adjustment;
GtkWidget *scrollbar;
GtkWidget *breadcrumb;
/* data storage */
GtkTreeModel *totmodel;
guint column1, column2;
gint nb_items;
GArray *items;
double rawmin, rawmax;
gdouble total;
//TODO: test of total, in waiting something else
GtkTreeModel *model;
gdouble *colsum;
gchar **collabel;
DataCol **cols;
gint nb_cols;
gint colindice;
gchar *title;
gchar *subtitle;
/* chart properties */
gint type; // column/pie/line
gboolean dual;
gboolean abs;
gboolean show_breadcrumb;
gboolean show_legend;
gboolean show_legend_wide;
gboolean legend_visible;
gboolean legend_wide_visible;
gboolean smallfont;
gboolean show_over;
gboolean show_average;
gboolean show_xval;
gboolean show_mono;
guint32 kcur;
gboolean minor;
gdouble minor_rate;
gdouble minimum, average;
gint usrbarw;
gchar *minor_symbol;
/* color datas */
GtkColorScheme color_scheme;
cairo_surface_t *surface;
//dynamics
gint hover, lasthover;
gint colhover, lastcolhover;
gboolean drillable;
struct _HbtkDrawContext context;
PangoFontDescription *pfd;
gchar buffer1[CHART_BUFFER_LENGTH];
gchar buffer2[CHART_BUFFER_LENGTH];
};
struct _GtkChartClass
{
GtkBoxClass parent_class;
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
};
typedef struct
{
GtkChart *chart;
HbtkDrawContext drawctx;
} GtkChartPrintData;
GType gtk_chart_get_type (void) G_GNUC_CONST;
void gtk_chart_print(GtkChart *chart, GtkWindow *parent, gchar *dirname, gchar *filename);
/* public function */
GtkWidget *gtk_chart_new(gint type);
void gtk_chart_set_datas_none (GtkChart *chart);
void gtk_chart_set_datas_total(GtkChart *chart, GtkTreeModel *model, guint column1, guint column2, gchar *title, gchar *subtitle);
void gtk_chart_set_datas_time (GtkChart *chart, GtkTreeView *treeview, DataTable *dt, guint nbrows, guint nbcols, gchar *title, gchar *subtitle);
void gtk_chart_set_type(GtkChart *chart, gint type);
void gtk_chart_set_color_scheme(GtkChart * chart, gint colorscheme);
void gtk_chart_queue_redraw(GtkChart *chart);
void gtk_chart_set_minor_prefs(GtkChart * chart, gdouble rate, gchar *symbol);
void gtk_chart_set_currency(GtkChart * chart, guint32 kcur);
void gtk_chart_show_average(GtkChart * chart, gdouble value, gboolean visible);
void gtk_chart_set_overdrawn(GtkChart * chart, gdouble minimum);
void gtk_chart_show_xval(GtkChart * chart, gboolean visible);
void gtk_chart_set_barw(GtkChart * chart, gdouble barw);
void gtk_chart_set_showmono(GtkChart * chart, gboolean mono);
void gtk_chart_set_smallfont(GtkChart * chart, gboolean small);
void gtk_chart_show_legend(GtkChart * chart, gboolean visible, gboolean showextracol);
void gtk_chart_show_overdrawn(GtkChart * chart, gboolean visible);
void gtk_chart_show_minor(GtkChart * chart, gboolean minor);
void gtk_chart_set_absolute(GtkChart * chart, gboolean abs);
G_END_DECLS
#endif /* __GTK_CHART_H__ */
homebank-5.9.7/src/gtk-dateentry.c 0000644 0001750 0001750 00000052550 15123470135 016337 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//#include
#include /* atoi, atof, atol */
#include /* gettext */
#include
#include
#include "gtk-dateentry.h"
//TODO: move this after GTK4
#include "ui-widgets.h"
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
//keypress
#define DBK(x) //(x);
enum {
CHANGED,
LAST_SIGNAL
};
enum {
PROPERTY_DATE = 5,
};
static guint dateentry_signals[LAST_SIGNAL] = {0,};
//G_DEFINE_TYPE(GtkDateEntry, gtk_date_entry, GTK_TYPE_BOX)
G_DEFINE_TYPE_WITH_CODE(GtkDateEntry, gtk_date_entry, GTK_TYPE_BOX, G_ADD_PRIVATE (GtkDateEntry))
gboolean using_twodigit_years = FALSE;
/* order of these in the current locale
** https://calendars.fandom.com/wiki/Date_format_by_country
** YMD: hungary
** DMY: united-kingdom
** MDY: united-states
*/
static GDateDMY dmy_order[3] =
{
G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
};
struct _GDateParseTokens {
gint num_ints;
gint n[3];
};
typedef struct _GDateParseTokens GDateParseTokens;
#define NUM_LEN 10
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if MYDEBUG == 1
static void
_hb_dbg_date(gchar *title, GDate *date)
{
gchar buffer1[128];
g_date_strftime (buffer1, 128-1, "%a %d-%m-%Y", date);
g_print(" %s: [%s]\n", title != NULL ? title:"==>", buffer1);
}
#endif
//https://en.wikipedia.org/wiki/Date_format_by_country
static void
hb_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
{
gchar num[4][NUM_LEN+1];
gint i;
const guchar *s;
//DB( g_print("\n[dateentry] fill parse token\n") );
/* We count 4, but store 3; so we can give an error
* if there are 4.
*/
num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
s = (const guchar *) str;
pt->num_ints = 0;
while (*s && pt->num_ints < 4)
{
i = 0;
while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
{
num[pt->num_ints][i] = *s;
++s;
++i;
}
if (i > 0)
{
num[pt->num_ints][i] = '\0';
++(pt->num_ints);
}
if (*s == '\0') break;
++s;
}
pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
}
static void
hb_date_determine_dmy_order(void)
{
GDateParseTokens testpt;
GDate d;
gchar buf[127];
gint i;
DB( g_print("\n[dateentry] determine dmy order\n") );
/* had to pick a random day - don't change this, some strftimes
* are broken on some days, and this one is good so far. */
g_date_set_dmy (&d, 4, 7, 1976);
g_date_strftime (buf, 127, "%x", &d);
DB( g_print(" dmy'04/07/1976' > '%s'\n", buf ));
hb_date_fill_parse_tokens (buf, &testpt);
using_twodigit_years = FALSE;
dmy_order[0] = G_DATE_DAY;
dmy_order[1] = G_DATE_MONTH;
dmy_order[2] = G_DATE_YEAR;
i = 0;
while (i < testpt.num_ints)
{
switch (testpt.n[i])
{
case 7:
dmy_order[i] = G_DATE_MONTH;
break;
case 4:
dmy_order[i] = G_DATE_DAY;
break;
case 76:
using_twodigit_years = TRUE; /* FALL THRU */
case 1976:
dmy_order[i] = G_DATE_YEAR;
break;
}
++i;
}
DB( g_print(" => dmy=%d mdy=%d ymd=%d\n", dmy_order[0]==G_DATE_DAY, dmy_order[0]==G_DATE_MONTH, dmy_order[0]==G_DATE_YEAR) );
}
static void
hb_date_parse_tokens(GDate *date, const gchar *str)
{
GDate tokendate;
gchar **num_array = NULL;
gint num_ints;
num_array = g_strsplit_set(str, " /-.", -1);
num_ints = g_strv_length( num_array );
DB( g_print(" num ints: %d\n", num_ints) );
//invalid date
g_date_clear(&tokendate, 1);
//user input day/month or month/day
if( num_ints == 2 )
{
gint num1 = atoi(num_array[0]);
gint num2 = atoi(num_array[1]);
if( num1 > 0 && num2 > 0 )
{
g_date_set_time_t(&tokendate, time(NULL));
//DMY
if( dmy_order[0] == G_DATE_DAY )
{
g_date_set_day(&tokendate, num1);
g_date_set_month(&tokendate, num2);
DB( g_print(" set d/m: %d %d\n", num1, num2) );
}
else
if( dmy_order[0] == G_DATE_MONTH )
{
g_date_set_month(&tokendate, num1);
g_date_set_day(&tokendate, num2);
DB( g_print(" set m/d: %d %d\n", num1, num2) );
}
}
}
else
//user input day
if( num_ints == 1 )
{
g_date_set_time_t(&tokendate, time(NULL));
g_date_set_day(&tokendate, atoi(num_array[0]));
DB( g_print(" set d: %d\n", atoi(num_array[0])) );
}
g_strfreev(num_array);
//update output date if tokendate is valid
if( g_date_valid(&tokendate) )
{
g_date_set_julian(date, g_date_get_julian(&tokendate));
}
else
{
g_date_clear(date, 1);
DB( g_print(" quick set fail\n") );
}
}
static void
update_text(GtkDateEntry *self)
{
GtkDateEntryPrivate *priv = self->priv;
gchar label[127];
DB( g_print("\n[dateentry] '%s' update text\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(self))) );
//%x : The preferred date representation for the current locale without the time.
//5.7 added %a to display abbreviated weekday
g_date_strftime (label, 127 - 1, "%a %x", priv->date);
gtk_entry_set_text (GTK_ENTRY (priv->entry), label);
DB( g_print(" = %s\n", label) );
}
static void
eval_date(GtkDateEntry *self)
{
GtkDateEntryPrivate *priv = self->priv;
DB( g_print("\n[dateentry] '%s' eval date\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(self))) );
DB( _hb_dbg_date("min", &priv->mindate) );
DB( _hb_dbg_date("max", &priv->maxdate) );
DB( _hb_dbg_date("in ", priv->date) );
g_date_clamp(priv->date, &priv->mindate, &priv->maxdate);
DB( _hb_dbg_date("out", priv->date) );
update_text(self);
if(priv->lastdate != g_date_get_julian(priv->date))
{
DB( g_print(" **emit 'changed' signal**\n") );
g_signal_emit_by_name (self, "changed", NULL, NULL);
}
priv->lastdate = g_date_get_julian(priv->date);
}
static void
parse_date(GtkDateEntry *self)
{
GtkDateEntryPrivate *priv = self->priv;
const gchar *str;
DB( g_print("\n[dateentry] '%s' parse date\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(self))) );
str = gtk_entry_get_text (GTK_ENTRY (priv->entry));
DB( g_print(" rawstr '%s'\n", str) );
//1) give a try to tokens = day, day/month, month/day
hb_date_parse_tokens(priv->date, str);
DB( g_print(" 1/ quick parsed :: valid=%d\n", g_date_valid(priv->date)) );
//2) invalid: glib failover
if(!g_date_valid(priv->date))
{
//2) we parse the string according to the locale
g_date_set_parse (priv->date, str);
DB( g_print(" 2/ glib parsed :: valid=%d > %d %d %d\n", g_date_valid(priv->date),
g_date_get_day(priv->date), g_date_get_month(priv->date), g_date_get_year(priv->date) ) );
//#1956185 adjust for 2 digits year
//#2137022: mswin parse 2digits year date != than linux
// parse 31/12/30 :: linux y=30 // mswin y=2030
// mswin mapping years 00-29 to 2000-2029 and 30-99 to 1930-1999
// glib pivot to 1930 as well
// 2026: hb windowing is 2060 >> 00-59 to 2000-2059 and 60-99 to 1960-1999
if( using_twodigit_years == TRUE && g_date_valid(priv->date) == TRUE )
{
guint year = g_date_get_year(priv->date);
//overwrite ms windows
#ifdef G_OS_WIN32
if( year >= 1900 )
year = year % 100;
DB( g_print(" > overwrite windows %d\n", year) );
#endif
if( year < 100 ) //probably unix
{
DB( g_print(" > adjusting %d to 4 digits\n", year) );
year += (year < 60) ? 2000 : 1900;
g_date_set_year(priv->date, year);
DB( g_print(" > set to %d\n", year) );
}
}
}
//3) invalid: warn user put today's
if(!g_date_valid(priv->date))
{
g_date_set_time_t(priv->date, time(NULL));
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_WARNING);
}
else
{
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_WARNING);
}
DB( _hb_dbg_date(NULL, priv->date) );
eval_date(self);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
gtk_date_entry_set_calendar (GtkWidget * widget, GtkDateEntry * dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
guint day, month;
DB( g_print("\n[dateentry] set calendar\n") );
/* GtkCalendar expects month to be in 0-11 range (inclusive) */
day = g_date_get_day (priv->date);
month = g_date_get_month (priv->date) - 1;
g_signal_handler_block(priv->calendar, priv->hid_dayselect);
gtk_calendar_select_month (GTK_CALENDAR (priv->calendar),
month,
g_date_get_year (priv->date));
gtk_calendar_select_day (GTK_CALENDAR (priv->calendar), day);
g_signal_handler_unblock(priv->calendar, priv->hid_dayselect);
}
static void
gtk_date_entry_cb_today_clicked (GtkWidget * widget, GtkDateEntry * dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] today_clicked\n") );
//revert to now (today)
g_date_set_time_t(priv->date, time(NULL));
eval_date(dateentry);
gtk_date_entry_set_calendar(widget, dateentry);
//1923368 keep the popover visible
//gtk_widget_hide (priv->popover);
}
static void
gtk_date_entry_cb_calendar_day_selected(GtkWidget * calendar, GtkDateEntry * dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
guint year, month, day;
DB( g_print("\n[dateentry] calendar_day_selected\n") );
gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day);
g_date_set_dmy (priv->date, day, month + 1, year);
eval_date(dateentry);
}
static gint
gtk_date_entry_cb_calendar_day_select_double_click(GtkWidget * calendar, gpointer user_data)
{
GtkDateEntry *dateentry = user_data;
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] calendar_day_select_double_click\n") );
gtk_widget_hide (priv->popover);
return FALSE;
}
static void
gtk_date_entry_cb_calendar_today_mark(GtkWidget *calendar, GtkDateEntry *dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
guint year, month, day;
DB( g_print("\n[dateentry] cb_calendar_mark_day\n") );
gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day);
//maybe 1828914
gtk_calendar_clear_marks(GTK_CALENDAR(priv->calendar));
if( year == g_date_get_year (&priv->nowdate) && month == (g_date_get_month (&priv->nowdate)-1) )
gtk_calendar_mark_day(GTK_CALENDAR(priv->calendar), g_date_get_day (&priv->nowdate));
}
static void
gtk_date_entry_cb_calendar_monthyear(GtkWidget *calendar, GtkDateEntry *dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
guint year, month, day;
DB( g_print("\n[dateentry] cb_calendar_monthyear\n") );
gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day);
if( year < 1900)
g_object_set(calendar, "year", 1900, NULL);
if( year > 2200)
g_object_set(calendar, "year", 2200, NULL);
gtk_date_entry_cb_calendar_today_mark(calendar, dateentry);
}
static gint
gtk_date_entry_cb_entry_key_pressed (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
GtkDateEntry *dateentry = user_data;
GtkDateEntryPrivate *priv = dateentry->priv;
GdkModifierType state;
guint keyval;
guint action;
DBK( g_print("\n[dateentry] '%s' entry key pressed", (gchar *)gtk_widget_get_name(GTK_WIDGET(dateentry))) );
gdk_event_get_keyval(event, &keyval);
gdk_event_get_state (event, &state);
DBK( g_print(" state: %s %s\n", (state & GDK_SHIFT_MASK) ? "shift" : "", (state & GDK_CONTROL_MASK) ? "ctrl" : "" ) );
DBK( g_print(" keyval: %s %s\n", (keyval == GDK_KEY_Up) ? "up" : "", (keyval == GDK_KEY_Down) ? "down" : "") );
if( (gdk_event_get_event_type(event) != GDK_KEY_PRESS) )
return FALSE;
//#1873643 preserve Up/Down (+ctrl) natural GTK focus change
if( (state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK) )
return FALSE;
if( (keyval == GDK_KEY_Up) || (keyval == GDK_KEY_Down) )
{
//let's bitwise key to an action-id
action = 0;
action |= (state & GDK_SHIFT_MASK) ? 2 : 0;
action |= (state & GDK_CONTROL_MASK) ? 4 : 0;
action |= (keyval == GDK_KEY_Down) ? 1 : 0;
DBK( g_print(" action: %d\n", action) );
switch(action)
{
case 0: g_date_add_days (priv->date, 1); break;
case 1: g_date_subtract_days (priv->date, 1); break;
case 2: g_date_add_months (priv->date, 1); break;
case 3: g_date_subtract_months (priv->date, 1); break;
case 6: g_date_add_years (priv->date, 1); break;
case 7: g_date_subtract_years (priv->date, 1); break;
}
eval_date(dateentry);
//stop handlers
return TRUE;
}
//propagate
return FALSE;
}
static void
gtk_date_entry_cb_entry_activate(GtkWidget *gtkentry, gpointer user_data)
{
GtkDateEntry *dateentry = user_data;
DB( g_print("\n- - - - - - - -\n[dateentry] '%s' entry_activate\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(dateentry))) );
parse_date(dateentry);
//5.8 done in parse_date
//eval_date(dateentry);
}
static gboolean
gtk_date_entry_cb_entry_focus_out(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
GtkDateEntry *dateentry = user_data;
DB( g_print("\n- - - - - - - -\n[dateentry] entry focus-out-event %d\n", gtk_widget_is_focus(GTK_WIDGET(dateentry))) );
parse_date(dateentry);
//5.8 done in parse_date
//eval_date(dateentry);
return FALSE;
}
static void
gtk_date_entry_cb_button_clicked (GtkWidget * widget, GtkDateEntry * dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] button_clicked\n") );
gtk_date_entry_set_calendar(widget, dateentry);
gtk_popover_set_relative_to (GTK_POPOVER (priv->popover), GTK_WIDGET (priv->entry));
//gtk_widget_get_clip(priv->arrow, &rect);
//gtk_popover_set_pointing_to (GTK_POPOVER (priv->popover), &rect);
gtk_date_entry_cb_calendar_today_mark(widget, dateentry);
gtk_widget_show_all (priv->popover);
}
static void
gtk_date_entry_destroy (GtkWidget *object)
{
GtkDateEntry *dateentry = GTK_DATE_ENTRY (object);
GtkDateEntryPrivate *priv = dateentry->priv;
g_return_if_fail(object != NULL);
g_return_if_fail(GTK_IS_DATE_ENTRY(object));
DB( g_print("\n[dateentry] destroy\n") );
DB( g_print(" free gtkentry: %p\n", priv->entry) );
DB( g_print(" free arrow: %p\n", priv->button) );
DB( g_print(" free dateentry: %p\n", dateentry) );
if(priv->date)
g_date_free(priv->date);
priv->date = NULL;
GTK_WIDGET_CLASS (gtk_date_entry_parent_class)->destroy (object);
}
static void
gtk_date_entry_dispose (GObject *gobject)
{
//GtkDateEntry *self = GTK_DATE_ENTRY (gobject);
DB( g_print("\n[dateentry] dispose\n") );
//g_clear_object (&self->priv->an_object);
G_OBJECT_CLASS (gtk_date_entry_parent_class)->dispose (gobject);
}
static void
gtk_date_entry_finalize (GObject *gobject)
{
//GtkDateEntry *self = GTK_DATE_ENTRY (gobject);
DB( g_print("\n[dateentry] finalize\n") );
//g_date_free(self->date);
//g_free (self->priv->a_string);
/* Always chain up to the parent class; as with dispose(), finalize()
* is guaranteed to exist on the parent's class virtual function table
*/
G_OBJECT_CLASS(gtk_date_entry_parent_class)->finalize (gobject);
}
static void
gtk_date_entry_class_init (GtkDateEntryClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = G_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
DB( g_print("\n[dateentry] class_init\n") );
//object_class->constructor = gtk_date_entry_constructor;
//object_class->set_property = gtk_date_entry_set_property;
//object_class->get_property = gtk_date_entry_get_property;
object_class->dispose = gtk_date_entry_dispose;
object_class->finalize = gtk_date_entry_finalize;
widget_class->destroy = gtk_date_entry_destroy;
dateentry_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDateEntryClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
//g_type_class_add_private (object_class, sizeof (GtkDateEntryPrivate));
}
static void
gtk_date_entry_init (GtkDateEntry *dateentry)
{
GtkDateEntryPrivate *priv;
GtkWidget *vbox;
DB( g_print("\n[dateentry] init\n") );
/* yes, also priv, need to keep the code readable */
/*dateentry->priv = G_TYPE_INSTANCE_GET_PRIVATE (dateentry,
GTK_TYPE_DATE_ENTRY,
GtkDateEntryPrivate);*/
dateentry->priv = gtk_date_entry_get_instance_private(dateentry);
priv = dateentry->priv;
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(dateentry)), GTK_STYLE_CLASS_LINKED);
priv->entry = gtk_entry_new ();
//todo: see if really useful
gtk_entry_set_width_chars(GTK_ENTRY(priv->entry), 16);
gtk_entry_set_max_width_chars(GTK_ENTRY(priv->entry), 16);
hbtk_box_prepend (GTK_BOX (dateentry), priv->entry);
priv->button = gtk_button_new ();
priv->arrow = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_button_set_image(GTK_BUTTON(priv->button), priv->arrow);
gtk_box_append (GTK_BOX (dateentry), priv->button);
priv->popover = gtk_popover_new (priv->button);
gtk_popover_set_position(GTK_POPOVER(priv->popover), GTK_POS_BOTTOM);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_popover_set_child (GTK_POPOVER(priv->popover), vbox);
gtk_widget_set_margin_start (vbox, 10);
gtk_widget_set_margin_end (vbox, 10);
gtk_widget_set_margin_top (vbox, 10);
gtk_widget_set_margin_bottom (vbox, 10);
priv->calendar = gtk_calendar_new ();
gtk_box_prepend(GTK_BOX(vbox), priv->calendar);
priv->BT_today = gtk_button_new_with_mnemonic ( gettext("_Today"));
gtk_box_prepend(GTK_BOX(vbox), priv->BT_today);
gtk_widget_show_all (GTK_WIDGET(dateentry));
/* initialize datas */
priv->date = g_date_new();
g_date_set_time_t(priv->date, time(NULL));
g_date_set_time_t(&priv->nowdate, time(NULL));
g_date_set_dmy(&priv->mindate, 1, 1, 1900); //693596
g_date_set_dmy(&priv->maxdate, 31, 12, 2200); //803533
//update_text(dateentry);
g_signal_connect (priv->entry, "key-press-event",
G_CALLBACK (gtk_date_entry_cb_entry_key_pressed), dateentry);
g_signal_connect_after (priv->entry, "focus-out-event",
G_CALLBACK (gtk_date_entry_cb_entry_focus_out), dateentry);
g_signal_connect (priv->entry, "activate",
G_CALLBACK (gtk_date_entry_cb_entry_activate), dateentry);
g_signal_connect (priv->button, "clicked",
G_CALLBACK (gtk_date_entry_cb_button_clicked), dateentry);
g_signal_connect (priv->calendar, "prev-year",
G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
g_signal_connect (priv->calendar, "next-year",
G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
g_signal_connect (priv->calendar, "prev-month",
G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
g_signal_connect (priv->calendar, "next-month",
G_CALLBACK (gtk_date_entry_cb_calendar_monthyear), dateentry);
priv->hid_dayselect = g_signal_connect (priv->calendar, "day-selected",
G_CALLBACK (gtk_date_entry_cb_calendar_day_selected), dateentry);
g_signal_connect (priv->calendar, "day-selected-double-click",
G_CALLBACK (gtk_date_entry_cb_calendar_day_select_double_click), dateentry);
g_signal_connect (priv->BT_today, "clicked",
G_CALLBACK (gtk_date_entry_cb_today_clicked), dateentry);
}
GtkWidget *
gtk_date_entry_new (GtkWidget *label)
{
GtkDateEntry *dateentry;
DB( g_print("\n[dateentry] new\n") );
dateentry = g_object_new (GTK_TYPE_DATE_ENTRY, NULL);
if(dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), priv->entry);
hb_date_determine_dmy_order();
}
return GTK_WIDGET(dateentry);
}
void
gtk_date_entry_set_error(GtkDateEntry *dateentry, gboolean error)
{
GtkDateEntryPrivate *priv = dateentry->priv;
if( error == TRUE )
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_ERROR);
else
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_ERROR);
}
void
gtk_date_entry_set_date(GtkDateEntry *dateentry, guint32 julian_days)
{
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] '%s' set date\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(dateentry))) );
g_return_if_fail (GTK_IS_DATE_ENTRY (dateentry));
if(g_date_valid_julian(julian_days))
{
g_date_set_julian (priv->date, julian_days);
}
else
{
g_date_set_time_t(priv->date, time(NULL));
}
DB( _hb_dbg_date(NULL, priv->date) );
eval_date(dateentry);
}
/*
**
*/
guint32
gtk_date_entry_get_date(GtkDateEntry *dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] '%s' get date\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(dateentry))) );
g_return_val_if_fail (GTK_IS_DATE_ENTRY (dateentry), 0);
DB( _hb_dbg_date(NULL, priv->date) );
return(g_date_get_julian(priv->date));
}
GDateWeekday
gtk_date_entry_get_weekday(GtkDateEntry *dateentry)
{
GtkDateEntryPrivate *priv = dateentry->priv;
DB( g_print("\n[dateentry] '%s' get weekday\n", (gchar *)gtk_widget_get_name(GTK_WIDGET(dateentry))) );
g_return_val_if_fail (GTK_IS_DATE_ENTRY (dateentry), 0);
return(g_date_get_weekday(priv->date));
}
homebank-5.9.7/src/ui-txn-split.h 0000664 0001750 0001750 00000004520 14736461415 016145 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_SPLIT_GTK_H__
#define __HB_SPLIT_GTK_H__
#include "ui-transaction.h"
#include "hb-split.h"
struct ui_split_dialog_data
{
GtkWidget *dialog;
GtkWidget *LV_split;
GtkWidget *PO_cat;
GtkWidget *ST_amount;
GtkWidget *ST_memo;
GtkWidget *BT_edit;
GtkWidget *BT_dup;
GtkWidget *BT_rem;
GtkWidget *BT_remall;
GtkWidget *BT_add;
GtkWidget *BT_apply;
GtkWidget *BT_cancel;
GtkWidget *IM_edit;
GtkWidget *LB_sumsplit;
GtkWidget *LB_remain;
GtkWidget *LB_txnamount;
GtkWidget *IB_wrnsum;
GtkWidget *IB_errtype;
GtkWidget *IB_inflimit;
//Transaction *ope;
GPtrArray *src_splits;
GPtrArray *tmp_splits;
gint mode;
gint txntype;
guint32 date;
Currency *cur;
gdouble amount;
gint amountsign;
gdouble sumsplit;
gdouble remsplit;
gboolean isedited;
gint nbsplit;
gint activeline;
};
#define SPLIT_MODE_EMPTY 0
#define SPLIT_MODE_AMOUNT 1
void ui_split_dialog_line_sensitive(guint line, gboolean sensitive, gpointer user_data);
void ui_split_dialog_compute(GtkWidget *widget, gpointer user_data);
void ui_split_dialog_inactiveline(GtkWidget *widget, gpointer user_data);
void ui_split_dialog_activeline(GtkWidget *widget, gpointer user_data);
void ui_split_dialog_get(struct ui_split_dialog_data *data);
void ui_split_dialog_set(struct ui_split_dialog_data *data);
GtkWidget *ui_split_dialog (GtkWidget *parent, GPtrArray **src_splits, gint txntype, guint32 date, gdouble amount, guint32 kcur, void (update_callbackFunction(GtkWidget*, gdouble)));
GtkWidget *ui_split_view_dialog (GtkWidget *parent, Transaction *ope);
#endif
homebank-5.9.7/src/hb-split.h 0000664 0001750 0001750 00000003412 14736461415 015311 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_SPLIT_H__
#define __HB_SPLIT_H__
//for the record, quicken is limited to 250
#define TXN_MAX_SPLIT 62
#include "hb-types.h"
struct _split
{
guint32 kcat;
gdouble amount;
gchar *memo;
//unsaved data
gushort pos;
};
void da_split_free(Split *item);
Split *da_split_malloc(void);
void da_split_destroy(GPtrArray *splits);
GPtrArray *da_split_new(void);
void da_splits_sort(GPtrArray *splits);
guint da_splits_length(GPtrArray *splits);
gboolean da_splits_delete(GPtrArray *splits, Split *item);
void da_splits_append(GPtrArray *splits, Split *item);
Split *da_split_duplicate(Split *src);
Split *da_splits_get(GPtrArray *splits, guint index);
GPtrArray *da_splits_clone(GPtrArray *src_splits);
guint da_splits_parse(GPtrArray *splits, gchar *cats, gchar *amounts, gchar *memos);
guint da_splits_tostring(GPtrArray *splits, gchar **cats, gchar **amounts, gchar **memos);
guint da_splits_consistency (GPtrArray *splits);
guint da_splits_anonymize (GPtrArray *splits);
#endif
homebank-5.9.7/src/language.h 0000644 0001750 0001750 00000001627 14736461415 015356 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LANGUAGE_H__
#define __LANGUAGE_H__
void language_init (const gchar *language);
#endif /* __LANGUAGE_H__ */
homebank-5.9.7/src/language.c 0000644 0001750 0001750 00000053362 15053414605 015344 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* Win32 language lookup table:
* Copyright (C) 2007-2008 Dieter Verfaillie
*/
#include "homebank.h"
#include
#include
#ifdef G_OS_WIN32
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501
#include
#include
#endif
#include "language.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
void
language_init (const gchar *language)
{
DB( g_print ("\n[language] init\n") );
DB( g_print (" pref is '%s'\n", language) );
#ifdef G_OS_WIN32
if (! language)
{
/* FIXME: This is a hack. gettext doesn't pick the right language
* by default on Windows, so we enforce the right one. The
* following code is an adaptation of Python code from
* pynicotine. For reasons why this approach is needed, and why
* the GetLocaleInfo() approach in other libs falls flat, see:
* http://blogs.msdn.com/b/michkap/archive/2007/04/15/2146890.aspx
*/
switch (GetUserDefaultUILanguage())
{
case 1078:
language = "af"; /* Afrikaans - South Africa */
break;
case 1052:
language = "sq"; /* Albanian - Albania */
break;
case 1118:
language = "am"; /* Amharic - Ethiopia */
break;
case 1025:
language = "ar"; /* Arabic - Saudi Arabia */
break;
case 5121:
language = "ar"; /* Arabic - Algeria */
break;
case 15361:
language = "ar"; /* Arabic - Bahrain */
break;
case 3073:
language = "ar"; /* Arabic - Egypt */
break;
case 2049:
language = "ar"; /* Arabic - Iraq */
break;
case 11265:
language = "ar"; /* Arabic - Jordan */
break;
case 13313:
language = "ar"; /* Arabic - Kuwait */
break;
case 12289:
language = "ar"; /* Arabic - Lebanon */
break;
case 4097:
language = "ar"; /* Arabic - Libya */
break;
case 6145:
language = "ar"; /* Arabic - Morocco */
break;
case 8193:
language = "ar"; /* Arabic - Oman */
break;
case 16385:
language = "ar"; /* Arabic - Qatar */
break;
case 10241:
language = "ar"; /* Arabic - Syria */
break;
case 7169:
language = "ar"; /* Arabic - Tunisia */
break;
case 14337:
language = "ar"; /* Arabic - U.A.E. */
break;
case 9217:
language = "ar"; /* Arabic - Yemen */
break;
case 1067:
language = "hy"; /* Armenian - Armenia */
break;
case 1101:
language = "as"; /* Assamese */
break;
case 2092:
language = NULL; /* Azeri (Cyrillic) */
break;
case 1068:
language = NULL; /* Azeri (Latin) */
break;
case 1069:
language = "eu"; /* Basque */
break;
case 1059:
language = "be"; /* Belarusian */
break;
case 1093:
language = "bn"; /* Bengali (India) */
break;
case 2117:
language = "bn"; /* Bengali (Bangladesh) */
break;
case 5146:
language = "bs"; /* Bosnian (Bosnia/Herzegovina) */
break;
case 1026:
language = "bg"; /* Bulgarian */
break;
case 1109:
language = "my"; /* Burmese */
break;
case 1027:
language = "ca"; /* Catalan */
break;
case 1116:
language = NULL; /* Cherokee - United States */
break;
case 2052:
language = "zh"; /* Chinese - People"s Republic of China */
break;
case 4100:
language = "zh"; /* Chinese - Singapore */
break;
case 1028:
language = "zh"; /* Chinese - Taiwan */
break;
case 3076:
language = "zh"; /* Chinese - Hong Kong SAR */
break;
case 5124:
language = "zh"; /* Chinese - Macao SAR */
break;
case 1050:
language = "hr"; /* Croatian */
break;
case 4122:
language = "hr"; /* Croatian (Bosnia/Herzegovina) */
break;
case 1029:
language = "cs"; /* Czech */
break;
case 1030:
language = "da"; /* Danish */
break;
case 1125:
language = "dv"; /* Divehi */
break;
case 1043:
language = "nl"; /* Dutch - Netherlands */
break;
case 2067:
language = "nl"; /* Dutch - Belgium */
break;
case 1126:
language = NULL; /* Edo */
break;
case 1033:
language = "en"; /* English - United States */
break;
case 2057:
language = "en"; /* English - United Kingdom */
break;
case 3081:
language = "en"; /* English - Australia */
break;
case 10249:
language = "en"; /* English - Belize */
break;
case 4105:
language = "en"; /* English - Canada */
break;
case 9225:
language = "en"; /* English - Caribbean */
break;
case 15369:
language = "en"; /* English - Hong Kong SAR */
break;
case 16393:
language = "en"; /* English - India */
break;
case 14345:
language = "en"; /* English - Indonesia */
break;
case 6153:
language = "en"; /* English - Ireland */
break;
case 8201:
language = "en"; /* English - Jamaica */
break;
case 17417:
language = "en"; /* English - Malaysia */
break;
case 5129:
language = "en"; /* English - New Zealand */
break;
case 13321:
language = "en"; /* English - Philippines */
break;
case 18441:
language = "en"; /* English - Singapore */
break;
case 7177:
language = "en"; /* English - South Africa */
break;
case 11273:
language = "en"; /* English - Trinidad */
break;
case 12297:
language = "en"; /* English - Zimbabwe */
break;
case 1061:
language = "et"; /* Estonian */
break;
case 1080:
language = "fo"; /* Faroese */
break;
case 1065:
language = NULL; /* Farsi */
break;
case 1124:
language = NULL; /* Filipino */
break;
case 1035:
language = "fi"; /* Finnish */
break;
case 1036:
language = "fr"; /* French - France */
break;
case 2060:
language = "fr"; /* French - Belgium */
break;
case 11276:
language = "fr"; /* French - Cameroon */
break;
case 3084:
language = "fr"; /* French - Canada */
break;
case 9228:
language = "fr"; /* French - Democratic Rep. of Congo */
break;
case 12300:
language = "fr"; /* French - Cote d"Ivoire */
break;
case 15372:
language = "fr"; /* French - Haiti */
break;
case 5132:
language = "fr"; /* French - Luxembourg */
break;
case 13324:
language = "fr"; /* French - Mali */
break;
case 6156:
language = "fr"; /* French - Monaco */
break;
case 14348:
language = "fr"; /* French - Morocco */
break;
case 58380:
language = "fr"; /* French - North Africa */
break;
case 8204:
language = "fr"; /* French - Reunion */
break;
case 10252:
language = "fr"; /* French - Senegal */
break;
case 4108:
language = "fr"; /* French - Switzerland */
break;
case 7180:
language = "fr"; /* French - West Indies */
break;
case 1122:
language = "fy"; /* Frisian - Netherlands */
break;
case 1127:
language = NULL; /* Fulfulde - Nigeria */
break;
case 1071:
language = "mk"; /* FYRO Macedonian */
break;
case 2108:
language = "ga"; /* Gaelic (Ireland) */
break;
case 1084:
language = "gd"; /* Gaelic (Scotland) */
break;
case 1110:
language = "gl"; /* Galician */
break;
case 1079:
language = "ka"; /* Georgian */
break;
case 1031:
language = "de"; /* German - Germany */
break;
case 3079:
language = "de"; /* German - Austria */
break;
case 5127:
language = "de"; /* German - Liechtenstein */
break;
case 4103:
language = "de"; /* German - Luxembourg */
break;
case 2055:
language = "de"; /* German - Switzerland */
break;
case 1032:
language = "el"; /* Greek */
break;
case 1140:
language = "gn"; /* Guarani - Paraguay */
break;
case 1095:
language = "gu"; /* Gujarati */
break;
case 1128:
language = "ha"; /* Hausa - Nigeria */
break;
case 1141:
language = NULL; /* Hawaiian - United States */
break;
case 1037:
language = "he"; /* Hebrew */
break;
case 1081:
language = "hi"; /* Hindi */
break;
case 1038:
language = "hu"; /* Hungarian */
break;
case 1129:
language = NULL; /* Ibibio - Nigeria */
break;
case 1039:
language = "is"; /* Icelandic */
break;
case 1136:
language = "ig"; /* Igbo - Nigeria */
break;
case 1057:
language = "id"; /* Indonesian */
break;
case 1117:
language = "iu"; /* Inuktitut */
break;
case 1040:
language = "it"; /* Italian - Italy */
break;
case 2064:
language = "it"; /* Italian - Switzerland */
break;
case 1041:
language = "ja"; /* Japanese */
break;
case 1099:
language = "kn"; /* Kannada */
break;
case 1137:
language = "kr"; /* Kanuri - Nigeria */
break;
case 2144:
language = "ks"; /* Kashmiri */
break;
case 1120:
language = "ks"; /* Kashmiri (Arabic) */
break;
case 1087:
language = "kk"; /* Kazakh */
break;
case 1107:
language = "km"; /* Khmer */
break;
case 1111:
language = NULL; /* Konkani */
break;
case 1042:
language = "ko"; /* Korean */
break;
case 1088:
language = "ky"; /* Kyrgyz (Cyrillic) */
break;
case 1108:
language = "lo"; /* Lao */
break;
case 1142:
language = "la"; /* Latin */
break;
case 1062:
language = "lv"; /* Latvian */
break;
case 1063:
language = "lt"; /* Lithuanian */
break;
case 1086:
language = "ms"; /* Malay - Malaysia */
break;
case 2110:
language = "ms"; /* Malay - Brunei Darussalam */
break;
case 1100:
language = "ml"; /* Malayalam */
break;
case 1082:
language = "mt"; /* Maltese */
break;
case 1112:
language = NULL; /* Manipuri */
break;
case 1153:
language = "mi"; /* Maori - New Zealand */
break;
case 1102:
language = "mr"; /* Marathi */
break;
case 1104:
language = "mn"; /* Mongolian (Cyrillic) */
break;
case 2128:
language = "mn"; /* Mongolian (Mongolian) */
break;
case 1121:
language = "ne"; /* Nepali */
break;
case 2145:
language = "ne"; /* Nepali - India */
break;
case 1044:
language = "no"; /* Norwegian (Bokmᅢᆬl) */
break;
case 2068:
language = "no"; /* Norwegian (Nynorsk) */
break;
case 1096:
language = "or"; /* Oriya */
break;
case 1138:
language = "om"; /* Oromo */
break;
case 1145:
language = NULL; /* Papiamentu */
break;
case 1123:
language = "ps"; /* Pashto */
break;
case 1045:
language = "pl"; /* Polish */
break;
case 1046:
language = "pt"; /* Portuguese - Brazil */
break;
case 2070:
language = "pt"; /* Portuguese - Portugal */
break;
case 1094:
language = "pa"; /* Punjabi */
break;
case 2118:
language = "pa"; /* Punjabi (Pakistan) */
break;
case 1131:
language = "qu"; /* Quecha - Bolivia */
break;
case 2155:
language = "qu"; /* Quecha - Ecuador */
break;
case 3179:
language = "qu"; /* Quecha - Peru */
break;
case 1047:
language = "rm"; /* Rhaeto-Romanic */
break;
case 1048:
language = "ro"; /* Romanian */
break;
case 2072:
language = "ro"; /* Romanian - Moldava */
break;
case 1049:
language = "ru"; /* Russian */
break;
case 2073:
language = "ru"; /* Russian - Moldava */
break;
case 1083:
language = NULL; /* Sami (Lappish) */
break;
case 1103:
language = "sa"; /* Sanskrit */
break;
case 1132:
language = NULL; /* Sepedi */
break;
case 3098:
language = "sr"; /* Serbian (Cyrillic) */
break;
case 2074:
language = "sr"; /* Serbian (Latin) */
break;
case 1113:
language = "sd"; /* Sindhi - India */
break;
case 2137:
language = "sd"; /* Sindhi - Pakistan */
break;
case 1115:
language = "si"; /* Sinhalese - Sri Lanka */
break;
case 1051:
language = "sk"; /* Slovak */
break;
case 1060:
language = "sl"; /* Slovenian */
break;
case 1143:
language = "so"; /* Somali */
break;
case 1070:
language = NULL; /* Sorbian */
break;
case 3082:
language = "es"; /* Spanish - Spain (Modern Sort) */
break;
case 1034:
language = "es"; /* Spanish - Spain (Traditional Sort) */
break;
case 11274:
language = "es"; /* Spanish - Argentina */
break;
case 16394:
language = "es"; /* Spanish - Bolivia */
break;
case 13322:
language = "es"; /* Spanish - Chile */
break;
case 9226:
language = "es"; /* Spanish - Colombia */
break;
case 5130:
language = "es"; /* Spanish - Costa Rica */
break;
case 7178:
language = "es"; /* Spanish - Dominican Republic */
break;
case 12298:
language = "es"; /* Spanish - Ecuador */
break;
case 17418:
language = "es"; /* Spanish - El Salvador */
break;
case 4106:
language = "es"; /* Spanish - Guatemala */
break;
case 18442:
language = "es"; /* Spanish - Honduras */
break;
case 58378:
language = "es"; /* Spanish - Latin America */
break;
case 2058:
language = "es"; /* Spanish - Mexico */
break;
case 19466:
language = "es"; /* Spanish - Nicaragua */
break;
case 6154:
language = "es"; /* Spanish - Panama */
break;
case 15370:
language = "es"; /* Spanish - Paraguay */
break;
case 10250:
language = "es"; /* Spanish - Peru */
break;
case 20490:
language = "es"; /* Spanish - Puerto Rico */
break;
case 21514:
language = "es"; /* Spanish - United States */
break;
case 14346:
language = "es"; /* Spanish - Uruguay */
break;
case 8202:
language = "es"; /* Spanish - Venezuela */
break;
case 1072:
language = NULL; /* Sutu */
break;
case 1089:
language = "sw"; /* Swahili */
break;
case 1053:
language = "sv"; /* Swedish */
break;
case 2077:
language = "sv"; /* Swedish - Finland */
break;
case 1114:
language = NULL; /* Syriac */
break;
case 1064:
language = "tg"; /* Tajik */
break;
case 1119:
language = NULL; /* Tamazight (Arabic) */
break;
case 2143:
language = NULL; /* Tamazight (Latin) */
break;
case 1097:
language = "ta"; /* Tamil */
break;
case 1092:
language = "tt"; /* Tatar */
break;
case 1098:
language = "te"; /* Telugu */
break;
case 1054:
language = "th"; /* Thai */
break;
case 2129:
language = "bo"; /* Tibetan - Bhutan */
break;
case 1105:
language = "bo"; /* Tibetan - People"s Republic of China */
break;
case 2163:
language = "ti"; /* Tigrigna - Eritrea */
break;
case 1139:
language = "ti"; /* Tigrigna - Ethiopia */
break;
case 1073:
language = "ts"; /* Tsonga */
break;
case 1074:
language = "tn"; /* Tswana */
break;
case 1055:
language = "tr"; /* Turkish */
break;
case 1090:
language = "tk"; /* Turkmen */
break;
case 1152:
language = "ug"; /* Uighur - China */
break;
case 1058:
language = "uk"; /* Ukrainian */
break;
case 1056:
language = "ur"; /* Urdu */
break;
case 2080:
language = "ur"; /* Urdu - India */
break;
case 2115:
language = "uz"; /* Uzbek (Cyrillic) */
break;
case 1091:
language = "uz"; /* Uzbek (Latin) */
break;
case 1075:
language = "ve"; /* Venda */
break;
case 1066:
language = "vi"; /* Vietnamese */
break;
case 1106:
language = "cy"; /* Welsh */
break;
case 1076:
language = "xh"; /* Xhosa */
break;
case 1144:
language = NULL; /* Yi */
break;
case 1085:
language = "yi"; /* Yiddish */
break;
case 1130:
language = "yo"; /* Yoruba */
break;
case 1077:
language = "zu"; /* Zulu */
break;
default:
language = NULL;
}
}
DB( g_print (" mswin detection is '%s'\n", language) );
#endif
/* We already set the locale according to the environment, so just
* return early if no language is set in gimprc.
*/
//if (! language)
//#2120855 also test ''
if (! language || strlen (language) == 0)
return;
#if MYDEBUG == 1
const gchar * const *locales = g_get_language_names();
g_print(" system LANGUAGE\n");
for (; locales != NULL && *locales != NULL; locales++)
{
g_print(" %s\n", *locales);
}
#endif
DB( g_print (" setenv to '%s' and LC_ALL to empty\n", language) );
g_setenv ("LANGUAGE", language, TRUE);
setlocale (LC_ALL, "");
}
homebank-5.9.7/src/hub-reptotal.c 0000664 0001750 0001750 00000046244 15005625006 016166 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-mainwindow.h"
#include "hub-reptotal.h"
#include "gtk-chart.h"
#include "list-report.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static gint list_topspending_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint pos1, pos2, retval = 0;
gdouble val1, val2;
gtk_tree_model_get(model, a,
LST_TOPSPEND_POS, &pos1,
LST_TOPSPEND_AMOUNT, &val1,
-1);
gtk_tree_model_get(model, b,
LST_TOPSPEND_POS, &pos2,
LST_TOPSPEND_AMOUNT, &val2,
-1);
//#1933164 should return
// > 0 if a sorts before b
// = 0 if a sorts with b
// < 0 if a sorts after b
switch(sortcol)
{
case LST_TOPSPEND_POS:
retval = pos1 - pos2;
//DB( g_print(" sort %3d = %3d :: %d\n", pos1, pos2, retval) );
break;
case LST_TOPSPEND_AMOUNT:
//retval = (ABS(val1) - ABS(val2)) > 0 ? -1 : 1;
retval = (val1 - val2) > 0 ? -1 : 1;
//DB( g_print(" sort %.2f = %.2f :: %d\n", val1, val2, retval) );
break;
}
return retval;
}
static GtkWidget *create_list_topspending(void)
{
GtkTreeStore *store;
GtkWidget *view;
/* create list store */
store = lst_report_new();
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
//5.7
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_TOPSPEND_POS , list_topspending_compare_func, GINT_TO_POINTER(LST_TOPSPEND_POS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_TOPSPEND_AMOUNT , list_topspending_compare_func, GINT_TO_POINTER(LST_TOPSPEND_AMOUNT), NULL);
//gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), list_topspending_compare_func, NULL, NULL);
return(view);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void ui_hub_reptotal_update(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
gchar *title = NULL, *fmt;
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
DB( g_print("\n[hub-total] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//TODO: reuse this ?
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->hubtot_total, GLOBALS->kcur, GLOBALS->minor);
switch( PREFS->hub_tot_view )
{
//todo: rework this
case HUB_TOT_VIEW_TOPCAT:
fmt = _("Top %d Spending / Category");
if(PREFS->hub_tot_raw)
fmt = _("Top %d Expense / Category");
title = g_strdup_printf(fmt, PREFS->rep_maxspenditems);
break;
case HUB_TOT_VIEW_TOPPAY:
fmt = _("Top %d Spending / Payee");
if(PREFS->hub_tot_raw)
fmt = _("Top %d Expense / Payee");
title = g_strdup_printf(fmt, PREFS->rep_maxspenditems);
break;
case HUB_TOT_VIEW_TOPACC:
fmt = _("Top %d Spending / Account");
if(PREFS->hub_tot_raw)
fmt = _("Top %d Expense / Account");
title = g_strdup_printf(fmt, PREFS->rep_maxspenditems);
break;
case HUB_TOT_VIEW_ACCBAL:
title = g_strdup_printf(_("Account Balance"));
break;
case HUB_TOT_VIEW_GRPBAL:
title = g_strdup_printf(_("Account Group Balance"));
break;
}
gtk_chart_set_color_scheme(GTK_CHART(data->RE_hubtot_chart), PREFS->report_color_scheme);
gtk_chart_set_smallfont (GTK_CHART(data->RE_hubtot_chart), PREFS->rep_smallfont);
gtk_chart_set_currency(GTK_CHART(data->RE_hubtot_chart), GLOBALS->kcur);
//set column1 != column2 will dual display
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_hubtot));
gtk_chart_set_datas_total(GTK_CHART(data->RE_hubtot_chart), model, LST_TOPSPEND_AMOUNT, LST_TOPSPEND_AMOUNT, title, strbuffer);
g_free(title);
}
void ui_hub_reptotal_clear(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
DB( g_print("\n[hub-total] clear\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_hubtot));
gtk_tree_store_clear (GTK_TREE_STORE(model));
}
void ui_hub_reptotal_populate(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
GtkTreeIter iter, parent, *tmpparent;
DataTable *dt;
gint range;
gint tmpsrc;
guint i, max_items, flags;
gdouble total, other;
gboolean tmpaccbal, valid;
DB( g_print("\n[hub-total] populate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->hubtot_filter == NULL)
return;
tmpsrc = REPORT_GRPBY_CATEGORY;
tmpaccbal = FALSE;
switch( PREFS->hub_tot_view )
{
case HUB_TOT_VIEW_TOPCAT:
tmpsrc = REPORT_GRPBY_CATEGORY;
break;
case HUB_TOT_VIEW_TOPPAY:
tmpsrc = REPORT_GRPBY_PAYEE;
break;
case HUB_TOT_VIEW_TOPACC:
tmpsrc = REPORT_GRPBY_ACCOUNT;
break;
case HUB_TOT_VIEW_ACCBAL:
tmpsrc = REPORT_GRPBY_ACCOUNT;
tmpaccbal = TRUE;
break;
case HUB_TOT_VIEW_GRPBAL:
tmpsrc = REPORT_GRPBY_ACCGROUP;
tmpaccbal = TRUE;
break;
}
//type = hbtk_radio_button_get_active(GTK_CONTAINER(data->RA_type));
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_hubtot_range));
PREFS->hub_tot_range = range;
DB( g_print(" range=%d\n", range) );
//if(range == FLT_RANGE_MISC_CUSTOM)
// return;
filter_preset_daterange_set(data->hubtot_filter, range, 0);
//#1989211 option to include xfer by default
if(PREFS->stat_includexfer == FALSE)
filter_preset_type_set(data->hubtot_filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
else
filter_preset_type_set(data->hubtot_filter, FLT_TYPE_ALL, FLT_INCLUDE);
DB( hb_print_date(data->hubtot_filter->mindate, "min:") );
DB( hb_print_date(data->hubtot_filter->maxdate, "max:") );
total = 0.0;
GQueue *txn_queue = hbfile_transaction_get_partial(data->hubtot_filter->mindate, data->hubtot_filter->maxdate);
flags = REPORT_COMP_FLG_NONE;
if(tmpaccbal)
flags |= REPORT_COMP_FLG_BALANCE;
if( !tmpaccbal )
{
DB( g_print(" - rawamount=%d\n", PREFS->hub_tot_raw) );
if( PREFS->hub_tot_raw == FALSE )
flags |= REPORT_COMP_FLG_CATSIGN;
//todo: future option
flags |= REPORT_COMP_FLG_SPENDING;
//flags |= REPORT_COMP_FLG_REVENUE;
}
dt = report_compute(tmpsrc, REPORT_INTVL_NONE, data->hubtot_filter, txn_queue, flags);
g_queue_free (txn_queue);
if(dt)
{
//todo: should use clear func
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_hubtot));
gtk_tree_store_clear (GTK_TREE_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_hubtot), NULL); /* Detach model from view */
DB( g_print(" rows=%d\n", dt->nbrows) );
// insert into the treeview
for(i=0 ; inbrows ; i++)
{
DataRow *dr;
gdouble value;
guint32 reskey;
//since 5.7 we use the dt-keylst here to insert cat before subcat
reskey = dt->keylist[i];
dr = report_data_get_row(dt, reskey);
DB( g_printf(" eval %d: %d '%s' %.2f %.2f = %.2f\n", i, reskey, dr->label, dr->rowexp, dr->rowinc, (dr->rowexp + dr->rowinc) ) );
//if( tmptype == REPORT_TYPE_EXPENSE && !dr->expense[0] ) continue;
//if( tmptype == REPORT_TYPE_INCOME && !dr->income[1] ) continue;
if( !dr->rowexp && !dr->rowinc )
{
DB( g_printf(" >skip: no data\n") );
continue;
}
//if( tmpsrc == REPORT_GRPBY_ACCOUNT && (i == 0) )
// continue;
//#2031245
/*if( tmpaccbal == TRUE )
value = dr->rowexp + dr->rowinc;
else
value = dr->rowexp;*/
//#2043523 always net value
value = dr->rowexp + dr->rowinc;
// manage the toplevel for category
tmpparent = NULL;
if( tmpsrc == REPORT_GRPBY_CATEGORY )
{
Category *tmpcat = da_cat_get(reskey);
if( tmpcat != NULL)
{
//if( list_topspending_get_top_level (GTK_TREE_MODEL(model), tmpcat->parent, &parent) == TRUE )
if( hbtk_tree_store_get_top_level(GTK_TREE_MODEL(model), LST_TOPSPEND_KEY, tmpcat->parent, &parent) )
{
tmpparent = &parent;
}
//compute total
if( tmpcat->parent == 0 )
{
if(value < 0.0 )
total += value;
}
}
}
else
{
if(value < 0.0 || tmpaccbal == TRUE )
total += value;
}
if( value < 0.0 || tmpaccbal == TRUE )
{
// append test
gtk_tree_store_append (GTK_TREE_STORE(model), &iter, tmpparent);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter,
LST_TOPSPEND_POS, i,
LST_TOPSPEND_KEY, reskey,
LST_TOPSPEND_NAME, dr->label,
LST_TOPSPEND_AMOUNT, value,
//LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5),
-1);
DB( g_printf(" >insert\n") );
}
else
{
DB( g_printf(" >skip: no data balance mode\n") );
}
}
//sort by expense descending
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), LST_TOPSPEND_AMOUNT, GTK_SORT_DESCENDING);
//5.7 order & limitation moved here
if( tmpaccbal == FALSE )
{
other = 0.0;
i = 0;
max_items = (guint)PREFS->rep_maxspenditems;
{
GtkTreeIter remiter, child;
gdouble othamt;
gboolean okchilditer, do_remove;
gint cpos;
DB( g_print(" aggregate items\n") );
valid = gtk_tree_model_get_iter_first(model, &iter);
while( valid )
{
DB( g_print(" freeze position %d\n", i) );
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
LST_TOPSPEND_POS, i++,
-1);
//2063145 also store child position
okchilditer = gtk_tree_model_iter_children (GTK_TREE_MODEL(model), &child, &iter);
cpos = 0;
while( okchilditer )
{
DB( g_print(" freeze child position %d\n", cpos) );
gtk_tree_store_set(GTK_TREE_STORE(model), &child,
LST_TOPSPEND_POS, cpos++,
-1);
okchilditer = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
do_remove = (i > max_items ) ? TRUE : FALSE;
remiter = iter;
valid = gtk_tree_model_iter_next(model, &iter);
if( do_remove )
{
gtk_tree_model_get (GTK_TREE_MODEL(model), &remiter,
LST_TOPSPEND_AMOUNT, &othamt,
-1);
if(othamt < 0.0)
other += othamt;
DB( g_print(" other += %.2f\n", othamt) );
hbtk_tree_store_remove_iter_with_child(model, &remiter);
}
}
// append 'Others'
if(ABS(other) > 0)
{
DB( g_print(" - %d : %s k='%d' v='%f'\n", max_items+1, "Other", 0, other) );
gtk_tree_store_append (GTK_TREE_STORE(model), &iter, NULL);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter,
LST_TOPSPEND_POS, max_items+1,
LST_TOPSPEND_KEY, 0,
LST_TOPSPEND_NAME, _("Other"),
LST_TOPSPEND_AMOUNT, other,
//LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5),
-1);
}
}
//sort by pos to have Other at bottom
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), LST_TOPSPEND_POS, GTK_SORT_ASCENDING);
}
/* Re-attach model to view */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_hubtot), model);
g_object_unref(model);
// update chart and widgets
{
gchar *daterange;
data->hubtot_total = total;
ui_hub_reptotal_update(widget, data);
daterange = filter_daterange_text_get(data->hubtot_filter);
gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_hubtot_range), daterange);
g_free(daterange);
}
//TODO: later needs to keep this until dispose LV
da_datatable_free (dt);
}
}
static void
ui_hub_reptotal_activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_string (old_state, NULL),
g_variant_get_string (new_state, NULL)) );
PREFS->hub_tot_view = HUB_TOT_VIEW_NONE;
if( !strcmp("topcat", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tot_view = HUB_TOT_VIEW_TOPCAT;
else
if( !strcmp("toppay", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tot_view = HUB_TOT_VIEW_TOPPAY;
else
if( !strcmp("topacc", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tot_view = HUB_TOT_VIEW_TOPACC;
else
if( !strcmp("accbal", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tot_view = HUB_TOT_VIEW_ACCBAL;
else
if( !strcmp("grpbal", g_variant_get_string(new_state, NULL)) )
PREFS->hub_tot_view = HUB_TOT_VIEW_GRPBAL;
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
ui_hub_reptotal_populate(GLOBALS->mainwindow, NULL);
}
static void
ui_hub_reptotal_activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_boolean (old_state),
g_variant_get_boolean (new_state)) );
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
PREFS->hub_tot_raw = g_variant_get_boolean (new_state);
ui_hub_reptotal_populate(GLOBALS->mainwindow, NULL);
}
static const GActionEntry actions[] = {
// name, function(), type, state,
{ "view", ui_hub_reptotal_activate_radio , "s", "'topcat'", NULL, {0,0,0} },
{ "raw" , ui_hub_reptotal_activate_toggle, NULL, "false" , NULL, {0,0,0} },
};
void ui_hub_reptotal_setup(struct hbfile_data *data)
{
GAction *action;
GVariant *new_state;
DB( g_print("\n[hub-total] setup\n") );
data->hubtot_filter = da_flt_malloc();
filter_reset(data->hubtot_filter);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_hubtot_range), PREFS->hub_tot_range);
//#1989211 option to include xfer by default
if(PREFS->stat_includexfer == FALSE)
filter_preset_type_set(data->hubtot_filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
if( !G_IS_SIMPLE_ACTION_GROUP(data->hubtot_action_group) )
return;
action = g_action_map_lookup_action (G_ACTION_MAP (data->hubtot_action_group), "view");
if( action )
{
const gchar *value = "cat";
switch( PREFS->hub_tot_view )
{
case HUB_TOT_VIEW_TOPCAT: value = "topcat"; break;
case HUB_TOT_VIEW_TOPPAY: value = "toppay"; break;
case HUB_TOT_VIEW_TOPACC: value = "topacc"; break;
case HUB_TOT_VIEW_ACCBAL: value = "accbal"; break;
case HUB_TOT_VIEW_GRPBAL: value = "grpbal"; break;
}
new_state = g_variant_new_string (value);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
}
//#2066161 raw amount persist
action = g_action_map_lookup_action (G_ACTION_MAP (data->hubtot_action_group), "raw");
if( action )
{
GVariant *new_bool = g_variant_new_boolean(PREFS->hub_tot_raw);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_bool);
}
}
void ui_hub_reptotal_dispose(struct hbfile_data *data)
{
DB( g_print("\n[hub-total] dispose\n") );
gtk_chart_set_datas_none(GTK_CHART(data->RE_hubtot_chart));
da_flt_free(data->hubtot_filter);
data->hubtot_filter = NULL;
}
GtkWidget *ui_hub_reptotal_create(struct hbfile_data *data)
{
GtkWidget *hub, *hbox, *bbox, *tbar;
GtkWidget *label, *widget, *image;
DB( g_print("\n[hub-total] create\n") );
// /!\ this widget has to be freed
widget = (GtkWidget *)create_list_topspending();
data->LV_hubtot = widget;
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margins(GTK_WIDGET(hub), 0, SPACING_SMALL, SPACING_SMALL, SPACING_SMALL);
data->GR_hubtot = hub;
/* chart + listview */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hbtk_box_prepend (GTK_BOX (hub), hbox);
widget = gtk_chart_new(CHART_TYPE_PIE);
data->RE_hubtot_chart = widget;
gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
gtk_chart_set_currency(GTK_CHART(widget), GLOBALS->kcur);
gtk_chart_show_legend(GTK_CHART(widget), TRUE, TRUE);
hbtk_box_prepend (GTK_BOX (hbox), widget);
//list toolbar
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (hub), tbar);
label = make_label_group(_("Total chart"));
data->LB_hubtot = label;
gtk_box_prepend (GTK_BOX (tbar), label);
/* total + date range */
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = gtk_menu_button_new();
gtk_box_prepend (GTK_BOX (bbox), widget);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
image = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
g_object_set (widget, "image", image, NULL);
GSimpleActionGroup *group = g_simple_action_group_new ();
data->hubtot_action_group = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
//gmenu test (see test folder into gtk)
GMenu *menu, *section;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append_section(menu, _("Top by"), G_MENU_MODEL(section));
g_menu_append (section, _("Category") , "actions.view::topcat");
g_menu_append (section, _("Payee") , "actions.view::toppay");
g_menu_append (section, _("Account") , "actions.view::topacc");
//g_object_unref (section);
//5.8
//section = g_menu_new ();
//g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("Raw amount"), "actions.raw");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section (menu, _("Balance"), G_MENU_MODEL(section));
g_menu_append (section, _("Account"), "actions.view::accbal");
g_menu_append (section, _("Account group"), "actions.view::grpbal");
g_object_unref (section);
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
data->CY_hubtot_range = make_daterange(NULL, DATE_RANGE_FLAG_CUSTOM_HIDDEN);
gtk_box_append (GTK_BOX (tbar), data->CY_hubtot_range);
//hbtk_radio_button_connect (GTK_CONTAINER(data->RA_type), "toggled", G_CALLBACK (ui_hub_reptotal_populate), NULL);
g_signal_connect (data->CY_hubtot_range, "changed", G_CALLBACK (ui_hub_reptotal_populate), NULL);
return hub;
}
homebank-5.9.7/src/hb-transaction.h 0000644 0001750 0001750 00000012367 15005625143 016500 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_TRANSACTION_H__
#define __HB_TRANSACTION_H__
#include "hb-types.h"
struct _transaction
{
gdouble amount;
guint32 kacc;
guchar paymode;
guchar grpflg;
gushort flags;
guint32 kpay;
guint32 kcat;
gchar *memo;
guint32 date;
gushort pos;
gushort status;
gchar *number; //info < 5.8
guint32 *tags;
guint32 kxfer; //strong link xfer key
guint32 kxferacc;
gdouble xferamount; //xfer target amount
GPtrArray *splits;
/* unsaved datas */
guint32 kcur; //init at loadxml (preprend) + add
gushort dspflags; //
guchar dupgid; //duplicate group id
guchar matchrate; //used only when find xfer target
gdouble balance; //init at dsp balance refresh
//GList *same; //used for import todo: change this
};
// saved flags -- data
//gushort is 2 bytes / 16 bits
//FREE (1<<0)
#define OF_INCOME (1<< 1)
//FREE (1<< 2)
#define OF_INTXFER (1<< 3)
#define OF_ADVXFER (1<< 4) //xfer with != kcur
#define OF_REMIND (1<< 5) //added 5.9
#define OF_CHEQ2 (1<< 6)
//FREE (1<< 7)
#define OF_SPLIT (1<< 8)
#define OF_ISIMPORT (1<< 9) //added 5.9
#define OF_ISPAST (1<<10) //added 5.9
//deprecated > 5.x
#define OLDF_VALID (1<< 0)
#define OLDF_REMIND (1<< 5)
//deprecated > 5.9
#define OLDF_AUTO (1<< 2) //scheduled
#define OLDF_LIMIT (1<< 7) //scheduled
#define OLDF_ADDED (1<< 9) //was 1<<3 < 5.3
#define OLDF_CHANGED (1<<10) //was 1<<4 < 5.3
#define OLDF_PREFILLED (1<<11)
// unsaved flags -- display/session
#define FLAG_TMP_ADDED (1<< 1)
#define FLAG_TMP_EDITED (1<< 2)
#define FLAG_TMP_PREFILLED (1<< 3) //scheduled
#define FLAG_TMP_OVER (1<< 4)
#define FLAG_TMP_LOWBAL (1<< 5)
#define FLAG_TMP_DUPSRC (1<< 9)
#define FLAG_TMP_DUPDST (1<<10)
#define FLAG_TMP_CHKSIGN (1<< 11)
typedef enum {
TXN_STATUS_NONE, //0
TXN_STATUS_CLEARED, //1
TXN_STATUS_RECONCILED, //2
TXN_STATUS_VOID //3 (OLD 4)
} HbTxnStatus;
#define TXN_OLDSTATUS_REMIND 3
#define TXN_OLDSTATUS_VOID 4
enum {
TXN_MARK_NONE,
TXN_MARK_DUPSRC,
TXN_MARK_DUPDST
};
enum
{
TXN_TYPE_EXPENSE,
TXN_TYPE_INCOME,
TXN_TYPE_INTXFER
};
Transaction *da_transaction_malloc(void);
//Transaction *da_transaction_copy(Transaction *src_txn, Transaction *dst_txn);
Transaction *da_transaction_init(Transaction *txn, guint32 kacc);
Transaction *da_transaction_init_from_template(Transaction *txn, Archive *arc);
Transaction *da_transaction_set_default_template(Transaction *txn);
Transaction *da_transaction_clone(Transaction *src_item);
void da_transaction_free(Transaction *item);
GList *da_transaction_new(void);
void da_transaction_destroy(void);
void da_transaction_queue_sort(GQueue *queue);
GList *da_transaction_sort(GList *list);
gboolean da_transaction_prepend(Transaction *item);
gboolean da_transaction_insert_sorted(Transaction *item);
void da_transaction_set_flag(Transaction *item);
void da_transaction_consistency(Transaction *item);
typedef enum
{
TXN_DLG_ACTION_NONE,
TXN_DLG_ACTION_ADD,
TXN_DLG_ACTION_INHERIT,
TXN_DLG_ACTION_EDIT
} HbTxnDlgAction;
typedef enum
{
TXN_DLG_TYPE_NONE,
TXN_DLG_TYPE_TXN,
TXN_DLG_TYPE_TPL,
TXN_DLG_TYPE_SCH
} HbTxnDlgType;
guint da_transaction_length(void);
void transaction_remove(Transaction *ope);
void transaction_changed(Transaction *txn, gboolean saverecondate);
gboolean da_transaction_insert_memo(gchar *memo, guint32 date);
gboolean da_transaction_insert_memos(Transaction *txn);
Transaction *transaction_add(GtkWindow *parent, gboolean addmode, Transaction *ope);
gchar *transaction_get_status_string(Transaction *txn);
gboolean transaction_is_balanceable(Transaction *ope);
gint transaction_get_type(Transaction *txn);
gboolean transaction_acc_move(Transaction *txn, guint32 okacc, guint32 nkacc);
Transaction *transaction_xfer_child_new_from_txn(Transaction *txn);
Transaction *transaction_xfer_child_strong_get(Transaction *src);
gint transaction_xfer_search_or_add_child(GtkWindow *parent, gboolean addmode, Transaction *ope, guint32 kdstacc);
void transaction_xfer_change_to_normal(Transaction *ope);
void transaction_xfer_change_to_child(Transaction *ope, Transaction *child);
void transaction_xfer_child_sync(Transaction *s_txn, Transaction *child);
void transaction_xfer_remove_child(Transaction *src);
Transaction *transaction_old_get_child_transfer(Transaction *src);
guint transaction_auto_all_from_payee(GList *txnlist);
gint transaction_similar_mark(Account *acc, guint32 daygap);
void transaction_similar_unmark(Account *acc);
gint transaction_check_chkcatsign_mark(Account *acc);
void transaction_check_chkcatsign_unmark(Account *acc);
#endif
homebank-5.9.7/src/hbtk-decimalentry.c 0000664 0001750 0001750 00000033546 15005625122 017166 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2023 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//#include
#include /* atoi, atof, atol */
#include /* gettext */
#include
#include
#include "hb-types.h"
#include "enums.h"
#include "hbtk-decimalentry.h"
//#include "hb-currency.h"
#include "hb-misc.h"
//TODO: move this after GTK4
//#include "ui-widgets.h"
/* = = = = = = = = = = = = = = = = */
#define DB(x) //(x);
#define DBI(x) //(x);
enum {
VALUE_CHANGED,
LAST_SIGNAL
};
static guint decimalentry_signals[LAST_SIGNAL] = {0,};
//G_DEFINE_TYPE(HbtkDecimalEntry, hbtk_decimal_entry, GTK_TYPE_BOX)
G_DEFINE_TYPE_WITH_CODE(HbtkDecimalEntry, hbtk_decimal_entry, GTK_TYPE_ENTRY, G_ADD_PRIVATE (HbtkDecimalEntry))
/* = = = = = = = = = = = = = = = = */
static const gdouble fac[9] =
{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
static double
my_round(const gdouble d, guint digits)
{
gdouble out;
digits = MIN(digits, 8);
out = ((gint64) (d < 0 ? (d * fac[digits]) - 0.5 : (d * fac[digits]) + 0.5)) / fac[digits];
return out;
}
static gint
_str_count_operator(gchar *text)
{
gint count = 0;
gchar *s = text;
//sign only at 1st pos, then count others
if( *s=='+' || *s=='-' )
s++;
while( *s++ )
{
if( *s=='+' || *s=='-' || *s=='*' || *s=='/')
count++;
}
return count;
}
static gboolean
_str_is_decimal(const gchar* txt, guint digits)
{
const gchar *s;
guint idx = 0, dccnt=0, npcnt=0, dpcnt=0;
gboolean retval = TRUE;
s = txt;
while( *s )
{
if( (idx > 0 && (*s=='-' || *s=='+'))
|| ( dccnt > 1)
|| ( dpcnt > (digits-1))
)
{
retval = FALSE;
break;
}
if( *s=='.' )
dccnt++; //decimal char
//count number parts
if( *s >= 0x30 && *s <= 0x39 )
{
if( dccnt == 0 )
npcnt++; //numeric part
else
dpcnt++; //decimal part
}
s++; idx++;
}
return retval;
}
static gdouble
_amount_operation(gchar operator, gdouble prvval, gdouble curval)
{
gdouble outval = 0.0;
switch( operator )
{
case '-': outval = prvval - curval; break;
case '+': outval = prvval + curval; break;
case '*': outval = prvval * curval; break;
case '/':
if( curval != 0.0 )
outval = prvval / curval;
break;
}
DB( g_print("compute: %g %c %g = %g\n", prvval, operator, curval, outval) );
return outval;
}
static gdouble
_parse_amount_test (const gchar *string, guint digits, gboolean *isvalid, gboolean *iscalc)
{
gdouble newval, nxtval;
const gchar *s, *remainder;
gchar pc, operator;
gboolean tmpvalid = TRUE;
gboolean tmpcalc = FALSE;
g_return_val_if_fail (string != NULL, 0.0);
DB( g_print("\n[decimalentry] _parse_amount_test\n") );
DB( g_print("rawtxt: '%s'\n", string) );
newval = 0.0;
pc = 0x20;
operator = '?';
s = remainder = string;
while( *s )
{
// *=x2A +=x2B -=x2D /=x2F :: .=x2E ,=x2C
if( (*s=='.' && pc=='.' ) //forbid ..
|| ((*s=='/' || *s=='*') && (pc=='/' || pc=='*')) //forbid // ** /* */
|| ((*s=='/' || *s=='*') && (pc=='+' || pc=='-')) //forbid -/ -* +/ +*
)
{
tmpvalid = FALSE;
goto abort;
}
if( pc >= 0x30 && pc <= 0x39 )
{
// reach an operator ?
if(*s=='-' || *s=='+' || *s=='/' || *s=='*')
{
gchar *tmpbuf = g_strndup(remainder, (gsize)(s - remainder));
tmpcalc = TRUE;
tmpvalid = _str_is_decimal(tmpbuf, digits);
DB( g_print(" chknum: '%s' :: %d\n", tmpbuf, tmpvalid) );
nxtval = g_strtod(tmpbuf, NULL);
g_free(tmpbuf);
if(!tmpvalid)
goto abort;
DB( g_print("nxtval: %g operator: '%c'\n", nxtval, operator) );
if( operator == '?' )
newval = nxtval;
else
newval = _amount_operation(operator, newval, nxtval);
remainder = s + 1;
operator = *s;
}
}
pc = *s++;
}
if (*remainder)
{
//store rawnumber
tmpvalid = _str_is_decimal(remainder, digits);
DB( g_print(" chknum: '%s' :: %d\n", remainder, tmpvalid) );
if(!tmpvalid)
goto abort;
nxtval = g_strtod(remainder, NULL);
DB( g_print("nxtval: %g operator: '%c'\n", nxtval, operator) );
if( operator == '?' )
newval = nxtval;
else
newval = _amount_operation(operator, newval, nxtval);
}
abort:
if( iscalc != NULL )
*iscalc = tmpcalc;
if( isvalid != NULL )
*isvalid = tmpvalid;
DB( g_print(" out > %g\n", newval) );
return newval;
}
/* = = = = = = = = = = = = = = = = */
static void
hbtk_decimal_entry_default_output (HbtkDecimalEntry *decimalentry)
{
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
gchar *buf;
DB( g_print("--------\n[decimalentry] output (%d digits)\n", priv->digits) );
g_signal_handler_block(G_OBJECT (priv->entry), priv->hid_insert);
g_signal_handler_block(G_OBJECT (priv->entry), priv->hid_changed);
buf = g_strdup_printf ("%0.*f", priv->digits, priv->value);
DB( g_print(" replace with '%s'\n", buf) );
gtk_entry_set_text (GTK_ENTRY (priv->entry), buf);
g_free (buf);
g_signal_handler_unblock(G_OBJECT (priv->entry), priv->hid_changed);
g_signal_handler_unblock(G_OBJECT (priv->entry), priv->hid_insert);
}
static void
hbtk_decimal_value_change (HbtkDecimalEntry *decimalentry, gdouble newval)
{
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
gboolean doemit = FALSE;
newval = my_round(newval, priv->digits);
if(priv->value != newval)
doemit = TRUE;
priv->value = newval;
hbtk_decimal_entry_default_output(decimalentry);
if(doemit == TRUE)
{
DB( g_print("\n **emit 'value-changed' signal**\n") );
g_signal_emit_by_name (decimalentry, "value-changed", NULL, NULL);
}
}
static void
hbtk_decimal_validate (HbtkDecimalEntry *decimalentry)
{
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
gdouble newval;
gboolean iscalc = FALSE;
DB( g_print("\n[decimalentry] validate\n") );
if( priv->valid == FALSE )
{
DB( g_print(" txt is invalid\n") );
priv->value = 0;
return;
}
gchar *curtxt = (gchar *)gtk_entry_get_text(GTK_ENTRY(priv->entry));
newval = _parse_amount_test(curtxt, priv->digits, NULL, &iscalc);
//simple amount
priv->forcedsign = FALSE;
if(!iscalc)
{
DB( g_print(" simple amount\n") );
//force sign
if( (*curtxt == '-') || (*curtxt == '+') )
{
DB( g_print(" force with sign\n") );
priv->forcedsign = TRUE;
if( (*curtxt == '-' && newval > 0) || (*curtxt == '+' && newval < 0) )
newval = newval * -1;
}
//default sign
/*else
{
DB( g_print(" force with privdata\n") );
if( (priv->income == TRUE && newval < 0) || (priv->income == FALSE && newval > 0) )
newval = newval * -1;
}*/
}
hbtk_decimal_value_change(decimalentry, newval);
}
static void
hbtk_decimal_entry_insert_text_handler (GtkEntry *entry, gchar *nt, gint length, gint *position, gpointer data)
{
HbtkDecimalEntry *decimalentry = HBTK_DECIMAL_ENTRY(entry);
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
DBI( g_print("\n[decimalentry] text-handler\n") );
DBI( g_print(" len:%d pos:%d nt:'%s' 0x%x\n", length, *position, nt, *nt) );
g_signal_handler_block(G_OBJECT (priv->entry), priv->hid_insert);
//most common: 1 char
if( (length == 1) )
{
//replace , by .
if(*nt == 0x2C)
{
*nt = 0x2E;
}
//allow only: *+,-./0123456789
if( (*nt < 0x2A) || (*nt > 0x39) )
goto stop;
//DBI( g_print(" insert char '%s'\n", nt) );
gtk_editable_insert_text (GTK_EDITABLE(priv->entry), nt, length, position);
}
//less common: pasted text
else
{
//TODO: maybe manage pasted computing later
if( _str_count_operator(nt) > 0 )
{
DBI( g_print(" insert bad computing '%s'\n", nt) );
gtk_editable_insert_text (GTK_EDITABLE(priv->entry), nt, length, position);
}
else
{
if( _str_is_decimal(nt, priv->digits) == FALSE )
{
DBI( g_print(" insert bad number '%s'\n", nt) );
gtk_editable_insert_text (GTK_EDITABLE(priv->entry), nt, length, position);
}
else
{
gchar *cleantxt = hb_string_dup_raw_amount_clean(nt, priv->digits);
gsize cleanlength = strlen(cleantxt);
DBI( g_print(" insert clean '%s'\n", cleantxt) );
gtk_editable_insert_text (GTK_EDITABLE(priv->entry), cleantxt, cleanlength, position);
g_free(cleantxt);
}
}
}
stop:
g_signal_handler_unblock(G_OBJECT (priv->entry), priv->hid_insert);
g_signal_stop_emission_by_name (G_OBJECT (priv->entry), "insert-text");
}
static void
hbtk_decimal_entry_cb_changed(GtkWidget *widget, gpointer user_data)
{
HbtkDecimalEntry *decimalentry = HBTK_DECIMAL_ENTRY(widget);
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
const gchar *curtxt;
gboolean isvalid;
DB( g_print("\n[decimalentry] changed\n") );
//check validity
curtxt = gtk_entry_get_text(GTK_ENTRY(priv->entry));
_parse_amount_test(curtxt, priv->digits, &isvalid, NULL);
DBI( g_print(" check '%s' %d\n", curtxt, isvalid) );
priv->valid = isvalid;
//test error class
if( !isvalid )
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_ERROR);
else
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET(priv->entry)), GTK_STYLE_CLASS_ERROR);
}
static void
hbtk_decimal_entry_cb_activate(GtkWidget *widget, gpointer user_data)
{
HbtkDecimalEntry *decimalentry = HBTK_DECIMAL_ENTRY(widget);
DB( g_print("\n[decimalentry] entry_activate\n") );
hbtk_decimal_validate(decimalentry);
}
static gboolean
hbtk_decimal_entry_cb_focus_out(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
HbtkDecimalEntry *decimalentry = HBTK_DECIMAL_ENTRY(widget);
DB( g_print("\n[decimalentry] focus-out-event %d\n", gtk_widget_is_focus(GTK_WIDGET(decimalentry))) );
hbtk_decimal_validate(decimalentry);
return FALSE;
}
static gboolean
hbtk_decimal_entry_cb_focus_in(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
HbtkDecimalEntry *decimalentry = HBTK_DECIMAL_ENTRY(widget);
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
DB( g_print("\n[decimalentry] focus-in-event %d\n", gtk_widget_is_focus(GTK_WIDGET(decimalentry))) );
if( priv->valid == TRUE && priv->value == 0.0 )
{
g_signal_handler_block(G_OBJECT (priv->entry), priv->hid_insert);
g_signal_handler_block(G_OBJECT (priv->entry), priv->hid_changed);
gtk_editable_select_region(GTK_EDITABLE(priv->entry), 0, -1);
gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
g_signal_handler_unblock(G_OBJECT (priv->entry), priv->hid_changed);
g_signal_handler_unblock(G_OBJECT (priv->entry), priv->hid_insert);
return FALSE;
}
return TRUE;
}
static void
hbtk_decimal_entry_class_init (HbtkDecimalEntryClass *class)
{
DB( g_print("\n[decimalentry] class_init\n") );
decimalentry_signals[VALUE_CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (HbtkDecimalEntryClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
hbtk_decimal_entry_init (HbtkDecimalEntry *decimalentry)
{
HbtkDecimalEntryPrivate *priv;
DB( g_print("\n[decimalentry] init\n") );
decimalentry->priv = hbtk_decimal_entry_get_instance_private(decimalentry);
priv = decimalentry->priv;
priv->valid = TRUE;
priv->digits = 2;
//priv->income = FALSE;
priv->entry = GTK_WIDGET(decimalentry);
//todo: see if really useful
gtk_entry_set_width_chars(GTK_ENTRY(priv->entry), 16);
gtk_entry_set_alignment(GTK_ENTRY(priv->entry), 1.0);
gtk_entry_set_max_width_chars(GTK_ENTRY(priv->entry), 16);
//gtk_box_pack_start (GTK_BOX (decimalentry), priv->entry, TRUE, TRUE, 0);
priv->hid_insert = g_signal_connect(G_OBJECT(priv->entry), "insert-text",
G_CALLBACK(hbtk_decimal_entry_insert_text_handler), NULL);
priv->hid_changed = g_signal_connect(G_OBJECT(priv->entry), "changed",
G_CALLBACK(hbtk_decimal_entry_cb_changed), NULL);
g_signal_connect (priv->entry, "activate",
G_CALLBACK (hbtk_decimal_entry_cb_activate), NULL);
g_signal_connect_after (priv->entry, "focus-in-event",
G_CALLBACK (hbtk_decimal_entry_cb_focus_in), NULL);
g_signal_connect_after (priv->entry, "focus-out-event",
G_CALLBACK (hbtk_decimal_entry_cb_focus_out), NULL);
}
/* = = = = = = = = public function = = = = = = = = */
//probably need get/set digit
//probably need _update method here
gdouble
hbtk_decimal_entry_get_value (HbtkDecimalEntry *decimalentry)
{
g_return_val_if_fail (HBTK_IS_DECIMAL_ENTRY (decimalentry), 0.0);
return decimalentry->priv->value;
}
gboolean
hbtk_decimal_entry_get_forcedsign (HbtkDecimalEntry *decimalentry)
{
g_return_val_if_fail (HBTK_IS_DECIMAL_ENTRY (decimalentry), FALSE);
return decimalentry->priv->forcedsign;
}
void
hbtk_decimal_entry_set_value (HbtkDecimalEntry *decimalentry, gdouble value)
{
g_return_if_fail (HBTK_IS_DECIMAL_ENTRY (decimalentry));
hbtk_decimal_value_change(decimalentry, value);
}
void
hbtk_decimal_entry_set_digits (HbtkDecimalEntry *decimalentry, guint value)
{
g_return_if_fail (HBTK_IS_DECIMAL_ENTRY (decimalentry));
decimalentry->priv->digits = value;
hbtk_decimal_value_change(decimalentry, decimalentry->priv->value);
}
GtkWidget *
hbtk_decimal_entry_new (GtkWidget *label)
{
DB( g_print("\n[decimalentry] new\n") );
HbtkDecimalEntry *decimalentry = g_object_new (HBTK_TYPE_DECIMAL_ENTRY, NULL);
if(decimalentry)
{
HbtkDecimalEntryPrivate *priv = decimalentry->priv;
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), priv->entry);
hbtk_decimal_entry_default_output(decimalentry);
}
return GTK_WIDGET(decimalentry);
}
homebank-5.9.7/src/dsp-account.c 0000644 0001750 0001750 00000342667 15123464605 016015 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-account.h"
#include "dsp-mainwindow.h"
#include "list-operation.h"
#include "hub-account.h"
#include "gtk-dateentry.h"
#include "ui-filter.h"
#include "ui-transaction.h"
#include "ui-txn-multi.h"
#include "ui-flt-widget.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern HbKvData CYA_FLT_TYPE[];
extern HbKvData CYA_FLT_STATUS[];
/* = = = = = = = = = = = = = = = = */
static void
_list_txn_selection_count_type(GtkTreeView *treeview, gint *nbrecon, gint *nbpending)
{
GtkTreeModel *model;
GList *lselection, *list;
gint tmprecon = 0;
gint tmppending = 0;
DB( g_print("\n[hub-ledger] selection count type\n") );
model = gtk_tree_view_get_model(treeview);
lselection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
list = g_list_last(lselection);
while(list != NULL)
{
GtkTreeIter iter;
Transaction *txn;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &txn, -1);
if(txn->status == TXN_STATUS_RECONCILED)
tmprecon++;
if(txn->flags & (OF_ISIMPORT|OF_ISPAST))
tmppending++;
list = g_list_previous(list);
}
g_list_foreach(lselection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(lselection);
if(nbrecon)
*nbrecon = tmprecon;
if(nbpending)
*nbpending = tmppending;
}
static void
hub_ledger_balance_refresh(GtkWidget *view)
{
struct hub_ledger_data *data;
Transaction *minbalope;
GList *list;
gdouble balance;
GtkTreeModel *model;
gdouble lbalance = 0;
guint32 ldate = 0;
gushort lpos = 1;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(view, GTK_TYPE_WINDOW)), "inst_data");
// noaction if show all account
if(data->showall)
return;
DB( g_print("\n[hub-ledger] balance refresh kacc=%d\n", data->acc != NULL ? (gint)data->acc->key : -1) );
balance = data->acc->initial;
//#1270687: sort if date changed
if(data->do_sort)
{
DB( g_print(" complete txn sort\n") );
da_transaction_queue_sort(data->acc->txn_queue);
data->do_sort = FALSE;
}
minbalope = NULL;
list = g_queue_peek_head_link(data->acc->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
gdouble value;
//#1267344 maybe no remind in running balance
if( transaction_is_balanceable(ope) )
balance += ope->amount;
ope->balance = balance;
// clear mark flags
ope->dspflags &= ~(FLAG_TMP_OVER|FLAG_TMP_LOWBAL);
//#1661806 add show overdraft
//#1672209 added round like for #400483
value = hb_amount_round(balance, 2);
if( (value != 0.0) && (value < data->acc->minimum) )
{
ope->dspflags |= FLAG_TMP_OVER;
}
//# mark lowest balance for future
if ((ope->date > GLOBALS->today))
{
if( balance < lbalance )
minbalope = ope;
}
if(ope->date == ldate)
{
ope->pos = ++lpos;
}
else
{
ope->pos = lpos = 1;
}
ldate = ope->date;
lbalance = balance;
list = g_list_next(list);
}
if( minbalope != NULL )
minbalope->dspflags |= FLAG_TMP_LOWBAL;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
list_txn_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
static void
hub_ledger_update(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
GtkTreeSelection *selection;
gint flags = GPOINTER_TO_INT(user_data);
gboolean lockrecon, visible;
gint count = 0;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//data = INST_DATA(widget);
DB( g_print("\n[hub-ledger] update kacc=%d\n", data->acc != NULL ? (gint)data->acc->key : -1) );
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
/* set window title */
if(flags & FLG_REG_TITLE)
{
DB( g_print("\n FLG_REG_TITLE\n") );
}
/* update toolbar & list */
if(flags & FLG_REG_VISUAL)
{
//gboolean visible;
DB( g_print("\n FLG_REG_VISUAL\n") );
//minor ?
hb_widget_visible (data->CM_minor, PREFS->euro_active);
//TODO: balance to show/hide
/*
visible = (homebank_pref_list_column_get(PREFS->lst_acc_columns, COL_DSPACC_RECON, NUM_LST_COL_DSPACC) < 0) ? FALSE : TRUE;
hb_widget_visible (data->LB_recon, visible);
hb_widget_visible (data->TX_balance[0], visible);
visible = (homebank_pref_list_column_get(PREFS->lst_acc_columns, COL_DSPACC_CLEAR, NUM_LST_COL_DSPACC) < 0) ? FALSE : TRUE;
hb_widget_visible (data->LB_clear, visible);
hb_widget_visible (data->TX_balance[1], visible);
visible = (homebank_pref_list_column_get(PREFS->lst_acc_columns, COL_DSPACC_TODAY, NUM_LST_COL_DSPACC) < 0) ? FALSE : TRUE;
hb_widget_visible (data->LB_today, visible);
hb_widget_visible (data->TX_balance[2], visible);
visible = (homebank_pref_list_column_get(PREFS->lst_acc_columns, COL_DSPACC_FUTURE, NUM_LST_COL_DSPACC) < 0) ? FALSE : TRUE;
hb_widget_visible (data->LB_futur, visible);
hb_widget_visible (data->TX_balance[3], visible);
*/
}
/* update balances */
if(flags & FLG_REG_BALANCE)
{
DB( g_print("\n FLG_REG_BALANCE\n") );
if(data->showall == FALSE)
{
Account *acc = data->acc;
hub_ledger_balance_refresh(widget);
DB( g_print(" update 4 balances widget\n") );
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[0]), acc->bal_recon, acc->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[1]), acc->bal_clear, acc->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[2]), acc->bal_today, acc->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[3]), acc->bal_future, acc->kcur, GLOBALS->minor);
}
else
{
GList *lst_acc, *lnk_acc;
gdouble recon, clear, today, future;
recon = clear = today = future = 0.0;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
recon += hb_amount_base(acc->bal_recon, acc->kcur);
clear += hb_amount_base(acc->bal_clear, acc->kcur);
today += hb_amount_base(acc->bal_today, acc->kcur);
future += hb_amount_base(acc->bal_future, acc->kcur);
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
DB( g_print(" update 4 balances widget\n") );
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[0]), recon, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[1]), clear, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[2]), today, GLOBALS->kcur, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[3]), future, GLOBALS->kcur, GLOBALS->minor);
}
ui_hub_account_compute(GLOBALS->mainwindow, NULL);
}
/* update disabled things */
if(flags & FLG_REG_SENSITIVE)
{
gboolean sensitive, psensitive, nsensitive;
GtkTreeModel *model;
gint sort_column_id;
GtkSortType order;
Transaction *ope;
DB( g_print(" FLG_REG_SENSITIVE\n") );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
count = gtk_tree_selection_count_selected_rows(selection);
DB( g_print(" count = %d\n", count) );
ope = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_ope));
//showall part
sensitive = !data->showall;
//hb_widget_visible(data->MI_exportqif, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "expqif")), sensitive);
//hb_widget_visible(data->MI_browse, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "browse")), sensitive);
//tools
//hb_widget_visible(data->MI_markdup, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "mrksign")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "mrkdup")), sensitive);
//hb_widget_visible(data->MI_chkintxfer, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "chkxfer")), sensitive);
//#1873248 Auto. assignments faulty sensitive on 'All transactions' window
//hb_widget_visible(data->MI_autoassign, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "runasg")), sensitive);
//1909749 lock/unlock reconciled
lockrecon = gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled));
if( ope != NULL )
{
if( (ope->status != TXN_STATUS_RECONCILED) )
lockrecon = FALSE;
DB( g_print(" lockrecon = %d (%d %d)\n", lockrecon, ope->status != TXN_STATUS_RECONCILED, gtk_switch_get_state (GTK_SWITCH(data->SW_lockreconciled)) ) );
}
//5.3.1 if closed account : disable any change
sensitive = TRUE;
if( data->closed == TRUE )
sensitive = FALSE;
gtk_widget_set_sensitive(data->TB_bar, sensitive);
//todo: subsititute ? or check carrefully
//gtk_widget_set_sensitive(data->ME_menuedit, sensitive);
//gtk_widget_set_sensitive(data->ME_menutxn, sensitive);
//gtk_widget_set_sensitive(data->ME_menutools, sensitive);
//gtk_widget_set_sensitive(data->ME_popmenu, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txncopy")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnpaste")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnpastet")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "mrksign")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "mrkdup")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "chkxfer")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "runasg")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "convert")), sensitive);
//5.7 browse menu
sensitive = account_has_website(data->acc);
//gtk_widget_set_sensitive(data->MI_browse, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "browse")), sensitive);
// multiple: disable inherit, edit
sensitive = (count != 1 ) ? FALSE : TRUE;
//gtk_widget_set_sensitive(data->MI_herit, sensitive);
//gtk_widget_set_sensitive(data->MI_popherit, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnherit")), sensitive);
//gtk_widget_set_sensitive(data->MI_edit, lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->MI_popedit, lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnedit")), lockrecon ? FALSE : sensitive);
gtk_widget_set_sensitive(data->BT_herit, sensitive);
gtk_widget_set_sensitive(data->BT_edit, lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->MI_popcopyamount, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "copyamt")), sensitive);
//txn have split
sensitive = (count == 1) && (ope != NULL) && (ope->flags & OF_SPLIT) ? TRUE : FALSE;
//gtk_widget_set_sensitive(data->MI_popviewsplit, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "viewsplit")), sensitive);
// single: disable multiedit
sensitive = (count <= 1 ) ? FALSE : TRUE;
//1909749 lock/unlock reconciled
gint nbrecon, nbpending;
_list_txn_selection_count_type(GTK_TREE_VIEW(data->LV_ope), &nbrecon, &nbpending);
if( (nbrecon > 0) && (gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled)) == TRUE) )
sensitive = FALSE;
//gtk_widget_set_sensitive(data->MI_multiedit , sensitive);
//gtk_widget_set_sensitive(data->MI_popmultiedit, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnmedit")), sensitive);
gtk_widget_set_sensitive(data->BT_multiedit , sensitive);
//pending action
sensitive = (count >=1 && nbpending > 0) ? TRUE : FALSE;
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnapprove")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnreject")), sensitive);
// no selection: disable reconcile, delete
sensitive = (count > 0 ) ? TRUE : FALSE;
//gtk_widget_set_sensitive(data->MI_copy, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txncopy")), sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnclip")), sensitive);
//gtk_widget_set_sensitive(data->ME_menustatus, lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->ME_popmenustatus, lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "stanon")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "staclr")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "starec")), lockrecon ? FALSE : sensitive);
//#1600356
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flgn")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg1")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg2")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg3")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg4")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg5")), lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "flg6")), lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->MI_delete, lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->MI_popdelete, lockrecon ? FALSE : sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txndel")), lockrecon ? FALSE : sensitive);
//gtk_widget_set_sensitive(data->MI_assign, sensitive);
//gtk_widget_set_sensitive(data->MI_popassign, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "newasg")), sensitive);
//gtk_widget_set_sensitive(data->MI_template, sensitive);
//gtk_widget_set_sensitive(data->MI_poptemplate, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "newtpl")), sensitive);
gtk_widget_set_sensitive(data->BT_delete, lockrecon ? FALSE : sensitive);
gtk_widget_set_sensitive(data->BT_clear, lockrecon ? FALSE : sensitive);
gtk_widget_set_sensitive(data->BT_reconcile, lockrecon ? FALSE : sensitive);
//edit menu
sensitive = g_queue_get_length(data->q_txn_clip) > 0 ? TRUE : FALSE;
//gtk_widget_set_sensitive(data->MI_pasten, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnpaste")), sensitive);
//gtk_widget_set_sensitive(data->MI_pastet, sensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnpastet")), sensitive);
// euro convert
visible = (data->showall == TRUE) ? FALSE : PREFS->euro_active;
if( (data->acc != NULL) && currency_is_euro(data->acc->kcur) )
visible = FALSE;
//hb_widget_visible(data->MI_conveuro, visible);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "convert")), visible);
//move up/down button : not when showall and when sort is not date
visible = FALSE;
psensitive = FALSE;
nsensitive = FALSE;
if( count == 1 && data->showall == FALSE )
{
Transaction *prevtxn, *nexttxn;
visible = TRUE;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(GTK_TREE_STORE(model)), &sort_column_id, &order);
if( (data->showall == TRUE) || (sort_column_id != LST_DSPOPE_DATE) )
visible = FALSE;
prevtxn = NULL; nexttxn = NULL;
Transaction *txn = list_txn_get_surround_transaction(GTK_TREE_VIEW(data->LV_ope), &prevtxn, &nexttxn);
if( prevtxn && txn )
{
psensitive = (prevtxn->date == txn->date) ? TRUE : FALSE;
}
if( nexttxn && txn )
{
nsensitive = (nexttxn->date == txn->date) ? TRUE : FALSE;
}
}
//hb_widget_visible(data->SP_updown, visible);
//hb_widget_visible(data->BT_up, visible);
//hb_widget_visible(data->BT_down, visible);
//todo: why assign sesnitivity twice ??
//hb_widget_visible(data->MI_poptxnup, visible);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnup")), sensitive);
//hb_widget_visible(data->MI_poptxndown, visible);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txndw")), sensitive);
//gtk_widget_set_sensitive(data->MI_poptxnup, psensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnup")), psensitive);
gtk_widget_set_sensitive(data->BT_up, psensitive);
//gtk_widget_set_sensitive(data->MI_poptxndown, nsensitive);
g_simple_action_set_enabled(G_SIMPLE_ACTION(g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txndw")), nsensitive);
gtk_widget_set_sensitive(data->BT_down, nsensitive);
}
//#1875100 show infobar for pending
visible = data->nb_pending > 0 ? TRUE : FALSE;
hb_widget_visible(data->IB_accnotif, visible);
if( visible == TRUE )
{
gchar *accmsg = g_strdup_printf(_("%d requires approval"), data->nb_pending);
gtk_label_set_markup(GTK_LABEL(data->LB_accnotif), accmsg);
g_free (accmsg);
}
//#1835588
visible = PREFS->date_future_nbdays > 0 ? TRUE : FALSE;
if( !(filter_preset_daterange_future_enable( data->filter, hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range)) )) )
visible = FALSE;
hb_widget_visible(data->CM_future, visible);
DB( g_print(" show future=%d\n", visible) );
/* update fltinfo */
DB( g_print(" FLG_REG_INFOBAR\n") );
DB( g_print(" statusbar\n") );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
count = gtk_tree_selection_count_selected_rows(selection);
DB( g_print(" nb selected = %d\n", count) );
/* if more than one ope selected, we make a sum to display to the user */
gdouble opeexp = 0.0;
gdouble opeinc = 0.0;
gchar buf1[64];
gchar buf2[64];
gchar buf3[64];
gchar fbufavg[64];
guint32 kcur;
kcur = (data->showall == TRUE) ? GLOBALS->kcur : data->acc->kcur;
if( count >= 1 )
{
GList *list, *tmplist;
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
list = gtk_tree_selection_get_selected_rows(selection, &model);
tmplist = g_list_first(list);
while (tmplist != NULL)
{
Transaction *item;
gtk_tree_model_get_iter(model, &iter, tmplist->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &item, -1);
if( data->showall == FALSE )
{
if( item->flags & OF_INCOME )
opeinc += item->amount;
else
opeexp += item->amount;
}
else
{
if( item->flags & OF_INCOME )
opeinc += hb_amount_base(item->amount, item->kcur);
else
opeexp += hb_amount_base(item->amount, item->kcur);
}
DB( g_print(" memo='%s', %.2f\n", item->memo, item->amount ) );
tmplist = g_list_next(tmplist);
}
g_list_free(list);
DB( g_print(" %f - %f = %f\n", opeinc, opeexp, opeinc + opeexp) );
hb_strfmon(buf1, 64-1, opeinc, kcur, GLOBALS->minor);
hb_strfmon(buf2, 64-1, -opeexp, kcur, GLOBALS->minor);
hb_strfmon(buf3, 64-1, opeinc + opeexp, kcur, GLOBALS->minor);
hb_strfmon(fbufavg, 64-1, (opeinc + opeexp) / count, kcur, GLOBALS->minor);
}
DB( g_print(" update selection message\n") );
gchar *msg;
if( count <= 1 )
{
msg = g_strdup_printf(_("%d transactions"), data->total);
}
else
msg = g_strdup_printf(_("%d transactions, %d selected, avg: %s, sum: %s (%s - %s)"), data->total, count, fbufavg, buf3, buf1, buf2);
gtk_label_set_markup(GTK_LABEL(data->TX_selection), msg);
g_free (msg);
//5.6 update lock/unlock
DB( g_print(" update lock/unlock\n") );
lockrecon = gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled));
DB( g_print(" lockrecon=%d\n", lockrecon) );
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_ope), lockrecon);
gchar *iconname = lockrecon == TRUE ? ICONNAME_CHANGES_PREVENT : ICONNAME_CHANGES_ALLOW;
g_object_set(data->IM_lockreconciled, "icon-name", iconname, NULL);
gtk_widget_set_tooltip_text (data->SW_lockreconciled,
lockrecon == TRUE ? _("Locked. Click to unlock") : _("Unlocked. Click to lock"));
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
}
/* these 5 functions are independant from account window */
/* account functions -------------------- */
static void
hub_ledger_edit_multiple(GtkWidget *widget, Transaction *txn, gint column_id, gpointer user_data)
{
struct hub_ledger_data *data;
GtkWidget *dialog;
DB( g_print("\n[hub-ledger] edit multiple\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" txn:%p, column: %d\n", txn, column_id) );
dialog = ui_multipleedit_dialog_new(GTK_WINDOW(data->window), GTK_TREE_VIEW(data->LV_ope));
if(txn != NULL && column_id != 0)
{
ui_multipleedit_dialog_prefill(dialog, txn, column_id);
}
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if( result == GTK_RESPONSE_ACCEPT )
{
gboolean do_sort;
gint changes;
//#1792808: sort if date changed
changes = ui_multipleedit_dialog_apply (dialog, &do_sort);
data->do_sort = do_sort;
if( changes > 0 )
{
//#1782749 update account status
if( data->acc != NULL )
data->acc->dspflags |= FLAG_ACC_TMP_EDITED;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
}
gtk_window_destroy (GTK_WINDOW(dialog));
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE|FLG_REG_BALANCE));
}
/* ---end ---- */
static void
hub_ledger_action_move_up(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
Transaction *txn = NULL;
Transaction *prevtxn = NULL;
gint count = 0;
DB( g_print("\n[hub-ledger] move up\n\n") );
txn = list_txn_get_surround_transaction(GTK_TREE_VIEW(data->LV_ope), &prevtxn, NULL);
if( txn && prevtxn )
{
if( txn->date == prevtxn->date )
{
DB( g_print(" swapping, as txn are same date\n") );
//swap position
gint savedpos = txn->pos;
txn->pos = prevtxn->pos;
prevtxn->pos = savedpos;
GLOBALS->changes_count++;
count++;
}
}
if( count > 0 )
{
data->do_sort = TRUE;
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE|FLG_REG_BALANCE));
}
}
static void
hub_ledger_action_move_down(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
Transaction *txn = NULL;
Transaction *nexttxn = NULL;
gint count = 0;
DB( g_print("\n[hub-ledger] move down\n\n") );
txn = list_txn_get_surround_transaction(GTK_TREE_VIEW(data->LV_ope), NULL, &nexttxn);
if( txn && nexttxn )
{
if( txn->date == nexttxn->date )
{
DB( g_print(" swapping, as txn are same date\n") );
//swap position
gint savedpos = txn->pos;
txn->pos = nexttxn->pos;
nexttxn->pos = savedpos;
GLOBALS->changes_count++;
count++;
}
}
if( count > 0 )
{
data->do_sort = TRUE;
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE|FLG_REG_BALANCE));
}
}
static gboolean
hub_ledger_cb_recon_change (GtkWidget *widget, gboolean state, gpointer user_data)
{
struct hub_ledger_data *data;
DB( g_print("\n[hub-ledger] cb recon change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->lockreconciled = gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled));
DB( g_print(" state=%d switch=%d\n", state, data->lockreconciled ) );
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE));
return FALSE;
}
static void
hub_ledger_collect_filtered_txn(GtkWidget *view, gboolean emptysearch)
{
struct hub_ledger_data *data;
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
gint flag, status;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(view, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[hub-ledger] collect_filtered_txn - kacc=%d\n", data->acc != NULL ? (gint)data->acc->key : -1) );
if(data->gpatxn != NULL)
g_ptr_array_free (data->gpatxn, TRUE);
//our txn storage to populate and quickfilter
data->gpatxn = g_ptr_array_sized_new(64);
flag = kiv_combo_box_get_active(GTK_COMBO_BOX(data->CY_flag));
status = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_status));
DB( g_print(" flag=%d\n", flag) );
data->nb_pending = 0;
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
// skip closed in showall mode
//#1861337 users want them
//if( data->showall == TRUE && (acc->flags & AF_CLOSED) )
// goto next_acc;
// skip other than current in normal mode
if( (data->showall == FALSE) && (data->acc != NULL) && (acc->key != data->acc->key) )
goto next_acc;
data->nb_pending += acc->nb_pending;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *ope = lnk_txn->data;
gboolean insert = FALSE;
//#1875100 skip any filter but pending
//#2109854 really show all txn (void were hidden)
if( status==FLT_STATUS_UNAPPROVED )
{
if( ope->flags & (OF_ISIMPORT|OF_ISPAST) )
insert = TRUE;
}
else
{
if( filter_txn_match(data->filter, ope) == 1 )
{
//#1600356 filter flag
if( (flag == GRPFLAG_ANY) || (ope->grpflg == flag) )
{
insert = TRUE;
}
}
}
//add to the list
if( insert == TRUE )
{
g_ptr_array_add(data->gpatxn, (gpointer)ope);
}
lnk_txn = g_list_next(lnk_txn);
}
next_acc:
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
//#1789698 not always empty
if( emptysearch == TRUE )
{
g_signal_handler_block(data->ST_search, data->handler_id[HID_SEARCH]);
gtk_entry_set_text (GTK_ENTRY(data->ST_search), "");
g_signal_handler_unblock(data->ST_search, data->handler_id[HID_SEARCH]);
}
}
static void
hub_ledger_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_print("\n[hub-ledger] selection changed cb\n") );
hub_ledger_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(FLG_REG_SENSITIVE));
}
static void
hub_ledger_listview_populate(GtkWidget *widget)
{
struct hub_ledger_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean hastext;
gchar *needle;
gint sort_column_id;
GtkSortType order;
guint i, qs_flag;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[hub-ledger] listview_populate - kacc=%d\n", data->acc != NULL ? (gint)data->acc->key : -1) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
//block handler here
g_signal_handlers_block_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope))), G_CALLBACK (hub_ledger_selection), NULL);
gtk_tree_store_clear (GTK_TREE_STORE(model));
// ref model to keep it
DB( g_print(" unplug model\n") );
g_object_ref(model);
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), NULL);
// perf: if you leave the sort, insert is damned slow
gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(GTK_TREE_STORE(model)), &sort_column_id, &order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_TREE_STORE(model)), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, PREFS->lst_ope_sort_order);
hastext = (gtk_entry_get_text_length (GTK_ENTRY(data->ST_search)) >= 2) ? TRUE : FALSE;
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
//build the mask flag for quick search
qs_flag = 0;
if(hastext)
{
qs_flag = list_txn_get_quicksearch_column_mask(GTK_TREE_VIEW(data->LV_ope));
}
data->total = 0;
data->totalsum = 0.0;
for(i=0;igpatxn->len;i++)
{
Transaction *txn = g_ptr_array_index(data->gpatxn, i);
gboolean insert = TRUE;
if(hastext)
{
insert = filter_txn_search_match(needle, txn, qs_flag);
}
if(insert)
{
//gtk_list_store_append (GTK_LIST_STORE(model), &iter);
//gtk_list_store_set (GTK_LIST_STORE(model), &iter,
//5.7 optim: prepend and not append
//gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, NULL, -1,
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model), &iter, NULL, 0,
MODEL_TXN_POINTER, txn,
-1);
if( data->showall == FALSE )
data->totalsum += txn->amount;
else
data->totalsum += hb_amount_base (txn->amount, txn->kcur);
data->total++;
}
}
// push back the sort id
DB( g_print(" sort model\n") );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_TREE_STORE(model)), sort_column_id, order);
DB( g_print(" plug model\n") );
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), model); /* Re-attach model to view */
g_object_unref(model);
g_signal_handlers_unblock_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope))), G_CALLBACK (hub_ledger_selection), NULL);
/* update info range text */
{
gchar *daterange;
daterange = filter_daterange_text_get(data->filter);
gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
//gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
g_free(daterange);
}
DB( g_print(" call update\n") );
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE+FLG_REG_BALANCE));
}
static void
hub_ledger_cb_button_lifenergy(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
gboolean lifnrgval;
DB( g_print("\n[hub-ledger] toggle life energy\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
lifnrgval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_lifnrg));
if( lifnrgval == TRUE && GLOBALS->lifen_earnbyh <= 0.0 )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Life Energy"),
_("'Earn by hour' is not set into the current Wallet.")
);
}
list_txn_set_life_energy(GTK_TREE_VIEW(data->LV_ope), lifnrgval);
}
static void
hub_ledger_info_cb_show_pending(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[hub-ledger] infobar show pending\n") );
//TODO: change date to all date ?
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_status), FLT_STATUS_UNAPPROVED);
}
static void
hub_ledger_cb_filter_daterange(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
gboolean future;
gint range;
DB( g_print("\n[hub-ledger] filter_daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
future = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_future));
//in 5.6 no longer custom open the filter
//if(range != FLT_RANGE_OTHER)
//{
filter_preset_daterange_set(data->filter, range, (data->showall == FALSE) ? data->acc->key : 0);
// add eventual x days into future display
filter_preset_daterange_add_futuregap(data->filter, future);
hub_ledger_collect_filtered_txn(data->LV_ope, FALSE);
hub_ledger_listview_populate(data->LV_ope);
/*}
else
{
if(ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, data->showall, TRUE) != GTK_RESPONSE_REJECT)
{
hub_ledger_collect_filtered_txn(data->LV_ope, FALSE);
hub_ledger_listview_populate(data->LV_ope);
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE+FLG_REG_BALANCE));
}
}*/
}
static void
hub_ledger_cb_refresh(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
DB( g_print("\n[hub-ledger] filterbar change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#2119051 add new txn needs to extend all date range
if(data->filter->range == FLT_RANGE_MISC_ALLDATE)
{
hub_ledger_cb_filter_daterange(data->window, NULL);
}
hub_ledger_collect_filtered_txn(data->LV_ope, FALSE);
hub_ledger_listview_populate(data->LV_ope);
}
static void
hub_ledger_cb_filterbar_change(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
gint type, status;
DB( g_print("\n[hub-ledger] filterbar change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_type));
status = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_status));
filter_preset_type_set(data->filter, type, FLT_INCLUDE);
filter_preset_status_set(data->filter, status);
hub_ledger_collect_filtered_txn(data->LV_ope, FALSE);
hub_ledger_listview_populate(data->LV_ope);
}
static void beta_hub_ledger_cb_preset_change(GtkWidget *widget, gpointer user_data);
static void
hub_ledger_cb_filter_reset(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
gint dspstatus;
GtkWidget *combobox;
DB( g_print("\n[hub-ledger] filter_reset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
filter_reset(data->filter);
//#1600356 grpflg
g_signal_handlers_block_by_func(data->CY_flag, G_CALLBACK (beta_hub_ledger_cb_preset_change), NULL);
kiv_combo_box_set_active(GTK_COMBO_BOX(data->CY_flag), GRPFLAG_ANY);
g_signal_handlers_unblock_by_func(data->CY_flag, G_CALLBACK (beta_hub_ledger_cb_preset_change), NULL);
filter_preset_daterange_set (data->filter, PREFS->date_range_txn, (data->showall == FALSE) ? data->acc->key : 0);
if(PREFS->hidereconciled)
filter_preset_status_set (data->filter, FLT_STATUS_UNRECONCILED);
// add eventual x days into future display
filter_preset_daterange_add_futuregap(data->filter, TRUE );
g_signal_handler_block(data->CY_range, data->handler_id[HID_RANGE]);
g_signal_handler_block(data->CY_type, data->handler_id[HID_TYPE]);
g_signal_handler_block(data->CY_status, data->handler_id[HID_STATUS]);
DB( g_print(" set range : %d\n", data->filter->range) );
DB( g_print(" set type : %d\n", data->filter->type) );
DB( g_print(" set status: %d\n", data->filter->status) );
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_type), data->filter->type);
//#1873324 ledger status quick filter do not reset
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_status), data->filter->rawstatus);
//#1878483 status with hidereconciled shows reconciled (due to filter !reconciled internal
dspstatus = data->filter->status;
if( (dspstatus == FLT_STATUS_RECONCILED) && (data->filter->option[FLT_GRP_STATUS] == 2) )
dspstatus = FLT_STATUS_UNRECONCILED;
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_status), dspstatus);
g_signal_handler_unblock(data->CY_status, data->handler_id[HID_STATUS]);
g_signal_handler_unblock(data->CY_type, data->handler_id[HID_TYPE]);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_RANGE]);
if( data->showall )
{
combobox = ui_flt_popover_hub_get_combobox(GTK_BOX(data->PO_hubfilter), NULL);
g_signal_handlers_block_by_func (G_OBJECT (combobox), G_CALLBACK (beta_hub_ledger_cb_preset_change), NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
g_signal_handlers_unblock_by_func (G_OBJECT (combobox), G_CALLBACK (beta_hub_ledger_cb_preset_change), NULL);
}
hub_ledger_collect_filtered_txn(data->LV_ope, TRUE);
hub_ledger_listview_populate(data->LV_ope);
}
static void beta_hub_ledger_cb_preset_change(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
Filter *newflt;
DB( g_print("\n[repdist] filter preset change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( !data->showall )
return;
newflt = ui_flt_popover_hub_get(GTK_BOX(data->PO_hubfilter), NULL);
if( newflt )
{
DB( g_print(" key:%d, copy filter\n", newflt->key) );
da_flt_copy(newflt, data->filter);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
}
else
hub_ledger_cb_filter_reset(widget, user_data);
ui_flt_manage_header_sensitive(data->PO_hubfilter, NULL);
}
/* used to remove a intxfer child from a treeview */
static void
_list_txn_remove_by_value(GtkTreeView *treeview, Transaction *txn)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
if( txn == NULL )
return;
DB( g_print(" remove by value %p\n\n", txn) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Transaction *tmp;
gtk_tree_model_get (model, &iter,
MODEL_TXN_POINTER, &tmp,
-1);
if( txn == tmp )
{
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
// this func to some toggle
static void
_list_txn_status_selected_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata)
{
gint targetstatus = GPOINTER_TO_INT(userdata);
Transaction *txn;
gboolean saverecondate = FALSE;
gtk_tree_model_get(model, iter, MODEL_TXN_POINTER, &txn, -1);
account_balances_sub(txn);
switch(targetstatus)
{
case TXN_STATUS_NONE:
switch(txn->status)
{
case TXN_STATUS_CLEARED:
case TXN_STATUS_RECONCILED:
txn->status = TXN_STATUS_NONE;
txn->dspflags |= FLAG_TMP_EDITED;
break;
}
break;
case TXN_STATUS_CLEARED:
switch(txn->status)
{
case TXN_STATUS_NONE:
case TXN_STATUS_RECONCILED:
txn->status = TXN_STATUS_CLEARED;
txn->dspflags |= FLAG_TMP_EDITED;
break;
case TXN_STATUS_CLEARED:
txn->status = TXN_STATUS_NONE;
txn->dspflags |= FLAG_TMP_EDITED;
break;
}
break;
case TXN_STATUS_RECONCILED:
switch(txn->status)
{
case TXN_STATUS_NONE:
case TXN_STATUS_CLEARED:
txn->status = TXN_STATUS_RECONCILED;
txn->dspflags |= FLAG_TMP_EDITED;
saverecondate = TRUE;
break;
case TXN_STATUS_RECONCILED:
txn->status = TXN_STATUS_CLEARED;
txn->dspflags |= FLAG_TMP_EDITED;
break;
}
break;
}
transaction_changed(txn, saverecondate);
account_balances_add(txn);
//#492755 removed 4.3 let the child transfer unchanged
//#2019193 option the sync xfer status
if( txn->flags & OF_INTXFER )
{
if( PREFS->xfer_syncstat == TRUE )
{
Transaction *child = transaction_xfer_child_strong_get(txn);
if(child != NULL)
{
GtkWindow *accwin = homebank_app_find_window(txn->kxferacc);
//#2080756 recompute bal
account_balances_sub(child);
child->status = txn->status;
child->dspflags |= FLAG_TMP_EDITED;
account_balances_add(child);
//#2080756 if open refresh target account balances
if(accwin != NULL)
{
DB( g_print(" xfer call refresh %d\n", txn->kxferacc));
hub_ledger_update(GTK_WIDGET(accwin), GINT_TO_POINTER(FLG_REG_BALANCE));
}
}
}
}
}
static void
hub_ledger_remove_single_transaction(GtkWindow *window, Transaction *txn)
{
struct hub_ledger_data *data;
if(txn == NULL)
return;
DB( g_print("\n[hub-ledger] remove single txn\n") );
data = g_object_get_data(G_OBJECT(window), "inst_data");
_list_txn_remove_by_value(GTK_TREE_VIEW(data->LV_ope), txn);
}
// future: refresh txn list of open ledger
void
beta_hub_ledger_refresh_txn_opens(void)
{
DB( g_print("\n[hub-ledger] refresh txn list of opens\n") );
GList *l = gtk_application_get_windows(GLOBALS->application);
while (l != NULL)
{
GtkWindow *tmpwin = l->data;
gint key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tmpwin), "key"));
//TODO: when multiple wallet, we should check as well for that
if( key == -1 || key > 0 )
{
gboolean refresh = FALSE;
DB( g_print(" window: %p: key=%d '%s'\n", tmpwin, key, gtk_window_get_title(tmpwin)) );
//showall always refresh
if( key == -1 )
refresh = TRUE;
else
{
Account *acc = da_acc_get(key);
if( acc && (acc->dspflags & FLAG_ACC_TMP_DIRTY) )
{
refresh = TRUE;
account_set_dirty(acc, 0, FALSE);
}
}
if( refresh )
{
DB( g_print(" >refresh\n") );
hub_ledger_cb_refresh(GTK_WIDGET(tmpwin), NULL);
}
}
l = g_list_next(l);
}
}
static void
hub_ledger_add_after_propagate(struct hub_ledger_data *data, Transaction *add_txn)
{
DB( g_print("\n[hub-ledger] add after propagate\n") );
if((data->showall == TRUE) || ( (data->acc != NULL) && (add_txn->kacc == data->acc->key) ) )
{
account_set_dirty(data->acc, 0, TRUE);
if( (add_txn->flags & OF_INTXFER) )
account_set_dirty(NULL, add_txn->kxferacc, TRUE);
beta_hub_ledger_refresh_txn_opens();
}
}
static void
hub_ledger_cb_editfilter(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if(ui_flt_manage_dialog_new(GTK_WINDOW(data->window), data->filter, data->showall, TRUE) != GTK_RESPONSE_REJECT)
{
gboolean usrfuture;
//5.9.6: no need to misc if it was not set into dialog
//if( data->filter->range == FLT_RANGE_MISC_CUSTOM )
//{
g_signal_handler_block(data->CY_range, data->handler_id[HID_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), data->filter->range);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_RANGE]);
//}
usrfuture = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_future));
filter_preset_daterange_add_futuregap(data->filter, usrfuture);
hub_ledger_collect_filtered_txn(data->LV_ope, TRUE);
hub_ledger_listview_populate(data->LV_ope);
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_SENSITIVE+FLG_REG_BALANCE));
//ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
if(data->showall)
ui_flt_manage_header_sensitive(data->PO_hubfilter, NULL);
}
}
static void
hub_ledger_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
DB( g_print("\n[hub-ledger] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_BALANCE));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_ope));
}
static gboolean
hub_ledger_cb_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
//already bind to menu Txn > Find ctrl+F
/*if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->ST_search);
}
else*/
if (keyval == GDK_KEY_Escape && gtk_widget_has_focus(data->ST_search))
{
hbtk_entry_set_text(GTK_ENTRY(data->ST_search), NULL);
gtk_widget_grab_focus(data->LV_ope);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void
hub_ledger_onRowActivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
struct hub_ledger_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gint col_id, count;
Transaction *ope;
gboolean lockrecon;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
//5.3.1 if closed account : disable any change
if( data->closed == TRUE )
return;
col_id = gtk_tree_view_column_get_sort_column_id (col);
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(treeview));
model = gtk_tree_view_get_model(treeview);
//get transaction double clicked to initiate the widget
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
DB( g_print (" %d rows been double-clicked on column=%d! ope=%s\n", count, col_id, ope->memo) );
if( count == 1)
{
//1909749 lock/unlock reconciled
lockrecon = FALSE;
if( (ope->status == TXN_STATUS_RECONCILED) && (gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled)) == TRUE) )
lockrecon = TRUE;
if( lockrecon == FALSE )
{
GAction *action = g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnedit");
//hub_ledger_action(GTK_WIDGET(treeview), GINT_TO_POINTER(ACTION_ACCOUNT_EDIT));
g_action_activate(action, NULL);
}
}
else
{
if( data->showall == FALSE )
{
if(col_id >= LST_DSPOPE_DATE && col_id != LST_DSPOPE_BALANCE)
{
hub_ledger_edit_multiple (data->window, ope, col_id, data);
}
}
}
}
/*
** populate the account window
*/
void
hub_ledger_window_init(GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[hub-ledger] init window\n") );
if( data->showall == TRUE )
{
//gtk_label_set_text (GTK_LABEL(data->LB_name), _("All transactions"));
//hb_widget_visible (data->IM_closed, FALSE);
}
else
{
//gtk_label_set_text (GTK_LABEL(data->LB_name), data->acc->name);
//hb_widget_visible (data->IM_closed, (data->acc->flags & AF_CLOSED) ? TRUE : FALSE);
DB( g_print(" sort transactions\n") );
da_transaction_queue_sort(data->acc->txn_queue);
}
list_txn_set_column_acc_visible(GTK_TREE_VIEW(data->LV_ope), data->showall);
if( (data->showall == FALSE) && !(data->acc->flags & AF_NOBUDGET) )
list_txn_set_warn_nocategory(GTK_TREE_VIEW(data->LV_ope), TRUE);
//DB( g_print(" mindate=%d, maxdate=%d %x\n", data->filter->mindate,data->filter->maxdate) );
DB( g_print(" set range or populate+update sensitive+balance\n") );
hub_ledger_cb_filter_reset(widget, user_data);
DB( g_print(" call update visual\n") );
hub_ledger_update(widget, GINT_TO_POINTER(FLG_REG_VISUAL|FLG_REG_SENSITIVE));
}
static gboolean
hub_ledger_cb_search_focus_in_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct hub_ledger_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[hub-ledger] search focus-in event\n") );
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
return FALSE;
}
static void
quick_search_text_changed_cb (GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
hub_ledger_listview_populate (data->window);
}
static gint
listview_context_cb (GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
{
struct hub_ledger_data *data;
GdkEventType type;
guint button = 0;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//#1993088 ledger closed account popmenu should be disabled
if( data->closed == TRUE )
goto end;
type = gdk_event_get_event_type(event);
gdk_event_get_button(event, &button);
if (type == GDK_BUTTON_PRESS && button == 3)
{
// check we are not in the header but in bin window
if (gdk_event_get_window(event) == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)))
{
//test to enable
//GAction *action;
//action = g_action_map_lookup_action (G_ACTION_MAP (data->actions), "txnadd");
//g_simple_action_set_enabled(G_SIMPLE_ACTION(action), TRUE);
//g_action_activate(action, NULL);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
#else
gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gdk_event_get_time(event));
#endif
// On indique à l'appelant que l'on a géré cet événement.
return TRUE;
}
// On indique à l'appelant que l'on n'a pas géré cet événement.
}
end:
return FALSE;
}
/* account action functions -------------------- */
static void
hub_ledger_action_txnaddherit(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkWidget *dialog;
gint result, kacc, type = 0;
guint changes = GLOBALS->changes_count;
homebank_app_date_get_julian();
if( !strcmp(g_action_get_name (G_ACTION (action)), "txnadd") )
{
DB( g_print(" (transaction) add multiple\n") );
data->cur_ope = da_transaction_malloc();
// miss from 5.2.8 ??
//da_transaction_set_default_template(src_txn);
type = TXN_DLG_ACTION_ADD;
result = HB_RESPONSE_ADD;
}
else
{
DB( g_print(" (transaction) inherit multiple\n") );
data->cur_ope = da_transaction_clone(list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_ope)));
//#1873311 inherit+kepplastdate=OFF = today
if( PREFS->heritdate == FALSE )
data->cur_ope->date = GLOBALS->today;
//#2083127 don't keep grpflags
data->cur_ope->grpflg = 0;
//#1432204 inherit => status none
data->cur_ope->status = TXN_STATUS_NONE;
type = TXN_DLG_ACTION_INHERIT;
result = HB_RESPONSE_ADDKEEP;
}
kacc = (data->acc != NULL) ? data->acc->key : 0;
dialog = create_deftransaction_window(GTK_WINDOW(data->window), type, TXN_DLG_TYPE_TXN, kacc );
while(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP)
{
if( result == HB_RESPONSE_ADD )
{
da_transaction_init(data->cur_ope, kacc);
}
deftransaction_set_transaction(dialog, data->cur_ope);
result = gtk_dialog_run (GTK_DIALOG (dialog));
DB( g_print(" dialog result is %d\n", result) );
if(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP || result == GTK_RESPONSE_ACCEPT)
{
Transaction *add_txn;
deftransaction_get(dialog, NULL);
add_txn = transaction_add(GTK_WINDOW(dialog), TRUE, data->cur_ope);
//2044601 if NULL xfer may have beed aborted
if( add_txn != NULL )
{
//#1831975
if(PREFS->txn_showconfirm)
deftransaction_external_confirm(dialog, add_txn);
DB( g_print(" added 1 transaction to %d\n", add_txn->kacc) );
hub_ledger_add_after_propagate(data, add_txn);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
//#1667201 already done into add
//data->acc->dspflags |= FLAG_ACC_TMP_ADDED;
GLOBALS->changes_count++;
}
else
{
//2044601 keep actual txn
result = HB_RESPONSE_ADDKEEP;
}
}
}
da_transaction_free (data->cur_ope);
deftransaction_dispose(dialog, NULL);
gtk_window_destroy (GTK_WINDOW(dialog));
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_txnedit(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
Transaction *active_txn;
guint changes = GLOBALS->changes_count;
gint result;
DB( g_print(" edit\n") );
active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_ope));
if(active_txn)
{
Transaction *old_txn, *new_txn;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
DB( g_print(" edit accept\n") );
//manage current window display stuff
DB( g_print(" date changed: %d\n", old_txn->date != new_txn->date ? 1 : 0 ) );
DB( g_print(" type changed: %d\n", transaction_get_type(old_txn) != transaction_get_type(new_txn) ? 1 : 0 ) );
/*what to evaluate here
1) txn remain in same account
date chnaged > sort
2) txn move
a) was not a xfer and become
b) was a xfer and removed
c) normal txn & account changed
*/
//#1270687: sort if date changed
//if(old_txn->date != new_txn->date)
// data->do_sort = TRUE;
//#1931816: sort is already done in deftransaction_external_edit
// but still to be done if showall
if(data->showall == FALSE)
{
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
DB( g_print(" >sort force\n") );
list_txn_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
//dirty and refresh open ledger
// txn changed of account
//TODO: maybe this should move to deftransaction_external_edit
if( data->acc != NULL && (new_txn->kacc != data->acc->key) )
{
DB( g_print(" >account change\n") );
account_set_dirty(data->acc, 0, TRUE);
account_set_dirty(NULL, new_txn->kacc, TRUE);
}
//#1812470 txn is xfer update target account window if open
if( (old_txn->flags & OF_INTXFER) && (old_txn->amount != new_txn->amount) )
{
GtkWindow *accwin;
DB( g_print(" >xfer amt change\n") );
accwin = homebank_app_find_window(new_txn->kxferacc);
if(accwin)
{
DB( g_print(" update xfer dst win\n") );
hub_ledger_update(GTK_WIDGET(accwin), GINT_TO_POINTER(FLG_REG_BALANCE));
}
}
//5.7 txn was xfer but is not : refresh list
if( ((old_txn->flags & OF_INTXFER) > 0) && ((new_txn->flags & OF_INTXFER)==0) )
{
DB( g_print("\n >break xfer - %d > %d\n", old_txn->kacc, old_txn->kxferacc) );
account_set_dirty(NULL, old_txn->kacc, TRUE);
account_set_dirty(NULL, new_txn->kacc, TRUE);
}
beta_hub_ledger_refresh_txn_opens();
//todo: probably move this to refresh as well
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_SENSITIVE|FLG_REG_BALANCE));
//TODO: saverecondate is handled in external edit already
transaction_changed(new_txn, FALSE);
//#2065625
GLOBALS->changes_count++;
}
da_transaction_free (old_txn);
}
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_txnmedit(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
hub_ledger_edit_multiple(data->window, NULL, 0, user_data);
}
static void
_helper_pending_sub(struct hub_ledger_data *data, Transaction *txn)
{
Account *acc = da_acc_get(txn->kacc);
txn->flags &= ~(OF_ISIMPORT|OF_ISPAST);
g_return_if_fail(acc != NULL);
if( acc->nb_pending > 0 )
acc->nb_pending--;
if( data->nb_pending > 0 )
data->nb_pending--;
account_flags_eval(acc);
}
static void
hub_ledger_action_txnapprove(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
guint changes = GLOBALS->changes_count;
//gint count;
DB( g_print(" approve\n") );
//#2042692
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
//count = g_list_length(selection);
list = g_list_last(selection);
while(list != NULL)
{
Transaction *txn;
GtkTreeIter iter;
if( gtk_tree_model_get_iter(model, &iter, list->data) == TRUE )
{
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &txn, -1);
if( txn->flags & (OF_ISIMPORT|OF_ISPAST))
{
DB( g_print(" approving txn '%s' %.2f\n", txn->memo, txn->amount) );
account_balances_sub(txn);
//#1875100
_helper_pending_sub(data, txn);
account_balances_add(txn);
txn->dspflags |= FLAG_TMP_EDITED;
GLOBALS->changes_count++;
}
}
list = g_list_previous(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_txndelete(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
gchar *title;
guint changes = GLOBALS->changes_count;
gint count, result;
DB( g_print(" delete\n") );
//#2042692
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
count = g_list_length(selection);
title = g_strdup_printf (_("Are you sure you want to delete the %d selected transaction?"), count);
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
title,
_("If you delete a transaction, it will be permanently lost."),
_("_Delete"),
TRUE
);
g_free(title);
if(result == GTK_RESPONSE_OK)
{
//block selection change to avoid refresh and call to update
g_signal_handlers_block_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope))), G_CALLBACK (hub_ledger_selection), NULL);
list = g_list_last(selection);
while(list != NULL)
{
Transaction *rem_txn;
GtkTreeIter iter;
//#1860232 crash here if no test when reach a txn already removed
if( gtk_tree_model_get_iter(model, &iter, list->data) == TRUE )
{
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &rem_txn, -1);
if( !data->lockreconciled || (rem_txn->status != TXN_STATUS_RECONCILED) )
{
DB( g_print(" delete %s %.2f\n", rem_txn->memo, rem_txn->amount) );
//#1716181 also remove from the ptr_array (quickfilter)
g_ptr_array_remove(data->gpatxn, (gpointer)rem_txn);
// 1) remove visible current and potential xfer
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
//manage target open window as well
if((rem_txn->flags & OF_INTXFER))
{
Transaction *child = transaction_xfer_child_strong_get(rem_txn);
if( child )
{
if( data->showall )
{
_list_txn_remove_by_value(GTK_TREE_VIEW(data->LV_ope), child);
//#1716181 also remove from the ptr_array (quickfilter)
g_ptr_array_remove(data->gpatxn, (gpointer)child);
data->total--;
GLOBALS->changes_count++;
}
//5.8.4 if open remove target
GtkWindow *accwin = homebank_app_find_window(rem_txn->kxferacc);
if(accwin != NULL)
{
hub_ledger_remove_single_transaction(accwin, child);
DB( g_print(" xfer call refresh %d\n", rem_txn->kxferacc));
hub_ledger_update(GTK_WIDGET(accwin), GINT_TO_POINTER(FLG_REG_BALANCE));
}
}
}
// 2) manage pending
if( rem_txn->flags & (OF_ISIMPORT|OF_ISPAST) )
{
_helper_pending_sub(data, rem_txn);
}
// 3) remove datamodel
transaction_remove(rem_txn);
data->total--;
GLOBALS->changes_count++;
}
}
list = g_list_previous(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
g_signal_handlers_unblock_by_func (G_OBJECT (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope))), G_CALLBACK (hub_ledger_selection), NULL);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
}
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_grpflag(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
const gchar *name;
gint count, newflag = 0;
guint changes = GLOBALS->changes_count;
GtkTreeModel *model;
GList *selection, *list;
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
if(count > 0 )
{
name = g_action_get_name (G_ACTION (action));
if( strlen(name) < 3)
return;
switch(name[3])
{
case 'n': newflag=GRPFLAG_NONE; break;
case '1': newflag=GRPFLAG_RED; break;
case '2': newflag=GRPFLAG_ORANGE; break;
case '3': newflag=GRPFLAG_YELLOW; break;
case '4': newflag=GRPFLAG_GREEN; break;
case '5': newflag=GRPFLAG_BLUE; break;
case '6': newflag=GRPFLAG_PURPLE; break;
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
list = g_list_first(selection);
while(list != NULL)
{
Transaction *ope;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
if( !data->lockreconciled || (ope->status != TXN_STATUS_RECONCILED) )
{
DB( g_print(" change flag %d > %d\n", ope->grpflg, newflag) );
if( ope->grpflg != newflag )
GLOBALS->changes_count++;
ope->grpflg = newflag;
}
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
//refresh main
if( GLOBALS->changes_count > changes )
gtk_widget_queue_draw (data->LV_ope);
}
}
static void
hub_ledger_action_status_none(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeSelection *selection;
gint count, result;
guint changes = GLOBALS->changes_count;
_list_txn_selection_count_type(GTK_TREE_VIEW(data->LV_ope), &count, NULL);
if(count > 0 )
{
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
_("Are you sure you want to change the status to None?"),
_("Some transaction in your selection are already Reconciled."),
_("_Change"),
FALSE
);
}
else
result = GTK_RESPONSE_OK;
if( result == GTK_RESPONSE_OK )
{
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)_list_txn_status_selected_foreach_func,
GINT_TO_POINTER(TXN_STATUS_NONE));
DB( g_print(" none\n") );
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
//gtk_widget_queue_resize (data->LV_acc);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
GLOBALS->changes_count++;
}
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_status_clear (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeSelection *selection;
gint count, result;
guint changes = GLOBALS->changes_count;
_list_txn_selection_count_type(GTK_TREE_VIEW(data->LV_ope), &count, NULL);
if(count > 0 )
{
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
_("Are you sure you want to change the status to Cleared?"),
_("Some transaction in your selection are already Reconciled."),
_("_Change"),
FALSE
);
}
else
result = GTK_RESPONSE_OK;
if( result == GTK_RESPONSE_OK )
{ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)_list_txn_status_selected_foreach_func,
GINT_TO_POINTER(TXN_STATUS_CLEARED));
DB( g_print(" clear\n") );
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
//gtk_widget_queue_resize (data->LV_acc);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
GLOBALS->changes_count++;
}
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_status_reconcile (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeSelection *selection;
gint count, result;
guint changes = GLOBALS->changes_count;
_list_txn_selection_count_type(GTK_TREE_VIEW(data->LV_ope), &count, NULL);
if(count > 0 )
{
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
_("Are you sure you want to toggle the status Reconciled?"),
_("Some transaction in your selection are already Reconciled."),
_("_Toggle"),
FALSE
);
}
else
result = GTK_RESPONSE_OK;
if( result == GTK_RESPONSE_OK )
{
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)_list_txn_status_selected_foreach_func,
GINT_TO_POINTER(TXN_STATUS_RECONCILED));
DB( g_print(" reconcile\n") );
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
//gtk_widget_queue_resize (data->LV_acc);
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_BALANCE));
GLOBALS->changes_count++;
}
//refresh main
if( GLOBALS->changes_count > changes )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_find (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gtk_widget_grab_focus(data->ST_search);
}
static void
hub_ledger_action_createtemplate (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
gchar *title;
gint result, count;
DB( g_print("\n[hub-ledger] make archive\n") );
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
if( count > 0 )
{
title = g_strdup_printf (_("Are you sure you want to create template from the %d selected transaction?"), count);
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
title,
NULL,
_("_Create"),
FALSE
);
g_free(title);
/*
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("%d archives will be created"),
GLOBALS->changes_count
);
*/
if(result == GTK_RESPONSE_OK)
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
list = g_list_first(selection);
while(list != NULL)
{
Archive *item;
Transaction *ope;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
DB( g_print(" create archive %s %.2f\n", ope->memo, ope->amount) );
item = da_archive_malloc();
da_archive_init_from_transaction(item, ope, TRUE);
//GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
da_archive_append_new(item);
GLOBALS->changes_count++;
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
//#2000809 add confirmation
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Create Template"),
_("%d created with a prefilled icon"),
count
);
}
}
}
static void
hub_ledger_action_createassignment (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
gchar *title;
gint result, count;
DB( g_print("\n[hub-ledger] make assignment\n") );
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
if( count > 0 )
{
title = g_strdup_printf (_("Are you sure you want to create assignment from the %d selected transaction?"), count);
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
title,
NULL,
_("_Create"),
FALSE
);
g_free(title);
if(result == GTK_RESPONSE_OK)
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
list = g_list_first(selection);
while(list != NULL)
{
Assign *item;
Transaction *ope;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
DB( g_print(" create assignment %s %.2f\n", ope->memo, ope->amount) );
item = da_asg_malloc();
da_asg_init_from_transaction(item, ope);
//5.7.1 free if fail
if( da_asg_append(item) == TRUE )
{
GLOBALS->changes_count++;
}
else
{
da_asg_free(item);
count--;
}
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
//#2000809 add confirmation
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Create Assignment"),
_("%d created with a prefilled icon"),
count
);
}
}
}
static void
hub_ledger_action_exportcsv (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gchar *name, *filepath;
GString *node;
GIOChannel *io;
gboolean hassplit, hasstatus;
guint flags;
DB( g_print("\n[hub-ledger] export csv\n") );
name = g_strdup_printf("%s.csv", (data->showall == TRUE) ? GLOBALS->owner :data->acc->name);
filepath = g_build_filename(PREFS->path_export, name, NULL);
if( ui_dialog_export_csv(GTK_WINDOW(data->window), &filepath, &hassplit, &hasstatus, data->showall) == GTK_RESPONSE_ACCEPT )
{
DB( g_printf(" filename is '%s'\n", filepath) );
io = g_io_channel_new_file(filepath, "w", NULL);
if(io != NULL)
{
flags = LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
//#2037468
if( data->showall )
flags |= LST_TXN_EXP_ACC;
if( hasstatus )
flags |= LST_TXN_EXP_CLR;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_ope), FALSE, hassplit, FALSE, flags);
g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
g_io_channel_unref (io);
g_string_free(node, TRUE);
}
}
g_free(filepath);
g_free(name);
}
static void
hub_ledger_action_print (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gchar *title, *name, *filepath;
GString *node;
guint flags;
gboolean statement = FALSE;
title = (data->showall == FALSE) ? data->acc->name : _("All transactions");
name = g_strdup_printf("hb-%s.pdf", title);
filepath = g_build_filename(PREFS->path_export, name, NULL);
g_free(name);
gint8 leftcols[8] = { 0, 1, 2, 3, -1 };
flags = LST_TXN_EXP_CLR;
if( data->showall )
{
flags |= LST_TXN_EXP_ACC;
//{ 0, 1, 2, 3, 4, -1 };
leftcols[4] = 4;
leftcols[5] = -1;
}
else
{
flags |= LST_TXN_EXP_BAL;
//{ 0, 1, 2, 3, -1 };
//2044850 revert fitwidth for statement
statement = TRUE;
}
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_ope), TRUE, FALSE, FALSE, flags);
hb_print_listview(GTK_WINDOW(data->window), node->str, leftcols, title, filepath, statement);
g_string_free(node, TRUE);
g_free(filepath);
/* old < 5.7
if( ui_dialog_export_pdf(GTK_WINDOW(data->window), &filepath) == GTK_RESPONSE_ACCEPT )
{
DB( g_printf(" filename is'%s'\n", filepath) );
hb_export_pdf_listview(GTK_TREE_VIEW(data->LV_ope), filepath, data->acc->name);
}
*/
}
static void
hub_ledger_action_check_catsign_mark (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] check category sign\n\n") );
data->chkcatsign = transaction_check_chkcatsign_mark (data->acc);
if( data->chkcatsign > 0 )
{
gchar *text = g_strdup_printf(_("%d category sign don't match"), data->chkcatsign);
gtk_label_set_text(GTK_LABEL(data->LB_chkcatsign), text);
g_free(text);
}
else
gtk_label_set_text(GTK_LABEL(data->LB_chkcatsign), _("No category sign don't match were found !"));
gtk_widget_show(data->IB_chkcatsign);
//#GTK+710888: hack waiting a fix
gtk_widget_queue_resize (data->IB_chkcatsign);
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
}
static void
hub_ledger_action_check_catsign_unmark(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] uncheck category sign\n\n") );
if(data->showall == FALSE)
{
data->chkcatsign = 0;
gtk_widget_hide(data->IB_chkcatsign);
transaction_check_chkcatsign_unmark(data->acc);
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
}
}
static void
hub_ledger_cb_bar_chkcatsign_response(GtkWidget *info_bar, gint response_id, gpointer user_data)
{
struct hub_ledger_data *data;
DB( g_print("\n[hub-ledger] bar_chlcatsign_response\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(info_bar, GTK_TYPE_WINDOW)), "inst_data");
switch( response_id )
{
case GTK_RESPONSE_CLOSE:
hub_ledger_action_check_catsign_unmark(NULL, NULL, data);
gtk_widget_hide (GTK_WIDGET (info_bar));
break;
}
}
static void
hub_ledger_action_duplicate_mark (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] check duplicate\n\n") );
// open dialog to select date tolerance in days
// with info message
// with check/fix button and progress bar
// parse listview txn, clear/mark duplicate
// apply filter
if(data->showall == FALSE)
{
gint daygap;
daygap = gtk_spin_button_get_value(GTK_SPIN_BUTTON(data->NB_txn_daygap));
data->similar = transaction_similar_mark (data->acc, daygap);
if( data->similar > 0 )
{
gchar *text = g_strdup_printf(_("There is %d group of similar transactions"), data->similar);
gtk_label_set_text(GTK_LABEL(data->LB_duplicate), text);
g_free(text);
}
else
gtk_label_set_text(GTK_LABEL(data->LB_duplicate), _("No similar transaction were found !"));
gtk_widget_show(data->IB_duplicate);
//#GTK+710888: hack waiting a fix
gtk_widget_queue_resize (data->IB_duplicate);
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
}
}
static void
hub_ledger_action_duplicate_unmark(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] uncheck duplicate\n\n") );
if(data->showall == FALSE)
{
data->similar = 0;
gtk_widget_hide(data->IB_duplicate);
transaction_similar_unmark(data->acc);
DB( g_print(" redraw LV_ope\n") );
gtk_widget_queue_draw (data->LV_ope);
}
}
static void
hub_ledger_cb_bar_duplicate_response(GtkWidget *info_bar, gint response_id, gpointer user_data)
{
struct hub_ledger_data *data;
DB( g_print("\n[hub-ledger] bar_duplicate_response\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(info_bar, GTK_TYPE_WINDOW)), "inst_data");
switch( response_id )
{
case HB_RESPONSE_REFRESH:
hub_ledger_action_duplicate_mark(NULL, NULL, data);
break;
case GTK_RESPONSE_CLOSE:
hub_ledger_action_duplicate_unmark(NULL, NULL, data);
gtk_widget_hide (GTK_WIDGET (info_bar));
break;
}
}
static void
hub_ledger_action_check_internal_xfer(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GtkTreeIter iter;
GList *badxferlist;
gboolean valid, lockrecon;
gint count;
// noaction if closed account
if(data->closed)
return;
DB( g_print("\n[hub-ledger] check intenal xfer\n") );
lockrecon = gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled));
badxferlist = NULL;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Transaction *txn;
gtk_tree_model_get (model, &iter,
MODEL_TXN_POINTER, &txn,
-1);
//#1909749 skip reconciled if lock is ON
if( lockrecon && txn->status == TXN_STATUS_RECONCILED )
goto next;
if( txn->flags & OF_INTXFER )
{
if( transaction_xfer_child_strong_get(txn) == NULL )
{
DB( g_print(" invalid xfer: '%s'\n", txn->memo) );
//test unrecoverable (kxferacc = 0)
if( txn->kxferacc <= 0 )
{
DB( g_print(" unrecoverable, revert to normal xfer\n") );
txn->dspflags |= FLAG_TMP_EDITED;
txn->paymode = PAYMODE_XFER;
txn->kxfer = 0;
txn->kxferacc = 0;
}
else
{
//perf must use preprend, see glib doc
badxferlist = g_list_prepend(badxferlist, txn);
}
}
}
next:
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
count = g_list_length (badxferlist);
DB( g_print(" found %d invalid int xfer\n", count) );
if(count <= 0)
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Check internal transfer result"),
_("No inconsistency found !")
);
}
else
{
gboolean do_fix;
do_fix = ui_dialog_msg_question(
GTK_WINDOW(data->window),
_("Check internal transfer result"),
_("Inconsistency were found: %d\n"
"do you want to review and fix?"),
count
);
if(do_fix == GTK_RESPONSE_YES)
{
GList *tmplist = g_list_first(badxferlist);
while (tmplist != NULL)
{
Transaction *stxn = tmplist->data;
//future (open dialog to select date tolerance in days)
transaction_xfer_search_or_add_child(GTK_WINDOW(data->window), FALSE, stxn, 0);
tmplist = g_list_next(tmplist);
}
}
}
g_list_free (badxferlist);
}
//#1983995 copy raw amount
static void
hub_ledger_action_copyrawamount(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
Transaction *ope;
ope = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_ope));
if(ope != NULL)
{
Currency *cur = da_cur_get(ope->kcur);
if( cur != NULL )
{
GtkClipboard *clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gdouble monval = hb_amount_round(ABS(ope->amount), cur->frac_digits);
gchar *text;
text = g_strdup_printf ("%.*f", cur->frac_digits, monval);
DB( g_print(" raw amount is '%s'\n", text) );
gtk_clipboard_set_text(clipboard, text, strlen(text));
g_free(text);
}
}
}
static void
hub_ledger_action_viewsplit(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
Transaction *ope;
ope = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_ope));
if(ope != NULL)
ui_split_view_dialog(data->window, ope);
}
static void
hub_ledger_action_exportqif(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gchar *filename;
// noaction if show all account
if(data->showall)
return;
DB( g_print("\n[hub-ledger] export QIF\n") );
if( ui_file_chooser_qif(GTK_WINDOW(data->window), &filename) == TRUE )
{
hb_export_qif_account_single(filename, data->acc);
g_free( filename );
}
}
static void
hub_ledger_action_converttoeuro(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gchar *msg;
gint result;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] convert euro\n") );
msg = g_strdup_printf(_("Every transaction amount will be divided by %.6f."), PREFS->euro_value);
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->window),
_("Are you sure you want to convert this account to Euro as Major currency?"),
msg,
_("_Convert"),
FALSE
);
g_free(msg);
if(result == GTK_RESPONSE_OK)
{
account_convert_euro(data->acc);
hub_ledger_update(data->LV_ope, GINT_TO_POINTER(FLG_REG_BALANCE));
}
}
static void
hub_ledger_action_edit_copy_clipboard(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkClipboard *clipboard;
GString *node;
guint flags;
DB( g_print("\n[hub-ledger] copy clipboard\n") );
data = user_data;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
flags = LST_TXN_EXP_CLR | LST_TXN_EXP_PMT | LST_TXN_EXP_CAT | LST_TXN_EXP_TAG;
if(data->showall == TRUE)
flags |= LST_TXN_EXP_ACC;
node = list_txn_to_string(GTK_TREE_VIEW(data->LV_ope), TRUE, FALSE, TRUE, flags);
clipboard = gtk_clipboard_get_default(gdk_display_get_default());
gtk_clipboard_set_text(clipboard, node->str, node->len);
g_string_free(node, TRUE);
}
//#1818052 wish: copy/paste one/multiple transaction(s)
static void
hub_ledger_action_edit_copy(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GtkTreeModel *model;
GList *selection, *list;
gint count;
// noaction if closed account
if(data->closed)
return;
DB( g_print("\n[hub-ledger] copy\n") );
//struct hub_ledger_data *data2;
//data2 = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(menuitem), GTK_TYPE_WINDOW)), "inst_data");
//DB( g_print("%p = %p\n", data, data2) );
g_queue_free_full(data->q_txn_clip, (GDestroyNotify)da_transaction_free);
data->q_txn_clip = g_queue_new();
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
count = 0;
list = g_list_first(selection);
while(list != NULL)
{
Transaction *ope, *newope;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
newope = da_transaction_clone(ope);
DB( g_print(" copy txn %p - '%.2f' '%s'\n", newope, newope->amount, newope->memo) );
g_queue_push_tail(data->q_txn_clip, newope);
count++;
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
if(count > 0 )
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_SENSITIVE));
}
static void
hub_ledger_action_edit_paste(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
GList *list;
gboolean istoday = FALSE;
// noaction if closed account
if(data->closed)
return;
DB( g_print("\n[hub-ledger] paste normal\n") );
istoday = ( !strcmp(g_action_get_name (G_ACTION (action)), "txnpastet")) ? TRUE : FALSE;
DB( g_print(" paste %d - as today=%d\n", (gint)g_queue_get_length(data->q_txn_clip), istoday) );
list = g_queue_peek_head_link(data->q_txn_clip);
while (list != NULL)
{
Transaction *item = list->data;
Transaction *add_txn;
DB( g_print(" paste txn %p - '%.2f' '%s'\n", item, item->amount, item->memo) );
if( istoday == TRUE )
item->date = GLOBALS->today;
add_txn = transaction_add(GTK_WINDOW(data->window), FALSE, item);
add_txn->dspflags |= FLAG_TMP_ADDED;
hub_ledger_add_after_propagate(data, add_txn);
//#2068634
GLOBALS->changes_count++;
list = g_list_next(list);
}
hub_ledger_update(data->window, GINT_TO_POINTER(FLG_REG_SENSITIVE+FLG_REG_BALANCE));
}
static void
hub_ledger_action_assign(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
gint count;
gboolean usermode = TRUE;
gboolean lockrecon;
// noaction if show all/closed account
if(data->showall || data->closed)
return;
DB( g_print("\n[hub-ledger] assign\n") );
lockrecon = gtk_switch_get_active (GTK_SWITCH(data->SW_lockreconciled));
count = transaction_auto_assign(g_queue_peek_head_link(data->acc->txn_queue), data->acc->key, lockrecon);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_ope));
GLOBALS->changes_count += count;
//inform the user
if(usermode == TRUE)
{
gchar *txt;
if(count == 0)
txt = _("No transaction changed");
else
txt = _("transaction changed: %d");
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Automatic assignment result"),
txt,
count);
}
//refresh main
if( count > 0 )
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
hub_ledger_action_browse(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
// noaction if show all account
if(data->showall)
return;
DB( g_print("\n[hub-ledger] browse\n") );
if( account_has_website(data->acc) )
{
homebank_util_url_show(data->acc->website);
}
}
static void
hub_ledger_action_close(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
DB( g_print("\n[hub-ledger] close\n") );
DB( g_print(" window %p\n", data->window) );
gtk_window_close(GTK_WINDOW(data->window));
}
/*static void not_implemented (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
char *text = g_strdup_printf ("Action “%s” not implemented", g_action_get_name (G_ACTION (action)));
g_print("%s\n", text);
g_free (text);
}*/
static GActionEntry win_entries[] = {
{ "txnadd" , hub_ledger_action_txnaddherit, NULL, NULL, NULL, {0,0,0} },
{ "txnherit" , hub_ledger_action_txnaddherit, NULL, NULL, NULL, {0,0,0} },
{ "txnedit" , hub_ledger_action_txnedit, NULL, NULL, NULL, {0,0,0} },
{ "txnmedit" , hub_ledger_action_txnmedit, NULL, NULL, NULL, {0,0,0} },
{ "txnapprove" , hub_ledger_action_txnapprove, NULL, NULL, NULL, {0,0,0} },
{ "txnreject" , hub_ledger_action_txndelete, NULL, NULL, NULL, {0,0,0} },
{ "stanon" , hub_ledger_action_status_none, NULL, NULL, NULL, {0,0,0} },
{ "staclr" , hub_ledger_action_status_clear, NULL, NULL, NULL, {0,0,0} },
{ "starec" , hub_ledger_action_status_reconcile, NULL, NULL, NULL, {0,0,0} },
//func use direct name[3]
{ "flgn" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg1" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg2" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg3" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg4" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg5" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "flg6" , hub_ledger_action_grpflag, NULL, NULL, NULL, {0,0,0} },
{ "viewsplit" , hub_ledger_action_viewsplit, NULL, NULL, NULL, {0,0,0} },
{ "copyamt" , hub_ledger_action_copyrawamount, NULL, NULL, NULL, {0,0,0} },
{ "newtpl" , hub_ledger_action_createtemplate, NULL, NULL, NULL, {0,0,0} },
{ "newasg" , hub_ledger_action_createassignment, NULL, NULL, NULL, {0,0,0} },
{ "txndel" , hub_ledger_action_txndelete, NULL, NULL, NULL, {0,0,0} },
{ "txnup" , hub_ledger_action_move_up, NULL, NULL, NULL, {0,0,0} },
{ "txndw" , hub_ledger_action_move_down, NULL, NULL, NULL, {0,0,0} },
{ "expqif" , hub_ledger_action_exportqif, NULL, NULL, NULL, {0,0,0} },
{ "expcsv" , hub_ledger_action_exportcsv, NULL, NULL, NULL, {0,0,0} },
{ "print" , hub_ledger_action_print, NULL, NULL, NULL, {0,0,0} },
{ "browse" , hub_ledger_action_browse, NULL, NULL, NULL, {0,0,0} },
{ "close" , hub_ledger_action_close, NULL, NULL, NULL, {0,0,0} },
{ "txncopy" , hub_ledger_action_edit_copy, NULL, NULL, NULL, {0,0,0} },
{ "txnpaste" , hub_ledger_action_edit_paste, NULL, NULL, NULL, {0,0,0} },
{ "txnpastet" , hub_ledger_action_edit_paste, NULL, NULL, NULL, {0,0,0} },
{ "txnclip" , hub_ledger_action_edit_copy_clipboard, NULL, NULL, NULL, {0,0,0} },
{ "txnfind" , hub_ledger_action_find, NULL, NULL, NULL, {0,0,0} },
{ "mrkdup" , hub_ledger_action_duplicate_mark, NULL, NULL, NULL, {0,0,0} },
{ "mrksign" , hub_ledger_action_check_catsign_mark, NULL, NULL, NULL, {0,0,0} },
{ "chkxfer" , hub_ledger_action_check_internal_xfer, NULL, NULL, NULL, {0,0,0} },
{ "runasg" , hub_ledger_action_assign, NULL, NULL, NULL, {0,0,0} },
{ "convert" , hub_ledger_action_converttoeuro, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
static void
_add_accel(gchar *action_name, gchar *accel)
{
const gchar *vaccels[] = { accel, NULL };
gtk_application_set_accels_for_action(GLOBALS->application, action_name, vaccels);
//g_menu_item_set_attribute(menuitem, "accel", "s", accel);
}
static void
_add_menuitem(GMenu *menu, gchar *label, gchar *action_name, gchar *accel)
{
GMenuItem *menuitem = g_menu_item_new(label, action_name);
if( accel != NULL )
_add_accel(action_name, accel);
g_menu_append_item (menu, menuitem);
g_object_unref (menuitem);
}
static GMenu *
hub_ledger_popmenu_create2(struct hub_ledger_data *data)
{
GMenu *menu, *submenu, *section;
//g_menu_append (submenu, , "ldgr.");
menu = g_menu_new();
section = g_menu_new ();
_add_menuitem (section, _("_Add...") , "ldgr.txnadd" , "N");
_add_menuitem (section, _("_Inherit...") , "ldgr.txnherit", "U");
_add_menuitem (section, _("_Edit...") , "ldgr.txnedit", "E");
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
submenu = g_menu_new ();
_add_menuitem (submenu, _("_None") , "ldgr.stanon", "C");
_add_menuitem (submenu, _("_Cleared") , "ldgr.staclr", "R");
_add_menuitem (submenu, _("_Reconciled") , "ldgr.starec", "R");
g_menu_append_submenu (section, _("_Status"), G_MENU_MODEL(submenu));
g_object_unref (submenu);
submenu = g_menu_new ();
_add_menuitem (submenu, _("None") , "ldgr.flgn", "0");
_add_menuitem (submenu, _("Red") , "ldgr.flg1", "1");
_add_menuitem (submenu, _("Orange") , "ldgr.flg2", "2");
_add_menuitem (submenu, _("Yellow") , "ldgr.flg3", "3");
_add_menuitem (submenu, _("Green") , "ldgr.flg4", "4");
_add_menuitem (submenu, _("Blue") , "ldgr.flg5", "5");
_add_menuitem (submenu, _("Purple") , "ldgr.flg6", "6");
g_menu_append_submenu (section, _("_Flag"), G_MENU_MODEL(submenu));
g_object_unref (submenu);
section = g_menu_new ();
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
_add_menuitem (section, _("Approve") , "ldgr.txnapprove", NULL);
_add_menuitem (section, _("Reject (Delete)...") , "ldgr.txnreject", NULL);
section = g_menu_new ();
_add_menuitem (section, _("_Multiple Edit...") , "ldgr.txnmedit", NULL);
_add_menuitem (section, _("View _Split") , "ldgr.viewsplit", NULL);
_add_menuitem (section, _("Copy raw amount") , "ldgr.copyamt", NULL);
_add_menuitem (section, _("Create template...") , "ldgr.newtpl", NULL);
_add_menuitem (section, _("Create assignment...") , "ldgr.newasg", NULL);
_add_menuitem (section, _("_Delete...") , "ldgr.txndel", "Delete");
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
section = g_menu_new ();
_add_menuitem (section, _("_Up") , "ldgr.txnup", NULL);
_add_menuitem (section, _("_Down") , "ldgr.txndw", NULL);
g_menu_append_section (menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
return menu;
}
static GMenu *
hub_ledger_menubar_create2(struct hub_ledger_data *data)
{
GMenu *menubar, *menu;
GMenuItem *menuitem;
gboolean showall, closed;
showall = data->showall;
closed = data->closed;
//g_menu_append (submenu, , "ldgr.");
menubar = g_menu_new();
//menu Account
menu = g_menu_new();
if( showall == FALSE )
_add_menuitem(menu, _("Export QIF...") , "ldgr.expqif" , NULL);
_add_menuitem(menu, _("Export CSV...") , "ldgr.expcsv" , NULL);
_add_menuitem(menu, _("Print...") , "ldgr.print" , "P");
if( showall == FALSE )
_add_menuitem(menu, _("Browse Website") , "ldgr.browse" , NULL);
_add_menuitem(menu, _("Close") , "ldgr.close" , "W");
//...
menuitem = g_menu_item_new (_("A_ccount"), NULL);
g_menu_item_set_submenu (menuitem, G_MENU_MODEL (menu));
g_object_unref (menu);
g_menu_append_item (menubar, menuitem);
g_object_unref (menuitem);
//menu Edit
menu = g_menu_new();
if( closed == FALSE )
{
_add_menuitem(menu, _("Copy") , "ldgr.txncopy" , "C");
_add_menuitem(menu, _("Paste") , "ldgr.txnpaste" , "V");
_add_menuitem(menu, _("Paste (today)") , "ldgr.txnpastet" , "V");
}
_add_menuitem(menu, _("Copy clipboard") , "ldgr.txnclip" , "C");
_add_menuitem(menu, _("Find") , "ldgr.txnfind" , "F");
//...
menuitem = g_menu_item_new (_("_Edit"), NULL);
g_menu_item_set_submenu (menuitem, G_MENU_MODEL (menu));
g_object_unref (menu);
g_menu_append_item (menubar, menuitem);
g_object_unref (menuitem);
//menu Tools
if( closed == FALSE && showall == FALSE )
{
menu = g_menu_new();
_add_menuitem(menu, _("Mark duplicate...") , "ldgr.mrkdup" , NULL);
_add_menuitem(menu, _("Mark category sign...") , "ldgr.mrksign" , NULL);
_add_menuitem(menu, _("Check internal transfer"), "ldgr.chkxfer" , NULL);
_add_menuitem(menu, _("Auto. assignments") , "ldgr.runasg" , NULL);
_add_menuitem(menu, _("Convert to Euro...") , "ldgr.convert" , NULL);
//...
menuitem = g_menu_item_new (_("_Tools"), NULL);
g_menu_item_set_submenu (menuitem, G_MENU_MODEL (menu));
g_object_unref (menu);
g_menu_append_item (menubar, menuitem);
g_object_unref (menuitem);
}
return menubar;
}
static GtkWidget *
hub_ledger_toolbar_create(struct hub_ledger_data *data)
{
GtkWidget *toolbar, *button, *bbox, *hbox, *widget, *label;
toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (toolbar), bbox);
data->BT_up = button = make_image_button2(ICONNAME_HB_OPE_MOVUP, _("Move transaction up"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txnup");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_down = button = make_image_button2(ICONNAME_HB_OPE_MOVDW, _("Move transaction down"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txndw");
gtk_box_prepend (GTK_BOX (bbox), button);
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (toolbar), bbox);
data->BT_add = button = make_image_button2(ICONNAME_HB_OPE_ADD, _("Add a new transaction"));
g_object_set(button, "label", _("Add"), "always-show-image", TRUE, NULL);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txnadd");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_herit = button = make_image_button2(ICONNAME_HB_OPE_HERIT, _("Inherit from the active transaction"));
g_object_set(button, "label", _("Inherit"), "always-show-image", TRUE, NULL);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txnherit");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_edit = button = make_image_button2(ICONNAME_HB_OPE_EDIT, _("Edit the active transaction"));
g_object_set(button, "label", _("Edit"), "always-show-image", TRUE, NULL);
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txnedit");
gtk_box_prepend (GTK_BOX (bbox), button);
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (toolbar), bbox);
data->BT_clear = button = make_image_button2(ICONNAME_HB_OPE_CLEARED, _("Toggle cleared for selected transaction(s)"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.staclr");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_reconcile = button = make_image_button2(ICONNAME_HB_OPE_RECONCILED, _("Toggle reconciled for selected transaction(s)"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.starec");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_multiedit = button = make_image_button2(ICONNAME_HB_OPE_MULTIEDIT, _("Edit multiple transaction"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txnmedit");
gtk_box_prepend (GTK_BOX (bbox), button);
data->BT_delete = button = make_image_button2(ICONNAME_HB_OPE_DELETE, _("Delete selected transaction(s)"));
gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "ldgr.txndel");
gtk_box_prepend (GTK_BOX (bbox), button);
//#1909749 lock/unlock reconciled
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_widget_set_margin_start (hbox, SPACING_LARGE);
gtk_box_prepend (GTK_BOX (toolbar), hbox);
label = gtk_label_new (_("Reconciled changes is"));
gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
data->LB_lockreconciled = label;
gtk_box_prepend (GTK_BOX (hbox), label);
//widget = hbtk_image_new_from_icon_name_16 (ICONNAME_CHANGES_PREVENT);
widget = gtk_image_new();
g_object_set(widget, "icon-name", ICONNAME_CHANGES_PREVENT, NULL);
data->IM_lockreconciled = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = gtk_switch_new();
data->SW_lockreconciled = widget;
gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
gtk_box_prepend (GTK_BOX (hbox), widget);
return toolbar;
}
static gboolean
hub_ledger_window_getgeometry(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
//struct hub_ledger_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[hub-ledger] get geometry\n") );
//store position and size
wg = &PREFS->acc_wg;
wg->s = gtk_window_is_maximized(GTK_WINDOW(widget));
if(!wg->s)
{
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
}
//DB( g_print(" window: l=%d, t=%d, w=%d, h=%d s=%d\n", wg->l, wg->t, wg->w, wg->h, wg->s) );
return FALSE;
}
static gboolean
hub_ledger_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct hub_ledger_data *data = user_data;
struct WinGeometry *wg;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print("\n[hub-ledger] -- delete-event\n") );
wg = &PREFS->acc_wg;
wg->s = gtk_window_is_maximized(GTK_WINDOW(widget));
//store columns
list_txn_get_columns(GTK_TREE_VIEW(data->LV_ope));
return FALSE;
}
static gboolean
hub_ledger_window_destroy (GtkWidget *widget, gpointer user_data)
{
struct hub_ledger_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[hub-ledger] -- destroy event\n") );
//enable define windows
GLOBALS->define_off--;
//5.8.6 unmark
if( data->similar > 0 )
{
DB( g_print(" unmark similar\n") );
hub_ledger_action_duplicate_unmark(NULL, NULL, data);
}
/* free title and filter */
DB( g_print(" user_data=%p to be free\n", user_data) );
g_free(data->wintitle);
if(data->gpatxn != NULL)
g_ptr_array_free (data->gpatxn, TRUE);
g_queue_free_full(data->q_txn_clip, (GDestroyNotify)da_transaction_free);
da_flt_free(data->filter);
g_free(data);
//our global list has changed, so update the treeview
//TODO: find another way to signal
//do it on mainwindow focus??
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_TXNLIST+UF_REFRESHALL));
return FALSE;
}
/*
* if accnum = 0 or acc is null : show all account
*/
GtkWidget *
hub_ledger_window_new(Account *acc)
{
struct hub_ledger_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainvbox, *intbox, *actionbox, *labelbox, *hbox;
GtkWidget *menubar, *bar, *scrollwin, *treeview, *label, *widget;
GActionGroup *actions;
GMenu *gmenumodel;
DB( g_print("\n[hub-ledger] create_hub_ledger_window\n") );
data = g_malloc0(sizeof(struct hub_ledger_data));
if(!data) return NULL;
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* setup TODO: to moove */
data->filter = da_flt_malloc();
data->q_txn_clip = g_queue_new ();
/* create window, etc */
window = gtk_application_window_new(GLOBALS->application);
gtk_widget_set_name(GTK_WIDGET(window), "ledger");
data->window = window;
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" new window=%p, inst_data=%p\n", window, data) );
//init global vars
data->acc = acc;
data->showall = FALSE;
data->closed = FALSE;
if( acc != NULL )
{
g_object_set_data(G_OBJECT(window), "key", GINT_TO_POINTER(acc->key));
if( (data->acc->flags & AF_CLOSED) == FALSE )
{
//#2058696 add number & institutioname
GString *node = g_string_sized_new(64);
g_string_append(node, acc->name);
//#2065758
if( (acc->number != NULL) && (strlen(acc->number) > 0) )
{
g_string_append(node, " : ");
g_string_append(node, acc->number);
}
//#2065758
if( (acc->bankname != NULL) && (strlen(acc->bankname) > 0) )
{
g_string_append(node, ", ");
g_string_append(node, acc->bankname);
}
g_string_append(node, " - HomeBank");
data->wintitle = g_string_free(node, FALSE);
}
else
{
data->wintitle = g_strdup_printf("%s %s - HomeBank", data->acc->name, _("(closed)"));
data->closed = TRUE;
}
}
else
{
g_object_set_data(G_OBJECT(window), "key", GINT_TO_POINTER(-1));
data->wintitle = g_strdup_printf(_("%s - HomeBank"), _("All transactions"));
data->showall = TRUE;
}
gtk_window_set_title (GTK_WINDOW (window), data->wintitle);
// action group
actions = (GActionGroup*)g_simple_action_group_new ();
data->actions = actions;
g_action_map_add_action_entries (G_ACTION_MAP (actions),
win_entries, G_N_ELEMENTS (win_entries), data);
gtk_widget_insert_action_group (window, "ldgr", actions);
/* connect signal */
g_signal_connect (window, "destroy", G_CALLBACK (hub_ledger_window_destroy), (gpointer)data);
g_signal_connect (window, "delete-event", G_CALLBACK (hub_ledger_window_dispose), (gpointer)data);
g_signal_connect (window, "configure-event", G_CALLBACK (hub_ledger_window_getgeometry), (gpointer)data);
g_signal_connect (window, "key-press-event", G_CALLBACK (hub_ledger_cb_on_key_press), (gpointer)data);
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child(GTK_WINDOW(window), mainvbox);
//1 - menubar
gmenumodel = hub_ledger_menubar_create2(data);
#if( (GTK_MAJOR_VERSION < 4) )
menubar = gtk_menu_bar_new_from_model(G_MENU_MODEL(gmenumodel));
#else
menubar = gtk_popover_menu_bar_new_from_model(G_MENU_MODEL(gmenumodel));
#endif
gtk_box_prepend (GTK_BOX (mainvbox), menubar);
//2 - account txn notification
bar = gtk_info_bar_new ();
data->IB_accnotif = bar;
gtk_box_prepend (GTK_BOX (mainvbox), bar);
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
//gtk_info_bar_set_show_close_button (GTK_INFO_BAR (bar), TRUE);
label = gtk_label_new (NULL);
data->LB_accnotif = label;
//gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
//gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
widget = gtk_button_new_with_label(_("Show"));
data->BT_info_showpending = widget;
gtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), widget);
//3 - info bar for duplicate
bar = gtk_info_bar_new_with_buttons (_("_Refresh"), HB_RESPONSE_REFRESH, NULL);
data->IB_duplicate = bar;
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
gtk_info_bar_set_show_close_button (GTK_INFO_BAR (bar), TRUE);
gtk_box_prepend (GTK_BOX (mainvbox), bar);
hbox = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
label = gtk_label_new (NULL);
data->LB_duplicate = label;
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_box_prepend (GTK_BOX (hbox), label);
//5.8.6
label = make_label(_("Date _gap:"), 0, 0.5);
gtk_box_prepend (GTK_BOX (hbox), label);
widget = make_numeric(label, 0, HB_DATE_MAX_GAP);
data->NB_txn_daygap = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
//3c - bar for category sign check
bar = gtk_info_bar_new ();
data->IB_chkcatsign = bar;
gtk_box_prepend (GTK_BOX (mainvbox), bar);
gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
gtk_info_bar_set_show_close_button (GTK_INFO_BAR (bar), TRUE);
label = gtk_label_new (NULL);
data->LB_chkcatsign = label;
//gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
//gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_box_prepend (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label);
//4 - windows interior
intbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hb_widget_set_margin(GTK_WIDGET(intbox), SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainvbox), intbox);
//4a - actionbox
actionbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (intbox), actionbox);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_widget_set_halign(hbox, GTK_ALIGN_START);
scrollwin = make_scrolled_window_ns(GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
hbtk_box_prepend (GTK_BOX (actionbox), scrollwin);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), hbox);
widget = make_daterange(NULL, DATE_RANGE_FLAG_CUSTOM_DISABLE);
data->CY_range = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = make_image_toggle_button2(ICONNAME_HB_OPE_FUTURE, NULL);
data->CM_future = widget;
//#2008521 set more accurate tooltip
gchar *tt = g_strdup_printf(_("Toggle show %d days ahead"), PREFS->date_future_nbdays);
gtk_widget_set_tooltip_text (widget, tt);
g_free(tt);
gtk_box_prepend (GTK_BOX (hbox), widget);
//5.8 flag
widget = make_fltgrpflag(NULL);
data->CY_flag = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_combo_box_new_with_data(label, CYA_FLT_TYPE);
data->CY_type = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
widget = hbtk_combo_box_new_with_data(label, CYA_FLT_STATUS);
data->CY_status = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
//5.8 beta test
if( data->showall )
{
widget = create_popover_widget(GTK_WINDOW(data->window), data->filter);
data->PO_hubfilter = widget;
gtk_box_prepend (GTK_BOX (hbox), widget);
}
widget = make_image_button2(ICONNAME_HB_FILTER, _("Edit filter"));
data->BT_filter = widget;
gtk_box_prepend (GTK_BOX (actionbox), widget);
//widget = gtk_button_new_with_mnemonic (_("Reset _filters"));
//widget = gtk_button_new_with_mnemonic (_("_Reset"));
widget = make_image_button2(ICONNAME_HB_CLEAR, _("Clear filter"));
data->BT_reset = widget;
gtk_box_prepend (GTK_BOX (actionbox), widget);
widget = make_image_button2(ICONNAME_HB_REFRESH, _("Refresh results"));
data->BT_refresh = widget;
gtk_box_prepend (GTK_BOX (actionbox), widget);
widget = make_image_toggle_button2(ICONNAME_HB_LIFEENERGY, _("Toggle Life Energy"));
data->BT_lifnrg = widget;
gtk_box_prepend (GTK_BOX (actionbox), widget);
//TRANSLATORS: this is for Euro specific users, a toggle to display in 'Minor' currency
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_box_prepend (GTK_BOX (actionbox), widget);
//quick search
widget = make_search ();
data->ST_search = widget;
gtk_widget_set_size_request(widget, HB_MINWIDTH_SEARCH, -1);
gtk_widget_set_halign(widget, GTK_ALIGN_END);
gtk_box_prepend (GTK_BOX (actionbox), widget);
/* grid line 2 */
labelbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_margin_start(labelbox, SPACING_TINY);
gtk_widget_set_margin_end(labelbox, SPACING_TINY);
//gtk_widget_set_hexpand(actionbox, TRUE);
gtk_box_prepend (GTK_BOX (intbox), labelbox);
// text total/selection
label = make_label(NULL, 0.0, 0.5);
gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
//#1930395 text selectable for copy/paste
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
//gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
gtk_widget_set_hexpand (label, TRUE);
data->TX_selection = label;
gtk_box_prepend (GTK_BOX (labelbox), label);
label = gtk_label_new(_("Reconciled:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (labelbox), label);
widget = gtk_label_new(NULL);
gtk_widget_set_margin_start(widget, SPACING_TINY);
data->TX_balance[0] = widget;
gtk_box_prepend (GTK_BOX (labelbox), widget);
label = gtk_label_new(_("Cleared:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (labelbox), label);
widget = gtk_label_new(NULL);
gtk_widget_set_margin_start(widget, SPACING_TINY);
data->TX_balance[1] = widget;
gtk_box_prepend (GTK_BOX (labelbox), widget);
label = gtk_label_new(_("Today:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (labelbox), label);
widget = gtk_label_new(NULL);
gtk_widget_set_margin_start(widget, SPACING_TINY);
data->TX_balance[2] = widget;
gtk_box_prepend (GTK_BOX (labelbox), widget);
label = gtk_label_new(_("Future:"));
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (labelbox), label);
widget = gtk_label_new(NULL);
gtk_widget_set_margin_start(widget, SPACING_TINY);
data->TX_balance[3] = widget;
gtk_box_prepend (GTK_BOX (labelbox), widget);
//list
GtkWidget *lbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (intbox), lbox);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
treeview = (GtkWidget *)create_list_transaction(LIST_TXN_TYPE_BOOK, PREFS->lst_ope_columns);
data->LV_ope = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (lbox), scrollwin);
list_txn_set_save_column_width(GTK_TREE_VIEW(treeview), TRUE);
/* toolbars */
bar = hub_ledger_toolbar_create(data);
data->TB_bar = bar;
gtk_style_context_add_class (gtk_widget_get_style_context (bar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (lbox), bar);
//TODO should move this
//setup
//TODO minor data seems no more used
data->lockreconciled = PREFS->safe_lock_recon;
gtk_switch_set_active(GTK_SWITCH(data->SW_lockreconciled), PREFS->safe_lock_recon);
list_txn_set_lockreconciled(GTK_TREE_VIEW(data->LV_ope), PREFS->safe_lock_recon);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope))), "minor", data->CM_minor);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_future), (PREFS->date_future_nbdays > 0) ? TRUE : FALSE );
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor), GLOBALS->minor);
gtk_widget_grab_focus(GTK_WIDGET(data->LV_ope));
// connect signals
g_signal_connect (data->BT_info_showpending, "clicked", G_CALLBACK (hub_ledger_info_cb_show_pending), NULL);
g_signal_connect (data->IB_duplicate , "response", G_CALLBACK (hub_ledger_cb_bar_duplicate_response), NULL);
g_signal_connect (data->IB_chkcatsign , "response", G_CALLBACK (hub_ledger_cb_bar_chkcatsign_response), NULL);
g_signal_connect (data->SW_lockreconciled, "state-set", G_CALLBACK (hub_ledger_cb_recon_change), NULL);
data->handler_id[HID_RANGE] = g_signal_connect (data->CY_range , "changed", G_CALLBACK (hub_ledger_cb_filter_daterange), NULL);
g_signal_connect (data->CY_flag, "changed", G_CALLBACK (hub_ledger_cb_filterbar_change), NULL);
data->handler_id[HID_TYPE] = g_signal_connect (data->CY_type , "changed", G_CALLBACK (hub_ledger_cb_filterbar_change), NULL);
data->handler_id[HID_STATUS] = g_signal_connect (data->CY_status, "changed", G_CALLBACK (hub_ledger_cb_filterbar_change), NULL);
g_signal_connect (data->CM_future, "toggled", G_CALLBACK (hub_ledger_cb_filter_daterange), NULL);
if( data->showall )
g_signal_connect( ui_flt_popover_hub_get_combobox(GTK_BOX(data->PO_hubfilter), NULL), "changed", G_CALLBACK (beta_hub_ledger_cb_preset_change), NULL);
g_signal_connect (data->BT_reset , "clicked", G_CALLBACK (hub_ledger_cb_filter_reset), NULL);
g_signal_connect (data->BT_refresh, "clicked", G_CALLBACK (hub_ledger_cb_refresh), NULL);
g_signal_connect (data->BT_filter , "clicked", G_CALLBACK (hub_ledger_cb_editfilter), NULL);
g_signal_connect (data->BT_lifnrg , "clicked", G_CALLBACK (hub_ledger_cb_button_lifenergy), NULL);
g_signal_connect (data->CM_minor , "toggled", G_CALLBACK (hub_ledger_toggle_minor), NULL);
data->handler_id[HID_SEARCH] = g_signal_connect (data->ST_search, "search-changed", G_CALLBACK (quick_search_text_changed_cb), data);
//#1879451 deselect all when quicksearch has the focus (to prevent delete)
g_signal_connect (data->ST_search, "focus-in-event", G_CALLBACK (hub_ledger_cb_search_focus_in_event), NULL);
//g_signal_connect (GTK_TREE_VIEW(treeview), "cursor-changed", G_CALLBACK (hub_ledger_update), (gpointer)2);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK (hub_ledger_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(treeview), "row-activated", G_CALLBACK (hub_ledger_onRowActivated), GINT_TO_POINTER(2));
//new GMenu
gmenumodel = hub_ledger_popmenu_create2(data);
GtkWidget *gtkmenu = gtk_menu_new_from_model(G_MENU_MODEL(gmenumodel));
//always attach to get sensitive gaction
gtk_menu_attach_to_widget(GTK_MENU(gtkmenu), treeview, NULL);
g_signal_connect (treeview, "button-press-event", G_CALLBACK (listview_context_cb), gtkmenu);
//setup, init and show window
wg = &PREFS->acc_wg;
DB( g_print(" set default size w:%d h:%d m:%d\n", wg->w, wg->h, wg->s) );
gtk_window_set_default_size(GTK_WINDOW(window), wg->w, wg->h);
//gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
if(wg->s == 0)
{
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
DB( g_print(" move to %d %d\n", wg->l, wg->t) );
}
else
gtk_window_maximize(GTK_WINDOW(window));
DB( g_print(" show\n") );
gtk_widget_show_all (window);
gtk_widget_hide(data->IB_accnotif);
gtk_widget_hide(data->IB_duplicate);
gtk_widget_hide(data->IB_chkcatsign);
return window;
}
homebank-5.9.7/src/hb-pref-data.h 0000664 0001750 0001750 00000002712 14736461415 016023 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_PREF_DATA_H__
#define __HB_PREF_DATA_H__
#include
typedef struct
{
gchar *locale;
gchar *name;
} LangName;
typedef struct
{
gushort id;
gushort mceii;
gchar *iso;
gchar *name;
gdouble value;
//gchar *prefix_symbol; /* max symbol is 3 digits in unicode */
//gchar *suffix_symbol; /* but mostly is 1 digit */
gchar *symbol;
gboolean sym_prefix;
gchar *decimal_char;
gchar *grouping_char;
gushort frac_digits;
} EuroParams;
gboolean euro_country_is_mceii(gint ctryid);
EuroParams *euro_country_get(guint ctryid);
gboolean euro_country_notmceii_rate_update(guint ctryid);
gchar *languagename_get(const gchar *locale);
#endif
homebank-5.9.7/src/rep-budget.h 0000644 0001750 0001750 00000004415 14736461415 015627 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_REPBUDGET_H__
#define __HOMEBANK_REPBUDGET_H__
enum {
HID_REPBUDGET_MINDATE,
HID_REPBUDGET_MAXDATE,
HID_REPBUDGET_MINMONTHYEAR,
HID_REPBUDGET_MAXMONTHYEAR,
HID_REPBUDGET_RANGE,
MAX_REPBUDGET_HID
};
/* list stat */
enum
{
LST_BUDGET_POS,
LST_BUDGET_KEY,
LST_BUDGET_NAME,
LST_BUDGET_SPENT,
LST_BUDGET_BUDGET,
LST_BUDGET_FULFILLED,
LST_BUDGET_RESULT,
LST_BUDGET_STATUS,
NUM_LST_BUDGET
};
#define LST_BUDGET_POS_UNBUDGETED G_MAXINT-2
typedef enum
{
REP_BUD_MODE_TOTAL,
REP_BUD_MODE_TIME
} HbRepBudMode;
struct repbudget_data
{
GQueue *txn_queue;
Filter *filter;
gdouble total_spent;
gdouble total_budget;
gboolean detail;
gboolean legend;
GtkWidget *window;
GActionGroup *actions;
gboolean mapped_done;
GtkWidget *TB_bar;
GtkWidget *BT_list;
GtkWidget *BT_progress;
GtkWidget *BT_detail;
GtkWidget *BT_refresh;
GtkWidget *BT_print;
GtkWidget *BT_export;
GtkWidget *TX_info;
GtkWidget *TX_daterange;
GtkWidget *CM_untiltoday;
GtkWidget *CM_onlyout;
GtkWidget *CM_minor;
GtkWidget *RA_mode;
GtkWidget *CY_type;
GtkWidget *GR_listbar;
GtkWidget *BT_expand;
GtkWidget *BT_collapse;
GtkWidget *LV_report;
//GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *LB_maxdate;
GtkWidget *SB_mindate, *SB_maxdate;
GtkWidget *CY_range;
GtkWidget *GR_result;
GtkWidget *TX_total[3];
GtkWidget *RE_progress;
GtkWidget *GR_detail;
GtkWidget *LV_detail;
gulong handler_id[MAX_REPBUDGET_HID];
};
GtkWidget *repbudget_window_new(void);
#endif
homebank-5.9.7/src/hb-split.c 0000664 0001750 0001750 00000014607 14736461407 015315 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-transaction.h"
#include "hb-split.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void da_split_free(Split *item)
{
if(item != NULL)
{
if(item->memo != NULL)
g_free(item->memo);
g_free(item);
}
}
Split *da_split_malloc(void)
{
return g_malloc0(sizeof(Split));
}
void da_split_destroy(GPtrArray *splits)
{
DB( g_print("da_split_destroy\n") );
if(splits != NULL)
g_ptr_array_free(splits, TRUE);
}
GPtrArray *da_split_new(void)
{
DB( g_print("da_split_new\n") );
return g_ptr_array_new_with_free_func((GDestroyNotify)da_split_free);
}
static GPtrArray *da_split_new_full(guint size)
{
DB( g_print("da_split_new\n") );
return g_ptr_array_new_full(size, (GDestroyNotify)da_split_free);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gint da_splits_compare_func(gpointer a, gpointer b)
{
Split *sa = *(Split **)a;
Split *sb = *(Split **)b;
DB( g_print(" sort %d %s - %d %s\n", sa->pos, sa->memo, sb->pos, sb->memo) );
return sa->pos - sb->pos;
}
void da_splits_sort(GPtrArray *splits)
{
DB( g_print("da_splits_sort\n") );
if(splits == NULL)
{
//g_warning("NULL splits");
return;
}
g_ptr_array_sort(splits, (GCompareFunc) da_splits_compare_func);
}
guint da_splits_length(GPtrArray *splits)
{
DB( g_print("da_splits_length\n") );
if(splits == NULL)
{
//g_warning("NULL splits");
return 0;
}
return splits->len;
}
gboolean da_splits_delete(GPtrArray *splits, Split *item)
{
DB( g_print("da_splits_remove\n") );
if(splits == NULL)
{
g_warning("NULL splits");
return FALSE;
}
return g_ptr_array_remove(splits, item);
}
void da_splits_append(GPtrArray *splits, Split *item)
{
DB( g_print("da_splits_append\n") );
if(splits == NULL)
{
g_warning("NULL splits");
return;
}
if(splits->len < TXN_MAX_SPLIT)
g_ptr_array_add (splits, item);
}
Split *da_split_duplicate(Split *src)
{
Split *new = da_split_malloc ();
new->kcat = src->kcat;
new->memo = g_strdup(src->memo);
new->amount = src->amount;
return new;
}
Split *da_splits_get(GPtrArray *splits, guint index)
{
return g_ptr_array_index(splits, index);
}
GPtrArray *da_splits_clone(GPtrArray *src_splits)
{
GPtrArray *new_splits;
guint i;
DB( g_print("da_splits_clone\n") );
if(src_splits == NULL)
{
//g_warning("NULL splits");
return NULL;
}
new_splits = da_split_new_full (src_splits->len);
for(i=0;ilen;i++)
{
Split *src, *new;
src = g_ptr_array_index(src_splits, i);
new = da_split_duplicate(src);
da_splits_append (new_splits, new);
}
return new_splits;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint da_splits_parse(GPtrArray *splits, gchar *cats, gchar *amounts, gchar *memos)
{
gchar **cat_a, **amt_a, **mem_a;
guint count, i;
Split *split;
if(splits == NULL)
return 0;
DB( g_print(" split parse %s :: %s :: %s\n", cats, amounts, memos) );
cat_a = g_strsplit (cats, "||", 0);
amt_a = g_strsplit (amounts, "||", 0);
mem_a = g_strsplit (memos, "||", 0);
count = g_strv_length(amt_a);
if( (count == g_strv_length(cat_a)) && (count == g_strv_length(mem_a)) )
{
for(i=0;ikcat = atoi(cat_a[i]);
split->memo = g_strdup(mem_a[i]);
split->amount = g_ascii_strtod(amt_a[i], NULL);
da_splits_append (splits, split);
}
}
else
{
g_warning("invalid split parse");
}
g_strfreev (mem_a);
g_strfreev (amt_a);
g_strfreev (cat_a);
return count;
}
guint da_splits_tostring(GPtrArray *splits, gchar **cats, gchar **amounts, gchar **memos)
{
guint i;
Split *split;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
GString *cat_a, *amt_a , *mem_a;
if(splits == NULL)
return 0;
DB( g_print(" splits tostring\n") );
cat_a = g_string_new (NULL);
amt_a = g_string_new (NULL);
mem_a = g_string_new (NULL);
for(i=0;ilen;i++)
{
split = g_ptr_array_index(splits, i);
g_string_append_printf (cat_a, "%d", split->kcat);
g_string_append(amt_a, g_ascii_dtostr (buf, sizeof (buf), split->amount) );
g_string_append(mem_a, split->memo);
if((i+1) < splits->len)
{
g_string_append(cat_a, "||");
g_string_append(amt_a, "||");
g_string_append(mem_a, "||");
}
}
*cats = g_string_free(cat_a, FALSE);
*amounts = g_string_free(amt_a, FALSE);
*memos = g_string_free(mem_a, FALSE);
return i;
}
guint da_splits_consistency (GPtrArray *splits)
{
Split *split;
guint i;
if(splits == NULL)
return 0;
for(i=0;ilen;i++)
{
split = g_ptr_array_index(splits, i);
//#1340142 check split category
if(da_cat_get(split->kcat) == NULL)
{
g_warning("split consistency: fixed invalid split cat %d", split->kcat);
split->kcat = 0;
GLOBALS->changes_count++;
}
//TODO #1910819 must round frac digit, should have beed done > 5.5
//split->amount = hb_amount_round(split->amount, cur->frac_digits);
}
return splits->len;
}
//#2026641
guint da_splits_anonymize (GPtrArray *splits)
{
Split *split;
guint cnt, i;
if(splits == NULL)
return 0;
cnt = da_splits_length (splits);
for(i=0;imemo != NULL)
g_free(split->memo);
split->memo = g_strdup_printf("memo %d", i);
}
return cnt;
}
homebank-5.9.7/src/hb-group.h 0000664 0001750 0001750 00000003046 14736461415 015315 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_GROUP_H__
#define __HB_GROUP_H__
typedef struct _group Group;
struct _group
{
guint32 key;
//gushort flags;
//gushort type;
gchar *name;
};
/*typedef enum
{
GRP_TYPE_ACC = 1,
//GRP_TYPE_PAY
//GRP_TYPE_CAT
} HbGroupType;
*/
void da_grp_free(Group *item);
Group *da_grp_malloc(void);
void da_grp_destroy(void);
void da_grp_new(void);
guint da_grp_length(void);
gboolean da_grp_create_none(void);
gboolean da_grp_remove(guint32 key);
gboolean da_grp_insert(Group *item);
gboolean da_grp_append(Group *item);
guint32 da_grp_get_max_key(void);
Group *da_grp_get_by_name(gchar *name);
Group *da_grp_get_by_imp_name(gchar *name);
Group *da_grp_get(guint32 key);
void group_delete_unused(void);
GList *group_glist_sorted(gint column);
#endif homebank-5.9.7/src/ui-payee.h 0000644 0001750 0001750 00000004710 14736461415 015305 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_PAYEE_GTK_H__
#define __HB_PAYEE_GTK_H__
enum
{
LST_DEFPAY_TOGGLE,
LST_DEFPAY_DATAS,
NUM_LST_DEFPAY
};
#define LST_DEFPAY_SORT_USETXN 2
#define LST_DEFPAY_SORT_USECFG 3
#define LST_DEFPAY_SORT_NAME 4
#define LST_DEFPAY_SORT_DEFPAY 5
#define LST_DEFPAY_SORT_DEFCAT 6
struct ui_pay_manage_dialog_data
{
GtkWidget *dialog;
GActionGroup * actions;
gboolean mapped_done;
GtkWidget *BT_showhidden;
GtkWidget *BT_showusage;
GtkWidget *ST_search;
GtkWidget *RE_addreveal;
GtkWidget *ST_name;
GtkWidget *LV_pay;
GtkWidget *BT_add;
GtkWidget *BT_edit;
GtkWidget *BT_merge;
GtkWidget *BT_delete;
GtkWidget *BT_hide;
gboolean usagefilled;
gint change;
};
struct payPopContext
{
GtkTreeModel *model;
guint except_key;
};
/* = = = = = = = = = = */
GtkWidget *ui_pay_entry_popover_get_entry(GtkBox *box);
Payee *ui_pay_entry_popover_get(GtkBox *box);
guint32 ui_pay_entry_popover_get_key_add_new(GtkBox *box);
guint32 ui_pay_entry_popover_get_key(GtkBox *box);
void ui_pay_entry_popover_set_active(GtkBox *box, guint32 key);
GtkWidget *ui_pay_entry_popover_new(GtkWidget *label);
/* = = = = = = = = = = */
guint ui_pay_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter);
void ui_pay_listview_quick_select(GtkTreeView *treeview, const gchar *uri);
void ui_pay_listview_add(GtkTreeView *treeview, Payee *item);
guint32 ui_pay_listview_get_selected_key(GtkTreeView *treeview);
void ui_pay_listview_remove_selected(GtkTreeView *treeview);
void ui_pay_listview_populate(GtkWidget *treeview, gchar *needle, gboolean showhidden);
GtkWidget *ui_pay_listview_new(gboolean withtoggle, gboolean withcount);
GtkWidget *ui_pay_manage_dialog (void);
#endif
homebank-5.9.7/src/dsp-mainwindow.c 0000644 0001750 0001750 00000230200 15120541536 016504 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-xml.h"
#include "hub-account.h"
#include "dsp-mainwindow.h"
#include "list-account.h"
#include "list-operation.h"
#include "hub-scheduled.h"
#include "hub-reptotal.h"
#include "hub-reptime.h"
#include "hub-transaction.h"
#include "dsp-account.h"
#include "ui-assist-import.h"
#include "ui-assist-start.h"
#include "ui-account.h"
#include "ui-currency.h"
#include "ui-payee.h"
#include "ui-category.h"
#include "ui-archive.h"
#include "ui-assign.h"
#include "ui-dialogs.h"
#include "ui-budget.h"
#include "ui-budget-tabview.h"
#include "ui-pref.h"
#include "ui-hbfile.h"
#include "ui-transaction.h"
#include "ui-tag.h"
#include "ui-widgets.h"
#include "rep-balance.h"
#include "rep-budget.h"
#include "rep-stats.h"
#include "rep-time.h"
#include "rep-vehicle.h"
#include "gtk-chart.h"
//old url prior 2019
//#define HOMEBANK_URL_HELP "https://www.gethomebank.org/help/"
//#define HOMEBANK_URL_HELP_ONLINE "https://launchpad.net/homebank/+addquestion"
//#define HOMEBANK_URL_HELP_PROBLEM "https://launchpad.net/homebank/+filebug"
//#define HOMEBANK_URL_HELP_TRANSLATE "https://launchpad.net/homebank/+translations"
#define HOMEBANK_URL_HELP "index.html"
#define HOMEBANK_URL_BASE "https://www.gethomebank.org"
#define HOMEBANK_URL_HELP_ONLINE HOMEBANK_URL_BASE "/support.php"
#define HOMEBANK_URL_HELP_DONATE HOMEBANK_URL_BASE "/donate.php"
#define HOMEBANK_URL_HELP_UPDATES HOMEBANK_URL_BASE "/downloads.php"
#define HOMEBANK_URL_HELP_PROBLEM HOMEBANK_URL_BASE "/development.php#bug"
#define HOMEBANK_URL_HELP_TRANSLATE HOMEBANK_URL_BASE "/development.php#translate"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = */
static void
activate_url (GtkAboutDialog *about, const gchar *link, gpointer data)
{
DB( g_print("activate url %s\n", link) );
homebank_util_url_show (link);
}
static void hbfile_about(void)
{
GtkWidget *dialog;
GdkPixbuf *pixbuf;
gchar *pathfilename;
gchar *version;
static const gchar *artists[] = {
"Maxime DOYEN",
NULL
};
static const gchar *authors[] = {
"Lead developer:\n" \
"Maxime DOYEN",
"\nContributor:\n" \
"Ga\xc3\xabtan LORIDANT (Maths formulas for charts)\n",
NULL
};
/*
const gchar *documenters[] = {
"Maxime DOYEN",
NULL
};
*/
static const gchar *copyright = "Copyright \xc2\xa9 1995-2025 - Maxime DOYEN";
version = g_strdup_printf (PACKAGE_VERSION "\nRunning against GTK+ %d.%d.%d",
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), HOMEBANK_URL_BASE);
gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), "Visit the HomeBank website");
gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), "homebank");
pathfilename = g_build_filename(homebank_app_get_images_dir(), "splash.png", NULL);
pixbuf = gdk_pixbuf_new_from_file(pathfilename, NULL);
g_free(pathfilename);
if( pixbuf )
{
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_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), );
g_signal_connect (dialog, "activate-link", G_CALLBACK (activate_url), NULL);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(version);
}
#define HB_MIMETYPE "application/x-homebank"
static void
ui_wallet_cb_recent_chooser_clear (GtkRecentChooser *chooser, struct hbfile_data *data)
{
GList *list;
DB( g_print("\n[ui-mainwindow] recent chooser clear\n") );
list = gtk_recent_manager_get_items(data->recent_manager);
while (list != NULL)
{
GtkRecentInfo *recentinfo = list->data;
const gchar *uri;
uri = gtk_recent_info_get_uri(recentinfo);
if( uri != NULL )
{
size_t len = strlen(uri);
DB( g_print(" uri: '%s' %ld\n", uri, len ) );
//if( !g_strcmp0(HB_MIMETYPE, gtk_recent_info_get_mime_type(recentinfo)) )
if( len > 4 && g_ascii_strcasecmp(uri + len - 4, ".xhb") == 0 )
{
GError *error = NULL;
const gchar *uri = gtk_recent_info_get_uri(recentinfo);
DB( g_print(" > should remove\n") );
gtk_recent_manager_remove_item(data->recent_manager, uri, &error);
if (error)
{
g_warning ("Could not remove uri \"%s\": %s", uri, error->message);
g_error_free (error);
}
}
}
gtk_recent_info_unref(recentinfo);
list = g_list_next(list);
}
g_list_free(list);
}
static void
ui_wallet_recent_chooser_item_activated_cb (GtkRecentChooser *chooser, struct hbfile_data *data)
{
gchar *uri, *path;
GError *error = NULL;
DB( g_print("\n[ui-mainwindow] recent chooser activated\n") );
uri = gtk_recent_chooser_get_current_uri (chooser);
DB( g_print(" '%s'\n", uri) );
path = g_filename_from_uri (uri, NULL, &error);
if (error)
{
g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
g_error_free (error);
return;
}
if( ui_dialog_msg_savechanges(data->window, NULL) == TRUE )
{
//todo: FixMe
/*
if (! load)
{
gpw_recent_remove (gpw, path);
}
*/
hbfile_change_filepath(path);
ui_wallet_open_internal(data->window, NULL);
}
else
{
g_free (path);
}
g_free (uri);
}
static void
ui_wallet_recent_add (struct hbfile_data *data, const gchar *path)
{
GtkRecentData *recent_data;
gchar *uri;
GError *error = NULL;
DB( g_print("\n[ui-mainwindow] recent_add\n") );
//#2043886 portable don't store to recent
#ifdef PORTABLE_APP
DB( g_print(" ignored in portable app\n") );
return;
#endif
DB( g_print(" - file has .xhb suffix = %d\n", g_str_has_suffix (path, ".xhb") ) );
if( g_str_has_suffix (path, ".xhb") == FALSE ) //ignore reverted file
return;
uri = g_filename_to_uri (path, NULL, &error);
if (error)
{
g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
g_error_free (error);
return;
}
recent_data = g_slice_new (GtkRecentData);
recent_data->display_name = NULL;
recent_data->description = NULL;
recent_data->mime_type = HB_MIMETYPE;
recent_data->app_name = (gchar *) g_get_application_name ();
recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
recent_data->groups = NULL;
recent_data->is_private = FALSE;
if (!gtk_recent_manager_add_full (data->recent_manager,
uri,
recent_data))
{
g_warning ("Unable to add '%s' to the list of recently used documents", uri);
}
g_free (uri);
g_free (recent_data->app_exec);
g_slice_free (GtkRecentData, recent_data);
}
static void
ui_wallet_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_wallet_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
}
static void
ui_wallet_close_openbooks(void)
{
GList *l = gtk_application_get_windows(GLOBALS->application);
while (l != NULL)
{
GtkWindow *window = l->data;
gint key;
key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "key"));
DB( g_print(" window: %p: key=%d '%s'\n", window, key, gtk_window_get_title(window)) );
if( key != 0 ) //wallet have no key
{
DB( g_print(" >closing\n") );
gtk_window_close (GTK_WINDOW(window));
}
l = g_list_next(l);
}
//empty loop before doing any further
//this will safely close any window
hb_window_run_pending();
}
static void
ui_wallet_clear(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
gboolean file_clear = GPOINTER_TO_INT(user_data);
GSList *list;
DB( g_print("\n[ui-mainwindow] clear\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
// Close opened account window
// Clear TreeView
ui_wallet_close_openbooks();
//5.5.1 should close any toplevel windows as well (reports...)
DB( g_print(" closing %d windows\n", g_slist_length(GLOBALS->openwindows) ));
list = GLOBALS->openwindows;
while( list != NULL)
{
GtkWindow *window = GTK_WINDOW(list->data);
DB( g_print(" closing type:%d title:'%s'\n",
gtk_window_get_window_type(window),
gtk_window_get_title(window) ));
gtk_window_close (GTK_WINDOW(window));
list = g_slist_next(list);
}
//empty loop before doing any further
//this will safely close any window
hb_window_run_pending();
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))));
//#2065628 missed clear lead txn and so may crash
gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_FUTURE]))));
gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_REMIND]))));
//TODO those 2 do nothing
ui_hub_reptotal_clear(GLOBALS->mainwindow, NULL);
ui_hub_reptime_clear(GLOBALS->mainwindow, NULL);
data->showall = FALSE;
ui_hub_account_dispose(data);
ui_hub_account_setup(data);
ui_hub_reptotal_dispose(data);
ui_hub_reptotal_setup(data);
ui_hub_reptime_dispose(data);
ui_hub_reptime_setup(data);
hb_window_run_pending();
hbfile_cleanup(file_clear);
hbfile_setup(file_clear);
hb_window_run_pending();
}
static void
ui_wallet_revert(GtkWidget *widget, gpointer user_data)
{
//struct hbfile_data *data;
gchar *basename;
gchar *title;
gchar *secondtext;
gint result;
DB( g_print("\n[ui-mainwindow] revert\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
basename = g_path_get_basename(GLOBALS->xhb_filepath);
title = g_strdup_printf (
_("Revert unsaved changes to file '%s'?"), basename);
secondtext =
_("- Changes made to the file will be permanently lost\n"
"- File will be reloaded from the last save (.xhb~)");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(GLOBALS->mainwindow),
title,
secondtext,
_("_Revert"),
TRUE
);
g_free(title);
g_free(basename);
if( result == GTK_RESPONSE_OK )
{
DB( g_print(" - should revert\n") );
hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~"));
ui_wallet_open_internal(widget, NULL);
hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
}
}
static void
ui_wallet_addtransactions(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkWidget *window;
gint result = HB_RESPONSE_ADD;
gint kacc, count;
Transaction *ope;
DB( g_print("\n[ui-mainwindow] add transactions\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
/* init the transaction */
homebank_app_date_get_julian();
//#1656531
kacc = (data->acc != NULL) ? data->acc->key : 0;
window = create_deftransaction_window(GTK_WINDOW(data->window), TXN_DLG_ACTION_ADD, TXN_DLG_TYPE_TXN, kacc);
ope = da_transaction_malloc();
count = 0;
while(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP)
{
DB( g_print(" - last result %d\n", result) );
/* fill in the transaction */
if( result == HB_RESPONSE_ADD )
{
da_transaction_init(ope, kacc);
}
deftransaction_set_transaction(window, ope);
result = gtk_dialog_run (GTK_DIALOG (window));
DB( g_print(" - dialog result is %d\n", result) );
if(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP || result == GTK_RESPONSE_ACCEPT)
{
Transaction *addtxn;
deftransaction_get(window, NULL);
addtxn = transaction_add(GTK_WINDOW(window), TRUE, ope);
//2044601 if NULL xfer may have beed aborted
if( addtxn != NULL )
{
//#1831975
if(PREFS->txn_showconfirm)
deftransaction_external_confirm(window, ope);
DB( g_print(" - added 1 transaction to %d\n", ope->kacc) );
ui_hub_account_compute(GLOBALS->mainwindow, NULL);
//dirty and refresh open ledger
account_set_dirty(NULL, addtxn->kacc, TRUE);
if( (addtxn->flags & OF_INTXFER) )
account_set_dirty(NULL, addtxn->kxferacc, TRUE);
beta_hub_ledger_refresh_txn_opens();
count++;
}
else
{
//2044601 keep actual txn
result = HB_RESPONSE_ADDKEEP;
DB( g_print(" - no add, keep current\n") );
}
}
}
da_transaction_free(ope);
deftransaction_dispose(window, NULL);
gtk_window_destroy (GTK_WINDOW(window));
/* todo optimize this */
if(count > 0)
{
GLOBALS->changes_count += count;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE+UF_REFRESHALL));
}
}
static gboolean
ui_wallet_open_backup_check_confirm(gchar *filepath)
{
gboolean retval = FALSE;
gchar *basename, *secondtext;
gboolean result;
basename = g_path_get_basename(filepath);
secondtext = g_strdup_printf (
_("You 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 the backup file?"),
secondtext,
_("_Open backup"),
FALSE
);
g_free(secondtext);
g_free(basename);
if( result == GTK_RESPONSE_OK )
retval = TRUE;
return retval;
}
void
ui_wallet_open_check(GtkWidget *widget, gchar *filepath)
{
//struct hbfile_data *data;
gboolean doopen = TRUE;
if( filepath == NULL )
return;
DB( g_print("\n[ui-mainwindow] open check\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" path='%s'\n", filepath) );
if (g_file_test (filepath, G_FILE_TEST_EXISTS) == TRUE)
{
//#1710955 test for backup open
if( hbfile_file_isbackup(filepath) )
{
if( ui_wallet_open_backup_check_confirm(filepath) == TRUE )
{
GLOBALS->hbfile_is_bak = TRUE;
doopen = TRUE;
}
else
{
doopen = FALSE;
}
}
}
else
{
g_warning (_("Unable to open '%s', the file does not exist.\n"), filepath);
}
if(doopen)
{
hbfile_change_filepath(g_strdup(filepath));
ui_wallet_open_internal(widget, NULL);
}
g_free(filepath);
}
static void
ui_wallet_open(GtkWidget *widget, gpointer user_data)
{
//struct hbfile_data *data;
gboolean bakmode = GPOINTER_TO_INT(user_data);;
gboolean doopen = TRUE;
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");
//#1791554 do ask for save confirm
if( bakmode != TRUE )
doopen = ui_dialog_msg_savechanges(widget,NULL);
if( doopen == TRUE )
{
if( ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_OPEN, &filename, bakmode) == TRUE )
{
ui_wallet_open_check(widget, filename);
}
}
}
/*
* open the file stored in GLOBALS->xhb_filepath
*/
void
ui_wallet_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_wallet_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) );
GLOBALS->xhb_timemodified = hbfile_file_get_time_modified(GLOBALS->xhb_filepath);
hbfile_file_hasrevert(GLOBALS->xhb_filepath);
if(PREFS->appendscheduled)
{
DB( g_print(" - auto: post all pending\n") );
scheduled_post_all_pending();
}
if(PREFS->do_update_currency)
{
DB( g_print(" - auto: update currency\n") );
//TODO: currency_needs_online
ui_cur_manage_dialog_update_currencies(GTK_WINDOW(GLOBALS->mainwindow), NULL);
}
homebank_lastopenedfiles_save();
//#1931816 should always sort all txn once
DB( g_print(" - sort txn all account\n") );
account_transaction_sort();
//TODO: delete this after computing done at xml read
DB( g_print(" - compute balance\n") );
account_compute_balances(TRUE);
ui_wallet_recent_add(data, GLOBALS->xhb_filepath);
}
else
{
gchar *msg = _("Unknown 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_wallet_clear(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
}
DB( g_print(" - start update window\n") );
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL+UF_REFRESHALL));
}
}
static void
ui_wallet_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, FALSE) == 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
{
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) );
if( GLOBALS->xhb_timemodified != time_modified )
{
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"),
TRUE
);
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);
}
if(r == XML_OK)
{
DB( g_print(" + OK...\n") );
GLOBALS->changes_count = 0;
GLOBALS->xhb_timemodified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
ui_wallet_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
);
}
}
void
ui_wallet_update(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
gint flags;
DB( g_print("\n[ui-mainwindow] update\n") );
//todo: remove this later
//as we direct call from destroy of other window, widget might already be gone
if( !GTK_IS_WIDGET(widget) ) //TODO: hit
{
DB( g_print(" bad widget prevent !\n") );
return;
}
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//data = INST_DATA(widget);
flags = GPOINTER_TO_INT(user_data);
/* changes */
#if MYDEBUG == 1
gchar *chgtxt = g_strdup_printf("%d", GLOBALS->changes_count);
gtk_label_set_label(GTK_LABEL(data->dbgchange), chgtxt);
g_free(chgtxt);
#endif
/* set window title */
if(flags & UF_TITLE)
{
gchar *basename;
gchar *changed;
DB( g_print(" 1: wintitle %p\n", data->wintitle) );
basename = g_path_get_basename(GLOBALS->xhb_filepath);
g_free(data->wintitle);
changed = (GLOBALS->changes_count > 0) ? "*" : "";
data->wintitle = g_strdup_printf("%s%s - %s - " PROGNAME, changed, basename, GLOBALS->owner);
gtk_window_set_title (GTK_WINDOW (gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), data->wintitle);
g_free(basename);
}
/* update disabled things */
if(flags & UF_SENSITIVE)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
gboolean active, sensitive;
DB( g_print(" 2: disabled, opelist count\n") );
//#1656531
data->acc = NULL;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
active = gtk_tree_selection_get_selected(selection, &model, &iter);
if(active)
{
Account *acc;
gint depth;
path = gtk_tree_model_get_path(model, &iter);
depth = gtk_tree_path_get_depth(path);
if( depth > 1 )
{
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPACC_DATAS, &acc, -1);
data->acc = acc;
DB( g_print(" depth is %d, acc=%p\n", depth, acc) );
}
else
active = FALSE;
}
//5.7 browse account website
sensitive = account_has_website(data->acc);
DB( g_print(" account has url: %d\n", sensitive) );
gtk_widget_set_sensitive(data->BT_browse, sensitive);
DB( g_print(" changes %d - new %d\n", GLOBALS->changes_count, GLOBALS->hbfile_is_new) );
// save
DB( g_print(" save\n") );
sensitive = (GLOBALS->changes_count != 0 ) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_save, sensitive);
gtk_widget_set_sensitive(data->BT_save, sensitive);
// backup
DB( g_print(" has changes & revert\n") );
sensitive = ( (GLOBALS->changes_count != 0) && GLOBALS->xhb_hasrevert ) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_revert, sensitive);
//1859346 restore backup avail anytime
//gtk_widget_set_sensitive(data->MI_openbak, sensitive);
// report dialog ope, disable manage acc/pay/cat/bud/tag
DB( g_print(" has report open ?\n") );
sensitive = GLOBALS->define_off == 0 ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_manacc, sensitive);
gtk_widget_set_sensitive(data->MI_manpay, sensitive);
gtk_widget_set_sensitive(data->MI_mancat, sensitive);
gtk_widget_set_sensitive(data->MI_manbud, sensitive);
gtk_widget_set_sensitive(data->MI_manbudtable, sensitive);
gtk_widget_set_sensitive(data->MI_mantag, sensitive);
gtk_widget_set_sensitive(data->MI_prefs, sensitive);
gtk_widget_set_sensitive(data->BT_manacc, sensitive);
gtk_widget_set_sensitive(data->BT_manpay, sensitive);
gtk_widget_set_sensitive(data->BT_mancat, sensitive);
gtk_widget_set_sensitive(data->BT_manbud, sensitive);
// empty account list: disable Archives, Edit, Filter, Add, Statistics, Overdrawn, Car Cost
DB( g_print(" has acc ?\n") );
sensitive = da_acc_length() > 0 ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_close, sensitive);
gtk_widget_set_sensitive(data->MI_exportqif, sensitive);
gtk_widget_set_sensitive(data->MI_anonymise, sensitive);
gtk_widget_set_sensitive(data->MI_mantpl, sensitive);
gtk_widget_set_sensitive(data->MI_txnadd, sensitive);
gtk_widget_set_sensitive(data->MI_txnshow, sensitive);
gtk_widget_set_sensitive(data->MI_txnshowall, sensitive);
gtk_widget_set_sensitive(data->MI_repstat, sensitive);
gtk_widget_set_sensitive(data->MI_reptime, sensitive);
gtk_widget_set_sensitive(data->MI_repbal, sensitive);
gtk_widget_set_sensitive(data->MI_repbudg, sensitive);
gtk_widget_set_sensitive(data->MI_repvehi, sensitive);
gtk_widget_set_sensitive(data->BT_mantpl, sensitive);
gtk_widget_set_sensitive(data->BT_txnshow, sensitive);
gtk_widget_set_sensitive(data->BT_txnadd, sensitive);
gtk_widget_set_sensitive(data->BT_repstat, sensitive);
gtk_widget_set_sensitive(data->BT_reptime, sensitive);
gtk_widget_set_sensitive(data->BT_repbal, sensitive);
gtk_widget_set_sensitive(data->BT_repbudg, sensitive);
gtk_widget_set_sensitive(data->BT_repvehi, sensitive);
// empty category list: disable Budget
DB( g_print(" has cat ?\n") );
sensitive = da_cat_length() > 1 ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_manbud, sensitive);
gtk_widget_set_sensitive(data->MI_manbudtable, sensitive);
gtk_widget_set_sensitive(data->BT_manbud, sensitive);
//#1501129 no need to disable, P & C can be created from assign dialog
//sensitive = ((da_cat_length() > 1) || (da_pay_length() > 1)) ? TRUE : FALSE;
//gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Assign"), sensitive);
gtk_widget_set_sensitive(data->MI_manasg, sensitive);
gtk_widget_set_sensitive(data->BT_manasg, sensitive);
// empty archive list: disable scheduled check
DB( g_print(" has archive ?\n") );
sensitive = da_archive_length() > 0 ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_addscheduled, sensitive);
// no active account: disable Edit, Over
DB( g_print(" account active ?\n") );
sensitive = (active == TRUE ) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->MI_txnshow, sensitive);
gtk_widget_set_sensitive(data->BT_txnshow, sensitive);
}
/* update toolbar, list */
if(flags & UF_VISUAL)
{
DB( g_print(" 8: visual\n") );
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->toolbar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->toolbar), PREFS->toolbar_style-1);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_acc), PREFS->grid_lines);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_upc), PREFS->grid_lines);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
//TODO: should add txn listview here as well
DB( g_print(" - show toolbar=%d\n", PREFS->wal_toolbar) );
hb_widget_visible(data->toolbar, PREFS->wal_toolbar);
DB( g_print(" - show totchart=%d\n", PREFS->wal_totchart) );
hb_widget_visible(data->GR_hubtot, PREFS->wal_totchart);
DB( g_print(" - show timchart=%d\n", PREFS->wal_timchart) );
hb_widget_visible(data->GR_hubtim, PREFS->wal_timchart);
DB( g_print(" - show upcoming=%d\n", PREFS->wal_upcoming) );
hb_widget_visible(data->GR_upc, PREFS->wal_upcoming);
DB( g_print(" minor %d\n", PREFS->euro_active) );
hb_widget_visible(data->MI_eurominor, PREFS->euro_active);
}
if(flags & UF_TXNLIST)
{
DB( g_print(" --txn list\n") );
list_txn_set_columns(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_FUTURE]), PREFS->lst_ope_columns);
list_txn_set_columns(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_REMIND]), PREFS->lst_ope_columns);
}
if(flags & UF_REFRESHALL)
{
DB( g_print(" --refreshall\n") );
ui_hub_account_compute(GLOBALS->mainwindow, NULL);
//5.7 don't process if not visible...
if( PREFS->wal_totchart )
ui_hub_reptotal_populate(GLOBALS->mainwindow, NULL);
if( PREFS->wal_timchart )
ui_hub_reptime_populate(GLOBALS->mainwindow, NULL);
if( PREFS->wal_upcoming )
{
ui_hub_scheduled_populate(GLOBALS->mainwindow, NULL);
ui_hub_transaction_populate(data);
}
}
}
/* hbfile action functions -------------------- */
static void
ui_wallet_action_new(void)
{
GtkWidget *widget = GLOBALS->mainwindow;
if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
{
//clear all, and init GLOBALS->xhb_filepath to default
ui_wallet_clear(widget, GINT_TO_POINTER(TRUE)); // GPOINTER_TO_INT(
ui_wallet_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
ui_newfile_assitant_new();
}
}
static void
ui_wallet_action_open(void)
{
ui_wallet_open(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
}
static void
ui_wallet_action_openbak(void)
{
ui_wallet_open(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
}
static void
ui_wallet_action_save(void)
{
ui_wallet_save(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
}
static void
ui_wallet_action_saveas(void)
{
ui_wallet_save(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
}
static void
ui_wallet_action_revert(void)
{
ui_wallet_revert(GLOBALS->mainwindow, NULL);
}
static void
ui_wallet_action_close(void)
{
GtkWidget *widget = GLOBALS->mainwindow;
if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
{
//clear all, and init GLOBALS->xhb_filepath to default
ui_wallet_clear(widget, GINT_TO_POINTER(TRUE));
ui_wallet_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
}
static void
ui_wallet_action_quit(void)
{
DB( g_print("\n[ui-mainwindow] action quit\n") );
gtk_window_close(GTK_WINDOW(GLOBALS->mainwindow));
}
static void
ui_wallet_action_file_statistics(void)
{
ui_dialog_file_statistics();
}
static void
ui_wallet_action_properties(void)
{
create_defhbfile_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
static void
ui_wallet_action_anonymize(void)
{
gint result;
gchar *title;
gchar *secondtext;
title = _("Are you sure you want to anonymize the file?");
secondtext =
_("Proceeding will anonymize any text, \n"
"like 'account x', 'payee y', 'memo z', ...");
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(GLOBALS->mainwindow),
title,
secondtext,
_("_Anonymize"),
TRUE
);
//#1707201
//if( result == GTK_RESPONSE_CANCEL )
// return;
if( result == GTK_RESPONSE_OK )
{
hbfile_anonymize();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
}
static void
ui_wallet_action_defcurrency(void)
{
ui_cur_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
static void
ui_wallet_action_defaccount(void)
{
ui_acc_manage_dialog();
//our global list has changed, so update the treeview
//todo: optimize this, should not call compute balance here
account_compute_balances (FALSE);
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
static void
ui_wallet_action_defpayee(void)
{
ui_pay_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_defcategory(void)
{
ui_cat_manage_dialog();
//todo:why refresh upcoming here??
//ui_wallet_populate_upcoming(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
//TODO: not ideal to do this
void
ui_wallet_defarchive(Archive *arc)
{
struct hbfile_data *data;
GtkTreeModel *model;
data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
// upcoming list have direct pointer to the arc (which may change during edit)
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
gtk_list_store_clear (GTK_LIST_STORE(model));
ui_arc_manage_dialog(arc);
ui_hub_scheduled_populate(GLOBALS->mainwindow, NULL);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_defarchive(void)
{
ui_wallet_defarchive(NULL);
}
static void
ui_wallet_action_defbudget(void)
{
ui_bud_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_defbudgettable(void)
{
ui_bud_tabview_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_defassign(void)
{
ui_asg_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_deftag(void)
{
ui_tag_manage_dialog();
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
static void
ui_wallet_action_preferences(void)
{
struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
gboolean prv_includeremind;
//#1914935 store previous includeremind state to detect a change
prv_includeremind = PREFS->includeremind;
defpref_dialog_new();
if(!PREFS->euro_active)
{
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(data->MI_eurominor), FALSE);
//gtk_toggle_action_set_active(action, FALSE);
//ui_wallet_action_toggle_minor(GTK_CHECK_MENU_ITEM(data->MI_eurominor), NULL);
}
homebank_pref_apply ();
//#1914935 if include remind change, we update balance
if( prv_includeremind != PREFS->includeremind )
{
account_compute_balances(FALSE);
}
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL+UF_REFRESHALL));
}
/* display action */
static void
ui_wallet_action_toggle_toolbar(GtkCheckMenuItem *menuitem, gpointer user_data)
{
//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
PREFS->wal_toolbar = gtk_check_menu_item_get_active(menuitem);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
}
static void
ui_wallet_action_toggle_upcoming(GtkCheckMenuItem *menuitem, gpointer user_data)
{
//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
gint flags = UF_VISUAL;
PREFS->wal_upcoming = gtk_check_menu_item_get_active(menuitem);
if( PREFS->wal_upcoming == TRUE )
flags |= UF_REFRESHALL;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(flags));
}
static void
ui_wallet_action_toggle_totchart(GtkCheckMenuItem *menuitem, gpointer user_data)
{
//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
gint flags = UF_VISUAL;
PREFS->wal_totchart = gtk_check_menu_item_get_active(menuitem);
if( PREFS->wal_totchart == TRUE )
flags |= UF_REFRESHALL;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(flags));
}
static void
ui_wallet_action_toggle_timchart(GtkCheckMenuItem *menuitem, gpointer user_data)
{
//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
gint flags = UF_VISUAL;
PREFS->wal_timchart = gtk_check_menu_item_get_active(menuitem);
if( PREFS->wal_timchart == TRUE )
flags |= UF_REFRESHALL;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(flags));
}
static void
ui_wallet_action_toggle_minor(GtkCheckMenuItem *menuitem, gpointer user_data)
{
struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
GLOBALS->minor = gtk_check_menu_item_get_active(menuitem);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
// top spending
gtk_chart_show_minor(GTK_CHART(data->RE_hubtot_chart), GLOBALS->minor);
gtk_chart_show_minor(GTK_CHART(data->RE_hubtim_chart), GLOBALS->minor);
ui_hub_reptotal_update(data->window, data);
ui_hub_reptime_update(data->window, data);
}
static void
ui_wallet_action_showalltransactions(GtkMenuItem *menuitem, gpointer user_data)
{
GtkWindow *window;
window = homebank_app_find_window(-1);
if( !window )
{
window = GTK_WINDOW(hub_ledger_window_new(NULL));
hub_ledger_window_init(GTK_WIDGET(window), NULL);
}
else
gtk_window_present(GTK_WINDOW(window));
}
static void
ui_wallet_action_showtransactions(void)
{
struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
GtkWindow *window;
//TODO: change this
if(data->acc == NULL)
return;
window = homebank_app_find_window(data->acc->key);
if( !window )
{
window = GTK_WINDOW(hub_ledger_window_new(data->acc));
hub_ledger_window_init(GTK_WIDGET(window), NULL);
}
else
gtk_window_present(GTK_WINDOW(window));
}
static void
ui_wallet_action_addtransactions(void)
{
ui_wallet_addtransactions(GLOBALS->mainwindow, NULL);
}
static void
ui_wallet_action_checkscheduled(void)
{
ui_hub_scheduled_postall(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
}
static void
ui_wallet_action_statistic(void)
{
repstats_window_new();
}
static void
ui_wallet_action_trendtime(void)
{
struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
reptime_window_new(data->acc != NULL ? data->acc->key : 0);
}
static void
ui_wallet_action_budget(void)
{
repbudget_window_new();
}
static void
ui_wallet_action_balance(void)
{
struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
repbalance_window_new(data->acc != NULL ? data->acc->key : 0);
}
static void
ui_wallet_action_vehiclecost(void)
{
repvehicle_window_new();
}
static void
ui_wallet_action_import(void)
{
/*const gchar *name;
gint filetype = FILETYPE_UNKNOWN;
name = gtk_action_get_name(action);
if( g_str_has_suffix (name, "QIF"))
filetype= FILETYPE_QIF;
else
if( g_str_has_suffix (name, "OFX"))
filetype= FILETYPE_OFX;
else
if( g_str_has_suffix (name, "CSV"))
filetype= FILETYPE_CSV_HB;*/
//DB( g_print("action %s type=%d\n", name, filetype) );
ui_import_assistant_new(NULL);
}
static void
ui_wallet_action_about(void)
{
hbfile_about();
}
static void
ui_wallet_action_export(void)
{
gchar *filename;
if( ui_file_chooser_qif(NULL, &filename) == TRUE )
{
hb_export_qif_account_all(filename);
g_free( filename );
}
}
static void
ui_wallet_action_help(void)
{
gchar *link;
link = g_build_filename("file:///", homebank_app_get_help_dir(), HOMEBANK_URL_HELP, NULL );
homebank_util_url_show (link);
g_free(link);
}
static void
ui_wallet_action_help_releasenotes(void)
{
gchar *link;
#ifdef G_OS_WIN32
link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog.txt", NULL );
#else
link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog", NULL );
#endif
homebank_util_url_show (link);
g_free(link);
}
//todo: move this to a ui-assist-welcome.c
static void
ui_wallet_action_help_welcome_cb (GtkWidget *widget, gpointer user_data)
{
GtkWidget *dialog;
gint response = GPOINTER_TO_INT(user_data);
dialog = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
if( GTK_IS_DIALOG(dialog) )
gtk_dialog_response (GTK_DIALOG(dialog), response);
}
gint
ui_wallet_action_help_welcome(void)
{
GtkWidget *dialog, *content_area;
GtkWidget *mainvbox, *table, *widget, *label, *check;
gchar *pathfilename;
dialog = gtk_dialog_new_with_buttons (_("Welcome to HomeBank"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"),
//_("_Exit"),
GTK_RESPONSE_CLOSE,
NULL);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
table = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
hb_widget_set_margin(table, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (content_area), table);
//get our icon
pathfilename = g_build_filename(homebank_app_get_images_dir(), "homebank-icon.svg", NULL);
if (g_file_test (pathfilename, G_FILE_TEST_EXISTS) == TRUE)
widget = gtk_image_new_from_file((const gchar *)pathfilename);
else
widget = hbtk_image_new_from_icon_name_32 ("homebank");
g_free(pathfilename);
gtk_grid_attach (GTK_GRID (table), widget, 0, 0, 1, 2);
label = make_label (_("HomeBank"), 0, 0);
gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, PANGO_ATTR_SCALE, PANGO_SCALE_XX_LARGE, -1);
gtk_grid_attach (GTK_GRID (table), label, 1, 0, 1, 1);
label = make_label (_("Free, easy, personal accounting for everyone"), 0, 0);
gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_grid_attach (GTK_GRID (table), label, 1, 1, 1, 1);
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_box_prepend (GTK_BOX (content_area), widget);
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (content_area), mainvbox);
gtk_widget_set_halign (mainvbox, GTK_ALIGN_CENTER);
hb_widget_set_margin(mainvbox, SPACING_LARGE);
//label = make_label (_("What do you want to do:"), 0, 0);
//gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
//gtk_box_prepend (GTK_BOX (mainvbox), label);
widget = gtk_button_new_with_mnemonic(_("Open _last opened file"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_OPENLAST));
widget = gtk_button_new_with_mnemonic(_("Create a _new file"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_CREATENEW));
widget = gtk_button_new_with_mnemonic(_("_Open an existing file"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_OPENEXISTING));
widget = gtk_button_new_with_mnemonic(_("Open the _example file"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_OPENSAMPLE));
widget = gtk_button_new_with_mnemonic(_("Read HomeBank _Manual"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_READMANUAL));
widget = gtk_button_new_with_mnemonic(_("Configure _preferences"));
gtk_box_prepend (GTK_BOX (mainvbox), widget);
g_signal_connect (widget, "clicked", G_CALLBACK (ui_wallet_action_help_welcome_cb), GINT_TO_POINTER(HB_WELCOME_CONFIGPREF));
check = gtk_check_button_new_with_mnemonic (_("Show this window next time"));
gtk_box_append (GTK_BOX (mainvbox), check);
//init
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check), PREFS->showwelcome);
gtk_widget_show_all (dialog);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
PREFS->showwelcome = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(check));
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
// do appropriate action
switch(result)
{
case HB_WELCOME_OPENLAST:
{
gchar *rawfilepath = homebank_lastopenedfiles_load();
ui_wallet_open_check(GLOBALS->mainwindow, rawfilepath);
}
break;
case HB_WELCOME_READMANUAL:
ui_wallet_action_help();
break;
case HB_WELCOME_CONFIGPREF:
ui_wallet_action_preferences();
break;
case HB_WELCOME_CREATENEW:
ui_wallet_action_new();
break;
case HB_WELCOME_OPENEXISTING:
ui_wallet_action_open();
break;
case HB_WELCOME_OPENSAMPLE:
hbfile_change_filepath(g_build_filename(homebank_app_get_datas_dir(), "example.xhb", NULL));
ui_wallet_open_internal(GLOBALS->mainwindow, NULL);
break;
}
return result;
}
static void
ui_wallet_action_help_updates(void)
{
const gchar *link = HOMEBANK_URL_HELP_UPDATES;
homebank_util_url_show (link);
}
static void
ui_wallet_action_help_online(void)
{
const gchar *link = HOMEBANK_URL_HELP_ONLINE;
homebank_util_url_show (link);
}
static void
ui_wallet_action_help_donate(void)
{
const gchar *link = HOMEBANK_URL_HELP_DONATE;
homebank_util_url_show (link);
}
static void
ui_wallet_action_help_translate(void)
{
const gchar *link = HOMEBANK_URL_HELP_TRANSLATE;
homebank_util_url_show (link);
}
static void
ui_wallet_action_help_problem(void)
{
const gchar *link = HOMEBANK_URL_HELP_PROBLEM;
homebank_util_url_show (link);
}
static void
ui_wallet_onRowActivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print ("\n[ui-mainwindow] A row has been double-clicked!\n") );
model = gtk_tree_view_get_model(treeview);
if (gtk_tree_model_get_iter(model, &iter, path))
{
Account *acc;
gint dt;
gtk_tree_model_get(model, &iter, LST_DSPACC_DATAS, &acc, LST_DSPACC_DATATYPE, &dt, -1);
if( (dt == DSPACC_TYPE_NORMAL) && (acc != NULL) )
{
DB( g_print ("Double-clicked row contains name %s\n", acc->name) );
ui_wallet_action_showtransactions();
//g_free(name);
}
}
}
static gboolean
ui_wallet_window_focus (GtkWidget *widget, GdkEvent event, gpointer user_data)
{
DB( g_print("\n[ui-mainwindow] focus-in\n") );
//#2111786 detect day change
if( homebank_app_date_get_julian() == TRUE )
{
DB( g_print(" --day change > recompute\n") );
account_compute_balances (FALSE);
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_REFRESHALL));
}
//return TRUE;//stop
return FALSE;//propagate
}
//#2060159 store after every move
static gboolean
ui_wallet_window_getgeometry(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
//struct hbfile_data *data = user_data;
struct WinGeometry *wg;
//DB( g_print("\n[ui-mainwindow] get geometry\n") );
//store position and size
wg = &PREFS->wal_wg;
wg->s = gtk_window_is_maximized(GTK_WINDOW(widget));
if(!wg->s)
{
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
}
/*
gint lastw, lasth;
gint neww, newh;
lastw = wg->w;
lasth = wg->h;
gtk_window_get_size(GTK_WINDOW(widget), &neww, &newh);
if( lastw != neww || lasth != newh )
{
gint ppos;
gdouble rate;
//DB( g_print(" size changed\n") );
//width
ppos = gtk_paned_get_position(GTK_PANED(data->hpaned));
rate = (gdouble)ppos / (gdouble)lastw;
gtk_paned_set_position(GTK_PANED(data->hpaned), (rate * neww) );
//height
ppos = gtk_paned_get_position(GTK_PANED(data->vpaned));
rate = (gdouble)ppos / (gdouble)lasth;
gtk_paned_set_position(GTK_PANED(data->vpaned), (rate * newh) );
}*/
//DB( g_print(" window: l=%d, t=%d, w=%d, h=%d s=%d\n", wg->l, wg->t, wg->w, wg->h, wg->s) );
return FALSE;
}
static void
ui_wallet_window_destroy(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
DB( g_print("\n[ui-mainwindow] -- destroy\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print(" destroy hubtot\n") );
//ui_hub_xxx_dispose(data);
gtk_widget_destroy(data->LV_hubtot);
ui_hub_reptotal_dispose(data);
DB( g_print(" destroy hubtim\n") );
gtk_widget_destroy(data->LV_hubtim);
ui_hub_reptime_dispose(data);
DB( g_print(" destroy hubacc\n") );
ui_hub_account_dispose(data);
DB( g_print(" destroy free data\n") );
g_free(data->wintitle);
g_free(user_data);
DB( g_print(" gtk_main_quit\n") );
gtk_main_quit();
}
static gboolean
ui_wallet_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct hbfile_data *data;
struct WinGeometry *wg;
gboolean retval = FALSE;
DB( g_print("\n[ui-mainwindow] -- delete-event\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
ui_wallet_close_openbooks();
//store position and size
wg = &PREFS->wal_wg;
wg->s = gtk_window_is_maximized(GTK_WINDOW(widget));
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)
{
retval = TRUE;
}
DB( g_print("retval: %d\n", retval) );
//TRUE:stop other handlers from being invoked for the event | FALSE: propagate
return retval;
}
enum
{
TARGET_URI_LIST
};
static GtkTargetEntry drop_types[] =
{
{"text/uri-list", 0, TARGET_URI_LIST}
};
static void
ui_wallet_window_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x, gint y,
GtkSelectionData *selection_data,
guint info, guint time, GtkWindow *window)
{
gchar **uris, **str;
gchar *newseldata;
gint n_uris, filetype, slen;
GError *error = NULL;
if (info != TARGET_URI_LIST)
return;
DB( g_print("\n[ui-mainwindow] drag_data_received\n") );
/* On MS-Windows, it looks like `selection_data->data' is not NULL terminated. */
slen = gtk_selection_data_get_length(selection_data);
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 ) );
g_free(newseldata);
//single file: check for xhb
if(n_uris == 1)
{
filetype = hb_filename_type_get_by_extension(*uris);
DB( g_print(" - filetype is homebank (%d)\n", filetype) );
if( filetype == FILETYPE_HOMEBANK )
{
gchar *path = g_filename_from_uri (*uris, NULL, &error);
if( path != NULL )
{
DB( g_print(" - path is '%s'\n", path) );
hbfile_change_filepath(g_strdup(path));
ui_wallet_open_internal(GTK_WIDGET(window), NULL);
goto end_drop;
}
else
{
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);
*/
}
//collect known filetype to import
DB( g_print(" - collect %d files\n", n_uris) );
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 )
{
gchar *path = g_filename_from_uri (*str, NULL, NULL);
if( path != NULL )
{
DB( g_print(" - append %d '%s'\n", slen, path ) );
paths[slen++] = path;
}
}
}
paths[slen] = NULL;
if( slen > 0 )
{
ui_import_assistant_new( paths );
}
end_drop:
g_strfreev (uris);
}
static GtkWidget *
ui_wallet_create_recent_chooser_menu (struct hbfile_data *data)
{
GtkWidget *recent_menu, *menuitem;
GtkRecentFilter *filter;
recent_menu = gtk_recent_chooser_menu_new_for_manager (data->recent_manager);
gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (recent_menu), FALSE);
gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU);
//todo: add a user pref for this
gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER (recent_menu), 10);
gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (recent_menu), FALSE);
//gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (recent_menu), TRUE);
filter = gtk_recent_filter_new ();
//gtk_recent_filter_add_application (filter, g_get_application_name());
gtk_recent_filter_add_pattern (filter, "*.[Xx][Hh][Bb]");
gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter);
gtk_menu_shell_append (GTK_MENU_SHELL (recent_menu), gtk_separator_menu_item_new());
menuitem = hbtk_menu_add_menuitem(recent_menu, _("Clear") );
g_signal_connect (recent_menu, "item-activated", G_CALLBACK (ui_wallet_recent_chooser_item_activated_cb), data);
g_signal_connect (menuitem, "activate", G_CALLBACK (ui_wallet_cb_recent_chooser_clear), (gpointer)data);
return recent_menu;
}
static GtkWidget *
ui_wallet_menubar_create(struct hbfile_data *data)
{
GtkWidget *menubar;
GtkWidget *menu, *submenu;
GtkWidget *menuitem;
GtkAccelGroup *accel_group = NULL;
menubar = gtk_menu_bar_new ();
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(data->window), accel_group);
menu = hbtk_menubar_add_menu(menubar, _("_File"), &data->ME_menufile);
data->MI_new = menuitem = hbtk_menu_add_menuitem(menu, _("_New"));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_n, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
data->MI_open = menuitem = hbtk_menu_add_menuitem(menu, _("_Open..."));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_o, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
menuitem = hbtk_menu_add_menuitem(menu, _("Open _Recent"));
submenu = ui_wallet_create_recent_chooser_menu (data);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_save = menuitem = hbtk_menu_add_menuitem(menu, _("_Save"));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_s, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
data->MI_saveas = menuitem = hbtk_menu_add_menuitem(menu, _("Save _As..."));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_s, GDK_SHIFT_MASK|GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_import = menuitem = hbtk_menu_add_menuitem(menu, _("Import..."));
//#2086475
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_i, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
data->MI_exportqif = hbtk_menu_add_menuitem(menu, _("Export as QIF..."));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_revert = hbtk_menu_add_menuitem(menu, _("Revert"));
data->MI_openbak = hbtk_menu_add_menuitem(menu, _("Restore backup"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_properties = hbtk_menu_add_menuitem(menu, _("Properties..."));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_close = menuitem = hbtk_menu_add_menuitem(menu, _("_Close"));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_w, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
data->MI_quit = menuitem = hbtk_menu_add_menuitem(menu, _("_Quit"));
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
menu = hbtk_menubar_add_menu(menubar, _("_Edit"), &data->ME_menuedit);
data->MI_prefs = hbtk_menu_add_menuitem(menu, _("Preferences..."));
menu = hbtk_menubar_add_menu(menubar, _("_View"), &data->ME_menuview);
data->MI_showtbar = menuitem = gtk_check_menu_item_new_with_mnemonic(_("_Toolbar"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
data->MI_showtotchart = menuitem = gtk_check_menu_item_new_with_mnemonic(_("T_otal Chart"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
data->MI_showtimchart = menuitem = gtk_check_menu_item_new_with_mnemonic(_("T_ime Chart"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
data->MI_showbotlist = menuitem = gtk_check_menu_item_new_with_mnemonic(_("_Bottom Lists"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_eurominor = menuitem = gtk_check_menu_item_new_with_mnemonic(_("Euro minor"));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_m, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
menu = hbtk_menubar_add_menu(menubar, _("_Manage"), &data->ME_menuman);
data->MI_manwal = hbtk_menu_add_menuitem(menu, _("Wallet..."));
data->MI_manacc = hbtk_menu_add_menuitem(menu, _("Acc_ounts..."));
data->MI_manpay = hbtk_menu_add_menuitem(menu, _("_Payees..."));
data->MI_mancat = hbtk_menu_add_menuitem(menu, _("Categories..."));
data->MI_mantag = hbtk_menu_add_menuitem(menu, _("Tags..."));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_mantpl = hbtk_menu_add_menuitem(menu, _("Scheduled/Template..."));
data->MI_manasg = hbtk_menu_add_menuitem(menu, _("Assignments..."));
data->MI_mancur = hbtk_menu_add_menuitem(menu, _("Currencies..."));
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_manbud = hbtk_menu_add_menuitem(menu, _("Budget..."));
data->MI_manbudtable = hbtk_menu_add_menuitem(menu, _("Budget (table view)..."));
menu = hbtk_menubar_add_menu(menubar, _("_Transactions"), &data->ME_menutxn);
data->MI_txnadd = hbtk_menu_add_menuitem(menu, _("Add..."));
data->MI_txnshow = hbtk_menu_add_menuitem(menu, _("Show..."));
data->MI_txnshowall = menuitem = hbtk_menu_add_menuitem(menu, _("Show All...") );
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_scheduler = hbtk_menu_add_menuitem(menu, _("Set scheduler..."));
data->MI_addscheduled = hbtk_menu_add_menuitem(menu, _("Post scheduled"));
menu = hbtk_menubar_add_menu(menubar, _("_Reports"), &data->ME_menurep);
data->MI_repstat = hbtk_menu_add_menuitem(menu, _("_Statistics..."));
data->MI_reptime = hbtk_menu_add_menuitem(menu, _("_Trend Time...") );
data->MI_repbal = hbtk_menu_add_menuitem(menu, _("_Balance...") );
data->MI_repbudg = hbtk_menu_add_menuitem(menu, _("B_udget..."));
data->MI_repvehi = hbtk_menu_add_menuitem(menu, _("_Vehicle cost..."));
menu = hbtk_menubar_add_menu(menubar, _("_Tools"), &data->ME_menutool);
data->MI_welcome = hbtk_menu_add_menuitem(menu, _("Show welcome dialog..."));
data->MI_filestats = hbtk_menu_add_menuitem(menu, _("File statistics...") );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_anonymise = hbtk_menu_add_menuitem(menu, _("Anonymize..."));
menu = hbtk_menubar_add_menu(menubar, _("_Help"), &data->ME_menuhelp);
data->MI_contents = menuitem = hbtk_menu_add_menuitem(menu, _("_Contents") );
gtk_widget_add_accelerator(menuitem, "activate", accel_group, GDK_KEY_F1, 0, GTK_ACCEL_VISIBLE);
data->MI_online = hbtk_menu_add_menuitem(menu, _("Get Help Online...") );
//donate also here ?
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_updates = hbtk_menu_add_menuitem(menu, _("Check for updates...") );
data->MI_relnote = hbtk_menu_add_menuitem(menu, _("Release Notes") );
data->MI_problem = hbtk_menu_add_menuitem(menu, _("Report a Problem...") );
data->MI_translate = hbtk_menu_add_menuitem(menu, _("Translate this Application...") );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
data->MI_about = hbtk_menu_add_menuitem(menu, _("_About") );
return menubar;
}
static GtkWidget *
ui_wallet_toolbar_create(struct hbfile_data *data)
{
GtkWidget *toolbar, *button, *menu;
toolbar = gtk_toolbar_new();
data->BT_new = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_FILE_NEW, _("New"), _("Create a new file"));
data->BT_open = button = gtk_widget_new(GTK_TYPE_MENU_TOOL_BUTTON,
"icon-name", ICONNAME_HB_FILE_OPEN,
"label", _("Open"),
"tooltip-text", _("Open a file"),
NULL);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM(button), 1);
menu = ui_wallet_create_recent_chooser_menu(data);
gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button), menu);
gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (button), _("Open a recently used file"));
data->BT_save = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_FILE_SAVE, _("Save"), _("Save the current file"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
data->BT_manacc = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_ACCOUNT , _("Account"), _("Manage the accounts"));
data->BT_manpay = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_PAYEE , _("Payee"), _("Manage the payees"));
data->BT_mancat = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_CATEGORY, _("Category"), _("Manage the categories"));
data->BT_mantpl = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_ARCHIVE , _("Scheduled/Template"), _("Manage the scheduled/template transactions"));
data->BT_manbud = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_BUDGET , _("Budget"), _("Manage the budget"));
data->BT_manasg = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_ASSIGN , _("Assignment"), _("Manage the automatic assignments"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
data->BT_txnshow = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_OPE_SHOW, _("Show"), _("Shows selected account transactions"));
data->BT_txnadd = button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_OPE_ADD, _("Add"), _("Add transactions"));
g_object_set(button, "is-important", TRUE, NULL);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
data->BT_repstat = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REP_STATS , _("Statistics"), _("Open the Statistics report"));
data->BT_reptime = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REP_TIME , _("Trend time"), _("Open the Trend Time report"));
data->BT_repbal = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REP_BALANCE, _("Balance"), _("Open the Balance report"));
data->BT_repbudg = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REP_BUDGET, _("Budget"), _("Open the Budget report"));
data->BT_repvehi = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REP_CAR , _("Vehicle cost"), _("Open the Vehicle cost report"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1);
data->BT_help = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_HELP , _("Help"), _("Get Help Online..."));
data->BT_donate = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_DONATE, _("Donate"), _("Donate to HomeBank project"));
return toolbar;
}
GtkWidget *
ui_wallet_window_new(GtkWidget *do_widget)
{
struct hbfile_data *data;
struct WinGeometry *wg;
GtkWidget *mainvbox, *vbox, *box, *vpaned, *hpaned, *sidebar, *stack;
GtkWidget *widget, *page, *menubar, *toolbar;
GtkWidget *window;
DB( g_print("\n[ui-mainwindow] create main window\n") );
data = g_malloc0(sizeof(struct hbfile_data));
if(!data) return NULL;
window = gtk_application_window_new(GLOBALS->application);
gtk_widget_set_name(GTK_WIDGET(window), "homebank");
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
// this is our mainwindow, so store it to GLOBALS data
data->window = window;
GLOBALS->mainwindow = window;
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child(GTK_WINDOW(window), mainvbox);
//menu and toolbar
data->recent_manager = gtk_recent_manager_get_default ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (mainvbox), box);
menubar = ui_wallet_menubar_create(data);
hbtk_box_prepend (GTK_BOX (box), menubar);
#if HB_UNSTABLE_SHOW == TRUE
menubar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(menubar)), GTK_STYLE_CLASS_MENUBAR);
gtk_box_prepend (GTK_BOX (box), menubar);
widget = gtk_label_new("Release Candidate Version ");
gimp_label_set_attributes (GTK_LABEL (widget),
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
-1);
gtk_box_append (GTK_BOX (menubar), widget);
widget = hbtk_image_new_from_icon_name_16 (ICONNAME_WARNING);
gtk_box_append (GTK_BOX (menubar), widget);
gtk_widget_set_tooltip_markup(GTK_WIDGET(menubar),
"Guidelines:\n"
"- use only for TESTING or find BUGs\n"
"- use A COPY of your data files\n"
"- BUGs or CRASH can still occur"
);
#endif
#if MYDEBUG == 1
widget = make_label(NULL, 1.0, 0.5);
gtk_widget_set_margin_end(widget, 12);
data->dbgchange = widget;
gtk_box_append (GTK_BOX (box), widget);
#endif
toolbar = ui_wallet_toolbar_create(data);
data->toolbar = toolbar;
gtk_box_prepend (GTK_BOX (mainvbox), toolbar);
/* Add the main area */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (mainvbox), vbox);
vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
data->vpaned = vpaned;
hbtk_box_prepend (GTK_BOX (vbox), vpaned);
// top part
hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
data->hpaned = hpaned;
gtk_paned_pack1 (GTK_PANED(vpaned), hpaned, FALSE, FALSE);
widget = ui_hub_account_create(data);
//gtk_widget_set_size_request (widget, 100, -1);
gtk_paned_pack1 (GTK_PANED(hpaned), widget, FALSE, FALSE);
//5.7 add home time chart
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(box), TRUE);
gtk_paned_pack2 (GTK_PANED(hpaned), box, TRUE, FALSE);
widget = ui_hub_reptotal_create(data);
//gtk_widget_set_size_request (widget, -1, 100);
hbtk_box_prepend (GTK_BOX (box), widget);
widget = ui_hub_reptime_create(data);
hbtk_box_prepend (GTK_BOX (box), widget);
// bottom part
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_prepend (GTK_BOX (box), sidebar);
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;
hbtk_box_prepend (GTK_BOX (box), stack);
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;
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d s=%d\n", wg->l, wg->t, wg->w, wg->h, wg->s) );
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
if(wg->s == 1)
gtk_window_maximize(GTK_WINDOW(window));
gtk_widget_show_all (window);
//#2035401 hide necessary widget to enable paned correct size
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
//#1662197/1660910 moved after resize/show
DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
if(PREFS->wal_hpaned > 0)
gtk_paned_set_position(GTK_PANED(data->hpaned), PREFS->wal_hpaned);
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") );
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(data->MI_showtbar), PREFS->wal_toolbar);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(data->MI_showtotchart), PREFS->wal_totchart);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(data->MI_showtimchart), PREFS->wal_timchart);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(data->MI_showbotlist), PREFS->wal_upcoming);
/* Drag and drop support, set targets to NULL because we add the
default uri_targets below */
/* support for opening a file by dragging onto the project window */
gtk_drag_dest_set (GTK_WIDGET (window),
GTK_DEST_DEFAULT_ALL,
drop_types,
G_N_ELEMENTS (drop_types),
GDK_ACTION_COPY);
g_signal_connect (G_OBJECT (window), "drag-data-received",
G_CALLBACK (ui_wallet_window_drag_data_received), window);
//connect all our signals
DB( g_print(" - connect signals\n") );
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), "changed", G_CALLBACK (ui_wallet_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_acc ), "row-activated", G_CALLBACK (ui_wallet_onRowActivated), GINT_TO_POINTER(2));
/* GtkWindow events */
g_signal_connect (window, "destroy", G_CALLBACK (ui_wallet_window_destroy), NULL);
g_signal_connect (window, "delete-event", G_CALLBACK (ui_wallet_window_dispose), (gpointer)data);
g_signal_connect (window, "configure-event", G_CALLBACK (ui_wallet_window_getgeometry), (gpointer)data);
g_signal_connect (window, "focus-in-event", G_CALLBACK (ui_wallet_window_focus), NULL);
//menu signals
g_signal_connect (data->MI_new , "activate", G_CALLBACK (ui_wallet_action_new), (gpointer)data);
g_signal_connect (data->MI_open , "activate", G_CALLBACK (ui_wallet_action_open), (gpointer)data);
g_signal_connect (data->MI_save , "activate", G_CALLBACK (ui_wallet_action_save), (gpointer)data);
g_signal_connect (data->MI_saveas , "activate", G_CALLBACK (ui_wallet_action_saveas), (gpointer)data);
g_signal_connect (data->MI_import , "activate", G_CALLBACK (ui_wallet_action_import), (gpointer)data);
g_signal_connect (data->MI_exportqif , "activate", G_CALLBACK (ui_wallet_action_export), (gpointer)data);
g_signal_connect (data->MI_revert , "activate", G_CALLBACK (ui_wallet_action_revert), (gpointer)data);
g_signal_connect (data->MI_openbak , "activate", G_CALLBACK (ui_wallet_action_openbak), (gpointer)data);
g_signal_connect (data->MI_properties , "activate", G_CALLBACK (ui_wallet_action_properties), (gpointer)data);
g_signal_connect (data->MI_close , "activate", G_CALLBACK (ui_wallet_action_close), (gpointer)data);
g_signal_connect (data->MI_quit , "activate", G_CALLBACK (ui_wallet_action_quit), (gpointer)data);
g_signal_connect (data->MI_prefs , "activate", G_CALLBACK (ui_wallet_action_preferences), (gpointer)data);
g_signal_connect (data->MI_showtbar , "toggled", G_CALLBACK (ui_wallet_action_toggle_toolbar), (gpointer)data);
g_signal_connect (data->MI_showtotchart , "toggled", G_CALLBACK (ui_wallet_action_toggle_totchart), (gpointer)data);
g_signal_connect (data->MI_showtimchart , "toggled", G_CALLBACK (ui_wallet_action_toggle_timchart), (gpointer)data);
g_signal_connect (data->MI_showbotlist , "toggled", G_CALLBACK (ui_wallet_action_toggle_upcoming), (gpointer)data);
g_signal_connect (data->MI_eurominor , "toggled", G_CALLBACK (ui_wallet_action_toggle_minor), (gpointer)data);
g_signal_connect (data->MI_manwal , "activate", G_CALLBACK (ui_wallet_action_properties), (gpointer)data);
g_signal_connect (data->MI_manacc , "activate", G_CALLBACK (ui_wallet_action_defaccount), (gpointer)data);
g_signal_connect (data->MI_manpay , "activate", G_CALLBACK (ui_wallet_action_defpayee), (gpointer)data);
g_signal_connect (data->MI_mancat , "activate", G_CALLBACK (ui_wallet_action_defcategory), (gpointer)data);
g_signal_connect (data->MI_mantpl , "activate", G_CALLBACK (ui_wallet_action_defarchive), (gpointer)data);
g_signal_connect (data->MI_manbud , "activate", G_CALLBACK (ui_wallet_action_defbudget), (gpointer)data);
g_signal_connect (data->MI_manbudtable , "activate", G_CALLBACK (ui_wallet_action_defbudgettable), (gpointer)data);
g_signal_connect (data->MI_manasg , "activate", G_CALLBACK (ui_wallet_action_defassign), (gpointer)data);
g_signal_connect (data->MI_mancur , "activate", G_CALLBACK (ui_wallet_action_defcurrency), (gpointer)data);
g_signal_connect (data->MI_mantag , "activate", G_CALLBACK (ui_wallet_action_deftag), (gpointer)data);
g_signal_connect (data->MI_txnadd , "activate", G_CALLBACK (ui_wallet_action_addtransactions), (gpointer)data);
g_signal_connect (data->MI_txnshow , "activate", G_CALLBACK (ui_wallet_action_showtransactions), (gpointer)data);
g_signal_connect (data->MI_txnshowall , "activate", G_CALLBACK (ui_wallet_action_showalltransactions), (gpointer)data);
g_signal_connect (data->MI_scheduler , "activate", G_CALLBACK (ui_wallet_action_properties), (gpointer)data);
g_signal_connect (data->MI_addscheduled , "activate", G_CALLBACK (ui_wallet_action_checkscheduled), (gpointer)data);
g_signal_connect (data->MI_repstat , "activate", G_CALLBACK (ui_wallet_action_statistic), (gpointer)data);
g_signal_connect (data->MI_reptime , "activate", G_CALLBACK (ui_wallet_action_trendtime), (gpointer)data);
g_signal_connect (data->MI_repbal , "activate", G_CALLBACK (ui_wallet_action_balance), (gpointer)data);
g_signal_connect (data->MI_repbudg , "activate", G_CALLBACK (ui_wallet_action_budget), (gpointer)data);
g_signal_connect (data->MI_repvehi , "activate", G_CALLBACK (ui_wallet_action_vehiclecost), (gpointer)data);
g_signal_connect (data->MI_welcome , "activate", G_CALLBACK (ui_wallet_action_help_welcome), (gpointer)data);
g_signal_connect (data->MI_filestats , "activate", G_CALLBACK (ui_wallet_action_file_statistics), (gpointer)data);
g_signal_connect (data->MI_anonymise , "activate", G_CALLBACK (ui_wallet_action_anonymize), (gpointer)data);
g_signal_connect (data->MI_contents , "activate", G_CALLBACK (ui_wallet_action_help), (gpointer)data);
g_signal_connect (data->MI_online , "activate", G_CALLBACK (ui_wallet_action_help_online), (gpointer)data);
g_signal_connect (data->MI_updates , "activate", G_CALLBACK (ui_wallet_action_help_updates), (gpointer)data);
g_signal_connect (data->MI_relnote , "activate", G_CALLBACK (ui_wallet_action_help_releasenotes), (gpointer)data);
g_signal_connect (data->MI_problem , "activate", G_CALLBACK (ui_wallet_action_help_problem), (gpointer)data);
g_signal_connect (data->MI_translate , "activate", G_CALLBACK (ui_wallet_action_help_translate), (gpointer)data);
g_signal_connect (data->MI_about , "activate", G_CALLBACK (ui_wallet_action_about), (gpointer)data);
//toolbar signals
g_signal_connect (G_OBJECT (data->BT_new ), "clicked", G_CALLBACK (ui_wallet_action_new), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_open ), "clicked", G_CALLBACK (ui_wallet_action_open), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_save ), "clicked", G_CALLBACK (ui_wallet_action_save), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_manacc), "clicked", G_CALLBACK (ui_wallet_action_defaccount), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_manpay), "clicked", G_CALLBACK (ui_wallet_action_defpayee), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_mancat), "clicked", G_CALLBACK (ui_wallet_action_defcategory), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_mantpl), "clicked", G_CALLBACK (ui_wallet_action_defarchive), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_manbud), "clicked", G_CALLBACK (ui_wallet_action_defbudgettable), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_manasg), "clicked", G_CALLBACK (ui_wallet_action_defassign), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_txnshow), "clicked", G_CALLBACK (ui_wallet_action_showtransactions), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_txnadd), "clicked", G_CALLBACK (ui_wallet_action_addtransactions), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_repstat), "clicked", G_CALLBACK (ui_wallet_action_statistic), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_reptime), "clicked", G_CALLBACK (ui_wallet_action_trendtime), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_repbal ), "clicked", G_CALLBACK (ui_wallet_action_balance), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_repbudg), "clicked", G_CALLBACK (ui_wallet_action_budget), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_repvehi), "clicked", G_CALLBACK (ui_wallet_action_vehiclecost), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_help ), "clicked", G_CALLBACK (ui_wallet_action_help_online), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_donate), "clicked", G_CALLBACK (ui_wallet_action_help_donate), (gpointer)data);
return window;
}
homebank-5.9.7/src/hub-reptime.h 0000664 0001750 0001750 00000003056 14736461415 016014 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HUB_REPTIME_H__
#define __HUB_REPTIME_H__
// /!\ prefs stored value
enum {
HUB_TIM_VIEW_NONE,
HUB_TIM_VIEW_SPENDING,
HUB_TIM_VIEW_ACCBALANCE,
HUB_TIM_VIEW_ALLBALANCE,
//5.7.3
HUB_TIM_VIEW_GRPBALANCE,
//5.7.5
HUB_TIM_VIEW_REVENUE,
HUB_TIM_VIEW_SPEREV,
};
/* list top spending */
enum
{
LST_REPTIME_POS,
LST_REPTIME_KEY,
LST_REPTIME_LABEL,
LST_REPTIME_ROW,
NUM_LST_REPTIME
};
void ui_hub_reptime_update(GtkWidget *widget, gpointer user_data);
void ui_hub_reptime_clear(GtkWidget *widget, gpointer user_data);
void ui_hub_reptime_populate(GtkWidget *widget, gpointer user_data);
void ui_hub_reptime_setup(struct hbfile_data *data);
void ui_hub_reptime_dispose(struct hbfile_data *data);
GtkWidget *ui_hub_reptime_create(struct hbfile_data *data);
#endif
homebank-5.9.7/src/ui-transaction.h 0000644 0001750 0001750 00000005475 15026021254 016522 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_TRANSACTION_GTK_H__
#define __HB_TRANSACTION_GTK_H__
#include "hb-transaction.h"
#include "ui-txn-split.h"
/* official GTK_RESPONSE are negative */
#define HB_RESPONSE_ADD 1
#define HB_RESPONSE_ADDKEEP 2
enum {
HID_AMOUNT,
MAX_HID_AMOUNT
};
struct deftransaction_data
{
GtkWidget *dialog;
GtkWidget *RA_type;
GtkWidget *LB_date, *GR_date;
GtkWidget *PO_date; //5.7 removed *LB_wday;
GtkWidget *LB_dateto, *PO_dateto;
GtkWidget *PO_pay;
GtkWidget *ST_memo;
GtkWidget *ST_amount, *BT_split, *LB_curr;
GtkWidget *ST_xferamt, *LB_xfercurr, *IM_xfernorate;
GtkWidget *CM_cheque;
GtkWidget *LB_mode, *NU_mode;
GtkWidget *ST_number;
GtkWidget *PO_cat;
GtkWidget *LB_accfrom, *PO_acc;
GtkWidget *LB_accto, *PO_accto;
GtkWidget *ST_tags, *CY_tags;
//GtkWidget *CY_status;
GtkWidget *RA_status;
GtkWidget *CM_remind;
GtkWidget *IB_warnsign, *LB_warnsign;
GtkWidget *LB_msgadded;
/* popover */
GtkWidget *MB_template;
GtkTreeModel *model;
GtkTreeModelFilter *modelfilter;
GtkWidget *LV_arc;
GtkWidget *CM_showsched;
GtkWidget *CM_showallacc;
GtkWidget *ST_search;
HbTxnDlgAction action;
HbTxnDlgType type;
guint evtsrcid;
gint accnum;
guint32 kacc;
gboolean showtemplate;
gint txnoldtype;
Transaction *ope;
};
enum
{
LST_DSPTPL_DATAS,
LST_DSPTPL_NAME,
NUM_LST_DSPTPL
};
GtkWidget *create_deftransaction_window (GtkWindow *parent, HbTxnDlgAction action, HbTxnDlgType type, guint32 kacc);
void deftransaction_set_amount(GtkWidget *widget, gdouble amount);
void deftransaction_external_confirm(GtkWidget *dialog, Transaction *ope);
gint deftransaction_external_edit(GtkWindow *parent, Transaction *old_txn, Transaction *new_txn);
void deftransaction_set_transaction(GtkWidget *widget, Transaction *ope);
void deftransaction_get (GtkWidget *widget, gpointer user_data);
void deftransaction_add (GtkWidget *widget, gpointer user_data);
void deftransaction_dispose(GtkWidget *widget, gpointer user_data);
void deftransaction_set_amount_from_split(GtkWidget *widget, gdouble amount);
#endif
homebank-5.9.7/src/hb-import.c 0000644 0001750 0001750 00000105164 15032170442 015453 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-import.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = */
static HbFileType
_str_search_marker(gchar *rawtext, gsize length)
{
if( g_str_has_prefix(rawtext, "!") )
{
if( g_str_has_prefix(rawtext, "!Type")
|| g_str_has_prefix(rawtext, "!Account")
|| g_str_has_prefix(rawtext, "!Option")
) return FILETYPE_QIF;
if( g_str_has_prefix(rawtext, "!type")
|| g_str_has_prefix(rawtext, "!account")
|| g_str_has_prefix(rawtext, "!option")
) return FILETYPE_QIF;
}
if( hb_csv_test_line(rawtext) )
return FILETYPE_CSV_HB;
if( g_strstr_len(rawtext, -1, "") != NULL )
return FILETYPE_OFX;
if( g_strstr_len(rawtext, -1, "") != NULL )
return FILETYPE_OFX;
if( g_str_has_prefix(rawtext, " 48
while( retval == FILETYPE_UNKNOWN && n_line <= 48 )
{
io_stat = g_io_channel_read_line(io, &tmpstr, &length, &eol_pos, &err);
if( io_stat == G_IO_STATUS_NORMAL)
{
n_line++;
#if MYDEBUG
//5.9.2 fast remove eol
if( length > 0 && eol_pos <= length )
tmpstr[eol_pos] = 0;
g_print(" line %d: ->|%s|<- %ld %ld\n", n_line, tmpstr, length, eol_pos);
#endif
retval = _str_search_marker(tmpstr, length);
g_free(tmpstr);
}
else
{
if( io_stat == G_IO_STATUS_EOF )
{
DB( g_print(" eof reached\n") );
break;
}
if( io_stat == G_IO_STATUS_ERROR )
{
DB (g_print(" + ERROR %s\n", err->message));
g_error_free(err);
err=NULL;
break;
}
}
}
g_io_channel_unref (io);
}
#if MYDEBUG
gchar *label[NUM_FILETYPE]={"???","hb","ofx","qif","csv"};
g_print(" > type is %s", label[retval]);
#endif
return retval;
}
static void
da_import_context_gen_txn_destroy(ImportContext *context)
{
GList *list;
DB( g_print("\n[import] free gen txn list\n") );
list = g_list_first(context->gen_lst_txn);
while (list != NULL)
{
GenTxn *gentxn = list->data;
da_gen_txn_free(gentxn);
list = g_list_next(list);
}
g_list_free(context->gen_lst_txn);
context->gen_lst_txn = NULL;
}
static void
da_import_context_gen_acc_destroy(ImportContext *context)
{
GList *list;
DB( g_print("\n[import] free gen acc list\n") );
list = g_list_first(context->gen_lst_acc);
while (list != NULL)
{
GenAcc *genacc = list->data;
da_gen_acc_free(genacc);
list = g_list_next(list);
}
g_list_free(context->gen_lst_acc);
context->gen_lst_acc = NULL;
}
static void
da_import_context_clear(ImportContext *context)
{
DB( g_print("\n[import] context clear\n") );
da_import_context_gen_txn_destroy(context);
da_import_context_gen_acc_destroy(context);
context->gen_next_acckey = 1;
}
void
da_import_context_destroy(ImportContext *context)
{
GList *list;
DB( g_print("\n[import] context destroy\n") );
da_import_context_gen_txn_destroy(context);
da_import_context_gen_acc_destroy(context);
DB( g_print(" free gen file list\n") );
list = g_list_first(context->gen_lst_file);
while (list != NULL)
{
GenFile *genfile = list->data;
da_gen_file_free(genfile);
list = g_list_next(list);
}
g_list_free(context->gen_lst_file);
context->gen_lst_file = NULL;
}
void
da_import_context_new(ImportContext *context)
{
context->gen_lst_file = NULL;
context->gen_lst_acc = NULL;
context->gen_lst_txn = NULL;
context->gen_next_acckey = 1;
}
/* = = = = = = = = = = = = = = = = */
GenFile *
da_gen_file_malloc(void)
{
return g_malloc0(sizeof(GenFile));
}
void
da_gen_file_free(GenFile *genfile)
{
if(genfile != NULL)
{
if(genfile->filepath != NULL)
g_free(genfile->filepath);
//#2111468 add error log
if(genfile->errlog != NULL)
g_free(genfile->errlog);
g_free(genfile);
}
}
GenFile *
da_gen_file_get(GList *lst_file, guint32 key)
{
GenFile *existfile = NULL;
GList *list;
DB( g_print("\n[genfile] get %d\n", key) );
list = g_list_first(lst_file);
while (list != NULL)
{
GenFile *genfile = list->data;
if( key == genfile->key )
{
existfile = genfile;
DB( g_print(" found\n") );
break;
}
list = g_list_next(list);
}
return existfile;
}
static GenFile *
da_gen_file_get_by_name(GList *lst_file, gchar *filepath)
{
GenFile *existfile = NULL;
GList *list;
DB( g_print("\n[genfile] get by name\n") );
list = g_list_first(lst_file);
while (list != NULL)
{
GenFile *genfile = list->data;
DB( g_print(" strcasecmp '%s' '%s'\n", filepath, genfile->filepath) );
if(!strcasecmp(filepath, genfile->filepath))
{
existfile = genfile;
DB( g_print(" found\n") );
break;
}
list = g_list_next(list);
}
return existfile;
}
GenFile *
da_gen_file_append_from_filename(ImportContext *ictx, gchar *filename)
{
GenFile *genfile = NULL;
gint filetype;
DB( g_print("\n[genfile] append from\n") );
DB( g_print(" filename:'%s'\n", filename) );
filetype = homebank_alienfile_recognize(filename);
// we keep everything here
//if( (filetype == FILETYPE_OFX) || (filetype == FILETYPE_QIF) || (filetype == FILETYPE_CSV_HB) )
if( filetype != FILETYPE_HOMEBANK )
{
GenFile *existgenfile;
existgenfile = da_gen_file_get_by_name(ictx->gen_lst_file, filename);
if(existgenfile == NULL)
{
genfile = da_gen_file_malloc();
genfile->filepath = g_strdup(filename);
//FILETYPE_UNKNOWN = invalid
genfile->filetype = filetype;
//append to list
DB( g_print(" add to list\n") );
genfile->key = g_list_length (ictx->gen_lst_file) + 1;
ictx->gen_lst_file = g_list_append(ictx->gen_lst_file, genfile);
}
}
return genfile;
}
/* = = = = = = = = = = = = = = = = */
GenAcc *
da_gen_acc_malloc(void)
{
return g_malloc0(sizeof(GenAcc));
}
void
da_gen_acc_free(GenAcc *genacc)
{
if(genacc != NULL)
{
if(genacc->name != NULL)
g_free(genacc->name);
if(genacc->number != NULL)
g_free(genacc->number);
g_free(genacc);
}
}
GenAcc *
da_gen_acc_get_by_key(GList *lst_acc, guint32 key)
{
GenAcc *existacc = NULL;
GList *list;
DB( g_print("da_gen_acc_get_by_key\n") );
list = g_list_first(lst_acc);
while (list != NULL)
{
GenAcc *genacc = list->data;
if( key == genacc->key )
{
existacc = genacc;
break;
}
list = g_list_next(list);
}
return existacc;
}
static GenAcc *
da_gen_acc_get_by_name(GList *lst_acc, gchar *name)
{
GenAcc *existacc = NULL;
GList *list;
DB( g_print("da_gen_acc_get_by_name\n") );
list = g_list_first(lst_acc);
while (list != NULL)
{
GenAcc *genacc = list->data;
//DB( g_print(" strcasecmp '%s' '%s'\n", name, genacc->name) );
if(!strcasecmp(name, genacc->name))
{
existacc = genacc;
//DB( g_print(" found\n") );
break;
}
list = g_list_next(list);
}
return existacc;
}
/* = = = = = = = = = = = = = = = = */
Account *
hb_import_acc_find_existing(gchar *name, gchar *number)
{
Account *retacc = NULL;
GList *lacc, *list;
DB( g_print("\n[import] acc_find_existing\n") );
DB( g_print(" - search number '%s'\n", number) );
lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
while (list != NULL)
{
Account *acc = list->data;
//DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) );
if(number != NULL && acc->number && strlen(acc->number) )
{
//prefer identifying with number & search number into acc->number
if(g_strstr_len(number, -1, acc->number) != NULL)
{
DB( g_print(" - match number '%s'\n", acc->number) );
retacc = acc;
break;
}
}
list = g_list_next(list);
}
//# 1815964 only test name if all number test failed
//if not found try with name
if(retacc == NULL)
{
DB( g_print(" - search name '%s'\n", name) );
list = g_list_first(lacc);
while (list != NULL)
{
Account *acc = list->data;
//DB( g_print(" - eval acc '%s' or '%s'\n", acc->name, acc->number) );
if(retacc == NULL && name != NULL)
{
if(g_strstr_len(name, -1, acc->name) != NULL)
{
DB( g_print(" - match name '%s'\n", acc->name) );
retacc = acc;
break;
}
}
list = g_list_next(list);
}
}
g_list_free(lacc);
return retacc;
}
GenAcc *
hb_import_gen_acc_get_next(ImportContext *ictx, gint filetype, gchar *name, gchar *number)
{
GenAcc *newacc;
DB( g_print("\n[import] acc_get_next\n") );
DB( g_print(" - type='%d', name='%s', number='%s'\n", filetype, name, number) );
// try to find a same name account
if( name != NULL )
{
newacc = da_gen_acc_get_by_name(ictx->gen_lst_acc, name);
if(newacc != NULL)
{
DB( g_print(" - found existing '%s'\n", name) );
goto end;
}
}
newacc = da_gen_acc_malloc();
if(newacc)
{
newacc->kfile = ictx->curr_kfile;
newacc->key = ictx->gen_next_acckey++;
newacc->kacc = DST_ACC_GLOBAL;
if(name != NULL)
{
newacc->is_unamed = FALSE;
newacc->name = g_strdup(name);
}
else
{
GenFile *genfile;
gchar *basename;
newacc->is_unamed = TRUE;
genfile = da_gen_file_get (ictx->gen_lst_file, newacc->kfile);
basename = g_path_get_basename(genfile->filepath);
newacc->name = g_strdup_printf("%s %d", basename, newacc->key);
g_free(basename);
}
if(number != NULL)
newacc->number = g_strdup(number);
ictx->gen_lst_acc = g_list_append(ictx->gen_lst_acc, newacc);
}
DB( g_print(" - create new '%s'\n", newacc->name) );
end:
newacc->filetype = filetype;
ictx->curr_kacc = newacc->key;
return newacc;
}
/* = = = = = = = = = = = = = = = = */
GenTxn *
da_gen_txn_malloc(void)
{
return g_malloc0(sizeof(GenTxn));
}
void
da_gen_txn_free(GenTxn *gentxn)
{
gint i;
if(gentxn != NULL)
{
if(gentxn->account != NULL)
g_free(gentxn->account);
if(gentxn->rawnumber != NULL)
g_free(gentxn->rawnumber);
if(gentxn->rawpayee != NULL)
g_free(gentxn->rawpayee);
if(gentxn->rawmemo != NULL)
g_free(gentxn->rawmemo);
// 5.5.1 add OFX fitid
if(gentxn->fitid != NULL)
g_free(gentxn->fitid);
if(gentxn->date != NULL)
g_free(gentxn->date);
if(gentxn->number != NULL)
g_free(gentxn->number);
if(gentxn->payee != NULL)
g_free(gentxn->payee);
if(gentxn->memo != NULL)
g_free(gentxn->memo);
if(gentxn->category != NULL)
g_free(gentxn->category);
if(gentxn->tags != NULL)
g_free(gentxn->tags);
for(i=0;isplits[i];
if(s->memo != NULL)
g_free(s->memo);
if(s->category != NULL)
g_free(s->category);
}
if(gentxn->lst_existing != NULL)
{
g_list_free(gentxn->lst_existing);
gentxn->lst_existing = NULL;
}
g_free(gentxn);
}
}
static gint
da_gen_txn_compare_func(GenTxn *a, GenTxn *b)
{
gint retval = (gint)(a->julian - b->julian);
//5.8 #2063416 same date txn
if(!retval)
retval = a->row - b->row;
if(!retval)
retval = (ABS(a->amount) - ABS(b->amount)) > 0 ? 1 : -1;
return (retval);
}
GList *
da_gen_txn_sort(GList *list)
{
return( g_list_sort(list, (GCompareFunc)da_gen_txn_compare_func));
}
void
da_gen_txn_move(GenTxn *sgentxn, GenTxn *dgentxn)
{
if(sgentxn != NULL && dgentxn != NULL)
{
memcpy(dgentxn, sgentxn, sizeof(GenTxn));
memset(sgentxn, 0, sizeof(GenTxn));
}
}
void
da_gen_txn_append(ImportContext *ctx, GenTxn *gentxn)
{
gentxn->kfile = ctx->curr_kfile;
gentxn->kacc = ctx->curr_kacc;
gentxn->to_import = TRUE;
//perf must use preprend, see glib doc
//sort will be done later, so we don't care here
ctx->gen_lst_txn = g_list_prepend(ctx->gen_lst_txn, gentxn);
}
/* = = = = = = = = = = = = = = = = */
static void _string_utf8_ucfirst(gchar **str)
{
gint str_len;
gchar *first, *lc;
if( *str == NULL )
return;
str_len = strlen(*str);
if( str_len <= 1 )
return;
first = g_utf8_strup(*str, 1);
lc = g_utf8_strdown( g_utf8_next_char(*str), -1 );
g_free(*str);
*str = g_strjoin(NULL, first, lc, NULL);
g_free(first);
g_free(lc);
}
static gchar *
_string_concat(gchar *str, gchar *addon)
{
gchar *retval;
DB( g_print(" - concat '%s' + '%s'\n", str, addon) );
if(str == NULL)
retval = g_strdup(addon);
else
{
retval = g_strjoin(" ", str, addon, NULL);
g_free(str);
}
DB( g_print(" - retval='%s'\n", retval) );
return retval;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
gchar *hb_import_filetype_char_get(GenAcc *genacc)
{
gchar *retval = "";
switch(genacc->filetype)
{
#ifndef NOOFX
case FILETYPE_OFX:
retval = "OFX/QFX";
break;
#endif
case FILETYPE_QIF:
retval = "QIF";
break;
case FILETYPE_CSV_HB:
retval = "CSV";
break;
}
return retval;
}
void
hb_import_load_all(ImportContext *ictx)
{
GList *list;
DB( g_print("\n[import] load all\n") );
da_import_context_clear (ictx);
list = g_list_first(ictx->gen_lst_file);
while (list != NULL)
{
GenFile *genfile = list->data;
if(genfile->filetype != FILETYPE_UNKNOWN)
{
//todo: move this to alien analysis
genfile->encoding = homebank_file_getencoding(genfile->filepath);
ictx->curr_kfile = genfile->key;
DB( g_print(" -> key = '%d'\n", genfile->key) );
DB( g_print(" -> filepath = '%s'\n", genfile->filepath) );
DB( g_print(" -> encoding = '%s'\n", genfile->encoding) );
genfile->loaded = FALSE;
genfile->invaliddatefmt = FALSE;
switch(genfile->filetype)
{
#ifndef NOOFX
case FILETYPE_OFX:
homebank_ofx_import(ictx, genfile);
break;
#endif
case FILETYPE_QIF:
homebank_qif_import(ictx, genfile);
break;
case FILETYPE_CSV_HB:
homebank_csv_import(ictx, genfile);
break;
}
genfile->loaded = TRUE;
}
list = g_list_next(list);
}
// sort by date
ictx->gen_lst_txn = da_gen_txn_sort(ictx->gen_lst_txn);
}
gint
hb_import_gen_acc_count_txn(ImportContext *ictx, GenAcc *genacc)
{
GList *list;
gint count = 0;
DB( g_print("\n[import] gen_acc_count_txn\n") );
genacc->n_txnall = 0;
genacc->n_txnimp = 0;
list = g_list_first(ictx->gen_lst_txn);
while (list != NULL)
{
GenTxn *gentxn = list->data;
if(gentxn->kacc == genacc->key)
{
genacc->n_txnall++;
count++;
DB( g_print(" count %03d: gentxn in=%d dup=%d '%s'\n", count, gentxn->to_import, gentxn->is_dst_similar, gentxn->memo) );
if(gentxn->to_import)
genacc->n_txnimp++;
}
list = g_list_next(list);
}
return count;
}
/**
* uncheck duplicate within the import context files
*/
gint
hb_import_gen_txn_check_duplicate(ImportContext *ictx, GenAcc *genacc)
{
GList *list1, *list2;
gboolean isimpsimilar = FALSE;
gint count = 0;
DB( g_print("\n[import] gen_txn_check_duplicate\n") );
list1 = g_list_first(ictx->gen_lst_txn);
while (list1 != NULL)
{
GenTxn *gentxn1 = list1->data;
isimpsimilar = FALSE;
DB( g_print(" 1eval %d %dd %.2f '%s'\n", gentxn1->kacc, gentxn1->julian, gentxn1->amount, gentxn1->fitid) );
if( (genacc->key == gentxn1->kacc) && (gentxn1->julian != 0) ) //same account, valid date
{
list2 = g_list_next(list1);
while (list2 != NULL)
{
GenTxn *gentxn2 = list2->data;
if( (gentxn2->julian > gentxn1->julian) || (isimpsimilar == TRUE) )
break;
DB( g_print(" 2eval %d %d %.2f '%s'in=%d dup=%d\n", gentxn2->kacc, gentxn2->julian, gentxn2->amount, gentxn2->fitid, gentxn1->to_import, gentxn1->is_imp_similar) );
// 5.5.1 add OFX fitid
if( (genacc->filetype == FILETYPE_OFX) )
{
GenAcc *gacc1, *gacc2;
//#1919080 check also account
gacc1 = da_gen_acc_get_by_key(ictx->gen_lst_acc, gentxn1->kacc);
gacc2 = da_gen_acc_get_by_key(ictx->gen_lst_acc, gentxn2->kacc);
if( gacc1 != NULL && gacc2 != NULL )
{
gint resacc, resfitid;
DB( g_print(" acc1='%s' <> acc2='%s'\n", gacc1->number, gacc2->number) );
resacc = hb_string_ascii_compare(gacc1->number, gacc2->number);
resfitid = hb_string_ascii_compare(gentxn2->fitid, gentxn1->fitid);
DB( g_print(" eval with fitid\n") );
if( !resacc && !resfitid )
{
isimpsimilar = TRUE;
DB( g_print(" found dup fitid\n") );
}
}
}
else
{
//DB( g_print(" eval with data\n") );
//todo: maybe reinforce controls here
if( (gentxn2->kacc == gentxn1->kacc)
&& (gentxn2->julian == gentxn1->julian)
&& (gentxn2->amount == gentxn1->amount)
&& (hb_string_compare(gentxn2->memo, gentxn1->memo) == 0)
&& (hb_string_compare(gentxn2->payee, gentxn1->payee) == 0)
//#1954017 add comparison on category
&& (hb_string_compare(gentxn2->category, gentxn1->category) == 0)
//#2114674 add comparison to number (cheque)
&& (hb_string_compare(gentxn2->number, gentxn1->number) == 0)
)
{
isimpsimilar = TRUE;
DB( g_print(" found dup data\n") );
}
}
list2 = g_list_next(list2);
}
if( isimpsimilar == TRUE )
{
gentxn1->to_import = FALSE;
gentxn1->is_imp_similar = TRUE;
count++;
}
}
list1 = g_list_next(list1);
}
genacc->n_txnsimimp = count;
return count;
}
/**
* uncheck existing txn into target account
*
*/
gint
hb_import_gen_txn_check_target_similar(ImportContext *ictx, GenAcc *genacc)
{
GList *list1, *list2;
gdouble amount;
gint count = 0;
DB( g_print("\n[import] gen_txn_check_target_similar\n") );
list1 = g_list_first(ictx->gen_lst_txn);
while (list1 != NULL)
{
GenTxn *gentxn = list1->data;
DB( g_print("\n--------\n src: a:%d d:%d %.17g '%s'\n", gentxn->kacc, gentxn->julian, gentxn->amount, gentxn->memo) );
if(genacc->key == gentxn->kacc)
{
//#5.5.1, commented and reported below
//as it override the state made with a call to hb_import_gen_txn_check_duplicate
//gentxn->to_import = TRUE;
gentxn->is_dst_similar = FALSE;
if(genacc->kacc == DST_ACC_SKIP)
{
gentxn->to_import = FALSE;
}
else
{
Account *acc = da_acc_get(genacc->kacc);
if(acc != NULL)
{
//clear previous existing
if(gentxn->lst_existing != NULL)
{
g_list_free(gentxn->lst_existing);
gentxn->lst_existing = NULL;
}
//#1866456
amount = (gentxn->togamount == TRUE) ? -gentxn->amount : gentxn->amount;
// try to find existing transaction
list2 = g_queue_peek_tail_link(acc->txn_queue);
while (list2 != NULL)
{
Transaction *txn = list2->data;
DB( g_print(" evl: a:%d d:%d %.17g '%s'\n", txn->kacc, txn->date, txn->amount, txn->memo) );
//break if the date goes below the gentxn date + gap
if( txn->date < (gentxn->julian - ictx->opt_daygap) )
break;
//#1586211 add of date tolerance
//todo: maybe reinforce controls here
if( ( genacc->kacc == txn->kacc )
&& ( gentxn->julian <= (txn->date + ictx->opt_daygap) )
&& ( gentxn->julian >= (txn->date - ictx->opt_daygap) )
//#2012999
&& ( hb_amount_cmp(amount, txn->amount) == 0 )
)
{
gentxn->lst_existing = g_list_append(gentxn->lst_existing, txn);
gentxn->to_import = FALSE;
gentxn->is_dst_similar = TRUE;
count++;
DB( g_print(" => found dst acc dup %d %.17g '%s' in=%d, dup=%d\n", gentxn->julian, amount, gentxn->memo, gentxn->to_import, gentxn->is_dst_similar) );
}
list2 = g_list_previous(list2);
}
}
}
//#5.5.1 added check here
if( (gentxn->is_dst_similar == TRUE) || (gentxn->is_imp_similar == TRUE) )
{
gentxn->to_import = FALSE;
}
}
list1 = g_list_next(list1);
}
genacc->n_txnsimdst = count;
return count;
}
/**
* try to identify xfer for OFX
*
*/
static gint
hb_import_gen_xfer_eval(ImportContext *ictx, GList *list)
{
GList *root, *list1, *list2;
GList *match = NULL;
gint count = 0;
DB( g_print("\n[import] gen xfer eval\n") );
DB( g_print(" n_txn=%d\n", g_list_length(list)) );
root = da_transaction_sort(list);
root = list1 = g_list_first(list);
while (list1 != NULL)
{
Transaction *txn1 = list1->data;
GenAcc *acc;
DB( g_print(" list:%p txn:%p\n", ictx->gen_lst_acc, txn1) );
if( txn1 != NULL )
{
acc = da_gen_acc_get_by_key(ictx->gen_lst_acc, txn1->kacc);
DB( g_print(" src: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn1->kacc, txn1->date, txn1->amount, txn1->memo, txn1->kxferacc) );
if( (acc != NULL) && (acc->filetype == FILETYPE_OFX) )
{
match = NULL;
count = 0;
list2 = g_list_next(root);
while (list2 != NULL)
{
Transaction *txn2 = list2->data;
if(!txn2)
goto next;
//DB( g_print(" -- chk: kacc:%d dat:%d amt:%.2f %s\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo) );
if( (txn2->date > txn1->date) )
break;
if( (txn2 == txn1) || (txn2->flags & OF_INTXFER) )
goto next;
//todo: maybe reinforce controls here
if( (txn2->kacc != txn1->kacc)
&& (txn2->date == txn1->date)
&& (txn2->amount == -txn1->amount)
&& (hb_string_compare(txn2->memo, txn1->memo) == 0)
)
{
DB( g_print(" match: kacc:%d dat:%d amt:%.2f %s kfxacc:%d\n", txn2->kacc, txn2->date, txn2->amount, txn2->memo, txn2->kxferacc) );
match = g_list_append(match, txn2);
count++;
}
next:
list2 = g_list_next(list2);
}
if(count == 1) //we found a single potential xfer, transform it
{
Transaction *txn2 ;
DB( g_print(" single found => convert both\n") );
list2 = g_list_first(match);
txn2 = list2->data;
if( txn1 && txn2 )
{
txn1->flags |= OF_INTXFER;
transaction_xfer_change_to_child(txn1, txn2);
}
}
// if more than one, we cannot be sure
g_list_free(match);
}
}
list1 = g_list_next(list1);
}
return count;
}
/**
* apply the user option: date format, payee/memo/info mapping
*
*/
gboolean
hb_import_option_apply(ImportContext *ictx, GenAcc *genacc)
{
GList *list;
gint i;
DB( g_print("\n[import] option apply\n") );
DB( g_print(" - type=%d\n", genacc->filetype) );
genacc->n_txnbaddate = 0;
i=0;
list = g_list_first(ictx->gen_lst_txn);
while (list != NULL)
{
GenTxn *gentxn = list->data;
if(gentxn->kacc == genacc->key)
{
if(genacc->filetype != FILETYPE_OFX)
{
gentxn->julian = hb_date_get_julian(gentxn->date, ictx->opt_dateorder);
if( gentxn->julian == 0 )
{
genacc->n_txnbaddate++;
}
}
if(genacc->filetype == FILETYPE_OFX)
{
DB( g_print(" - ofx option apply\n") );
g_free(gentxn->payee);
g_free(gentxn->memo);
g_free(gentxn->number);
gentxn->payee = NULL;
gentxn->memo = NULL;
gentxn->number = NULL;
// OFX:check_number
gentxn->number = g_strdup(gentxn->rawnumber);
//#1791482 map name to info (concat only)
switch(ictx->opt_ofxname)
{
//ofxname is stored into rawpayee
//case 0: PRF_OFXNAME_IGNORE
case PRF_OFXNAME_MEMO:
gentxn->memo = g_strdup(gentxn->rawpayee);
break;
case PRF_OFXNAME_PAYEE:
gentxn->payee = g_strdup(gentxn->rawpayee);
break;
case PRF_OFXNAME_NUMBER:
//#1909323 no need to free here
//g_free(gentxn->number);
gentxn->number = _string_concat(gentxn->number, gentxn->rawpayee);
break;
}
if(gentxn->rawmemo != NULL)
{
switch(ictx->opt_ofxmemo)
{
//case 0: PRF_OFXMEMO_IGNORE
case PRF_OFXMEMO_NUMBER:
gentxn->number = _string_concat(gentxn->number, gentxn->rawmemo);
break;
case PRF_OFXMEMO_MEMO:
gentxn->memo = _string_concat(gentxn->memo, gentxn->rawmemo);
break;
case PRF_OFXMEMO_PAYEE:
gentxn->payee = _string_concat(gentxn->payee, gentxn->rawmemo);
break;
}
}
DB( g_print(" - payee is '%s'\n", gentxn->payee) );
DB( g_print(" - memo is '%s'\n", gentxn->memo) );
DB( g_print(" - info is '%s'\n", gentxn->number) );
DB( g_print("\n") );
}
else
if(genacc->filetype == FILETYPE_QIF)
{
DB( g_print(" - qif option apply\n") );
g_free(gentxn->payee);
g_free(gentxn->memo);
gentxn->payee = NULL;
gentxn->memo = NULL;
if(!ictx->opt_qifswap)
{
gentxn->payee = g_strdup(gentxn->rawpayee);
if(ictx->opt_qifmemo)
gentxn->memo = g_strdup(gentxn->rawmemo);
}
else
{
gentxn->payee = g_strdup(gentxn->rawmemo);
if(ictx->opt_qifmemo)
gentxn->memo = g_strdup(gentxn->rawpayee);
}
DB( g_print(" - payee is '%s'\n", gentxn->payee) );
DB( g_print(" - memo is '%s'\n", gentxn->memo) );
}
else
if(genacc->filetype == FILETYPE_CSV_HB)
{
DB( g_print(" - csv option apply\n") );
DB( g_print(" [%d] row=%d\n", i, gentxn->row) );
//#1791656 missing: info, payee and tags
g_free(gentxn->payee);
g_free(gentxn->memo);
g_free(gentxn->number);
gentxn->payee = g_strdup(gentxn->rawpayee);
gentxn->memo = g_strdup(gentxn->rawmemo);
gentxn->number = g_strdup(gentxn->rawnumber);
}
//at last do ucfirst
if( (ictx->opt_ucfirst == TRUE) )
{
_string_utf8_ucfirst(&gentxn->memo);
_string_utf8_ucfirst(&gentxn->payee);
//category ?
}
//1866456 and toggle amount
gentxn->togamount = ictx->opt_togamount;
}
i++;
list = g_list_next(list);
}
DB( g_print(" - nb_err=%d\n", genacc->n_txnbaddate) );
return genacc->n_txnbaddate == 0 ? TRUE : FALSE;
}
/**
* convert a GenTxn to a Transaction
*
*/
Transaction *
hb_import_convert_txn(GenAcc *genacc, GenTxn *gentxn)
{
Transaction *newope;
Account *accitem;
Payee *payitem;
Category *catitem;
gint nsplit;
DB( g_print("\n[import] convert txn\n") );
newope = NULL;
DB( g_print(" - gentxt '%s' %s %s\n", gentxn->account, gentxn->date, gentxn->memo) );
DB( g_print(" - genacc '%s' '%p' k=%d\n", gentxn->account, genacc, genacc->kacc) );
if( genacc != NULL )
{
newope = da_transaction_malloc();
newope->kacc = genacc->kacc;
newope->date = gentxn->julian;
newope->paymode = gentxn->paymode;
newope->number = g_strdup(gentxn->number);
newope->memo = g_strdup(gentxn->memo);
newope->amount = gentxn->amount;
//#773282 invert amount for ccard accounts
//todo: manage this (qif), it is not set to true anywhere
//= it is into the qif account see hb-import-qif.c
//if(ictx->is_ccard)
// gentxn->amount *= -1;
//#1866456 manual toggle amount
if( gentxn->togamount == TRUE )
{
newope->amount = -gentxn->amount;
}
// payee + append
if( gentxn->payee != NULL )
{
payitem = da_pay_get_by_name(gentxn->payee);
if(payitem == NULL)
{
//DB( g_print(" -> append pay: '%s'\n", item->payee ) );
payitem = da_pay_malloc();
payitem->name = g_strdup(gentxn->payee);
//payitem->imported = TRUE;
da_pay_append(payitem);
//ictx->cnt_new_pay += 1;
}
newope->kpay = payitem->key;
}
// LCategory of transaction
// L[Transfer account name]
// LCategory of transaction/Class of transaction
// L[Transfer account]/Class of transaction
if( gentxn->category != NULL )
{
if(g_str_has_prefix(gentxn->category, "[")) // this is a transfer account name
{
gchar *accname;
//DB ( g_print(" -> transfer to: '%s'\n", item->category) );
//remove brackets
accname = hb_strdup_nobrackets(gentxn->category);
// search target account + append if not exixts
accitem = da_acc_get_by_name(accname);
if(accitem == NULL)
{
DB( g_print(" -> append int xfer dest acc: '%s'\n", accname ) );
accitem = da_acc_malloc();
accitem->name = g_strdup(accname);
//accitem->imported = TRUE;
//accitem->imp_name = g_strdup(accname);
da_acc_append(accitem);
}
newope->kxferacc = accitem->key;
newope->flags |= OF_INTXFER;
g_free(accname);
}
else
{
//DB ( g_print(" -> append cat: '%s'\n", item->category) );
catitem = da_cat_append_ifnew_by_fullname(gentxn->category);
if( catitem != NULL )
{
//ictx->cnt_new_cat += 1;
newope->kcat = catitem->key;
}
}
}
//#1791656 miss tags also...
if( gentxn->tags != NULL )
{
g_free(newope->tags);
newope->tags = tags_parse(gentxn->tags);
}
// splits, if not a xfer
//TODO: it seems this never happen
if( gentxn->paymode != OLDPAYMODE_INTXFER )
//if( !(gentxn->flags & OF_INTXFER) )
{
if( gentxn->nb_splits > 0 )
{
newope->splits = da_split_new();
for(nsplit=0;nsplitnb_splits;nsplit++)
{
GenSplit *s = &gentxn->splits[nsplit];
Split *hbs;
guint32 kcat = 0;
DB( g_print(" -> append split %d: '%s' '%.2f' '%s'\n", nsplit, s->category, s->amount, s->memo) );
if( s->category != NULL )
{
catitem = da_cat_append_ifnew_by_fullname(s->category);
if( catitem != NULL )
{
kcat = catitem->key;
}
}
//todo: remove this when no more use ||
hb_string_remove_char('|', s->memo);
hbs = da_split_malloc ();
hbs->kcat = kcat;
hbs->memo = g_strdup(s->memo);
hbs->amount = s->amount;
//#1866456 manual toggle amount
if( gentxn->togamount == TRUE )
{
hbs->amount = -s->amount;
}
da_splits_append(newope->splits, hbs);
hbs = NULL;
}
}
}
newope->dspflags |= FLAG_TMP_ADDED;
da_transaction_set_flag(newope);
if( gentxn->reconciled )
{
Account *acc = da_acc_get(newope->kacc);
newope->status = TXN_STATUS_RECONCILED;
//#1581863 store reconciled date
if( acc != NULL )
acc->rdate = GLOBALS->today;
}
else
if( gentxn->cleared )
newope->status = TXN_STATUS_CLEARED;
}
return newope;
}
void
hb_import_apply(ImportContext *ictx)
{
GList *list, *lacc;
GList *txnlist;
guint changes = 0;
guint nbofxtxn = 0;
DB( g_print("\n[import] apply\n") );
DB( g_print("\n--1-- insert acc\n") );
//create accounts
list = g_list_first(ictx->gen_lst_acc);
while (list != NULL)
{
GenAcc *genacc = list->data;
DB( g_print(" genacc: %d %s %s => %d\n", genacc->key, genacc->name, genacc->number, genacc->kacc) );
//we do create the common account once
if( (genacc->kacc == DST_ACC_GLOBAL) )
{
gchar *globalname = _("imported account");
Account *acc = da_acc_get_by_name(globalname);
if( acc == NULL )
{
acc = da_acc_malloc ();
acc->name = g_strdup(globalname);
if( da_acc_append(acc) )
{
changes++;
}
}
//store the target acc key
genacc->kacc = acc->key;
}
else
if( (genacc->kacc == DST_ACC_NEW) )
{
Account *acc = da_acc_malloc ();
acc->name = g_strdup(genacc->name);
if( da_acc_append(acc) )
{
acc->number = g_strdup(genacc->number);
acc->initial = genacc->initial;
//store the target acc key
genacc->kacc = acc->key;
changes++;
}
//#5.6.2 fix leak
else
{
da_acc_free(acc);
}
}
list = g_list_next(list);
}
// also keep every transactions into a temporary list
// we do this to keep a finished real txn list for detect xfer below
DB( g_print("\n--2-- insert txn\n") );
txnlist = NULL;
lacc = g_list_first(ictx->gen_lst_acc);
while (lacc != NULL)
{
GenAcc *genacc = lacc->data;
DB( g_print(" => genacc='%s'\n", genacc->name) );
if(genacc->kacc != DST_ACC_SKIP)
{
list = g_list_first(ictx->gen_lst_txn);
while (list != NULL)
{
GenTxn *gentxn = list->data;
if(gentxn->kacc == genacc->key && gentxn->to_import == TRUE)
{
Transaction *txn, *dtxn;
txn = hb_import_convert_txn(genacc, gentxn);
if( txn )
{
dtxn = transaction_add(NULL, FALSE, txn);
//perf must use preprend, see glib doc
DB( g_print(" prepend %p to txnlist\n", dtxn) );
//#2000480 avoid insert null txn
if( dtxn != NULL )
{
//#1875100 add pending
if(ictx->set_pending == TRUE)
dtxn->flags |= OF_ISIMPORT;
txnlist = g_list_prepend(txnlist, dtxn);
}
da_transaction_free(txn);
//#1820618 forgot to report changes count
changes++;
}
}
if( genacc->filetype == FILETYPE_OFX )
nbofxtxn++;
list = g_list_next(list);
}
}
else
{
DB( g_print(" marked as skipped\n") );
}
lacc = g_list_next(lacc);
}
//auto from payee
if( ictx->do_auto_payee == TRUE )
{
DB( g_print("\n--3-- call auto payee\n") );
transaction_auto_all_from_payee(txnlist);
}
//auto assign
if( ictx->do_auto_assign == TRUE )
{
DB( g_print("\n--4-- call auto assign\n") );
transaction_auto_assign(txnlist, 0, FALSE);
}
//check for ofx internal xfer
if( nbofxtxn > 0 )
{
DB( g_print("\n--5-- call hb_import_gen_xfer_eval\n") );
hb_import_gen_xfer_eval(ictx, txnlist);
}
DB( g_print("\n--end-- adding %d changes\n", changes) );
GLOBALS->changes_count += changes;
g_list_free(txnlist);
}
#if MYDEBUG
static void _import_context_debug_file_list(ImportContext *ctx)
{
GList *list;
g_print("\n--debug-- file list %d\n", g_list_length(ctx->gen_lst_file) );
list = g_list_first(ctx->gen_lst_file);
while (list != NULL)
{
GenFile *item = list->data;
g_print(" genfile: %d '%s' '%s'\ndf=%d invalid=%d\n", item->key, item->filepath, item->encoding, item->datefmt, item->invaliddatefmt);
list = g_list_next(list);
}
}
static void _import_context_debug_acc_list(ImportContext *ctx)
{
GList *list;
g_print("\n--debug-- acc list %d\n", g_list_length(ctx->gen_lst_acc) );
list = g_list_first(ctx->gen_lst_acc);
while (list != NULL)
{
GenAcc *item = list->data;
g_print(" genacc: %d %s %s => %d\n", item->key, item->name, item->number, item->kacc);
list = g_list_next(list);
}
}
static void _import_context_debug_txn_list(ImportContext *ctx)
{
GList *list;
g_print("\n--debug-- txn list %d\n", g_list_length(ctx->gen_lst_txn) );
list = g_list_first(ctx->gen_lst_txn);
while (list != NULL)
{
GenTxn *item = list->data;
g_print(" gentxn: (%d) %s %s (%d) %s %.2f\n", item->kfile, item->account, item->date, item->julian, item->memo, item->amount);
list = g_list_next(list);
}
}
#endif
homebank-5.9.7/src/rep-stats.h 0000644 0001750 0001750 00000004662 14736461415 015517 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HOMEBANK_REPDIST_H__
#define __HOMEBANK_REPDIST_H__
enum {
HID_REPDIST_MINDATE,
HID_REPDIST_MAXDATE,
HID_REPDIST_RANGE,
HID_REPDIST_FORECAST,
HID_REPDIST_VIEW,
MAX_REPDIST_HID
};
struct repstats_data
{
DataTable *trend;
gint trendrows;
gint trendcols;
gint sel_colid;
GQueue *txn_queue;
Filter *filter;
gboolean detail;
gboolean legend;
gboolean rate;
gdouble total_expense;
gdouble total_income;
gint charttype;
GtkWidget *window;
GActionGroup *actions;
gboolean mapped_done;
GtkWidget *TB_bar;
GtkWidget *BT_list;
GtkWidget *BT_column;
GtkWidget *BT_donut;
GtkWidget *BT_stack;
GtkWidget *BT_stack100;
GtkWidget *BT_detail;
GtkWidget *BT_legend;
GtkWidget *BT_rate;
GtkWidget *BT_filter;
GtkWidget *BT_refresh;
GtkWidget *BT_print;
GtkWidget *BT_export;
GtkWidget *TX_info;
GtkWidget *CM_minor;
GtkWidget *RA_mode;
GtkWidget *CY_src;
GtkWidget *CY_type, *LB_type;
GtkWidget *CY_intvl, *LB_intvl;
GtkWidget *CM_forecast;
GtkWidget *GR_listbar;
GtkWidget *BT_expand;
GtkWidget *BT_collapse;
//beta start
GtkWidget *PO_hubfilter;
GtkWidget *BT_reset;
GtkWidget *TX_fltactive, *TT_fltactive;
//beat end
GtkWidget *RG_zoomx, *LB_zoomx;
GtkWidget *SW_total, *SW_trend;
GtkWidget *LV_report;
GtkWidget *LV_report2;
GtkWidget *CM_balance;
GtkWidget *CM_byamount;
GtkWidget *CM_compare;
GtkWidget *PO_mindate, *PO_maxdate;
GtkWidget *CY_range;
GtkWidget *GR_result;
GtkWidget *TX_daterange;
GtkWidget *TX_total[3];
GtkWidget *RE_chart;
GtkWidget *RE_chart2;
GtkWidget *GR_detail;
GtkWidget *LV_detail;
gulong hid[MAX_REPDIST_HID];
};
GtkWidget *repstats_window_new(void);
#endif
homebank-5.9.7/src/ui-dialogs.h 0000644 0001750 0001750 00000004333 14736461415 015625 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_DIALOGS_GTK_H__
#define __HB_DIALOGS_GTK_H__
#define HB_RESPONSE_SELECTION 1
#define HB_RESPONSE_CREATE_NEW 2
gchar *dialog_get_name(gchar *title, gchar *origname, GtkWindow *parentwindow);
gint ui_dialog_msg_confirm_alert(GtkWindow *parent, gchar *title, gchar *secondtext, gchar *actionverb, gboolean destructive);
gint ui_dialog_about(GtkWindow *parent, gchar *title, gchar *message_format, ...);
gint ui_dialog_msg_question(GtkWindow *parent, gchar *title, gchar *message_format, ...);
void ui_dialog_msg_infoerror(GtkWindow *parent, GtkMessageType type, gchar *title, gchar *message_format, ...);
gboolean ui_file_chooser_qif(GtkWindow *parent, gchar **storage_ptr);
gboolean ui_file_chooser_csv(GtkWindow *parent, GtkFileChooserAction action, gchar **storage_ptr, gchar *name);
gboolean ui_file_chooser_xhb(GtkFileChooserAction action, gchar **storage_ptr, gboolean bakmode);
gboolean ui_file_chooser_folder(GtkWindow *parent, gchar *title, gchar **storage_ptr);
gint ui_dialog_export_csv(GtkWindow *parent, gchar **storage_ptr, gboolean *split, gboolean *status, gboolean showall);
gint ui_dialog_export_pdf(GtkWindow *parent, gchar **storage_ptr);
void ui_dialog_upgrade_choose_currency(void);
gboolean ui_dialog_msg_savechanges(GtkWidget *widget, gpointer user_data);
void ui_dialog_file_statistics(void);
gint ui_dialog_transaction_xfer_select_child(GtkWindow *parent, Transaction *stxn, GList *matchlist, Transaction **child);
#endif
homebank-5.9.7/src/Makefile.in 0000664 0001750 0001750 00000106556 15121556131 015465 0 ustar franam franam # Makefile.in generated by automake 1.17 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2024 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
am__rm_f = rm -f $(am__rm_f_notfound)
am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
bin_PROGRAMS = homebank$(EXEEXT)
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am_homebank_OBJECTS = dsp-account.$(OBJEXT) dsp-mainwindow.$(OBJEXT) \
gtk-chart.$(OBJEXT) gtk-chart-colors.$(OBJEXT) \
gtk-chart-progress.$(OBJEXT) gtk-dateentry.$(OBJEXT) \
hb-account.$(OBJEXT) hb-archive.$(OBJEXT) hb-assign.$(OBJEXT) \
hb-category.$(OBJEXT) hb-currency.$(OBJEXT) \
hb-encoding.$(OBJEXT) hb-export.$(OBJEXT) hb-filter.$(OBJEXT) \
hb-hbfile.$(OBJEXT) hb-import.$(OBJEXT) \
hb-import-ofx.$(OBJEXT) hb-import-qif.$(OBJEXT) \
hb-import-csv.$(OBJEXT) hb-misc.$(OBJEXT) hb-payee.$(OBJEXT) \
hb-group.$(OBJEXT) hb-preferences.$(OBJEXT) \
hb-pref-data.$(OBJEXT) hb-report.$(OBJEXT) hb-tag.$(OBJEXT) \
hb-split.$(OBJEXT) hbtk-decimalentry.$(OBJEXT) \
hb-transaction.$(OBJEXT) hb-xml.$(OBJEXT) \
hbtk-switcher.$(OBJEXT) homebank.$(OBJEXT) \
hub-account.$(OBJEXT) hub-reptime.$(OBJEXT) \
hub-reptotal.$(OBJEXT) hub-scheduled.$(OBJEXT) \
hub-transaction.$(OBJEXT) language.$(OBJEXT) \
list-account.$(OBJEXT) list-operation.$(OBJEXT) \
list-report.$(OBJEXT) list-scheduled.$(OBJEXT) \
rep-balance.$(OBJEXT) rep-budget.$(OBJEXT) rep-stats.$(OBJEXT) \
rep-time.$(OBJEXT) rep-vehicle.$(OBJEXT) ui-account.$(OBJEXT) \
ui-archive.$(OBJEXT) ui-assign.$(OBJEXT) \
ui-assist-import.$(OBJEXT) ui-assist-start.$(OBJEXT) \
ui-budget.$(OBJEXT) ui-budget-tabview.$(OBJEXT) \
ui-category.$(OBJEXT) ui-currency.$(OBJEXT) \
ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) \
ui-flt-widget.$(OBJEXT) ui-hbfile.$(OBJEXT) ui-group.$(OBJEXT) \
ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) ui-tag.$(OBJEXT) \
ui-transaction.$(OBJEXT) ui-txn-multi.$(OBJEXT) \
ui-txn-split.$(OBJEXT) ui-widgets-data.$(OBJEXT) \
ui-widgets.$(OBJEXT)
homebank_OBJECTS = $(am_homebank_OBJECTS)
am__DEPENDENCIES_1 =
homebank_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/dsp-account.Po \
./$(DEPDIR)/dsp-mainwindow.Po ./$(DEPDIR)/gtk-chart-colors.Po \
./$(DEPDIR)/gtk-chart-progress.Po ./$(DEPDIR)/gtk-chart.Po \
./$(DEPDIR)/gtk-dateentry.Po ./$(DEPDIR)/hb-account.Po \
./$(DEPDIR)/hb-archive.Po ./$(DEPDIR)/hb-assign.Po \
./$(DEPDIR)/hb-category.Po ./$(DEPDIR)/hb-currency.Po \
./$(DEPDIR)/hb-encoding.Po ./$(DEPDIR)/hb-export.Po \
./$(DEPDIR)/hb-filter.Po ./$(DEPDIR)/hb-group.Po \
./$(DEPDIR)/hb-hbfile.Po ./$(DEPDIR)/hb-import-csv.Po \
./$(DEPDIR)/hb-import-ofx.Po ./$(DEPDIR)/hb-import-qif.Po \
./$(DEPDIR)/hb-import.Po ./$(DEPDIR)/hb-misc.Po \
./$(DEPDIR)/hb-payee.Po ./$(DEPDIR)/hb-pref-data.Po \
./$(DEPDIR)/hb-preferences.Po ./$(DEPDIR)/hb-report.Po \
./$(DEPDIR)/hb-split.Po ./$(DEPDIR)/hb-tag.Po \
./$(DEPDIR)/hb-transaction.Po ./$(DEPDIR)/hb-xml.Po \
./$(DEPDIR)/hbtk-decimalentry.Po ./$(DEPDIR)/hbtk-switcher.Po \
./$(DEPDIR)/homebank.Po ./$(DEPDIR)/hub-account.Po \
./$(DEPDIR)/hub-reptime.Po ./$(DEPDIR)/hub-reptotal.Po \
./$(DEPDIR)/hub-scheduled.Po ./$(DEPDIR)/hub-transaction.Po \
./$(DEPDIR)/language.Po ./$(DEPDIR)/list-account.Po \
./$(DEPDIR)/list-operation.Po ./$(DEPDIR)/list-report.Po \
./$(DEPDIR)/list-scheduled.Po ./$(DEPDIR)/rep-balance.Po \
./$(DEPDIR)/rep-budget.Po ./$(DEPDIR)/rep-stats.Po \
./$(DEPDIR)/rep-time.Po ./$(DEPDIR)/rep-vehicle.Po \
./$(DEPDIR)/ui-account.Po ./$(DEPDIR)/ui-archive.Po \
./$(DEPDIR)/ui-assign.Po ./$(DEPDIR)/ui-assist-import.Po \
./$(DEPDIR)/ui-assist-start.Po \
./$(DEPDIR)/ui-budget-tabview.Po ./$(DEPDIR)/ui-budget.Po \
./$(DEPDIR)/ui-category.Po ./$(DEPDIR)/ui-currency.Po \
./$(DEPDIR)/ui-dialogs.Po ./$(DEPDIR)/ui-filter.Po \
./$(DEPDIR)/ui-flt-widget.Po ./$(DEPDIR)/ui-group.Po \
./$(DEPDIR)/ui-hbfile.Po ./$(DEPDIR)/ui-payee.Po \
./$(DEPDIR)/ui-pref.Po ./$(DEPDIR)/ui-tag.Po \
./$(DEPDIR)/ui-transaction.Po ./$(DEPDIR)/ui-txn-multi.Po \
./$(DEPDIR)/ui-txn-split.Po ./$(DEPDIR)/ui-widgets-data.Po \
./$(DEPDIR)/ui-widgets.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(homebank_SOURCES)
DIST_SOURCES = $(homebank_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
ALL_LINGUAS = @ALL_LINGUAS@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CATALOGS = @CATALOGS@
CATOBJEXT = @CATOBJEXT@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CYGPATH_W = @CYGPATH_W@
DATADIRNAME = @DATADIRNAME@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DEPS_CFLAGS = @DEPS_CFLAGS@
DEPS_LIBS = @DEPS_LIBS@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
ETAGS = @ETAGS@
EXEEXT = @EXEEXT@
GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
GMOFILES = @GMOFILES@
GMSGFMT = @GMSGFMT@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
INSTOBJEXT = @INSTOBJEXT@
INTLLIBS = @INTLLIBS@
INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
INTLTOOL_MERGE = @INTLTOOL_MERGE@
INTLTOOL_PERL = @INTLTOOL_PERL@
INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
LIBSOUP_LIBS = @LIBSOUP_LIBS@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
MKINSTALLDIRS = @MKINSTALLDIRS@
MSGFMT = @MSGFMT@
MSGFMT_OPTS = @MSGFMT_OPTS@
MSGMERGE = @MSGMERGE@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
POFILES = @POFILES@
POSUB = @POSUB@
POW_LIB = @POW_LIB@
PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
USE_NLS = @USE_NLS@
VERSION = @VERSION@
XGETTEXT = @XGETTEXT@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
am__xargs_n = @am__xargs_n@
bindir = @bindir@
build_alias = @build_alias@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host_alias = @host_alias@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
intltool__v_merge_options_ = @intltool__v_merge_options_@
intltool__v_merge_options_0 = @intltool__v_merge_options_0@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
common_defines = \
-DSHARE_DIR=\""$(pkgdatadir)"\" \
-DDATA_DIR=\""$(datadir)"\"
HOMEBANK_CORE =
USER_INTERFACE =
homebank_SOURCES = \
dsp-account.c \
dsp-account.h \
dsp-mainwindow.c \
dsp-mainwindow.h \
enums.h \
hb-types.h \
gtk-chart.c \
gtk-chart.h \
gtk-chart-colors.c \
gtk-chart-colors.h \
gtk-chart-progress.c \
gtk-chart-progress.h \
gtk-dateentry.c \
gtk-dateentry.h \
hb-account.c \
hb-account.h \
hb-archive.c \
hb-archive.h \
hb-assign.c \
hb-assign.h \
hb-category.c \
hb-category.h \
hb-currency.c \
hb-currency.h \
hb-encoding.c \
hb-encoding.h \
hb-export.c \
hb-export.h \
hb-filter.c \
hb-filter.h \
hb-hbfile.c \
hb-hbfile.h \
hb-import.c \
hb-import.h \
hb-import-ofx.c \
hb-import-qif.c \
hb-import-csv.c \
hb-misc.c \
hb-misc.h \
hb-payee.c \
hb-payee.h \
hb-group.c \
hb-group.h \
hb-preferences.c \
hb-preferences.h \
hb-pref-data.c \
hb-pref-data.h \
hb-report.c \
hb-report.h \
hb-tag.c \
hb-tag.h \
hb-split.c \
hb-split.h \
hbtk-decimalentry.c \
hbtk-decimalentry.h \
hb-transaction.c \
hb-transaction.h \
hb-xml.c \
hb-xml.h \
hbtk-switcher.c \
hbtk-switcher.h \
homebank.c \
homebank.h \
icon-names.h \
hub-account.c \
hub-account.h \
hub-reptime.c \
hub-reptime.h \
hub-reptotal.c \
hub-reptotal.h \
hub-scheduled.c \
hub-scheduled.h \
hub-transaction.c \
hub-transaction.h \
language.c \
language.h \
list-account.c \
list-account.h \
list-operation.c \
list-operation.h \
list-report.c \
list-report.h \
list-scheduled.c \
list-scheduled.h \
rep-balance.c \
rep-balance.h \
rep-budget.c \
rep-budget.h \
rep-stats.c \
rep-stats.h \
rep-time.c \
rep-time.h \
rep-vehicle.c \
rep-vehicle.h \
ui-account.c \
ui-account.h \
ui-archive.c \
ui-archive.h \
ui-assign.c \
ui-assign.h \
ui-assist-import.c \
ui-assist-import.h \
ui-assist-start.c \
ui-assist-start.h \
ui-budget.c \
ui-budget.h \
ui-budget-tabview.c \
ui-budget-tabview.h \
ui-category.c \
ui-category.h \
ui-currency.c \
ui-currency.h \
ui-dialogs.c \
ui-dialogs.h \
ui-filter.c \
ui-filter.h \
ui-flt-widget.c \
ui-flt-widget.h \
ui-hbfile.c \
ui-hbfile.h \
ui-group.c \
ui-group.h \
ui-payee.c \
ui-payee.h \
ui-pref.c \
ui-pref.h \
ui-tag.c \
ui-tag.h \
ui-transaction.c \
ui-transaction.h \
ui-txn-multi.c \
ui-txn-multi.h \
ui-txn-split.c \
ui-txn-split.h \
ui-widgets-data.c \
ui-widgets.c \
ui-widgets.h
homebank_LDADD = $(DEPS_LIBS) \
$(LIBSOUP_LIBS)
AM_CPPFLAGS = \
$(DEPS_CFLAGS) \
$(LIBSOUP_CFLAGS) \
$(common_defines)
all: all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
} \
; done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && $(am__rm_f) $$files
clean-binPROGRAMS:
-$(am__rm_f) $(bin_PROGRAMS)
homebank$(EXEEXT): $(homebank_OBJECTS) $(homebank_DEPENDENCIES) $(EXTRA_homebank_DEPENDENCIES)
@rm -f homebank$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(homebank_OBJECTS) $(homebank_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-account.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-mainwindow.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-progress.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-dateentry.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-account.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-archive.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-assign.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-category.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-currency.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-encoding.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-export.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-filter.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-group.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-hbfile.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-csv.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-ofx.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-qif.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-misc.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-payee.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-pref-data.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-preferences.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-report.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-split.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-tag.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-transaction.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-xml.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hbtk-decimalentry.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hbtk-switcher.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/homebank.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hub-account.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hub-reptime.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hub-reptotal.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hub-scheduled.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hub-transaction.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/language.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list-account.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list-operation.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list-report.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list-scheduled.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep-balance.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep-budget.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep-stats.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep-time.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep-vehicle.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-account.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-archive.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assign.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-import.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-start.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget-tabview.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-category.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-currency.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-dialogs.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-filter.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-flt-widget.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-group.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-hbfile.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-payee.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-pref.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-tag.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-transaction.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-txn-multi.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-txn-split.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-widgets-data.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-widgets.Po@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@: >>$@
am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
distclean: distclean-am
-rm -f ./$(DEPDIR)/dsp-account.Po
-rm -f ./$(DEPDIR)/dsp-mainwindow.Po
-rm -f ./$(DEPDIR)/gtk-chart-colors.Po
-rm -f ./$(DEPDIR)/gtk-chart-progress.Po
-rm -f ./$(DEPDIR)/gtk-chart.Po
-rm -f ./$(DEPDIR)/gtk-dateentry.Po
-rm -f ./$(DEPDIR)/hb-account.Po
-rm -f ./$(DEPDIR)/hb-archive.Po
-rm -f ./$(DEPDIR)/hb-assign.Po
-rm -f ./$(DEPDIR)/hb-category.Po
-rm -f ./$(DEPDIR)/hb-currency.Po
-rm -f ./$(DEPDIR)/hb-encoding.Po
-rm -f ./$(DEPDIR)/hb-export.Po
-rm -f ./$(DEPDIR)/hb-filter.Po
-rm -f ./$(DEPDIR)/hb-group.Po
-rm -f ./$(DEPDIR)/hb-hbfile.Po
-rm -f ./$(DEPDIR)/hb-import-csv.Po
-rm -f ./$(DEPDIR)/hb-import-ofx.Po
-rm -f ./$(DEPDIR)/hb-import-qif.Po
-rm -f ./$(DEPDIR)/hb-import.Po
-rm -f ./$(DEPDIR)/hb-misc.Po
-rm -f ./$(DEPDIR)/hb-payee.Po
-rm -f ./$(DEPDIR)/hb-pref-data.Po
-rm -f ./$(DEPDIR)/hb-preferences.Po
-rm -f ./$(DEPDIR)/hb-report.Po
-rm -f ./$(DEPDIR)/hb-split.Po
-rm -f ./$(DEPDIR)/hb-tag.Po
-rm -f ./$(DEPDIR)/hb-transaction.Po
-rm -f ./$(DEPDIR)/hb-xml.Po
-rm -f ./$(DEPDIR)/hbtk-decimalentry.Po
-rm -f ./$(DEPDIR)/hbtk-switcher.Po
-rm -f ./$(DEPDIR)/homebank.Po
-rm -f ./$(DEPDIR)/hub-account.Po
-rm -f ./$(DEPDIR)/hub-reptime.Po
-rm -f ./$(DEPDIR)/hub-reptotal.Po
-rm -f ./$(DEPDIR)/hub-scheduled.Po
-rm -f ./$(DEPDIR)/hub-transaction.Po
-rm -f ./$(DEPDIR)/language.Po
-rm -f ./$(DEPDIR)/list-account.Po
-rm -f ./$(DEPDIR)/list-operation.Po
-rm -f ./$(DEPDIR)/list-report.Po
-rm -f ./$(DEPDIR)/list-scheduled.Po
-rm -f ./$(DEPDIR)/rep-balance.Po
-rm -f ./$(DEPDIR)/rep-budget.Po
-rm -f ./$(DEPDIR)/rep-stats.Po
-rm -f ./$(DEPDIR)/rep-time.Po
-rm -f ./$(DEPDIR)/rep-vehicle.Po
-rm -f ./$(DEPDIR)/ui-account.Po
-rm -f ./$(DEPDIR)/ui-archive.Po
-rm -f ./$(DEPDIR)/ui-assign.Po
-rm -f ./$(DEPDIR)/ui-assist-import.Po
-rm -f ./$(DEPDIR)/ui-assist-start.Po
-rm -f ./$(DEPDIR)/ui-budget-tabview.Po
-rm -f ./$(DEPDIR)/ui-budget.Po
-rm -f ./$(DEPDIR)/ui-category.Po
-rm -f ./$(DEPDIR)/ui-currency.Po
-rm -f ./$(DEPDIR)/ui-dialogs.Po
-rm -f ./$(DEPDIR)/ui-filter.Po
-rm -f ./$(DEPDIR)/ui-flt-widget.Po
-rm -f ./$(DEPDIR)/ui-group.Po
-rm -f ./$(DEPDIR)/ui-hbfile.Po
-rm -f ./$(DEPDIR)/ui-payee.Po
-rm -f ./$(DEPDIR)/ui-pref.Po
-rm -f ./$(DEPDIR)/ui-tag.Po
-rm -f ./$(DEPDIR)/ui-transaction.Po
-rm -f ./$(DEPDIR)/ui-txn-multi.Po
-rm -f ./$(DEPDIR)/ui-txn-split.Po
-rm -f ./$(DEPDIR)/ui-widgets-data.Po
-rm -f ./$(DEPDIR)/ui-widgets.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-binPROGRAMS
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/dsp-account.Po
-rm -f ./$(DEPDIR)/dsp-mainwindow.Po
-rm -f ./$(DEPDIR)/gtk-chart-colors.Po
-rm -f ./$(DEPDIR)/gtk-chart-progress.Po
-rm -f ./$(DEPDIR)/gtk-chart.Po
-rm -f ./$(DEPDIR)/gtk-dateentry.Po
-rm -f ./$(DEPDIR)/hb-account.Po
-rm -f ./$(DEPDIR)/hb-archive.Po
-rm -f ./$(DEPDIR)/hb-assign.Po
-rm -f ./$(DEPDIR)/hb-category.Po
-rm -f ./$(DEPDIR)/hb-currency.Po
-rm -f ./$(DEPDIR)/hb-encoding.Po
-rm -f ./$(DEPDIR)/hb-export.Po
-rm -f ./$(DEPDIR)/hb-filter.Po
-rm -f ./$(DEPDIR)/hb-group.Po
-rm -f ./$(DEPDIR)/hb-hbfile.Po
-rm -f ./$(DEPDIR)/hb-import-csv.Po
-rm -f ./$(DEPDIR)/hb-import-ofx.Po
-rm -f ./$(DEPDIR)/hb-import-qif.Po
-rm -f ./$(DEPDIR)/hb-import.Po
-rm -f ./$(DEPDIR)/hb-misc.Po
-rm -f ./$(DEPDIR)/hb-payee.Po
-rm -f ./$(DEPDIR)/hb-pref-data.Po
-rm -f ./$(DEPDIR)/hb-preferences.Po
-rm -f ./$(DEPDIR)/hb-report.Po
-rm -f ./$(DEPDIR)/hb-split.Po
-rm -f ./$(DEPDIR)/hb-tag.Po
-rm -f ./$(DEPDIR)/hb-transaction.Po
-rm -f ./$(DEPDIR)/hb-xml.Po
-rm -f ./$(DEPDIR)/hbtk-decimalentry.Po
-rm -f ./$(DEPDIR)/hbtk-switcher.Po
-rm -f ./$(DEPDIR)/homebank.Po
-rm -f ./$(DEPDIR)/hub-account.Po
-rm -f ./$(DEPDIR)/hub-reptime.Po
-rm -f ./$(DEPDIR)/hub-reptotal.Po
-rm -f ./$(DEPDIR)/hub-scheduled.Po
-rm -f ./$(DEPDIR)/hub-transaction.Po
-rm -f ./$(DEPDIR)/language.Po
-rm -f ./$(DEPDIR)/list-account.Po
-rm -f ./$(DEPDIR)/list-operation.Po
-rm -f ./$(DEPDIR)/list-report.Po
-rm -f ./$(DEPDIR)/list-scheduled.Po
-rm -f ./$(DEPDIR)/rep-balance.Po
-rm -f ./$(DEPDIR)/rep-budget.Po
-rm -f ./$(DEPDIR)/rep-stats.Po
-rm -f ./$(DEPDIR)/rep-time.Po
-rm -f ./$(DEPDIR)/rep-vehicle.Po
-rm -f ./$(DEPDIR)/ui-account.Po
-rm -f ./$(DEPDIR)/ui-archive.Po
-rm -f ./$(DEPDIR)/ui-assign.Po
-rm -f ./$(DEPDIR)/ui-assist-import.Po
-rm -f ./$(DEPDIR)/ui-assist-start.Po
-rm -f ./$(DEPDIR)/ui-budget-tabview.Po
-rm -f ./$(DEPDIR)/ui-budget.Po
-rm -f ./$(DEPDIR)/ui-category.Po
-rm -f ./$(DEPDIR)/ui-currency.Po
-rm -f ./$(DEPDIR)/ui-dialogs.Po
-rm -f ./$(DEPDIR)/ui-filter.Po
-rm -f ./$(DEPDIR)/ui-flt-widget.Po
-rm -f ./$(DEPDIR)/ui-group.Po
-rm -f ./$(DEPDIR)/ui-hbfile.Po
-rm -f ./$(DEPDIR)/ui-payee.Po
-rm -f ./$(DEPDIR)/ui-pref.Po
-rm -f ./$(DEPDIR)/ui-tag.Po
-rm -f ./$(DEPDIR)/ui-transaction.Po
-rm -f ./$(DEPDIR)/ui-txn-multi.Po
-rm -f ./$(DEPDIR)/ui-txn-split.Po
-rm -f ./$(DEPDIR)/ui-widgets-data.Po
-rm -f ./$(DEPDIR)/ui-widgets.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
distclean distclean-compile distclean-generic distclean-tags \
distdir dvi dvi-am html html-am info info-am install \
install-am install-binPROGRAMS install-data install-data-am \
install-dvi install-dvi-am install-exec install-exec-am \
install-html install-html-am install-info install-info-am \
install-man install-pdf install-pdf-am install-ps \
install-ps-am install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
ps ps-am tags tags-am uninstall uninstall-am \
uninstall-binPROGRAMS
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
# Tell GNU make to disable its built-in pattern rules.
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
homebank-5.9.7/src/ui-budget-tabview.c 0000644 0001750 0001750 00000305063 14736461407 017114 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 2018-2019 Adrien Dorsaz
* Copyright (C) 2019-2023 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "dsp-mainwindow.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "hbtk-switcher.h"
#include "ui-budget-tabview.h"
/****************************************************************************/
/* Implementation notes */
/****************************************************************************/
/*
* This dialog allows user to manage its budget within a GtkTreeView.
*
* The view rows are separated in three main tree roots:
* - Income: contains all Homebank categories of income type (see GF_INCOME)
* - Expense: contains all Homebank categories of expense type
* - Total: contains 3 sub-rows:
* - Income: sum all amounts of the Income root
* - Expense: sum all amounts of the Expense root
* - Summary: difference between the two above sub-rows
*
* The view columns contain:
* - Category: Homebank categories organised in hierarchy
* according to the main tree roots above and the categories hierarchy
*
* - Annual Total: sum all amounts of the year for the category
*
* - Monthly Average: average of the amounts for the category
*
* - Monthly: set the monthly amount when the Same flag is active
* - That column contains a toggle check box to enable or not monthly values
* Check it to disable the GF_CUSTOM flag of Homebank categories
* "Does this category has same amount planned every month ?"
*
* - 12 columns for each month of the year containing their specific amount
*
* The dialog shows 3 radio buttons on top to choose between 3 viewing modes:
* - Summary: show Homebank categories with budget set or set with GF_FORCED
* - Expense: show all available Homebank categories of expense type
* - Income: show all available Homebank categories of income type
*
*/
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* Global data */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static gchar *UI_BUD_TABVIEW_MONTHS[] = {
N_("Jan"), N_("Feb"), N_("Mar"),
N_("Apr"), N_("May"), N_("Jun"),
N_("Jul"), N_("Aug"), N_("Sept"),
N_("Oct"), N_("Nov"), N_("Dec"),
NULL};
/* The different view mode available */
static gchar *UI_BUD_TABVIEW_VIEW_MODE[] = {
N_("Summary"),
N_("Expense"),
N_("Income"),
NULL
};
/* These values has to correspond to UI_BUD_TABVIEW_VIEW_MODE[] */
enum ui_bud_tabview_view_mode
{
UI_BUD_TABVIEW_VIEW_SUMMARY = 0,
UI_BUD_TABVIEW_VIEW_EXPENSE,
UI_BUD_TABVIEW_VIEW_INCOME
};
typedef enum ui_bud_tabview_view_mode ui_bud_tabview_view_mode_t;
/* These values corresponds to the return of category_type_get from hb-category */
enum ui_bud_tabview_cat_type
{
UI_BUD_TABVIEW_CAT_TYPE_EXPENSE = -1,
UI_BUD_TABVIEW_CAT_TYPE_NONE = 0, // Not real category type: used to retrieve tree roots
UI_BUD_TABVIEW_CAT_TYPE_INCOME = 1
};
typedef enum ui_bud_tabview_cat_type ui_bud_tabview_cat_type_t;
/* enum for the Budget Tree Store model */
enum ui_bud_tabview_store
{
UI_BUD_TABVIEW_CATEGORY_KEY = 0,
UI_BUD_TABVIEW_CATEGORY_NAME,
UI_BUD_TABVIEW_CATEGORY_FULLNAME,
UI_BUD_TABVIEW_CATEGORY_TYPE,
UI_BUD_TABVIEW_IS_ROOT, // To retrieve easier the 3 main tree roots
UI_BUD_TABVIEW_IS_TOTAL, // To retrieve rows inside the Total root
UI_BUD_TABVIEW_IS_CHILD_HEADER, // The row corresponds to the head child which is shown before the separator
UI_BUD_TABVIEW_IS_SEPARATOR, // Row to just display a separator in Tree View
UI_BUD_TABVIEW_IS_MONITORING_FORCED,
UI_BUD_TABVIEW_IS_SAME_AMOUNT,
UI_BUD_TABVIEW_IS_SUB_CATEGORY,
UI_BUD_TABVIEW_HAS_BUDGET,
UI_BUD_TABVIEW_TOTAL,
UI_BUD_TABVIEW_SAME_AMOUNT,
UI_BUD_TABVIEW_JANUARY,
UI_BUD_TABVIEW_FEBRUARY,
UI_BUD_TABVIEW_MARCH,
UI_BUD_TABVIEW_APRIL,
UI_BUD_TABVIEW_MAY,
UI_BUD_TABVIEW_JUNE,
UI_BUD_TABVIEW_JULY,
UI_BUD_TABVIEW_AUGUST,
UI_BUD_TABVIEW_SEPTEMBER,
UI_BUD_TABVIEW_OCTOBER,
UI_BUD_TABVIEW_NOVEMBER,
UI_BUD_TABVIEW_DECEMBER,
UI_BUD_TABVIEW_NUMBER_COLOMNS
};
typedef enum ui_bud_tabview_store ui_bud_tabview_store_t;
// Retrieve a row iterator according to specific criterias
const struct ui_bud_tabview_search_criteria
{
// Search by non-zero category key
guint32 row_category_key;
// Search by other criterias
ui_bud_tabview_cat_type_t row_category_type;
gboolean row_is_root;
gboolean row_is_total;
// Found iterator, NULL if not found
GtkTreeIter *iterator;
} ui_bud_tabview_search_criteria_default = {0, UI_BUD_TABVIEW_CAT_TYPE_NONE, FALSE, FALSE, NULL} ;
typedef struct ui_bud_tabview_search_criteria ui_bud_tabview_search_criteria_t;
/*
* Local headers
**/
// GtkTreeStore model
static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search);
static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category);
static void ui_bud_tabview_model_collapse (GtkTreeView *view);
static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget);
static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget);
static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
#if HB_BUD_TABVIEW_EDIT_ENABLE
static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
#endif
static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data);
static GtkTreeModel * ui_bud_tabview_model_new ();
// GtkTreeView widget
static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode);
static gboolean ui_bud_tabview_view_search (GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data);
#if HB_BUD_TABVIEW_EDIT_ENABLE
static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
#endif
static void ui_bud_tabview_view_on_select(GtkTreeSelection *treeselection, gpointer user_data);
static GtkWidget *ui_bud_tabview_view_new (gpointer user_data);
// UI actions
#if HB_BUD_TABVIEW_EDIT_ENABLE
static void ui_bud_tabview_cell_update_category(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
#endif
static void ui_bud_tabview_cell_update_amount(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
static void ui_bud_tabview_cell_update_is_same_amount(GtkCellRendererText *renderer, gchar *filter_path, gpointer user_data);
static void ui_bud_tabview_view_update_mode (GtkToggleButton *button, gpointer user_data);
static void ui_bud_tabview_view_expand (GtkButton *button, gpointer user_data);
static void ui_bud_tabview_view_collapse (GtkButton *button, gpointer user_data);
static gboolean ui_bud_tabview_get_selected_category (GtkTreeModel **budget, GtkTreeIter *iter, Category **category, ui_bud_tabview_data_t *data);
#if HB_BUD_TABVIEW_EDIT_ENABLE
static gboolean ui_bud_tabview_get_selected_root_iter (GtkTreeModel **budget, GtkTreeIter *iter, ui_bud_tabview_data_t *data);
static void ui_bud_tabview_category_add_full_filled (GtkWidget *source, gpointer user_data);
static void ui_bud_tabview_category_add (GtkButton *button, gpointer user_data);
static void ui_bud_tabview_category_delete (GtkButton *button, gpointer user_data);
static void ui_bud_tabview_category_merge_full_filled (GtkWidget *source, gpointer user_data);
static void ui_bud_tabview_category_merge (GtkButton *button, gpointer user_data);
#endif
static void ui_bud_tabview_category_reset (GtkButton *button, gpointer user_data);
static gboolean ui_bud_tabview_on_key_press(GtkWidget *widget, GdkEvent *event, gpointer user_data);
static void ui_bud_tabview_dialog_close(ui_bud_tabview_data_t *data, gint response);
/**
* GtkTreeStore model
**/
// Look for category by deterministic characteristics
// Only categories with specific characteristics can be easily found
// like roots, total rows and categories with real key id
// You are responsible to g_free iterator
static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search)
{
guint32 category_key;
ui_bud_tabview_cat_type_t category_type;
gboolean is_found = FALSE, is_root, is_total, is_separator;
search->iterator = NULL;
gtk_tree_model_get (model, iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
-1);
if (search->row_category_key > 0 // Look for iter of real category row
&& category_key == search->row_category_key
&& !(is_total)
&& !(is_root)
)
{
DB(g_print("\tFound row with key %d\n", category_key));
is_found = TRUE;
}
else if (search->row_category_key == 0 // Look for iter of fake category row
&& is_root == search->row_is_root
&& is_total == search->row_is_total
&& category_type == search->row_category_type
&& !is_separator
)
{
DB(g_print("\tFound row with is_root = %d, is_total %d, type = %d\n", is_root, is_total, category_type));
is_found = TRUE;
}
// If found, save result to struct
if (is_found)
{
search->iterator = g_malloc0(sizeof(GtkTreeIter));
*search->iterator = *iter;
}
return is_found;
}
/* Recursive function which add a new row in the budget model with all its ancestors */
static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category)
{
GtkTreeIter child;
GtkTreeIter *parent;
Category *bdg_category;
gboolean cat_is_same_amount;
ui_bud_tabview_search_criteria_t parent_search = ui_bud_tabview_search_criteria_default;
bdg_category = da_cat_get(*key_category);
if (bdg_category == NULL)
{
return;
}
cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
/* Check if parent category already exists */
parent_search.row_category_key = bdg_category->parent;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&parent_search);
if (bdg_category->parent == 0)
{
// If we are one of the oldest parent, stop recursion
gtk_tree_store_insert (
budget,
&child,
balanceIter,
-1);
}
else
{
if (parent_search.iterator)
{
DB(g_print(" Recursion optimisation: parent key %d already exists\n", parent_search.row_category_key));
// If parent already exists, stop recursion
parent = parent_search.iterator;
}
else
{
// Parent has not been found, ask to create it first
ui_bud_tabview_model_add_category_with_lineage(budget, balanceIter, &(bdg_category->parent));
// Now, we are sure parent exists, look for it again
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&parent_search);
parent = parent_search.iterator;
}
gtk_tree_store_insert (
budget,
&child,
parent,
-1);
}
DB(g_print(" >insert '%s'\n", bdg_category->fullname));
gtk_tree_store_set(
budget,
&child,
UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->typename,
UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
UI_BUD_TABVIEW_IS_MONITORING_FORCED, (bdg_category->flags & GF_FORCED),
UI_BUD_TABVIEW_IS_ROOT, FALSE,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, cat_is_same_amount,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
UI_BUD_TABVIEW_IS_SUB_CATEGORY, bdg_category->parent != 0,
UI_BUD_TABVIEW_HAS_BUDGET, (bdg_category->flags & GF_BUDGET),
UI_BUD_TABVIEW_SAME_AMOUNT, bdg_category->budget[0],
UI_BUD_TABVIEW_JANUARY, bdg_category->budget[1],
UI_BUD_TABVIEW_FEBRUARY, bdg_category->budget[2],
UI_BUD_TABVIEW_MARCH, bdg_category->budget[3],
UI_BUD_TABVIEW_APRIL, bdg_category->budget[4],
UI_BUD_TABVIEW_MAY, bdg_category->budget[5],
UI_BUD_TABVIEW_JUNE, bdg_category->budget[6],
UI_BUD_TABVIEW_JULY, bdg_category->budget[7],
UI_BUD_TABVIEW_AUGUST, bdg_category->budget[8],
UI_BUD_TABVIEW_SEPTEMBER, bdg_category->budget[9],
UI_BUD_TABVIEW_OCTOBER, bdg_category->budget[10],
UI_BUD_TABVIEW_NOVEMBER, bdg_category->budget[11],
UI_BUD_TABVIEW_DECEMBER, bdg_category->budget[12],
-1);
// Always add child header and separator
parent = gtk_tree_iter_copy(&child);
gtk_tree_store_insert_with_values(
budget,
&child,
parent,
-1,
UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
-1);
gtk_tree_store_insert_with_values(
budget,
&child,
parent,
-1,
UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
-1);
gtk_tree_iter_free(parent);
g_free(parent_search.iterator);
return;
}
// Collapse all categories except root
static void ui_bud_tabview_model_collapse (GtkTreeView *view)
{
GtkTreeModel *budget;
GtkTreePath *path;
ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
budget = gtk_tree_view_get_model (view);
gtk_tree_view_collapse_all(view);
// Keep root categories expanded
// Retrieve income root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator != NULL)
{
path = gtk_tree_model_get_path(budget, root_search.iterator);
gtk_tree_view_expand_row(view, path, FALSE);
}
// Retrieve expense root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator != NULL)
{
path = gtk_tree_model_get_path(budget, root_search.iterator);
gtk_tree_view_expand_row(view, path, FALSE);
}
// Retrieve total root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator != NULL)
{
path = gtk_tree_model_get_path(budget, root_search.iterator);
gtk_tree_view_expand_row(view, path, FALSE);
}
g_free(root_search.iterator);
return;
}
// Create tree roots for the store
static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget)
{
GtkTreeIter iter, root;
gtk_tree_store_insert_with_values (
budget,
&root,
NULL,
-1,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
UI_BUD_TABVIEW_IS_ROOT, TRUE,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
-1);
// For add category dialog: copy of the root to be able to select it
gtk_tree_store_insert_with_values (
budget,
&iter,
&root,
-1,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
UI_BUD_TABVIEW_CATEGORY_KEY, 0,
UI_BUD_TABVIEW_IS_ROOT, FALSE,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
-1);
// For add category dialog: add a separator to distinguish root with children
gtk_tree_store_insert_with_values (
budget,
&iter,
&root,
-1,
UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
-1);
gtk_tree_store_insert_with_values (
budget,
&root,
NULL,
-1,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
UI_BUD_TABVIEW_IS_ROOT, TRUE,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
-1);
// For add category dialog: copy of the root to be able to select it
gtk_tree_store_insert_with_values (
budget,
&iter,
&root,
-1,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
UI_BUD_TABVIEW_CATEGORY_KEY, 0,
UI_BUD_TABVIEW_IS_ROOT, FALSE,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
-1);
// For add category dialog: add a separator to distinguish root with children
gtk_tree_store_insert_with_values (
budget,
&iter,
&root,
-1,
UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
-1);
gtk_tree_store_insert_with_values (
budget,
&root,
NULL,
-1,
UI_BUD_TABVIEW_CATEGORY_NAME, _("Totals"),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _("Totals"),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
UI_BUD_TABVIEW_IS_ROOT, TRUE,
UI_BUD_TABVIEW_IS_TOTAL, FALSE,
-1);
return;
}
static gdouble test_sum(Category *catitem, gdouble *total_tab)
{
gdouble totalcat = 0.0;
for(gint j=1;j<=12;j++)
{
if(!(catitem->flags & GF_CUSTOM))
{
total_tab[j] += catitem->budget[0];
totalcat += catitem->budget[0];
}
else
{
total_tab[j] += catitem->budget[j];
totalcat += catitem->budget[j];
}
}
total_tab[0] += totalcat;
return totalcat;
}
static void test_total_sum(GtkTreeStore *budget, GtkTreeIter *root, gdouble *total_tab)
{
GtkTreeIter iter, child;
gboolean valid, cvalid, cheader, sep;
guint32 key, ckey;
//valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(budget), &iter);
valid = gtk_tree_model_iter_children (GTK_TREE_MODEL(budget), &iter, root);
while (valid)
{
Category *catitem;
gtk_tree_model_get(GTK_TREE_MODEL(budget), &iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &key,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &cheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &sep,
-1);
if( cheader == FALSE && sep == FALSE )
{
gdouble tmpsum = 0.0;
catitem = da_cat_get(key);
DB( g_print(" iter: %d %s\n", key, catitem->name) );
tmpsum += test_sum(catitem, total_tab);
// children ?
cvalid = gtk_tree_model_iter_children (GTK_TREE_MODEL(budget), &child, &iter);
while (cvalid)
{
gtk_tree_model_get(GTK_TREE_MODEL(budget), &child,
UI_BUD_TABVIEW_CATEGORY_KEY, &ckey,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &cheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &sep,
-1);
if( cheader == FALSE && sep == FALSE )
{
gdouble cbudget;
catitem = da_cat_get(ckey);
DB( g_print(" child: %d %s\n", ckey, catitem->name) );
cbudget = test_sum(catitem, total_tab);
tmpsum += cbudget;
gtk_tree_store_set (
budget,
&child,
UI_BUD_TABVIEW_TOTAL, cbudget,
-1);
}
cvalid = gtk_tree_model_iter_next(GTK_TREE_MODEL(budget), &child);
}
gtk_tree_store_set (
budget,
&iter,
UI_BUD_TABVIEW_TOTAL, tmpsum,
-1);
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(budget), &iter);
}
}
// Update (or insert) total rows for a budget according to the view mode
// This function will is used to initiate model and to refresh it after change by user
static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget)
{
ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
GtkTreeIter total_root, child;
double total_income[13] = {0}, total_expense[13] = {0};
// Retrieve required root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
//#2036404 compute total
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
test_total_sum(budget, root_search.iterator, total_income);
//#2036404 compute total
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
test_total_sum(budget, root_search.iterator, total_expense);
// Retrieve total root and insert required total rows
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (!root_search.iterator)
{
return;
}
total_root = *root_search.iterator;
// Retrieve and set totals
root_search.row_is_root = FALSE;
root_search.row_is_total = TRUE;
// First, look for Incomes
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator)
{
child = *root_search.iterator;
}
else
{
gtk_tree_store_insert(budget, &child, &total_root, -1);
}
gtk_tree_store_set (
budget,
&child,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
UI_BUD_TABVIEW_IS_TOTAL, TRUE,
UI_BUD_TABVIEW_TOTAL, total_income[0],
UI_BUD_TABVIEW_JANUARY, total_income[1],
UI_BUD_TABVIEW_FEBRUARY, total_income[2],
UI_BUD_TABVIEW_MARCH, total_income[3],
UI_BUD_TABVIEW_APRIL, total_income[4],
UI_BUD_TABVIEW_MAY, total_income[5],
UI_BUD_TABVIEW_JUNE, total_income[6],
UI_BUD_TABVIEW_JULY, total_income[7],
UI_BUD_TABVIEW_AUGUST, total_income[8],
UI_BUD_TABVIEW_SEPTEMBER, total_income[9],
UI_BUD_TABVIEW_OCTOBER, total_income[10],
UI_BUD_TABVIEW_NOVEMBER, total_income[11],
UI_BUD_TABVIEW_DECEMBER, total_income[12],
-1);
// Then look for Expenses
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator)
{
child = *root_search.iterator;
}
else
{
gtk_tree_store_insert(budget, &child, &total_root, -1);
}
gtk_tree_store_set (
budget,
&child,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
UI_BUD_TABVIEW_IS_TOTAL, TRUE,
UI_BUD_TABVIEW_TOTAL, total_expense[0],
UI_BUD_TABVIEW_JANUARY, total_expense[1],
UI_BUD_TABVIEW_FEBRUARY, total_expense[2],
UI_BUD_TABVIEW_MARCH, total_expense[3],
UI_BUD_TABVIEW_APRIL, total_expense[4],
UI_BUD_TABVIEW_MAY, total_expense[5],
UI_BUD_TABVIEW_JUNE, total_expense[6],
UI_BUD_TABVIEW_JULY, total_expense[7],
UI_BUD_TABVIEW_AUGUST, total_expense[8],
UI_BUD_TABVIEW_SEPTEMBER, total_expense[9],
UI_BUD_TABVIEW_OCTOBER, total_expense[10],
UI_BUD_TABVIEW_NOVEMBER, total_expense[11],
UI_BUD_TABVIEW_DECEMBER, total_expense[12],
-1);
// Finally, set Balance total row
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (root_search.iterator)
{
child = *root_search.iterator;
}
else
{
gtk_tree_store_insert(budget, &child, &total_root, -1);
}
gtk_tree_store_set (
budget,
&child,
UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
UI_BUD_TABVIEW_IS_TOTAL, TRUE,
UI_BUD_TABVIEW_TOTAL, total_income[0] + total_expense[0],
UI_BUD_TABVIEW_JANUARY, total_income[1] + total_expense[1],
UI_BUD_TABVIEW_FEBRUARY, total_income[2] + total_expense[2],
UI_BUD_TABVIEW_MARCH, total_income[3] + total_expense[3],
UI_BUD_TABVIEW_APRIL, total_income[4] + total_expense[4],
UI_BUD_TABVIEW_MAY, total_income[5] + total_expense[5],
UI_BUD_TABVIEW_JUNE, total_income[6] + total_expense[6],
UI_BUD_TABVIEW_JULY, total_income[7] + total_expense[7],
UI_BUD_TABVIEW_AUGUST, total_income[8] + total_expense[8],
UI_BUD_TABVIEW_SEPTEMBER, total_income[9] + total_expense[9],
UI_BUD_TABVIEW_OCTOBER, total_income[10] + total_expense[10],
UI_BUD_TABVIEW_NOVEMBER, total_income[11] + total_expense[11],
UI_BUD_TABVIEW_DECEMBER, total_income[12] + total_expense[12],
-1);
g_free(root_search.iterator);
return;
}
// Filter shown rows according to VIEW mode
static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gboolean is_visible, is_root, is_total, is_separator, is_childheader;
ui_bud_tabview_data_t* data;
ui_bud_tabview_view_mode_t view_mode;
guint32 category_key;
ui_bud_tabview_cat_type_t category_type;
Category *bdg_category;
is_visible = TRUE;
data = user_data;
view_mode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
-1);
// On specific mode, hide categories of opposite type
if (!is_total
&& category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME
&& view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE)
{
is_visible = FALSE;
}
if (!is_total
&& category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE
&& view_mode == UI_BUD_TABVIEW_VIEW_INCOME)
{
is_visible = FALSE;
}
// Hide fake first child root used for add dialog
if (is_childheader
|| is_separator)
{
is_visible = FALSE;
}
// On balance mode, hide not forced empty categories
if (!is_total
&& !is_root
&& !is_childheader
&& !is_separator
&& view_mode == UI_BUD_TABVIEW_VIEW_SUMMARY)
{
bdg_category = da_cat_get(category_key);
if (bdg_category != NULL)
{
// Either the category has some budget, or its display is forced
is_visible = (bdg_category->flags & (GF_BUDGET|GF_FORCED));
// Force display if one of its children should be displayed
if (!is_visible)
{
GtkTreeIter child;
Category *subcat;
guint32 subcat_key;
gint child_id=0;
while (gtk_tree_model_iter_nth_child(model,
&child,
iter,
child_id))
{
gtk_tree_model_get(model, &child,
UI_BUD_TABVIEW_CATEGORY_KEY, &subcat_key,
-1);
if (subcat_key != 0)
{
subcat = da_cat_get (subcat_key);
if (subcat != NULL)
{
is_visible = (subcat->flags & (GF_BUDGET|GF_FORCED));
}
}
// Stop loop on first visible children
if (is_visible)
{
break;
}
++child_id;
}
}
}
}
return is_visible;
}
#if HB_BUD_TABVIEW_EDIT_ENABLE
// Filter rows to show only parent categories
static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gboolean is_visible, is_root, is_total, is_separator, is_childheader;
Category *bdg_category;
guint32 category_key;
ui_bud_tabview_cat_type_t category_type;
ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
view_mode = hbtk_radio_button_get_active(GTK_CONTAINER(data->RA_mode));
is_visible = TRUE;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
-1);
// Show root according to view_mode
if (is_root)
{
// Always hide total root
if(category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE)
{
is_visible = FALSE;
}
else if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
{
is_visible = FALSE;
}
else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
{
is_visible = FALSE;
}
}
// Hide Total rows
if (is_total) {
is_visible = FALSE;
}
if (category_key > 0 && (is_separator || is_childheader))
{
is_visible = FALSE;
}
else if (category_key > 0)
{
// Hide rows according to currently view mode
if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
{
is_visible = FALSE;
}
else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
{
is_visible = FALSE;
}
else
{
// Show categories without parents
bdg_category = da_cat_get(category_key);
if (bdg_category != NULL)
{
if (bdg_category->parent > 0)
{
is_visible = FALSE;
}
}
}
}
return is_visible;
}
// Filter rows to show only mergeable categories
static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gboolean is_visible, is_root, is_total, is_separator, is_childheader;
guint32 category_key;
ui_bud_tabview_cat_type_t category_type;
is_visible = TRUE;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
-1);
// Hide source merge row
if (data->MERGE_source_category_key == category_key)
{
is_visible = FALSE;
}
// Hide Total root
if (is_root
&& category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE )
{
is_visible = FALSE;
}
// Hide Total rows
if (is_total)
{
is_visible = FALSE;
}
if ((is_separator || is_childheader))
{
GtkTreeIter parent;
gtk_tree_model_iter_parent(model, &parent, iter);
// Show child header and separator if parent has more than 2 children
is_visible = (gtk_tree_model_iter_n_children(model, &parent) > 2);
}
return is_visible;
}
#endif
static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *cat_a, GtkTreeIter *cat_b, gpointer user_data)
{
const gchar* cat_a_name;
const gchar* cat_b_name;
ui_bud_tabview_cat_type_t cat_a_type, cat_b_type;
guint32 cat_a_key, cat_b_key;
gboolean cat_a_is_childheader, cat_a_is_separator, cat_b_is_childheader, cat_b_is_separator;
gint order = 0;
gtk_tree_model_get(model, cat_a,
UI_BUD_TABVIEW_CATEGORY_NAME, &cat_a_name,
UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_a_type,
UI_BUD_TABVIEW_CATEGORY_KEY, &cat_a_key,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_a_is_childheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &cat_a_is_separator,
-1);
gtk_tree_model_get(model, cat_b,
UI_BUD_TABVIEW_CATEGORY_NAME, &cat_b_name,
UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_b_type,
UI_BUD_TABVIEW_CATEGORY_KEY, &cat_b_key,
UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_b_is_childheader,
UI_BUD_TABVIEW_IS_SEPARATOR, &cat_b_is_separator,
-1);
// Sort first by category type
if (cat_a_type != cat_b_type)
{
switch (cat_a_type)
{
case UI_BUD_TABVIEW_CAT_TYPE_INCOME:
order = -1;
break;
case UI_BUD_TABVIEW_CAT_TYPE_EXPENSE:
order = 0;
break;
case UI_BUD_TABVIEW_CAT_TYPE_NONE:
order = 1;
break;
}
}
else
{
// On standard categories, just order by name
if (!cat_a_is_childheader && !cat_a_is_separator
&& !cat_b_is_childheader && !cat_b_is_separator)
{
order = g_utf8_collate(g_utf8_casefold(cat_a_name, -1),
g_utf8_casefold(cat_b_name, -1)
);
}
// Otherwise, fake categories have to be first (header and separator)
else if (cat_a_is_childheader || cat_a_is_separator)
{
if (!cat_b_is_separator && !cat_b_is_childheader)
{
order = -1;
}
// When both are fake, header has to be first
else
{
order = (cat_a_is_childheader ? -1 : 1);
}
}
else
{
// Same idea for fake categories when cat_b is fake, but
// with reversed result, because sort function return
// result according to cat_a
if (!cat_a_is_separator && !cat_a_is_childheader)
{
order = 1;
}
else
{
order = (cat_b_is_childheader ? 1 : -1);
}
}
}
return order;
}
static void ui_bud_tabview_model_populate (GtkTreeStore *budget)
{
GtkTreeIter *iter_income, *iter_expense;
guint32 n_category;
ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
DB( g_print("\n[ui-budget] model populate\n") )
/* Create tree roots */
ui_bud_tabview_model_insert_roots (budget);
// Retrieve required root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
iter_income = root_search.iterator;
root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
iter_expense = root_search.iterator;
/* Create rows for real categories */
n_category = da_cat_get_max_key();
for(guint32 i=1; i<=n_category; ++i)
{
Category *bdg_category;
gboolean cat_is_income;
bdg_category = da_cat_get(i);
if (bdg_category == NULL)
{
continue;
}
cat_is_income = (category_type_get (bdg_category) == 1);
DB(g_print(" category %d:'%s' isincome=%d, issub=%d hasbudget=%d parent=%d\n",
bdg_category->key, bdg_category->name,
cat_is_income, (bdg_category->flags & GF_SUB),
(bdg_category->flags & GF_BUDGET), bdg_category->parent));
// Compute totals and initiate category in right tree root
if (cat_is_income)
{
ui_bud_tabview_model_add_category_with_lineage(budget, iter_income, &(bdg_category->key));
}
else if (!cat_is_income)
{
ui_bud_tabview_model_add_category_with_lineage(budget, iter_expense, &(bdg_category->key));
}
}
/* Create rows for total root */
ui_bud_tabview_model_update_monthly_total(GTK_TREE_STORE(budget));
g_free(root_search.iterator);
}
// the budget model creation
static GtkTreeModel * ui_bud_tabview_model_new ()
{
GtkTreeStore *budget;
// Create Tree Store
budget = gtk_tree_store_new ( UI_BUD_TABVIEW_NUMBER_COLOMNS,
G_TYPE_UINT, // UI_BUD_TABVIEW_CATEGORY_KEY
G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_NAME
G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_FULLNAME
G_TYPE_INT, // UI_BUD_TABVIEW_CATEGORY_TYPE
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_ROOT
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_TOTAL
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_CHILD_HEADER
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SEPARATOR
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_MONITORING_FORCED
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SAME_AMOUNT
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SUB_CATEGORY
G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_HAS_BUDGET
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_TOTAL
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SAME_AMOUNT
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JANUARY
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_FEBRUARY
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MARCH
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_APRIL
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MAY
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JUNE
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JULY
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_AUGUST
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SEPTEMBER
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_OCTOBER
G_TYPE_DOUBLE, // UI_BUD_TABVIEW_NOVEMBER
G_TYPE_DOUBLE // UI_BUD_TABVIEW_DECEMBER
);
// Populate the store
ui_bud_tabview_model_populate(budget);
/* Sort categories on same node level */
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(budget),
UI_BUD_TABVIEW_CATEGORY_NAME, ui_bud_tabview_model_row_sort,
NULL, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (budget),
UI_BUD_TABVIEW_CATEGORY_NAME, GTK_SORT_ASCENDING);
return GTK_TREE_MODEL(budget);
}
/**
* GtkTreeView functions
**/
static void ui_bud_tabview_icon_cell_data_function (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
//ui_bud_tabview_data_t *data = user_data;
gchar *iconname = NULL;
gboolean has_budget, is_monitoring_forced;
//ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_MONITORING_FORCED, &is_monitoring_forced,
UI_BUD_TABVIEW_HAS_BUDGET, &has_budget,
-1);
//view_mode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
//5.3 added
if( is_monitoring_forced )
iconname = ICONNAME_HB_ITEM_FORCED;
else
//if (view_mode != UI_BUD_TABVIEW_VIEW_SUMMARY )
//{
if( has_budget )
iconname = ICONNAME_HB_ITEM_BUDGET;
//}
g_object_set(renderer, "icon-name", iconname, NULL);
}
// Display category name in bold if it has budget
static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
//ui_bud_tabview_data_t *data = user_data;
gboolean has_budget, is_sub_category;
PangoWeight weight = PANGO_WEIGHT_NORMAL;
//ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_SUB_CATEGORY, &is_sub_category,
UI_BUD_TABVIEW_HAS_BUDGET, &has_budget,
-1);
//view_mode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
//if (view_mode != UI_BUD_TABVIEW_VIEW_SUMMARY && has_budget)
if (has_budget)
{
weight = PANGO_WEIGHT_BOLD;
}
g_object_set(renderer,
//"style", is_sub_category ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
"weight", weight,
NULL);
}
// to enable or not edition on month columns
static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GtkAdjustment *adjustment;
gboolean is_same_amount, is_root, is_total, is_visible, is_editable;
ui_bud_tabview_cat_type_t row_category_type;
gdouble amount = 0.0;
gchar *text;
gchar *fgcolor;
const ui_bud_tabview_store_t column_id = GPOINTER_TO_INT(user_data);
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_CATEGORY_TYPE, &row_category_type,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
-1);
// Text to display
if (is_same_amount)
{
gtk_tree_model_get(model, iter, UI_BUD_TABVIEW_SAME_AMOUNT, &amount, -1);
}
else if (column_id >= UI_BUD_TABVIEW_JANUARY && column_id <= UI_BUD_TABVIEW_DECEMBER)
{
gtk_tree_model_get(model, iter, column_id, &amount, -1);
}
text = g_strdup_printf("%.2f", amount);
fgcolor = get_normal_color_amount(amount);
// Default styling values
is_visible = TRUE;
is_editable = FALSE;
if (is_root)
{
is_visible = FALSE;
is_editable = FALSE;
}
else if (is_total)
{
is_visible = TRUE;
is_editable = FALSE;
if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
{
is_visible = FALSE;
}
}
else if (is_same_amount)
{
is_visible = TRUE;
is_editable = FALSE;
if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
{
is_editable = TRUE;
}
}
else if (! is_same_amount)
{
is_visible = TRUE;
is_editable = TRUE;
if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
{
is_editable = FALSE;
}
}
// Finally, visibility depends on set amount
is_visible = (is_visible && (is_editable || amount != 0.0));
adjustment = gtk_adjustment_new(
0.0, // initial-value
-G_MAXDOUBLE, // minmal-value
G_MAXDOUBLE, // maximal-value
0.5, // step increment
10, // page increment
0); // page size (0 because irrelevant for GtkSpinButton)
g_object_set(renderer,
"text", text,
"visible", is_visible,
"editable", is_editable,
"foreground", fgcolor,
"xalign", 1.0f,
"adjustment", adjustment,
"digits", 2,
NULL);
g_free(text);
}
// to enable or not edition on month columns
static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gboolean is_same_amount, is_total, is_root, is_visible, is_sensitive;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
-1);
// Default values
is_visible = TRUE;
is_sensitive = TRUE;
if (is_root || is_total)
{
is_visible = FALSE;
is_sensitive = FALSE;
}
g_object_set(renderer,
"activatable", TRUE,
"active", is_same_amount,
"visible", is_visible,
"sensitive", is_sensitive,
NULL);
}
// Compute dynamically the annual total
static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gboolean is_root = FALSE;
gchar *text;
gchar *fgcolor;
gboolean is_visible = TRUE;
gdouble celltotal;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
//UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
//UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
//UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_TOTAL, &celltotal,
-1);
/*if (is_same_amount)
{
total = 12.0 * amount;
}
else
{
for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
{
gtk_tree_model_get(model, iter, i, &amount, -1);
total += amount;
}
}
text = g_strdup_printf("%.2f // %.2f", total, celltotal);*/
text = g_strdup_printf("%.2f", celltotal);
fgcolor = get_normal_color_amount(celltotal);
if (is_root)
{
is_visible = FALSE;
}
// Finally, visibility depends on set amount
//is_visible = (is_visible && amount != 0.0);
//#1859275 visibility to be tested on total
is_visible = (is_visible && celltotal != 0.0);
g_object_set(renderer,
"text", text,
"foreground", fgcolor,
"visible", is_visible,
"xalign", 1.0f,
NULL);
g_free(text);
}
// Compute monthly average
static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
gdouble amount = 0.0, celltotal;
gdouble average = 0.0;
gchar *text;
gchar *fgcolor;
gboolean is_visible = TRUE;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_TOTAL, &celltotal,
-1);
/*if (is_same_amount)
{
average = amount;
}
else
{
for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
{
gtk_tree_model_get(model, iter, i, &amount, -1);
average += amount;
}
average = hb_amount_round(average / 12.0, 2);
}*/
average = hb_amount_round(celltotal / 12.0, 2);
text = g_strdup_printf("%.2f", average);
fgcolor = get_normal_color_amount(average);
if (is_root)
{
is_visible = FALSE;
}
// Finally, visibility depends on set amount
is_visible = (is_visible && average != 0.0);
g_object_set(renderer,
"text", text,
"foreground", fgcolor,
"visible", is_visible,
"xalign", 1.0f,
NULL);
g_free(text);
}
// When view mode is toggled:
// - recreate the view to update columns rendering
static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode)
{
ui_bud_tabview_data_t *data = user_data;
GtkTreeModel *budget;
GtkWidget *view;
GtkTreePath* firstRow;
view = data->TV_budget;
budget = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(budget));
if (data->TV_is_expanded)
{
gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
}
else
{
ui_bud_tabview_model_collapse(GTK_TREE_VIEW(view));
}
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)));
firstRow = gtk_tree_path_new_from_string("0");
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), firstRow, data->TVC_category, TRUE, 0, 0);
DB(g_print("[ui_bud_tabview] : button state changed to: %d\n", view_mode));
return;
}
static gboolean ui_bud_tabview_view_search (GtkTreeModel *filter, gint column, const gchar *key, GtkTreeIter *filter_iter, gpointer data)
{
gboolean is_matching = FALSE, is_root, is_total;
GtkTreeModel *budget;
GtkTreeIter iter;
gchar *category_name;
budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
&iter,
filter_iter);
gtk_tree_model_get(budget, &iter,
UI_BUD_TABVIEW_CATEGORY_NAME, &category_name,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
-1);
if (!is_root && !is_total
&& g_strstr_len(g_utf8_casefold(category_name, -1), -1,
g_utf8_casefold(key, -1)))
{
is_matching = TRUE;
}
// GtkTreeViewSearchEqualFunc has to return FALSE only if iter matches.
return !is_matching;
}
#if HB_BUD_TABVIEW_EDIT_ENABLE
static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
gboolean is_separator;
gtk_tree_model_get(model, iter,
UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
-1);
return is_separator;
}
#endif
// the budget view creation which run the model creation tool
static GtkWidget *ui_bud_tabview_view_new (gpointer user_data)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer, *cat_name_renderer;
GtkWidget *view;
ui_bud_tabview_data_t *data = user_data;
view = gtk_tree_view_new();
/* icon column */
col = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_icon_cell_data_function, (gpointer) data, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
/* --- Category column --- */
col = gtk_tree_view_column_new();
data->TVC_category = col;
gtk_tree_view_column_set_title(col, _("Category"));
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment(col, 0.5);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), col);
// Category Name
cat_name_renderer = gtk_cell_renderer_text_new();
//#2004053 + add 5.6.2
g_object_set(cat_name_renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_set_min_width(col, HB_MINWIDTH_LIST);
gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_column_pack_start (col, cat_name_renderer, TRUE);
gtk_tree_view_column_add_attribute(col, cat_name_renderer, "markup", UI_BUD_TABVIEW_CATEGORY_NAME);
gtk_tree_view_column_set_cell_data_func(col, cat_name_renderer, ui_bud_tabview_view_display_category_name, (gpointer) data, NULL);
#if HB_BUD_TABVIEW_EDIT_ENABLE
g_object_set(cat_name_renderer, "editable", TRUE, NULL);
g_signal_connect(cat_name_renderer, "edited", G_CALLBACK(ui_bud_tabview_cell_update_category), (gpointer) data);
#endif
/* --- Annual Total --- */
col = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(col, _("Annual Total"));
gtk_tree_view_column_set_title(col, _("Annual\nTotal"));
//gtk_tree_view_column_set_title(col, _("Total"));
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment(col, 1.0);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_view_display_annual_total, NULL, NULL);
/* --- Monthly average --- */
col = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(col, _("Monthly Average"));
gtk_tree_view_column_set_title(col, _("Monthly\nAverage"));
//gtk_tree_view_column_set_title(col, _("Average"));
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment(col, 1.0);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_view_display_monthly_average, NULL, NULL);
/* --- Monthly column --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, _("Monthly"));
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment(col, 1.0);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
// Monthly toggler
renderer = gtk_cell_renderer_toggle_new();
//5.7 fix memhit because value was nor float...
g_object_set(renderer,
"xalign", 0.0f,
NULL);
gtk_tree_view_column_pack_start(col, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_view_display_is_same_amount, NULL, NULL);
g_signal_connect (renderer, "toggled", G_CALLBACK(ui_bud_tabview_cell_update_is_same_amount), (gpointer) data);
// Monthly amount
renderer = gtk_cell_renderer_spin_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_view_display_amount, GINT_TO_POINTER(UI_BUD_TABVIEW_SAME_AMOUNT), NULL);
g_object_set_data(G_OBJECT(renderer), "ui_bud_tabview_column_id", GINT_TO_POINTER(UI_BUD_TABVIEW_SAME_AMOUNT));
g_signal_connect(renderer, "edited", G_CALLBACK(ui_bud_tabview_cell_update_amount), (gpointer) data);
/* --- Each month amount --- */
for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
{
int month = i - UI_BUD_TABVIEW_JANUARY ;
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, _(UI_BUD_TABVIEW_MONTHS[month]));
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment(col, 1.0);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_spin_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(col, renderer, ui_bud_tabview_view_display_amount, GINT_TO_POINTER(i), NULL);
g_object_set_data(G_OBJECT(renderer), "ui_bud_tabview_column_id", GINT_TO_POINTER(i));
g_signal_connect(renderer, "edited", G_CALLBACK(ui_bud_tabview_cell_update_amount), (gpointer) data);
}
/* --- Empty column to expand according to the window width --- */
col = gtk_tree_view_column_new();
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_SINGLE);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), TRUE);
gtk_tree_view_set_search_column(GTK_TREE_VIEW(view), UI_BUD_TABVIEW_CATEGORY_NAME);
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(view), (GtkTreeViewSearchEqualFunc) ui_bud_tabview_view_search, NULL, NULL);
g_object_set(view,
"enable-grid-lines", PREFS->grid_lines,
"enable-tree-lines", FALSE,
NULL);
gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW (view), TRUE);
return view;
}
/*
* UI actions
**/
#if HB_BUD_TABVIEW_EDIT_ENABLE
// Update homebank category on user change
static void ui_bud_tabview_cell_update_category(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
GtkTreeIter filter_iter, iter;
GtkTreeModel *filter, *budget;
Category* category;
guint32 category_key;
gboolean is_root, is_total;
DB(g_print("\n[ui_bud_tabview] category name updated with new name '%s'\n", new_text));
view = data->TV_budget;
// Read filter data
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
gtk_tree_model_get_iter_from_string (filter, &filter_iter, filter_path);
// Convert data to budget model
budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
&iter,
&filter_iter);
gtk_tree_model_get (budget, &iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
-1);
category = da_cat_get (category_key);
if (! category || is_root || is_total)
{
return;
}
// Update category name
category_rename(category, new_text);
// Notify of changes
data->change++;
// Update budget model
// Current row
gtk_tree_store_set(
GTK_TREE_STORE(budget),
&iter,
UI_BUD_TABVIEW_CATEGORY_NAME, category->name,
UI_BUD_TABVIEW_CATEGORY_FULLNAME, category->fullname,
-1);
return;
}
#endif
// Update amount in budget model and homebank category on user change
static void ui_bud_tabview_cell_update_amount(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data)
{
const ui_bud_tabview_store_t column_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer), "ui_bud_tabview_column_id"));
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
GtkTreeIter filter_iter, iter;
GtkTreeModel *filter, *budget;
Category* category;
gdouble amount;
gint forcedsign;
guint32 category_key;
DB(g_print("\n[ui_bud_tabview] amount updated:\n"));
view = data->TV_budget;
// Read filter data
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
gtk_tree_model_get_iter_from_string (filter, &filter_iter, filter_path);
// Convert data to budget model
budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
&iter,
&filter_iter);
gtk_tree_model_get (budget, &iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
-1);
category = da_cat_get (category_key);
if (! category)
{
return;
}
//#2071648 enable input +/- force sign
forcedsign = hb_amount_forced_sign(new_text);
amount = g_strtod(new_text, NULL);
switch( forcedsign )
{
case HB_AMT_SIGN_EXP:
if( amount > 0)
amount *= -1;
break;
case HB_AMT_SIGN_INC:
if( amount < 0)
amount *= -1;
break;
//#2052304 ensure sign to category sign
default:
if( amount > 0 && !(category->flags & GF_INCOME) )
amount *= -1;
break;
}
DB(g_print("\tcolumn: %d (month: %d), category key: %d, amount %.2f\n", column_id, column_id - UI_BUD_TABVIEW_JANUARY + 1, category_key, amount));
// Update Category
category->budget[column_id - UI_BUD_TABVIEW_JANUARY + 1] = amount;
// Reset Budget Flag
category->flags &= ~(GF_BUDGET);
if (category->flags & GF_FORCED)
{
category->flags |= GF_BUDGET;
}
else
{
for(gint budget_id = 0; budget_id <=12; ++budget_id)
{
if( category->budget[budget_id] != 0.0)
{
category->flags |= GF_BUDGET;
break;
}
}
}
// Notify of changes
data->change++;
// Update budget model
// Current row
gtk_tree_store_set(
GTK_TREE_STORE(budget),
&iter,
UI_BUD_TABVIEW_HAS_BUDGET, (category->flags & GF_BUDGET),
column_id, amount,
-1);
// Refresh total rows
ui_bud_tabview_model_update_monthly_total (GTK_TREE_STORE(budget));
return;
}
// Update the row to (dis/enable) same amount for this category
static void ui_bud_tabview_cell_update_is_same_amount(GtkCellRendererText *renderer, gchar *filter_path, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
GtkTreeIter filter_iter, iter;
GtkTreeModel *filter, *budget;
Category* category;
gboolean issame;
guint32 category_key;
DB(g_print("\n[ui_bud_tabview] Is same amount updated:\n"));
view = data->TV_budget;
// Read filter data
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
gtk_tree_model_get_iter_from_string (filter, &filter_iter, filter_path);
// Convert data to budget model
budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
&iter,
&filter_iter);
gtk_tree_model_get (budget, &iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, &issame,
-1);
category = da_cat_get (category_key);
if (! category)
{
return;
}
// Value has been toggled !
issame = !(issame);
DB(g_print("\tcategory key: %d, issame: %d (before: %d)\n", category_key, issame, !(issame)));
// Update Category
// Reset Forced Flag
category->flags &= ~(GF_CUSTOM);
if (issame == FALSE)
{
category->flags |= (GF_CUSTOM);
}
// Notify of changes
data->change++;
// Update budget model
// Current row
gtk_tree_store_set(
GTK_TREE_STORE(budget),
&iter,
UI_BUD_TABVIEW_IS_SAME_AMOUNT, issame,
-1);
// Refresh total rows
ui_bud_tabview_model_update_monthly_total (GTK_TREE_STORE(budget));
return;
}
// Update budget view and model according to the new view mode selected
static void ui_bud_tabview_view_update_mode (GtkToggleButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
// Mode is directly set by radio button, because the UI_BUD_TABVIEW_VIEW_MODE and enum
// for view mode are constructed to correspond (manually)
view_mode = hbtk_switcher_get_active (HBTK_SWITCHER(data->RA_mode));
DB(g_print("\n[ui_bud_tabview] view mode toggled to: %d\n", view_mode));
ui_bud_tabview_view_toggle((gpointer) data, view_mode);
return;
}
// Expand all categories inside the current view
static void ui_bud_tabview_view_expand (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
view = data->TV_budget;
data->TV_is_expanded = TRUE;
gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
return;
}
// Collapse all categories inside the current view
static void ui_bud_tabview_view_collapse (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
view = data->TV_budget;
data->TV_is_expanded = FALSE;
ui_bud_tabview_model_collapse (GTK_TREE_VIEW(view));
return;
}
// From TreeView, retrieve the category, the budget TreeStore and the iter inside that store.
// * budget: a GtkTreeModel
// * iter: an unintialized GtkTreeIter
// * category: a pointer of Category which will point to the found Category
// If category is not retrieved, return FALSE and reset budget, iter and category.
// That's especillaly useful for buttons outside the GtkTreeView.
// Warning: iter has to be already allocated
static gboolean ui_bud_tabview_get_selected_category (GtkTreeModel **budget, GtkTreeIter *iter, Category **category, ui_bud_tabview_data_t *data)
{
GtkWidget *view;
GtkTreeModel *filter;
GtkTreeSelection *selection;
GtkTreeIter filter_iter;
guint32 item_key;
DB( g_print("[ui_bud_tabview_get_selected_category] retrieve category from selected row\n") );
view = data->TV_budget;
// Read filter to retrieve the currently selected row
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
*budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
// Retrieve selected row from filter if possible
if (gtk_tree_selection_get_selected(selection, &filter, &filter_iter))
{
// Convert data to budget model
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
iter,
&filter_iter);
// Avoid to return fake categories
gtk_tree_model_get (*budget, iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &item_key,
-1);
if (item_key != 0)
{
DB(g_print("look category with key: %d\n", item_key));
*category = da_cat_get(item_key);
return (*category != NULL);
}
}
return FALSE;
}
#if HB_BUD_TABVIEW_EDIT_ENABLE
// From TreeView, retrieve the the budget TreeStore and the root iter inside that store.
// * budget: a GtkTreeModel
// * iter: an unintialized GtkTreeIter
// Return true if selected row is a root row.
// Warning: iter has to be already allocated
static gboolean ui_bud_tabview_get_selected_root_iter (GtkTreeModel **budget, GtkTreeIter *iter, ui_bud_tabview_data_t *data)
{
GtkWidget *view;
GtkTreeModel *filter;
GtkTreeSelection *selection;
GtkTreeIter filter_iter;
gboolean is_root;
DB( g_print("[ui_bud_tabview_get_selected_root_iter] retrieve root iter from selected row\n") );
view = data->TV_budget;
// Read filter to retrieve the currently selected row
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
*budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
// Retrieve selected row from filter if possible
if (gtk_tree_selection_get_selected(selection, &filter, &filter_iter))
{
// Convert data to budget model
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
iter,
&filter_iter);
gtk_tree_model_get (*budget, iter,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
-1);
return (is_root);
}
return FALSE;
}
// Check if add category dialog is full filled
static void ui_bud_tabview_category_add_full_filled (GtkWidget *source, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
const gchar* new_raw_name;
gchar* new_name;
gboolean is_name_filled = FALSE, is_parent_choosen = FALSE;
// Check a name for the new category is given:
new_raw_name = gtk_entry_get_text(GTK_ENTRY(data->EN_add_name));
if (new_raw_name && *new_raw_name)
{
new_name = g_strdup(new_raw_name);
g_strstrip(new_name);
if (strlen(new_name) > 0)
{
is_name_filled = TRUE;
}
g_free(new_name);
}
// Check an entry has been selected in parent combobox
is_parent_choosen = (gtk_combo_box_get_active(GTK_COMBO_BOX(data->COMBO_add_parent)) > -1);
// Dis/Enable apply dialog button
gtk_widget_set_sensitive(data->BT_apply, is_name_filled && is_parent_choosen);
return;
}
// Add a category according to the current selection
static void ui_bud_tabview_category_add (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *apply;
ui_bud_tabview_view_mode_t view_mode;
GtkTreeModel *budget, *categories;
GtkTreeIter default_parent_iter, categories_iter;
GtkWidget *dialog, *content, *grid, *combobox, *textentry, *widget;
GtkCellRenderer *renderer;
Category *category;
gint gridrow, response;
gboolean exists_default_select = FALSE;
view_mode = hbtk_radio_button_get_active(GTK_CONTAINER(data->RA_mode));
// Setup budget and retrieve default selection from budget dialog
if (ui_bud_tabview_get_selected_category (&budget, &default_parent_iter, &category, data))
{
exists_default_select = TRUE;
if (category->parent != 0)
{
ui_bud_tabview_search_criteria_t parent_search = ui_bud_tabview_search_criteria_default;
parent_search.row_category_key = category->parent;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&parent_search);
if (!parent_search.iterator) {
DB(g_print(" -> error: not found good parent iterator !\n"));
return;
}
default_parent_iter = *parent_search.iterator;
g_free(parent_search.iterator);
}
}
else if (ui_bud_tabview_get_selected_root_iter (&budget, &default_parent_iter, data))
{
// If currently selected row is a root row, use it as default value
exists_default_select = TRUE;
}
// Selectable categories from original model
categories = gtk_tree_model_filter_new(budget, NULL);
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(categories),
ui_bud_tabview_model_row_filter_parents, data, NULL);
if (exists_default_select)
{
gtk_tree_model_filter_convert_child_iter_to_iter(GTK_TREE_MODEL_FILTER(categories),
&categories_iter,
&default_parent_iter);
}
DB( g_print("[ui_bud_tabview] open sub-dialog to add a category\n") );
dialog = gtk_dialog_new_with_buttons (_("Add a category"),
GTK_WINDOW(data->dialog),
GTK_DIALOG_MODAL,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
NULL);
// Apply button will be enabled only when parent category and name are choosen
apply = gtk_dialog_add_button(GTK_DIALOG(dialog),
_("_Apply"),
GTK_RESPONSE_APPLY);
data->BT_apply = apply;
gtk_widget_set_sensitive(apply, FALSE);
//window contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
// design content
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_MEDIUM);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(grid), SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (content), grid);
// First row display parent selector
gridrow = 0;
widget = gtk_label_new(_("Parent category"));
gtk_grid_attach (GTK_GRID (grid), widget, 0, gridrow, 1, 1);
combobox = gtk_combo_box_new_with_model(categories);
data->COMBO_add_parent = combobox;
gtk_grid_attach (GTK_GRID (grid), combobox, 1, gridrow, 1, 1);
gtk_combo_box_set_row_separator_func(
GTK_COMBO_BOX(combobox),
ui_bud_tabview_view_separator,
data,
NULL
);
gtk_combo_box_set_id_column(GTK_COMBO_BOX(combobox), UI_BUD_TABVIEW_CATEGORY_KEY);
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(combobox), renderer, TRUE);
gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combobox), renderer, "text", UI_BUD_TABVIEW_CATEGORY_FULLNAME);
// Next row displays the new category entry
gridrow++;
widget = gtk_label_new(_("Category name"));
gtk_grid_attach (GTK_GRID (grid), widget, 0, gridrow, 1, 1);
textentry = gtk_entry_new();
data->EN_add_name = textentry;
gtk_grid_attach (GTK_GRID (grid), textentry, 1, gridrow, 1, 1);
// Signals to enable Apply button
g_signal_connect (data->COMBO_add_parent, "changed", G_CALLBACK(ui_bud_tabview_category_add_full_filled), (gpointer)data);
g_signal_connect (data->EN_add_name, "changed", G_CALLBACK(ui_bud_tabview_category_add_full_filled), (gpointer)data);
if (exists_default_select)
{
gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combobox), &categories_iter);
}
gtk_widget_show_all (dialog);
response = gtk_dialog_run (GTK_DIALOG (dialog));
// When the response is APPLY, the form was full filled
if (response == GTK_RESPONSE_APPLY) {
Category *new_item;
const gchar *new_name;
gchar *parent_name;
guint32 parent_key;
ui_bud_tabview_cat_type_t parent_type;
ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
GtkTreeIter parent_iter, *root_iter;
DB( g_print("[ui_bud_tabview] applying creation of a new category\n") );
// Retrieve info from dialog
gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combobox), &categories_iter);
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(categories),
&parent_iter,
&categories_iter);
gtk_tree_model_get (budget, &parent_iter,
UI_BUD_TABVIEW_CATEGORY_NAME, &parent_name,
UI_BUD_TABVIEW_CATEGORY_KEY, &parent_key,
UI_BUD_TABVIEW_CATEGORY_TYPE, &parent_type,
-1);
DB( g_print(" -> from parent cat: %s (key: %d, type: %d)\n",
parent_name, parent_key ,parent_type) );
// Retrieve required root
root_search.row_is_root = TRUE;
root_search.row_is_total = FALSE;
root_search.row_category_type = parent_type;
gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
(GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
&root_search);
if (!root_search.iterator) {
DB(g_print(" -> error: not found good tree root !\n"));
return;
}
root_iter = root_search.iterator;
// Build new category from name and parent iterator
new_name = gtk_entry_get_text(GTK_ENTRY(textentry));
data->change++;
new_item = da_cat_malloc();
new_item->name = g_strdup(new_name);
g_strstrip(new_item->name);
new_item->parent = parent_key;
if (parent_key)
{
new_item->flags |= GF_SUB;
}
if (parent_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
{
new_item->flags |= GF_INCOME;
}
// On balance mode, enable forced display too to render it to user
if (view_mode == UI_BUD_TABVIEW_VIEW_SUMMARY)
{
new_item->flags |= GF_FORCED;
}
if(da_cat_append(new_item))
{
GtkWidget *view;
GtkTreeModel *filter;
GtkTreeIter filter_iter;
GtkTreePath *path;
DB( g_print(" => add cat: %p (%d), type=%d\n", new_item->name, new_item->key, category_type_get(new_item)) );
// Finally add it to model
ui_bud_tabview_model_add_category_with_lineage (GTK_TREE_STORE(budget), root_iter, &(new_item->key));
// Expand view up to the newly added item, so expand its parent which is already known as iter
view = data->TV_budget;
filter = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
if(gtk_tree_model_filter_convert_child_iter_to_iter(GTK_TREE_MODEL_FILTER(filter),
&filter_iter,
&parent_iter)
)
{
path = gtk_tree_model_get_path(filter, &filter_iter);
gtk_tree_view_expand_row(GTK_TREE_VIEW(view), path, TRUE);
}
}
}
gtk_window_destroy (GTK_WINDOW(dialog));
return;
}
// Delete a category according to the current selection
static void ui_bud_tabview_category_delete (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkTreeModel *budget;
GtkTreeIter iter;
Category* category;
gint response;
DB( g_print("[ui_bud_tabview] open sub-dialog to delete a category\n") );
// Retrieve selected row from filter if possible
if (ui_bud_tabview_get_selected_category (&budget, &iter, &category, data))
{
gchar *title = NULL;
gchar *secondtext = NULL;
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), category->name);
if( category->usage_count > 0 )
{
secondtext = _("This category is used.\n"
"Any transaction using that category will be set to (no category)");
}
response = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( response == GTK_RESPONSE_OK )
{
gtk_tree_store_remove(GTK_TREE_STORE(budget),
&iter);
category_move(category->key, 0);
da_cat_delete(category->key);
data->change++;
}
}
return;
}
// Check if add category dialog is full filled
static void ui_bud_tabview_category_merge_full_filled (GtkWidget *source, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gboolean is_target_choosen = FALSE;
is_target_choosen = (gtk_combo_box_get_active(GTK_COMBO_BOX(data->COMBO_merge_target)) > -1);
// Dis/Enable apply dialog button
gtk_widget_set_sensitive(data->BT_apply, is_target_choosen);
return;
}
static void ui_bud_tabview_category_merge (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *apply;
GtkTreeModel *budget, *categories;
GtkTreeIter iter_source, iter, categories_iter;
GtkWidget *dialog, *content, *grid, *combobox, *widget, *checkbutton;
GtkCellRenderer *renderer;
gint gridrow, response;
Category *merge_source;
gchar *label_source, *label_delete;
// Retrieve source of merge
if (ui_bud_tabview_get_selected_category(&budget, &iter_source, &merge_source, data))
{
DB( g_print("[ui_bud_tabview] open sub-dialog to merge category: %s\n", merge_source->name) );
dialog = gtk_dialog_new_with_buttons (_("Merge categories"),
GTK_WINDOW(data->dialog),
GTK_DIALOG_MODAL,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
NULL);
// Apply button will be enabled only when a target merge category is choosen
apply = gtk_dialog_add_button(GTK_DIALOG(dialog),
_("_Apply"),
GTK_RESPONSE_APPLY);
data->BT_apply = apply;
gtk_widget_set_sensitive(apply, FALSE);
//window contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
// design content
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_MEDIUM);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(grid), SPACING_MEDIUM);
gtk_box_prepend (GTK_BOX (content), grid);
// First row display parent selector
gridrow = 0;
label_source = g_strdup_printf(_("Transactions assigned to category '%s', will be moved to the category selected below."), merge_source->name);
widget = gtk_label_new (label_source);
gtk_grid_attach (GTK_GRID (grid), widget, 0, gridrow, 4, 1);
// Line to select merge target
gridrow++;
widget = gtk_label_new(_("Target category"));
gtk_grid_attach (GTK_GRID (grid), widget, 1, gridrow, 1, 1);
// Target category list is built from original model with a filter
categories = gtk_tree_model_filter_new(budget, NULL);
data->MERGE_source_category_key = merge_source->key;
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(categories),
ui_bud_tabview_model_row_merge_filter_with_headers, data, NULL);
combobox = gtk_combo_box_new_with_model(categories);
data->COMBO_merge_target = combobox;
gtk_grid_attach (GTK_GRID (grid), combobox, 2, gridrow, 1, 1);
gtk_combo_box_set_row_separator_func(
GTK_COMBO_BOX(combobox),
ui_bud_tabview_view_separator,
data,
NULL
);
gtk_combo_box_set_id_column(GTK_COMBO_BOX(combobox), UI_BUD_TABVIEW_CATEGORY_KEY);
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(combobox), renderer, TRUE);
gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(combobox), renderer, "text", UI_BUD_TABVIEW_CATEGORY_FULLNAME);
// Next row displays the automatic delete option
gridrow++;
label_delete = g_strdup_printf (
_("_Delete the category '%s'"), merge_source->name);
checkbutton = gtk_check_button_new_with_mnemonic(label_delete);
gtk_grid_attach (GTK_GRID (grid), checkbutton, 0, gridrow, 4, 1);
// Signals to enable Apply button
g_signal_connect (data->COMBO_merge_target, "changed", G_CALLBACK(ui_bud_tabview_category_merge_full_filled), (gpointer)data);
gtk_widget_show_all (dialog);
response = gtk_dialog_run (GTK_DIALOG (dialog));
// When the response is APPLY, the form was full filled
if (response == GTK_RESPONSE_APPLY)
{
Category *merge_target, *parent_source;
gint target_key;
DB( g_print("[ui_bud_tabview] applying merge\n") );
// Retrieve info from dialog
gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combobox), &categories_iter);
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(categories),
&iter,
&categories_iter);
gtk_tree_model_get (budget, &iter,
UI_BUD_TABVIEW_CATEGORY_KEY, &target_key,
-1);
merge_target = da_cat_get(target_key);
DB( g_print(" -> to target category: %s (key: %d)\n",
merge_target->name, target_key) );
// Merge categories (according to ui-category.c)
category_move(merge_source->key, merge_target->key);
merge_target->usage_count += merge_source->usage_count;
merge_source->usage_count = 0;
// Keep the income type with us
parent_source = da_cat_get(merge_source->parent);
if(parent_source != NULL && (parent_source->flags & GF_INCOME))
{
merge_target->flags |= GF_INCOME;
}
// Clean source merge
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton)))
{
da_cat_delete(merge_source->key);
gtk_tree_store_remove(GTK_TREE_STORE(budget),
&iter_source);
}
data->change++;
}
data->MERGE_source_category_key = 0;
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(label_source);
g_free(label_delete);
}
return;
}
#endif
// Reset inputs done for the currently selected category
static void ui_bud_tabview_category_reset (GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkTreeModel *budget;
GtkTreeIter iter;
Category* category;
gint response;
DB( g_print("[ui_bud_tabview] open sub-dialog to confirm category reset\n") );
// Retrieve selected row from filter if possible
if (ui_bud_tabview_get_selected_category (&budget, &iter, &category, data))
{
gchar *title = NULL;
gchar *secondtext = NULL;
title = g_strdup_printf (
_("Are you sure you want to clear inputs for '%s'?"), category->name);
secondtext = _("If you proceed, every amount will be set to 0.");
response = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Clear"),
TRUE
);
g_free(title);
if( response == GTK_RESPONSE_OK )
{
// Update data
for(int i=0;i<=12;i++)
{
category->budget[i] = 0;
}
// Reset budget flag according to GF_FORCED
category->flags &= ~(GF_BUDGET);
//mdo: no
/*if (category->flags & GF_FORCED)
{
category->flags |= GF_BUDGET;
}*/
data->change++;
// Update GtkTreeStore
gtk_tree_store_set(
GTK_TREE_STORE(budget),
&iter,
UI_BUD_TABVIEW_HAS_BUDGET, (category->flags & GF_BUDGET),
UI_BUD_TABVIEW_SAME_AMOUNT, category->budget[0],
UI_BUD_TABVIEW_JANUARY, category->budget[1],
UI_BUD_TABVIEW_FEBRUARY, category->budget[2],
UI_BUD_TABVIEW_MARCH, category->budget[3],
UI_BUD_TABVIEW_APRIL, category->budget[4],
UI_BUD_TABVIEW_MAY, category->budget[5],
UI_BUD_TABVIEW_JUNE, category->budget[6],
UI_BUD_TABVIEW_JULY, category->budget[7],
UI_BUD_TABVIEW_AUGUST, category->budget[8],
UI_BUD_TABVIEW_SEPTEMBER, category->budget[9],
UI_BUD_TABVIEW_OCTOBER, category->budget[10],
UI_BUD_TABVIEW_NOVEMBER, category->budget[11],
UI_BUD_TABVIEW_DECEMBER, category->budget[12],
-1);
// Refresh total rows
ui_bud_tabview_model_update_monthly_total (GTK_TREE_STORE(budget));
}
}
return;
}
static void ui_bud_tabview_category_toggle_monitoring(GtkButton *button, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkTreeModel *budget;
GtkTreeIter iter;
Category* category;
DB( g_print("[ui_bud_tabview] open sub-dialog to confirm category reset\n") );
// Retrieve selected row from filter if possible
if (ui_bud_tabview_get_selected_category (&budget, &iter, &category, data))
{
gboolean is_monitoring_forced;
// Toggle monitoring
is_monitoring_forced = !(category->flags & GF_FORCED);
// Update Category
// Reset Forced and Budget Flags
category->flags &= ~(GF_FORCED);
category->flags &= ~(GF_BUDGET);
if (is_monitoring_forced == TRUE)
{
category->flags |= (GF_FORCED);
category->flags |= (GF_BUDGET);
}
else
{
for(gint budget_id = 0; budget_id <=12; ++budget_id)
{
if( category->budget[budget_id] != 0.0)
{
category->flags |= GF_BUDGET;
break;
}
}
}
// Notify of changes
data->change++;
// Update budget model
// Current row
gtk_tree_store_set(
GTK_TREE_STORE(budget),
&iter,
UI_BUD_TABVIEW_IS_MONITORING_FORCED, is_monitoring_forced,
UI_BUD_TABVIEW_HAS_BUDGET, (category->flags & GF_BUDGET),
-1);
// Refresh total rows
ui_bud_tabview_model_update_monthly_total (GTK_TREE_STORE(budget));
}
return;
}
static gboolean ui_bud_tabview_on_key_press(GtkWidget *source, GdkEvent *event, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GdkModifierType state;
guint keyval;
gdk_event_get_state (event, &state);
gdk_event_get_keyval(event, &keyval);
// On Control-f enable search entry
if (state & GDK_CONTROL_MASK && keyval == GDK_KEY_f)
{
gtk_widget_grab_focus(data->EN_search);
return TRUE;
}
return GDK_EVENT_PROPAGATE;
}
static void ui_bud_tabview_view_on_select(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
GtkWidget *view;
GtkTreeModel *filter, *budget;
GtkTreeIter filter_iter, iter;
GtkTreeSelection *selection;
gboolean is_root, is_total, is_monitoring_forced;
ui_bud_tabview_cat_type_t category_type;
view = data->TV_budget;
selection = data->TV_selection;
// Block signals
g_signal_handler_block(data->BT_category_force_monitoring, data->HID_category_monitoring_toggle);
// Reset buttons
#if HB_BUD_TABVIEW_EDIT_ENABLE
gtk_widget_set_sensitive(data->BT_category_add, FALSE);
gtk_widget_set_sensitive(data->BT_category_delete, FALSE);
gtk_widget_set_sensitive(data->BT_category_merge, FALSE);
#endif
gtk_widget_set_sensitive(data->BT_category_reset, FALSE);
gtk_widget_set_sensitive(data->BT_category_force_monitoring, FALSE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->BT_category_force_monitoring), FALSE);
// Read filter to retrieve the currently selected row in real model
filter = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
// Activate buttons if selected row is editable
if (gtk_tree_selection_get_selected(selection, &filter, &filter_iter))
{
// Convert data to budget model
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
&iter,
&filter_iter);
// Check the iter is an editable one
gtk_tree_model_get (budget, &iter,
UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
UI_BUD_TABVIEW_IS_ROOT, &is_root,
UI_BUD_TABVIEW_IS_TOTAL, &is_total,
UI_BUD_TABVIEW_IS_MONITORING_FORCED, &is_monitoring_forced,
-1);
// If category is neither a root, neither a total row, every operations can be applied
if (!is_root && !is_total)
{
#if HB_BUD_TABVIEW_EDIT_ENABLE
gtk_widget_set_sensitive(data->BT_category_add, TRUE);
gtk_widget_set_sensitive(data->BT_category_delete, TRUE);
gtk_widget_set_sensitive(data->BT_category_merge, TRUE);
#endif
gtk_widget_set_sensitive(data->BT_category_reset, TRUE);
gtk_widget_set_sensitive(data->BT_category_force_monitoring, TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->BT_category_force_monitoring), is_monitoring_forced);
}
// If category is a root (except the Total one), we can use it to add a child row
#if HB_BUD_TABVIEW_EDIT_ENABLE
else if (category_type != UI_BUD_TABVIEW_CAT_TYPE_NONE
&& is_root
&& !is_total)
{
gtk_widget_set_sensitive(data->BT_category_add, TRUE);
}
#endif
}
// Unblock signals
g_signal_handler_unblock(data->BT_category_force_monitoring, data->HID_category_monitoring_toggle);
return;
}
/*
** index 0 is all month, then 1 -> 12 are months
*/
static gchar *ui_bud_manage_getcsvbudgetstr(Category *item)
{
gchar *retval = NULL;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
//DB( g_print(" get budgetstr for '%s'\n", item->name) );
if( !(item->flags & GF_CUSTOM) )
{
if( item->budget[0] )
{
//g_ascii_dtostr (buf, sizeof (buf), item->budget[0]);
//#1750257 use locale numdigit
g_snprintf(buf, sizeof (buf), "%.2f", item->budget[0]);
retval = g_strdup(buf);
//DB( g_print(" => %d: %s\n", 0, retval) );
}
}
else
{
gint i;
for(i=1;i<=12;i++)
{
//if( item->budget[i] )
//{
gchar *tmp = retval;
//g_ascii_dtostr (buf, sizeof (buf), item->budget[i]);
//#1750257 use locale numdigit
g_snprintf(buf, sizeof (buf), "%.2f", item->budget[i]);
if(retval != NULL)
{
retval = g_strconcat(retval, ";", buf, NULL);
g_free(tmp);
}
else
retval = g_strdup(buf);
//DB( g_print(" => %d: %s\n", i, retval) );
//}
}
}
return retval;
}
static void ui_bud_tabview_manage_clearall(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gint result;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] clear all\n") );
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
_("Clear the entire Budget"),
_("Are you sure you want to permanently\nclear the budget?"),
_("_Clear"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
GtkTreeModel *filter, *model;
GList *lcat, *list;
filter = gtk_tree_view_get_model(GTK_TREE_VIEW(data->TV_budget));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_store_clear(GTK_TREE_STORE(model));
lcat = list = category_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
Category *category = list->data;
// Update data
for(int i=0;i<=12;i++)
{
category->budget[i] = 0;
}
// Reset budget flag
category->flags &= ~(GF_BUDGET);
list = g_list_next(list);
}
g_list_free(lcat);
//update the treeview
ui_bud_tabview_model_populate(GTK_TREE_STORE(model));
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->TV_budget));
}
}
static void ui_bud_tabview_manage_load_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gchar *filename = NULL;
GIOChannel *io;
const gchar *encoding;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[ui-budget] load csv - data %p\n", data) );
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
encoding = homebank_file_getencoding(filename);
io = g_io_channel_new_file(filename, "r", NULL);
if(io != NULL)
{
GtkTreeModel *filter, *model;
gboolean error = FALSE;
gchar *tmpstr;
gint io_stat;
DB( g_print(" -> encoding should be %s\n", encoding) );
if( encoding != NULL )
{
g_io_channel_set_encoding(io, encoding, NULL);
}
filter = gtk_tree_view_get_model(GTK_TREE_VIEW(data->TV_budget));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
gtk_tree_store_clear(GTK_TREE_STORE(model));
for(;;)
{
io_stat = g_io_channel_read_line(io, &tmpstr, NULL, NULL, NULL);
if( io_stat == G_IO_STATUS_EOF)
break;
if( io_stat == G_IO_STATUS_NORMAL)
{
if( tmpstr != NULL)
{
gchar **str_array;
gboolean budget;
Category *tmpitem;
gint i;
hb_string_strip_crlf(tmpstr);
str_array = g_strsplit (tmpstr, ";", 15);
// lvl; type; name; value(s)...
if( (g_strv_length (str_array) < 4 || *str_array[1] != '*') && (g_strv_length (str_array) < 15))
{
error = TRUE;
break;
}
DB( g_print(" csv read '%s : %s : %s ...'\n", str_array[0], str_array[1], str_array[2]) );
tmpitem = da_cat_get_by_fullname(str_array[2]);
if( tmpitem != NULL )
{
DB( g_print(" found cat, updating '%s' '%s'\n", tmpitem->name, tmpitem->fullname) );
data->change++;
tmpitem->flags &= ~(GF_CUSTOM); //delete flag
if( *str_array[1] == '*' )
{
//tmpitem->budget[0] = g_ascii_strtod(str_array[3], NULL);
//#1750257 use locale numdigit
tmpitem->budget[0] = g_strtod(str_array[3], NULL);
DB( g_print(" monthly '%.2f'\n", tmpitem->budget[0]) );
}
else
{
tmpitem->flags |= (GF_CUSTOM);
for(i=1;i<=12;i++)
{
//tmpitem->budget[i] = g_ascii_strtod(str_array[2+i], NULL);
//#1750257 use locale numdigit
tmpitem->budget[i] = g_strtod(str_array[2+i], NULL);
DB( g_print(" month %d '%.2f'\n", i, tmpitem->budget[i]) );
}
}
// if any value,set the flag to visual indicator
budget = FALSE;
tmpitem->flags &= ~(GF_BUDGET); //delete flag
for(i=0;i<=12;i++)
{
if(tmpitem->budget[i])
{
budget = TRUE;
break;
}
}
if(budget == TRUE)
tmpitem->flags |= GF_BUDGET;
}
g_strfreev (str_array);
}
g_free(tmpstr);
}
}
g_io_channel_unref (io);
//update the treeview
ui_bud_tabview_model_populate(GTK_TREE_STORE(model));
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->TV_budget));
if( error == TRUE )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("File format error"),
_("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
);
}
}
g_free( filename );
}
}
static void ui_bud_tabview_manage_save_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
ui_bud_tabview_data_t *data = user_data;
gchar *filename = NULL;
GIOChannel *io;
DB( g_print("\n[ui-budget] save csv\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
GList *lcat, *list;
lcat = list = category_glist_sorted(HB_GLIST_SORT_KEY);
while (list != NULL)
{
gchar *outstr, *outvalstr;
Category *category = list->data;
gchar lvl, type;
if( category->flags & GF_BUDGET )
{
lvl = (category->parent == 0) ? '1' : '2';
type = (category->flags & GF_CUSTOM) ? ' ' : '*';
outvalstr = ui_bud_manage_getcsvbudgetstr(category);
outstr = g_strdup_printf("%c;%c;%s;%s\n", lvl, type, category->fullname, outvalstr);
DB( g_print("%s", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
g_free(outvalstr);
}
list = g_list_next(list);
}
g_io_channel_unref (io);
g_list_free(lcat);
}
g_free( filename );
}
}
static const GActionEntry win_actions[] = {
{ "imp" , ui_bud_tabview_manage_load_csv, NULL, NULL, NULL, {0,0,0} },
{ "exp" , ui_bud_tabview_manage_save_csv, NULL, NULL, NULL, {0,0,0} },
{ "del" , ui_bud_tabview_manage_clearall, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
static void ui_bud_tabview_dialog_close(ui_bud_tabview_data_t *data, gint response)
{
DB( g_print("[ui_bud_tabview] dialog close\n") );
GLOBALS->changes_count += data->change;
return;
}
// Open / create the main dialog, the budget view and the budget model
GtkWidget *ui_bud_tabview_manage_dialog(void)
{
ui_bud_tabview_data_t *data;
struct WinGeometry *wg;
GtkWidget *dialog, *content, *grid;
GtkWidget *radiomode;
GtkWidget *widget;
GtkWidget *vbox, *hbox, *bbox;
GtkWidget *search_entry;
GtkWidget *scrollwin, *treeview;
GtkWidget *tbar;
GtkWidget *image;
GtkTreeModel *model, *filter;
gint response;
gint w, h, dw, dh;
gint gridrow;
data = g_malloc0(sizeof(ui_bud_tabview_data_t));
data->change = 0;
if(!data) return NULL;
DB( g_print("\n[ui_bud_tabview] open dialog\n") );
// create window
dialog = gtk_dialog_new_with_buttons (_("Manage Budget"),
GTK_WINDOW(GLOBALS->mainwindow),
GTK_DIALOG_MODAL,
_("_Close"),
GTK_RESPONSE_ACCEPT,
NULL);
data->dialog = dialog;
//#2007714 keep dimension
wg = &PREFS->dbud_wg;
if( wg->w == 0 && wg->h == 0 )
{
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 3:2
dw = (dh * 3) / 2;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
}
else
gtk_window_set_default_size(GTK_WINDOW(dialog), wg->w, wg->h);
// store data inside dialog property to retrieve them easily in callbacks
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)&data);
DB( g_print(" - new dialog=%p, inst_data=%p\n", dialog, data) );
//window contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); // return a vbox
// design content
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING_MEDIUM);
gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING_MEDIUM);
hb_widget_set_margin(GTK_WIDGET(grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content), grid);
// First row displays radio button to change mode (edition / view) and search entry
gridrow = 0;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (grid), hbox, 0, gridrow, 1, 1);
// edition mode radio buttons
radiomode = hbtk_switcher_new (GTK_ORIENTATION_HORIZONTAL);
hbtk_switcher_setup(HBTK_SWITCHER(radiomode), UI_BUD_TABVIEW_VIEW_MODE, TRUE);
data->RA_mode = radiomode;
gtk_box_set_center_widget(GTK_BOX (hbox), radiomode);
// future
//menubutton
widget = gtk_menu_button_new();
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_box_append(GTK_BOX (hbox), widget);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Import CSV..."), "win.imp");
g_menu_append (section, _("E_xport CSV..."), "win.exp");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Clear All..."), "win.del");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (widget, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
// Search
search_entry = make_search();
data->EN_search = search_entry;
gtk_box_append (GTK_BOX (hbox), search_entry);
// Next row displays the budget tree with its toolbar
gridrow++;
// We use a Vertical Box to link tree with searchbar and toolbar
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_grid_attach (GTK_GRID (grid), vbox, 0, gridrow, 1, 1);
// Scrolled Window will permit to display budgets with a lot of active categories
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
treeview = ui_bud_tabview_view_new ((gpointer) data);
data->TV_budget = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
data->TV_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
// Toolbar to add, remove categories, expand and collapse categorie
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (vbox), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
#if HB_BUD_TABVIEW_EDIT_ENABLE
// Add / Remove / Merge
widget = make_image_button(ICONNAME_LIST_ADD, _("Add category"));
data->BT_category_add = widget;
gtk_widget_set_sensitive(widget, FALSE);
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_LIST_DELETE, _("Remove category"));
data->BT_category_delete = widget;
gtk_widget_set_sensitive(widget, FALSE);
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = gtk_button_new_with_label (_("Merge"));
data->BT_category_merge = widget;
gtk_widget_set_sensitive(widget, FALSE);
gtk_box_prepend(GTK_BOX(bbox), widget);
#endif
// Clear Input
widget = gtk_button_new_with_label (_("Clear input"));
data->BT_category_reset = widget;
gtk_widget_set_sensitive(widget, FALSE);
gtk_box_prepend (GTK_BOX (bbox), widget);
// Force monitoring
widget = gtk_check_button_new_with_mnemonic (_("_Force monitoring this category"));
data->BT_category_force_monitoring = widget;
gtk_widget_set_sensitive (widget, FALSE);
gtk_box_prepend (GTK_BOX (tbar), widget);
g_object_set(widget,
"draw-indicator", TRUE,
NULL);
// Expand / Collapse
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (tbar), bbox);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expand = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapse = widget;
gtk_box_prepend(GTK_BOX(bbox), widget);
/* signal connect */
g_signal_connect (data->RA_mode, "changed", G_CALLBACK(ui_bud_tabview_view_update_mode), (gpointer)data);
// Connect to key press to handle some events like Control-f
gtk_tree_view_set_search_entry(GTK_TREE_VIEW(treeview), GTK_ENTRY(search_entry));
g_signal_connect (dialog, "key-press-event", G_CALLBACK (ui_bud_tabview_on_key_press), (gpointer)data);
// Tree View
g_signal_connect (data->TV_selection, "changed", G_CALLBACK(ui_bud_tabview_view_on_select), (gpointer)data);
// toolbar buttons
#if HB_BUD_TABVIEW_EDIT_ENABLE
g_signal_connect (data->BT_category_add, "clicked", G_CALLBACK(ui_bud_tabview_category_add), (gpointer)data);
g_signal_connect (data->BT_category_delete, "clicked", G_CALLBACK (ui_bud_tabview_category_delete), (gpointer)data);
g_signal_connect (data->BT_category_merge, "clicked", G_CALLBACK (ui_bud_tabview_category_merge), (gpointer)data);
#endif
g_signal_connect (data->BT_category_reset, "clicked", G_CALLBACK (ui_bud_tabview_category_reset), (gpointer)data);
data->HID_category_monitoring_toggle = g_signal_connect (data->BT_category_force_monitoring, "toggled", G_CALLBACK (ui_bud_tabview_category_toggle_monitoring), (gpointer)data);
g_signal_connect (data->BT_expand, "clicked", G_CALLBACK (ui_bud_tabview_view_expand), (gpointer)data);
g_signal_connect (data->BT_collapse, "clicked", G_CALLBACK (ui_bud_tabview_view_collapse), (gpointer)data);
// dialog
g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &dialog);
// tree model to map HomeBank categories to the tree view
model = ui_bud_tabview_model_new();
filter = gtk_tree_model_filter_new(model, NULL);
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), ui_bud_tabview_model_row_filter, data, NULL);
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), filter);
g_object_unref(model); // Remove model with filter
g_object_unref(filter); // Remove filter with view
// By default, show the balance mode with all categories expanded
data->TV_is_expanded = TRUE;
ui_bud_tabview_view_toggle((gpointer) data, UI_BUD_TABVIEW_VIEW_SUMMARY);
gtk_widget_show_all (dialog);
response = gtk_dialog_run (GTK_DIALOG (dialog));
//#2007714 keep dimension
wg = &PREFS->dbud_wg;
gtk_window_get_size(GTK_WINDOW(dialog), &wg->w, &wg->h);
ui_bud_tabview_dialog_close(data, response);
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-export.h 0000644 0001750 0001750 00000002664 14736461415 015505 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_EXPORT_H__
#define __HB_EXPORT_H__
#include
#define PDF_NUMCOL 6 + 1
typedef struct _pdfprintcontext PdfPrintContext;
struct _pdfprintcontext {
gdouble w, h;
gdouble mt, mb, ml, mr;
gdouble column_width[PDF_NUMCOL];
gchar *column_txt[PDF_NUMCOL];
PangoFontDescription *desc;
};
void hb_export_qif_account_all(gchar *filename);
void hb_export_qif_account_single(gchar *filename, Account *acc);
void hb_export_pdf_listview(GtkTreeView *treeview, gchar *filepath, gchar *accname);
void hb_print_listview(GtkWindow *parent, gchar *tabtext, gint8 *leftcols, gchar *title, gchar *filepat, gboolean statement);
#endif
homebank-5.9.7/src/ui-group.h 0000664 0001750 0001750 00000003024 14736461415 015335 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_GROUP_GTK_H__
#define __HB_GROUP_GTK_H__
/* = = = = = = = = = = */
struct grpPopContext
{
GtkTreeModel *model;
guint except_key;
};
/* = = = = = = = = = = */
guint32 ui_grp_comboboxentry_get_key(GtkComboBox *entry_box);
guint32 ui_grp_comboboxentry_get_key_add_new(GtkComboBox *entry_box);
Group *ui_grp_comboboxentry_get(GtkComboBox *entry_box);
gboolean ui_grp_comboboxentry_set_active(GtkComboBox *entry_box, guint32 key);
void ui_grp_comboboxentry_add(GtkComboBox *entry_box, Group *pay);
void ui_grp_comboboxentry_populate(GtkComboBox *entry_box, GHashTable *hash);
void ui_grp_comboboxentry_populate_except(GtkComboBox *entry_box, GHashTable *hash, guint except_key);
GtkWidget *ui_grp_comboboxentry_new(GtkWidget *label);
#endif
homebank-5.9.7/src/list-operation.c 0000644 0001750 0001750 00000165420 14774455134 016544 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-widgets.h"
#include "list-operation.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* This is not pretty. Of course you can also use a
* separate compare function for each sort ID value */
static gint
list_txn_sort_iter_compare_strings(gchar *s1, gchar *s2)
{
return hb_string_utf8_compare(s1, s2);
}
static gint
list_txn_sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
Transaction *ope1, *ope2;
gdouble tmpval = 0;
gtk_tree_model_get(model, a, MODEL_TXN_POINTER, &ope1, -1);
gtk_tree_model_get(model, b, MODEL_TXN_POINTER, &ope2, -1);
switch (sortcol)
{
case LST_DSPOPE_STATUS:
if(!(retval = (ope1->dspflags & FLAG_TMP_ADDED) - (ope2->dspflags & FLAG_TMP_ADDED) ) )
{
retval = (ope1->dspflags & FLAG_TMP_EDITED) - (ope2->dspflags & FLAG_TMP_EDITED);
}
break;
case LST_DSPOPE_GRPFLAG:
retval = ope1->grpflg - ope2->grpflg;
break;
case LST_DSPOPE_DATE:
//5.7 let date as last sorting
break;
case LST_DSPOPE_ACCOUNT:
{
Account *a1, *a2;
a1 = da_acc_get(ope1->kacc);
a2 = da_acc_get(ope2->kacc);
retval = list_txn_sort_iter_compare_strings((a1 != NULL) ? a1->name : NULL, (a2 != NULL) ? a2->name : NULL);
}
break;
case LST_DSPOPE_PAYNUMBER:
if(!(retval = ope1->paymode - ope2->paymode))
{
retval = list_txn_sort_iter_compare_strings(ope1->number, ope2->number);
}
break;
case LST_DSPOPE_PAYEE:
{
//#1721980
gchar *name1 = NULL;
gchar *name2 = NULL;
if( ope1->flags & OF_INTXFER )
{
Account *acc = da_acc_get(ope1->kxferacc);
name1 = (acc != NULL) ? acc->name : NULL;
}
else
{
Payee *pay = da_pay_get(ope1->kpay);
name1 = (pay != NULL) ? pay->name : NULL;
}
if( ope2->flags & OF_INTXFER )
{
Account *acc = da_acc_get(ope2->kxferacc);
name2 = (acc != NULL) ? acc->name : NULL;
}
else
{
Payee *pay = da_pay_get(ope2->kpay);
name2 = (pay != NULL) ? pay->name : NULL;
}
retval = list_txn_sort_iter_compare_strings(name1, name2);
}
break;
case LST_DSPOPE_MEMO:
retval = list_txn_sort_iter_compare_strings(ope1->memo, ope2->memo);
break;
case LST_DSPOPE_CLR:
retval = ope1->status - ope2->status;
break;
case LST_DSPOPE_AMOUNT:
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME:
//tmpval = ope1->amount - ope2->amount;
//#1945636 amount sort in base currency
tmpval = hb_amount_base(ope1->amount, ope1->kcur) - hb_amount_base(ope2->amount, ope2->kcur);
retval = tmpval > 0 ? 1 : -1;
break;
case LST_DSPOPE_CATEGORY:
{
//2027201 order - split -
gchar *name1 = NULL;
gchar *name2 = NULL;
if( ope1->flags & OF_SPLIT )
name1 = _("- split -");
else
{
Category *cat = da_cat_get(ope1->kcat);
name1 = (cat != NULL) ? cat->fullname : NULL;
}
if( ope2->flags & OF_SPLIT )
name2 = _("- split -");
else
{
Category *cat = da_cat_get(ope2->kcat);
name2 = (cat != NULL) ? cat->fullname : NULL;
}
retval = list_txn_sort_iter_compare_strings(name1, name2);
}
break;
case LST_DSPOPE_TAGS:
{
gchar *t1, *t2;
t1 = tags_tostring(ope1->tags);
t2 = tags_tostring(ope2->tags);
retval = list_txn_sort_iter_compare_strings(t1, t2);
g_free(t2);
g_free(t1);
}
break;
case LST_DSPOPE_MATCH:
{
tmpval = ope1->matchrate - ope2->matchrate;
retval = tmpval > 0 ? 1 : -1;
if(!retval)
{
retval = ope1->date - ope2->date;
}
}
break;
default:
g_return_val_if_reached(0);
}
//5.7 let date as last sorting
if( retval == 0 )
{
if(! (retval = ope1->date - ope2->date) )
{
//g_print("sort on balance d1=%d, d2=%d %f %f\n", ope1->date, ope2->date, ope1->balance , ope2->balance);
tmpval = ope1->pos - ope2->pos;
retval = tmpval > 0 ? 1 : -1;
}
//g_print("ret=%d\n", ret);
}
return retval;
}
static void
list_txn_cell_set_color(GtkCellRenderer *renderer, Transaction *txn)
{
DB( g_print("\n[list_txn] cell eval future\n") );
if( PREFS->custom_bg_future == FALSE)
return;
if(txn->date > GLOBALS->today)
{
GdkRGBA bgrgba;
DB( g_print(" %s\n", PREFS->color_bg_future) );
gdk_rgba_parse(&bgrgba, PREFS->color_bg_future);
bgrgba.alpha = (GLOBALS->theme_is_dark) ? 0.165 : 0.33;
g_object_set(renderer,
"cell-background-rgba", &bgrgba,
NULL);
}
else
{
g_object_set(renderer,
"cell-background-rgba", NULL,
NULL);
}
}
static void
list_txn_eval_future(GtkCellRenderer *renderer, Transaction *txn)
{
DB( g_print("\n[list_txn] eval future\n") );
if(txn->date > GLOBALS->today)
{
g_object_set(renderer,
"style", PANGO_STYLE_OBLIQUE,
NULL);
}
else
{
//5.4.3 scale disabled
g_object_set(renderer,
"style-set", FALSE,
NULL);
}
//if( txn->marker == TXN_MARK_DUPDST )
if( txn->dspflags & FLAG_TMP_DUPDST )
{
g_object_set(renderer,
// "strikethrough-set", TRUE,
"strikethrough", TRUE,
NULL);
}
else
{
g_object_set(renderer, "strikethrough-set", FALSE,
NULL);
}
//if( txn->marker == TXN_MARK_DUPSRC )
if( txn->dspflags & FLAG_TMP_DUPSRC )
{
g_object_set(renderer,
// "weight-set", TRUE,
"weight", PANGO_WEIGHT_BOLD,
NULL);
}
else
{
g_object_set(renderer, "weight-set", FALSE,
NULL);
}
}
static void
list_txn_cell_data_func_status (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
switch(GPOINTER_TO_INT(user_data))
{
//status icons
case 1:
if( ope->dspflags & FLAG_TMP_EDITED )
iconname = ICONNAME_HB_ITEM_EDITED;
else
if( ope->dspflags & FLAG_TMP_ADDED )
iconname = ICONNAME_HB_ITEM_ADDED;
break;
//actions icons
case 2:
//temporary icons
if( ope->dspflags & FLAG_TMP_DUPDST )
iconname = ICONNAME_HB_ITEM_SIMILAR;
else
if( ope->flags & OF_ISIMPORT )
iconname = ICONNAME_HB_ITEM_IMPORT;
else
if( ope->flags & OF_ISPAST )
iconname = ICONNAME_HB_ITEM_PAST;
else
if( ope->date > GLOBALS->today )
iconname = ICONNAME_HB_ITEM_FUTURE;
break;
}
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_status_dupgid (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
Transaction *ope;
gchar buffer[6];
buffer[0] = 0;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
if( ope->dspflags & (FLAG_TMP_DUPSRC|FLAG_TMP_DUPDST) )
g_snprintf(buffer, 6-1, ":%d", ope->dupgid);
else
if( ope->dspflags & (FLAG_TMP_CHKSIGN) )
{
buffer[0]='*';
buffer[1]=0;
}
}
g_object_set(renderer, "text", buffer, NULL);
}
static void
list_txn_cell_data_func_account (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
Account *acc = da_acc_get(ope->kacc);
//fixed 5.6.3
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
if( acc )
{
text = acc->name;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
list_txn_cell_data_func_grpflag (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
const gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
iconname = ope->grpflg > 0 ? get_grpflag_icon_name(ope->grpflg) : NULL;
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_match_rate (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
gchar buffer[8];
gtk_tree_model_get(model, iter, MODEL_TXN_POINTER, &ope, -1);
g_snprintf(buffer, 8-1, "%d %%", ope->matchrate);
g_object_set(renderer, "text", buffer, NULL);
}
static void
list_txn_cell_data_func_date (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar buffer[256];
GDate date;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split != NULL )
{
g_object_set(renderer, "text", NULL, NULL);
}
else
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
if(ope->date > 0)
{
g_date_set_julian (&date, ope->date);
g_date_strftime (buffer, 256-1, PREFS->date_format, &date);
#if MYDEBUG
gchar *ds = g_strdup_printf ("%s [%02d]", buffer, ope->pos );
g_object_set(renderer, "text", ds, NULL);
g_free(ds);
#else
g_object_set(renderer, "text", buffer, NULL);
#endif
}
else
g_object_set(renderer, "text", "????", NULL);
}
}
static void
list_txn_cell_data_func_info (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *iconname = NULL;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
list_txn_cell_set_color(renderer, ope);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
if( split == NULL )
{
iconname = (ope->flags & OF_INTXFER) ? ICONNAME_HB_PM_INTXFER : (gchar *)get_paymode_icon_name(ope->paymode);
}
g_object_set(renderer, "icon-name", iconname, NULL);
break;
case 2:
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
text = ope->number;
}
#if MYDEBUG
gchar *ds = g_strdup_printf ("%s kx[%d] f[%d]", text == NULL ? "" : text, ope->kxfer, ope->flags );
g_object_set(renderer, "text", ds, NULL);
g_free(ds);
#else
g_object_set(renderer, "text", text, NULL);
#endif
break;
}
}
static void
list_txn_cell_data_func_payee (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
//#926782
if(ope->flags & OF_INTXFER)
{
Account *acc = da_acc_get(ope->kxferacc);
//5.6 use acc strings for 5.3 add > < for internal xfer
if( acc )
text = ( ope->flags & OF_INCOME ) ? acc->xferincname : acc->xferexpname;
}
else
{
Payee *pay = da_pay_get(ope->kpay);
text = (pay != NULL) ? pay->name : NULL;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
list_txn_cell_data_func_tags (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
Transaction *ope;
gchar *str;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
if(ope->tags != NULL)
{
str = tags_tostring(ope->tags);
g_object_set(renderer, "text", str, NULL);
g_free(str);
}
else
g_object_set(renderer, "text", "", NULL);
}
else
g_object_set(renderer, "text", NULL, NULL);
}
static void
list_txn_cell_data_func_memo (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
g_object_set(renderer, "text", ope->memo, NULL);
}
else if( split != NULL )
{
g_object_set(renderer, "text", split->memo, NULL);
}
}
static void
list_txn_cell_data_func_account_icon (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
Account *acc;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
acc = da_acc_get(ope->kacc);
if( acc && (acc->flags & AF_CLOSED) )
{
iconname = ICONNAME_HB_ITEM_CLOSED;
}
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_clr (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
gchar *iconname = NULL;
//const gchar *c = "";
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
list_txn_cell_set_color(renderer, ope);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
//todo: remove this
//if( (data->lockreconciled == TRUE) && (ope->status == TXN_STATUS_RECONCILED) )
// iconname = ICONNAME_CHANGES_PREVENT;
if( ope->flags & OF_REMIND )
iconname = ICONNAME_HB_ITEM_REMIND;
break;
case 2:
{
if( split == NULL )
{
switch(ope->status)
{
/*case TXN_STATUS_CLEARED: c = "c"; break;
case TXN_STATUS_RECONCILED: c = "R"; break;
case TXN_STATUS_REMIND: c = "!"; break;*/
case TXN_STATUS_CLEARED: iconname = ICONNAME_HB_ITEM_CLEAR; break;
case TXN_STATUS_RECONCILED:
iconname = ICONNAME_HB_ITEM_RECON;
if( (data->lockreconciled == TRUE) )
iconname = ICONNAME_HB_ITEM_RECONLOCK;
break;
//case TXN_STATUS_REMIND: iconname = ICONNAME_HB_ITEM_REMIND; break;
case TXN_STATUS_VOID: iconname = ICONNAME_HB_ITEM_VOID; break;
}
}
break;
}
}
//TODO 5.6 after switch to on the change prevent do not display, maybe gtk bug
//DB( g_print("\n[list_txn] clr lockrecon=%d, icon=%s", data->lockreconciled, iconname) );
//g_object_set(renderer, "text", c, NULL);
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
gint column = GPOINTER_TO_INT(user_data);
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint type;
gdouble amount, samount;
gchar *color;
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
g_return_if_fail(data != NULL);
// get the transaction
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_SPLITAMT, &samount,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
if(column == LST_DSPOPE_BALANCE)
amount = ope->balance;
else
amount = ope->amount;
if(column == LST_DSPOPE_INCOME || column == LST_DSPOPE_EXPENSE)
{
type = (ope->flags & OF_INCOME) ? LST_DSPOPE_INCOME : LST_DSPOPE_EXPENSE;
if( type != column)
{
g_object_set(renderer, "markup", NULL, NULL);
return;
}
}
//for detail display the split part (if any)
if( data->list_type == LIST_TXN_TYPE_DETAIL )
amount = samount;
}
else if( split != NULL )
{
amount = split->amount;
}
//if(amount != 0)
//{
//5.8 test for life energy
if( data->life_energy == FALSE || column == LST_DSPOPE_BALANCE || column == LST_DSPOPE_INCOME )
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, ope->kcur, GLOBALS->minor);
else
hb_strlifeenergy(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, ope->kcur, GLOBALS->minor);
color = get_normal_color_amount(amount);
//if( (column == LST_DSPOPE_BALANCE) && (ope->overdraft == TRUE) && (PREFS->custom_colors == TRUE) )
if( (column == LST_DSPOPE_BALANCE) && (PREFS->custom_colors == TRUE) && (ope->dspflags & FLAG_TMP_OVER) )
{
color = PREFS->color_warn;
}
//5.6.3 future alpha
if( color != NULL && ope->date > GLOBALS->today )
{
g_object_set(renderer,
"foreground", color,
NULL);
}
else
{
g_object_set(renderer,
"foreground", color,
"text", buf,
NULL);
}
g_object_set(renderer,
"text", buf,
NULL);
//}
//test
if( (column == LST_DSPOPE_BALANCE) && (ope->dspflags & FLAG_TMP_LOWBAL))
g_object_set(renderer, "weight", PANGO_WEIGHT_BOLD, NULL);
else
g_object_set(renderer, "weight", PANGO_WEIGHT_NORMAL, NULL);
}
static void
list_txn_cell_data_func_category (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
Category *cat;
gchar *color = NULL;
gchar *text = NULL;
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_cell_set_color(renderer, ope);
list_txn_eval_future(renderer, ope);
if(ope->flags & OF_SPLIT)
{
text = _("- split -");
}
else
{
if( ope->kcat > 0 )
{
cat = da_cat_get(ope->kcat);
text = (cat != NULL) ? cat->fullname : "";
}
else
{
//#1673902 add a visual marker for uncategorized txn
//#1844881 but not for internal xfer
if( (data->warnnocategory == TRUE) && !(ope->flags & OF_INTXFER) )
{
color = PREFS->color_warn;
text = _("- this needs a category -");
}
}
}
}
else
if( split != NULL )
{
if( split->kcat > 0 )
{
cat = da_cat_get(split->kcat);
text = (cat != NULL) ? cat->fullname : "";
}
}
//if( color != NULL )
//{
g_object_set(renderer,
"foreground", color,
NULL);
//}
g_object_set(renderer,
"text", text,
NULL);
}
/* = = = = = = = = = = = = = = = = */
//#1967708 encode csv string: add string delimiter ", and doubled if inside
static void list_txn_to_string_csv_text(GString *node, gchar csep, gchar *text)
{
gchar sep[2];
sep[0] = csep;
sep[1] = 0;
DB( g_print("---- stt csv text ----\n") );
DB( g_print(" text: '%s' '%s' %ld\n", text, sep, strlen(sep)) );
if( text == NULL )
{
g_string_append (node, "");
DB( g_print(" >skipped null\n") );
}
else
{
size_t textlen = strlen(text);
if( textlen == 0 )
{
g_string_append (node, "");
DB( g_print(" >skipped empty\n") );
}
else
{
//sep into string ?
DB( g_print(" search '%s' in '%s' %ld\n", sep, text, textlen) );
if( g_strstr_len(text, textlen, sep) == NULL )
{
//no: put native text
g_string_append (node, text);
DB( g_print(" >not found\n") );
}
else
{
DB( g_print(" >found, so add \"xxx\"\n") );
//yes: encode with string delimiter
g_string_append_c (node, '"' );
// " not inside inside ?
if( g_strstr_len(text, textlen, "\"") == NULL )
{
//no: put native text
DB( g_print(" >no \" found put text\n") );
g_string_append (node, text);
}
else
{
//yes: double the text delimiter
GString *dtext = g_string_new(text);
DB( g_print(" >\" found double \" into text\n") );
g_string_replace(dtext, "\"", "\"\"", 0);
g_string_append (node, dtext->str);
g_string_free(dtext, TRUE);
}
g_string_append_c (node, '"' );
}
}
}
DB( g_print("---- end csv text ----\n") );
}
static void list_txn_to_string_line(GString *node, gchar sep, Transaction *ope, guint32 kcat, gchar *memo, gdouble amount, guint flags)
{
Account *acc;
Payee *payee;
Category *category;
gchar *tags;
gint digits = 2;
char strbuf[G_ASCII_DTOSTR_BUF_SIZE];
DB( g_print("----\n") );
//#2090183 get currency digits
acc = da_acc_get(ope->kacc);
if( acc != NULL )
{
Currency *cur = da_cur_get(acc->kcur);
if( cur != NULL)
digits = cur->frac_digits;
}
//account
if( flags & LST_TXN_EXP_ACC )
{
g_string_append (node, (acc != NULL) ? acc->name : "");
g_string_append_c (node, sep );
}
//date
hb_sprint_date(strbuf, ope->date);
g_string_append (node, strbuf );
//paymode
if( flags & LST_TXN_EXP_PMT )
{
g_snprintf(strbuf, sizeof (strbuf), "%d", ope->paymode);
g_string_append_c (node, sep );
g_string_append (node, strbuf );
}
//info
//g_string_append (node, (ope->number != NULL) ? ope->number : "" );
g_string_append_c (node, sep );
DB( g_print(" num: '%s'\n", ope->number) );
list_txn_to_string_csv_text(node, sep, ope->number);
//payee
payee = da_pay_get(ope->kpay);
g_string_append_c (node, sep );
//#2078281
if(payee != NULL)
{
//g_string_append (node, (payee->name != NULL) ? payee->name : "");
DB( g_print(" pay: '%s'\n", payee->name) );
list_txn_to_string_csv_text(node, sep, payee->name);
}
//memo
//g_string_append (node, (memo != NULL) ? memo : "" );
g_string_append_c (node, sep );
//#2051440 include split memo :D
DB( g_print(" mem: '%s'\n", memo) );
list_txn_to_string_csv_text(node, sep, memo);
//amount
//#793719
//g_ascii_dtostr (amountbuf, sizeof (amountbuf), ope->amount);
//#1750257 use locale numdigit
//g_ascii_formatd (amountbuf, sizeof (amountbuf), "%.2f", ope->amount);
//TODO: or not we should use the currency fmt here
g_snprintf(strbuf, sizeof (strbuf), "%.*f", digits, amount);
DB( g_print("amount = %f '%s'\n", amount, strbuf) );
g_string_append_c (node, sep );
g_string_append (node, strbuf );
//#1847907 v 5.3.2 add reconcile as c column like in pdf export
//status
if( flags & LST_TXN_EXP_CLR )
{
DB( g_print("clr = %s\n", transaction_get_status_string(ope)) );
g_string_append_c (node, sep );
g_string_append (node, transaction_get_status_string(ope) );
}
//category
if( flags & LST_TXN_EXP_CAT )
{
g_string_append_c (node, sep );
category = da_cat_get(kcat);
if(category != NULL)
{
//g_string_append (node, (category->fullname != NULL) ? category->fullname : "" );
DB( g_print(" cat: '%s'\n", category->fullname) );
list_txn_to_string_csv_text(node, sep, category->fullname);
}
}
//tags
if( flags & LST_TXN_EXP_TAG )
{
tags = tags_tostring(ope->tags);
g_string_append_c (node, sep );
g_string_append (node, tags != NULL ? tags : "");
g_free(tags);
}
//balance
if( flags & LST_TXN_EXP_BAL )
{
g_snprintf(strbuf, sizeof (strbuf), "%.*f", digits, ope->balance);
DB( g_print(" balance = %f '%s'\n", ope->balance, strbuf) );
g_string_append_c (node, sep );
g_string_append (node, strbuf );
}
//eol
g_string_append (node, "\n" );
}
GString *list_txn_to_string(GtkTreeView *treeview, gboolean isclipboard, gboolean hassplit, gboolean selectonly, guint flags)
{
struct list_txn_data *data = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeSelection *selection;
gboolean valid;
GString *node;
Transaction *ope;
gdouble amount, samount;
gchar sep;
DB( g_print("\n[list_txn] to string\n") );
//adding account, status, split, balance break csv reimport
//date payment paynumber payee memo amount category tags
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
node = g_string_new(NULL);
sep = (isclipboard == TRUE) ? '\t' : ';';
// header line
if( flags & LST_TXN_EXP_ACC )
{
//g_string_append (node, "account" );
g_string_append (node, _("Account") );
g_string_append_c (node, sep );
}
//g_string_append (node, "date" );
g_string_append (node, _("Date") );
if( flags & LST_TXN_EXP_PMT )
{
g_string_append_c (node, sep );
//g_string_append (node, "paymode" );
g_string_append (node, _("Payment") );
}
g_string_append_c (node, sep );
//g_string_append (node, "info" );
g_string_append (node, _("Number") );
g_string_append_c (node, sep );
//g_string_append (node, "payee" );
g_string_append (node, _("Payee") );
g_string_append_c (node, sep );
//g_string_append (node, "memo" );
g_string_append (node, _("Memo") );
g_string_append_c (node, sep );
//g_string_append (node, "amount" );
g_string_append (node, _("Amount") );
//#1847907 v 5.3.2 add reconcile as c column like in pdf export
if( flags & LST_TXN_EXP_CLR )
{
g_string_append_c (node, sep );
g_string_append (node, "C" ); //CLR/STATUS
}
if( flags & LST_TXN_EXP_CAT )
{
g_string_append_c (node, sep );
//g_string_append (node, "category" );
g_string_append (node, _("Category") );
}
if( flags & LST_TXN_EXP_TAG )
{
g_string_append_c (node, sep );
//g_string_append (node, "tags" );
g_string_append (node, _("Tags") );
}
if( flags & LST_TXN_EXP_BAL )
{
g_string_append_c (node, sep );
//g_string_append (node, "balance" );
g_string_append (node, _("Balance") );
}
g_string_append (node, "\n" );
DB( g_print(" head: '%s'", node->str) );
// each txn
//total = 0.0;
model = gtk_tree_view_get_model(treeview);
selection = gtk_tree_view_get_selection(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gtk_tree_model_get (model, &iter,
MODEL_TXN_POINTER, &ope,
MODEL_TXN_SPLITAMT, &samount,
-1);
if( selectonly && gtk_tree_selection_iter_is_selected(selection, &iter) == FALSE )
goto next;
//normal export: if we don't want split or the txn has no split
if( (hassplit == FALSE) )
{
amount = ope->amount;
//for detail display the split part (if any)
if( data && (data->list_type == LIST_TXN_TYPE_DETAIL) )
amount = samount;
list_txn_to_string_line(node, sep, ope, ope->kcat, ope->memo, amount, flags);
//total += amount;
}
else
{
if( (ope->splits == NULL) )
{
list_txn_to_string_line(node, sep, ope, ope->kcat, ope->memo, ope->amount, flags);
//total += ope->amount;
}
else
{
guint i, nbsplit = da_splits_length(ope->splits);
DB( g_print(" split detail\n") );
for(i=0;isplits, i);
DB( g_print(" split %d\n", i) );
list_txn_to_string_line(node, sep, ope, split->kcat, split->memo, split->amount, flags);
//total += split->amount;
}
}
}
next:
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
gboolean list_txn_column_id_isvisible(GtkTreeView *treeview, gint sort_id)
{
GtkTreeViewColumn *column;
gint n, id;
for(n=0; n < NUM_LST_DSPOPE-1 ; n++ ) // -1 cause account not to be processed
{
column = gtk_tree_view_get_column (treeview, n);
if(column == NULL)
continue;
if( gtk_tree_view_column_get_visible(column) )
{
id = gtk_tree_view_column_get_sort_column_id (column);
if( sort_id == id )
return TRUE;
}
}
return FALSE;
}
static GtkTreeViewColumn *list_txn_get_column(GList *list, gint search_id)
{
GtkTreeViewColumn *column = NULL;
GList *tmp;
gint id;
tmp = g_list_first(list);
while (tmp != NULL)
{
id = gtk_tree_view_column_get_sort_column_id(tmp->data);
if( search_id == id )
{
column = tmp->data;
break;
}
tmp = g_list_next(tmp);
}
return column;
}
guint list_txn_get_quicksearch_column_mask(GtkTreeView *treeview)
{
GtkTreeViewColumn *column;
guint n, mask;
gint id;
mask = 0;
for(n=0; n < NUM_LST_DSPOPE-1 ; n++ ) // -1 cause account not to be processed
{
column = gtk_tree_view_get_column (treeview, n);
if(column == NULL)
continue;
if( gtk_tree_view_column_get_visible(column) )
{
id = gtk_tree_view_column_get_sort_column_id (column);
switch(id)
{
case LST_DSPOPE_MEMO: mask |= FLT_QSEARCH_MEMO; break;
case LST_DSPOPE_PAYNUMBER: mask |= FLT_QSEARCH_NUMBER; break;
case LST_DSPOPE_PAYEE: mask |= FLT_QSEARCH_PAYEE; break;
case LST_DSPOPE_CATEGORY: mask |= FLT_QSEARCH_CATEGORY; break;
case LST_DSPOPE_TAGS: mask |= FLT_QSEARCH_TAGS; break;
case LST_DSPOPE_AMOUNT:
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME: mask |= FLT_QSEARCH_AMOUNT; break;
}
}
}
return mask;
}
void list_txn_set_save_column_width(GtkTreeView *treeview, gboolean save_column_width)
{
struct list_txn_data *data;
DB( g_print("\n[list_txn] save column width\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
if( data )
{
data->save_column_width = save_column_width;
}
}
void list_txn_set_lockreconciled(GtkTreeView *treeview, gboolean lockreconciled)
{
struct list_txn_data *data;
DB( g_print("\n[list_txn] set lock reconciled\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->lockreconciled = lockreconciled;
DB( g_print(" lockrecon=%d\n", data->lockreconciled) );
}
void list_txn_set_warn_nocategory(GtkTreeView *treeview, gboolean warn)
{
struct list_txn_data *data;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->warnnocategory = warn;
}
void list_txn_set_life_energy(GtkTreeView *treeview, gboolean life_energy)
{
struct list_txn_data *data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->life_energy = life_energy;
//gtk_widget_queue_draw (GTK_WIDGET(treeview));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(treeview));
}
void list_txn_set_column_acc_visible(GtkTreeView *treeview, gboolean visible)
{
struct list_txn_data *data;
GList *list;
GtkTreeViewColumn *column;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->showall = visible;
list = gtk_tree_view_get_columns( treeview );
//if acc visible: balance must be invisible
column = list_txn_get_column(list, LST_DSPOPE_ACCOUNT);
if(column)
gtk_tree_view_column_set_visible (column, visible);
column = list_txn_get_column(list, LST_DSPOPE_BALANCE);
if(column)
gtk_tree_view_column_set_visible (column, !visible);
g_list_free(list);
}
void list_txn_sort_force(GtkTreeSortable *sortable, gpointer user_data)
{
gint sort_column_id;
GtkSortType order;
DB( g_print("\n[list_txn] sort\n") );
gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &order);
DB( g_print(" - id %d order %d\n", sort_column_id, order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), sort_column_id, order);
}
void list_txn_get_columns(GtkTreeView *treeview)
{
struct list_txn_data *data;
GtkTreeViewColumn *column;
gint i, col_id;
gint *col_id_ptr;
gint *col_width_ptr;
DB( g_print("\n[list_txn] get columns position/width\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
//default for LIST_TXN_TYPE_BOOK
col_id_ptr = PREFS->lst_ope_columns;
col_width_ptr = PREFS->lst_ope_col_width;
if( data->list_type == LIST_TXN_TYPE_DETAIL )
{
col_id_ptr = PREFS->lst_det_columns;
col_width_ptr = PREFS->lst_det_col_width;
}
DB( g_print(" nbcol=%d, nbsortid=%d\n", NUM_LST_DSPOPE, gtk_tree_view_get_n_columns (treeview)) );
for(i=0 ; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 'caus: account and blank column
{
column = gtk_tree_view_get_column(treeview, i);
if(column != NULL)
{
col_id = gtk_tree_view_column_get_sort_column_id (column);
if( col_id >= 0 )
{
gboolean visible;
visible = gtk_tree_view_column_get_visible (column);
if( col_id == LST_DSPOPE_BALANCE) //keep initial state of balance
visible = data->tvc_is_visible;
if( visible )
{
col_id_ptr[i] = col_id;
//5.2 moved here to keep old width in case not visible
col_width_ptr[col_id-1] = gtk_tree_view_column_get_width(column);
}
else
col_id_ptr[i] = -col_id;
DB( g_print(" col-%2d => %2d '%s' w=%d\n", i, col_id, gtk_tree_view_column_get_title(column), PREFS->lst_ope_col_width[col_id-1] ) );
}
else //should not occurs
col_id_ptr[i] = 0;
}
}
}
void list_txn_set_columns(GtkTreeView *treeview, gint *col_id)
{
struct list_txn_data *data;
GtkTreeViewColumn *column, *base;
gboolean visible;
GList *list;
gint i = 0;
gint id;
gint *col_width_ptr;
DB( g_print("\n[list_txn] set columns order/width\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
#if MYDEBUG == 1
gchar *type = NULL;
switch(data->list_type)
{
case LIST_TXN_TYPE_BOOK: type="Book";break;
case LIST_TXN_TYPE_DETAIL: type="Detail";break;
case LIST_TXN_TYPE_OTHER: type="OtherOpe";break;
case LIST_TXN_TYPE_XFERSOURCE: type="xferSrc";break;
case LIST_TXN_TYPE_XFERTARGET: type="xferTgt";break;
}
DB( g_print(" type='%s'\n", type) );
DB( g_print("debug column sortid\n") );
DB( g_print(" |.0|.1|.2|.3|.4|.5|.6|.7|.8|.9|10|11|12|13|14|15|16\n ") );
for(i=0; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 cause account not to be processed
{
DB( g_print("|%2d",col_id[i]) );
}
DB( g_print("\n") );
#endif
DB( g_print("apply column prefs\n") );
list = gtk_tree_view_get_columns( treeview );
//5.8 fix 4 first columns
base = list_txn_get_column(list, LST_DSPOPE_STATUS);
column = list_txn_get_column(list, LST_DSPOPE_GRPFLAG);
gtk_tree_view_move_column_after(treeview, column, base);
base = column;
column = list_txn_get_column(list, LST_DSPOPE_ACCOUNT);
gtk_tree_view_move_column_after(treeview, column, base);
base = column;
column = list_txn_get_column(list, LST_DSPOPE_DATE);
gtk_tree_view_move_column_after(treeview, column, base);
base = column;
for(i=0; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 cause account not to be processed
{
/* hidden are stored as col_id negative */
id = ABS(col_id[i]);
column = list_txn_get_column(list, id);
DB( g_print(" get colid=%2d '%s' [%p]\n", id, column != NULL ? gtk_tree_view_column_get_title(column) : "null", column) );
if( column != NULL )
{
//5.8 first columns are created in correct order, just ignore stored position
if( id!=LST_DSPOPE_STATUS && id!=LST_DSPOPE_GRPFLAG && id!=LST_DSPOPE_ACCOUNT && id!=LST_DSPOPE_DATE )
{
gtk_tree_view_move_column_after(treeview, column, base);
base = column;
}
else
{
DB( g_print(" >skipped\n") );
}
visible = col_id[i] < 0 ? FALSE : TRUE;
/* display exception for detail/import list */
if(data->list_type != LIST_TXN_TYPE_BOOK)
{
if( id == LST_DSPOPE_AMOUNT )
visible = TRUE;
if( id == LST_DSPOPE_STATUS || id == LST_DSPOPE_EXPENSE || id == LST_DSPOPE_INCOME )
visible = FALSE;
}
if(data->list_type == LIST_TXN_TYPE_DETAIL)
{
if( id == LST_DSPOPE_STATUS )
visible = TRUE;
}
if( id == LST_DSPOPE_BALANCE )
{
data->tvc_is_visible = visible;
}
gtk_tree_view_column_set_visible (column, visible);
//5.6 do not apply to allow autosize on XFER dialog
if( (data->list_type != LIST_TXN_TYPE_XFERSOURCE) && (data->list_type != LIST_TXN_TYPE_XFERTARGET) )
{
col_width_ptr = PREFS->lst_ope_col_width;
if( data->list_type == LIST_TXN_TYPE_DETAIL )
col_width_ptr = PREFS->lst_det_col_width;
if( id == LST_DSPOPE_PAYNUMBER
|| id == LST_DSPOPE_PAYEE
|| id == LST_DSPOPE_MEMO
|| id == LST_DSPOPE_CATEGORY
|| id == LST_DSPOPE_TAGS
|| id == LST_DSPOPE_ACCOUNT )
{
gtk_tree_view_column_set_fixed_width( column, col_width_ptr[id - 1]);
}
}
}
}
g_list_free(list );
}
static void list_txn_sort_column_changed(GtkTreeSortable *sortable, gpointer user_data)
{
struct list_txn_data *data = user_data;
gint id;
GtkSortType order;
gboolean showBalance;
gtk_tree_sortable_get_sort_column_id(sortable, &id, &order);
DB( g_print("list_txn_columns_changed %d %d\n", id, order) );
//here save the transaction list columnid and sort order
PREFS->lst_ope_sort_id = id;
PREFS->lst_ope_sort_order = order;
//manage visibility of balance column
//showBalance = (id == LST_DSPOPE_DATE && order == GTK_SORT_ASCENDING) ? data->tvc_is_visible : FALSE;
showBalance = (id == LST_DSPOPE_DATE) ? data->tvc_is_visible : FALSE;
if(data->showall == TRUE) showBalance = FALSE;
gtk_tree_view_column_set_visible (data->tvc_balance, showBalance);
}
static void
list_txn_column_popup_menuitem_on_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
{
GtkTreeViewColumn *column = user_data;
DB( g_print("toggled\n") );
gtk_tree_view_column_set_visible(column, gtk_check_menu_item_get_active(checkmenuitem) );
}
//beta
static void
list_txn_popmenu_destroy(GtkTreeView *treeview, gpointer user_data)
{
DB( g_print ("\n[list_txn] menu destroy\n") );
}
static gboolean
list_txn_column_popup_callback ( GtkWidget *button,
GdkEventButton *ev,
gpointer user_data )
{
struct list_txn_data *data = user_data;
GtkWidget *menu, *menuitem;
GtkTreeViewColumn *column;
gint i, col_id;
if (ev->type == GDK_BUTTON_PRESS && ev->button == 3)
{
DB( g_print("should popup\n") );
menu = gtk_menu_new ();
//beta
g_signal_connect (menu, "destroy", G_CALLBACK (list_txn_popmenu_destroy), NULL);
//note: deactive this disable any menuitem action
g_signal_connect (menu, "selection-done", G_CALLBACK (gtk_widget_destroy), NULL);
for(i=0 ; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 'caus: account and blank column
{
column = gtk_tree_view_get_column(GTK_TREE_VIEW(data->treeview), i);
if( column != NULL )
{
col_id = gtk_tree_view_column_get_sort_column_id (column);
if( (col_id == -1)
|| (col_id == LST_DSPOPE_STATUS)
|| (col_id == LST_DSPOPE_ACCOUNT)
|| (col_id == LST_DSPOPE_DATE)
|| (col_id == LST_DSPOPE_BALANCE)
)
continue;
//if( (data->tvc_is_visible == FALSE) && (col_id == LST_DSPOPE_BALANCE) )
// continue;
if( (data->list_type == LIST_TXN_TYPE_DETAIL) &&
( (col_id == LST_DSPOPE_AMOUNT)
|| (col_id == LST_DSPOPE_EXPENSE)
|| (col_id == LST_DSPOPE_INCOME)
)
)
continue;
menuitem = gtk_check_menu_item_new_with_label ( gtk_tree_view_column_get_title (column) );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gtk_tree_view_column_get_visible (column) );
gtk_widget_show (menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (list_txn_column_popup_menuitem_on_activate), column);
}
}
gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
gtk_menu_popup_at_pointer(GTK_MENU (menu), NULL);
#else
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, ev->button, ev->time);
#endif
}
return FALSE;
}
static GtkTreeViewColumn *
list_txn_column_amount_create(gint list_type, gchar *title, gint sortcolumnid, GtkTreeCellDataFunc func)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
if(list_type == LIST_TXN_TYPE_BOOK)
{
gtk_tree_view_column_set_reorderable(column, TRUE);
}
gtk_tree_view_column_set_cell_data_func(column, renderer, func, GINT_TO_POINTER(sortcolumnid), NULL);
return column;
}
static GtkTreeViewColumn *
list_txn_column_text_create(gint list_type, gchar *title, gint sortcolumnid, GtkTreeCellDataFunc func, gpointer user_data)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, title);
//#1861337 add icon for closed account
if( sortcolumnid == LST_DSPOPE_ACCOUNT )
{
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_account_icon, NULL, NULL);
}
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, func, user_data, NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
if(list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL)
{
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
}
return column;
}
static GtkTreeViewColumn *
list_txn_column_paynumber_create(gint list_type)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Pay./Number"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_info, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_info, GINT_TO_POINTER(2), NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_PAYNUMBER);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
if(list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL)
{
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
}
return column;
}
Transaction *list_txn_get_surround_transaction(GtkTreeView *treeview, Transaction **prev, Transaction **next)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeIter *previter, *nextiter;
GList *list;
Transaction *ope;
ope = NULL;
model = gtk_tree_view_get_model(treeview);
list = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
if(list != NULL)
{
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
if(prev != NULL)
{
previter = gtk_tree_iter_copy(&iter);
if( gtk_tree_model_iter_previous(model, previter) )
{
gtk_tree_model_get(model, previter, MODEL_TXN_POINTER, prev, -1);
}
gtk_tree_iter_free(previter);
}
if(next != NULL)
{
nextiter = gtk_tree_iter_copy(&iter);
if( gtk_tree_model_iter_next(model, nextiter) )
{
gtk_tree_model_get(model, nextiter, MODEL_TXN_POINTER, next, -1);
}
gtk_tree_iter_free(nextiter);
}
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
return ope;
}
Transaction *list_txn_get_active_transaction(GtkTreeView *treeview)
{
GtkTreeModel *model;
GList *list;
Transaction *ope;
ope = NULL;
model = gtk_tree_view_get_model(treeview);
list = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
if(list != NULL)
{
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
return ope;
}
static gboolean
gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
gpointer data)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeViewColumn *column;
GtkTreeModel *model;
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
gboolean retval = FALSE;
gint colid;
if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
&x, &y,
keyboard_tip,
&model, NULL, &iter) == FALSE )
return FALSE;
gtk_tree_view_get_path_at_pos(tree_view, x, y, &path, &column, NULL, NULL);
colid = gtk_tree_view_column_get_sort_column_id(column);
//if( colid == LST_DSPOPE_STATUS || colid == LST_DSPOPE_CLR )
if( colid == LST_DSPOPE_STATUS )
{
GString *node = g_string_sized_new(16);
Transaction *ope;
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
#if MYDEBUG == 1
gchar *txtpath = gtk_tree_path_to_string(path);
g_string_append_printf(node, "col:%d, row:%s\n0x%04x",
colid,
txtpath, ope->flags);
#endif
if( colid == LST_DSPOPE_STATUS )
{
gboolean addlf = FALSE;
if( ope->flags & OF_ISIMPORT )
{
g_string_append(node, _("Imported") );
addlf = TRUE;
}
if( ope->flags & OF_ISPAST )
{
if(addlf)
g_string_append(node, "\n" );
g_string_append(node, _("Past date") );
}
}
gtk_tooltip_set_markup (tooltip, node->str);
gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
g_string_free(node, TRUE);
retval = TRUE;
}
gtk_tree_path_free (path);
return retval;
}
static void list_txn_destroy( GtkWidget *widget, gpointer user_data )
{
struct list_txn_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[list_txn] destroy event occurred\n") );
if( data->save_column_width )
{
list_txn_get_columns(GTK_TREE_VIEW(data->treeview));
}
DB( g_print(" - view=%p, inst_data=%p\n", widget, data) );
g_free(data);
}
/*
** create our transaction list
** hb: Status Date PayNumber Payee Category Tags CLR (Amount) Expense, Income (Balance) Memo (Account) (Match)
** quic: flg Date (Account) Check # Payee Memo Category Tag att. Exp clr Inc Total/Balance
** ynab: (Account) flg Date (Check #) Payee Category Memo Exp Inc (Balance) clr
** mmex: flg Date Number (Account) Payee Status Category Tag Exp Inc (Balance) Notes
*/
GtkWidget *create_list_transaction(gint list_type, gboolean *pref_columns)
{
struct list_txn_data *data;
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column, *col_acc = NULL, *col_status = NULL, *col_match = NULL;
DB( g_print ("\n[list_txn] new\n") );
data = g_malloc0(sizeof(struct list_txn_data));
if(!data) return NULL;
data->list_type = list_type;
data->warnnocategory = FALSE;
data->save_column_width = FALSE;
/* create list store */
store = gtk_tree_store_new(
3, G_TYPE_POINTER, // MODEL_TXN_POINTER
G_TYPE_DOUBLE, // MODEL_TXN_SPLITAMT amount part of split for detail only
G_TYPE_POINTER // MODEL_TXN_SPLITPTR
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
data->treeview = treeview;
g_object_unref(store);
//store our private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, data) );
// connect our dispose function
g_signal_connect (treeview, "destroy", G_CALLBACK (list_txn_destroy), (gpointer)data);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
//gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
// COLUMN_DESCRIPTION);
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_MULTIPLE);
// 1 -- status/column pref
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Status"));
col_status = column;
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_cell_renderer_set_padding(renderer, 1, 0);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status, GINT_TO_POINTER(2), NULL);
//5.8.6
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status_dupgid, NULL, NULL);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_STATUS);
//gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//add system icon to 1st column
GtkWidget *img = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
gtk_widget_show(img);
gtk_tree_view_column_set_widget(column, img);
if( list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL )
g_signal_connect ( G_OBJECT (gtk_tree_view_column_get_button (column)),
"button-press-event",
G_CALLBACK ( list_txn_column_popup_callback ),
data );
// 2 -- flag
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Flag"));
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set(renderer, "xalign", 0.25, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_grpflag, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_GRPFLAG);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// 3 -- account
//5.2 Account is always created but not visible for BOOK
column = list_txn_column_text_create(list_type, _("Account"), LST_DSPOPE_ACCOUNT, list_txn_cell_data_func_account, NULL);
gtk_tree_view_column_set_reorderable(column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
col_acc = column;
// 4 -- match rate
//5.5 Match Rate
if(list_type == LIST_TXN_TYPE_XFERTARGET)
{
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Match"));
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
g_object_set(renderer, "xalign", 0.5, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_match_rate, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_MATCH);
//gtk_tree_view_column_set_resizable(column, TRUE);
col_match = column;
gtk_tree_view_column_set_clickable(column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
// 5 -- date
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Date"));
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_date, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_DATE);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//info
column = list_txn_column_paynumber_create(list_type);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Payee"), LST_DSPOPE_PAYEE, list_txn_cell_data_func_payee, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Memo"), LST_DSPOPE_MEMO, list_txn_cell_data_func_memo, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column status CLR */
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Status"));
gtk_tree_view_column_set_title(column, _("St."));
//#2043152
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_clr, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_clr, GINT_TO_POINTER(2), NULL);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_CLR);
//gtk_tree_view_column_set_sort_indicator (column, FALSE);
//gtk_tree_view_column_set_resizable(column, TRUE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Amount"), LST_DSPOPE_AMOUNT, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Expense"), LST_DSPOPE_EXPENSE, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Income"), LST_DSPOPE_INCOME, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Category"), LST_DSPOPE_CATEGORY, list_txn_cell_data_func_category, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//set expander column for LIST_TXN_TYPE_DETAIL
if(list_type == LIST_TXN_TYPE_DETAIL)
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Tags"), LST_DSPOPE_TAGS, list_txn_cell_data_func_tags, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
if(list_type == LIST_TXN_TYPE_BOOK)
{
column = list_txn_column_amount_create(list_type, _("Balance"), LST_DSPOPE_BALANCE, list_txn_cell_data_func_amount);
data->tvc_balance = column;
gtk_tree_view_column_set_clickable(column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
/* column 9: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* sort */
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_STATUS , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_STATUS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_DATE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_DATE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_PAYNUMBER, list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_PAYNUMBER), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_PAYEE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_PAYEE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_MEMO , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_MEMO), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_AMOUNT , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_AMOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_EXPENSE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_EXPENSE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_INCOME , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_INCOME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_CATEGORY, list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_CATEGORY), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_TAGS , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_TAGS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_CLR , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_CLR), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_ACCOUNT , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_ACCOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_MATCH , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_MATCH), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_GRPFLAG , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_GRPFLAG), NULL);
/* apply user preference for columns */
list_txn_set_columns(GTK_TREE_VIEW(treeview), pref_columns);
/* force account column for detail treeview */
gtk_tree_view_move_column_after(GTK_TREE_VIEW(treeview), col_acc, col_status);
/* move match column */
if(list_type == LIST_TXN_TYPE_XFERTARGET)
{
gtk_tree_view_move_column_after(GTK_TREE_VIEW(treeview), col_match, col_status);
}
/* by default book don't display acc column, except showall */
//#1821850 detail account column visible
gboolean visible = (list_type == LIST_TXN_TYPE_BOOK) ? FALSE: TRUE;
gtk_tree_view_column_set_visible (col_acc, visible);
/* set initial sort order */
DB( g_print("set sort to %d %d\n", PREFS->lst_ope_sort_id, PREFS->lst_ope_sort_order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), PREFS->lst_ope_sort_id, PREFS->lst_ope_sort_order);
//add tooltip
gtk_widget_set_has_tooltip (GTK_WIDGET (treeview), TRUE);
/* signals */
if(list_type == LIST_TXN_TYPE_BOOK)
g_signal_connect (GTK_TREE_SORTABLE(store), "sort-column-changed", G_CALLBACK (list_txn_sort_column_changed), data);
g_signal_connect (treeview, "query-tooltip",
G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
return(treeview);
}
homebank-5.9.7/src/dsp-account.h 0000644 0001750 0001750 00000006142 15021305420 015765 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_DSPACCOUNT_H__
#define __HB_DSPACCOUNT_H__
/* official GTK_RESPONSE are negative */
#define HB_RESPONSE_REFRESH 1
enum
{
ACTION_ACCOUNT_ADD,
ACTION_ACCOUNT_INHERIT,
ACTION_ACCOUNT_EDIT,
ACTION_ACCOUNT_MULTIEDIT,
ACTION_ACCOUNT_NONE,
ACTION_ACCOUNT_CLEAR,
ACTION_ACCOUNT_RECONCILE,
ACTION_ACCOUNT_DELETE,
ACTION_ACCOUNT_FILTER,
ACTION_ACCOUNT_CLOSE,
MAX_ACTION_ACCOUNT
};
enum {
FLG_REG_TITLE = 1 << 0, //1
FLG_REG_SENSITIVE = 1 << 1, //2
FLG_REG_BALANCE = 1 << 2, //4
FLG_REG_VISUAL = 1 << 3, //8
FLG_REG_REFRESHALL = 1 << 4 //16
};
enum {
HID_RANGE,
HID_TYPE,
HID_STATUS,
HID_SEARCH,
MAX_HID
};
struct hub_ledger_data
{
GtkWidget *window;
GActionGroup *actions;
GtkWidget *IB_accnotif, *LB_accnotif, *BT_info_showpending;
GtkWidget *IB_duplicate, *LB_duplicate, *NB_txn_daygap;
GtkWidget *IB_chkcatsign, *LB_chkcatsign;
GtkWidget *TB_bar;
GtkWidget *BT_add, *BT_herit, *BT_edit;
GtkWidget *BT_clear, *BT_reconcile;
GtkWidget *BT_multiedit, *BT_delete;
GtkWidget *BT_up, *BT_down;
GtkWidget *SW_lockreconciled, *IM_lockreconciled, *LB_lockreconciled;
GtkWidget *CY_range;
GtkWidget *CM_future;
GtkWidget *CY_flag;
GtkWidget *CY_type;
GtkWidget *CY_status;
// GtkWidget *CY_month, *NB_year;
GtkWidget *PO_hubfilter;
GtkWidget *BT_reset, *BT_refresh, *BT_filter;
GtkWidget *BT_lifnrg;
GtkWidget *TX_selection;
GtkWidget *ST_search;
GtkWidget *TX_daterange;
//GtkWidget *IM_closed;
//GtkWidget *LB_name;
GtkWidget *CM_minor;
//GtkWidget *LB_recon, *LB_clear, *LB_today, *LB_futur;
GtkWidget *TX_balance[4];
GtkWidget *LV_ope;
gint busy;
gchar *wintitle;
Account *acc;
Transaction *cur_ope;
GQueue *q_txn_clip; //txn clipboard copy/paste
GPtrArray *gpatxn; //quickfilter
gboolean showall;
gboolean closed;
gboolean lockreconciled;
gboolean do_sort;
/* status counters */
guint nb_pending;
gint hidden, total, similar, chkcatsign;
gdouble totalsum;
Filter *filter;
guint timer_tag;
gulong handler_id[MAX_HID];
//gint change; /* change shouldbe done directly */
};
#define DEFAULT_DELAY 750 /* Default delay in ms */
GtkWidget *hub_ledger_window_new(Account *acc);
void beta_hub_ledger_refresh_txn_opens(void);
void hub_ledger_window_init(GtkWidget *widget, gpointer user_data);
#endif /* __HOMEBANK_DSPACCOUNT_H__ */
homebank-5.9.7/src/ui-account.h 0000644 0001750 0001750 00000006621 14736461415 015641 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ACCOUNT_GTK_H__
#define __HB_ACCOUNT_GTK_H__
enum {
LST_DEFACC_SORT_POS = 1,
LST_DEFACC_SORT_NAME
};
enum
{
LST_DEFACC_TOGGLE,
LST_DEFACC_DATAS,
NUM_LST_DEFACC
};
enum
{
ACC_LST_INSERT_NORMAL,
ACC_LST_INSERT_REPORT
};
/* = = = = = = = = = = */
enum
{
ACTION_NEW,
ACTION_MODIFY,
ACTION_REMOVE,
};
enum
{
FIELD_NAME,
//todo: for stock account
//FIELD_TYPE,
FIELD_BANK,
FIELD_NUMBER,
FIELD_BUDGET,
FIELD_CLOSED,
FIELD_INITIAL,
FIELD_MINIMUM,
FIELD_CHEQUE1,
FIELD_CHEQUE2,
MAX_ACC_FIELD
};
struct ui_acc_manage_data
{
GList *tmp_list;
gint change;
gint action;
guint32 lastkey;
GtkWidget *dialog;
gboolean mapped_done;
GtkWidget *ST_search;
GtkWidget *LV_acc;
GtkWidget *BT_add, *BT_edit, *BT_rem;
GtkWidget *BT_up, *BT_down;
GtkWidget *notebook;
GtkWidget *CY_type;
GtkWidget *CY_curr;
GtkWidget *ST_institution;
GtkWidget *ST_number;
GtkWidget *ST_group;
GtkWidget *ST_website;
GtkWidget *TB_notes;
GtkWidget *CM_closed;
GtkWidget *ST_initial;
//GtkWidget *ST_warning;
GtkWidget *ST_minimum;
GtkWidget *ST_maximum;
GtkWidget *CY_template;
GtkWidget *CM_nosummary;
GtkWidget *CM_nobudget;
GtkWidget *CM_noreport;
GtkWidget *CM_outflowsum;
GtkWidget *ST_cheque1;
GtkWidget *ST_cheque2;
};
struct accPopContext
{
GtkTreeModel *model;
guint32 except_key;
//guint32 kcur;
gint insert_type;
};
GtkWidget *ui_acc_manage_dialog (void);
/* = = = = = = = = = = */
void ui_acc_entry_popover_populate(GtkBox *box, GHashTable *hash, gint insert_type);
void ui_acc_entry_popover_populate_except(GtkBox *box, GHashTable *hash, guint except_key, gint insert_type);
GtkTreeModel *ui_acc_entry_popover_get_model(GtkBox *box);
GtkWidget *ui_acc_entry_popover_get_entry(GtkBox *box);
Account *ui_acc_entry_popover_get(GtkBox *box);
guint32 ui_acc_entry_popover_get_key_add_new(GtkBox *box);
guint32 ui_acc_entry_popover_get_key(GtkBox *box);
void ui_acc_entry_popover_set_single(GtkBox *box);
void ui_acc_entry_popover_set_active(GtkBox *box, guint32 key);
GtkWidget *ui_acc_entry_popover_new(GtkWidget *label);
/* = = = = = = = = = = */
guint ui_acc_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter);
void ui_acc_listview_quick_select(GtkTreeView *treeview, const gchar *uri);
void ui_acc_listview_set_active(GtkTreeView *treeview, guint32 key);
void ui_acc_listview_add(GtkTreeView *treeview, Account *item);
guint32 ui_acc_listview_get_selected_key(GtkTreeView *treeview);
void ui_acc_listview_remove_selected(GtkTreeView *treeview);
void ui_acc_listview_populate(GtkWidget *view, gint insert_type, gchar *needle);
GtkWidget *ui_acc_listview_new(gboolean withtoggle);
#endif
homebank-5.9.7/src/hb-encoding.h 0000644 0001750 0001750 00000005757 14736461415 015760 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ENCODING_H__
#define __HB_ENCODING_H__
struct _GeditEncoding
{
gint index;
const gchar *charset;
const gchar *name;
};
typedef struct _GeditEncoding GeditEncoding;
typedef enum
{
GEDIT_ENCODING_ISO_8859_1,
GEDIT_ENCODING_ISO_8859_2,
GEDIT_ENCODING_ISO_8859_3,
GEDIT_ENCODING_ISO_8859_4,
GEDIT_ENCODING_ISO_8859_5,
GEDIT_ENCODING_ISO_8859_6,
GEDIT_ENCODING_ISO_8859_7,
GEDIT_ENCODING_ISO_8859_8,
GEDIT_ENCODING_ISO_8859_8_I,
GEDIT_ENCODING_ISO_8859_9,
GEDIT_ENCODING_ISO_8859_10,
GEDIT_ENCODING_ISO_8859_13,
GEDIT_ENCODING_ISO_8859_14,
GEDIT_ENCODING_ISO_8859_15,
GEDIT_ENCODING_ISO_8859_16,
GEDIT_ENCODING_UTF_7,
GEDIT_ENCODING_UTF_16,
GEDIT_ENCODING_UTF_16_BE,
GEDIT_ENCODING_UTF_16_LE,
GEDIT_ENCODING_UTF_32,
GEDIT_ENCODING_UCS_2,
GEDIT_ENCODING_UCS_4,
GEDIT_ENCODING_ARMSCII_8,
GEDIT_ENCODING_BIG5,
GEDIT_ENCODING_BIG5_HKSCS,
GEDIT_ENCODING_CP_866,
GEDIT_ENCODING_EUC_JP,
GEDIT_ENCODING_EUC_JP_MS,
GEDIT_ENCODING_CP932,
GEDIT_ENCODING_EUC_KR,
GEDIT_ENCODING_EUC_TW,
GEDIT_ENCODING_GB18030,
GEDIT_ENCODING_GB2312,
GEDIT_ENCODING_GBK,
GEDIT_ENCODING_GEOSTD8,
GEDIT_ENCODING_HZ,
GEDIT_ENCODING_IBM_850,
GEDIT_ENCODING_IBM_852,
GEDIT_ENCODING_IBM_855,
GEDIT_ENCODING_IBM_857,
GEDIT_ENCODING_IBM_862,
GEDIT_ENCODING_IBM_864,
GEDIT_ENCODING_ISO_2022_JP,
GEDIT_ENCODING_ISO_2022_KR,
GEDIT_ENCODING_ISO_IR_111,
GEDIT_ENCODING_JOHAB,
GEDIT_ENCODING_KOI8_R,
GEDIT_ENCODING_KOI8__R,
GEDIT_ENCODING_KOI8_U,
GEDIT_ENCODING_SHIFT_JIS,
GEDIT_ENCODING_TCVN,
GEDIT_ENCODING_TIS_620,
GEDIT_ENCODING_UHC,
GEDIT_ENCODING_VISCII,
GEDIT_ENCODING_WINDOWS_1250,
GEDIT_ENCODING_WINDOWS_1251,
GEDIT_ENCODING_WINDOWS_1252,
GEDIT_ENCODING_WINDOWS_1253,
GEDIT_ENCODING_WINDOWS_1254,
GEDIT_ENCODING_WINDOWS_1255,
GEDIT_ENCODING_WINDOWS_1256,
GEDIT_ENCODING_WINDOWS_1257,
GEDIT_ENCODING_WINDOWS_1258,
GEDIT_ENCODING_LAST,
GEDIT_ENCODING_UTF_8,
GEDIT_ENCODING_UNKNOWN
} GeditEncodingIndex;
const GeditEncoding *gedit_encoding_get_from_index (gint index);
const GeditEncoding *gedit_encoding_get_utf8 (void);
const gchar *homebank_file_getencoding(gchar *filename);
gchar *homebank_utf8_ensure(gchar *buffer);
#endif
homebank-5.9.7/src/rep-vehicle.c 0000644 0001750 0001750 00000121046 15005634150 015753 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "rep-vehicle.h"
#include "list-operation.h"
#include "gtk-chart.h"
#include "gtk-dateentry.h"
#include "dsp-mainwindow.h"
#include "ui-category.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* prototypes */
static void repvehicle_export_csv(GtkWidget *widget, gpointer user_data);
static void repvehicle_compute(GtkWidget *widget, gpointer user_data);
static void repvehicle_update(GtkWidget *widget, gpointer user_data);
static void repvehicle_setup_categories(struct repvehicle_data *data, GArray *array);
static GtkWidget *list_vehicle_create(void);
static void repvehicle_date_change(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
DB( g_print("\n[vehiclecost] date change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
// set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->CY_range, data->handler_id[HID_REPVEHICLE_RANGE]);
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_MISC_CUSTOM);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPVEHICLE_RANGE]);
repvehicle_compute(widget, NULL);
}
static void repvehicle_action_refresh(GtkWidget *toolbutton, gpointer user_data)
{
struct repvehicle_data *data = user_data;
repvehicle_compute(data->window, NULL);
}
static void repvehicle_action_export(GtkWidget *toolbutton, gpointer user_data)
{
struct repvehicle_data *data = user_data;
repvehicle_export_csv(data->window, NULL);
}
static void repvehicle_range_change(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
gint range;
DB( g_print("\n[vehiclecost] range change\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = hbtk_combo_box_get_active_id(GTK_COMBO_BOX(data->CY_range));
if(range != FLT_RANGE_MISC_CUSTOM)
{
filter_preset_daterange_set(data->filter, range, 0);
//#2046032 set min/max date for both widget
//5.8 check for error
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_mindate), ( data->filter->mindate > data->filter->maxdate ) ? TRUE : FALSE);
gtk_date_entry_set_error(GTK_DATE_ENTRY(data->PO_maxdate), ( data->filter->maxdate < data->filter->mindate ) ? TRUE : FALSE);
g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPVEHICLE_MINDATE]);
g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPVEHICLE_MAXDATE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPVEHICLE_MINDATE]);
g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPVEHICLE_MAXDATE]);
repvehicle_compute(widget, NULL);
}
}
static gint repvehicle_transaction_compare_func(CarCost *a, CarCost *b)
{
gint retval;
//retval = (gint)(a->ope->date - b->ope->date);
//if( retval == 0 )
retval = a->meter - b->meter;
return retval;
}
static void repvehicle_export_csv(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gchar *filename = NULL;
GIOChannel *io;
gchar *outstr, *name;
DB( g_print("\n[repvehicle] export csv\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
name = "hb-vehicle.csv";
if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
// header
outstr = g_strdup_printf("%s;%s;%s;%s;%s;%s;%s;%s\n",
_("Date"),
_("Meter"),
_("Fuel"),
_("Price"),
_("Amount"),
_("Dist."),
PREFS->vehicle_unit_100,
PREFS->vehicle_unit_distbyvol
);
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
guint32 julian;
gint meter, dist;
//#1947931 distbyvol in double not int
gdouble fuel, price, amount, centkm, distbyvol;
gboolean partial;
gchar datebuf[16];
gtk_tree_model_get (model, &iter,
LST_CAR_DATE , &julian,
LST_CAR_METER , &meter,
LST_CAR_FUEL , &fuel,
LST_CAR_PRICE , &price,
LST_CAR_AMOUNT , &amount,
LST_CAR_DIST , &dist,
LST_CAR_100KM , ¢km,
LST_CAR_DISTBYVOL, &distbyvol,
LST_CAR_PARTIAL, &partial,
-1);
hb_sprint_date(datebuf, julian);
outstr = g_strdup_printf("%s;%d;%.2f;%.2f;%.2f;%d;%.2f;%.2f;%d\n",
datebuf, meter, fuel, price, amount, dist, centkm, distbyvol, partial);
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
DB( g_print("%s", outstr) );
g_free(outstr);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
g_io_channel_unref (io);
}
g_free( filename );
}
}
//#1277622
static gboolean
repvehicle_eval_memofield(CarCost *item, gchar *text)
{
gboolean retval = FALSE;
gchar *d, *v1, *v2;
gint len;
if(item != NULL)
{
item->meter = 0;
item->fuel = 0.0;
item->partial = FALSE;
}
if( text != NULL)
{
//TODO: optim here
len = strlen(text);
d = g_strstr_len(text, len, "d=");
v1 = g_strstr_len(text, len, "v=");
v2 = g_strstr_len(text, len, "v~");
if(d && (v1 || v2))
{
retval = TRUE;
if(item != NULL)
{
item->meter = atol(d+2);
if(v1)
{
item->fuel = g_strtod(v1+2, NULL);
}
else
{
item->fuel = g_strtod(v2+2, NULL);
item->partial = TRUE;
}
}
}
}
return retval;
}
static gboolean
my_g_array_exists(GArray *array, guint32 kcat)
{
gboolean retval = FALSE;
Category *cat;
guint32 *key, i;
//#2000452 removed binary_search
for(i=0;ilen;i++)
{
key = &g_array_index(array, guint32, i);
cat = da_cat_get(*key);
if( (cat != NULL) && (cat->key == kcat) )
{
retval = TRUE;
break;
}
}
DB( g_print(" normal search %d ? %d\n", kcat, retval) );
return retval;
}
static void
my_garray_add(GArray *array, guint32 key)
{
Category *cat;
DB( g_print("\n[vehiclecost] garray_add\n") );
cat = da_cat_get(key);
if(!cat)
return;
//#1873660 add parent as well
if( cat->parent > 0 )
{
if( my_g_array_exists(array, cat->parent) == FALSE )
{
DB( g_print(" store kcat=%d '%s' (parent)\n", cat->parent, cat->fullname) );
g_array_append_vals(array, &cat->parent, 1);
}
}
//add category
if( my_g_array_exists(array, cat->key) == FALSE )
{
DB( g_print(" store kcat=%d '%s'\n", cat->key, cat->name) );
g_array_append_vals(array, &cat->key, 1);
}
}
static void repvehicle_compute(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
GArray *ga_cat;
GList *list;
DB( g_print("\n[vehiclecost] compute\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
// clear the glist
da_vehiclecost_destroy(data->vehicle_list);
data->vehicle_list = NULL;
g_queue_free (data->txn_queue);
data->txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
// pass1 to collect categories
ga_cat = g_array_new(FALSE, FALSE, sizeof(guint32));
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
// eval normal transaction
if(!(ope->flags & OF_SPLIT))
{
if( repvehicle_eval_memofield(NULL, ope->memo) == TRUE )
{
my_garray_add(ga_cat, ope->kcat);
}
}
else
{
guint i, nbsplit = da_splits_length(ope->splits);
Split *split;
for(i=0;isplits, i);
if( repvehicle_eval_memofield(NULL, split->memo) == TRUE )
{
my_garray_add(ga_cat, split->kcat);
}
}
}
list = g_list_next(list);
}
//here ga_cat contains cat+subcat of txn where there is vehicle cost data d= v=
// pass2: collect transactions + fill carcost items
list = g_queue_peek_head_link(data->txn_queue);
while (list != NULL)
{
Transaction *ope = list->data;
Category *cat;
CarCost tmp, *item;
//TODO: rely on a persistent flag // attribution ?
// eval normal transaction
if(!(ope->flags & OF_SPLIT))
{
cat = da_cat_get(ope->kcat);
if( (cat != NULL) )
{
if( (my_g_array_exists(ga_cat, cat->key) || my_g_array_exists(ga_cat, cat->parent) ) )
{
if( repvehicle_eval_memofield(&tmp, ope->memo) == TRUE )
{
item = da_vehiclecost_malloc();
item->kcat = ope->kcat;
item->date = ope->date;
item->memo = ope->memo;
item->amount = hb_amount_base(ope->amount, ope->kcur);
item->meter = tmp.meter;
item->fuel = tmp.fuel;
item->partial = tmp.partial;
data->vehicle_list = g_list_prepend(data->vehicle_list, item);
DB( g_print(" store txn kcat=%d acc=%d %4.2f '%s' \n", ope->kcat, ope->kacc, ope->amount, ope->memo) );
}
else
{
item = da_vehiclecost_malloc();
item->kcat = ope->kcat;
item->date = ope->date;
item->amount = hb_amount_base(ope->amount, ope->kcur);
item->meter = 0;
data->vehicle_list = g_list_prepend(data->vehicle_list, item);
DB( g_print(" store txn kcat=%d acc=%d %4.2f '%s' (other)\n", ope->kcat, ope->kacc, ope->amount, ope->memo) );
}
}
}
}
// eval split transaction
else
{
guint i, nbsplit = da_splits_length(ope->splits);
Split *split;
for(i=0;isplits, i);
cat = da_cat_get(split->kcat);
if( (cat != NULL) )
{
if( (my_g_array_exists(ga_cat, cat->key) || my_g_array_exists(ga_cat, cat->parent) ) )
{
if( repvehicle_eval_memofield(&tmp, split->memo) == TRUE )
{
item = da_vehiclecost_malloc();
item->kcat = split->kcat;
item->date = ope->date;
item->memo = split->memo;
item->amount = hb_amount_base(split->amount, ope->kcur);
item->meter = tmp.meter;
item->fuel = tmp.fuel;
item->partial = tmp.partial;
data->vehicle_list = g_list_prepend(data->vehicle_list, item);
DB( g_print(" store txn kcat=%d acc=%d %4.2f '%s' (split)\n", split->kcat, ope->kacc, split->amount, split->memo) );
}
else
{
item = da_vehiclecost_malloc();
item->kcat = split->kcat;
item->date = ope->date;
item->amount = hb_amount_base(split->amount, ope->kcur);
item->meter = tmp.meter;
data->vehicle_list = g_list_prepend(data->vehicle_list, item);
DB( g_print("- store txn kcat=%d acc=%d %4.2f '%s' (other plit)\n", ope->kcat, ope->kacc, ope->amount, ope->memo) );
}
}
}
}
}
list = g_list_next(list);
}
repvehicle_setup_categories(data, ga_cat);
g_array_free(ga_cat, TRUE);
// sort by meter #399170
data->vehicle_list = g_list_sort(data->vehicle_list, (GCompareFunc)repvehicle_transaction_compare_func);
repvehicle_update(widget, NULL);
}
static void repvehicle_update(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
GList *list;
gchar *buf;
guint32 selkey;
gint nb_refuel = 0;
int nb_fullrefuel = 0;
guint lastmeter = 0;
DB( g_print("\n[vehiclecost] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
// get the category key
//selkey = ui_cat_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_cat));
selkey = ui_cat_entry_popover_get_key(GTK_BOX(data->PO_cat));
DB( g_print(" selkey=%d\n\n", selkey) );
// clear and detach our model
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
gtk_list_store_clear (GTK_LIST_STORE(model));
g_object_ref(model);
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), NULL);
data->total_misccost = 0;
data->total_fuelcost = 0;
data->total_fuel = 0;
data->total_dist = 0;
gdouble partial_fuel = 0;
guint partial_dist = 0;
if( selkey == 0 )
goto noselkey;
list = g_list_first(data->vehicle_list);
while (list != NULL)
{
CarCost *item = list->data;
Category *itemcat;
gint dist;
gdouble centkm;
gdouble distbyvol;
gdouble amount;
itemcat = da_cat_get(item->kcat);
if(! itemcat )
continue;
if( (itemcat->key == selkey || itemcat->parent == selkey) )
{
DB( g_print(" add treeview kcat=%d %s\n", item->kcat, item->memo) );
amount = item->amount;
if( item->meter == 0 )
{
data->total_misccost += amount;
}
else
{
if(nb_refuel > 0 )
{
//previtem = g_list_nth_data(data->vehicle_list, nb_refuel-1);
//if(previtem != NULL) previtem->dist = item->meter - previtem->meter;
//DB( g_print(" + previous item dist = %d\n", item->meter - previtem->meter) );
item->dist = item->meter - lastmeter;
//DB( g_print(" + last meter = %d\n", lastmeter) );
}
lastmeter = item->meter;
nb_refuel++;
//DB( g_print("\n eval %02d :: d=%d v=%.2f $%.2f dist=%d\n", nb_refuel, item->meter, item->fuel, amount, item->dist) );
//DB( g_print(" + %s :: pf=%.2f pd=%d\n", item->partial ? "partial" : "full", partial_fuel, partial_dist) );
centkm = 0;
dist = 0;
//bugfix #159066 partial/full
if(item->partial == FALSE)
{
//#1836380 if we don't have a full already, the computing will be wrong
if( nb_fullrefuel > 0 )
{
// full refuel after partial
if(partial_fuel && partial_dist)
{
partial_fuel += item->fuel;
partial_dist += item->dist;
dist = item->dist;
centkm = partial_dist != 0 ? partial_fuel * 100 / partial_dist : 0;
//DB( g_print(" + centkm=%.2f %.2f * 100 / %d (full after partial)\n", centkm, partial_fuel, partial_dist) );
}
else
{
dist = item->dist;
centkm = item->dist != 0 ? item->fuel * 100 / item->dist : 0;
//DB( g_print(" + centkm=%.2f :: %.2f * 100 / %d (full after full)\n", centkm, item->fuel, item->dist) );
}
}
partial_fuel = 0;
partial_dist = 0;
nb_fullrefuel++;
}
// partial refuel
else
{
partial_fuel += item->fuel;
partial_dist += item->dist;
dist = item->dist;
//DB( g_print(" + centkm= not computable\n") );
}
distbyvol = 0;
if(centkm != 0)
//#2073233 round to 1 digit
distbyvol = hb_amount_round((1/centkm)*100, 1);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_CAR_DATE, item->date,
LST_CAR_MEMO, item->memo,
LST_CAR_METER, item->meter,
LST_CAR_FUEL, item->fuel,
LST_CAR_PRICE, ABS(amount) / item->fuel,
LST_CAR_AMOUNT, amount,
LST_CAR_DIST, dist,
LST_CAR_100KM, centkm,
LST_CAR_DISTBYVOL, distbyvol,
LST_CAR_PARTIAL, item->partial,
-1);
//DB( g_print("\n insert d=%d v=%4.2f $%8.2f %d %5.2f\n", item->meter, item->fuel, amount, dist, centkm) );
if(item->dist)
{
data->total_fuelcost += amount;
data->total_fuel += item->fuel;
data->total_dist += item->dist;
}
}
}
list = g_list_next(list);
}
noselkey:
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
g_object_unref(model);
gdouble coef = data->total_dist ? 100 / (gdouble)data->total_dist : 0;
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
DB( g_print(" coef = 100 / %.2f = %.2f\n", (gdouble)data->total_dist, coef) );
// row 1 is for 100km
/*
gtk_label_set_text(GTK_LABEL(data->LA_total[1][1]), "1:1"); //Consumption
gtk_label_set_text(GTK_LABEL(data->LA_total[2][1]), "2:1"); //Fuel cost
gtk_label_set_text(GTK_LABEL(data->LA_total[3][1]), "3:1"); //Other cost
gtk_label_set_text(GTK_LABEL(data->LA_total[4][1]), "4:1"); //Total cost
*/
// 100km fuel
buf = g_strdup_printf(PREFS->vehicle_unit_vol, data->total_fuel * coef);
gtk_label_set_text(GTK_LABEL(data->LA_avera[CAR_RES_FUEL]), buf);
g_free(buf);
// 100km fuelcost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_avera[CAR_RES_FUELCOST]), data->total_fuelcost * coef, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_avera[CAR_RES_FUELCOST]), data->total_fuelcost * coef, GLOBALS->kcur, GLOBALS->minor);
// 100km other cost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_avera[CAR_RES_OTHERCOST]), data->total_misccost * coef, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_avera[CAR_RES_OTHERCOST]), data->total_misccost * coef, GLOBALS->kcur, GLOBALS->minor);
// 100km cost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_avera[CAR_RES_TOTALCOST]), (data->total_fuelcost + data->total_misccost) * coef, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_avera[CAR_RES_TOTALCOST]), (data->total_fuelcost + data->total_misccost) * coef, GLOBALS->kcur, GLOBALS->minor);
// row 2 is for total
/*
gtk_label_set_text(GTK_LABEL(data->LA_total[1][2]), "1:2"); //Consumption
gtk_label_set_text(GTK_LABEL(data->LA_total[2][2]), "2:2"); //Fuel cost
gtk_label_set_text(GTK_LABEL(data->LA_total[3][2]), "3:2"); //Other cost
gtk_label_set_text(GTK_LABEL(data->LA_total[4][2]), "4:2"); //Total
*/
// total distance
buf = g_strdup_printf(PREFS->vehicle_unit_dist0, data->total_dist);
gtk_label_set_text(GTK_LABEL(data->LA_total[CAR_RES_METER]), buf);
g_free(buf);
// total fuel
buf = g_strdup_printf(PREFS->vehicle_unit_vol, data->total_fuel);
gtk_label_set_text(GTK_LABEL(data->LA_total[CAR_RES_FUEL]), buf);
g_free(buf);
// total fuelcost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_total[CAR_RES_FUELCOST]), data->total_fuelcost, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_total[CAR_RES_FUELCOST]), data->total_fuelcost, GLOBALS->kcur, GLOBALS->minor);
// total other cost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_total[CAR_RES_OTHERCOST]), data->total_misccost, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_total[CAR_RES_OTHERCOST]), data->total_misccost, GLOBALS->kcur, GLOBALS->minor);
// total cost
//hb_label_set_colvaluecurr(GTK_LABEL(data->LA_total[CAR_RES_TOTALCOST]), data->total_fuelcost + data->total_misccost, GLOBALS->kcur);
hb_label_set_colvalue(GTK_LABEL(data->LA_total[CAR_RES_TOTALCOST]), data->total_fuelcost + data->total_misccost, GLOBALS->kcur, GLOBALS->minor);
}
static void repvehicle_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct repvehicle_data *data;
DB( g_print("\n[vehiclecost] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
repvehicle_update(widget, NULL);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
/*
statistic_update_total(widget,NULL);
//hbfile_update(data->LV_acc, (gpointer)4);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
gtk_chart_show_minor(GTK_CHART(data->RE_bar), minor);
gtk_chart_show_minor(GTK_CHART(data->RE_pie), minor);
*/
}
static void repvehicle_setup_categories(struct repvehicle_data *data, GArray *array)
{
Category *cat;
guint32 kcat, *key, i;
DB( g_print("\n[vehiclecost] setup categories\n") );
kcat = GLOBALS->vehicle_category;
//g_signal_handler_block(data->PO_cat, data->handler_id[HID_REPVEHICLE_VEHICLE]);
if( array != NULL )
{
//get previous category
//kcat = ui_cat_comboboxentry_get_key(GTK_COMBO_BOX(data->PO_cat));
kcat = ui_cat_entry_popover_get_key(GTK_BOX(data->PO_cat));
//populate with the array
//ui_cat_combobox_entry_clear(GTK_COMBO_BOX(data->PO_cat));
ui_cat_entry_popover_clear(GTK_BOX(data->PO_cat));
cat = da_cat_get(0);
if(cat)
{
DB( g_print(" add %d '%s'\n", cat->key, cat->fullname) );
//ui_cat_comboboxentry_add(GTK_COMBO_BOX(data->PO_cat), cat);
ui_cat_entry_popover_add(GTK_BOX(data->PO_cat), cat);
}
for(i=0;ilen;i++)
{
key = &g_array_index(array, guint32, i);
cat = da_cat_get(*key);
if(cat)
{
DB( g_print(" add %d %s\n", cat->key, cat->fullname) );
//ui_cat_comboboxentry_add(GTK_COMBO_BOX(data->PO_cat), cat);
ui_cat_entry_popover_add(GTK_BOX(data->PO_cat), cat);
}
}
}
//ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(data->PO_cat), kcat);
ui_cat_entry_popover_set_active(GTK_BOX(data->PO_cat), kcat);
//g_signal_handler_unblock(data->PO_cat, data->handler_id[HID_REPVEHICLE_VEHICLE]);
}
static GtkWidget *
repvehicle_toolbar_create(struct repvehicle_data *data)
{
GtkWidget *toolbar, *button;
toolbar = gtk_toolbar_new();
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_REFRESH, _("Refresh"), _("Refresh results"));
data->BT_refresh = button;
button = hbtk_toolbar_add_toolbutton(GTK_TOOLBAR(toolbar), ICONNAME_HB_FILE_EXPORT, _("Export"), _("Export as CSV"));
data->BT_export = button;
return toolbar;
}
//reset the filter
static void repvehicle_filter_setup(struct repvehicle_data *data)
{
DB( g_print("\n[vehiclecost] reset filter\n") );
filter_reset(data->filter);
/* 3.4 : make int transfer out of stats */
filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
filter_preset_type_set(data->filter, FLT_TYPE_INTXFER, FLT_EXCLUDE);
//g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPVEHICLE_MINDATE]);
//g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPVEHICLE_MAXDATE]);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
//g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPVEHICLE_MINDATE]);
//g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPVEHICLE_MAXDATE]);
}
static void repvehicle_window_setup(struct repvehicle_data *data)
{
DB( g_print("\n[vehiclecost] setup\n") );
DB( g_print(" init data\n") );
repvehicle_filter_setup(data);
DB( g_print(" populate\n") );
repvehicle_setup_categories(data, NULL);
DB( g_print(" set widgets default\n") );
hbtk_combo_box_set_active_id(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
DB( g_print(" connect widgets signals\n") );
g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repvehicle_toggle_minor), NULL);
data->handler_id[HID_REPVEHICLE_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repvehicle_date_change), (gpointer)data);
data->handler_id[HID_REPVEHICLE_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repvehicle_date_change), (gpointer)data);
data->handler_id[HID_REPVEHICLE_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repvehicle_range_change), NULL);
//data->handler_id[HID_REPVEHICLE_VEHICLE] = g_signal_connect (data->PO_cat, "changed", G_CALLBACK (repvehicle_update), NULL);
data->handler_id[HID_REPVEHICLE_VEHICLE] = g_signal_connect (ui_cat_entry_popover_get_entry(GTK_BOX(data->PO_cat)), "changed", G_CALLBACK (repvehicle_update), NULL);
g_signal_connect (G_OBJECT (data->BT_refresh), "clicked", G_CALLBACK (repvehicle_action_refresh), (gpointer)data);
g_signal_connect (G_OBJECT (data->BT_export) , "clicked", G_CALLBACK (repvehicle_action_export), (gpointer)data);
}
static gboolean repvehicle_window_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repvehicle_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n[vehiclecost] window mapped\n") );
//setup, init and show window
repvehicle_window_setup(data);
repvehicle_compute(data->window, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static gboolean
repvehicle_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct repvehicle_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[vehiclecost] dispose\n") );
g_queue_free (data->txn_queue);
da_vehiclecost_destroy(data->vehicle_list);
da_flt_free(data->filter);
g_free(data);
//store position and size
wg = &PREFS->cst_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
//enable define windows
GLOBALS->define_off--;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
//unref window to our open window list
GLOBALS->openwindows = g_slist_remove(GLOBALS->openwindows, widget);
return FALSE;
}
//allocate our object/memory
static void repvehicle_window_acquire(struct repvehicle_data *data)
{
DB( g_print("\n[vehiclecost] acquire\n") );
data->txn_queue = g_queue_new ();
data->filter = da_flt_malloc();
data->vehicle_list = NULL;
}
// the window creation
GtkWidget *repvehicle_window_new(void)
{
struct repvehicle_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *vbox, *scrollwin, *treeview;
GtkWidget *label, *widget, *table;
gint row, col;
DB( g_print("\n[vehiclecost] new\n") );
data = g_malloc0(sizeof(struct repvehicle_data));
if(!data) return NULL;
repvehicle_window_acquire(data);
//disable define windows
GLOBALS->define_off++;
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//ref window to our open window list
GLOBALS->openwindows = g_slist_prepend(GLOBALS->openwindows, window);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
gtk_window_set_title (GTK_WINDOW (window), _("Vehicle cost report"));
//window contents
mainbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
hb_widget_set_margin(GTK_WIDGET(mainbox), SPACING_SMALL);
gtk_window_set_child(GTK_WINDOW(window), mainbox);
//control part
table = gtk_grid_new ();
gtk_widget_set_hexpand (GTK_WIDGET(table), FALSE);
gtk_box_prepend (GTK_BOX (mainbox), table);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0;
label = make_label_group(_("Display"));
gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
row++;
//label = make_label_widget(_("Vehi_cle:"));
//#2001566 make label consistent with properties dialog
label = make_label_widget(_("_Category:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
//widget = ui_cat_comboboxentry_new(label);
widget = ui_cat_entry_popover_new(label);
data->PO_cat = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
row++;
widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
data->CM_minor = widget;
gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
//-- filter
row++;
widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
gtk_widget_set_margin_top(widget, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
row++;
label = make_label_group(_("Filter"));
gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
row++;
//label = make_label_group(_("Date filter"));
label = make_label_group(_("Date"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 2, 1);
row++;
label = make_label_widget(_("_Range:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->CY_range = make_daterange(label, DATE_RANGE_FLAG_CUSTOM_DISABLE);
gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
row++;
label = make_label_widget(_("_From:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_mindate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
row++;
label = make_label_widget(_("_To:"));
gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
data->PO_maxdate = gtk_date_entry_new(label);
gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
//part: info + report
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_margin_start (vbox, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (mainbox), vbox);
widget = repvehicle_toolbar_create(data);
data->TB_bar = widget;
gtk_box_prepend (GTK_BOX (vbox), widget);
// total
table = gtk_grid_new ();
gtk_widget_set_hexpand (GTK_WIDGET(table), FALSE);
gtk_box_prepend (GTK_BOX (vbox), table);
hb_widget_set_margin(GTK_WIDGET(table), SPACING_SMALL);
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
row = 0; col = 1;
label = make_label_widget(_("Meter:"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
col++;
label = make_label_widget(_("Consumption:"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
col++;
label = make_label_widget(_("Fuel cost:"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
col++;
label = make_label_widget(_("Other cost:"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
col++;
label = make_label_widget(_("Total cost:"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
row++;
col = 0;
label = make_label_widget(PREFS->vehicle_unit_100);
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
for(col = 1;colLA_avera[col] = label;
}
row++;
col = 0;
label = make_label_widget(_("Total"));
gtk_grid_attach (GTK_GRID (table), label, col, row, 1, 1);
for(col = 1;colLA_total[col] = label;
}
//detail
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
treeview = list_vehicle_create();
data->LV_report = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
// connect dialog signals
g_signal_connect (window, "delete-event", G_CALLBACK (repvehicle_window_dispose), (gpointer)data);
g_signal_connect (window, "map-event" , G_CALLBACK (repvehicle_window_mapped), NULL);
// setup, init and show window
wg = &PREFS->cst_wg;
if( wg->l && wg->t )
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
/* toolbar */
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_widget_show_all (window);
//minor ?
hb_widget_visible(data->CM_minor, PREFS->euro_active);
return(window);
}
/*
** ============================================================================
*/
static void list_vehicle_cell_data_func_date (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
GDate *date;
guint32 julian;
gchar buf[256];
gtk_tree_model_get(model, iter,
LST_CAR_DATE, &julian,
-1);
date = g_date_new_julian (julian);
g_date_strftime (buf, 256-1, PREFS->date_format, date);
g_date_free(date);
g_object_set(renderer, "text", buf, NULL);
}
static void list_vehicle_cell_data_func_distbyvol (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gdouble distance;
gchar *text;
gtk_tree_model_get(model, iter, user_data, &distance, -1);
if(distance != 0)
{
text = g_strdup_printf(PREFS->vehicle_unit_dist1, distance);
g_object_set(renderer, "text", text, NULL);
g_free(text);
}
else
g_object_set(renderer, "text", "-", NULL);
}
static void list_vehicle_cell_data_func_distance (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
guint distance;
gchar *text;
gtk_tree_model_get(model, iter, user_data, &distance, -1);
if(distance != 0)
{
text = g_strdup_printf(PREFS->vehicle_unit_dist0, distance);
g_object_set(renderer, "text", text, NULL);
g_free(text);
}
else
g_object_set(renderer, "text", "-", NULL);
}
static void list_vehicle_cell_data_func_volume (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gdouble volume;
gboolean partial;
gchar *text;
gtk_tree_model_get(model, iter, user_data, &volume, LST_CAR_PARTIAL, &partial, -1);
if(volume != 0)
{
text = g_strdup_printf(PREFS->vehicle_unit_vol, volume);
g_object_set(renderer,
"text", text,
"style-set", TRUE,
"style", partial ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
NULL);
g_free(text);
}
else
g_object_set(renderer, "text", "-", NULL);
}
static void list_vehicle_cell_data_func_amount (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gdouble value;
gchar *color;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gtk_tree_model_get(model, iter,
user_data, &value,
-1);
if( value )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
color = get_normal_color_amount(value);
g_object_set(renderer,
"foreground", color,
"text", buf,
NULL); }
else
{
g_object_set(renderer, "text", "", NULL);
}
}
static GtkTreeViewColumn *list_vehicle_column_volume(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_vehicle_cell_data_func_volume, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static GtkTreeViewColumn *list_vehicle_column_distbyvol(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_vehicle_cell_data_func_distbyvol, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static GtkTreeViewColumn *list_vehicle_column_distance(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_vehicle_cell_data_func_distance, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
static GtkTreeViewColumn *list_vehicle_column_amount(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_vehicle_cell_data_func_amount, GINT_TO_POINTER(id), NULL);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_sort_column_id (column, id);
return column;
}
/*
** create our statistic list
*/
static GtkWidget *list_vehicle_create(void)
{
GtkListStore *store;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
/* create list store */
store = gtk_list_store_new(
NUM_LST_CAR,
G_TYPE_UINT, //date
G_TYPE_STRING, //memo
G_TYPE_UINT, //meter
G_TYPE_DOUBLE, //fuel
G_TYPE_DOUBLE, //price
G_TYPE_DOUBLE, //amount
G_TYPE_UINT, //dist
G_TYPE_DOUBLE, //100km
G_TYPE_DOUBLE, //distbyvol
G_TYPE_BOOLEAN //ispartial
);
//treeview
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
/* column date */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Date"));
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
renderer = gtk_cell_renderer_text_new();
//#2004631 date and column title alignement
//g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
//gtk_tree_view_column_add_attribute(column, renderer, "text", LST_CAR_DATE);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_vehicle_cell_data_func_date, NULL, NULL);
/*
LST_CAR_DATE,
LST_CAR_MEMO,
LST_CAR_METER,
LST_CAR_FUEL,
LST_CAR_PRICE,
LST_CAR_AMOUNT,
LST_CAR_DIST,
LST_CAR_100KM
*/
/* column: Memo */
/*
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Memo"));
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_add_attribute(column, renderer, "text", LST_CAR_MEMO);
//gtk_tree_view_column_set_cell_data_func(column, renderer, repvehicle_text_cell_data_function, NULL, NULL);
*/
/* column: Meter */
column = list_vehicle_column_distance(_("Meter"), LST_CAR_METER);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Fuel load */
column = list_vehicle_column_volume(_("Fuel"), LST_CAR_FUEL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Price by unit */
column = list_vehicle_column_amount(_("Price"), LST_CAR_PRICE);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Amount */
column = list_vehicle_column_amount(_("Amount"), LST_CAR_AMOUNT);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: Distance done */
column = list_vehicle_column_distance(_("Dist."), LST_CAR_DIST);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: consumption for 100Km */
column = list_vehicle_column_volume(PREFS->vehicle_unit_100, LST_CAR_100KM);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column: km by liter (distance by volume */
column = list_vehicle_column_distbyvol(PREFS->vehicle_unit_distbyvol, LST_CAR_DISTBYVOL);
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
/* column last: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), FALSE);
return(view);
}
homebank-5.9.7/src/hb-xml.h 0000644 0001750 0001750 00000002305 14736461415 014754 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_XML_H__
#define __HB_XML_H__
enum
{
XML_UNSET,
XML_OK,
XML_IO_ERROR,
XML_FILE_ERROR,
XML_VERSION_ERROR,
XML_NOT_WRITABLE,
};
typedef struct _ParseContext ParseContext;
struct _ParseContext
{
gdouble file_version; //version of the xml structure
gint data_version; //last hb version file was saved with
};
gint homebank_load_xml(gchar *filename);
gint homebank_save_xml(gchar *filename);
#endif
homebank-5.9.7/src/hb-account.h 0000644 0001750 0001750 00000010677 15057055056 015620 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ACCOUNT_H__
#define __HB_ACCOUNT_H__
#include "hb-types.h"
struct _account
{
guint32 key;
gushort flags;
gushort type;
guint32 pos; //display position
guint32 kcur;
gchar *name;
gchar *number;
gchar *bankname;
guint32 kgrp;
gdouble initial;
//gdouble warning;
gdouble minimum;
gdouble maximum;
guint32 cheque1;
guint32 cheque2;
gchar *website; //5.7 add
gchar *notes;
guint32 karc;
guint16 cccday; //creditcard close day
guint32 rdate; //last reconciled date
/* unsaved datas */
GQueue *txn_queue;
gushort dspflags;
gushort nb_pending; //5.9
gdouble bal_recon; //bank balance (reconciled transaction)
gdouble bal_clear; //cleared
gdouble bal_today; //today balance (every transaction until today)
gdouble bal_future; //future balance (every transaction)
gchar *xferincname; //xfer payee display name: '< account'
gchar *xferexpname; //xfer payee display name: '> account'
//gboolean flt_select; //true if selected into filter
};
// data flags
//gushort is 2 bytes / 16 bits
//FREE (1<<0)
#define AF_CLOSED (1<<1)
#define AF_NOSUMMARY (1<<4)
#define AF_NOBUDGET (1<<5)
#define AF_NOREPORT (1<<6)
#define AF_OUTFLOWSUM (1<<7)
#define AF_HASNOTICE (1<< 9) //added 5.9 for pending/import
//deprecated
#define AF_OLDBUDGET (1<<0)
// unsaved flags -- display/session
#define FLAG_ACC_TMP_ADDED (1<< 1)
#define FLAG_ACC_TMP_EDITED (1<< 2)
#define FLAG_ACC_TMP_DIRTY (1<< 3) //indicate any display needs a refresh
enum
{
// + https://www.kashoo.com/blog/what-are-the-different-account-types-in-accounting/
// + AceMoney: Bank / Cash / Credit / Investment / Loan
ACC_TYPE_NONE = 0,
ACC_TYPE_BANK = 1, //Banque
ACC_TYPE_CASH = 2, //Espèce
ACC_TYPE_ASSET = 3, //Actif (avoir)
ACC_TYPE_CREDITCARD = 4, //Carte crédit
ACC_TYPE_LIABILITY = 5, //Passif (dettes)
ACC_TYPE_CHECKING = 6, //OFX A standard checking account
ACC_TYPE_SAVINGS = 7, //OFX A standard savings account
// OFX_MONEYMRKT OFX A money market account
// OFX_CREDITLINE OFX A line of credit
// OFX_INVESTMENT OFX An investment account
// ACC_TYPE_STOCK = 11, //Actions
//ACC_TYPE_MUTUALFUND = 12, //Fond de placement
//ACC_TYPE_INCOME = 13, //Revenus
//ACC_TYPE_EXPENSE = 14, //Dépenses
//ACC_TYPE_EQUITY = 15, //Capitaux propres
// ACC_TYPE_,
ACC_TYPE_MAXVALUE
};
enum {
ACC_USAGE_NONE,
ACC_USAGE_TXN,
ACC_USAGE_TXN_XFER,
ACC_USAGE_ARC,
ACC_USAGE_ARC_XFER
};
Account *da_acc_malloc(void);
void da_acc_free(Account *item);
Account *da_acc_malloc(void);
void da_acc_destroy(void);
void da_acc_new(void);
guint da_acc_length(void);
gboolean da_acc_create_none(void);
gboolean da_acc_delete(guint32 key);
gboolean da_acc_insert(Account *item);
gboolean da_acc_append(Account *item);
guint32 da_acc_get_max_key(void);
Account *da_acc_get_by_name(gchar *name);
Account *da_acc_get_by_imp_name(gchar *name);
Account *da_acc_get(guint32 key);
guint32 da_acc_get_first_key(void);
void da_acc_consistency(Account *item);
void da_acc_anonymize(Account *item);
void da_acc_pos_sanitize(void);
void account_transaction_sort(void);
guint account_is_used(guint32 key);
gboolean account_has_website(Account *item);
gboolean account_exists(gchar *name);
gboolean account_rename(Account *item, gchar *newname);
void account_set_currency(Account *item, guint32 kcur);
void account_set_dirty(Account *acc, guint32 key, gboolean isdirty);
void account_flags_eval(Account *item);
void account_compute_balances(gboolean init);
gboolean account_balances_add(Transaction *txn);
gboolean account_balances_sub(Transaction *txn);
GList *account_glist_sorted(gint column);
void account_convert_euro(Account *acc);
#endif
homebank-5.9.7/src/ui-budget-tabview.h 0000644 0001750 0001750 00000003702 14736461415 017113 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 2018-2019 Adrien Dorsaz
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_BUD_TABVIEW_H__
#define __HB_BUD_TABVIEW_H__
//by default Maxime don't want table budget view to add/remove/rename categories
#define HB_BUD_TABVIEW_EDIT_ENABLE 0
struct ui_bud_tabview_data
{
GtkWidget *dialog;
GActionGroup * actions;
// Number of changes to notify globally
gint change;
// Tree view with budget
GtkWidget *TV_budget;
GtkTreeViewColumn *TVC_category;
GtkTreeSelection *TV_selection;
// Radio buttons of view mode
GtkWidget *RA_mode;
// Tool bar
#if HB_BUD_TABVIEW_EDIT_ENABLE
GtkWidget *BT_category_add, *BT_category_delete, *BT_category_merge;
#endif
GtkWidget *BT_category_reset, *BT_category_force_monitoring;
gulong HID_category_monitoring_toggle;
GtkWidget *BT_expand, *BT_collapse;
// Should the tree be collapsed
gboolean TV_is_expanded;
#if HB_BUD_TABVIEW_EDIT_ENABLE
// Add Dialog
GtkWidget *COMBO_add_parent, *EN_add_name, *BT_apply;
// Merge Dialog
GtkWidget *COMBO_merge_target, *CHECK_merge_delete;
guint32 MERGE_source_category_key;
#endif
// Search
GtkWidget *EN_search;
};
typedef struct ui_bud_tabview_data ui_bud_tabview_data_t;
GtkWidget *ui_bud_tabview_manage_dialog(void);
#endif
homebank-5.9.7/src/gtk-chart-progress.c 0000644 0001750 0001750 00000144311 15120541561 017277 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "homebank.h"
#include "ui-widgets.h"
#include "gtk-chart-colors.h"
#include "gtk-chart-progress.h"
#include "rep-budget.h"
extern gchar *CHART_CATEGORY;
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define DB(x);
//#define DB(x) (x);
//calculation
#define DBC(x);
//#define DBC(x) (x);
//DB dynamics
#define DBD(x);
//#define DBD(x) (x);
#define DYNAMICS 1
#define DBGDRAW_RECT 0
#define DBGDRAW_TEXT 0
#define DBGDRAW_ITEM 0
/* --- prototypes --- */
static void ui_chart_progress_class_init (ChartProgressClass *klass);
static void ui_chart_progress_init (ChartProgress *chart);
static void ui_chart_progress_destroy (GtkWidget *chart);
/*static void ui_chart_progress_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);*/
static gboolean drawarea_configure_event_callback (GtkWidget *widget, GdkEvent *event, gpointer user_data);
static gboolean drawarea_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data);
static void drawarea_style_changed_callback(GtkWidget *widget, gpointer user_data);
static gboolean drawarea_scroll_event_callback( GtkWidget *widget, GdkEvent *event, gpointer user_data);
static gboolean drawarea_motionnotifyevent_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data);
static void ui_chart_progress_first_changed( GtkAdjustment *adj, gpointer user_data);
//static void ui_chart_progress_clear(ChartProgress *chart);
static gboolean drawarea_full_redraw(GtkWidget *widget, gpointer user_data);
static void ui_chart_progress_queue_redraw(ChartProgress *chart);
/* --- variables --- */
static GtkBoxClass *parent_class = NULL;
/* --- functions --- */
static void ui_chart_progress_set_font_size(ChartProgress *chart, PangoLayout *layout, gint font_size)
{
PangoAttrList *attrs;
PangoAttribute *attr;
double scale = PANGO_SCALE_MEDIUM;
//PANGO_SCALE_MEDIUM = normal size
//DB( g_print("\n[chartprogress] set font size\n") );
switch(font_size)
{
case CHART_FONT_SIZE_TITLE:
//size = chart->pfd_size + 3;
scale = PANGO_SCALE_X_LARGE;
break;
case CHART_FONT_SIZE_SUBTITLE:
//size = chart->pfd_size + 1;
scale = PANGO_SCALE_LARGE;
break;
//case CHART_FONT_SIZE_NORMAL:
//size = chart->pfd_size - 1;
// break;
case CHART_FONT_SIZE_SMALL:
//size = chart->pfd_size - 2;
scale = PANGO_SCALE_SMALL;
break;
}
//DB( g_print(" size=%d\n", size) );
attrs = pango_attr_list_new ();
attr = pango_attr_scale_new(scale);
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (layout, attrs);
pango_layout_set_font_description (layout, chart->pfd);
//pango_attribute_destroy(attr);
pango_attr_list_unref (attrs);
}
/*
** print a integer number
*/
static gchar *ui_chart_progress_print_int(ChartProgress *chart, gdouble value)
{
hb_strfmon(chart->buffer, CHART_BUFFER_LENGTH-1, value, chart->kcur, chart->minor);
return chart->buffer;
}
/*
** get the bar under the mouse pointer
*/
static gint ui_chart_progress_get_hover(GtkWidget *widget, gint x, gint y, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
HbtkDrawProgContext *context = &chart->context;
gint retval, first, index, py;
gint blkw = context->blkw;
double oy;
gboolean docursor = FALSE;
DB( g_print("\n[chartprogress] get hover\n") );
retval = -1;
oy = context->t + context->title_zh + context->header_zh + context->subtitle_zh;
//DB( g_print(" y=%d, oy=%f, cb=%f\n", y, oy, chart->b) );
if( (y <= context->b && y >= oy) && (x >= context->l && x <= context->r) )
{
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
py = (y - oy);
index = first + (py / blkw);
if(index < chart->nb_items)
{
StackItem *item = &g_array_index(chart->items, StackItem, index);
retval = index;
if( item->n_child > 1 )
docursor = TRUE;
}
DB( g_print(" hover=%d\n", retval) );
}
//5.7 cursor change
{
GdkWindow *gdkwindow;
GdkCursor *cursor;
gdkwindow = gtk_widget_get_window (GTK_WIDGET(widget));
cursor = gdk_cursor_new_for_display(gdk_window_get_display(gdkwindow), (docursor == TRUE) ? GDK_HAND2 : GDK_ARROW );
gdk_window_set_cursor (gdkwindow, cursor);
if(GDK_IS_CURSOR(cursor))
g_object_unref(cursor);
}
return(retval);
}
static void ui_chart_progress_first_changed( GtkAdjustment *adj, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
//gint first;
DB( g_print("\n[chartprogress] bar first changed\n") );
//first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
//DB( g_print(" first=%d\n", first) );
/*
DB( g_print("scrollbar\n adj=%8x, low=%.2f upp=%.2f val=%.2f step=%.2f page=%.2f size=%.2f\n", adj,
adj->lower, adj->upper, adj->value, adj->step_increment, adj->page_increment, adj->page_size) );
*/
/* Set the number of decimal places to which adj->value is rounded */
//gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
//gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
drawarea_full_redraw (chart->drawarea, chart);
gtk_widget_queue_draw(chart->drawarea);
}
/*
** scrollbar set values for upper, page size, and also show/hide
*/
static void ui_chart_progress_scrollbar_setvalues(ChartProgress *chart)
{
GtkAdjustment *adj = chart->adjustment;
HbtkDrawProgContext *context = &chart->context;
gint first;
g_return_if_fail (GTK_IS_ADJUSTMENT (adj));
DB( g_print("\n[chartprogress] sb_set_values\n") );
//if(visible < entries)
//{
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
DB( g_print(" entries=%d, visible=%d\n", chart->nb_items, context->visible) );
DB( g_print(" first=%d, upper=%d, pagesize=%d\n", first, chart->nb_items, context->visible) );
gtk_adjustment_set_upper(adj, (gdouble)chart->nb_items);
gtk_adjustment_set_page_size(adj, (gdouble)context->visible);
gtk_adjustment_set_page_increment(adj, (gdouble)context->visible);
if(first+context->visible > chart->nb_items)
{
gtk_adjustment_set_value(adj, (gdouble)chart->nb_items - context->visible);
}
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION < 18) )
gtk_adjustment_changed (adj);
#endif
//gtk_widget_show(GTK_WIDGET(scrollbar));
//}
//else
//gtk_widget_hide(GTK_WIDGET(scrollbar));
}
static void ui_chart_progress_clear_items(ChartProgress *chart)
{
gint i;
DB( g_print("\n[chartprogress] clear\n") );
if(chart->items != NULL)
{
for(i=0;inb_items;i++)
{
StackItem *item = &g_array_index(chart->items, StackItem, i);
g_free(item->label); //we free label as it comes from a model_get into setup_with_model
g_free(item->status); //we free status as it comes from a model_get into setup_with_model
}
g_array_free(chart->items, TRUE);
chart->items = NULL;
}
chart->nb_items = 0;
}
static void ui_chart_progress_clear(ChartProgress *chart)
{
DB( g_print("\n[chartprogress] clear\n") );
//free & clear any previous allocated datas
if(chart->title != NULL)
{
g_free(chart->title);
chart->title = NULL;
}
if(chart->subtitle != NULL)
{
g_free(chart->subtitle);
chart->subtitle = NULL;
}
ui_chart_progress_clear_items(chart);
}
static void ui_chart_progress_setup_with_model(ChartProgress *chart, gint indice)
{
GtkTreeModel *model;
GtkTreeIter iter;
guint i;
gboolean valid = FALSE;
DB( g_print("\n[chartprogress] setup with model\n") );
model = chart->model;
DB( g_print(" indice: %d\n", indice) );
if( indice < 0 )
{
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), NULL);
gtk_widget_hide(chart->breadcrumb);
}
else
{
GtkTreePath *path = gtk_tree_path_new_from_indices(indice, -1);
gchar *pathstr, *itrlabel;
pathstr = gtk_tree_path_to_string(path);
DB( g_print(" total: path: %s\n", pathstr) );
gtk_tree_model_get_iter(model, &iter, path);
chart->nb_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), &iter);
// update the breadcrumb
gtk_tree_model_get (GTK_TREE_MODEL(model), &iter,
LST_BUDGET_NAME, &itrlabel,
-1);
gchar *bc = g_markup_printf_escaped("%s > %s", _(CHART_CATEGORY), itrlabel);
gtk_label_set_markup(GTK_LABEL(chart->breadcrumb), bc);
g_free(bc);
gtk_widget_show(chart->breadcrumb);
// move to xx:0
gtk_tree_path_append_index(path, 0);
valid = gtk_tree_model_get_iter(model, &iter, path);
DB( g_print(" total: path: %s\n", pathstr) );
gtk_tree_path_free(path);
g_free(pathstr);
}
chart->items = g_array_sized_new(FALSE, FALSE, sizeof(StackItem), chart->nb_items);
DB( g_print(" nbitems=%d, struct=%d\n", chart->nb_items, (gint)sizeof(StackItem)) );
i = 0;
while (valid)
{
gint pos;
gchar *label, *status;
gdouble value1, value2;
StackItem item;
//TODO: remove id here....
gtk_tree_model_get (GTK_TREE_MODEL(model), &iter,
LST_BUDGET_POS, &pos,
//LST_BUDGET_KEY, &key,
LST_BUDGET_NAME, &label,
LST_BUDGET_SPENT, &value1,
LST_BUDGET_BUDGET, &value2,
//LST_BUDGET_FULFILLED, &toto,
//LST_BUDGET_RESULT, &result,
LST_BUDGET_STATUS, &status,
-1);
//#2023696 add unbudgeted into listview & exclude from charts
if( pos == LST_BUDGET_POS_UNBUDGETED )
{
//subtract the LST_REPORT_POS_TOTAL line not to be drawed
chart->nb_items--;
//fix leak
g_free(label);
g_free(status);
goto next;
}
item.label = label;
item.spent = value1;
item.budget = value2;
item.status = status;
item.n_child = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), &iter);
/* additional pre-compute */
item.result = item.spent - item.budget;
item.rawrate = 0;
if(ABS(item.budget) > 0)
{
item.rawrate = item.spent / item.budget;
}
//#2099843 strict compare 0.0
item.warn = (hb_amount_cmp(item.result, 0.0) < 0) ? TRUE : FALSE;
item.rate = CLAMP(item.rawrate, 0, 1.0);
g_array_append_vals(chart->items, &item, 1);
//don't g_free(label); here done into chart_clear
//don't g_free(status); here done into chart_clear
next:
i++;
valid = gtk_tree_model_iter_next (model, &iter);
}
}
static void ui_chart_progress_layout_area(cairo_t *cr, ChartProgress *chart, HbtkDrawProgContext *context)
{
PangoLayout *layout;
gchar *valstr;
int tw, th;
gint blkw;
gint i;
DB( g_print("\n[chartprogress] layout area\n") );
DB( g_print(" print %d ctx:%p\n", context->isprint, context) );
/* Create a PangoLayout, set the font and text */
layout = pango_cairo_create_layout (cr);
// compute title
context->title_zh = 0;
if(chart->title)
{
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_TITLE * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_TITLE);
pango_layout_set_font_description (layout, chart->pfd);
pango_layout_set_text (layout, chart->title, -1);
pango_layout_get_size (layout, &tw, &th);
context->title_zh = (th / PANGO_SCALE) + CHART_SPACING;
DBC( g_print(" - title: %s w=%d h=%d\n", chart->title, tw/PANGO_SCALE, th/PANGO_SCALE) );
}
// compute period
context->subtitle_zh = 0;
if(chart->subtitle)
{
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_PERIOD * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_SUBTITLE);
pango_layout_set_font_description (layout, chart->pfd);
pango_layout_set_text (layout, chart->subtitle, -1);
pango_layout_get_size (layout, &tw, &th);
context->subtitle_zh = (th / PANGO_SCALE) + CHART_SPACING;
DBC( g_print(" - period: %s w=%d h=%d\n", chart->subtitle, tw/PANGO_SCALE, th/PANGO_SCALE) );
}
// compute other text
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_NORMAL * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_NORMAL);
pango_layout_set_font_description (layout, chart->pfd);
//breadcrumb top et position
//pango_layout_set_text (layout, _(CHART_CATEGORY), -1);
//pango_layout_get_size (layout, &tw, &th);
gtk_widget_set_margin_top(chart->breadcrumb, context->subtitle_y);
double title_w = 0;
//init budget
pango_layout_set_text (layout, chart->budget_title, -1);
pango_layout_get_size (layout, &tw, &th);
context->bud_col_w = tw / PANGO_SCALE;
//init result
pango_layout_set_text (layout, chart->result_title, -1);
pango_layout_get_size (layout, &tw, &th);
context->res_col_w = tw / PANGO_SCALE;
for(i=0;inb_items;i++)
{
StackItem *item = &g_array_index(chart->items, StackItem, i);
// category width
if( item->label != NULL )
{
pango_layout_set_text (layout, item->label, -1);
pango_layout_get_size (layout, &tw, &th);
title_w = MAX(title_w, (tw / PANGO_SCALE));
}
DBC( g_print(" - calc '%s' title_w=%f (pw=%d)\n", item->label, title_w, tw) );
// compute budget/result width
valstr = ui_chart_progress_print_int(chart, item->budget);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
context->bud_col_w = MAX(context->bud_col_w, (tw / PANGO_SCALE));
// compute budget/result width
valstr = ui_chart_progress_print_int(chart, (item->spent - item->budget));
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
context->res_col_w = MAX(context->res_col_w ,(tw / PANGO_SCALE));
if( item->status != NULL )
{
pango_layout_set_text (layout, item->status, -1);
pango_layout_get_size (layout, &tw, &th);
context->rel_col_w = MAX(context->rel_col_w, (tw / PANGO_SCALE));
}
}
DB( g_print(" bud-col: w=%f\n", context->bud_col_w) );
DB( g_print(" res-col: w=%f\n", context->res_col_w) );
DB( g_print(" rel-col: w=%f\n", context->rel_col_w) );
// collect other width, add margins
context->header_zh = (th / PANGO_SCALE) + CHART_SPACING;
//chart->title_y = chart->t;
context->subtitle_y = context->t + context->title_zh;
context->header_y = context->subtitle_y + context->subtitle_zh;
//old pre 5.6
//context->cat_col_w = title_w + CHART_SPACING;
double rem_w = context->w - context->bud_col_w - context->res_col_w - context->rel_col_w - (double)(CHART_SPACING*3);
if( (title_w) <= (rem_w/3) )
{
context->cat_col_w = title_w;
}
else
{
context->cat_col_w = (rem_w/3);
}
context->graph_width = context->w - context->cat_col_w - context->bud_col_w - context->res_col_w - context->rel_col_w - (double)(CHART_SPACING*3);
context->graph_height = context->h - context->title_zh - context->subtitle_zh - context->header_zh;
DB( g_print(" gfx_w = %.2f - %.2f - %.2f - %.2f - %.2f - %.2f\n",
context->w, context->cat_col_w, context->bud_col_w, context->res_col_w, context->rel_col_w, (double)(CHART_SPACING*3)) );
DB( g_print(" gfx_w = %.2f\n", context->graph_width) );
//if expand : we compute available space
//chart->barw = MAX(32, (chart->graph_width)/chart->nb_items);
//chart->barw = 32; // usr setted or defaut to BARW
blkw = context->barw + floor(context->barw * 0.2);
context->blkw = blkw;
context->visible = (context->graph_height - context->t) / blkw;
context->visible = MIN(context->visible, chart->nb_items);
g_object_unref (layout);
}
static void ui_chart_progress_recompute(ChartProgress *chart)
{
GtkWidget *drawarea = chart->drawarea;
HbtkDrawProgContext *context = &chart->context;
GtkAllocation allocation;
cairo_surface_t *surf;
cairo_t *cr;
DB( g_print("\n[chartprogress] recompute \n") );
if( !gtk_widget_get_realized(chart->drawarea) || chart->surface == NULL )
return;
gtk_widget_get_allocation(drawarea, &allocation);
context->l = CHART_MARGIN;
context->t = CHART_MARGIN;
context->r = allocation.width - CHART_MARGIN;
context->b = allocation.height - CHART_MARGIN;
context->w = allocation.width - (CHART_MARGIN*2);
context->h = allocation.height - (CHART_MARGIN*2);
DB( g_print("l=%d t=%d w=%d h=%d b=%d r=%d\n",
context->l, context->t, context->w, context->h, context->b, context->r) );
//todo: seems not working well...
surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
cr = cairo_create (surf);
ui_chart_progress_layout_area(cr, chart, context);
cairo_destroy(cr);
cairo_surface_destroy(surf);
gtk_adjustment_set_value(chart->adjustment, 0);
ui_chart_progress_scrollbar_setvalues(chart);
gtk_widget_show(chart->scrollbar);
//gtk_widget_queue_draw( chart->drawarea );
}
#if (DBGDRAW_RECT + DBGDRAW_TEXT + DBGDRAW_ITEM) > 0
static void ui_chart_progress_draw_help(cairo_t *cr, ChartProgress *chart, HbtkDrawProgContext *context)
{
double x, y, y2;
gint first = 0;
gint i;
DB( g_print("\n[chartprogress] draw help\n") );
DB( g_print(" rect is: %f %f %f %f, barw=%f, ctx=%p\n", context->l, context->t, context->w, context->h, context->barw, context) );
cairo_set_line_width (cr, 1);
double dashlength;
dashlength = 4;
cairo_set_dash (cr, &dashlength, 1, 0);
#if DBGDRAW_RECT == 1
//clip area
cairo_set_line_width(cr, 1.0);
cairo_set_source_rgb(cr, 0.0, 1.0, 0.0); //green
cairo_rectangle(cr, context->l+0.5, context->t+0.5, context->w - 1, context->h - 1);
cairo_stroke(cr);
//graph area
y = context->header_y + context->header_zh;
cairo_set_source_rgb(cr, 1.0, 0.5, 0.0); //orange
cairo_rectangle(cr, context->l+context->cat_col_w+0.5+CHART_SPACING, y+0.5, context->graph_width - 1, context->graph_height - 1);
cairo_stroke(cr);
#endif
#if DBGDRAW_TEXT == 1
//title rect
cairo_set_source_rgb(cr, .0, .0, 1.0); //blue
cairo_rectangle(cr, context->l+0.5, context->t+0.5, context->w - 1, context->title_zh);
cairo_stroke(cr);
//period rect
cairo_rectangle(cr, context->l+0.5, context->subtitle_y+0.5, context->w - 1, context->subtitle_zh);
cairo_stroke(cr);
//header rect
cairo_set_source_rgb(cr, .0, 1.0, 1.0); //cyan
cairo_rectangle(cr, context->l+0.5, context->header_y+0.5, context->w - 1, context->header_zh);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); //blue
y = context->t + context->title_zh + context->header_zh + context->subtitle_zh;
//category column
x = context->l+0.5;
cairo_rectangle(cr, x, y+0.5, context->cat_col_w - 1, context->h);
cairo_stroke(cr);
//budget column
x += context->cat_col_w + context->graph_width + (CHART_SPACING*2);
cairo_rectangle(cr, x+0.5, y+0.5, context->bud_col_w - 1, context->h);
cairo_stroke(cr);
//result column
x += context->bud_col_w + CHART_SPACING;
cairo_rectangle(cr, x+0.5, y+0.5, context->res_col_w - 1, context->h);
cairo_stroke(cr);
//rel column (there is a space before text)
x += context->res_col_w;
cairo_rectangle(cr, x+0.5, y+0.5, context->rel_col_w - 1, context->h);
cairo_stroke(cr);
#endif
// draw item lines
#if DBGDRAW_ITEM == 1
y2 = y+0.5;
cairo_set_line_width(cr, 1.0);
cairo_set_source_rgb(cr, 1.0, 0.0, 1.0); // violet
for(i=first; i<=(first+context->visible) ;i++)
{
cairo_move_to(cr, context->l, y2);
cairo_line_to(cr, context->r, y2);
y2 += context->blkw;
}
cairo_stroke(cr);
#endif
}
#endif
static void _draw_text(cairo_t *cr, PangoLayout *layout, gchar *text, double tx, double ty)
{
int tw, th;
//text rect
#if DBGDRAW_TEXT == 1
cairo_save(cr);
cairo_set_source_rgb(cr, .0, .0, 1.0);
cairo_rectangle(cr, tx - (tw /PANGO_SCALE), ty, (tw /PANGO_SCALE), (th /PANGO_SCALE));
cairo_stroke(cr);
cairo_restore(cr);
#endif
pango_layout_set_text (layout, text, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, tx - (tw /PANGO_SCALE), ty);
pango_cairo_show_layout (cr, layout);
}
/*
** draw all visible bars
*/
static void ui_chart_progress_draw_bars(cairo_t *cr, ChartProgress *chart, HbtkDrawProgContext *context)
{
double x, y, x2, y2, h, tx, ty;
gint first;
gint i, idx;
gchar *valstr;
PangoLayout *layout;
int tw, th;
DB( g_print("\n[chartprogress] draw bars\n") );
DB( g_print(" print %d ctx:%p\n", context->isprint, context) );
DB( g_print(" rect is: %f %f %f %f, barw=%f, ctx=%p\n", context->l, context->t, context->w, context->h, context->barw, context) );
layout = pango_cairo_create_layout (cr);
x = context->l + context->cat_col_w;
y = context->t + context->title_zh + context->header_zh + context->subtitle_zh;
first = context->first;
if(!context->isprint)
cairo_user_set_rgbcol(cr, &global_colors[THTEXT]);
else
cairo_user_set_rgbcol(cr, &global_colors[BLACK]);
// draw title
if(chart->title)
{
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_TITLE * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_TITLE);
pango_layout_set_font_description (layout, chart->pfd);
pango_layout_set_text (layout, chart->title, -1);
//pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, context->l, context->t);
pango_cairo_show_layout (cr, layout);
}
// draw period
if(chart->subtitle)
{
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_PERIOD * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_SUBTITLE);
pango_layout_set_font_description (layout, chart->pfd);
pango_layout_set_text (layout, chart->subtitle, -1);
//pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, context->l, context->subtitle_y);
pango_cairo_show_layout (cr, layout);
}
// draw column title
if(!context->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
//pango_font_description_set_size(chart->pfd, CHART_FONT_SIZE_NORMAL * PANGO_SCALE);
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_NORMAL);
pango_layout_set_font_description (layout, chart->pfd);
ty = context->header_y;
//budget
tx = context->l + context->cat_col_w + context->graph_width + context->bud_col_w + CHART_SPACING;
_draw_text(cr, layout, chart->budget_title, tx, ty);
//result
tx = context->l + context->cat_col_w + context->graph_width + context->bud_col_w + context->res_col_w + (CHART_SPACING*3);
_draw_text(cr, layout, chart->result_title, tx, ty);
// draw items
for(i=0; ivisible ;i++)
{
StackItem *item;
gint barw = context->barw;
gint blkw = context->blkw;
idx = i + first;
if( (guint)idx > (chart->items->len - 1) )
break;
DB( g_print("------\nbar[%02d] idx=%d", i, idx) );
item = &g_array_index(chart->items, StackItem, idx);
x2 = x + CHART_SPACING;
y2 = y + (CHART_SPACING/2) + (blkw * i);
DB( g_print(" '%-32s' wrn=%d %.2f%% (%.2f%%) :: s=% 4.2f b=% 4.2f = r=% 4.2f\n",
item->label, item->warn, item->rawrate, item->rate, item->spent, item->budget, item->result) );
valstr = item->label;
ui_chart_progress_set_font_size(chart, layout, CHART_FONT_SIZE_NORMAL);
pango_layout_set_font_description (layout, chart->pfd);
pango_layout_set_width(layout, context->cat_col_w * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
double ytext = y2 + ((barw - (th / PANGO_SCALE))/2);
if(!context->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
//cairo_move_to(cr, context->l + context->cat_col_w - (tw / PANGO_SCALE) - CHART_SPACING, ytext);
cairo_move_to(cr, context->l, ytext);
pango_cairo_show_layout (cr, layout);
// bar background
if(!context->isprint)
cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.15);
else
cairo_user_set_rgbacol(cr, &global_colors[BLACK], 0.15);
cairo_rectangle(cr, x2, y2, context->graph_width, barw);
cairo_fill(cr);
//bar with color :: todo migrate this
h = floor(item->rate * context->graph_width);
if(item->warn)
{
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[chart->color_scheme.cs_red], idx == chart->hover);
}
else
{
if(item->rate > 0.8 && item->rate < 1.0)
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[chart->color_scheme.cs_orange], idx == chart->hover);
else
cairo_user_set_rgbcol_over(cr, &chart->color_scheme.colors[chart->color_scheme.cs_green], idx == chart->hover);
}
cairo_rectangle(cr, x2, y2, h, barw);
cairo_fill(cr);
// bar text spent value
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
pango_layout_set_width(layout, -1);
if( item->result != 0)
{
double left;
valstr = ui_chart_progress_print_int(chart, item->spent);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
DB( g_print(" try print '%s' w=%d into h=%f\n", valstr, tw/PANGO_SCALE, h) );
left = x2 + h;
// draw inside
if( h >= ( (tw / PANGO_SCALE) + (CHART_SPACING*2)) )
{
cairo_user_set_rgbcol(cr, &global_colors[WHITE]);
left -= (tw / PANGO_SCALE) + CHART_SPACING;
}
// draw outside
else
{
//cairo_user_set_rgbacol(cr, &global_colors[THTEXT], 0.78);
//#1897696 draw out of budget text in red
if( item->warn )
cairo_user_set_rgbcol (cr, &chart->color_scheme.colors[chart->color_scheme.cs_red]);
else
{
if(!context->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
}
left += CHART_SPACING;
}
DB( g_print(" > move to %.f\n", left));
cairo_move_to(cr, left, ytext);
pango_cairo_show_layout (cr, layout);
}
// column budget value
valstr = ui_chart_progress_print_int(chart, item->budget);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
if(!context->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
cairo_move_to(cr, context->l + context->cat_col_w + context->graph_width + context->bud_col_w + (CHART_SPACING*2) - (tw / PANGO_SCALE), ytext);
pango_cairo_show_layout (cr, layout);
// column result value
if( item->result != 0)
{
valstr = ui_chart_progress_print_int(chart, item->result);
if(item->warn)
//cairo_set_source_rgb(cr, COLTOCAIRO(164), COLTOCAIRO(0), COLTOCAIRO(0));
cairo_user_set_rgbcol(cr, &chart->color_scheme.colors[chart->color_scheme.cs_red]);
else
{
if(!context->isprint)
cairo_user_set_rgbacol (cr, &global_colors[THTEXT], 0.78);
else
cairo_user_set_rgbacol (cr, &global_colors[BLACK], 0.78);
}
tx = context->l + context->cat_col_w + context->graph_width + context->bud_col_w + context->res_col_w + (CHART_SPACING*3);
pango_layout_set_text (layout, valstr, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, tx - (tw / PANGO_SCALE) , ytext);
pango_cairo_show_layout (cr, layout);
// status
if( item->status )
{
pango_layout_set_text (layout, item->status, -1);
pango_layout_get_size (layout, &tw, &th);
cairo_move_to(cr, tx, ytext);
pango_cairo_show_layout (cr, layout);
}
}
//y += blkw;
}
g_object_unref (layout);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static gboolean drawarea_full_redraw(GtkWidget *widget, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
HbtkDrawProgContext *context = &chart->context;
cairo_t *cr;
DB( g_print("\n[chartprogress] drawarea full redraw\n") );
cr = cairo_create (chart->surface);
/* fillin the back in white */
if(!context->isprint)
cairo_user_set_rgbcol(cr, &global_colors[THBASE]);
else
cairo_user_set_rgbcol(cr, &global_colors[WHITE]);
cairo_paint(cr);
if(chart->nb_items == 0)
{
cairo_destroy(cr);
return FALSE;
}
cairo_rectangle(cr, context->l, context->t, context->w, context->h);
cairo_clip(cr);
#if (DBGDRAW_RECT + DBGDRAW_TEXT + DBGDRAW_ITEM) > 0
ui_chart_progress_draw_help(cr, chart, context);
#endif
context->first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
ui_chart_progress_draw_bars(cr, chart, context);
cairo_destroy(cr);
return TRUE;
}
//static void drawarea_get_style(GtkWidget *widget, gpointer user_data)
static gboolean
drawarea_configure_event_callback (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
HbtkDrawProgContext *context = &chart->context;
GtkAllocation allocation;
GtkStyleContext *stylctx;
PangoFontDescription *desc;
gboolean colfound;
GdkRGBA color;
DB( g_print("\n[chartprogress] drawarea configure \n") );
DB( g_print("w=%d h=%d\n", allocation.width, allocation.height) );
gtk_widget_get_allocation (widget, &allocation);
if (chart->surface)
cairo_surface_destroy (chart->surface);
chart->surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
CAIRO_CONTENT_COLOR,
allocation.width,
allocation.height);
stylctx = gtk_widget_get_style_context (widget);
chart_color_global_default();
// get base color
colfound = gtk_style_context_lookup_color(stylctx, "theme_base_color", &color);
if(!colfound)
colfound = gtk_style_context_lookup_color(stylctx, "base_color", &color);
if( colfound )
{
struct rgbcol *tcol = &global_colors[THBASE];
tcol->r = color.red * 255;
tcol->g = color.green * 255;
tcol->b = color.blue * 255;
DB( g_print(" - theme base col: %x %x %x\n", tcol->r, tcol->g, tcol->b) );
}
//get text color
colfound = gtk_style_context_lookup_color(stylctx, "theme_text_color", &color);
if(!colfound)
//#1916932 colfound was not assigned
colfound = gtk_style_context_lookup_color(stylctx, "text_color", &color);
if( colfound )
{
struct rgbcol *tcol = &global_colors[THTEXT];
tcol->r = color.red * 255;
tcol->g = color.green * 255;
tcol->b = color.blue * 255;
DB( g_print(" - theme text (bg) col: %x %x %x\n", tcol->r, tcol->g, tcol->b) );
}
/* get and copy the font */
gtk_style_context_get(stylctx, GTK_STATE_FLAG_NORMAL, "font", &desc, NULL);
if(chart->pfd)
{
pango_font_description_free (chart->pfd);
chart->pfd = NULL;
}
chart->pfd = pango_font_description_copy(desc);
chart->pfd_size = pango_font_description_get_size (desc) / PANGO_SCALE;
context->barw = (6 + chart->pfd_size) * PHI;
//leak: we should free desc here ?
//or no need to copy above ?
//pango_font_description_free(desc);
DB( g_print("family: %s\n", pango_font_description_get_family(chart->pfd) ) );
DB( g_print("size : %d (%d)\n", chart->pfd_size, chart->pfd_size/PANGO_SCALE ) );
DB( g_print("isabs : %d\n", pango_font_description_get_size_is_absolute (chart->pfd) ) );
if( gtk_widget_get_realized(widget) )
{
ui_chart_progress_recompute(chart);
drawarea_full_redraw(widget, user_data);
}
/* We've handled the configure event, no need for further processing. */
return TRUE;
}
//state
static void drawarea_style_changed_callback(GtkWidget *widget, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
DB( g_print("\n[chartprogress] drawarea style changed\n") );
if( gtk_widget_get_realized(widget))
{
drawarea_full_redraw(widget, chart);
}
}
static gboolean drawarea_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
HbtkDrawProgContext *context = &chart->context;
if( !gtk_widget_get_realized(widget) || chart->surface == NULL )
return FALSE;
DB( g_print("\n[chartprogress] drawarea draw cb\n") );
cairo_set_source_surface (cr, chart->surface, 0, 0);
cairo_paint (cr);
/* always redraw directly the hover block */
gint first;
double ox, oy;
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
ox = context->l + context->cat_col_w + CHART_SPACING;
oy = context->t + context->title_zh + context->header_zh + context->subtitle_zh;
if(chart->hover != -1)
{
DB( g_print(" draw hover\n") );
cairo_rectangle(cr, context->l, context->t, context->w, context->h);
cairo_clip(cr);
oy += CHART_SPACING/2 + (chart->hover - first) * context->blkw;
cairo_user_set_rgbacol(cr, &global_colors[WHITE], OVER_ALPHA);
cairo_rectangle(cr, ox, oy, context->graph_width, context->barw);
cairo_fill(cr);
}
return FALSE;
}
static gboolean
drawarea_cb_root_activate_link (GtkWidget *label, const gchar *uri, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
DB( g_print("\n[chartprogress] root breadcrumb clicked\n") );
ui_chart_progress_clear_items(chart);
ui_chart_progress_setup_with_model(chart, -1);
ui_chart_progress_queue_redraw(chart);
return TRUE;
}
static gboolean
drawarea_cb_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
guint button = 0;
if (chart->surface == NULL)
return FALSE; /* paranoia check, in case we haven't gotten a configure event */
DB( g_print("\n[chartprogress] mouse button press event\n") );
gdk_event_get_button(event, &button);
if (button == GDK_BUTTON_PRIMARY)
{
if( chart->hover >= 0 )
{
StackItem *item = &g_array_index(chart->items, StackItem, chart->hover);
if( item->n_child > 1 )
{
ui_chart_progress_clear_items(chart);
ui_chart_progress_setup_with_model(chart, chart->hover);
ui_chart_progress_queue_redraw(chart);
}
}
}
/* We've handled the event, stop processing */
return TRUE;
}
static gboolean drawarea_motionnotifyevent_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
HbtkDrawProgContext *context = &chart->context;
gdouble x_win, y_win;
gint x, y;
if(chart->surface == NULL || chart->nb_items == 0)
return FALSE;
DBD( g_print("\n[chartprogress] drawarea motion cb\n") );
gdk_event_get_coords(event, &x_win, &y_win);
x = x_win;
y = y_win;
chart->hover = ui_chart_progress_get_hover(widget, x, y, chart);
// rollover redraw ?
DBD( g_print(" %d, %d :: hover: last=%d, curr=%d\n", x, y, chart->lasthover, chart->hover) );
if(chart->lasthover != chart->hover)
{
GdkRectangle update_rect;
gint first;
double oy;
DBD( g_print(" motion rollover redraw :: hover=%d\n", chart->hover) );
first = (gint)gtk_adjustment_get_value(GTK_ADJUSTMENT(chart->adjustment));
oy = context->t + context->title_zh + context->header_zh + context->subtitle_zh;
if(chart->lasthover != -1)
{
update_rect.x = context->l;
update_rect.y = oy + (chart->lasthover - first) * context->blkw;
update_rect.width = context->r;
update_rect.height = context->blkw;
/* Now invalidate the affected region of the drawing area. */
gdk_window_invalidate_rect (gtk_widget_get_window (widget),
&update_rect,
FALSE);
}
update_rect.x = context->l;
update_rect.y = oy + (chart->hover - first) * context->blkw;
update_rect.width = context->r;
update_rect.height = context->blkw;
/* Now invalidate the affected region of the drawing area. */
gdk_window_invalidate_rect (gtk_widget_get_window (widget),
&update_rect,
FALSE);
//gtk_widget_queue_draw( widget );
//retval = FALSE;
}
chart->lasthover = chart->hover;
return TRUE;
}
static gboolean drawarea_scroll_event_callback( GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
ChartProgress *chart = GTK_CHARTPROGRESS(user_data);
GtkAdjustment *adj = chart->adjustment;
GdkScrollDirection direction;
gdouble first, upper, pagesize;
DB( g_print("\n[chartprogress] scroll\n") );
first = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj));
//lower = gtk_adjustment_get_lower(GTK_ADJUSTMENT(adj));
upper = gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj));
pagesize = gtk_adjustment_get_page_size(GTK_ADJUSTMENT(adj));
DB( g_print("- pos is %.2f, [%.2f - %.2f]\n", first, 0.0, upper) );
gdk_event_get_scroll_direction(event, &direction);
switch(direction)
{
case GDK_SCROLL_UP:
gtk_adjustment_set_value(adj, first - 1);
break;
case GDK_SCROLL_DOWN:
gtk_adjustment_set_value(adj, CLAMP(first + 1, 0, upper - pagesize) );
break;
default:
break;
}
drawarea_full_redraw(widget, user_data);
return TRUE;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* public functions */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
gtk_chart_progress_begin_print (GtkPrintOperation *operation,
GtkPrintContext *context,
gpointer user_data)
{
GtkChartProgPrintData *data = user_data;
HbtkDrawProgContext *drawctx;
gdouble t, l, w, h;
cairo_t *cr;
DB( g_print("\n[chartprogress] begin print\n") );
//gtk_print_context_get_hard_margins(context, &t, &b, &l, &r);
t = 0;
l = 0;
w = gtk_print_context_get_width(context);
h = gtk_print_context_get_height(context);
//TODO: test keeping a ratio and handle orientation myself
/*
settings = gtk_print_operation_get_print_settings(operation);
ratio = (height < width) ? width/height : height/width;
DB( g_print(" orientation: %d\n", gtk_print_settings_get_orientation(settings)) );
DB( g_print(" w=%g h=%g // ratio %g\n", width, height, ratio) );
if( height < width )
height = width * ratio;
*/
//setup our context
drawctx = &data->drawctx;
drawctx->isprint = TRUE;
drawctx->l = l;
drawctx->t = t;
drawctx->w = w;
drawctx->h = h;
//hack
PangoContext *pctx = gtk_print_context_create_pango_context(context);
PangoFontDescription *pfd = pango_context_get_font_description(pctx);
drawctx->barw = (6 + (pango_font_description_get_size(pfd) / PANGO_SCALE )) * PHI;
g_object_unref(pctx);
cr = gtk_print_context_get_cairo_context (context);
DB( g_print(" rect is: %f %f %f %f, barw=%f, ctx=%p\n", l, t, w, h, drawctx->barw, drawctx) );
ui_chart_progress_layout_area(cr, data->chart, drawctx);
data->num_pages = ceil((gdouble)data->chart->nb_items / (gdouble)drawctx->visible);
g_print(" nb pages: %d, nbitems %d / visi %d = %.2f\n", data->num_pages, data->chart->nb_items, drawctx->visible, (gdouble)data->chart->nb_items / (gdouble)drawctx->visible);
gtk_print_operation_set_n_pages (operation, data->num_pages);
}
static void gtk_chart_progress_draw_page (GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, gpointer user_data)
{
GtkChartProgPrintData *data = user_data;
HbtkDrawProgContext *drawctx;
cairo_t *cr;
DB( g_print("\n[chartprogress] draw page\n") );
cr = gtk_print_context_get_cairo_context (context);
drawctx = &data->drawctx;
//cairo_rectangle (cr, drawctx->l, drawctx->t, drawctx->w - drawctx->r, drawctx->h - drawctx->b);
//cairo_clip(cr);
cairo_set_line_width (cr, 1);
//draw debug rectangle
/*
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); //red
cairo_rectangle(cr, 0, 0, drawctx->w-drawctx->r, drawctx->h-drawctx->b);
cairo_stroke(cr);
*/
drawctx->first = page_nr * drawctx->visible;
g_print(" draw page %d, with first=%d\n", page_nr, drawctx->first);
#if (DBGDRAW_RECT + DBGDRAW_TEXT + DBGDRAW_ITEM) > 0
ui_chart_progress_draw_help(cr, data->chart, drawctx);
#endif
ui_chart_progress_draw_bars(cr, data->chart, drawctx);
}
void gtk_chart_progress_print(ChartProgress *chart, GtkWindow *parent, gchar *dirname, gchar *filename)
{
GtkChartProgPrintData *data;
GtkPrintOperation *operation;
GtkPrintSettings *settings;
gchar *ext, *uri = NULL;
GError *error = NULL;
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
g_print("\n[chartprogress] print\n");
data = g_new0 (GtkChartProgPrintData, 1);
data->chart = chart;
settings = gtk_print_settings_new ();
//TODO: this doesn't work for unknown reason...
gtk_print_settings_set_orientation(settings, GTK_PAGE_ORIENTATION_PORTRAIT);
if( dirname != NULL && filename != NULL )
{
if (g_strcmp0 (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT), "ps") == 0)
ext = ".ps";
else if (g_strcmp0 (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT), "svg") == 0)
ext = ".svg";
else
ext = ".pdf";
uri = g_strconcat ("file://", dirname, "/", filename, ext, NULL);
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
}
operation = gtk_print_operation_new ();
g_signal_connect (G_OBJECT (operation), "begin-print", G_CALLBACK (gtk_chart_progress_begin_print), data);
g_signal_connect (G_OBJECT (operation), "draw-page", G_CALLBACK (gtk_chart_progress_draw_page), data);
//g_signal_connect (G_OBJECT (operation), "end-print", G_CALLBACK (end_print), data);
gtk_print_operation_set_use_full_page (operation, FALSE);
gtk_print_operation_set_unit (operation, GTK_UNIT_POINTS);
gtk_print_operation_set_embed_page_setup (operation, TRUE);
gtk_print_operation_set_print_settings (operation, settings);
//test forec pagfe
//GtkPageSetup *ps = gtk_print_operation_get_default_page_setup(operation);
GtkPageSetup *ps = gtk_page_setup_new();
if( ps )
gtk_page_setup_set_orientation(ps, GTK_PAGE_ORIENTATION_LANDSCAPE);
else
g_print("pagesetup fail\n");
gtk_print_operation_set_default_page_setup(operation, ps);
gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW (parent), &error);
//to use with GTK_PRINT_OPERATION_ACTION_EXPORT
//gtk_print_operation_set_export_filename(operation, "/home/max/Desktop/testpdffile.pdf");
//gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_EXPORT, GTK_WINDOW (window), &error);
g_object_unref (operation);
g_object_unref (settings);
g_free (uri);
if (error)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s", error->message);
g_error_free (error);
g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
gtk_widget_show (dialog);
}
g_free(data);
}
static void ui_chart_progress_queue_redraw(ChartProgress *chart)
{
DB( g_print("\n[chartprogress] queue redraw\n") );
if( gtk_widget_get_realized(GTK_WIDGET(chart)) )
{
ui_chart_progress_recompute(chart);
drawarea_full_redraw(chart->drawarea, chart);
gtk_widget_queue_draw( chart->drawarea );
}
}
/*
** change the model and/or column
*/
void ui_chart_progress_set_dualdatas(ChartProgress *chart, GtkTreeModel *model, gchar *coltitle1, gchar *coltitle2, gchar *title, gchar *subtitle)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
DB( g_print("\n[chartprogress] set dual datas\n") );
ui_chart_progress_clear(chart);
if( GTK_IS_TREE_MODEL(model) )
{
DB( g_print(" store model %p and columns=%s:%s\n", model, coltitle1, coltitle2) );
chart->model = model;
if(coltitle1)
chart->budget_title = coltitle1;
if(coltitle2)
chart->result_title = coltitle2;
if(title != NULL)
chart->title = g_strdup(title);
if(subtitle != NULL)
chart->subtitle = g_strdup(subtitle);
ui_chart_progress_setup_with_model(chart, -1);
ui_chart_progress_queue_redraw(chart);
}
}
/*
** change the tooltip title
*/
void ui_chart_progress_set_title(ChartProgress * chart, gchar *title)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
chart->title = g_strdup(title);
DB( g_print("\n[chartprogress] set title = %s\n", chart->title) );
ui_chart_progress_recompute(chart);
}
void ui_chart_progress_set_subtitle(ChartProgress * chart, gchar *subtitle)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
chart->subtitle = g_strdup(subtitle);
DB( g_print("\n[chartprogress] set period = %s\n", chart->subtitle) );
ui_chart_progress_recompute(chart);
}
/*
** change the minor visibility
*/
void ui_chart_progress_show_minor(ChartProgress * chart, gboolean minor)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
chart->minor = minor;
ui_chart_progress_queue_redraw(chart);
}
void ui_chart_progress_set_color_scheme(ChartProgress * chart, gint index)
{
colorscheme_init(&chart->color_scheme, index);
}
/*
** set the minor parameters
*/
/*void ui_chart_progress_set_minor_prefs(ChartProgress * chart, gdouble rate, gchar *symbol)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
chart->minor_rate = rate;
chart->minor_symbol = symbol;
}*/
void ui_chart_progress_set_currency(ChartProgress * chart, guint32 kcur)
{
g_return_if_fail (GTK_IS_CHARTPROGRESS (chart));
chart->kcur = kcur;
}
/* = = = = = = = = = = = = = = = = */
static void
ui_chart_progress_init (ChartProgress * chart)
{
GtkWidget *widget, *hbox, *frame, *overlay, *label;
HbtkDrawProgContext *context = &chart->context;
DB( g_print("\n[chartprogress] init\n") );
chart->surface = NULL;
chart->nb_items = 0;
chart->hover = -1;
chart->title = NULL;
chart->subtitle = NULL;
chart->pfd = NULL;
chart->budget_title = "Budget";
chart->result_title = "Result";
context->barw = GTK_CHARTPROGRESS_BARW;
ui_chart_progress_set_color_scheme(chart, CHART_COLMAP_HOMEBANK);
widget=GTK_WIDGET(chart);
gtk_box_set_homogeneous(GTK_BOX(widget), FALSE);
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
hbtk_box_prepend (GTK_BOX (widget), frame);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_frame_set_child(GTK_FRAME(frame), hbox);
overlay = gtk_overlay_new ();
chart->drawarea = gtk_drawing_area_new();
gtk_widget_set_size_request(chart->drawarea, 150, 150 );
#if DYNAMICS == 1
gtk_widget_set_has_tooltip(chart->drawarea, TRUE);
#endif
gtk_widget_show(chart->drawarea);
gtk_overlay_set_child (GTK_OVERLAY(overlay), chart->drawarea);
hbtk_box_prepend (GTK_BOX (hbox), overlay);
label = gtk_label_new(NULL);
chart->breadcrumb = label;
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_label_set_track_visited_links(GTK_LABEL(label), FALSE);
gtk_overlay_add_overlay( GTK_OVERLAY(overlay), label );
gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (overlay), label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_set_margin_start(label, SPACING_MEDIUM);
gtk_widget_set_margin_top(label, SPACING_MEDIUM*4);
/* scrollbar */
chart->adjustment = GTK_ADJUSTMENT(gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0));
chart->scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL,GTK_ADJUSTMENT (chart->adjustment));
gtk_box_append (GTK_BOX (hbox), chart->scrollbar);
g_signal_connect( G_OBJECT(chart->drawarea), "configure-event", G_CALLBACK (drawarea_configure_event_callback), chart);
//g_signal_connect( G_OBJECT(chart->drawarea), "realize", G_CALLBACK(drawarea_realize_callback), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "draw", G_CALLBACK(drawarea_draw_callback), chart );
//TODO check this to redraw when gtk theme chnage
g_signal_connect( G_OBJECT(chart->drawarea), "style-updated", G_CALLBACK(drawarea_style_changed_callback), chart ) ;
#if DYNAMICS == 1
gtk_widget_add_events(GTK_WIDGET(chart->drawarea),
GDK_EXPOSURE_MASK |
//GDK_POINTER_MOTION_MASK |
//GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_SCROLL_MASK
);
//g_signal_connect( G_OBJECT(chart->drawarea), "query-tooltip", G_CALLBACK(drawarea_querytooltip_callback), chart );
g_signal_connect( G_OBJECT(chart->drawarea), "scroll-event", G_CALLBACK(drawarea_scroll_event_callback), chart ) ;
g_signal_connect( G_OBJECT(chart->drawarea), "motion-notify-event", G_CALLBACK(drawarea_motionnotifyevent_callback), chart );
#endif
g_signal_connect (G_OBJECT(chart->adjustment), "value-changed", G_CALLBACK (ui_chart_progress_first_changed), chart);
g_signal_connect (G_OBJECT(chart->breadcrumb), "activate-link", G_CALLBACK (drawarea_cb_root_activate_link), chart);
//g_signal_connect( G_OBJECT(chart->drawarea), "leave-notify-event", G_CALLBACK(ui_chart_progress_leave), chart );
//g_signal_connect( G_OBJECT(chart->drawarea), "enter-notify-event", G_CALLBACK(ui_chart_progress_enter), chart );
g_signal_connect( G_OBJECT(chart->drawarea), "button-press-event", G_CALLBACK(drawarea_cb_button_press_event), chart );
//g_signal_connect( G_OBJECT(chart->drawarea), "button-release-event", G_CALLBACK(ui_chart_progress_button_release), chart );
}
void
ui_chart_progress_destroy (GtkWidget * object)
{
ChartProgress *chart = GTK_CHARTPROGRESS(object);
g_return_if_fail (GTK_IS_CHARTPROGRESS (object));
DB( g_print("\n[chartprogress] destroy\n") );
ui_chart_progress_clear(GTK_CHARTPROGRESS (object));
if(chart->pfd)
{
pango_font_description_free (chart->pfd);
chart->pfd = NULL;
}
if (chart->surface)
{
cairo_surface_destroy (chart->surface);
chart->surface = NULL;
}
GTK_WIDGET_CLASS (parent_class)->destroy (object);
}
static void ui_chart_progress_class_init (ChartProgressClass * class)
{
//GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
DB( g_print("\n[chartprogress] class_init\n") );
//gobject_class = (GObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
parent_class = g_type_class_peek_parent (class);
//gobject_class->dispose = ui_chart_progress_dispose;
//gobject_class->finalize = ui_chart_progress_finalize;
//gobject_class->set_property = ui_chart_progress_set_property;
//gobject_class->get_property = ui_chart_progress_get_property;
widget_class->destroy = ui_chart_progress_destroy;
}
//gtk_chart_class_intern_init
GType ui_chart_progress_get_type ()
{
static GType ui_chart_progress_type = 0;
if (G_UNLIKELY(ui_chart_progress_type == 0))
{
const GTypeInfo ui_chart_progress_info =
{
sizeof (ChartProgressClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) ui_chart_progress_class_init,
NULL, /* class_finalize */
NULL, /* class_init */
sizeof (ChartProgress),
0, /* n_preallocs */
(GInstanceInitFunc) ui_chart_progress_init,
NULL /* value_table */
};
ui_chart_progress_type = g_type_register_static (GTK_TYPE_BOX, "ChartProgress",
&ui_chart_progress_info, 0);
}
return ui_chart_progress_type;
}
GtkWidget *
ui_chart_progress_new (void)
{
GtkWidget *chart;
DB( g_print("\n======================================================\n") );
DB( g_print("\n[chartprogress] new\n") );
chart = (GtkWidget *)g_object_new (GTK_TYPE_CHARTPROGRESS, NULL);
return chart;
}
homebank-5.9.7/src/list-account.c 0000644 0001750 0001750 00000057000 14736461407 016170 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "list-account.h"
#include "hub-account.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
static void lst_accview_to_string_row(GString *node, ToStringMode mode, GtkTreeModel *model, GtkTreeIter *iter, gchar *sub, gint flags)
{
gpointer p;
gint type;
gchar *text = "";
gchar sep;
gdouble clear, recon, today, future;
guint32 kcur = GLOBALS->kcur;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
clear = recon = today = future = 0.0;
gtk_tree_model_get (model, iter,
LST_DSPACC_DATATYPE, &type,
LST_DSPACC_DATAS, &p,
-1);
if( type == DSPACC_TYPE_HEADER )
{
PnlAccGrp *g = p;
text = g->name;
//g_string_append_printf(node, "%s\t\t\t\t\n", text);
g_string_append_printf(node, "%s", text);
if( flags & LST_TXN_ACC_REC )
g_string_append_c(node, sep);
if( flags & LST_TXN_ACC_CLR )
g_string_append_c(node, sep);
if( flags & LST_TXN_ACC_TOD )
g_string_append_c(node, sep);
if( flags & LST_TXN_ACC_FUT )
g_string_append_c(node, sep);
g_string_append_c(node, '\n');
}
else
{
if( type == DSPACC_TYPE_NORMAL )
{
Account *acc = p;
kcur = acc->kcur;
text = acc->name;
recon = acc->bal_recon;
clear = acc->bal_clear;
today = acc->bal_today;
future = acc->bal_future;
}
else
{
PnlAccGrp *g = p;
recon = g->bal_recon;
clear = g->bal_clear;
today = g->bal_today;
future = g->bal_future;
}
if( type == DSPACC_TYPE_SUBTOTAL )
text = _("Total");
else
if( type == DSPACC_TYPE_TOTAL )
text = _("Grand total");
g_string_append_printf(node, "%s%s", sub, text);
if( flags & LST_TXN_ACC_REC )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, recon, kcur, FALSE);
g_string_append_c(node, sep);
g_string_append(node, buf);
}
if( flags & LST_TXN_ACC_CLR )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, clear, kcur, FALSE);
g_string_append_c(node, sep);
g_string_append(node, buf);
}
if( flags & LST_TXN_ACC_TOD )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, today, kcur, FALSE);
g_string_append_c(node, sep);
g_string_append(node, buf);
}
if( flags & LST_TXN_ACC_FUT )
{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, future, kcur, FALSE);
g_string_append_c(node, sep);
g_string_append(node, buf);
}
g_string_append(node, "\n");
}
}
GString *lst_accview_to_string(GtkTreeView *treeview, ToStringMode mode)
{
GString *node;
GtkTreeModel *model;
GtkTreeIter iter, child;
gboolean valid;
guint32 nbcols, i;
gint uid, flags = 0;
gchar sep;
DB( g_print("\n[lst_accview] to string\n") );
node = g_string_new(NULL);
sep = (mode == HB_STRING_EXPORT) ? ';' : '\t';
// header (nbcols-2 for icon column)
nbcols = gtk_tree_view_get_n_columns (treeview) - 1;
for( i=1 ; i < nbcols ; i++ )
{
GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i);
//todo: ? restrict to visibility
if( GTK_IS_TREE_VIEW_COLUMN(column) )
{
if( gtk_tree_view_column_get_visible(column))
{
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
switch(uid)
{
case COL_DSPACC_RECON : flags |= LST_TXN_ACC_REC; break;
case COL_DSPACC_CLEAR : flags |= LST_TXN_ACC_CLR; break;
case COL_DSPACC_TODAY : flags |= LST_TXN_ACC_TOD; break;
case COL_DSPACC_FUTURE: flags |= LST_TXN_ACC_FUT; break;
}
g_string_append(node, gtk_tree_view_column_get_title (column));
if( i < nbcols-1 )
{
g_string_append_c(node, sep);
}
}
}
}
g_string_append_c(node, '\n');
//lines
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
lst_accview_to_string_row(node, mode, model, &iter, "", flags);
if( gtk_tree_model_iter_has_child(model, &iter) )
{
valid = gtk_tree_model_iter_children(model, &child, &iter);
while (valid)
{
lst_accview_to_string_row(node, mode, model, &child, "- ", flags);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child);
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
/*
** draw some icons according to the stored data structure
*/
static void lst_accview_cell_data_func_status (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Account *acc;
gchar *iconname = NULL;
gint dt;
gtk_tree_model_get(model, iter,
LST_DSPACC_DATATYPE, &dt,
LST_DSPACC_DATAS, &acc,
-1);
if( dt == DSPACC_TYPE_NORMAL )
{
switch(GPOINTER_TO_INT(user_data))
{
case 1:
if(acc->dspflags & FLAG_ACC_TMP_EDITED)
iconname = ICONNAME_HB_ITEM_EDITED;
else
if(acc->dspflags & FLAG_ACC_TMP_ADDED)
iconname = ICONNAME_HB_ITEM_ADDED;
// override if closed account
if( acc->flags & AF_CLOSED )
iconname = ICONNAME_HB_ITEM_CLOSED;
break;
case 2:
if( acc->flags & AF_HASNOTICE )
iconname = ICONNAME_WARNING;
break;
}
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
/*
** draw some text from the stored data structure
*/
static void lst_accview_cell_data_func_text (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gpointer p;
gint dt;
gint weight = PANGO_WEIGHT_BOLD;
gchar *text = NULL;
//gchar *groupname;
gtk_tree_model_get(model, iter,
LST_DSPACC_DATATYPE, &dt,
LST_DSPACC_DATAS, &p,
//LST_DSPACC_NAME, &groupname,
-1);
if( p == NULL )
goto end;
switch( dt )
{
case DSPACC_TYPE_NORMAL:
{
Account *acc = p;
weight = PANGO_WEIGHT_NORMAL;
text = acc->name;
}
break;
case DSPACC_TYPE_HEADER:
{
PnlAccGrp *g = p;
text = g->name;
}
break;
case DSPACC_TYPE_SUBTOTAL:
text = _("Total");
break;
case DSPACC_TYPE_TOTAL:
text = _("Grand total");
break;
}
end:
g_object_set(renderer, "weight", weight, "text", text, NULL);
}
static void
lst_accview_cell_data_func_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gpointer p;
gint dt;
gint colid = GPOINTER_TO_INT(user_data); //LST_DSPACC_(BANK/TODAY/FUTURE)
gdouble amount = -1;
gint weight = PANGO_WEIGHT_NORMAL;
gchar *color = NULL;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
LST_DSPACC_DATATYPE, &dt,
LST_DSPACC_DATAS, &p,
-1);
if( p == NULL )
goto end;
if( dt == DSPACC_TYPE_NORMAL )
{
Account *acc = p;
switch(colid)
{
case COL_DSPACC_CLEAR: amount = acc->bal_clear; break;
case COL_DSPACC_RECON: amount = acc->bal_recon; break;
case COL_DSPACC_TODAY: amount = acc->bal_today; break;
case COL_DSPACC_FUTURE: amount = acc->bal_future; break;
}
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, acc->kcur, GLOBALS->minor);
color = get_minimum_color_amount(amount, acc->minimum);
text = buf;
}
else
{
gboolean expanded = TRUE;
if( dt == DSPACC_TYPE_HEADER )
{
GtkTreePath* tp = gtk_tree_model_get_path(model, iter);
expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtk_tree_view_column_get_tree_view(col)), tp);
gtk_tree_path_free(tp);
}
if( dt != DSPACC_TYPE_HEADER || (expanded == FALSE) )
{
PnlAccGrp *g = p;
switch(colid)
{
case COL_DSPACC_CLEAR: amount = g->bal_clear; break;
case COL_DSPACC_RECON: amount = g->bal_recon; break;
case COL_DSPACC_TODAY: amount = g->bal_today; break;
case COL_DSPACC_FUTURE: amount = g->bal_future; break;
}
//nota: header is always in base currency
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, GLOBALS->kcur, GLOBALS->minor);
if(expanded)
weight = PANGO_WEIGHT_BOLD;
color = get_normal_color_amount(amount);
text = buf;
}
}
end:
g_object_set(renderer,
"weight", weight,
"foreground", color,
"text", text,
NULL);
}
static gint
lst_accview_func_compare (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint retval = 0;
gint dt1, dt2;
gpointer p1, p2;
gint pos1, pos2;
gtk_tree_model_get(model, a,
LST_DSPACC_POS, &pos1,
LST_DSPACC_DATATYPE, &dt1,
LST_DSPACC_DATAS, &p1,
-1);
gtk_tree_model_get(model, b,
LST_DSPACC_POS, &pos2,
LST_DSPACC_DATATYPE, &dt2,
LST_DSPACC_DATAS, &p2,
-1);
if( p1 == NULL || p2 == NULL )
return 0;
if( dt1 == DSPACC_TYPE_NORMAL && dt2 == DSPACC_TYPE_NORMAL )
{
Account *entry1 = p1;
Account *entry2 = p2;
retval = entry1->pos - entry2->pos;
}
else
if( dt1 == DSPACC_TYPE_HEADER && dt2 == DSPACC_TYPE_HEADER )
{
PnlAccGrp *elt1 = p1;
PnlAccGrp *elt2 = p2;
retval = pos1 - pos2;
if( !retval )
retval = hb_string_utf8_compare(elt1->name, elt2->name);
}
return retval;
}
static gboolean
lst_accview_func_selection(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
{
GtkTreeIter iter;
DB( g_print ("\n[lst_accview] selection\n") );
if( gtk_tree_path_get_depth( path ) < 2 )
return FALSE;
if(gtk_tree_model_get_iter(model, &iter, path))
{
gint dt;
gtk_tree_model_get(model, &iter,
LST_DSPACC_DATATYPE, &dt,
-1);
if( dt != DSPACC_TYPE_NORMAL )
return FALSE;
}
return TRUE;
}
static void
lst_accview_columns_prefs_get(GtkTreeView *treeview)
{
GtkTreeViewColumn *column;
gint i, uid;
DB( g_print ("\n[lst_accview] columns prefs get\n") );
DB( g_print(" nbcol=%d, nbsortid=%d\n", NUM_LST_COL_DSPACC, gtk_tree_view_get_n_columns (treeview)) );
for(i=0 ; i < NUM_LST_COL_DSPACC ; i++ )
{
column = gtk_tree_view_get_column(treeview, i);
if(column != NULL)
{
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
if( uid >= 0 )
{
gboolean visible;
visible = gtk_tree_view_column_get_visible (column);
PREFS->lst_acc_columns[i] = (visible == TRUE) ? uid : -uid;
DB( g_print(" col-%2d => %2d '%s' w=%d\n", i, uid, gtk_tree_view_column_get_title(column), PREFS->lst_ope_col_width[uid-1] ) );
/* save width for accounts */
if( uid == COL_DSPACC_ACCOUNTS )
{
PREFS->pnl_acc_col_acc_width = gtk_tree_view_column_get_width(column);
}
}
else //should not occurs
PREFS->lst_ope_columns[i] = 0;
}
}
}
static void
lst_accview_columns_prefs_set(GtkTreeView *treeview, gint *cols_id)
{
GtkTreeViewColumn *column;
gboolean visible;
gint uid, i = 0;
gint tmpval;
DB( g_print ("\n[lst_accview] columns prefs set\n") );
for(i=0; i < NUM_LST_COL_DSPACC ; i++ )
{
column = gtk_tree_view_get_column(treeview, i);
if( column != NULL )
{
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
tmpval = homebank_pref_list_column_get(cols_id, uid, NUM_LST_COL_DSPACC);
DB( g_print(" - pos:%2d uid:%d sortid:%2d (%s)\n", i, uid, tmpval, gtk_tree_view_column_get_title(column)) );
visible = tmpval < 0 ? FALSE : TRUE;
gtk_tree_view_column_set_visible (column, visible);
}
}
}
static GtkTreeViewColumn *
lst_accview_column_amount_new(gchar *name, gint id)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(id));
gtk_tree_view_column_set_title(column, name);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_accview_cell_data_func_amount, GINT_TO_POINTER(id), NULL);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
//#2004631 date and column title alignement
gtk_tree_view_column_set_alignment (column, 1.0);
//gtk_tree_view_column_set_spacing( column, 16 );
//gtk_tree_view_column_set_sort_column_id (column, LST_DSPACC_BANK);
return column;
}
static void
lst_accview_popmenu_cb_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
{
GtkTreeViewColumn *column = user_data;
//GtkWidget *treeview;
DB( g_print ("\n[lst_accview] menuitem activated\n") );
if( !GTK_IS_TREE_VIEW_COLUMN(column) )
return;
//TDOO: useless until we link dsp_accoutn balance to this list
//treeview = gtk_tree_view_column_get_tree_view(GTK_TREE_VIEW_COLUMN(column));
gtk_tree_view_column_set_visible(column, gtk_check_menu_item_get_active(checkmenuitem) );
//lst_accview_columns_prefs_get(GTK_TREE_VIEW(treeview));
}
static gint
lst_accview_cb_button_pressed_event (GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
{
GdkEventType type;
guint button = 0;
type = gdk_event_get_event_type(event);
gdk_event_get_button(event, &button);
DB( g_print ("\n[lst_accview] popmenu\n") );
if (type == GDK_BUTTON_PRESS && button == 3)
{
// check we ARE in the header but in bin window
if (gdk_event_get_window(event) != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)))
{
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
#else
gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gdk_event_get_time(event));
#endif
// On indique à l'appelant que l'on a géré cet événement.
return TRUE;
}
// On indique à l'appelant que l'on n'a pas géré cet événement.
}
return FALSE;
}
static void
lst_accview_popmenu_destroy(GtkTreeView *treeview, gpointer user_data)
{
DB( g_print ("\n[lst_accview] menu destroy\n") );
}
static GtkWidget *
lst_accview_popmenu_new(GtkTreeView *treeview)
{
GtkTreeViewColumn *column;
GtkWidget *menu;
GtkWidget *menuitem;
gint i, uid;
//GtkAccelGroup *accel_group = NULL;
//accel_group = gtk_accel_group_new();
//gtk_window_add_accel_group(GTK_WINDOW(data->window), accel_group);
DB( g_print ("\n[lst_accview] create popmenu\n") );
menu = gtk_menu_new();
//data->ME_popmenu = menu;
for(i=0 ; i < NUM_LST_COL_DSPACC ; i++ )
{
column = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i);
if( column != NULL )
{
//uid = gtk_tree_view_column_get_sort_column_id (column);
uid = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(column), "uid"));
DB( g_print (" col %d\n", uid) );
if( (uid == -1)
|| (uid == COL_DSPACC_STATUS)
|| (uid == COL_DSPACC_ACCOUNTS)
)
continue;
menuitem = gtk_check_menu_item_new_with_label ( gtk_tree_view_column_get_title (column) );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gtk_tree_view_column_get_visible (column) );
g_signal_connect (menuitem, "activate",
G_CALLBACK (lst_accview_popmenu_cb_activate), column);
}
}
g_signal_connect (menu, "destroy", G_CALLBACK (lst_accview_popmenu_destroy), NULL);
gtk_widget_show_all(menu);
return menu;
}
static gboolean
gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
gpointer data)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeModel *model;
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
GString *node;
gint dt;
Account *acc;
gchar buffer[256];
gchar *escapedname;
if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
&x, &y,
keyboard_tip,
&model, &path, &iter))
return FALSE;
gtk_tree_model_get(model, &iter,
LST_DSPACC_DATATYPE, &dt,
LST_DSPACC_DATAS, &acc,
-1);
// no tooltip if not an account
if( (dt != DSPACC_TYPE_NORMAL) )
{
gtk_tree_path_free (path);
return FALSE;
}
//#1993599 tooltip fail when & into acc name
escapedname = g_markup_escape_text(acc->name, -1);
node = g_string_sized_new(255);
g_string_append(node, "");
g_string_append(node, escapedname);
g_string_append(node, "\n");
g_free(escapedname);
//#2064754 always display a tooltip to keep consistency
g_string_append(node, "\n");
g_string_append(node, _("last reconciled"));
g_string_append(node, ": ");
if( acc->rdate > 0 )
{
GDate *date;
//format date
date = g_date_new_julian (acc->rdate);
g_date_strftime (buffer, 256-1, PREFS->date_format, date);
g_date_free(date);
g_string_append(node, buffer);
}
else
g_string_append(node, _("none"));
if( (acc->minimum < 0) && (acc->bal_today >= acc->minimum) )
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
g_string_append(node, "\n");
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, acc->bal_today - acc->minimum, acc->kcur, FALSE);
g_string_append(node, formatd_buf);
g_string_append(node, " ");
g_string_append(node, _("until overdraft"));
g_string_append(node, " (");
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, acc->minimum, acc->kcur, FALSE);
g_string_append(node, formatd_buf);
g_string_append(node, ")");
}
if( (acc->maximum > 0) && (acc->maximum >= acc->bal_today) )
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
g_string_append(node, "\n");
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, acc->maximum - acc->bal_today, acc->kcur, FALSE);
g_string_append(node, formatd_buf);
g_string_append(node, " ");
g_string_append(node, _("before maximum"));
g_string_append(node, " (");
hb_strfmon(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, acc->maximum, acc->kcur, FALSE);
g_string_append(node, formatd_buf);
g_string_append(node, ")");
}
gtk_tooltip_set_markup (tooltip, node->str);
gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
gtk_tree_path_free (path);
g_string_free(node, TRUE);
return TRUE;
}
static void
lst_accview_destroy(GtkWidget *widget, gpointer user_data)
{
struct lst_accview_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[lst_accview] destroy\n") );
lst_accview_columns_prefs_get(GTK_TREE_VIEW(data->treeview));
gtk_widget_destroy (data->menu);
}
GtkWidget *lst_accview_new(void)
{
struct lst_accview_data *data;
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
DB( g_print ("\n[lst_accview] create\n") );
data = g_malloc0(sizeof(struct lst_accview_data));
if(!data) return NULL;
/* create list store */
store = gtk_tree_store_new(
NUM_LST_DSPACC,
G_TYPE_INT, /* datatype */
G_TYPE_INT, /* pos */
G_TYPE_POINTER /* datas*/
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
data->treeview = treeview;
g_object_unref(store);
//store our window private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, data) );
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
//gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
// COLUMN_DESCRIPTION);
//gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW (view), TRUE);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_SINGLE);
/* Status */
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Status"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_accview_cell_data_func_status, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_accview_cell_data_func_status, GINT_TO_POINTER(2), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//add system icon to 1st column
gtk_tree_view_column_set_clickable(column, TRUE);
GtkWidget *img = hbtk_image_new_from_icon_name_16 (ICONNAME_EMBLEM_SYSTEM);
gtk_widget_show(img);
gtk_tree_view_column_set_widget(column, img);
/* Account */
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
column = gtk_tree_view_column_new();
g_object_set_data(G_OBJECT(column), "uid", GUINT_TO_POINTER(COL_DSPACC_ACCOUNTS));
gtk_tree_view_column_set_title(column, _("Accounts"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, lst_accview_cell_data_func_text, NULL, NULL);
//#2004631 date and column title alignement
//gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_min_width(column, HB_MINWIDTH_LIST/2);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_column_set_fixed_width(column, PREFS->pnl_acc_col_acc_width);
gtk_tree_view_set_expander_column(GTK_TREE_VIEW (treeview), column);
/* Reconciled */
column = lst_accview_column_amount_new(_("Reconciled"), COL_DSPACC_RECON);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* Cleared */
column = lst_accview_column_amount_new(_("Cleared"), COL_DSPACC_CLEAR);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* Today */
column = lst_accview_column_amount_new(_("Today"), COL_DSPACC_TODAY);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* Future */
column = lst_accview_column_amount_new(_("Future"), COL_DSPACC_FUTURE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column 7: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* disable selection for level 1 of the tree */
gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), lst_accview_func_selection, NULL, NULL);
//sort etc
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), lst_accview_func_compare, NULL, NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
lst_accview_columns_prefs_set(GTK_TREE_VIEW(treeview), PREFS->lst_acc_columns);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
GtkWidget *popmenu = lst_accview_popmenu_new(GTK_TREE_VIEW(treeview));
data->menu = popmenu;
//add tooltip
gtk_widget_set_has_tooltip (GTK_WIDGET (treeview), TRUE);
g_signal_connect (treeview, "destroy", G_CALLBACK (lst_accview_destroy), NULL);
g_signal_connect (treeview, "button-press-event", G_CALLBACK (lst_accview_cb_button_pressed_event), popmenu);
g_signal_connect (treeview, "query-tooltip",
G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
return(treeview);
}
homebank-5.9.7/src/hub-transaction.h 0000664 0001750 0001750 00000002156 14736461415 016674 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "dsp-mainwindow.h"
#ifndef __HUB_TRANSACTION_H__
#define __HUB_TRANSACTION_H__
typedef enum {
HUB_TXN_TYPE_NONE,
HUB_TXN_TYPE_FUTURE,
HUB_TXN_TYPE_REMIND
} HbHubTxnType;
void ui_hub_transaction_populate(struct hbfile_data *data);
GtkWidget *ui_hub_transaction_create(struct hbfile_data *data, HbHubTxnType type);
#endif homebank-5.9.7/src/hb-report.c 0000644 0001750 0001750 00000122361 15120541746 015461 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-report.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define DB1(x);
//#define DB1(x) (x);
#define DB2(x);
//#define DB2(x) (x);
#define DB3(x);
//#define DB3(x) (x);
//date debug
#define DBD(x);
//#define DBD(x) (x);
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
extern gchar *CYA_ABMONTHS[];
static void _datatable_init_count_key_row(DataTable *dt, gint grpby, Filter *flt);
/* = = = = = = = = = = = = = = = = = = = = */
/* CarCost */
void da_vehiclecost_free(CarCost *item)
{
if(item != NULL)
{
g_free(item);
}
}
CarCost *da_vehiclecost_malloc(void)
{
return g_malloc0(sizeof(CarCost));
}
void da_vehiclecost_destroy(GList *list)
{
GList *tmplist = g_list_first(list);
while (tmplist != NULL)
{
CarCost *item = tmplist->data;
da_vehiclecost_free(item);
tmplist = g_list_next(tmplist);
}
g_list_free(list);
}
/* = = = = = = = = = = = = = = = = = = = = */
static void da_datacol_free(DataCol *col)
{
if(col)
{
g_free(col->label);
g_free(col->xlabel);
g_free(col->misclabel);
//5.9 leak
g_free(col);
}
}
static DataCol *da_datacol_malloc(void)
{
return g_malloc0(sizeof(DataCol));
}
static void da_datarow_free(DataRow *row)
{
if(row)
{
g_free(row->label);
g_free(row->xlabel);
g_free(row->misclabel);
g_free(row->colexp);
g_free(row->colinc);
//5.9 leak
g_free(row);
}
}
static DataRow *da_datarow_malloc(guint32 nbcol)
{
DataRow *row;
row = g_malloc0(sizeof(DataRow));
if(!row)
return NULL;
row->colexp = g_malloc0((nbcol) * sizeof(gdouble));
row->colinc = g_malloc0((nbcol) * sizeof(gdouble));
row->nbcols = nbcol;
//5.9 check
if( !row->colexp || !row->colinc )
{
da_datarow_free(row);
return NULL;
}
return row;
}
//hub-reptime/hub-reptotal/repstats
void da_datatable_free(DataTable *dt)
{
guint i;
DB1( g_print("\n[report] da_datatable_free: %p\n", dt) );
if(dt != NULL)
{
DB1( g_print(" free keylist\n") );
g_free(dt->keylist);
dt->keylist = NULL;
DB1( g_print(" free datacol\n") );
for(i=0;inbcols;i++)
{
da_datacol_free(dt->cols[i]);
}
g_free(dt->cols);
dt->cols = NULL;
DB1( g_print(" free datarow\n") );
for(i=0;inbrows;i++)
{
da_datarow_free(dt->rows[i]);
}
g_free(dt->rows);
dt->rows = NULL;
DB1( g_print(" free total datarow\n") );
da_datarow_free(dt->totrow);
dt->totrow = NULL;
DB1( g_print(" free keyindex\n") );
g_free(dt->keyindex);
dt->keyindex = NULL;
DB1( g_print(" free datatable\n") );
g_free(dt);
DB1( g_print(" end free\n") );
}
}
static DataTable *da_datatable_malloc(gint grpby, gint intvl, Filter *flt)
{
DataTable *dt;
gboolean okcols, okrows;
guint i;
DB1( g_print("\n[report] da_datatable_malloc: %p\n", dt) );
dt = g_malloc0(sizeof(DataTable));
if(!dt)
return NULL;
_datatable_init_count_key_row(dt, grpby, flt);
dt->nbcols = report_interval_count(intvl, flt->mindate, flt->maxdate);
//dt->nbkeys = maximum keys for acc/pay/cat/tag
//dt->nbrows = nb of items
//dt->nbcols = nb of cols
DB1( g_print(" grpby:%d\n intvl:%d\n maxk:%d\n rows:%d\n cols:%d\n", grpby, intvl, dt->nbkeys, dt->nbrows, dt->nbcols) );
DB1( g_print(" alloc %d keyindex\n", dt->nbkeys) );
dt->keyindex = g_malloc0(dt->nbkeys * sizeof(gpointer));
//ordered list to insert cat before subcat
DB1( g_print(" alloc %d keylist\n", dt->nbrows) );
dt->keylist = g_malloc0( dt->nbrows * sizeof(guint32) );
DB1( g_print(" alloc %d row vector\n", dt->nbrows) );
dt->rows = g_malloc0(dt->nbrows * sizeof(gpointer));
DB1( g_print(" alloc %d col vector\n", dt->nbcols) );
dt->cols = g_malloc0(dt->nbcols * sizeof(gpointer));
DB1( g_print(" alloc 1 total row\n") );
dt->totrow = da_datarow_malloc(dt->nbcols);
//5.9 check
if( !dt->keyindex || !dt->keylist || !dt->rows || !dt->cols || !dt->totrow )
{
da_datatable_free(dt);
return NULL;
}
okcols = okrows = TRUE;
DB1( g_print(" alloc %d rows\n", dt->nbrows) );
for(i=0;inbrows;i++)
{
DataRow *dr = da_datarow_malloc(dt->nbcols);
//5.9 check
if( !dr ) { okcols = FALSE; break; }
dt->rows[i] = dr;
}
DB1( g_print(" alloc %d cols\n", dt->nbcols) );
for(i=0;inbcols;i++)
{
DataCol *dc = da_datacol_malloc();
//5.9 check
if( !dc ) { okrows = FALSE; break; }
dt->cols[i] = dc;
}
if( !okcols || !okrows )
{
da_datatable_free(dt);
return NULL;
}
return dt;
}
/* = = = = = = = = = = = = = = = = = = = = */
// slide the date to monday of the week
static void _hb_date_clamp_iso8601(GDate *date)
{
GDateWeekday wday;
//ISO 8601 from must be monday, to slice in correct week
wday = g_date_get_weekday(date);
g_date_subtract_days (date, wday - G_DATE_MONDAY);
}
// slide the date to start quarter
static void _hb_date_clamp_quarter_start(GDate *date)
{
guint quarternum = ((g_date_get_month(date)-1)/3)+1;
DBD( g_print(" init=%02d/%d > Q%d\n", g_date_get_month(date), g_date_get_year(date), quarternum) );
g_date_set_day(date, 1);
g_date_set_month(date, ((quarternum-1)*3)+1);
DBD( g_print(" start=%02d/%d\n", g_date_get_month(date), g_date_get_year(date)) );
}
// slide the date to start halfyear
static void _hb_date_clamp_halfyear_start(GDate *date)
{
guint halfyearnum = ((g_date_get_month(date)-1)/6)+1;
DBD( g_print(" init=%02d/%d > H%d\n", g_date_get_month(date), g_date_get_year(date), halfyearnum) );
g_date_set_day(date, 1);
g_date_set_month(date, ((halfyearnum-1)*6)+1);
DBD( g_print(" start=%02d/%d\n", g_date_get_month(date), g_date_get_year(date)) );
}
static guint _date_in_week(guint32 from, guint32 opedate, guint days)
{
GDate *date1, *date2;
gint pos;
date1 = g_date_new_julian(from);
date2 = g_date_new_julian(opedate);
DBD( g_print(" from=%d %02d-%02d-%04d ",
g_date_get_weekday(date1), g_date_get_day(date1), g_date_get_month(date1), g_date_get_year(date1)) );
//#1915643 week iso 8601
_hb_date_clamp_iso8601(date1);
pos = (opedate - g_date_get_julian(date1)) / days;
DBD( g_print(" shifted=%d %02d-%02d-%04d pos=%d\n",
g_date_get_weekday(date1), g_date_get_day(date1), g_date_get_month(date1), g_date_get_year(date1), pos) );
g_date_free(date2);
g_date_free(date1);
return(pos);
}
/*
** return the week list position correponding to the passed date
*/
static guint DateInWeek(guint32 from, guint32 opedate)
{
return _date_in_week(from, opedate, 7);
}
/*
** return the FortNight list position correponding to the passed date
*/
static guint DateInFortNight(guint32 from, guint32 opedate)
{
return _date_in_week(from, opedate, 14);
}
/*
** return the month list position correponding to the passed date
*/
static guint DateInMonth(guint32 from, guint32 opedate)
{
GDate *date1, *date2;
guint pos;
DBD( g_print("DateInMonth %d ,%d\n", from, opedate) );
//todo
// this return sometimes -1, -2 which is wrong
date1 = g_date_new_julian(from);
date2 = g_date_new_julian(opedate);
pos = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1);
DBD( g_print(" from=%d-%d ope=%d-%d => %d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
g_date_free(date2);
g_date_free(date1);
return(pos);
}
//for fiscal sub gap between 1st fiscal and 1/1/year
//int quarterNumber = (date.Month-1)/3+1;
//DateTime firstDayOfQuarter = new DateTime(date.Year, (quarterNumber-1)*3+1,1);
//DateTime lastDayOfQuarter = firstDayOfQuarter.AddMonths(3).AddDays(-1);
static guint DateInQuarter(guint32 from, guint32 opedate)
{
GDate *date1, *date2;
guint pos;
date1 = g_date_new_julian(from);
date2 = g_date_new_julian(opedate);
//#1758532 slide quarter start
_hb_date_clamp_quarter_start(date1);
pos = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/3;
DBD( g_print("-- from=%02d/%d ope=%02d/%d => pos=%d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
g_date_free(date2);
g_date_free(date1);
return(pos);
}
static guint DateInHalfYear(guint32 from, guint32 opedate)
{
GDate *date1, *date2;
guint pos;
date1 = g_date_new_julian(from);
date2 = g_date_new_julian(opedate);
//#2034618 slide halfyear start
_hb_date_clamp_halfyear_start(date1);
pos = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/6;
DBD( g_print(" from=%d-%d ope=%d-%d => %d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
g_date_free(date2);
g_date_free(date1);
return(pos);
}
/*
** return the year list position correponding to the passed date
*/
static guint DateInYear(guint32 from, guint32 opedate)
{
GDate *date;
guint year_from, year_ope, pos;
date = g_date_new_julian(from);
year_from = g_date_get_year(date);
g_date_set_julian(date, opedate);
year_ope = g_date_get_year(date);
g_date_free(date);
pos = year_ope - year_from;
DBD( g_print(" from=%d ope=%d => %d\n", year_from, year_ope, pos) );
return(pos);
}
static guint32 _datatable_interval_get_jdate(gint intvl, guint32 jfrom, gint idx)
{
GDate *date = g_date_new_julian(jfrom);
GDateWeekday wday;
guint32 jdate;
switch(intvl)
{
case REPORT_INTVL_DAY:
g_date_add_days(date, idx);
break;
case REPORT_INTVL_WEEK:
g_date_add_days(date, idx*7);
//#1915643 week iso 8601
//ISO 8601 from must be monday, to slice in correct week
wday = g_date_get_weekday(date);
g_date_subtract_days (date, wday-G_DATE_MONDAY);
g_date_add_days (date, G_DATE_WEDNESDAY);
break;
//#2000290
case REPORT_INTVL_FORTNIGHT:
_hb_date_clamp_iso8601(date);
g_date_add_days(date, idx*14);
break;
case REPORT_INTVL_MONTH:
g_date_add_months(date, idx);
break;
case REPORT_INTVL_QUARTER:
g_date_add_months(date, idx*3);
break;
case REPORT_INTVL_HALFYEAR:
g_date_add_months(date, idx*6);
break;
case REPORT_INTVL_YEAR:
g_date_add_years(date, idx);
break;
}
jdate = g_date_get_julian(date);
g_date_free(date);
return jdate;
}
/* = = = = = = = = = = = = = = = = = = = = */
static void datatable_set_keylist(DataTable *dt, guint32 idx, guint32 key)
{
if( idx < dt->nbrows )
dt->keylist[idx] = key;
else
g_warning("datatable invalid set keylist %d of %d", idx , dt->nbrows);
}
static void datatable_set_keyindex(DataTable *dt, guint32 key, guint32 idx)
{
if( key < dt->nbkeys )
dt->keyindex[key] = idx;
else
g_warning("datatable invalid set keyindex %d of %d", key , dt->nbkeys);
}
static void _datatable_init_count_key_row(DataTable *dt, gint grpby, Filter *flt)
{
guint n_row, n_key;
n_row = n_key = 0;
if( grpby != REPORT_GRPBY_MONTH && grpby != REPORT_GRPBY_YEAR )
{
switch(grpby)
{
case REPORT_GRPBY_NONE:
n_row = 1;
n_key = 1;
break;
case REPORT_GRPBY_TYPE:
n_row = 2;
n_key = 2;
break;
case REPORT_GRPBY_CATEGORY:
n_row = da_cat_length();
n_key = da_cat_get_max_key();
break;
case REPORT_GRPBY_PAYEE:
n_row = da_pay_length();
n_key = da_pay_get_max_key();
break;
//5.7: todo check this +1
case REPORT_GRPBY_ACCOUNT:
n_row = da_acc_length();
n_key = da_acc_get_max_key();
break;
case REPORT_GRPBY_ACCGROUP:
n_row = da_grp_length();
n_key = da_grp_get_max_key();
break;
case REPORT_GRPBY_TAG:
n_row = da_tag_length();
n_key = da_tag_get_max_key();
break;
}
}
else
{
GDate *date1 = g_date_new_julian(flt->mindate);
GDate *date2 = g_date_new_julian(flt->maxdate);
switch(grpby)
{
case REPORT_GRPBY_MONTH:
n_row = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1) + 1;
n_key = n_row;
break;
case REPORT_GRPBY_YEAR:
n_row = g_date_get_year(date2) - g_date_get_year(date1) + 1;
n_key = n_row;
break;
}
g_date_free(date2);
g_date_free(date1);
}
//we add 1 to every key as 0 is an item
dt->nbkeys = n_key + 1;
dt->nbrows = n_row;
}
static void _datatable_init_items_cat(DataTable *dt)
{
GList *lcat, *l;
guint idx = 0;
lcat = l = category_glist_sorted(HB_GLIST_SORT_NAME);
while (l != NULL )
{
Category *cat = l->data;
DataRow *dr = dt->rows[idx];
if( dr != NULL )
{
dr->pos = idx;
dr->label = g_strdup( da_cat_get_name(cat) );
//dt->keyindex[cat->key] = idx;
datatable_set_keyindex(dt, cat->key, idx);
DB2( g_print(" +cat k:%d, idx:%d %s'%s'\n", cat->key, idx, cat->parent==0 ? "" : " +", dr->label) );
}
l = g_list_next(l);
idx++;
}
g_list_free(lcat);
//init keylist
DB2( g_print(" add cat keys") );
idx = 0;
lcat = l = category_glist_sorted(HB_GLIST_SORT_KEY);
while (l != NULL)
{
Category *cat = l->data;
//dt->keylist[idx] = cat->key;
datatable_set_keylist(dt, idx, cat->key);
DB2( g_print(" %d", cat->key) );
l = g_list_next(l);
idx++;
}
g_list_free(lcat);
DB2( g_print("\n") );
}
static void _datatable_init_items_pay(DataTable *dt)
{
GList *lpay, *l;
guint idx = 0;
lpay = l = payee_glist_sorted(HB_GLIST_SORT_NAME);
while (l != NULL )
{
Payee *pay = l->data;
DataRow *dr = dt->rows[idx];
if( dr != NULL )
{
dr->pos = idx;
dr->label = g_strdup( da_pay_get_name(pay) );
DB2( g_print(" +pay k:%d, idx:%d '%s'\n", pay->key, idx, dr->label) );
//store transpose
//dt->keyindex[pay->key] = idx;
datatable_set_keyindex(dt, pay->key, idx);
//store keylist
//dt->keylist[idx] = pay->key;
datatable_set_keylist(dt, idx, pay->key);
}
l = g_list_next(l);
idx++;
}
g_list_free(lpay);
}
static void _datatable_init_items_acc(DataTable *dt)
{
GList *lacc, *l;
guint idx = 0;
lacc = l = account_glist_sorted(HB_GLIST_SORT_NAME);
while (l != NULL )
{
Account *acc = l->data;
DataRow *dr = dt->rows[idx];
if( dr != NULL )
{
dr->pos = idx;
dr->label = g_strdup( acc->name );
DB2( g_print(" +acc k:%d, idx:%d '%s'\n", acc->key, idx, dr->label) );
//store transpose
dt->keyindex[acc->key] = idx;
//datatable_set_keyindex(dt, acc->key, idx);
//store keylist
dt->keylist[idx] = acc->key;
//datatable_set_keylist(dt, idx, acc->key);
}
l = g_list_next(l);
idx++;
}
g_list_free(lacc);
}
static void _datatable_init_items_accgrp(DataTable *dt)
{
GList *lgrp, *l;
guint idx = 0;
lgrp = l = group_glist_sorted(HB_GLIST_SORT_NAME);
while (l != NULL )
{
Group *grp = l->data;
DataRow *dr = dt->rows[idx];
if( dr != NULL )
{
dr->pos = idx;
dr->label = g_strdup( (grp->key == 0) ? _("(no group)") : grp->name );
DB2( g_print(" +grp k:%d, idx:%d '%s'\n", grp->key, idx, dr->label) );
//store transpose
dt->keyindex[grp->key] = idx;
//datatable_set_keyindex(dt, acc->key, idx);
//store keylist
dt->keylist[idx] = grp->key;
//datatable_set_keylist(dt, idx, acc->key);
}
l = g_list_next(l);
idx++;
}
g_list_free(lgrp);
}
static void _datatable_init_items_tag(DataTable *dt)
{
GList *ltag, *l;
guint idx = 0;
ltag = l = tag_glist_sorted(HB_GLIST_SORT_NAME);
while (l != NULL )
{
Tag *tag = l->data;
DataRow *dr = dt->rows[idx];
if( dr != NULL )
{
dr->pos = idx;
dr->label = g_strdup( (tag->key == 0) ? _("(no tag)") : tag->name );
DB2( g_print(" +tag k:%d, idx:%d '%s'\n", tag->key, idx, dr->label) );
//store transpose
//dt->keyindex[tag->key] = idx;
datatable_set_keyindex(dt, tag->key, idx);
//store keylist
//dt->keylist[idx] = tag->key;
datatable_set_keylist(dt, idx, tag->key);
}
l = g_list_next(l);
idx++;
}
g_list_free(ltag);
}
static void datatable_data_get_xlabel(GDate *date, guint intvl, gchar *intvlname, gsize slen)
{
intvlname[0] ='\0';
switch(intvl)
{
case REPORT_INTVL_DAY:
case REPORT_INTVL_FORTNIGHT:
g_date_strftime (intvlname, slen, "%b-%d", date);
break;
case REPORT_INTVL_WEEK:
//TRANSLATORS: printf string for year of week W, ex. 2019-W52 for week 52 of 2019
g_snprintf(intvlname, slen, _("w%02d"), g_date_get_iso8601_week_of_year(date));
break;
case REPORT_INTVL_MONTH:
g_snprintf(intvlname, slen, "%s", _(CYA_ABMONTHS[g_date_get_month(date)]));
break;
case REPORT_INTVL_QUARTER:
//todo: will be innacurrate here if fiscal year start not 1/jan
//TRANSLATORS: printf string for year of quarter Q, ex. 2019-Q4 for quarter 4 of 2019
g_snprintf(intvlname, slen, _("q%d"), ((g_date_get_month(date)-1)/3)+1);
break;
case REPORT_INTVL_HALFYEAR:
//#2007712
//TRANSLATORS: printf string for half-year H, ex. 2019-H1 for 1st half-year of 2019
g_snprintf(intvlname, slen, _("h%d"), g_date_get_month(date) < 7 ? 1 : 2);
break;
case REPORT_INTVL_YEAR:
g_snprintf(intvlname, slen, "%d", g_date_get_year(date));
break;
}
}
static void datatable_init_items(DataTable *dt, gint grpby, guint32 jfrom)
{
GDate *date;
gchar buffer[64], *name;
guint32 jdate;
guint idx, slen = 63;
GDateYear prevyear = 0;
DB1( g_print("\n-- datatable int rows --\n") );
if( grpby == REPORT_GRPBY_CATEGORY
|| grpby == REPORT_GRPBY_PAYEE
|| grpby == REPORT_GRPBY_ACCOUNT
|| grpby == REPORT_GRPBY_ACCGROUP
|| grpby == REPORT_GRPBY_TAG
)
{
switch(grpby)
{
case REPORT_GRPBY_CATEGORY:
_datatable_init_items_cat(dt);
break;
case REPORT_GRPBY_PAYEE:
_datatable_init_items_pay(dt);
break;
case REPORT_GRPBY_ACCOUNT:
_datatable_init_items_acc(dt);
break;
case REPORT_GRPBY_ACCGROUP:
_datatable_init_items_accgrp(dt);
break;
case REPORT_GRPBY_TAG:
_datatable_init_items_tag(dt);
break;
}
return;
}
date = g_date_new();
jdate = GLOBALS->today;
for(idx=0;idxnbrows;idx++)
{
DataRow *dr = dt->rows[idx];
guint intvl = REPORT_INTVL_NONE;
name = NULL;
switch(grpby)
{
case REPORT_GRPBY_NONE:
//technical label for sum
name = "none";
DB2( g_print(" +none k:%d, idx:%d '%s'\n", idx, idx, name) );
break;
case REPORT_GRPBY_TYPE:
name = idx == 0 ? "exp" : "inc";
DB2( g_print(" +type k:%d, idx:%d '%s'\n", idx, idx, name) );
break;
case REPORT_GRPBY_MONTH:
intvl = REPORT_INTVL_MONTH;
jdate = report_interval_snprint_name(buffer, sizeof(buffer)-1, intvl, jfrom, idx);
name = buffer;
DB2( g_print(" +month k:%d, idx:%d '%s'\n", idx, idx, name) );
break;
case REPORT_GRPBY_YEAR:
intvl = REPORT_INTVL_YEAR;
jdate = report_interval_snprint_name(buffer, sizeof(buffer)-1, intvl, jfrom, idx);
name = buffer;
DB2( g_print(" +year k:%d, idx:%d '%s'\n", idx, idx, name) );
break;
}
dr->pos = idx;
if( name != NULL )
dr->label = g_strdup(name);
//store transpose
//dt->keyindex[idx] = idx;
datatable_set_keyindex(dt, idx, idx);
//store keylist
//dt->keylist[idx] = idx;
datatable_set_keylist(dt, idx, idx);
//manage xlabel
g_date_set_julian(date, jdate);
datatable_data_get_xlabel(date, intvl, buffer, slen);
dr->xlabel = g_strdup(buffer);
//set flags => moved into forecast
//if( jdate > GLOBALS->today )
// dc->flags |= RF_FORECAST;
if( (intvl != REPORT_INTVL_YEAR) && (g_date_get_year(date) > prevyear) && (g_date_get_month(date) == 1) )
{
g_snprintf(buffer, slen, "%d", g_date_get_year(date));
dr->misclabel = g_strdup(buffer);
dr->flags |= RF_NEWYEAR;
}
prevyear = g_date_get_year(date);
}
g_date_free(date);
/*end:
//debug keyindex
g_print(" keyindex:");
for(idx=0;idxnbkeys;idx++)
g_print(" %d", dt->keyindex[idx]);
g_print("\n");
//debug keylist
g_print(" keylist:");
for(idx=0;idxnbrows;idx++)
g_print(" %d", dt->keylist[idx]);
g_print("\n");
*/
}
static void datatable_init_columns(DataTable *dt, gint intvl, guint32 jfrom)
{
GDate *date;
gchar intvlname[64];
guint32 jdate;
guint idx, slen = 63;
GDateYear prevyear = 0;
date = g_date_new();
for(idx=0;idxnbcols;idx++)
{
DataCol *dc = dt->cols[idx];
//TODO: check
report_interval_snprint_name(intvlname, sizeof(intvlname)-1, intvl, jfrom, idx);
dc->label = g_strdup(intvlname);
//new stuff
jdate = _datatable_interval_get_jdate(intvl, jfrom, idx);
g_date_set_julian(date, jdate);
datatable_data_get_xlabel(date, intvl, intvlname, slen);
dc->xlabel = g_strdup(intvlname);
//set flags => moved into forecast
if( jdate > GLOBALS->today )
dc->flags |= RF_FORECAST;
if( (intvl != REPORT_INTVL_YEAR) && (g_date_get_year(date) > prevyear) && (g_date_get_month(date) == 1) )
{
g_snprintf(intvlname, slen, "%d", g_date_get_year(date));
dc->misclabel = g_strdup(intvlname);
dc->flags |= RF_NEWYEAR;
}
prevyear = g_date_get_year(date);
}
g_date_free(date);
}
static gint datatable_data_get_txnsign(DataTable *dt, guint32 kcat, gdouble amount)
{
gint txnsign = 0;
//raw expense/income mode
if( (dt->flags & REPORT_COMP_FLG_CATSIGN) )
{
txnsign = category_root_type_get(kcat);
//todo: fallback maybe ?
//if( txnsign == 0 )
}
else
{
txnsign = (amount < 0.0) ? -1 : 1;
}
return txnsign;
}
static void datatable_add(DataTable *dt, guint32 key, guint32 col, gdouble amount, gint sign, gboolean addtotal)
{
DataRow *dr;
if( hb_amount_cmp(amount, 0.0) == 0 )
return;
dr = report_data_get_row(dt, key);
if( dr == NULL )
return;
if( col < dt->nbcols )
{
//total mode must filter opposite sign categories
if( dt->intvl == REPORT_INTVL_NONE )
{
if( (dt->flags & REPORT_COMP_FLG_SPENDING) && sign == 1 )
return;
//future
// if( (dt->flags & REPORT_COMP_FLG_REVENUE) && sign == -1 )
// return;
}
DB3( g_print(">>> add to k:%d c:%d %.2f sign=%d\n", key, col, amount, sign) );
if( sign == -1 )
{
dr->colexp[col] += amount;
dr->rowexp += amount;
if( addtotal == TRUE )
{
dt->totrow->colexp[col] += amount;
dt->totrow->rowexp += amount;
}
}
else
if( sign == 1 )
{
dr->colinc[col] += amount;
dr->rowinc += amount;
if( addtotal == TRUE )
{
dt->totrow->colinc[col] += amount;
dt->totrow->rowinc += amount;
}
}
}
else
g_warning("datatable add invalid col %d of %d", col, dt->nbcols);
}
static void datatable_data_cols_cumulate(DataTable *dt, gint intvl, Filter *flt)
{
DB2( g_print("\n-- cumulate columns --\n") );
DB2( g_print(" n_row=%d n_col=%d\n", dt->nbrows, dt->nbcols) );
//cumulate columns
if( dt->nbcols > 1 )
{
guint row, col;
for(row=0;rownbrows;row++)
{
DataRow *dr = dt->rows[row];
DB2( g_print(" row %d: ", row) );
for(col=1;colnbcols;col++)
{
guint32 jdate = _datatable_interval_get_jdate(intvl, flt->mindate, col);
if( jdate < dt->maxpostdate)
{
DB2( g_print("%d, ", col) );
dr->colinc[col] += dr->colinc[col-1];
dr->colexp[col] += dr->colexp[col-1];
dt->totrow->colinc[col] += dr->colinc[col-1];
dt->totrow->colexp[col] += dr->colexp[col-1];
}
}
DB2( g_print("\n") );
}
}
}
static void datatable_data_add_balance(DataTable *dt, gint grpby, gint intvl, Filter *flt)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
DB1( g_print(" -- try add balance\n") );
if( (grpby != REPORT_GRPBY_NONE)
&& (grpby != REPORT_GRPBY_ACCOUNT)
&& (grpby != REPORT_GRPBY_ACCGROUP)
)
return;
DB1( g_print(" -- add balance\n") );
//TODO: we should rely on rows...
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
guint32 pos = 0, txnsign;
gdouble amount;
//#1674045 ony rely on nosummary
if( (acc->flags & (AF_NOREPORT)) )
goto next_acc;
//#2000294 enable account filtering
if( filter_acc_match(flt, acc) == 0 )
goto next_acc;
//add initial amount to col[0]
amount = hb_amount_base(acc->initial, acc->kcur);
//we can't use report_items_get_key here
//for none, we only allocate the row[0], so pos is 0
pos = 0;
switch(grpby)
{
case REPORT_GRPBY_ACCOUNT:
pos = acc->key;
break;
case REPORT_GRPBY_ACCGROUP:
pos = acc->kgrp;
break;
}
txnsign = (amount < 0.0) ? -1 : 1;
datatable_add(dt, pos, 0, amount, txnsign, TRUE);
DB2( g_print(" ** add initbal %8.2f to row:%d, col:%d\n", amount, acc->key, 0) );
//add txn amount prior mindate to col[0]
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
//5.5 forgot to filter...
//#1886123 include remind based on user prefs
if( !transaction_is_balanceable(txn) )
goto next_txn;
//enable filters : make no sense or not
//if( (filter_txn_match(flt, txn) == 1) )
//{
pos = report_items_get_key(grpby, flt->mindate, txn);
amount = report_txn_amount_get(flt, txn);
amount = hb_amount_base(amount, txn->kcur);
if( txn->date < flt->mindate )
{
DB2( g_print(" **add %8.2f row:%d, col:%d\n", amount, pos, 0) );
txnsign = datatable_data_get_txnsign(dt, txn->kcat, amount);
datatable_add(dt, pos, 0, amount, txnsign, TRUE);
}
//}
next_txn:
lnk_txn = g_list_next(lnk_txn);
}
next_acc:
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void datatable_data_add_txn(DataTable *dt, gint grpby, gint intvl, Transaction *txn, Filter *flt)
{
guint i;
guint key = 0;
guint col = 0;
gdouble amount;
gint txnsign;
amount = report_txn_amount_get(flt, txn);
amount = hb_amount_base(amount, txn->kcur);
txnsign = datatable_data_get_txnsign(dt, txn->kcat, amount);
DB2( g_print("\n srctxn %.2f, sign=%d, split=%d, cat=%d, pay=%d, acc=%d\n", amount, txnsign,
(txn->flags & OF_SPLIT), txn->kcat, txn->kpay, txn->kacc) );
col = report_interval_get_pos(intvl, flt->mindate, txn);
DB2( g_print(" col=%d (max is %d)\n", col, dt->nbcols) );
switch( grpby )
{
case REPORT_GRPBY_CATEGORY:
//case REPORT_GRPBY_SUBCATEGORY:
//for split, affect the amount to the category
if( txn->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(txn->splits);
Split *split;
gboolean status;
gint sinsert;
DB2( g_print(" %d splits\n", nbsplit) );
for(i=0;isplits, i);
status = da_flt_status_cat_get(flt, split->kcat);
sinsert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_CATEGORY] == 2) sinsert ^= 1;
if( (flt->option[FLT_GRP_CATEGORY] == 0) || sinsert)
{
Category *cat = da_cat_get(split->kcat);
amount = hb_amount_base(split->amount, txn->kcur);
if( cat != NULL )
{
txnsign = datatable_data_get_txnsign(dt, split->kcat, amount);
DB2( g_print(" cat split insert=%d, kcat=%d %.2f %d\n", sinsert, split->kcat, amount, txnsign) );
datatable_add(dt, cat->key, col, amount, txnsign, TRUE);
if( cat->parent != 0 )
datatable_add(dt, cat->parent, col, amount, txnsign, FALSE);
}
}
else
{
DB2( g_print(" >skipped, cat get=%d\n", sinsert) );
}
}
}
else
{
Category *cat = da_cat_get(txn->kcat);
if( cat != NULL )
{
DB2( g_print(" cat normal kcat=%d %2.f\n", txn->kcat, amount) );
datatable_add(dt, cat->key, col, amount, txnsign, TRUE);
if( cat->parent != 0 )
datatable_add(dt, cat->parent, col, amount, txnsign, FALSE);
}
}
break;
case REPORT_GRPBY_TAG:
if(txn->tags != NULL)
{
guint32 *tptr = txn->tags;
while(*tptr)
{
//#2031693 not -1 here
key = *tptr;
datatable_add(dt, key, col, amount, txnsign, TRUE);
tptr++;
}
}
//2031693 + add notags
else
datatable_add(dt, 0, col, amount, txnsign, TRUE);
break;
//5.8 define exp/inc by category type
case REPORT_GRPBY_TYPE:
if( txn->flags & OF_SPLIT )
{
guint nbsplit = da_splits_length(txn->splits);
Split *split;
DB2( g_print(" %d splits\n", nbsplit) );
for(i=0;isplits, i);
//we don't care cat filter here
amount = hb_amount_base(split->amount, txn->kcur);
txnsign = datatable_data_get_txnsign(dt, split->kcat, amount);
DB2( g_print(" by type cat split add kcat=%d %.2f %d\n", split->kcat, amount, txnsign) );
if( (dt->flags & REPORT_COMP_FLG_SPENDING) && txnsign == -1 )
{
DB2( g_print(" add exp\n") );
datatable_add(dt, 0, col, amount, txnsign, FALSE); //expense
}
if( (dt->flags & REPORT_COMP_FLG_REVENUE) && txnsign == 1 )
{
DB2( g_print(" add inc\n") );
datatable_add(dt, 1, col, amount, txnsign, FALSE); //income
}
}
}
else
{
DB2( g_print(" by type: sign=%d\n", txnsign) );
if( (dt->flags & REPORT_COMP_FLG_SPENDING) && txnsign == -1 )
{
DB2( g_print(" add exp\n") );
datatable_add(dt, 0, col, amount, txnsign, FALSE); //expense
}
if( (dt->flags & REPORT_COMP_FLG_REVENUE) && txnsign == 1 )
{
DB2( g_print(" add inc\n") );
datatable_add(dt, 1, col, amount, txnsign, FALSE); //income
}
}
break;
//all others
default:
key = report_items_get_key(grpby, flt->mindate, txn);
datatable_add(dt, key, col, amount, txnsign, TRUE);
break;
}
}
//5.7 forecast attempt
static void report_compute_forecast(DataTable *dt, gint grpby, gint intvl, Filter *flt)
{
GList *list;
gint nbinsert;
guint32 curdate, maxpostdate;
GDate *post_date;
DB1( g_print("\n[report] compute_forecast\n") );
if( intvl == REPORT_INTVL_NONE )
{
DB1( g_print(" no forecast for total mode\n") );
return;
}
post_date = g_date_new();
//TODO: change this
maxpostdate = dt->maxpostdate;
list = g_list_first(GLOBALS->arc_list);
while (list != NULL)
{
Archive *arc = list->data;
DB2( g_print("--------\n eval post of '%s' %.2f\n", arc->memo, arc->amount) );
if(scheduled_is_postable(arc) == TRUE)
{
Account *acc;
Transaction *txn, *child;
DB2( g_print(" is postable\n") );
nbinsert = 0;
child = NULL;
txn = da_transaction_malloc();
da_transaction_init_from_template(txn, arc);
acc = da_acc_get(txn->kacc);
if(acc)
{
//#5.7.3 don't forecast...
if( acc->flags & (AF_NOREPORT|AF_CLOSED) )
goto nextarc;
txn->kcur = acc->kcur;
}
txn->date = curdate = arc->nextdate;
//#2048236 also add child xfer txn to computing
if( txn->flags & OF_INTXFER )
{
child = transaction_xfer_child_new_from_txn(txn);
}
//if arc->nexdate is prior flt->mindate, it will be filtered out
if( (filter_txn_match(flt, txn) == 1) )
{
while(curdate <= maxpostdate)
{
txn->date = curdate;
DB3( hb_print_date(curdate, ">>> curdate=") );
datatable_data_add_txn(dt, grpby, intvl, txn, flt);
//#2048236 also add child xfer txn to computing
if( child != NULL )
{
child->date = curdate;
datatable_data_add_txn(dt, grpby, intvl, child, flt);
}
//mark column as forecast
guint idx = report_interval_get_pos(intvl, flt->mindate, txn);
DataCol *dc = report_data_get_col(dt, idx);
if( dc != NULL )
dc->flags |= RF_FORECAST;
curdate = scheduled_date_get_next_post(post_date, arc, curdate);
nbinsert++;
// break if over limit
if( (arc->rec_flags & TF_LIMIT) && (nbinsert >= arc->limit) )
break;
}
}
da_transaction_free (txn);
da_transaction_free (child);
}
nextarc:
list = g_list_next(list);
}
g_date_free(post_date);
}
/* = = = = = = = = = = = = = = = = = = = = */
DataTable *report_compute(gint grpby, gint intvl, Filter *flt, GQueue *txn_queue, gint flags)
{
DataTable *dt;
GList *list;
guint32 maxpostdate = flt->maxdate;
DB1( g_print("\n[report] == report_compute ==\n") );
DB2(
{
gchar *txt = "??";
switch(grpby){
case REPORT_GRPBY_NONE: txt="none"; break;
case REPORT_GRPBY_CATEGORY: txt="cat"; break;
case REPORT_GRPBY_PAYEE: txt="pay"; break;
case REPORT_GRPBY_ACCOUNT: txt="acc"; break;
case REPORT_GRPBY_TAG: txt="tag"; break;
case REPORT_GRPBY_MONTH: txt="month"; break;
case REPORT_GRPBY_YEAR: txt="year"; break;
case REPORT_GRPBY_ACCGROUP: txt="accgrp"; break;
case REPORT_GRPBY_TYPE: txt="type"; break;
}
g_print(" grpby: %d %s\n", grpby, txt);
}
);
DB2( hb_print_date(maxpostdate, "maxdate") );
//set maxpostdate
/*if( do_forecast )
{
if( filter_preset_daterange_future_enable(flt, flt->range) )
{
GDate *post_date = g_date_new();
g_date_set_time_t(post_date, time(NULL));
g_date_add_months(post_date, PREFS->rep_forecat_nbmonth);
g_date_set_day(post_date, g_date_get_days_in_month(g_date_get_month(post_date), g_date_get_year(post_date)));
maxpostdate = g_date_get_julian(post_date);
DB2( hb_print_date(maxpostdate, "max forecast date") );
g_date_free(post_date);
flt->maxdate = maxpostdate;
}
}*/
dt = da_datatable_malloc(grpby, intvl, flt);
if( dt == NULL )
return dt;
dt->flags = flags;
dt->grpby = grpby;
dt->intvl = intvl;
dt->maxpostdate = maxpostdate;
datatable_init_items(dt, grpby, flt->mindate);
datatable_init_columns(dt, intvl, flt->mindate);
//balance must keep xfer
/*if( do_balance == TRUE )
{
}*/
//process txn
DB2( g_print("\n-- ventilate txn amount to row/col --\n") );
DB2( g_print(" option: signbycat=%d\n", flags & REPORT_COMP_FLG_CATSIGN) );
DB2( g_print(" option: spending =%d\n", flags & REPORT_COMP_FLG_SPENDING) );
DB2( g_print(" option: revenue =%d\n", flags & REPORT_COMP_FLG_REVENUE) );
list = g_queue_peek_head_link(txn_queue);
while (list != NULL)
{
Transaction *txn = list->data;
if( (filter_txn_match(flt, txn) == 1) )
{
datatable_data_add_txn(dt, grpby, intvl, txn, flt);
}
list = g_list_next(list);
}
//TODO: add prefs
//if( PREFS->xxx )
if( flags & REPORT_COMP_FLG_FORECAST )
{
DB2( g_print("\n-- compute forecast --\n") );
report_compute_forecast(dt, grpby, intvl, flt);
}
//truncate in sign based category
if( flags & REPORT_COMP_FLG_CATSIGN )
{
DB2( g_print("\n-- typebycat post compute --\n") );
if( grpby == REPORT_GRPBY_TYPE )
{
guint col;
DataRow *dr1 = dt->rows[0]; //exp
DataRow *dr2 = dt->rows[1]; //inc
for(col=0;colnbcols;col++)
{
if( flags & REPORT_COMP_FLG_SPENDING && dr1->colexp[col] > 0.0 )
dr1->colexp[col] = 0.0;
if( flags & REPORT_COMP_FLG_REVENUE && dr2->colinc[col] < 0.0 )
dr2->colinc[col] = 0.0;
}
DB2( g_print(" processed %d\n", col) );
}
}
//process balance mode
if( flags & REPORT_COMP_FLG_BALANCE )
{
DB2( g_print("\n-- compute balance --\n") );
datatable_data_add_balance(dt, grpby, intvl, flt);
datatable_data_cols_cumulate(dt, intvl, flt);
}
return dt;
}
DataCol *report_data_get_col(DataTable *dt, guint32 idx)
{
DataCol *retval = NULL;
if( idx < dt->nbcols )
{
retval = dt->cols[idx];
}
else
g_warning("datatable invalid get col %d of %d", idx, dt->nbcols);
return retval;
}
//get a specific row by key
//hub-reptime/hub-reptotal/repstats
DataRow *report_data_get_row(DataTable *dt, guint32 key)
{
DataRow *retval = NULL;
guint32 idx;
if( key < dt->nbkeys )
{
//we should transpose here
idx = dt->keyindex[key];
if( idx < dt->nbrows )
{
DB3( g_print(">>> get row=%d for key=%d\n", idx, key) );
retval = dt->rows[idx];
}
else
g_warning("datatable invalid get row %d of %d", idx, dt->nbrows);
}
else
g_warning("datatable invalid get row key %d of %d", key, dt->nbkeys);
return retval;
}
//gtk-chart/list-report
gdouble da_datarow_get_cell_sum(DataRow *dr, guint32 index)
{
if( index < dr->nbcols )
{
return (dr->colexp[index] + dr->colinc[index]);
}
g_warning("invalid datarow column");
return 0;
}
//rep-stat
guint report_items_get_key(gint grpby, guint jfrom, Transaction *txn)
{
Account *acc;
guint key = 0;
switch(grpby)
{
case REPORT_GRPBY_NONE:
key = 0;
break;
case REPORT_GRPBY_CATEGORY:
key = category_report_id(txn->kcat, FALSE);
break;
//case REPORT_GRPBY_SUBCATEGORY:
// pos = txn->kcat;
// break;
case REPORT_GRPBY_PAYEE:
key = txn->kpay;
break;
case REPORT_GRPBY_ACCOUNT:
key = txn->kacc;
break;
case REPORT_GRPBY_ACCGROUP:
acc = da_acc_get(txn->kacc);
if( acc != NULL )
key = acc->kgrp;
break;
//TODO! miss TAG
case REPORT_GRPBY_MONTH:
key = DateInMonth(jfrom, txn->date);
break;
case REPORT_GRPBY_YEAR:
key = DateInYear(jfrom, txn->date);
break;
}
return key;
}
//rep- balance/budget/time
gint report_interval_get_pos(gint intvl, guint jfrom, Transaction *txn)
{
gint pos = 0;
switch(intvl)
{
case REPORT_INTVL_DAY:
pos = txn->date - jfrom;
break;
case REPORT_INTVL_WEEK:
//#1915643 week iso 8601
//pos = (txn->date - jfrom)/7;
pos = DateInWeek(jfrom, txn->date);
break;
//#2000290
case REPORT_INTVL_FORTNIGHT:
pos = DateInFortNight(jfrom, txn->date);
break;
case REPORT_INTVL_MONTH:
pos = DateInMonth(jfrom, txn->date);
break;
case REPORT_INTVL_QUARTER:
pos = DateInQuarter(jfrom, txn->date);
break;
case REPORT_INTVL_HALFYEAR:
pos = DateInHalfYear(jfrom, txn->date);
break;
case REPORT_INTVL_YEAR:
pos = DateInYear(jfrom, txn->date);
break;
default: //REPORT_INTVL_NONE
pos = 0;
break;
}
return pos;
}
//rep-stats
//rep- balance/time
gint report_interval_count(gint intvl, guint32 jfrom, guint32 jto)
{
GDate *date1, *date2;
gint nbintvl = 0;
date1 = g_date_new_julian(jfrom);
date2 = g_date_new_julian(jto);
switch(intvl)
{
case REPORT_INTVL_DAY:
nbintvl = (jto - jfrom);
break;
case REPORT_INTVL_WEEK:
//#2000292 weeknum iso 8601 as well
_hb_date_clamp_iso8601(date1);
nbintvl = (g_date_days_between(date1, date2) / 7);
break;
//#2000290
case REPORT_INTVL_FORTNIGHT:
_hb_date_clamp_iso8601(date1);
nbintvl = (g_date_days_between(date1, date2) / 14);
break;
case REPORT_INTVL_MONTH:
nbintvl = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1);
break;
case REPORT_INTVL_QUARTER:
//#1758532 slide quarter start
_hb_date_clamp_quarter_start(date1);
nbintvl = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/3;
break;
case REPORT_INTVL_HALFYEAR:
//#2034618 slide halfyear start
_hb_date_clamp_halfyear_start(date1);
nbintvl = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/6;
break;
case REPORT_INTVL_YEAR:
nbintvl = g_date_get_year(date2) - g_date_get_year(date1);
break;
}
g_date_free(date2);
g_date_free(date1);
//5.9 check
if( nbintvl < 0 )
{
g_warning("report: intvl<0");
#if MYDEBUG
hb_print_date(jfrom, NULL);
hb_print_date(jto, NULL);
#endif
nbintvl = 0;
}
return 1 + nbintvl;
}
//used in list-report / rep- balance/budget/time
guint32 report_interval_snprint_name(gchar *s, gint slen, gint intvl, guint32 jfrom, gint idx)
{
GDate *date;
guint32 jdate = _datatable_interval_get_jdate(intvl, jfrom, idx);
date = g_date_new_julian(jdate);
switch(intvl)
{
case REPORT_INTVL_DAY:
g_date_strftime (s, slen, PREFS->date_format, date);
break;
case REPORT_INTVL_WEEK:
//TRANSLATORS: printf string for year of week W, ex. 2019-W52 for week 52 of 2019
g_snprintf(s, slen, _("%d-w%02d"), g_date_get_year(date), g_date_get_iso8601_week_of_year(date));
break;
//#2000290
case REPORT_INTVL_FORTNIGHT:
g_date_strftime (s, slen, PREFS->date_format, date);
break;
case REPORT_INTVL_MONTH:
g_snprintf(s, slen, "%d-%s", g_date_get_year(date), _(CYA_ABMONTHS[g_date_get_month(date)]));
break;
case REPORT_INTVL_QUARTER:
//todo: will be innacurrate here if fiscal year start not 1/jan
//TRANSLATORS: printf string for year of quarter Q, ex. 2019-Q4 for quarter 4 of 2019
g_snprintf(s, slen, _("%d-q%d"), g_date_get_year(date), ((g_date_get_month(date)-1)/3)+1);
break;
case REPORT_INTVL_HALFYEAR:
//#2007712
//TRANSLATORS: printf string for half-year H, ex. 2019-H1 for 1st half-year of 2019
g_snprintf(s, slen, _("%d-h%d"), g_date_get_year(date), g_date_get_month(date) < 7 ? 1 : 2);
break;
case REPORT_INTVL_YEAR:
g_snprintf(s, slen, "%d", g_date_get_year(date));
break;
default:
*s ='\0';
break;
}
g_date_free(date);
return jdate;
}
//TODO: maybe migrate this to filter as well
//#1562372 in case of a split we need to take amount for filter categories only
//used in rep-time
gdouble report_txn_amount_get(Filter *flt, Transaction *txn)
{
gdouble amount;
amount = txn->amount;
if( flt->option[FLT_GRP_CATEGORY] > 0 ) //not inactive
{
if( txn->flags & OF_SPLIT )
{
guint i, nbsplit = da_splits_length(txn->splits);
Split *split;
gboolean status;
gint sinsert;
amount = 0.0;
for(i=0;isplits, i);
status = da_flt_status_cat_get(flt, split->kcat);
sinsert = ( status == TRUE ) ? 1 : 0;
if(flt->option[FLT_GRP_CATEGORY] == 2) sinsert ^= 1;
DB2( g_print(" split insert=%d, kcat=%d\n", sinsert, split->kcat) );
if( (flt->option[FLT_GRP_CATEGORY] == 0) || sinsert)
{
amount += split->amount;
}
}
}
}
return amount;
}
homebank-5.9.7/src/ui-tag.c 0000664 0001750 0001750 00000117606 15005624021 014743 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty ofdeftransaction_amountchanged
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "ui-dialogs.h"
#include "ui-widgets.h"
#include "ui-tag.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
//TODO: ui_tag_combobox still used in rep_time
guint32
ui_tag_combobox_get_key(GtkComboBox *combobox)
{
return hbtk_combo_box_get_active_id(combobox);
}
void
ui_tag_combobox_populate_except(GtkComboBoxText *combobox, guint except_key)
{
GList *ltag, *list;
//populate template
//hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), 0, "----");
//gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
ltag = list = tag_glist_sorted(HB_GLIST_SORT_NAME);
while (list != NULL)
{
Tag *item = list->data;
if( item->key != except_key )
{
DB( g_print(" populate: %d\n", item->key) );
hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), item->key, item->name);
}
list = g_list_next(list);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
g_list_free(ltag);
}
void
ui_tag_combobox_populate(GtkComboBoxText *combobox)
{
ui_tag_combobox_populate_except(combobox, -1);
}
GtkWidget *
ui_tag_combobox_new(GtkWidget *label)
{
GtkWidget *combobox;
combobox = hbtk_combo_box_new(label);
return combobox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
ui_tag_popover_cb_toggled (GtkToggleButton *togglebutton, gpointer user_data)
{
GtkWidget *entry = user_data;
GtkAllocation allocation;
GtkPopover *popover;
DB( g_print ("[tag popover] open\n") );
if(GTK_IS_ENTRY(entry))
{
gtk_widget_get_allocation (entry, &allocation);
popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(togglebutton));
if(GTK_IS_POPOVER(popover))
{
gtk_widget_set_size_request (GTK_WIDGET(popover), allocation.width + (2*SPACING_POPOVER), -1);
DB( g_print("should set width to %d\n", allocation.width + (2*SPACING_POPOVER)) );
}
}
}
static void ui_tag_popover_cb_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
GtkTreeSelection *treeselection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkEntry *entry = user_data;
if( GTK_IS_ENTRY(entry) )
{
treeselection = gtk_tree_view_get_selection(tree_view);
if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
{
Tag *item;
gtk_tree_model_get(model, &iter, LST_DEFTAG_DATAS, &item, -1);
hbtk_entry_tag_name_append(GTK_ENTRY(user_data), item->name);
}
}
}
GtkWidget *
ui_tag_popover_list(GtkWidget *entry)
{
GtkWidget *box, *menubutton, *image, *scrollwin, *treeview;
menubutton = gtk_menu_button_new ();
image = hbtk_image_new_from_icon_name_16 ("pan-down-symbolic");
gtk_button_set_image(GTK_BUTTON(menubutton), image);
gtk_menu_button_set_direction (GTK_MENU_BUTTON(menubutton), GTK_ARROW_LEFT );
//gtk_widget_set_halign (menubutton, GTK_ALIGN_END);
gtk_widget_show_all(menubutton);
//GtkWidget *template = ui_popover_tpl_create(data);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
treeview = ui_tag_listview_new(FALSE, TRUE);
//data.LV_tag = treeview;
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollwin), treeview);
gtk_widget_show_all(box);
gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(treeview), TRUE);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(treeview), TRUE);
GtkWidget *popover = create_popover (menubutton, box, GTK_POS_LEFT);
//gtk_widget_set_size_request (popover, HB_MINWIDTH_LIST, HB_MINHEIGHT_LIST);
gtk_widget_set_vexpand(popover, TRUE);
gtk_menu_button_set_popover(GTK_MENU_BUTTON(menubutton), popover);
ui_tag_listview_populate(treeview, 0);
g_signal_connect (menubutton, "toggled", G_CALLBACK (ui_tag_popover_cb_toggled), entry);
g_signal_connect (treeview, "row-activated", G_CALLBACK (ui_tag_popover_cb_row_activated), entry);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_popover_popdown), popover);
#else
g_signal_connect_swapped(treeview, "row-activated", G_CALLBACK(gtk_widget_hide), popover);
#endif
return menubutton;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
guint ui_tag_listview_toggle_to_filter(GtkTreeView *treeview, Filter *filter)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gboolean toggled;
guint change = 0;
DB( g_print("(ui_tag_listview) toggle_to_filter\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
//selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_tag));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Tag *tagitem;
gtk_tree_model_get (model, &iter,
LST_DEFTAG_TOGGLE, &toggled,
LST_DEFTAG_DATAS, &tagitem,
-1);
DB( g_print(" get tag k:%3d = %d (%s)\n", tagitem->key, toggled, tagitem->name) );
change += da_flt_status_tag_set(filter, tagitem->key, toggled);
//tagitem->flt_select = toggled;
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
return change;
}
static void
ui_tag_listview_toggled_cb (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_DEFTAG_TOGGLE, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFTAG_TOGGLE, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
}
void
ui_tag_listview_quick_select(GtkTreeView *treeview, const gchar *uri)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
gboolean toggle;
gint qselect = hb_clicklabel_to_int(uri);
DB( g_print("(ui_acc_listview) quick select\n") );
DB( g_print(" comboboxlink '%s' %d\n", uri, qselect) );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
switch(qselect)
{
case HB_LIST_QUICK_SELECT_ALL:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFTAG_TOGGLE, TRUE, -1);
break;
case HB_LIST_QUICK_SELECT_NONE:
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFTAG_TOGGLE, FALSE, -1);
break;
case HB_LIST_QUICK_SELECT_INVERT:
gtk_tree_model_get (model, &iter, LST_DEFTAG_TOGGLE, &toggle, -1);
toggle ^= 1;
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_DEFTAG_TOGGLE, toggle, -1);
break;
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
}
static gint
ui_tag_listview_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
Tag *entry1, *entry2;
gint retval = 0;
gtk_tree_model_get(model, a, LST_DEFTAG_DATAS, &entry1, -1);
gtk_tree_model_get(model, b, LST_DEFTAG_DATAS, &entry2, -1);
switch (sortcol)
{
case LST_DEFTAG_SORT_NAME:
retval = hb_string_utf8_compare(entry1->name, entry2->name);
break;
case LST_DEFTAG_SORT_USETXN:
retval = entry1->nb_use_all - entry2->nb_use_all;
break;
case LST_DEFTAG_SORT_USECFG:
retval = (entry1->nb_use_all - entry1->nb_use_txn) - (entry2->nb_use_all - entry2->nb_use_txn);
break;
default:
g_return_val_if_reached(0);
}
return retval;
}
static void
ui_tag_listview_count_txn_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Tag *entry;
gchar buffer[256];
gtk_tree_model_get(model, iter, LST_DEFTAG_DATAS, &entry, -1);
if(entry->nb_use_txn > 0)
{
g_snprintf(buffer, 256-1, "%d", entry->nb_use_txn);
g_object_set(renderer, "text", buffer, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void
ui_tag_listview_count_cfg_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Tag *entry;
gchar buffer[256];
guint use;
gtk_tree_model_get(model, iter, LST_DEFTAG_DATAS, &entry, -1);
use = entry->nb_use_all - entry->nb_use_txn;
if(use > 0)
{
g_snprintf(buffer, 256-1, "%d", use);
g_object_set(renderer, "text", buffer, NULL);
}
else
g_object_set(renderer, "text", "", NULL);
}
static void
ui_tag_listview_name_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
Tag *entry;
gchar *name;
gtk_tree_model_get(model, iter, LST_DEFTAG_DATAS, &entry, -1);
if(entry->key == 0)
name = _("(no tag)");
else
name = entry->name;
g_object_set(renderer, "text", name, NULL);
}
#if MYDEBUG == 1
static void
ui_tag_listview_cell_data_function_debugkey (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Tag *item;
gchar *string;
gtk_tree_model_get(model, iter, LST_DEFTAG_DATAS, &item, -1);
string = g_strdup_printf ("[%d]", item->key );
g_object_set(renderer, "text", string, NULL);
g_free(string);
}
#endif
/* = = = = = = = = = = = = = = = = */
/**
* tag_list_add:
*
* Add a single element (useful for dynamics add)
*
* Return value: --
*
*/
void
ui_tag_listview_add(GtkTreeView *treeview, Tag *item)
{
if( item->name != NULL )
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(treeview);
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFTAG_TOGGLE, FALSE,
LST_DEFTAG_DATAS, item,
-1);
gtk_tree_selection_select_iter (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter);
}
}
guint32
ui_tag_listview_get_selected_key(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Tag *item;
gtk_tree_model_get(model, &iter, LST_DEFTAG_DATAS, &item, -1);
if( item!= NULL )
return item->key;
}
return 0;
}
void
ui_tag_listview_remove_selected(GtkTreeView *treeview)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
selection = gtk_tree_view_get_selection(treeview);
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
}
void ui_tag_listview_populate(GtkWidget *view, gint insert_type)
{
GtkTreeModel *model;
GtkTreeIter iter;
GList *ltag, *list;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
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(view), NULL); /* Detach model from view */
/* populate */
//g_hash_table_foreach(GLOBALS->h_tag, (GHFunc)ui_tag_listview_populate_ghfunc, model);
ltag = list = g_hash_table_get_values(GLOBALS->h_tag);
while (list != NULL)
{
Tag *item = list->data;
DB( g_print(" populate: %d\n", item->key) );
//gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_prepend (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DEFTAG_TOGGLE , FALSE,
LST_DEFTAG_DATAS, item,
-1);
list = g_list_next(list);
}
g_list_free(ltag);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
}
GtkWidget *
ui_tag_listview_new(gboolean withtoggle, gboolean withcount)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// create list store
store = gtk_list_store_new(NUM_LST_DEFTAG,
G_TYPE_BOOLEAN,
G_TYPE_POINTER
);
// treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
#if MYDEBUG == 1
column = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_tag_listview_cell_data_function_debugkey, NULL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
#endif
// column 1: toggle
if( withtoggle == TRUE )
{
renderer = gtk_cell_renderer_toggle_new ();
column = gtk_tree_view_column_new_with_attributes (_("Visible"),
renderer,
"active", LST_DEFTAG_TOGGLE,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE);
g_signal_connect (renderer, "toggled",
G_CALLBACK (ui_tag_listview_toggled_cb), store);
g_object_set_data(G_OBJECT(treeview), "togrdr_data", renderer);
}
// column: usage
if( withcount == TRUE )
{
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
//TRANSLATORS: 'txn' is abbrevation for transaction
gtk_tree_view_column_set_title(column, _("# txn"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_tag_listview_count_txn_cell_data_function, GINT_TO_POINTER(LST_DEFTAG_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFTAG_SORT_USETXN);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by default hide this column
gtk_tree_view_column_set_visible(column, FALSE);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 0.5, NULL);
column = gtk_tree_view_column_new();
//TRANSLATORS: 'txn' is abbrevation for configuration
gtk_tree_view_column_set_title(column, _("# cfg"));
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_tag_listview_count_cfg_cell_data_function, GINT_TO_POINTER(LST_DEFTAG_DATAS), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFTAG_SORT_USECFG);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//by default hide this column
gtk_tree_view_column_set_visible(column, FALSE);
}
// column 2: name
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Tag"));
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);
if( withtoggle == FALSE )
{
g_object_set(renderer,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
}
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, ui_tag_listview_name_cell_data_function, GINT_TO_POINTER(LST_DEFTAG_DATAS), NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DEFTAG_SORT_NAME);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
// treeviewattribute
//gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), FALSE);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
// treeview attribute
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(treeview), withcount);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFTAG_SORT_NAME, ui_tag_listview_compare_func, GINT_TO_POINTER(LST_DEFTAG_SORT_NAME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFTAG_SORT_USETXN, ui_tag_listview_compare_func, GINT_TO_POINTER(LST_DEFTAG_SORT_USETXN), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DEFTAG_SORT_USECFG, ui_tag_listview_compare_func, GINT_TO_POINTER(LST_DEFTAG_SORT_USECFG), NULL);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), LST_DEFTAG_SORT_NAME, GTK_SORT_ASCENDING);
gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
return treeview;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void ui_tag_manage_filter_text_handler (GtkEntry *entry,
const gchar *text,
gint length,
gint *position,
gpointer data)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint i, count=0;
gchar *result = g_new0 (gchar, length+1);
for (i=0; i < length; i++)
{
if (text[i]==' ')
continue;
result[count++] = text[i];
}
if (count > 0) {
g_signal_handlers_block_by_func (G_OBJECT (editable),
G_CALLBACK (ui_tag_manage_filter_text_handler),
data);
gtk_editable_insert_text (editable, result, count, position);
g_signal_handlers_unblock_by_func (G_OBJECT (editable),
G_CALLBACK (ui_tag_manage_filter_text_handler),
data);
}
g_signal_stop_emission_by_name (G_OBJECT (editable), "insert_text");
g_free (result);
}
static void
ui_tag_manage_dialog_delete_unused(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data = user_data;
gboolean result;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_tag_manage_dialog) delete unused - data %p\n", data) );
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
_("Delete unused tag"),
_("Are you sure you want to\npermanently delete unused tag?"),
_("_Delete"),
TRUE
);
if( result == GTK_RESPONSE_OK )
{
GtkTreeModel *model;
//#1996275 fill usage before delete !
if( data->usagefilled == FALSE )
{
tags_fill_usage();
data->usagefilled = TRUE;
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_tag));
gtk_list_store_clear (GTK_LIST_STORE(model));
//#1917075
data->change += tags_delete_unused();
//ui_tag_manage_dialog_refilter(data);
ui_tag_listview_populate(data->LV_tag, 0);
}
}
static void
ui_tag_manage_dialog_load_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data = user_data;
gchar *filename = NULL;
gchar *error;
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_tag_manage_dialog) load csv - data %p\n", data) );
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_OPEN, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
if( !tag_load_csv(filename, &error) )
{
ui_dialog_msg_infoerror(GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("File format error"),
_("The CSV file must contains the exact numbers of column,\nseparated by a semi-colon, please see the help for more details.")
);
}
g_free( filename );
ui_tag_listview_populate(data->LV_tag, 0);
}
}
static void
ui_tag_manage_dialog_save_csv(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data = user_data;
gchar *filename = NULL;
DB( g_print("(ui_tag_manage_dialog) save csv\n") );
//data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->dialog), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
tag_save_csv(filename);
g_free( filename );
}
}
static void
ui_tag_manage_dialog_cb_show_usage (GtkToggleButton *button, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
gboolean showusage;
GtkTreeViewColumn *column;
DB( g_print("(ui_tag_manage_dialog) show usage\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW)), "inst_data");
if( data->usagefilled == FALSE )
{
tags_fill_usage();
data->usagefilled = TRUE;
}
showusage = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->BT_showusage));
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_tag), LST_DEFTAG_SORT_USETXN);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
column = hbtk_treeview_get_column_by_id(GTK_TREE_VIEW(data->LV_tag), LST_DEFTAG_SORT_USECFG);
if(column != NULL)
{
gtk_tree_view_column_set_visible(column, showusage);
}
}
/**
* ui_tag_manage_dialog_add:
*
*/
static void
ui_tag_manage_dialog_add(GtkWidget *widget, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
gboolean isadded;
Tag *item;
gchar *name;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(defayee) add (data=%p)\n", data) );
name = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_name));
item = da_tag_malloc ();
item->name = g_strdup(name);
g_strstrip(item->name);
isadded = FALSE;
if( strlen(item->name) > 0 )
{
isadded = da_tag_append(item);
if( isadded == TRUE )
{
ui_tag_listview_add(GTK_TREE_VIEW(data->LV_tag), item);
data->change++;
}
}
//#2051349 warn user and free lack
if( isadded == FALSE )
{
DB( g_print(" existing item\n") );
da_tag_free (item);
ui_dialog_msg_infoerror (GTK_WINDOW(data->dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Duplicate tag name. Try another name.") );
}
gtk_entry_set_text(GTK_ENTRY(data->ST_name), "");
}
static void ui_tag_manage_dialog_edit_entry_cb(GtkEditable *editable, gpointer user_data)
{
GtkDialog *window = user_data;
const gchar *buffer;
buffer = gtk_entry_get_text(GTK_ENTRY(editable));
gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT, strlen(buffer) > 0 ? TRUE : FALSE);
}
static void ui_tag_manage_dialog_edit(GtkWidget *dowidget, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
GtkWidget *dialog, *content_area, *content_grid, *group_grid;
GtkWidget *label, *widget;
GtkWidget *ST_name;
gint crow, row;
guint32 key;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(dowidget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(defayee) modify %p\n", data) );
key = ui_tag_listview_get_selected_key(GTK_TREE_VIEW(data->LV_tag));
if( key > 0 )
{
Tag *item;
item = da_tag_get( key );
dialog = gtk_dialog_new_with_buttons (_("Edit Tag"),
GTK_WINDOW (data->dialog),
0,
_("_Cancel"),
GTK_RESPONSE_REJECT,
_("_OK"),
GTK_RESPONSE_ACCEPT,
NULL);
content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
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);
hb_widget_set_margin(GTK_WIDGET(content_grid), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (content_area), content_grid);
crow = 0;
// group :: General
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(_("General"));
//gtk_grid_attach (GTK_GRID (group_grid), label, 0, 0, 3, 1);
row = 1;
label = make_label_widget(_("_Name:"));
gtk_grid_attach (GTK_GRID (group_grid), label, 1, row, 1, 1);
widget = gtk_entry_new();
gtk_entry_set_width_chars(GTK_ENTRY(widget), 24);
ST_name = widget;
gtk_widget_set_hexpand(widget, TRUE);
gtk_grid_attach (GTK_GRID (group_grid), widget, 2, row, 1, 1);
//setup
//#1992284 manage tag edit input is empty
gtk_entry_set_text(GTK_ENTRY(ST_name), item->name);
gtk_widget_grab_focus (ST_name);
gtk_entry_set_activates_default (GTK_ENTRY(ST_name), TRUE);
gtk_dialog_set_default_response(GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT);
//#2018414 disable input space...
g_signal_connect(G_OBJECT(ST_name), "insert-text", G_CALLBACK(ui_tag_manage_filter_text_handler), NULL);
g_signal_connect (G_OBJECT (ST_name), "changed", G_CALLBACK (ui_tag_manage_dialog_edit_entry_cb), dialog);
gtk_widget_show_all(content_grid);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
const gchar *name;
// 1: manage renaming
name = gtk_entry_get_text(GTK_ENTRY(ST_name));
// ignore if item is empty
if (name && *name)
{
if( tag_rename(item, name) )
{
//to redraw the active entry
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_tag));
data->change++;
}
else
{
ui_dialog_msg_infoerror(GTK_WINDOW(dialog), GTK_MESSAGE_ERROR,
_("Error"),
_("Cannot rename this Tag,\n"
"from '%s' to '%s',\n"
"this name already exists."),
item->name,
name
);
}
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
static void ui_tag_manage_dialog_merge(GtkWidget *widget, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox;
GtkWidget *getwidget, *togglebutton;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_tag_manage_dialog) merge %p\n", data) );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_tag));
//if true there is a selected node
if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
Tag *srctag;
gchar *title;
gchar *secondtext;
gtk_tree_model_get(model, &iter, LST_DEFTAG_DATAS, &srctag, -1);
title = g_strdup_printf (
_("Merge tag '%s'"), srctag->name);
dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_NONE,
title,
NULL
);
gtk_dialog_add_buttons (GTK_DIALOG(dialog),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("Merge"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
content = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
secondtext = _("Transactions assigned to this tag,\n"
"will be moved to the tag selected below.");
g_object_set(GTK_MESSAGE_DIALOG (dialog), "secondary-text", secondtext, NULL);
g_free(title);
getwidget = ui_tag_combobox_new(NULL);
gtk_box_prepend (GTK_BOX (mainvbox), getwidget);
secondtext = g_strdup_printf (
_("_Delete the tag '%s'"), srctag->name);
togglebutton = gtk_check_button_new_with_mnemonic(secondtext);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), TRUE);
g_free(secondtext);
gtk_box_prepend (GTK_BOX (mainvbox), togglebutton);
//setup
ui_tag_combobox_populate_except(GTK_COMBO_BOX_TEXT(getwidget), srctag->key);
gtk_widget_grab_focus (getwidget);
gtk_widget_show_all(mainvbox);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_OK)
{
GtkTreeModel *model;
Tag *newtag;
guint dsttagkey;
dsttagkey = ui_tag_combobox_get_key(GTK_COMBO_BOX(getwidget));
//do nothing if src = dst...
if( srctag->key != dsttagkey )
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_tag));
gtk_list_store_clear (GTK_LIST_STORE(model));
DB( g_print(" -> move cat to %d (subcat=%d)\n", dstcatkey, dosubcat) );
tag_move(srctag->key, dsttagkey);
newtag = da_tag_get(dsttagkey);
//#1771720: update count
newtag->nb_use_all += srctag->nb_use_all;
newtag->nb_use_txn += srctag->nb_use_txn;
srctag->nb_use_all = 0;
srctag->nb_use_txn = 0;
// add the new tag to listview
if(newtag)
ui_tag_listview_add(GTK_TREE_VIEW(data->LV_tag), newtag);
// delete the old tag
if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)) )
{
DB( g_print(" -> delete %d '%s'\n", srctag->key, srctag->name ) );
ui_tag_listview_remove_selected(GTK_TREE_VIEW(data->LV_tag));
da_tag_delete(srctag->key);
}
data->change++;
ui_tag_listview_populate(data->LV_tag, 0);
}
}
// cleanup and destroy
gtk_window_destroy (GTK_WINDOW(dialog));
}
}
/*
** delete the selected tag to our treeview and temp GList
*/
static void ui_tag_manage_dialog_delete(GtkWidget *widget, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
Tag *item;
guint32 key;
gint result;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("(ui_tag_manage_dialog) delete (data=%p)\n", data) );
key = ui_tag_listview_get_selected_key(GTK_TREE_VIEW(data->LV_tag));
if( key > 0 )
{
gchar *title;
gchar *secondtext = NULL;
item = da_tag_get(key);
title = g_strdup_printf (
_("Are you sure you want to permanently delete '%s'?"), item->name);
if( item->nb_use_all > 0 )
{
secondtext = _("This tag is used.\n"
"That tag will be deleted from any transaction using it.");
}
result = ui_dialog_msg_confirm_alert(
GTK_WINDOW(data->dialog),
title,
secondtext,
_("_Delete"),
TRUE
);
g_free(title);
if( result == GTK_RESPONSE_OK )
{
//#1915729 no need to move to 0 (crash), just delete is safe
//tag_move(key, 0);
ui_tag_listview_remove_selected(GTK_TREE_VIEW(data->LV_tag));
da_tag_delete(key);
data->change++;
}
}
}
static void ui_tag_manage_dialog_update(GtkWidget *treeview, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
gboolean sensitive;
guint32 key;
DB( g_print("\n(ui_tag_manage_dialog) cursor changed\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
key = ui_tag_listview_get_selected_key(GTK_TREE_VIEW(data->LV_tag));
sensitive = (key > 0) ? TRUE : FALSE;
gtk_widget_set_sensitive(data->BT_edit, sensitive);
gtk_widget_set_sensitive(data->BT_merge, sensitive);
gtk_widget_set_sensitive(data->BT_delete, sensitive);
}
/*
**
*/
static void ui_tag_manage_dialog_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
ui_tag_manage_dialog_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
}
static void ui_tag_manage_dialog_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
DB( g_print("ui_tag_manage_dialog_onRowActivated()\n") );
model = gtk_tree_view_get_model(treeview);
gtk_tree_model_get_iter_first(model, &iter);
if(gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), &iter) == FALSE)
{
ui_tag_manage_dialog_edit(GTK_WIDGET(treeview), NULL);
}
}
static void ui_tag_manage_setup(struct ui_tag_manage_dialog_data *data)
{
DB( g_print("\n[ui-tag] setup\n") );
DB( g_print(" init data\n") );
data->change = 0;
DB( g_print(" populate\n") );
//tag_fill_usage();
ui_tag_listview_populate(data->LV_tag, 0);
//DB( g_print(" set widgets default\n") );
DB( g_print(" connect widgets signals\n") );
g_signal_connect (G_OBJECT (data->BT_showusage) , "toggled", G_CALLBACK (ui_tag_manage_dialog_cb_show_usage), NULL);
g_object_bind_property (data->BT_add, "active", data->RE_addreveal, "reveal-child", G_BINDING_BIDIRECTIONAL);
g_signal_connect (G_OBJECT (data->ST_name), "activate", G_CALLBACK (ui_tag_manage_dialog_add), NULL);
g_signal_connect(G_OBJECT(data->ST_name), "insert-text", G_CALLBACK(ui_tag_manage_filter_text_handler), NULL);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_tag)), "changed", G_CALLBACK (ui_tag_manage_dialog_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(data->LV_tag), "row-activated", G_CALLBACK (ui_tag_manage_dialog_onRowActivated), NULL);
g_signal_connect (G_OBJECT (data->BT_edit), "clicked", G_CALLBACK (ui_tag_manage_dialog_edit), NULL);
g_signal_connect (G_OBJECT (data->BT_merge), "clicked", G_CALLBACK (ui_tag_manage_dialog_merge), NULL);
g_signal_connect (G_OBJECT (data->BT_delete), "clicked", G_CALLBACK (ui_tag_manage_dialog_delete), NULL);
}
static gboolean
ui_tag_manage_mapped (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
struct ui_tag_manage_dialog_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( data->mapped_done == TRUE )
return FALSE;
DB( g_print("\n(ui_tag_manage_mapped)\n") );
ui_tag_manage_setup(data);
ui_tag_manage_dialog_update(data->LV_tag, NULL);
data->mapped_done = TRUE;
return FALSE;
}
static const GActionEntry win_actions[] = {
{ "imp" , ui_tag_manage_dialog_load_csv, NULL, NULL, NULL, {0,0,0} },
{ "exp" , ui_tag_manage_dialog_save_csv, NULL, NULL, NULL, {0,0,0} },
{ "del" , ui_tag_manage_dialog_delete_unused, NULL, NULL, NULL, {0,0,0} },
// { "actioname" , not_implemented, NULL, NULL, NULL, {0,0,0} },
};
GtkWidget *ui_tag_manage_dialog (void)
{
struct ui_tag_manage_dialog_data *data;
GtkWidget *dialog, *content, *mainvbox, *box, *bbox, *tbar, *treeview, *scrollwin, *table, *addreveal;
GtkWidget *widget, *image;
gint w, h, dw, dh, row;
data = g_malloc0(sizeof(struct ui_tag_manage_dialog_data));
if(!data) return NULL;
dialog = gtk_dialog_new_with_buttons (_("Manage Tags"),
GTK_WINDOW(GLOBALS->mainwindow),
0,
_("_Close"), GTK_RESPONSE_ACCEPT,
NULL);
/*dialog = g_object_new (GTK_TYPE_DIALOG, "use-header-bar", TRUE, NULL);
gtk_window_set_title (GTK_WINDOW (dialog), _("Manage Tags"));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW(GLOBALS->mainwindow));
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
*/
//gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
data->dialog = dialog;
//set a nice dialog size
gtk_window_get_size(GTK_WINDOW(GLOBALS->mainwindow), &w, &h);
dh = (h*1.33/PHI);
//ratio 2:3
dw = (dh * 2) / 3;
DB( g_print(" main w=%d h=%d => diag w=%d h=%d\n", w, h, dw, dh) );
gtk_window_set_default_size (GTK_WINDOW(dialog), dw, dh);
//store our dialog private data
g_object_set_data(G_OBJECT(dialog), "inst_data", (gpointer)data);
DB( g_print("(ui_tag_manage_dialog) dialog=%p, inst_data=%p\n", dialog, data) );
//dialog contents
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_SMALL);
hbtk_box_prepend (GTK_BOX (content), mainvbox);
hb_widget_set_margin(GTK_WIDGET(mainvbox), SPACING_LARGE);
//our table
table = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
hbtk_box_prepend (GTK_BOX (mainvbox), table);
row = 0;
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_grid_attach (GTK_GRID (table), bbox, 0, row, 2, 1);
//test headerbar
//content = gtk_dialog_get_header_bar(GTK_DIALOG (dialog));
widget = make_image_toggle_button(ICONNAME_HB_BUTTON_USAGE, _("Show Usage") );
data->BT_showusage = widget;
gtk_box_prepend(GTK_BOX (bbox), widget);
//menubutton
widget = gtk_menu_button_new();
image = hbtk_image_new_from_icon_name_16 (ICONNAME_HB_BUTTON_MENU);
g_object_set (widget, "image", image, NULL);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
gtk_box_append(GTK_BOX (bbox), widget);
GMenu *menu = g_menu_new ();
GMenu *section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Import CSV..."), "win.imp");
g_menu_append (section, _("E_xport CSV..."), "win.exp");
g_object_unref (section);
section = g_menu_new ();
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_menu_append (section, _("_Delete unused..."), "win.del");
g_object_unref (section);
GActionGroup *group = (GActionGroup*)g_simple_action_group_new ();
data->actions = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), win_actions, G_N_ELEMENTS (win_actions), data);
gtk_widget_insert_action_group (widget, "win", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
// list
row++;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_grid_attach (GTK_GRID (table), box, 0, row, 2, 1);
scrollwin = make_scrolled_window(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX(box), scrollwin);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrollwin), HB_MINHEIGHT_LIST);
gtk_widget_set_hexpand (scrollwin, TRUE);
gtk_widget_set_vexpand (scrollwin, TRUE);
treeview = ui_tag_listview_new(FALSE, TRUE);
data->LV_tag = treeview;
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollwin), treeview);
tbar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_MEDIUM);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_prepend (GTK_BOX (box), tbar);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
data->BT_add = widget = make_image_toggle_button(ICONNAME_LIST_ADD, _("Add"));
gtk_box_prepend(GTK_BOX(bbox), widget);
data->BT_delete = widget = make_image_button(ICONNAME_LIST_DELETE, _("Delete"));
gtk_box_prepend(GTK_BOX(bbox), widget);
bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_prepend (GTK_BOX (tbar), bbox);
data->BT_edit = widget = make_image_button(ICONNAME_LIST_EDIT, _("Edit"));
gtk_box_prepend(GTK_BOX(bbox), widget);
data->BT_merge = widget = make_image_button(ICONNAME_HB_LIST_MERGE, _("Move/Merge"));
gtk_box_prepend(GTK_BOX(bbox), widget);
row++;
addreveal = gtk_revealer_new ();
data->RE_addreveal = addreveal;
gtk_grid_attach (GTK_GRID (table), addreveal, 0, row, 2, 1);
data->ST_name = gtk_entry_new ();
gtk_entry_set_placeholder_text(GTK_ENTRY(data->ST_name), _("new tag") );
gtk_widget_set_hexpand (data->ST_name, TRUE);
gtk_revealer_set_child(GTK_REVEALER(addreveal), data->ST_name);
// connect dialog signals
g_signal_connect (dialog, "map-event", G_CALLBACK (ui_tag_manage_mapped), &dialog);
// show & run dialog
DB( g_print(" run dialog\n") );
gtk_widget_show_all (dialog);
// wait for the user
gtk_dialog_run (GTK_DIALOG (dialog));
// cleanup and destroy
GLOBALS->changes_count += data->change;
gtk_window_destroy (GTK_WINDOW(dialog));
g_free(data);
return NULL;
}
homebank-5.9.7/src/hb-currency.h 0000644 0001750 0001750 00000004610 14736461415 016007 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_CURRENCY_H__
#define __HB_CURRENCY_H__
#include "hb-types.h"
typedef struct _iso4217 Currency4217;
struct _currency
{
guint32 key;
gushort flags;
gchar *name;
gchar *iso_code;
gboolean sym_prefix;
gchar *symbol; /* max symbol is 3 digits in unicode but mostly is 1 digit */
gchar *decimal_char;
gchar *grouping_char;
gshort frac_digits;
gshort _pad1;
gdouble rate;
guint32 mdate;
/* unsaved datas */
gchar format[8]; /* hold decimal format: '%.xf' */
gchar monfmt[32]; /* hold monetary format: 'prefix %s suffix' */
};
// 0 is free
#define CF_CUSTOM (1<<1)
struct _iso4217
{
gchar *curr_iso_code;
guint curr_frac_digit;
gchar *curr_dec_char;
gchar *curr_grp_char;
gboolean curr_is_prefix;
gchar *curr_symbol;
gchar *name;
};
void da_cur_free(Currency *item);
Currency *da_cur_malloc(void);
void da_cur_destroy(void);
void da_cur_new(void);
guint da_cur_length(void);
gboolean da_cur_delete(guint32 key);
void da_cur_init_from4217(Currency *cur, Currency4217 *curfmt);
void da_cur_initformat(Currency *item);
gboolean da_cur_insert(Currency *item);
gboolean da_cur_append(Currency *item);
guint32 da_cur_get_max_key(void);
Currency *da_cur_get_by_name(gchar *name);
Currency *da_cur_get_by_iso_code(gchar *iso_code);
Currency *da_cur_get(guint32 key);
void currency_get_system_iso(void);
gboolean currency_is_euro(guint32 key);
gboolean currency_is_used(guint32 key);
Currency *currency_add_from_user(Currency4217 *curfmt);
gboolean currency_online_sync(GError **error, GString *node);
Currency4217 *iso4217format_get(gchar *code);
#endif
homebank-5.9.7/src/hb-preferences.c 0000644 0001750 0001750 00000151462 15120464176 016454 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hb-preferences.h"
#include "hb-pref-data.h"
#include "hb-filter.h"
#include "gtk-chart-colors.h"
#ifdef G_OS_WIN32
#include
#endif
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
#define DBKF(x);
//#define DBKF(x) (x);
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void homebank_pref_init_wingeometry(struct WinGeometry *wg, gint l, gint t, gint w, gint h)
{
wg->l = l;
wg->t = t;
wg->w = w;
wg->h = h;
wg->s = 0;
}
//vehicle_unit_100
//vehicle_unit_distbyvol
//=> used for column title
static void _homebank_pref_init_measurement_units(void)
{
// unit is kilometer
if(!PREFS->vehicle_unit_ismile)
{
PREFS->vehicle_unit_dist0 = "%d km";
PREFS->vehicle_unit_dist1 = "%.1f km";
PREFS->vehicle_unit_100 = "100 km";
}
// unit is miles
else
{
PREFS->vehicle_unit_dist0 = "%d mi.";
PREFS->vehicle_unit_dist1 = "%.1f mi.";
PREFS->vehicle_unit_100 = "100 mi.";
}
// unit is Liters
if(!PREFS->vehicle_unit_isgal)
{
//TRANSLATORS: format a liter number with l/L as abbreviation
PREFS->vehicle_unit_vol = _("%.2f l");
if(!PREFS->vehicle_unit_ismile)
//TRANSLATORS: kilometer per liter
PREFS->vehicle_unit_distbyvol = _("km/l");
else
//TRANSLATORS: miles per liter
PREFS->vehicle_unit_distbyvol = _("mi./l");
}
// unit is gallon
else
{
PREFS->vehicle_unit_vol = "%.2f gal.";
if(!PREFS->vehicle_unit_ismile)
PREFS->vehicle_unit_distbyvol = "km/gal.";
else
PREFS->vehicle_unit_distbyvol = "mi./gal.";
}
}
void homebank_pref_free(void)
{
DB( g_print("\n[preferences] free\n") );
g_free(PREFS->date_format);
g_free(PREFS->api_rate_url);
g_free(PREFS->api_rate_key);
g_free(PREFS->icontheme);
g_free(PREFS->color_exp);
g_free(PREFS->color_inc);
g_free(PREFS->color_warn);
g_free(PREFS->color_bg_future);
g_free(PREFS->path_hbfile);
g_free(PREFS->path_import);
g_free(PREFS->path_export);
//g_free(PREFS->path_navigator);
g_free(PREFS->language);
g_free(PREFS->pnl_list_tab);
g_free(PREFS->minor_cur.symbol);
g_free(PREFS->minor_cur.decimal_char);
g_free(PREFS->minor_cur.grouping_char);
memset(PREFS, 0, sizeof(struct Preferences));
}
gint
homebank_pref_list_column_get(gint *cols_id, gint uid, gint maxcol)
{
gint i;
for(i=0; i < maxcol ; i++ )
{
if( uid == ABS(cols_id[i]) )
return cols_id[i];
/* secure point */
if( i > 50)
break;
}
return uid;
}
void homebank_pref_setdefault_lst_det_columns(void)
{
gint i = 0;
PREFS->lst_det_columns[i++] = LST_DSPOPE_STATUS; //always displayed
PREFS->lst_det_columns[i++] = LST_DSPOPE_DATE; //always displayed
PREFS->lst_det_columns[i++] = -LST_DSPOPE_PAYNUMBER;
PREFS->lst_det_columns[i++] = LST_DSPOPE_PAYEE;
PREFS->lst_det_columns[i++] = LST_DSPOPE_CATEGORY;
PREFS->lst_det_columns[i++] = -LST_DSPOPE_TAGS;
PREFS->lst_det_columns[i++] = LST_DSPOPE_CLR;
PREFS->lst_det_columns[i++] = LST_DSPOPE_AMOUNT;
PREFS->lst_det_columns[i++] = -LST_DSPOPE_EXPENSE;
PREFS->lst_det_columns[i++] = -LST_DSPOPE_INCOME;
PREFS->lst_det_columns[i++] = -LST_DSPOPE_BALANCE;
PREFS->lst_det_columns[i++] = LST_DSPOPE_MEMO;
PREFS->lst_det_columns[i++] = LST_DSPOPE_ACCOUNT;
PREFS->lst_det_columns[i++] = LST_DSPOPE_MATCH;
PREFS->lst_det_columns[i++] = LST_DSPOPE_GRPFLAG;
for( i=0;ilst_det_col_width[i] = -1;
}
void homebank_pref_setdefault_lst_ope_columns(void)
{
gint i = 0;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_STATUS; //always displayed
PREFS->lst_ope_columns[i++] = LST_DSPOPE_DATE; //always displayed
PREFS->lst_ope_columns[i++] = LST_DSPOPE_PAYNUMBER;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_PAYEE;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_CATEGORY;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_TAGS;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_CLR;
PREFS->lst_ope_columns[i++] = -LST_DSPOPE_AMOUNT;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_EXPENSE;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_INCOME;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_BALANCE;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_MEMO;
PREFS->lst_ope_columns[i++] = -LST_DSPOPE_ACCOUNT;
PREFS->lst_ope_columns[i++] = -LST_DSPOPE_MATCH;
PREFS->lst_ope_columns[i++] = LST_DSPOPE_GRPFLAG;
PREFS->lst_ope_sort_id = LST_DSPOPE_DATE;
PREFS->lst_ope_sort_order = GTK_SORT_ASCENDING;
for( i=0;ilst_ope_col_width[i] = -1;
}
void homebank_pref_setdefault_lst_sch_columns(void)
{
gint i = 0;
//nextdate here
PREFS->lst_sch_columns[i++] = COL_SCH_UID_PAYNUMBER;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_PAYEE;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_CATEGORY;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_CLR;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_AMOUNT;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_EXPENSE;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_INCOME;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_MEMO;
PREFS->lst_sch_columns[i++] = COL_SCH_UID_ACCOUNT;
PREFS->pnl_upc_col_pay_show = 1;
PREFS->pnl_upc_col_pay_width = -1;
PREFS->pnl_upc_col_cat_show = 1;
PREFS->pnl_upc_col_cat_width = -1;
PREFS->pnl_upc_col_mem_show = 1;
PREFS->pnl_upc_col_mem_width = -1;
PREFS->pnl_upc_range = FLT_SCHEDULED_ALLDATE;
}
void homebank_pref_setdefault_win(void)
{
gint w = 1024, h = 600;
// windows position/size 1024x600 for netbook
// see https://gs.statcounter.com/screen-resolution-stats/desktop/worldwide
// and gnome HIG
homebank_pref_init_wingeometry(&PREFS->wal_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->acc_wg, 0, 0, w, h);
w = (w * 0.8);
h = (h * 0.8);
homebank_pref_init_wingeometry(&PREFS->sta_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->tme_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->ove_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->bud_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->cst_wg, 0, 0, w, h);
homebank_pref_init_wingeometry(&PREFS->txn_wg, 0, 0, -1, -1);
}
void homebank_pref_setdefault(void)
{
gint i;
DB( g_print("\n[preferences] pref init\n") );
homebank_pref_free();
PREFS->language = NULL;
PREFS->date_format = g_strdup(DEFAULT_FORMAT_DATE);
PREFS->path_hbfile = g_strdup_printf("%s", g_get_home_dir ());
PREFS->path_hbbak = g_strdup_printf("%s", g_get_home_dir ());
PREFS->path_import = g_strdup_printf("%s", g_get_home_dir ());
PREFS->path_export = g_strdup_printf("%s", g_get_home_dir ());
PREFS->showsplash = TRUE;
PREFS->showwelcome = TRUE;
PREFS->loadlast = TRUE;
PREFS->appendscheduled = FALSE;
PREFS->do_update_currency = FALSE;
PREFS->bak_is_automatic = TRUE;
PREFS->bak_max_num_copies = 5;
PREFS->heritdate = FALSE;
PREFS->txn_showconfirm = FALSE;
PREFS->txn_showtemplate = FALSE;
PREFS->hidereconciled = FALSE;
PREFS->showremind = TRUE;
//#1918334 no reason to show void by default
PREFS->showvoid = FALSE;
PREFS->includeremind = FALSE;
//#1980562
PREFS->safe_lock_recon = TRUE;
PREFS->safe_pend_recon = TRUE;
PREFS->safe_pend_past = TRUE;
PREFS->safe_pend_past_days = 90;
//#1673048
PREFS->txn_memoacp = TRUE;
PREFS->txn_memoacp_days = 365;
//5.8 paymode
i = 0;
PREFS->lst_paymode[i++] = PAYMODE_NONE;
PREFS->lst_paymode[i++] = PAYMODE_CCARD;
PREFS->lst_paymode[i++] = PAYMODE_CHECK;
PREFS->lst_paymode[i++] = PAYMODE_CASH;
PREFS->lst_paymode[i++] = PAYMODE_XFER;
PREFS->lst_paymode[i++] = PAYMODE_DCARD;
PREFS->lst_paymode[i++] = PAYMODE_REPEATPMT;
PREFS->lst_paymode[i++] = PAYMODE_EPAYMENT;
PREFS->lst_paymode[i++] = PAYMODE_DEPOSIT;
PREFS->lst_paymode[i++] = PAYMODE_FEE;
PREFS->lst_paymode[i++] = PAYMODE_DIRECTDEBIT;
PREFS->lst_paymode[i++] = PAYMODE_MOBPHONE;
//#2044601
PREFS->xfer_showdialog = TRUE;
//#1887212
PREFS->xfer_daygap = 2;
PREFS->xfer_syncstat = FALSE;
PREFS->toolbar_style = 4; //text beside icons
PREFS->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
PREFS->gtk_override = FALSE;
PREFS->gtk_fontsize = 10;
PREFS->icontheme = g_strdup("Default");
PREFS->custom_colors = TRUE;
PREFS->custom_bg_future = TRUE;
PREFS->color_use_palette = TRUE;
PREFS->color_exp = g_strdup(DEFAULT_EXP_COLOR);
PREFS->color_inc = g_strdup(DEFAULT_INC_COLOR);
PREFS->color_warn = g_strdup(DEFAULT_WARN_COLOR);
PREFS->color_bg_future = g_strdup(DEFAULT_FUTURE_BG_COLOR);
/* fiscal year */
PREFS->fisc_year_day = 1;
PREFS->fisc_year_month = 1;
homebank_pref_setdefault_win();
currency_get_system_iso();
PREFS->wal_toolbar = TRUE;
PREFS->wal_totchart = TRUE;
PREFS->wal_timchart = TRUE;
PREFS->wal_upcoming = TRUE;
PREFS->pnl_acc_col_acc_width = -1;
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_TYPE;
PREFS->hub_tot_view = 1;
PREFS->hub_tot_range = FLT_RANGE_THIS_MONTH;
PREFS->hub_tim_view = 1;
PREFS->hub_tim_range = FLT_RANGE_LAST_12MONTHS;
i = 0;
PREFS->lst_acc_columns[i++] = COL_DSPACC_STATUS;
PREFS->lst_acc_columns[i++] = COL_DSPACC_ACCOUNTS;
PREFS->lst_acc_columns[i++] = COL_DSPACC_CLEAR;
PREFS->lst_acc_columns[i++] = COL_DSPACC_RECON;
PREFS->lst_acc_columns[i++] = COL_DSPACC_TODAY;
PREFS->lst_acc_columns[i++] = COL_DSPACC_FUTURE;
//5.8 schedule/upcoming
homebank_pref_setdefault_lst_sch_columns();
i = 0;
PREFS->lst_impope_columns[i++] = LST_DSPOPE_DATE; //always displayed
PREFS->lst_impope_columns[i++] = LST_DSPOPE_MEMO;
PREFS->lst_impope_columns[i++] = LST_DSPOPE_AMOUNT;
PREFS->lst_impope_columns[i++] = LST_DSPOPE_PAYNUMBER;
PREFS->lst_impope_columns[i++] = LST_DSPOPE_PAYEE;
PREFS->lst_impope_columns[i++] = LST_DSPOPE_CATEGORY;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_CLR;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_STATUS; //always displayed
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_EXPENSE;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_INCOME;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_BALANCE;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_ACCOUNT;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_MATCH;
PREFS->lst_impope_columns[i++] = -LST_DSPOPE_GRPFLAG;
//book list column
homebank_pref_setdefault_lst_ope_columns();
//detail list column
homebank_pref_setdefault_lst_det_columns();
//PREFS->base_cur.nbdecimal = 2;
//PREFS->base_cur.separator = TRUE;
//PREFS->date_range_wal = FLT_RANGE_LASTMONTH;
//PREFS->date_range_txn = FLT_RANGE_LAST12MONTHS;
//PREFS->date_range_rep = FLT_RANGE_THISYEAR;
//v5.2 change to let the example file show things
//PREFS->date_range_wal = FLT_RANGE_MISC_ALLDATE;
PREFS->date_range_txn = FLT_RANGE_MISC_ALLDATE;
PREFS->date_range_rep = FLT_RANGE_MISC_ALLDATE;
PREFS->date_future_nbdays = 0;
PREFS->rep_maxspenditems = 10;
//forecast
PREFS->rep_forcast = TRUE;
PREFS->rep_forecat_nbmonth = 6;
//import/export
PREFS->dtex_nointro = TRUE;
PREFS->dtex_dodefpayee = FALSE;
PREFS->dtex_doautoassign = FALSE;
PREFS->dtex_ucfirst = FALSE;
//#2040010
PREFS->dtex_datefmt = PRF_DATEFMT_YMD;
PREFS->dtex_ofxname = 1;
PREFS->dtex_ofxmemo = 2;
PREFS->dtex_qifmemo = TRUE;
PREFS->dtex_qifswap = FALSE;
PREFS->dtex_csvsep = PRF_DTEX_CSVSEP_SEMICOLON;
//currency api
PREFS->api_rate_url = g_strdup("https://api.frankfurter.app/latest");
PREFS->api_rate_key = NULL;
//todo: add intelligence here
PREFS->euro_active = FALSE;
PREFS->euro_country = 0;
PREFS->euro_value = 1.0;
da_cur_initformat(&PREFS->minor_cur);
//PREFS->euro_nbdec = 2;
//PREFS->euro_thsep = TRUE;
//PREFS->euro_symbol = g_strdup("??");
PREFS->stat_byamount = FALSE;
PREFS->stat_showdetail = FALSE;
PREFS->stat_showrate = FALSE;
PREFS->stat_includexfer = FALSE;
PREFS->budg_showdetail = FALSE;
PREFS->budg_unexclsub = FALSE;
PREFS->report_color_scheme = CHART_COLMAP_HOMEBANK;
//PREFS->chart_legend = FALSE;
PREFS->vehicle_unit_ismile = FALSE;
PREFS->vehicle_unit_isgal = FALSE;
_homebank_pref_init_measurement_units();
}
/*
** load preference from homedir/.homebank (HB_DATA_PATH)
*/
static void homebank_pref_get_wingeometry(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
struct WinGeometry *storage)
{
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
gint *wg;
gsize length;
wg = g_key_file_get_integer_list(key_file, group_name, key, &length, NULL);
memcpy(storage, wg, 5*sizeof(gint));
g_free(wg);
// #606613 ensure left/top to be > 0
if(storage->l < 0)
storage->l = 0;
if(storage->t < 0)
storage->t = 0;
}
}
static void homebank_pref_get_intlist(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gint *storage,
gsize maxlength)
{
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
gint *src;
gsize length;
src = g_key_file_get_integer_list(key_file, group_name, key, &length, NULL);
DBKF( g_print(" - length %d (max=%d)\n", (int)length, (int)maxlength) );
if( length == maxlength )
{
DBKF( g_print(" > storing\n") );
memcpy(storage, src, length*sizeof(gint));
}
g_free(src);
}
}
static void homebank_pref_get_boolean(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gboolean *storage)
{
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
*storage = g_key_file_get_boolean(key_file, group_name, key, NULL);
DBKF( g_print(" > stored boolean %d for %s at %p\n", *storage, key, storage) );
}
}
static void homebank_pref_get_integer(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gint *storage)
{
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
*storage = g_key_file_get_integer(key_file, group_name, key, NULL);
DBKF( g_print(" > stored integer %d for %s at %p\n", *storage, key, storage) );
}
}
static void homebank_pref_get_guint32(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
guint32 *storage)
{
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
*storage = g_key_file_get_integer(key_file, group_name, key, NULL);
DBKF( g_print(" > stored guint32 %d for %s at %p\n", *storage, key, storage) );
}
}
static void homebank_pref_get_short(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gshort *storage)
{
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
*storage = (gshort)g_key_file_get_integer(key_file, group_name, key, NULL);
DBKF( g_print(" > stored short %d for %s at %p\n", *storage, key, storage) );
}
}
static void homebank_pref_get_string(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gchar **storage)
{
gchar *string;
DBKF( g_print(" search %s in %s\n", key, group_name) );
if( g_key_file_has_key(key_file, group_name, key, NULL) )
{
/* free any previous string */
if( *storage != NULL )
{
DBKF( g_print(" storage was not null, freeing\n") );
g_free(*storage);
}
*storage = NULL;
string = g_key_file_get_string(key_file, group_name, key, NULL);
if( string != NULL )
{
//*storage = g_strdup(string);
//leak
*storage = string; //already a new allocated string
DBKF( g_print(" > stored '%s' for %s at %p\n", *storage, key, *storage) );
}
}
/*
if (error)
{
g_warning ("error: %s\n", error->message);
g_error_free(error);
error = NULL;
}
*/
}
static gint homebank_pref_upgrade_560_daterange(gint oldrange)
{
gint newrange = FLT_RANGE_UNSET;
switch(oldrange)
{
case OLD56_FLT_RANGE_THISMONTH: newrange = FLT_RANGE_THIS_MONTH; break;
case OLD56_FLT_RANGE_LASTMONTH: newrange = FLT_RANGE_LAST_MONTH; break;
case OLD56_FLT_RANGE_THISQUARTER: newrange = FLT_RANGE_THIS_QUARTER; break;
case OLD56_FLT_RANGE_LASTQUARTER: newrange = FLT_RANGE_LAST_QUARTER; break;
case OLD56_FLT_RANGE_THISYEAR: newrange = FLT_RANGE_THIS_YEAR; break;
case OLD56_FLT_RANGE_LASTYEAR: newrange = FLT_RANGE_LAST_YEAR; break;
case OLD56_FLT_RANGE_LAST30DAYS: newrange = FLT_RANGE_LAST_30DAYS; break;
case OLD56_FLT_RANGE_LAST60DAYS: newrange = FLT_RANGE_LAST_60DAYS; break;
case OLD56_FLT_RANGE_LAST90DAYS: newrange = FLT_RANGE_LAST_90DAYS; break;
case OLD56_FLT_RANGE_LAST12MONTHS: newrange = FLT_RANGE_LAST_12MONTHS; break;
case OLD56_FLT_RANGE_ALLDATE: newrange = FLT_RANGE_MISC_ALLDATE; break;
}
DB( g_print(" %d => %d\n", oldrange, newrange) );
return newrange;
}
static void homebank_pref_currfmt_convert(Currency *cur, gchar *prefix, gchar *suffix)
{
if( (prefix != NULL) && (strlen(prefix) > 0) )
{
cur->symbol = g_strdup(prefix);
cur->sym_prefix = TRUE;
}
else if( (suffix != NULL) )
{
cur->symbol = g_strdup(suffix);
cur->sym_prefix = FALSE;
}
}
//#beta start
void homebank_pref_icon_symbolic(gboolean active)
{
//ensure we have a provider
if(!GLOBALS->provider)
{
GLOBALS->provider = gtk_css_provider_new ();
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (GLOBALS->provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
if( active )
{
gtk_css_provider_load_from_data (GLOBALS->provider, "* {-gtk-icon-style: symbolic;}", -1, NULL);
}
else if(GLOBALS->provider != NULL )
{
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (GLOBALS->provider));
g_clear_object (&GLOBALS->provider);
}
}
//#beta end
//#2076474
void homebank_pref_apply_scheme(void)
{
GtkSettings *settings = gtk_settings_get_default();
DB( g_print("\n[preferences] pref apply scheme\n") );
DB( g_print(" scheme : %d\n", GLOBALS->color_scheme) );
DB( g_print(" appdark: %d\n", PREFS->gtk_darktheme) );
GLOBALS->theme_is_dark = FALSE;
if( (GLOBALS->color_scheme == DEFAULT) && PREFS->gtk_darktheme )
GLOBALS->theme_is_dark = TRUE;
else
if( GLOBALS->color_scheme == PREFER_DARK )
GLOBALS->theme_is_dark = TRUE;
g_object_set(settings, "gtk-application-prefer-dark-theme", GLOBALS->theme_is_dark, NULL);
}
void homebank_pref_apply(void)
{
GtkSettings *settings = gtk_settings_get_default();
DB( g_print("\n[preferences] pref apply\n") );
if( PREFS->gtk_override == TRUE )
{
PangoFontDescription *pfd;
gchar *oldfn, *newfn;
g_object_get(settings, "gtk-font-name", &oldfn, NULL);
pfd = pango_font_description_from_string(oldfn);
DB( g_print(" font-name '%s' == '%s' %d\n", oldfn, pango_font_description_get_family(pfd), pango_font_description_get_size(pfd)/PANGO_SCALE) );
g_free(oldfn);
pango_font_description_set_size(pfd, PREFS->gtk_fontsize*PANGO_SCALE);
newfn = pango_font_description_to_string(pfd);
DB( g_print(" font-name '%s' == '%s' %d\n", newfn, pango_font_description_get_family(pfd), pango_font_description_get_size(pfd)/PANGO_SCALE) );
g_object_set(settings, "gtk-font-name", newfn, NULL);
g_free(newfn);
pango_font_description_free(pfd);
}
else
{
gtk_settings_reset_property(settings, "gtk-font-name");
}
homebank_pref_apply_scheme();
//gtk_settings_set_string_property (gtk_settings_get_default (), "gtk-icon-theme-name", PREFS->icontheme, "gtkrc:0");
g_object_set(gtk_settings_get_default (), "gtk-icon-theme-name", PREFS->icontheme, NULL);
homebank_pref_icon_symbolic(PREFS->icon_symbolic);
}
gboolean homebank_pref_load(void)
{
GKeyFile *keyfile;
gboolean retval = FALSE;
gchar *group, *filename;
guint32 version = 0;
gboolean loaded;
GError *error = NULL;
DB( g_print("\n[preferences] pref load\n") );
keyfile = g_key_file_new();
if(keyfile)
{
filename = g_build_filename(homebank_app_get_config_dir(), "preferences", NULL );
DB( g_print(" - filename: %s\n", filename) );
error = NULL;
loaded = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error);
if( error )
{
g_warning("unable to load file %s: %s", filename, error->message);
g_error_free (error);
}
if( loaded == TRUE )
{
group = "General";
DBKF( g_print(" -> ** General\n") );
//since 4.51 > integer 1 digit each
//since 5.5.6 > 2 digit each
homebank_pref_get_guint32 (keyfile, group, "Version", &version);
if(version == 0) // old double number
{
gdouble v = g_key_file_get_double (keyfile, group, "Version", NULL);
version = (guint32)(v * 10);
}
DBKF( g_print(" - version: %d\n", version) );
homebank_pref_get_string(keyfile, group, "Language", &PREFS->language);
homebank_pref_get_short(keyfile, group, "BarStyle" , &PREFS->toolbar_style);
if(version <= 6 && PREFS->toolbar_style == 0) // force system to text beside
{
PREFS->toolbar_style = 4;
}
//5.4.3
homebank_pref_get_boolean(keyfile, group, "GtkOverride", &PREFS->gtk_override);
homebank_pref_get_short(keyfile, group, "GtkFontSize" , &PREFS->gtk_fontsize);
homebank_pref_get_boolean(keyfile, group, "GtkDarkTheme", &PREFS->gtk_darktheme);
//beta
homebank_pref_get_string(keyfile, group, "IconTheme", &PREFS->icontheme);
homebank_pref_get_boolean(keyfile, group, "IconSymbolic", &PREFS->icon_symbolic);
if(version <= 2) // retrieve old settings
{
guint32 color = 0;
homebank_pref_get_guint32(keyfile, group, "ColorExp" , &color);
g_free(PREFS->color_exp);
PREFS->color_exp = g_strdup_printf("#%06x", color);
homebank_pref_get_guint32(keyfile, group, "ColorInc" , &color);
g_free(PREFS->color_inc);
PREFS->color_inc = g_strdup_printf("#%06x", color);
homebank_pref_get_guint32(keyfile, group, "ColorWarn", &color);
g_free(PREFS->color_warn);
PREFS->color_warn = g_strdup_printf("#%06x", color);
}
else
{
homebank_pref_get_boolean(keyfile, group, "CustomColors", &PREFS->custom_colors);
homebank_pref_get_boolean(keyfile, group, "CustomBgFuture", &PREFS->custom_bg_future);
homebank_pref_get_boolean(keyfile, group, "ColorUsePalette", &PREFS->color_use_palette);
homebank_pref_get_string(keyfile, group, "ColorExp" , &PREFS->color_exp);
homebank_pref_get_string(keyfile, group, "ColorInc" , &PREFS->color_inc);
homebank_pref_get_string(keyfile, group, "ColorWarn", &PREFS->color_warn);
homebank_pref_get_string(keyfile, group, "ColorBgFuture", &PREFS->color_bg_future);
if( version <= 500 )
{
gboolean rules_hint = FALSE;
homebank_pref_get_boolean(keyfile, group, "RulesHint", &rules_hint);
if( rules_hint == TRUE )
PREFS->grid_lines = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL;
}
else
homebank_pref_get_short(keyfile, group, "GridLines", &PREFS->grid_lines);
//we disable showwelcome for old users
if( version < 540 )
PREFS->showwelcome = FALSE;
}
DBKF( g_print(" - color exp: %s\n", PREFS->color_exp) );
DBKF( g_print(" - color inc: %s\n", PREFS->color_inc) );
DBKF( g_print(" - color wrn: %s\n", PREFS->color_warn) );
homebank_pref_get_string(keyfile, group, "WalletPath", &PREFS->path_hbfile);
homebank_pref_get_string(keyfile, group, "BackupPath", &PREFS->path_hbbak);
//#1870433 default backup path folder not initialized with wallet folder
if( version < 530 )
{
homebank_pref_get_string(keyfile, group, "WalletPath", &PREFS->path_hbbak);
}
homebank_pref_get_string(keyfile, group, "ImportPath", &PREFS->path_import);
homebank_pref_get_string(keyfile, group, "ExportPath", &PREFS->path_export);
homebank_pref_get_boolean(keyfile, group, "ShowSplash", &PREFS->showsplash);
homebank_pref_get_boolean(keyfile, group, "ShowWelcome", &PREFS->showwelcome);
homebank_pref_get_boolean(keyfile, group, "LoadLast", &PREFS->loadlast);
homebank_pref_get_boolean(keyfile, group, "AppendScheduled", &PREFS->appendscheduled);
homebank_pref_get_boolean(keyfile, group, "UpdateCurrency", &PREFS->do_update_currency);
homebank_pref_get_boolean(keyfile, group, "BakIsAutomatic", &PREFS->bak_is_automatic);
homebank_pref_get_short (keyfile, group, "BakMaxNumCopies", &PREFS->bak_max_num_copies);
homebank_pref_get_boolean(keyfile, group, "HeritDate", &PREFS->heritdate);
homebank_pref_get_boolean(keyfile, group, "ShowConfirm", &PREFS->txn_showconfirm);
homebank_pref_get_boolean(keyfile, group, "ShowTemplate", &PREFS->txn_showtemplate);
homebank_pref_get_boolean(keyfile, group, "HideReconciled", &PREFS->hidereconciled);
homebank_pref_get_boolean(keyfile, group, "ShowRemind", &PREFS->showremind);
homebank_pref_get_boolean(keyfile, group, "ShowVoid", &PREFS->showvoid);
homebank_pref_get_boolean(keyfile, group, "IncludeRemind", &PREFS->includeremind);
homebank_pref_get_boolean(keyfile, group, "LockReconciled", &PREFS->safe_lock_recon);
homebank_pref_get_boolean(keyfile, group, "SafePendRecon", &PREFS->safe_pend_recon);
homebank_pref_get_boolean(keyfile, group, "SafePendPast", &PREFS->safe_pend_past);
homebank_pref_get_short (keyfile, group, "SafePendPastDays", &PREFS->safe_pend_past_days);
homebank_pref_get_boolean(keyfile, group, "TxnMemoAcp", &PREFS->txn_memoacp);
homebank_pref_get_short (keyfile, group, "TxnMemoAcpDays", &PREFS->txn_memoacp_days);
homebank_pref_get_boolean(keyfile, group, "TxnXferShowDialog", &PREFS->xfer_showdialog);
homebank_pref_get_short (keyfile, group, "TxnXferDayGap", &PREFS->xfer_daygap);
homebank_pref_get_boolean(keyfile, group, "TxnXferSyncDate", &PREFS->xfer_syncdate);
homebank_pref_get_boolean(keyfile, group, "TxnXferSyncStatus", &PREFS->xfer_syncstat);
if( g_key_file_has_key(keyfile, group, "ColumnsOpe", NULL) )
{
gboolean *bsrc;
gint *src, i, j;
gsize length;
if(version <= 2) //retrieve old 0.1 or 0.2 visibility boolean
{
bsrc = g_key_file_get_boolean_list(keyfile, group, "ColumnsOpe", &length, NULL);
if( length == NUM_LST_DSPOPE-1 )
{
//and convert
for(i=0; ilst_ope_columns[i] = (bsrc[i] == TRUE) ? i+1 : -(i+1);
}
}
g_free(bsrc);
}
else
{
src = g_key_file_get_integer_list(keyfile, group, "ColumnsOpe", &length, NULL);
DBKF( g_print(" - length %d (max=%d)\n", (int)length, NUM_LST_DSPOPE) );
if( length == NUM_LST_DSPOPE )
{
DBKF( g_print(" - copying column order from pref file\n") );
memcpy(PREFS->lst_ope_columns, src, length*sizeof(gint));
}
else
{
if(version <= 7)
{
if( length == NUM_LST_DSPOPE-2 ) //1 less column before v4.5.1
{
DBKF( g_print(" - upgrade from v7\n") );
DBKF( g_print(" - copying column order from pref file\n") );
memcpy(PREFS->lst_ope_columns, src, length*sizeof(gint));
//append balance column
PREFS->lst_ope_columns[10] = LST_DSPOPE_BALANCE;
}
}
if(version < 500)
{
if( length == NUM_LST_DSPOPE-2 ) //1 less column before v4.5.1
{
DBKF( g_print(" - upgrade prior v5.0\n") );
DBKF( g_print(" - copying column order from pref file\n") );
gboolean added = FALSE;
for(i=0,j=0; ilst_ope_columns[j++] = LST_DSPOPE_CLR;
added = TRUE;
}
PREFS->lst_ope_columns[j++] = src[i];
}
}
}
}
g_free(src);
}
}
homebank_pref_get_intlist(keyfile, group, "ColumnsOpeWidth", PREFS->lst_ope_col_width, NUM_LST_DSPOPE);
homebank_pref_get_integer(keyfile, group, "OpeSortId", &PREFS->lst_ope_sort_id);
homebank_pref_get_integer(keyfile, group, "OpeSortOrder", &PREFS->lst_ope_sort_order);
DBKF( g_print(" - set sort to %d %d\n", PREFS->lst_ope_sort_id, PREFS->lst_ope_sort_order) );
//detail list
homebank_pref_get_intlist(keyfile, group, "ColumnsDet", PREFS->lst_det_columns, NUM_LST_DSPOPE);
homebank_pref_get_intlist(keyfile, group, "ColumnsDetWidth", PREFS->lst_det_col_width, NUM_LST_DSPOPE);
homebank_pref_get_short(keyfile, group, "FiscYearDay", &PREFS->fisc_year_day);
homebank_pref_get_short(keyfile, group, "FiscYearMonth", &PREFS->fisc_year_month);
//5.8 payment, NUM_PAYMODE_KEY-1 because PAYMODE_OBSOLETEINTXFER
PREFS->lst_paymode[0] = PAYMODE_NONE;
homebank_pref_get_intlist(keyfile, group, "Payment", &PREFS->lst_paymode[1], NUM_PAYMODE_KEY-1);
group = "Windows";
DBKF( g_print(" -> ** Windows\n") );
homebank_pref_get_wingeometry(keyfile, group, "Wal", &PREFS->wal_wg);
homebank_pref_get_wingeometry(keyfile, group, "Acc", &PREFS->acc_wg);
homebank_pref_get_wingeometry(keyfile, group, "Sta", &PREFS->sta_wg);
homebank_pref_get_wingeometry(keyfile, group, "Tme", &PREFS->tme_wg);
homebank_pref_get_wingeometry(keyfile, group, "Ove", &PREFS->ove_wg);
homebank_pref_get_wingeometry(keyfile, group, "Bud", &PREFS->bud_wg);
homebank_pref_get_wingeometry(keyfile, group, "Car", &PREFS->cst_wg);
homebank_pref_get_wingeometry(keyfile, group, "Txn", &PREFS->txn_wg);
homebank_pref_get_wingeometry(keyfile, group, "DBud", &PREFS->dbud_wg);
if(version <= 7) //set maximize to 0
{
PREFS->wal_wg.s = 0;
PREFS->acc_wg.s = 0;
PREFS->txn_wg.s = 0;
PREFS->sta_wg.s = 0;
PREFS->tme_wg.s = 0;
PREFS->ove_wg.s = 0;
PREFS->bud_wg.s = 0;
PREFS->cst_wg.s = 0;
}
homebank_pref_get_integer(keyfile, group, "WalVPaned", &PREFS->wal_vpaned);
homebank_pref_get_integer(keyfile, group, "WalHPaned", &PREFS->wal_hpaned);
homebank_pref_get_boolean(keyfile, group, "WalToolbar", &PREFS->wal_toolbar);
homebank_pref_get_boolean(keyfile, group, "WalTotalChart", &PREFS->wal_totchart);
homebank_pref_get_boolean(keyfile, group, "WalTimeChart", &PREFS->wal_timchart);
homebank_pref_get_boolean(keyfile, group, "WalUpcoming", &PREFS->wal_upcoming);
if( version < 570 )
{
homebank_pref_get_boolean(keyfile, group, "WalSpending", &PREFS->wal_totchart);
}
//since 5.1.3
group = "Panels";
DBKF( g_print(" -> ** Panels\n") );
homebank_pref_get_short(keyfile, group, "AccColAccW", &PREFS->pnl_acc_col_acc_width);
homebank_pref_get_short(keyfile, group, "AccShowBy" , &PREFS->pnl_acc_show_by);
{
gint *src;
gsize length;
src = g_key_file_get_integer_list(keyfile, group, "AccColumns", &length, NULL);
DB( g_print(" - length %d (max=%d)\n", (int)length, NUM_LST_DSPOPE) );
if( length == NUM_LST_COL_DSPACC )
{
DB( g_print(" - copying column order from pref file\n") );
memcpy(PREFS->lst_acc_columns, src, length*sizeof(gint));
}
g_free(src);
}
//hub total/time
homebank_pref_get_short(keyfile, group, "HubTotView" , &PREFS->hub_tot_view);
homebank_pref_get_short(keyfile, group, "HubTotViewRange", &PREFS->hub_tot_range);
homebank_pref_get_short(keyfile, group, "HubTotRaw" , &PREFS->hub_tot_raw);
homebank_pref_get_short(keyfile, group, "HubTimView" , &PREFS->hub_tim_view);
homebank_pref_get_short(keyfile, group, "HubTimViewRange", &PREFS->hub_tim_range);
homebank_pref_get_short(keyfile, group, "HubTimRaw" , &PREFS->hub_tim_raw);
//scheduled/upcoming
if( g_key_file_has_key(keyfile, group, "ColumnsSch", NULL) )
{
gint *src;
gsize length;
src = g_key_file_get_integer_list(keyfile, group, "ColumnsSch", &length, NULL);
DBKF( g_print(" - length %d (max=%d)\n", (int)length, NUM_COL_SCH_UID) );
if( length == NUM_COL_SCH_UID )
{
DBKF( g_print(" - copying column order from pref file\n") );
memcpy(PREFS->lst_sch_columns, src, length*sizeof(gint));
}
g_free(src);
}
homebank_pref_get_short(keyfile, group, "UpcColPayV", &PREFS->pnl_upc_col_pay_show);
homebank_pref_get_short(keyfile, group, "UpcColCatV", &PREFS->pnl_upc_col_cat_show);
homebank_pref_get_short(keyfile, group, "UpcColMemV", &PREFS->pnl_upc_col_mem_show);
homebank_pref_get_short(keyfile, group, "UpcColPayW", &PREFS->pnl_upc_col_pay_width);
homebank_pref_get_short(keyfile, group, "UpcColCatW", &PREFS->pnl_upc_col_cat_width);
homebank_pref_get_short(keyfile, group, "UpcColMemW", &PREFS->pnl_upc_col_mem_width);
homebank_pref_get_integer(keyfile, group, "UpcRange", &PREFS->pnl_upc_range);
homebank_pref_get_string(keyfile, group, "PnlLstTab", &PREFS->pnl_list_tab);
group = "Format";
DBKF( g_print(" -> ** Format\n") );
homebank_pref_get_string(keyfile, group, "DateFmt", &PREFS->date_format);
if(version < 460)
{
gboolean useimperial = FALSE;
homebank_pref_get_boolean(keyfile, group, "UKUnits", &useimperial);
if(useimperial)
{
PREFS->vehicle_unit_ismile = TRUE;
PREFS->vehicle_unit_isgal = TRUE;
}
}
homebank_pref_get_boolean(keyfile, group, "UnitIsMile", &PREFS->vehicle_unit_ismile);
homebank_pref_get_boolean(keyfile, group, "UnitIsGal", &PREFS->vehicle_unit_isgal);
group = "Filter";
DBKF( g_print(" -> ** Filter\n") );
//homebank_pref_get_integer(keyfile, group, "DateRangeWal", &PREFS->date_range_wal);
homebank_pref_get_integer(keyfile, group, "DateRangeTxn", &PREFS->date_range_txn);
homebank_pref_get_integer(keyfile, group, "DateFutureNbDays", &PREFS->date_future_nbdays);
homebank_pref_get_integer(keyfile, group, "DateRangeRep", &PREFS->date_range_rep);
if(version <= 7)
{
// shift date range >= 5, since we inserted a new one at position 5
//if(PREFS->date_range_wal >= OLD56_FLT_RANGE_LASTYEAR)
// PREFS->date_range_wal++;
if(PREFS->date_range_txn >= OLD56_FLT_RANGE_LASTYEAR)
PREFS->date_range_txn++;
if(PREFS->date_range_rep >= OLD56_FLT_RANGE_LASTYEAR)
PREFS->date_range_rep++;
}
group = "API";
DBKF( g_print(" -> ** API\n") );
homebank_pref_get_string(keyfile, group, "APIRateUrl", &PREFS->api_rate_url);
homebank_pref_get_string(keyfile, group, "APIRateKey", &PREFS->api_rate_key);
//5.7.2 fix wrong host set as defaut in 5.7
if(version < 572)
{
if( hb_string_ascii_compare("https://api.exchangerate.host", PREFS->api_rate_url) == 0 )
{
DBKF( g_print(" fix bad host in 5.7\n") );
g_free(PREFS->api_rate_url);
PREFS->api_rate_url = g_strdup("https://api.frankfurter.app/latest");
}
}
group = "Euro";
DBKF( g_print(" -> ** Euro\n") );
//homebank_pref_get_string(keyfile, group, "DefCurrency" , &PREFS->curr_default);
homebank_pref_get_boolean(keyfile, group, "Active", &PREFS->euro_active);
homebank_pref_get_integer(keyfile, group, "Country", &PREFS->euro_country);
//2066110
PREFS->euro_mceii = euro_country_is_mceii(PREFS->euro_country);
gchar *ratestr = g_key_file_get_string (keyfile, group, "ChangeRate", NULL);
if(ratestr != NULL) PREFS->euro_value = g_ascii_strtod(ratestr, NULL);
if(version <= 1)
{
homebank_pref_get_string(keyfile, group, "Symbol", &PREFS->minor_cur.symbol);
PREFS->minor_cur.frac_digits = g_key_file_get_integer (keyfile, group, "NBDec", NULL);
//PREFS->euro_nbdec = g_key_file_get_integer (keyfile, group, "NBDec", NULL);
//PREFS->euro_thsep = g_key_file_get_boolean (keyfile, group, "Sep", NULL);
//gchar *tmpstr = g_key_file_get_string (keyfile, group, "Symbol", &error);
}
else
{
if(version < 460)
{
gchar *prefix = NULL;
gchar *suffix = NULL;
homebank_pref_get_string(keyfile, group, "PreSymbol", &prefix);
homebank_pref_get_string(keyfile, group, "SufSymbol", &suffix);
homebank_pref_currfmt_convert(&PREFS->minor_cur, prefix, suffix);
g_free(prefix);
g_free(suffix);
}
else
{
homebank_pref_get_string(keyfile, group, "Symbol", &PREFS->minor_cur.symbol);
homebank_pref_get_boolean(keyfile, group, "IsPrefix", &PREFS->minor_cur.sym_prefix);
}
homebank_pref_get_string(keyfile, group, "DecChar" , &PREFS->minor_cur.decimal_char);
homebank_pref_get_string(keyfile, group, "GroupChar", &PREFS->minor_cur.grouping_char);
homebank_pref_get_short(keyfile, group, "FracDigits", &PREFS->minor_cur.frac_digits);
//fix 378992/421228
if( PREFS->minor_cur.frac_digits > MAX_FRAC_DIGIT )
PREFS->minor_cur.frac_digits = MAX_FRAC_DIGIT;
da_cur_initformat(&PREFS->minor_cur);
}
//PREFS->euro_symbol = g_locale_to_utf8(tmpstr, -1, NULL, NULL, NULL);
group = "Report";
DBKF( g_print(" -> ** Report\n") );
homebank_pref_get_boolean(keyfile, group, "StatByAmount", &PREFS->stat_byamount);
homebank_pref_get_boolean(keyfile, group, "StatDetail", &PREFS->stat_showdetail);
homebank_pref_get_boolean(keyfile, group, "StatRate", &PREFS->stat_showrate);
homebank_pref_get_boolean(keyfile, group, "StatIncXfer", &PREFS->stat_includexfer);
homebank_pref_get_boolean(keyfile, group, "BudgDetail", &PREFS->budg_showdetail);
homebank_pref_get_boolean(keyfile, group, "BudgUnExclSub", &PREFS->budg_unexclsub);
homebank_pref_get_integer(keyfile, group, "ColorScheme", &PREFS->report_color_scheme);
homebank_pref_get_boolean(keyfile, group, "SmallFont", &PREFS->rep_smallfont);
homebank_pref_get_integer(keyfile, group, "MaxSpendItems", &PREFS->rep_maxspenditems);
homebank_pref_get_boolean(keyfile, group, "Forecast", &PREFS->rep_forcast);
homebank_pref_get_integer(keyfile, group, "ForecastNbMonth", &PREFS->rep_forecat_nbmonth);
group = "Exchange";
DBKF( g_print(" -> ** Exchange\n") );
homebank_pref_get_boolean(keyfile, group, "DoIntro", &PREFS->dtex_nointro);
homebank_pref_get_boolean(keyfile, group, "UcFirst", &PREFS->dtex_ucfirst);
homebank_pref_get_integer(keyfile, group, "DateFmt", &PREFS->dtex_datefmt);
homebank_pref_get_integer(keyfile, group, "DayGap", &PREFS->dtex_daygap);
homebank_pref_get_integer(keyfile, group, "OfxName", &PREFS->dtex_ofxname);
homebank_pref_get_integer(keyfile, group, "OfxMemo", &PREFS->dtex_ofxmemo);
homebank_pref_get_boolean(keyfile, group, "QifMemo", &PREFS->dtex_qifmemo);
homebank_pref_get_boolean(keyfile, group, "QifSwap", &PREFS->dtex_qifswap);
homebank_pref_get_integer(keyfile, group, "CsvSep", &PREFS->dtex_csvsep);
homebank_pref_get_boolean(keyfile, group, "DoDefPayee", &PREFS->dtex_dodefpayee);
homebank_pref_get_boolean(keyfile, group, "DoAutoAssign", &PREFS->dtex_doautoassign);
//group = "Chart";
//PREFS->chart_legend = g_key_file_get_boolean (keyfile, group, "Legend", NULL);
/* file upgrade */
if(version < 560)
{
DBKF( g_print(" ugrade 5.6 daterange\n") );
//convert old daterange
//PREFS->date_range_wal = homebank_pref_upgrade_560_daterange(PREFS->date_range_wal); //top spending
PREFS->date_range_txn = homebank_pref_upgrade_560_daterange(PREFS->date_range_txn); //transactions
PREFS->date_range_rep = homebank_pref_upgrade_560_daterange(PREFS->date_range_rep); //report options
}
/*
#if MYDEBUG == 1
gsize length;
gchar *contents = g_key_file_to_data (keyfile, &length, NULL);
//g_print(" keyfile:\n%s\n len=%d\n", contents, length);
g_free(contents);
#endif
*/
}
g_free(filename);
g_key_file_free (keyfile);
_homebank_pref_init_measurement_units();
}
return retval;
}
static void homebank_pref_set_string(
GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
gchar *string)
{
DB( g_print(" - homebank_pref_set_string :: group='%s' key='%s' value='%s'\n", group_name, key, string) );
if( string != NULL && *string != '\0')
g_key_file_set_string (key_file, group_name, key, string);
else
g_key_file_set_string (key_file, group_name, key, "");
}
/*
** save preference to homedir/.homebank (HB_DATA_PATH)
*/
gboolean homebank_pref_save(void)
{
GKeyFile *keyfile;
gboolean retval = FALSE;
gchar *group, *filename;
gsize length;
GError *error = NULL;
DB( g_print("\n[preferences] pref save\n") );
keyfile = g_key_file_new();
if(keyfile )
{
DBKF( g_print(" -> ** general\n") );
group = "General";
//g_key_file_set_integer (keyfile, group, "Version", PREF_VERSION);
//5.9.6 integer with 2 digit each
g_key_file_set_integer (keyfile, group, "Version", HB_VERSION_NUM);
homebank_pref_set_string (keyfile, group, "Language", PREFS->language);
g_key_file_set_integer (keyfile, group, "BarStyle", PREFS->toolbar_style);
//g_key_file_set_integer (keyfile, group, "BarImageSize", PREFS->image_size);
g_key_file_set_boolean (keyfile, group, "GtkOverride", PREFS->gtk_override);
g_key_file_set_integer (keyfile, group, "GtkFontSize", PREFS->gtk_fontsize);
g_key_file_set_boolean (keyfile, group, "GtkDarkTheme", PREFS->gtk_darktheme);
g_key_file_set_string (keyfile, group, "IconTheme", PREFS->icontheme);
g_key_file_set_boolean (keyfile, group, "IconSymbolic", PREFS->icon_symbolic);
g_key_file_set_boolean (keyfile, group, "CustomColors", PREFS->custom_colors);
g_key_file_set_boolean (keyfile, group, "CustomBgFuture", PREFS->custom_bg_future);
g_key_file_set_boolean (keyfile, group, "ColorUsePalette", PREFS->color_use_palette);
g_key_file_set_string (keyfile, group, "ColorExp" , PREFS->color_exp);
g_key_file_set_string (keyfile, group, "ColorInc" , PREFS->color_inc);
g_key_file_set_string (keyfile, group, "ColorWarn", PREFS->color_warn);
g_key_file_set_string (keyfile, group, "ColorBgFuture", PREFS->color_bg_future);
g_key_file_set_integer (keyfile, group, "GridLines", PREFS->grid_lines);
homebank_pref_set_string (keyfile, group, "WalletPath" , PREFS->path_hbfile);
homebank_pref_set_string (keyfile, group, "BackupPath" , PREFS->path_hbbak);
homebank_pref_set_string (keyfile, group, "ImportPath" , PREFS->path_import);
homebank_pref_set_string (keyfile, group, "ExportPath" , PREFS->path_export);
g_key_file_set_boolean (keyfile, group, "BakIsAutomatic", PREFS->bak_is_automatic);
g_key_file_set_integer (keyfile, group, "BakMaxNumCopies", PREFS->bak_max_num_copies);
g_key_file_set_boolean (keyfile, group, "ShowSplash", PREFS->showsplash);
g_key_file_set_boolean (keyfile, group, "ShowWelcome", PREFS->showwelcome);
g_key_file_set_boolean (keyfile, group, "LoadLast", PREFS->loadlast);
g_key_file_set_boolean (keyfile, group, "AppendScheduled", PREFS->appendscheduled);
g_key_file_set_boolean (keyfile, group, "UpdateCurrency", PREFS->do_update_currency);
g_key_file_set_boolean (keyfile, group, "HeritDate", PREFS->heritdate);
g_key_file_set_boolean (keyfile, group, "ShowConfirm", PREFS->txn_showconfirm);
g_key_file_set_boolean (keyfile, group, "ShowTemplate", PREFS->txn_showtemplate);
g_key_file_set_boolean (keyfile, group, "HideReconciled", PREFS->hidereconciled);
g_key_file_set_boolean (keyfile, group, "ShowRemind", PREFS->showremind);
g_key_file_set_boolean (keyfile, group, "ShowVoid", PREFS->showvoid);
g_key_file_set_boolean (keyfile, group, "IncludeRemind", PREFS->includeremind);
g_key_file_set_boolean (keyfile, group, "LockReconciled", PREFS->safe_lock_recon);
g_key_file_set_boolean (keyfile, group, "SafePendRecon", PREFS->safe_pend_recon);
g_key_file_set_boolean (keyfile, group, "SafePendPast", PREFS->safe_pend_past);
g_key_file_set_integer (keyfile, group, "SafePendPastDays" , PREFS->safe_pend_past_days);
g_key_file_set_boolean (keyfile, group, "TxnMemoAcp", PREFS->txn_memoacp);
g_key_file_set_integer (keyfile, group, "TxnMemoAcpDays" , PREFS->txn_memoacp_days);
g_key_file_set_boolean (keyfile, group, "TxnXferShowDialog", PREFS->xfer_showdialog);
g_key_file_set_integer (keyfile, group, "TxnXferDayGap" , PREFS->xfer_daygap);
g_key_file_set_boolean (keyfile, group, "TxnXferSyncDate", PREFS->xfer_syncdate);
g_key_file_set_boolean (keyfile, group, "TxnXferSyncStatus", PREFS->xfer_syncstat);
//ledger colums
g_key_file_set_integer_list(keyfile, group, "ColumnsOpe", PREFS->lst_ope_columns, NUM_LST_DSPOPE);
g_key_file_set_integer_list(keyfile, group, "ColumnsOpeWidth", PREFS->lst_ope_col_width, NUM_LST_DSPOPE);
g_key_file_set_integer (keyfile, group, "OpeSortId" , PREFS->lst_ope_sort_id);
g_key_file_set_integer (keyfile, group, "OpeSortOrder" , PREFS->lst_ope_sort_order);
//detail colmuns
g_key_file_set_integer_list(keyfile, group, "ColumnsDet", PREFS->lst_det_columns, NUM_LST_DSPOPE);
g_key_file_set_integer_list(keyfile, group, "ColumnsDetWidth", PREFS->lst_det_col_width, NUM_LST_DSPOPE);
g_key_file_set_integer (keyfile, group, "FiscYearDay" , PREFS->fisc_year_day);
g_key_file_set_integer (keyfile, group, "FiscYearMonth" , PREFS->fisc_year_month);
//5.8 payment, NUM_PAYMODE_KEY-1 because PAYMODE_OBSOLETEINTXFER
g_key_file_set_integer_list(keyfile, group, "Payment", &PREFS->lst_paymode[1], NUM_PAYMODE_KEY-1);
// added v3.4
DBKF( g_print(" -> ** windows\n") );
group = "Windows";
g_key_file_set_integer_list(keyfile, group, "Wal", (gint *)&PREFS->wal_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Acc", (gint *)&PREFS->acc_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Sta", (gint *)&PREFS->sta_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Tme", (gint *)&PREFS->tme_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Ove", (gint *)&PREFS->ove_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Bud", (gint *)&PREFS->bud_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Car", (gint *)&PREFS->cst_wg, 5);
g_key_file_set_integer_list(keyfile, group, "Txn", (gint *)&PREFS->txn_wg, 5);
g_key_file_set_integer_list(keyfile, group, "DBud", (gint *)&PREFS->dbud_wg, 5);
g_key_file_set_integer (keyfile, group, "WalVPaned" , PREFS->wal_vpaned);
g_key_file_set_integer (keyfile, group, "WalHPaned" , PREFS->wal_hpaned);
g_key_file_set_boolean (keyfile, group, "WalToolbar", PREFS->wal_toolbar);
g_key_file_set_boolean (keyfile, group, "WalTotalChart", PREFS->wal_totchart);
g_key_file_set_boolean (keyfile, group, "WalTimeChart", PREFS->wal_timchart);
g_key_file_set_boolean (keyfile, group, "WalUpcoming", PREFS->wal_upcoming);
//since 5.1.3
DBKF( g_print(" -> ** Panels\n") );
group = "Panels";
g_key_file_set_integer(keyfile, group, "AccColAccW", PREFS->pnl_acc_col_acc_width);
g_key_file_set_integer(keyfile, group, "AccShowBy" , PREFS->pnl_acc_show_by);
g_key_file_set_integer_list(keyfile, group, "AccColumns", PREFS->lst_acc_columns, NUM_LST_COL_DSPACC);
//hub total/time
g_key_file_set_integer(keyfile, group, "HubTotView" , PREFS->hub_tot_view);
g_key_file_set_integer(keyfile, group, "HubTotViewRange" , PREFS->hub_tot_range);
g_key_file_set_integer(keyfile, group, "HubTotRaw" , PREFS->hub_tot_raw);
g_key_file_set_integer(keyfile, group, "HubTimView" , PREFS->hub_tim_view);
g_key_file_set_integer(keyfile, group, "HubTimViewRange" , PREFS->hub_tim_range);
g_key_file_set_integer(keyfile, group, "HubTimRaw" , PREFS->hub_tim_raw);
//scheduled/upcoming
g_key_file_set_integer_list(keyfile, group, "ColumnsSch", PREFS->lst_sch_columns, NUM_COL_SCH_UID);
g_key_file_set_integer(keyfile, group, "UpcColPayV", PREFS->pnl_upc_col_pay_show);
g_key_file_set_integer(keyfile, group, "UpcColCatV", PREFS->pnl_upc_col_cat_show);
g_key_file_set_integer(keyfile, group, "UpcColMemV", PREFS->pnl_upc_col_mem_show);
g_key_file_set_integer(keyfile, group, "UpcColPayW", PREFS->pnl_upc_col_pay_width);
g_key_file_set_integer(keyfile, group, "UpcColCatW", PREFS->pnl_upc_col_cat_width);
g_key_file_set_integer(keyfile, group, "UpcColMemW", PREFS->pnl_upc_col_mem_width);
g_key_file_set_integer(keyfile, group, "UpcRange", PREFS->pnl_upc_range);
homebank_pref_set_string (keyfile, group, "PnlLstTab", PREFS->pnl_list_tab);
DBKF( g_print(" -> ** format\n") );
group = "Format";
homebank_pref_set_string (keyfile, group, "DateFmt" , PREFS->date_format);
//g_key_file_set_boolean (keyfile, group, "UKUnits" , PREFS->imperial_unit);
g_key_file_set_boolean (keyfile, group, "UnitIsMile" , PREFS->vehicle_unit_ismile);
g_key_file_set_boolean (keyfile, group, "UnitIsGal" , PREFS->vehicle_unit_isgal);
DBKF( g_print(" -> ** filter\n") );
group = "Filter";
//g_key_file_set_integer (keyfile, group, "DateRangeWal", PREFS->date_range_wal);
g_key_file_set_integer (keyfile, group, "DateRangeTxn", PREFS->date_range_txn);
g_key_file_set_integer (keyfile, group, "DateFutureNbDays", PREFS->date_future_nbdays);
g_key_file_set_integer (keyfile, group, "DateRangeRep", PREFS->date_range_rep);
DBKF( g_print(" -> ** API\n") );
group = "API";
homebank_pref_set_string(keyfile, group, "APIRateUrl", PREFS->api_rate_url);
homebank_pref_set_string(keyfile, group, "APIRateKey", PREFS->api_rate_key);
DBKF( g_print(" -> ** euro\n") );
//euro options
group = "Euro";
//homebank_pref_set_string(keyfile, group, "DefCurrency" , PREFS->curr_default);
g_key_file_set_boolean (keyfile, group, "Active" , PREFS->euro_active);
if( PREFS->euro_active )
{
g_key_file_set_integer (keyfile, group, "Country", PREFS->euro_country);
gchar ratestr[64];
g_ascii_dtostr(ratestr, 63, PREFS->euro_value);
homebank_pref_set_string (keyfile, group, "ChangeRate", ratestr);
homebank_pref_set_string (keyfile, group, "Symbol" , PREFS->minor_cur.symbol);
g_key_file_set_boolean (keyfile, group, "IsPrefix" , PREFS->minor_cur.sym_prefix);
homebank_pref_set_string (keyfile, group, "DecChar" , PREFS->minor_cur.decimal_char);
homebank_pref_set_string (keyfile, group, "GroupChar" , PREFS->minor_cur.grouping_char);
g_key_file_set_integer (keyfile, group, "FracDigits", PREFS->minor_cur.frac_digits);
}
//report options
DBKF( g_print(" -> ** report\n") );
group = "Report";
g_key_file_set_boolean (keyfile, group, "StatByAmount" , PREFS->stat_byamount);
g_key_file_set_boolean (keyfile, group, "StatDetail" , PREFS->stat_showdetail);
g_key_file_set_boolean (keyfile, group, "StatRate" , PREFS->stat_showrate);
g_key_file_set_boolean (keyfile, group, "StatIncXfer" , PREFS->stat_includexfer);
g_key_file_set_boolean (keyfile, group, "BudgDetail" , PREFS->budg_showdetail);
g_key_file_set_boolean (keyfile, group, "BudgUnExclSub", PREFS->budg_unexclsub);
g_key_file_set_integer (keyfile, group, "ColorScheme" , PREFS->report_color_scheme);
g_key_file_set_boolean (keyfile, group, "SmallFont" , PREFS->rep_smallfont);
g_key_file_set_integer (keyfile, group, "MaxSpendItems", PREFS->rep_maxspenditems);
g_key_file_set_boolean (keyfile, group, "Forecast" , PREFS->rep_forcast);
g_key_file_set_integer (keyfile, group, "ForecastNbMonth", PREFS->rep_forecat_nbmonth);
group = "Exchange";
g_key_file_set_boolean (keyfile, group, "DoIntro", PREFS->dtex_nointro);
g_key_file_set_boolean (keyfile, group, "UcFirst", PREFS->dtex_ucfirst);
g_key_file_set_integer (keyfile, group, "DateFmt", PREFS->dtex_datefmt);
g_key_file_set_integer (keyfile, group, "DayGap", PREFS->dtex_daygap);
g_key_file_set_integer (keyfile, group, "OfxName", PREFS->dtex_ofxname);
g_key_file_set_integer (keyfile, group, "OfxMemo", PREFS->dtex_ofxmemo);
g_key_file_set_boolean (keyfile, group, "QifMemo", PREFS->dtex_qifmemo);
g_key_file_set_boolean (keyfile, group, "QifSwap", PREFS->dtex_qifswap);
g_key_file_set_integer (keyfile, group, "CsvSep", PREFS->dtex_csvsep);
g_key_file_set_boolean (keyfile, group, "DoDefPayee", PREFS->dtex_dodefpayee);
g_key_file_set_boolean (keyfile, group, "DoAutoAssign", PREFS->dtex_doautoassign);
//group = "Chart";
//g_key_file_set_boolean (keyfile, group, "Legend", PREFS->chart_legend);
//g_key_file_set_string (keyfile, group, "", PREFS->);
//g_key_file_set_boolean (keyfile, group, "", PREFS->);
//g_key_file_set_integer (keyfile, group, "", PREFS->);
DB( g_print(" -> ** g_key_file_to_data\n") );
gchar *contents = g_key_file_to_data (keyfile, &length, NULL);
//DB( g_print(" keyfile:\n%s\nlen=%d\n", contents, length) );
filename = g_build_filename(homebank_app_get_config_dir(), "preferences", NULL );
DB( g_print(" -> filename: %s\n", filename) );
g_file_set_contents(filename, contents, length, &error);
if( error )
{
g_warning("unable to save file %s: %s", filename, error->message);
g_error_free (error);
error = NULL;
}
DB( g_print(" -> contents: %s\n", contents) );
DB( g_print(" -> freeing filename\n") );
g_free(filename);
DB( g_print(" -> freeing buffer\n") );
g_free(contents);
DB( g_print(" -> freeing keyfile\n") );
g_key_file_free (keyfile);
}
_homebank_pref_init_measurement_units();
return retval;
}
homebank-5.9.7/src/enums.h 0000644 0001750 0001750 00000006504 14736461415 014721 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_ENUMS_H__
#define __HB_ENUMS_H__
/*
** paymode pixbuf
*/
#define OLDPAYMODE_INTXFER 5
typedef enum {
DEFAULT,
PREFER_DARK,
PREFER_LIGHT
} ColorScheme;
typedef enum {
HB_STRING_NONE,
HB_STRING_CLIPBOARD,
HB_STRING_PRINT,
HB_STRING_EXPORT
} ToStringMode;
enum
{
PAYMODE_NONE,
PAYMODE_CCARD,
PAYMODE_CHECK,
PAYMODE_CASH,
PAYMODE_XFER,
PAYMODE_OBSOLETEINTXFER,
/* 4.1 new payments here */
PAYMODE_DCARD = 6,
PAYMODE_REPEATPMT,
PAYMODE_EPAYMENT,
PAYMODE_DEPOSIT,
PAYMODE_FEE,
/* 4.6 new paymode */
PAYMODE_DIRECTDEBIT,
/* 5.8 new paymode */
PAYMODE_MOBPHONE,
// PAYMODE_,
NUM_PAYMODE_MAX
};
#define NUM_PAYMODE_KEY 12
/* list display transaction (dsp_account) */
enum
{
//0 is invalid column
LST_DSPOPE_STATUS = 1, /* 1 fake column */
LST_DSPOPE_DATE, /* 2 fake column */
LST_DSPOPE_PAYNUMBER, /* 3 fake column <5.8 xxx_INFO */
LST_DSPOPE_PAYEE, /* 4 fake column */
LST_DSPOPE_MEMO, /* 5 fake column */
LST_DSPOPE_AMOUNT, /* 6 fake column */
LST_DSPOPE_EXPENSE, /* 7 fake column */
LST_DSPOPE_INCOME, /* 8 fake column */
LST_DSPOPE_CATEGORY, /* 9 fake column */
LST_DSPOPE_TAGS, /* 10 fake column */
LST_DSPOPE_BALANCE, /* 11 fake column */
LST_DSPOPE_CLR, /* 12 fake column */
/* here we insert account column, only used for detail */
LST_DSPOPE_ACCOUNT, /* 13 fake column : not stored */
LST_DSPOPE_MATCH, /* 14 fake column : not stored */
LST_DSPOPE_GRPFLAG,
NUM_LST_DSPOPE
};
typedef enum {
GRPFLAG_ANY = -1,
GRPFLAG_NONE = 0,
GRPFLAG_RED = 1,
GRPFLAG_ORANGE,
GRPFLAG_YELLOW,
GRPFLAG_GREEN,
GRPFLAG_BLUE,
GRPFLAG_PURPLE,
NUM_GRPFLAG
} HbGrpFlag;
//sort for various glist
enum {
HB_GLIST_SORT_KEY, //0
HB_GLIST_SORT_NAME, //1
HB_GLIST_SORT_POS //2
};
//
enum {
HB_LIST_QUICK_SELECT_UNSET,
HB_LIST_QUICK_SELECT_ALL,
HB_LIST_QUICK_SELECT_NONE,
HB_LIST_QUICK_SELECT_INVERT
};
//
enum {
PRF_DATEFMT_MDY,
PRF_DATEFMT_DMY,
PRF_DATEFMT_YMD,
NUM_PRF_DATEFMT
};
enum {
PRF_OFXNAME_IGNORE,
PRF_OFXNAME_MEMO,
PRF_OFXNAME_PAYEE,
PRF_OFXNAME_NUMBER
};
enum {
PRF_OFXMEMO_IGNORE,
PRF_OFXMEMO_NUMBER,
PRF_OFXMEMO_MEMO,
PRF_OFXMEMO_PAYEE
};
#define PRF_DTEX_CSVSEP_BUFFER "\t,; "
enum {
PRF_DTEX_CSVSEP_TAB,
PRF_DTEX_CSVSEP_COMMA,
PRF_DTEX_CSVSEP_SEMICOLON,
PRF_DTEX_CSVSEP_SPACE,
};
/*
** list pixbuf (account/transaction)
*//*
enum
{
LST_PIXBUF_ADD,
LST_PIXBUF_EDIT,
LST_PIXBUF_REMIND,
LST_PIXBUF_VALID,
LST_PIXBUF_AUTO,
LST_PIXBUF_WARNING,
NUM_LST_PIXBUF
};*/
/*
** toolbar item type
*//*
enum
{
TOOLBAR_SEPARATOR,
TOOLBAR_BUTTON,
TOOLBAR_TOGGLE
};*/
#endif
homebank-5.9.7/src/ui-pref.h 0000644 0001750 0001750 00000010513 14765331301 015124 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __HB_PREFERENCE_GTK_H__
#define __HB_PREFERENCE_GTK_H__
struct defpref_data
{
//-- common
GtkWidget *dialog;
GtkWidget *LV_page;
GtkWidget *GR_page;
GtkWidget *label;
GtkWidget *image;
GtkWidget *BT_clear;
//-- general
GtkWidget *CM_show_splash;
GtkWidget *CM_load_last;
GtkWidget *CM_append_scheduled;
GtkWidget *CM_do_update_currency;
//-- interface
GtkWidget *CM_custom_colors;
GtkWidget *CM_custom_bg_future;
GtkWidget *CY_gridlines;
GtkWidget *CM_rep_smallfont;
GtkWidget *CY_toolbar;
GtkWidget *CM_gtk_darktheme;
GtkWidget *CY_icontheme;
GtkWidget *CM_iconsymbolic;
GtkWidget *CM_gtk_override;
GtkWidget *LB_gtk_fontsize, *NB_gtk_fontsize;
GtkWidget *CY_color_scheme;
GtkWidget *DA_colors;
GtkWidget *CM_use_palette;
GtkWidget *CP_exp_color;
GtkWidget *CP_inc_color;
GtkWidget *CP_warn_color;
GtkWidget *CP_fut_bg_color;
//-- locale
GtkWidget *CY_language;
GtkWidget *LB_date, *ST_datefmt;
GtkWidget *NB_fiscyearday;
GtkWidget *CY_fiscyearmonth;
GtkWidget *CM_unitismile;
GtkWidget *CM_unitisgal;
//-- transactions
GtkWidget *CY_daterange_txn;
GtkWidget *ST_datefuture_nbdays;
GtkWidget *CM_hide_reconciled;
GtkWidget *CM_show_remind;
GtkWidget *CM_show_void;
GtkWidget *CM_include_remind;
GtkWidget *CM_lock_reconciled;
GtkWidget *CM_safe_pend_recon;
GtkWidget *CM_safe_pend_past;
GtkWidget *ST_safe_pend_past_days;
GtkWidget *CM_herit_date;
GtkWidget *CM_herit_grpflg;
GtkWidget *CM_memoacp;
GtkWidget *ST_memoacp_days;
GtkWidget *CM_show_confirm;
GtkWidget *CM_show_template;
GtkWidget *CM_xfer_showdialog;
GtkWidget *ST_xfer_daygap;
GtkWidget *CM_xfer_syncdate;
GtkWidget *CM_xfer_syncstat;
//5.8 paymode
GtkWidget *LV_paymode;
//-- import/export
GtkWidget *CY_dtex_datefmt;
GtkWidget *CY_dtex_ofxname;
GtkWidget *CY_dtex_ofxmemo;
GtkWidget *CM_dtex_qifmemo;
GtkWidget *CM_dtex_qifswap;
GtkWidget *CM_dtex_ucfirst;
GtkWidget *CY_dtex_csvsep;
//-- report
//GtkWidget *CY_daterange_wal;
GtkWidget *ST_maxspenditems;
GtkWidget *CY_daterange_rep;
GtkWidget *CM_stat_byamount;
GtkWidget *CM_stat_showdetail;
GtkWidget *CM_stat_showrate;
GtkWidget *CM_stat_incxfer;
GtkWidget *CM_budg_showdetail;
GtkWidget *CM_budg_unexclsub;
//-- forecast
GtkWidget *CM_forecast;
GtkWidget *LB_forecast_nbmonth;
GtkWidget *ST_forecast_nbmonth;
//-- backup
GtkWidget *CM_bak_is_automatic;
GtkWidget *GR_bak_freq;
GtkWidget *LB_bak_max_num_copies, *NB_bak_max_num_copies;
//-- folders
GtkWidget *ST_path_hbfile, *BT_path_hbfile;
GtkWidget *ST_path_hbbak, *BT_path_hbbak;
GtkWidget *ST_path_import, *BT_path_import;
GtkWidget *ST_path_export, *BT_path_export;
//GtkWidget *ST_path_attach, *BT_path_attach;
//-- euro
gint country;
GtkWidget *CM_euro_enable;
GtkWidget *LB_euro_preset, *CY_euro_preset;
GtkWidget *GRP_configuration;
GtkWidget *ST_euro_country;
GtkWidget *LB_euro_src;
GtkWidget *NB_euro_value;
GtkWidget *LB_euro_dst;
GtkWidget *GRP_format;
GtkWidget *LB_numbereuro;
GtkWidget *ST_euro_symbol;
GtkWidget *CM_euro_isprefix;
GtkWidget *ST_euro_decimalchar;
GtkWidget *NB_euro_fracdigits;
GtkWidget *ST_euro_groupingchar;
//-- advanced
GtkWidget *ST_adv_apirate_url;
GtkWidget *ST_adv_apirate_key;
};
enum {
PRF_PATH_WALLET,
PRF_PATH_BACKUP,
PRF_PATH_IMPORT,
PRF_PATH_EXPORT,
PRF_PATH_ATTACH,
};
struct pref_list_datas {
gshort level;
gshort key;
const gchar *iconname;
const gchar *label;
};
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void free_pref_icons(void);
void load_pref_icons(void);
GtkWidget *defpref_dialog_new (void);
#endif
homebank-5.9.7/src/ui-widgets.c 0000644 0001750 0001750 00000133401 15114606615 015635 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "gtk-chart.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
extern HbKvData CYA_FLT_RANGE_DWF[];
extern HbKvData CYA_FLT_RANGE_MQY[];
extern HbKvData CYA_FLT_RANGE_YTO[];
extern HbKvData CYA_FLT_RANGE_LASTXXD[];
extern HbKvData CYA_FLT_RANGE_COMMON[];
extern HbKvData CYA_FLT_RANGE_CUSTOM[];
extern gchar *CYA_ABMONTHS[];
extern gchar *CYA_ARC_UNIT[];
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* GTK4 transitional anticipation */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#if( (GTK_MAJOR_VERSION < 4) )
void gtk_window_set_child (GtkWindow* window, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(window), child); }
void gtk_popover_set_child (GtkPopover* popover, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(popover), child); }
void gtk_frame_set_child (GtkFrame* frame, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(frame), child); }
void gtk_overlay_set_child (GtkOverlay* overlay, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(overlay), child); }
void gtk_scrolled_window_set_child (GtkScrolledWindow* scrolled_window, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(scrolled_window), child); }
void gtk_revealer_set_child (GtkRevealer* revealer, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(revealer), child); }
void gtk_expander_set_child (GtkExpander* expander, GtkWidget* child)
{ gtk_container_add (GTK_CONTAINER(expander), child); }
void gtk_box_prepend (GtkBox* box, GtkWidget* child)
{ gtk_box_pack_start (GTK_BOX(box), child, FALSE, FALSE, 0); }
void gtk_box_append (GtkBox* box, GtkWidget* child)
{ gtk_box_pack_end (GTK_BOX(box), child, FALSE, FALSE, 0); }
void gtk_window_destroy (GtkWindow* window)
{ gtk_widget_destroy (GTK_WIDGET(window)); }
#endif
//TODO: to be removed when migrate
GtkWidget *hbtk_image_new_from_icon_name_16(const gchar *icon_name)
{
return gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
}
GtkWidget *hbtk_image_new_from_icon_name_24(const gchar *icon_name)
{
return gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
}
GtkWidget *hbtk_image_new_from_icon_name_32(const gchar *icon_name)
{
return gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DIALOG);
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* GTK3 obsolete */
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
GtkWidget *
hbtk_toolbar_add_toolbutton(GtkToolbar *toolbar, gchar *icon_name, gchar *label, gchar *tooltip_text)
{
GtkWidget *button = gtk_widget_new(GTK_TYPE_TOOL_BUTTON,
"icon-name", icon_name,
"label", label,
NULL);
if(tooltip_text != NULL)
gtk_widget_set_tooltip_text(button, tooltip_text);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
return button;
}
GtkWidget *
hbtk_menubar_add_menu(GtkWidget *menubar, gchar *label, GtkWidget **menuitem_ptr)
{
GtkWidget *menu, *menuitem;
menu = gtk_menu_new();
menuitem = gtk_menu_item_new_with_mnemonic(label);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);
if(menuitem_ptr)
*menuitem_ptr = menuitem;
return menu;
}
GtkWidget *
hbtk_menu_add_menuitem(GtkWidget *menu, gchar *label)
{
GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic(label);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
return menuitem;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
//TODO: only WEIGHT & SCALE are used for now
void
gimp_label_set_attributes (GtkLabel *label,
...)
{
PangoAttribute *attr = NULL;
PangoAttrList *attrs;
va_list args;
g_return_if_fail (GTK_IS_LABEL (label));
attrs = pango_attr_list_new ();
va_start (args, label);
do
{
PangoAttrType attr_type = va_arg (args, PangoAttrType);
if (attr_type <= 0)
attr_type = PANGO_ATTR_INVALID;
switch (attr_type)
{
case PANGO_ATTR_LANGUAGE:
attr = pango_attr_language_new (va_arg (args, PangoLanguage *));
break;
case PANGO_ATTR_FAMILY:
attr = pango_attr_family_new (va_arg (args, const gchar *));
break;
case PANGO_ATTR_STYLE:
attr = pango_attr_style_new (va_arg (args, PangoStyle));
break;
case PANGO_ATTR_WEIGHT:
attr = pango_attr_weight_new (va_arg (args, PangoWeight));
break;
case PANGO_ATTR_VARIANT:
attr = pango_attr_variant_new (va_arg (args, PangoVariant));
break;
case PANGO_ATTR_STRETCH:
attr = pango_attr_stretch_new (va_arg (args, PangoStretch));
break;
case PANGO_ATTR_SIZE:
attr = pango_attr_size_new (va_arg (args, gint));
break;
case PANGO_ATTR_FONT_DESC:
attr = pango_attr_font_desc_new (va_arg (args,
const PangoFontDescription *));
break;
case PANGO_ATTR_FOREGROUND:
{
const PangoColor *color = va_arg (args, const PangoColor *);
attr = pango_attr_foreground_new (color->red,
color->green,
color->blue);
}
break;
case PANGO_ATTR_BACKGROUND:
{
const PangoColor *color = va_arg (args, const PangoColor *);
attr = pango_attr_background_new (color->red,
color->green,
color->blue);
}
break;
case PANGO_ATTR_UNDERLINE:
attr = pango_attr_underline_new (va_arg (args, PangoUnderline));
break;
case PANGO_ATTR_STRIKETHROUGH:
attr = pango_attr_strikethrough_new (va_arg (args, gboolean));
break;
case PANGO_ATTR_RISE:
attr = pango_attr_rise_new (va_arg (args, gint));
break;
case PANGO_ATTR_SCALE:
attr = pango_attr_scale_new (va_arg (args, gdouble));
break;
default:
//g_warning ("%s: invalid PangoAttribute type %d", G_STRFUNC, attr_type);
case PANGO_ATTR_INVALID:
attr = NULL;
break;
}
if (attr)
{
attr->start_index = 0;
attr->end_index = -1;
pango_attr_list_insert (attrs, attr);
}
}
while (attr);
va_end (args);
gtk_label_set_attributes (label, attrs);
pango_attr_list_unref (attrs);
}
// can't get rid of FILL|EXPAND for now
void hbtk_box_prepend (GtkBox* box, GtkWidget* child)
{
gtk_box_pack_start (GTK_BOX(box), child, TRUE, TRUE, 0);
}
gint hb_clicklabel_to_int(const gchar *uri)
{
gint retval = HB_LIST_QUICK_SELECT_UNSET;
if (g_strcmp0 (uri, "all") == 0)
{
retval = HB_LIST_QUICK_SELECT_ALL;
}
else
if (g_strcmp0 (uri, "non") == 0)
{
retval = HB_LIST_QUICK_SELECT_NONE;
}
else
if (g_strcmp0 (uri, "inv") == 0)
{
retval = HB_LIST_QUICK_SELECT_INVERT;
}
return retval;
}
void hb_window_run_pending(void)
{
while (gtk_events_pending ())
gtk_main_iteration ();
}
void hb_widget_set_margins(GtkWidget *widget, gint top, gint right, gint bottom, gint left)
{
gtk_widget_set_margin_top (widget, top);
gtk_widget_set_margin_end (widget, right);
gtk_widget_set_margin_bottom (widget, bottom);
gtk_widget_set_margin_start (widget, left);
}
void hb_widget_set_margin(GtkWidget *widget, gint margin)
{
hb_widget_set_margins (widget, margin, margin, margin, margin);
}
void hb_widget_visible(GtkWidget *widget, gboolean visible)
{
if(!GTK_IS_WIDGET(widget))
return;
if(visible)
{
gtk_widget_show(widget);
}
else
{
gtk_widget_hide(widget);
}
}
void ui_label_set_integer(GtkLabel *label, gint value)
{
gchar buf[16];
g_snprintf(buf, 16, "%d", value);
gtk_label_set_text (label, buf);
}
void hbtk_entry_tag_name_append(GtkEntry *entry, gchar *tagname)
{
GtkEntryBuffer *buffer;
const gchar *text;
guint len;
text = gtk_entry_get_text(entry);
if( g_strstr_len(text, -1, tagname) == NULL )
{
DB( g_print(" gtkentry append tagname '%s'\n", tagname) );
buffer = gtk_entry_get_buffer(GTK_ENTRY(entry));
if(buffer)
{
len = gtk_entry_buffer_get_length(buffer);
DB( g_print("- add ' %s' %p %d\n", tagname, buffer, len) );
if(len > 0)
gtk_entry_buffer_insert_text(buffer, len, " ", 1);
gtk_entry_buffer_insert_text(buffer, len+1, tagname, -1);
}
}
}
void hbtk_entry_set_text(GtkEntry *entry, gchar *text)
{
//DB( g_print(" set text to '%s'\n", text) );
gtk_entry_set_text(GTK_ENTRY(entry), ( text != NULL ) ? text : "");
}
gboolean hbtk_entry_replace_text(GtkEntry *entry, gchar **storage)
{
const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
gint tmpcmp = hb_string_ascii_compare(*storage, (gchar *)text);
DB( g_print(" storage is '%p' at '%p'\n", *storage, storage) );
if( tmpcmp != 0 )
{
// free any previous string
g_free(*storage);
*storage = g_strdup(text);
DB( g_print(" replace with '%s'", text) );
return TRUE;
}
return FALSE;
}
// redraw a single row of a listview (not work with GTK_SELECTION_MULTIPLE)
void
hbtk_listview_redraw_selected_row(GtkTreeView *treeview)
{
GtkTreeModel *model;
GtkTreeSelection *selection;
GtkTreeIter iter;
GtkTreePath *path;
selection = gtk_tree_view_get_selection(treeview);
if( gtk_tree_selection_get_selected(selection, &model, &iter) )
{
path = gtk_tree_model_get_path(model, &iter);
gtk_tree_model_row_changed(model, path, &iter);
gtk_tree_path_free (path);
}
}
gboolean
hbtk_tree_store_get_top_level(GtkTreeModel *model, gint column_id, guint32 key, GtkTreeIter *return_iter)
{
GtkTreeIter iter;
gboolean valid;
guint32 tmpkey;
if( model != NULL && key > 0 )
{
valid = gtk_tree_model_get_iter_first(model, &iter);
while (valid)
{
gtk_tree_model_get (model, &iter, column_id, &tmpkey, -1);
if(tmpkey == key)
{
*return_iter = iter;
return TRUE;
}
valid = gtk_tree_model_iter_next(model, &iter);
}
}
return FALSE;
}
void
hbtk_tree_store_remove_iter_with_child(GtkTreeModel *model, GtkTreeIter *iter)
{
GtkTreeIter child;
gboolean valid;
valid = gtk_tree_model_iter_children(model, &child, iter);
while( valid )
{
valid = gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
if( valid )
valid = gtk_tree_model_iter_next(model, &child);
}
gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
}
GtkTreeViewColumn *
hbtk_treeview_get_column_by_id(GtkTreeView *treeview, gint search_id)
{
GtkTreeViewColumn *column = NULL;
GList *list, *tmp;
gint id;
list = gtk_tree_view_get_columns( treeview );
tmp = g_list_first(list);
while (tmp != NULL)
{
id = gtk_tree_view_column_get_sort_column_id(tmp->data);
if( search_id == id )
{
column = tmp->data;
break;
}
tmp = g_list_next(tmp);
}
g_list_free(list);
return column;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
GtkWidget *make_clicklabel(gchar *id, gchar *str)
{
GtkWidget *label;
gchar buffer[255];
g_snprintf(buffer, 254, "%s", id, str);
label = gtk_label_new(buffer);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_label_set_track_visited_links(GTK_LABEL(label), FALSE);
gtk_widget_set_halign(label, GTK_ALIGN_START);
return GTK_WIDGET(label);
}
GtkWidget *make_label_group(gchar *str)
{
GtkWidget *label = gtk_label_new (str);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
return label;
}
GtkWidget *make_label_left(char *str)
{
GtkWidget *label = gtk_label_new_with_mnemonic (str);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
gtk_widget_set_halign (label, GTK_ALIGN_START);
return label;
}
GtkWidget *make_label_widget(char *str)
{
GtkWidget *label = gtk_label_new_with_mnemonic (str);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
gtk_widget_set_halign (label, GTK_ALIGN_END);
return label;
}
GtkWidget *make_label(char *str, gfloat xalign, gfloat yalign)
{
GtkWidget *label = gtk_label_new_with_mnemonic (str);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION < 16) )
gtk_misc_set_alignment (GTK_MISC (label), xalign, yalign);
#else
gtk_label_set_xalign(GTK_LABEL(label), xalign);
gtk_label_set_yalign(GTK_LABEL(label), yalign);
#endif
return label;
}
/*
**
*/
GtkWidget *make_text(gfloat xalign)
{
GtkWidget *entry;
entry = gtk_entry_new ();
gtk_editable_set_editable (GTK_EDITABLE(entry), FALSE);
g_object_set(entry, "xalign", xalign, NULL);
return entry;
}
GtkWidget *make_search(void)
{
GtkWidget *search;
search = gtk_search_entry_new();
gtk_entry_set_placeholder_text(GTK_ENTRY(search), _("Search...") );
gtk_entry_set_width_chars(GTK_ENTRY(search), 24);
return search;
}
/*
**
*/
GtkWidget *make_string(GtkWidget *label)
{
GtkWidget *entry;
entry = gtk_entry_new ();
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), entry);
return entry;
}
static GtkWidget *
_raw_image_button(GType type, gchar *icon_name, gchar *tooltip_text, gboolean flat, gboolean force_large)
{
GtkWidget *image, *button;
image = gtk_widget_new(GTK_TYPE_IMAGE, "icon-name", icon_name, /*"margin", 2,*/ NULL);
if(force_large)
g_object_set(image, "icon-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
button = gtk_widget_new(type, "image", image, "tooltip-text", tooltip_text, NULL);
if( flat )
gtk_style_context_add_class (gtk_widget_get_style_context (button), GTK_STYLE_CLASS_FLAT);
return button;
}
//TODO: imagebutton with svg -symbolic looks blurry if we set LARGE_TOOLBAR
GtkWidget *make_image_button(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_BUTTON, icon_name, tooltip_text, FALSE, FALSE);
}
GtkWidget *make_image_toggle_button(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_TOGGLE_BUTTON, icon_name, tooltip_text, FALSE, FALSE);
}
GtkWidget *make_image_button2(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_BUTTON, icon_name, tooltip_text, FALSE, !PREFS->icon_symbolic);
}
GtkWidget *make_image_toggle_button2(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_TOGGLE_BUTTON, icon_name, tooltip_text, FALSE, !PREFS->icon_symbolic);
}
GtkWidget *make_image_radio_button(gchar *icon_name, gchar *tooltip_text)
{
GtkWidget * button = _raw_image_button(GTK_TYPE_RADIO_BUTTON, icon_name, tooltip_text, FALSE, FALSE);
gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);
return button;
}
/* toolbar stuff */
GtkWidget *make_tb(void)
{
GtkWidget *widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
gtk_style_context_add_class (gtk_widget_get_style_context (widget), GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
return widget;
}
GtkWidget *make_tb_separator(void)
{
GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
gtk_widget_set_margin_start(widget, SPACING_SMALL);
gtk_widget_set_margin_end (widget, SPACING_SMALL);
return widget;
}
GtkWidget *make_tb_image_button(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_BUTTON, icon_name, tooltip_text, TRUE, TRUE);
}
GtkWidget *make_tb_image_toggle_button(gchar *icon_name, gchar *tooltip_text)
{
return _raw_image_button(GTK_TYPE_TOGGLE_BUTTON, icon_name, tooltip_text, TRUE, TRUE);
}
GtkWidget *make_tb_image_radio_button(gchar *icon_name, gchar *tooltip_text)
{
GtkWidget *button = _raw_image_button(GTK_TYPE_RADIO_BUTTON, icon_name, tooltip_text, TRUE, TRUE);
gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);
return button;
}
/*
**
*/
GtkWidget *make_memo_entry(GtkWidget *label)
{
GtkListStore *store;
GtkWidget *entry;
GtkEntryCompletion *completion;
GList *lmem, *list;
store = gtk_list_store_new (1, G_TYPE_STRING);
completion = gtk_entry_completion_new ();
gtk_entry_completion_set_model (completion, GTK_TREE_MODEL(store));
gtk_entry_completion_set_text_column (completion, 0);
entry = gtk_entry_new ();
gtk_entry_set_completion (GTK_ENTRY (entry), completion);
g_object_unref(store);
//populate
//gtk_list_store_clear (GTK_LIST_STORE(store));
lmem = list = g_hash_table_get_keys(GLOBALS->h_memo);
while (list != NULL)
{
GtkTreeIter iter;
//gtk_list_store_append (GTK_LIST_STORE(store), &iter);
//gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, list->data, -1);
gtk_list_store_insert_with_values(GTK_LIST_STORE(store), &iter, -1,
0, list->data,
-1);
list = g_list_next(list);
}
g_list_free(lmem);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), entry);
return entry;
}
/*
**
*/
GtkWidget *make_string_maxlength(GtkWidget *label, guint max_length)
{
GtkWidget *entry;
entry = make_string(label);
gtk_entry_set_width_chars(GTK_ENTRY(entry), max_length+2);
gtk_entry_set_max_length(GTK_ENTRY(entry), max_length);
return entry;
}
static void hb_amount_insert_text_handler (GtkEntry *entry, const gchar *text, gint length, gint *position, gpointer data)
{
GtkEditable *editable = GTK_EDITABLE(entry);
gint i, digits, count=0, dcpos=-1;
gchar *clntxt;
DB( g_print("-----\ninsert_text-handler: instxt:%s pos:%d len:%d\n", text, *position, length) );
digits = gtk_spin_button_get_digits(GTK_SPIN_BUTTON(entry));
// most common : only 1 char to be inserted
if( length == 1 )
{
const gchar *curtxt = gtk_entry_get_text(entry);
for (i=0 ; curtxt[i]!='\0' ; i++)
{
if(curtxt[i]==',' || curtxt[i]=='.')
dcpos = i;
}
DB( g_print(" dcpos:'%d'\n", dcpos) );
clntxt = g_new0 (gchar, length+1);
for (i=0 ; i < length ; i++)
{
if( g_ascii_isdigit(text[i]) && ( (*position <= dcpos + digits) || dcpos < 0) )
goto doinsert;
//5.4.3 + sign now authorized
if( (text[i]=='-' || text[i]=='+') && *position==0 ) /* -/+ sign only at position 0 */
goto doinsert;
if( dcpos < 0 && (text[i]=='.' || text[i]==',') ) /* decimal separator if not in previous string */
clntxt[count++] = '.';
continue;
doinsert:
clntxt[count++] = text[i];
}
}
// less common: paste a full text
else
{
clntxt = hb_string_dup_raw_amount_clean(text, digits);
count = strlen(clntxt);
}
if (count > 0)
{
DB( g_print(" insert %d char '%s' at %d\n", count, clntxt, *position) );
g_signal_handlers_block_by_func (G_OBJECT (editable), G_CALLBACK (hb_amount_insert_text_handler), data);
gtk_editable_insert_text (editable, clntxt, count, position);
g_signal_handlers_unblock_by_func (G_OBJECT (editable), G_CALLBACK (hb_amount_insert_text_handler), data);
}
g_free (clntxt);
g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text");
}
GtkWidget *make_amount(GtkWidget *label)
{
GtkWidget *spinner;
GtkAdjustment *adj;
//adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -G_MAXDOUBLE, G_MAXDOUBLE, 0.01, 1.0, 0.0);
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -8589934588, 8589934588, 0.01, 1.0, 0.0);
spinner = gtk_spin_button_new (adj, 1.0, 2);
g_object_set(spinner, "xalign", 1.0, NULL);
//5.7
gtk_entry_set_width_chars(GTK_ENTRY(spinner), 13);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), spinner);
g_signal_connect(G_OBJECT(spinner), "insert-text",
G_CALLBACK(hb_amount_insert_text_handler),
NULL);
return spinner;
}
GtkWidget *make_amount_pos(GtkWidget *label)
{
GtkWidget *widget = make_amount(label);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), 0, 8589934588);
return widget;
}
GtkWidget *make_exchange_rate(GtkWidget *label)
{
GtkWidget *spinner;
GtkAdjustment *adj;
//#1871383 wish: increase exchange rate size
//adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 99999, 0.01, 1.0, 0.0);
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 999999, 0.01, 1.0, 0.0);
spinner = gtk_spin_button_new (adj, 1.0, 8);
//gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
g_object_set(spinner, "xalign", 1.0, NULL);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), spinner);
return spinner;
}
/*
**
*/
GtkWidget *make_numeric(GtkWidget *label, gdouble min, gdouble max)
{
GtkWidget *spinner;
GtkAdjustment *adj;
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, min, max, 1.0, 10.0, 0.0);
spinner = gtk_spin_button_new (adj, 0, 0);
//gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
g_object_set(spinner, "xalign", 1.0, NULL);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), spinner);
return spinner;
}
GtkWidget *
make_scrolled_window_ns(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy)
{
GtkWidget *scrollwin;
scrollwin = gtk_scrolled_window_new(NULL,NULL);
//gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), hscrollbar_policy, vscrollbar_policy);
return scrollwin;
}
GtkWidget *
make_scrolled_window(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy)
{
GtkWidget *scrollwin;
scrollwin = gtk_scrolled_window_new(NULL,NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), hscrollbar_policy, vscrollbar_policy);
return scrollwin;
}
/*
**
*/
GtkWidget *make_scale(GtkWidget *label)
{
GtkWidget *scale;
scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, GTK_CHART_MINBARW, GTK_CHART_SPANBARW, 1.0);
gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
gtk_range_set_value(GTK_RANGE(scale), GTK_CHART_BARW);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), scale);
return scale;
}
/*
**
*/
GtkWidget *make_long(GtkWidget *label)
{
GtkWidget *spinner;
spinner = make_numeric(label, 0.0, G_MAXINT);
return spinner;
}
static gint
hbtk_monthyear_spin_input (GtkSpinButton *spin_button,
gdouble *new_val)
{
const gchar *text;
gchar **str;
gboolean found = FALSE;
gint month = 0;
gint year;
gchar *endm;
gint i;
gchar *tmp1, *tmp2;
text = gtk_entry_get_text (GTK_ENTRY (spin_button));
str = g_strsplit (text, " ", 2);
if (g_strv_length (str) == 2)
{
//month = strtol (str[0], &endh, 10);
for (i = 0; i < 12; i++)
{
tmp1 = g_ascii_strup (_(CYA_ABMONTHS[i+1]), -1);
tmp2 = g_ascii_strup (str[0], -1);
if (strstr (tmp1, tmp2) == tmp1)
{
found = TRUE;
month = i;
}
g_free (tmp1);
g_free (tmp2);
if (found)
break;
}
year = strtol (str[1], &endm, 10) - 1900;
//g_print(" input: m=%d y=%d => %d\n", month, year, month + year * 12);
if (found && !*endm && 0 <= month && month < 12 )
{
*new_val = month + (year * 12);
found = TRUE;
//g_print(" affect newval %f\n", *new_val);
}
}
g_strfreev (str);
if (!found)
{
*new_val = 0.0;
return GTK_INPUT_ERROR;
}
return TRUE;
}
static gint
hbtk_monthyear_spin_output (GtkSpinButton *spin_button)
{
GtkAdjustment *adjustment;
gchar *buf;
gint month;
gint year;
gint retval = TRUE;
adjustment = gtk_spin_button_get_adjustment (spin_button);
month = ((gint)gtk_adjustment_get_value (adjustment) % 12);
year = (gint)gtk_adjustment_get_value (adjustment) / 12.0;
//g_print(" output: %d => m:%d y:%d\n", (gint)gtk_adjustment_get_value (adjustment), month, year);
buf = g_strdup_printf ("%s %04d", _(CYA_ABMONTHS[month+1]), year+1900);
//g_signal_handlers_block_by_func(spin_button, time_spin_input, NULL);
if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
{
//g_print(" update text '%s'\n", buf);
gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
retval = TRUE;
}
//g_signal_handlers_unblock_by_func(spin_button, time_spin_input, NULL);
g_free (buf);
return retval;
}
static guint32 hbtk_monthyear_get_internal(GtkSpinButton *spin, guint type)
{
GDate date;
guint month, year;
gint val;
if(!GTK_IS_SPIN_BUTTON(spin))
return GLOBALS->today;
val = gtk_spin_button_get_value_as_int(spin);
year = 1900 + (val / 12);
month = (val % 12) + 1;
g_date_clear(&date, 1);
g_date_set_year(&date, year);
g_date_set_month(&date, month);
if(type == 0)
g_date_set_day(&date, 1);
else
g_date_set_day(&date, g_date_get_days_in_month(month, year));
return g_date_get_julian(&date);
}
guint32 hbtk_monthyear_getmin(GtkSpinButton *spin)
{
return hbtk_monthyear_get_internal(spin, 0);
}
guint32 hbtk_monthyear_getmax(GtkSpinButton *spin)
{
return hbtk_monthyear_get_internal(spin, 1);
}
void hbtk_monthyear_set(GtkSpinButton *spin, guint32 julian)
{
GDate date;
gdouble newval;
if(!GTK_IS_SPIN_BUTTON(spin))
return;
g_date_set_julian(&date, julian);
newval = (g_date_get_month(&date)-1) + (g_date_get_year(&date) - 1900) * 12;
gtk_spin_button_set_value(spin, newval);
}
GtkWidget *make_monthyear(GtkWidget *label)
{
GtkWidget *spinner;
GtkAdjustment *adj;
adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, (2200-1900)*12, 1, 12, 0);
//adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 14010, 30, 60, 0);
spinner = gtk_spin_button_new (adj, 0, 0);
//gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
//gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
//g_object_set(spinner, "xalign", 1.0, NULL);
//#2081574 width too small
gtk_entry_set_width_chars(GTK_ENTRY(spinner), 10);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), spinner);
g_signal_connect (spinner, "output", G_CALLBACK (hbtk_monthyear_spin_output), NULL);
g_signal_connect (spinner, "input", G_CALLBACK (hbtk_monthyear_spin_input), NULL);
return spinner;
}
/*
GtkWidget *make_year(GtkWidget *label)
{
GtkWidget *spinner;
GtkAdjustment *adj;
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 1900, 2200, 1.0, 10.0, 0.0);
spinner = gtk_spin_button_new (adj, 0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
g_object_set(spinner, "xalign", 1.0, NULL);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), spinner);
return spinner;
}*/
GtkWidget *
create_popover (GtkWidget *parent,
GtkWidget *child,
GtkPositionType pos)
{
GtkWidget *popover;
popover = gtk_popover_new (parent);
gtk_popover_set_position (GTK_POPOVER (popover), pos);
gtk_popover_set_child (GTK_POPOVER(popover), child);
gtk_widget_show (child);
hb_widget_set_margin(child, SPACING_POPOVER);
return popover;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#ifdef G_OS_WIN32
static GtkWidget *hbtk_container_get_children_named(GtkContainer *container, gchar *buildname)
{
GList *lchild, *list;
GtkWidget *widget = NULL;
gint i;
if(!GTK_IS_CONTAINER(container))
return NULL;
lchild = list = gtk_container_get_children (container);
for(i=0;list != NULL;i++)
{
if( hb_string_ascii_compare(buildname, (gchar *)gtk_buildable_get_name(list->data)) == 0 )
{
widget = list->data;
break;
}
list = g_list_next(list);
}
g_list_free(lchild);
return widget;
}
void hbtk_assistant_hack_button_order(GtkAssistant *assistant)
{
GtkWidget *bmain, *bchild, *barea, *bbutton;
//main_box
bmain = gtk_bin_get_child(GTK_BIN(assistant));
if( !bmain ) return;
DB( g_print(" got %s\n", gtk_buildable_get_name(bmain)) );
bchild = hbtk_container_get_children_named(GTK_CONTAINER(bmain), "content_box");
if( !bchild ) return;
DB( g_print(" got %s\n", gtk_buildable_get_name(bchild)) );
barea = hbtk_container_get_children_named(GTK_CONTAINER(bchild), "action_area");
if( !barea ) return;
DB( g_print(" got %s\n", gtk_buildable_get_name(barea)) );
//assistant buttonbox is GTK_PACK_END
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "back");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 5);
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "forward");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 4);
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "apply");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 3);
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "last");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 2);
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "cancel");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 1);
bbutton = hbtk_container_get_children_named(GTK_CONTAINER(barea), "close");
if(bbutton) gtk_box_reorder_child(GTK_BOX(barea), bbutton, 0);
}
#endif
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
#define HB_KV_BUFFER_MAX_LEN 16
#define HB_KV_ITEMS_MAX_LEN 32
gchar *hbtk_get_label(HbKvData *kvdata, guint32 key)
{
gchar *retval = NULL;
guint32 i;
for(i=0;iname == NULL )
break;
if( tmp->key == key )
{
//#1820372
retval = (gchar *)_(tmp->name);
break;
}
}
return retval;
}
static gboolean hbtk_combo_box_is_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
//GtkTreePath *path;
gboolean retval;
gchar *txt;
gtk_tree_model_get (model, iter, 0, &txt, -1);
retval = *txt == 0 ? TRUE : FALSE;
//leak
g_free(txt);
return retval;
}
guint32 hbtk_combo_box_get_active_id (GtkComboBox *combobox)
{
const gchar* buf;
guint32 retval;
buf = gtk_combo_box_get_active_id(GTK_COMBO_BOX(combobox));
retval = buf != NULL ? atoi(buf) : 0;
return retval;
}
void hbtk_combo_box_set_active_id (GtkComboBox *combobox, guint32 key)
{
gchar buf[HB_KV_BUFFER_MAX_LEN];
g_snprintf(buf, HB_KV_BUFFER_MAX_LEN-1, "%d", key);
gtk_combo_box_set_active_id(GTK_COMBO_BOX(combobox), buf);
}
void hbtk_combo_box_text_append (GtkComboBox *combobox, guint32 key, gchar *text)
{
gchar buf[HB_KV_BUFFER_MAX_LEN];
g_snprintf(buf, HB_KV_BUFFER_MAX_LEN-1, "%d", key);
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combobox), buf, text);
}
GtkWidget *hbtk_combo_box_new (GtkWidget *label)
{
GtkWidget *combobox;
GList *renderers, *list;
combobox = gtk_combo_box_text_new();
//#2131030 add ellipsise to renderer for template
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(combobox));
if(g_list_length(renderers) == 1)
{
list = g_list_first(renderers);
if(GTK_IS_CELL_RENDERER(list->data))
{
g_object_set(GTK_CELL_RENDERER(list->data),
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
NULL);
}
}
g_list_free(renderers);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), combobox);
return combobox;
}
GtkWidget *hbtk_combo_box_new_with_data (GtkWidget *label, HbKvData *kvdata)
{
GtkWidget *combobox = hbtk_combo_box_new(label);
HbKvData *tmp;
gboolean hassep;
guint32 i;
hassep = FALSE;
for(i=0;iname == NULL )
break;
//no gettext for separator
if( *tmp->name != 0 )
{
hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), tmp->key, (gchar *)_(tmp->name));
}
else
{
hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), tmp->key, (gchar *)"");
hassep = TRUE;
}
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
if(hassep)
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combobox), hbtk_combo_box_is_separator, NULL, NULL);
return combobox;
}
GtkWidget *hbtk_combo_box_new_with_array (GtkWidget *label, gchar **items)
{
GtkWidget *combobox = hbtk_combo_box_new(label);
guint32 i;
for (i = 0; items[i] != NULL; i++)
{
if(*items[i] != 0)
hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), i, (gchar *)_(items[i]));
else
hbtk_combo_box_text_append(GTK_COMBO_BOX(combobox), i, (gchar *)"");
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
return combobox;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void
make_daterange_set_sensitive (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
gpointer data)
{
gint id_column = GPOINTER_TO_INT(data);
gboolean sensitive;
gchar *textid;
gint id;
g_return_if_fail (id_column >= 0);
gtk_tree_model_get (tree_model, iter, id_column, &textid, -1);
id = atoi(textid);
sensitive = (id != FLT_RANGE_MISC_CUSTOM);
g_object_set (cell, "sensitive", sensitive, NULL);
g_free(textid);
}
static void make_daterange_fill_items(GtkComboBox *combo_box, HbKvData *kvdata)
{
HbKvData *tmp;
guint32 i;
for(i=0;iname == NULL )
break;
//no gettext for separator
hbtk_combo_box_text_append(GTK_COMBO_BOX(combo_box), tmp->key, (*tmp->name != 0) ? (gchar *)_(tmp->name) : (gchar *)"");
}
}
GtkWidget *make_daterange(GtkWidget *label, HbDateRangeFlags flags)
{
GtkWidget *combo_box;
GList *renderers, *list;
combo_box = hbtk_combo_box_new(label);
if( !(flags & DATE_RANGE_FLAG_BUDGET_MODE) )
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_DWF);
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_MQY);
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_YTO);
if( !(flags & DATE_RANGE_FLAG_BUDGET_MODE) )
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_LASTXXD);
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_COMMON);
if( !(flags & DATE_RANGE_FLAG_CUSTOM_HIDDEN) )
make_daterange_fill_items(GTK_COMBO_BOX(combo_box), CYA_FLT_RANGE_CUSTOM);
if( flags & DATE_RANGE_FLAG_CUSTOM_DISABLE )
{
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(combo_box));
if(g_list_length(renderers) == 1)
{
gint id_column = gtk_combo_box_get_id_column (GTK_COMBO_BOX (combo_box));
list = g_list_first(renderers);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
list->data,
make_daterange_set_sensitive,
GINT_TO_POINTER(id_column),
NULL);
}
g_list_free(renderers);
}
//TODO: option removed into GTK4 ??
gtk_combo_box_set_wrap_width (GTK_COMBO_BOX(combo_box), 3);
//gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), FLT_RANGE_MISC_ALLDATE);
//5.8.6 leave unset to trigger a change
//hbtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), FLT_RANGE_MISC_ALLDATE);
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box), hbtk_combo_box_is_separator, NULL, NULL);
return combo_box;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
// kiv common
enum
{
LST_KIV_KEY,
LST_KIV_ICONNAME,
LST_KIV_LABEL,
LST_KIV_ONOFF,
NUM_LST_KIV
};
static GtkListStore *
kiv_store_new(void)
{
GtkListStore *store = gtk_list_store_new (
NUM_LST_KIV,
G_TYPE_INT,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
return store;
}
static gboolean
kiv_combo_box_is_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
gint key;
gtk_tree_model_get (model, iter, LST_KIV_KEY, &key, -1);
return (key == HBTK_IS_SEPARATOR ? TRUE : FALSE);
}
static GtkWidget *
kiv_combo_box_new_with_model(GtkTreeModel *model, GtkWidget *label)
{
GtkWidget *widget;
GtkCellRenderer *renderer;
widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
//leak
g_object_unref(model);
renderer = gtk_cell_renderer_pixbuf_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget), renderer, "icon-name", LST_KIV_ICONNAME);
renderer = gtk_cell_renderer_text_new();
g_object_set(renderer, "xpad", SPACING_SMALL, NULL);
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget), renderer, "text", LST_KIV_LABEL);
gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
if(label)
gtk_label_set_mnemonic_widget (GTK_LABEL(label), widget);
return widget;
}
guint32
kiv_combo_box_get_active (GtkComboBox *combo_box)
{
GtkTreeModel *model = gtk_combo_box_get_model (combo_box);
GtkTreeIter iter;
if (gtk_combo_box_get_active_iter (combo_box, &iter))
{
gint key;
gtk_tree_model_get (model, &iter, LST_KIV_KEY, &key, -1);
return (guint32)key;
}
return 0;
}
void
kiv_combo_box_set_active (GtkComboBox *combo_box, guint32 active_key)
{
GtkTreeModel *model = gtk_combo_box_get_model (combo_box);
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
{
gint key;
gtk_tree_model_get (model, &iter, LST_KIV_KEY, &key, -1);
if (key == (gint)active_key)
{
gtk_combo_box_set_active_iter (combo_box, &iter);
break;
}
} while (gtk_tree_model_iter_next (model, &iter));
}
}
static HbKivData *
hb_kvidata_get_by_key(HbKivData *kivdata, guint32 key)
{
HbKivData *tmp = NULL;
guint32 i;
for(i=0;iname == NULL )
break;
if( tmp->key == key )
break;
}
return tmp;
}
HbKivData CYA_TXN_GRPFLAG[NUM_GRPFLAG+3] =
{
{ GRPFLAG_ANY, NULL, N_("Any flag") },
{ GRPFLAG_RED, "hb-gf-red", N_("Red") },
{ GRPFLAG_ORANGE, "hb-gf-orange", N_("Orange") },
{ GRPFLAG_YELLOW, "hb-gf-yellow", N_("Yellow") },
{ GRPFLAG_GREEN, "hb-gf-green", N_("Green") },
{ GRPFLAG_BLUE, "hb-gf-blue", N_("Blue") },
{ GRPFLAG_PURPLE, "hb-gf-purple", N_("Purple") },
{ HBTK_IS_SEPARATOR, NULL, "" },
{ GRPFLAG_NONE, NULL, N_("No Flag") },
{ 0, NULL , NULL }
};
const gchar *get_grpflag_icon_name(guint32 key)
{
HbKivData *tmp = hb_kvidata_get_by_key(CYA_TXN_GRPFLAG, key);
return (tmp != NULL) ? tmp->iconname : NULL;
}
GtkWidget *make_fltgrpflag(GtkWidget *label)
{
GtkWidget *combo_box;
HbKivData *tmp = NULL;
GtkTreeIter iter;
guint32 i;
GtkListStore *store = kiv_store_new();
combo_box = kiv_combo_box_new_with_model(GTK_TREE_MODEL(store), label);
for(i=0;iname == NULL )
break;
//no gettext for separator
//#2116088 ledger window quickfilter bar abnormal height
label = (*tmp->name != 0) ? (gchar *)_(tmp->name) : (gchar *)"";
gtk_list_store_insert_with_values(store, &iter, -1,
LST_KIV_KEY, tmp->key,
LST_KIV_ICONNAME, tmp->iconname,
LST_KIV_LABEL, label,
-1);
}
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box), kiv_combo_box_is_separator, NULL, NULL);
kiv_combo_box_set_active(GTK_COMBO_BOX(combo_box), GRPFLAG_ANY);
return combo_box;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* OFX transactiontype
id ofx english french
---------------------------------------------------------------------
0 -------- (none) (aucun)
1 -------- credit card carte de crédit
2 OFX_CHECK Check cheque
3 OFX_CASH Cash withdrawal retrait espece
4 OFX_XFER Transfer virement
5 -------- internal transfer virement compte
6 -------- (debit card) (carte de paiement)
7 OFX_REPEATPMT Repeating payment/standing order Paiement recurrent/Virement auto.
8 OFX_PAYMENT Electronic payment télépaiement
9 OFX_DEP Deposit dépôt
10 OFX_FEE FI fee frais bancaires
OFX_DIRECTDEBIT Merchant initiated debit prelevement
OFX_OTHER Some other type of transaction autre
other OFX values:
OFX_CREDIT Generic credit
OFX_DEBIT Generic debit
OFX_INT Interest earned or paid (Note: Depends on signage of amount)
OFX_DIV Dividend
OFX_SRVCHG Service charge
-OFX_DEP Deposit
OFX_ATM ATM debit or credit (Note: Depends on signage of amount)
OFX_POS Point of sale debit or credit (Note: Depends on signage of amount)
-OFX_XFER Transfer
-OFX_CHECK Check
-OFX_PAYMENT Electronic payment
-OFX_CASH Cash withdrawal
OFX_DIRECTDEP Direct deposit
OFX_DIRECTDEBIT Merchant initiated debit
-OFX_REPEATPMT Repeating payment/standing order
OFX_OTHER Somer other type of transaction
*/
HbKivData CYA_TXN_PAYMODE[NUM_PAYMODE_MAX] =
{
{ PAYMODE_NONE, "hb-pm-none", N_("(none)") },
{ PAYMODE_CCARD, "hb-pm-ccard", N_("Credit card") },
{ PAYMODE_CHECK, "hb-pm-check", N_("Check") },
{ PAYMODE_CASH, "hb-pm-cash" , N_("Cash") },
{ PAYMODE_XFER, "hb-pm-transfer", N_("Bank Transfer") },
{ PAYMODE_DCARD, "hb-pm-dcard", N_("Debit card") },
{ PAYMODE_REPEATPMT, "hb-pm-standingorder", N_("Standing order") },
{ PAYMODE_EPAYMENT, "hb-pm-epayment", N_("Electronic payment") },
{ PAYMODE_DEPOSIT, "hb-pm-deposit", N_("Deposit") },
//TRANSLATORS: Financial institution fee
{ PAYMODE_FEE, "hb-pm-fifee", N_("FI fee") },
{ PAYMODE_DIRECTDEBIT, "hb-pm-directdebit", N_("Direct Debit") },
//#1817274
{ PAYMODE_MOBPHONE, "hb-pm-mobphone", N_("Mobile Phone") },
{ 0, NULL , NULL }
};
/* nota: used in ui-filter */
const gchar *get_paymode_icon_name(guint32 key)
{
HbKivData *tmp = NULL;
/*HbKivData *kivdata = CYA_TXN_PAYMODE;
guint32 i;
for(i=0;iname == NULL )
break;
if( tmp->key == key )
{
retval = tmp->iconname;
break;
}
}*/
tmp = hb_kvidata_get_by_key(CYA_TXN_PAYMODE, key);
return (tmp != NULL) ? tmp->iconname : NULL;
}
void paymode_list_get_order(GtkTreeView *treeview)
{
GtkTreeModel *model;
GtkTreeIter iter;
gint i = 1;
model = gtk_tree_view_get_model (treeview);
PREFS->lst_paymode[0] = PAYMODE_NONE;
if (gtk_tree_model_get_iter_first (model, &iter))
do {
guint32 key;
gboolean active;
gtk_tree_model_get (model, &iter,
LST_KIV_KEY, &key,
LST_KIV_ONOFF, &active,
-1);
PREFS->lst_paymode[i++] = (active == TRUE) ? key : -key;
} while (gtk_tree_model_iter_next (model, &iter));
}
static void
paymode_list_fixed_toggled (GtkCellRendererToggle *cell,
gchar *path_str,
gpointer data)
{
GtkTreeModel *model = (GtkTreeModel *)data;
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
gboolean fixed;
/* get toggled iter */
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, LST_KIV_ONOFF, &fixed, -1);
/* do something with the value */
fixed ^= 1;
/* set new value */
gtk_list_store_set (GTK_LIST_STORE (model), &iter, LST_KIV_ONOFF, fixed, -1);
/* clean up */
gtk_tree_path_free (path);
}
static void
paymode_store_populate(GtkListStore *store, gboolean prefmode)
{
HbKivData *tmp;
GtkTreeIter iter;
guint i;
//populate our combobox model
for(i=0 ; ilst_paymode[i];
//hide None for prefmode
if( prefmode == TRUE && key == 0 )
continue;
tmp = hb_kvidata_get_by_key(CYA_TXN_PAYMODE, ABS(key));
if( tmp != NULL )
{
if( prefmode == FALSE && key < 0 )
continue;
gtk_list_store_insert_with_values(store, &iter, -1,
LST_KIV_KEY, tmp->key,
LST_KIV_ICONNAME, tmp->iconname,
LST_KIV_LABEL, _(tmp->name),
LST_KIV_ONOFF, key > 0 ? TRUE : FALSE,
-1);
}
}
}
GtkWidget *make_paymode_list(void)
{
GtkListStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
store = kiv_store_new();
paymode_store_populate(store, TRUE);
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
/* column for fixed toggles */
renderer = gtk_cell_renderer_toggle_new ();
g_signal_connect (renderer, "toggled", G_CALLBACK (paymode_list_fixed_toggled), store);
column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "active", LST_KIV_ONOFF, NULL);
gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), GTK_TREE_VIEW_COLUMN_FIXED);
//gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
renderer = gtk_cell_renderer_pixbuf_new ();
column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "icon-name", LST_KIV_ICONNAME, NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_KIV_ICONNAME);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column for description */
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", LST_KIV_LABEL, NULL);
//gtk_tree_view_column_set_sort_column_id (column, LST_KIV_LABEL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW(treeview), TRUE);
return GTK_WIDGET(treeview);
}
/*
** Make a paymode combobox widget
*/
GtkWidget *make_paymode(GtkWidget *label)
{
GtkListStore *store = kiv_store_new();
paymode_store_populate(store, FALSE);
return kiv_combo_box_new_with_model(GTK_TREE_MODEL(store), label);
}
homebank-5.9.7/src/hub-transaction.c 0000664 0001750 0001750 00000011376 15005634227 016664 0 ustar franam franam /* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2025 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* HomeBank is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "homebank.h"
#include "hub-transaction.h"
#include "hub-account.h"
#include "dsp-mainwindow.h"
#include "list-operation.h"
#include "ui-transaction.h"
#include "ui-widgets.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
void ui_hub_transaction_populate(struct hbfile_data *data)
{
GList *lst_acc, *lnk_acc;
GList *lnk_txn;
GtkTreeModel *model1, *model2;
GtkTreeIter iter;
DB( g_print("\n[ui_hub_txn] populate\n") );
model1 = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_FUTURE]));
model2 = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_txn[HUB_TXN_TYPE_REMIND]));
gtk_tree_store_clear (GTK_TREE_STORE(model1));
gtk_tree_store_clear (GTK_TREE_STORE(model2));
lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
lnk_acc = g_list_first(lst_acc);
while (lnk_acc != NULL)
{
Account *acc = lnk_acc->data;
// skip closed in showall mode
if( (acc->flags & AF_CLOSED) )
goto next_acc;
lnk_txn = g_queue_peek_head_link(acc->txn_queue);
while (lnk_txn != NULL)
{
Transaction *txn = lnk_txn->data;
if(txn->date > GLOBALS->today)
{
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model1), &iter, NULL, -1,
MODEL_TXN_POINTER, txn,
MODEL_TXN_SPLITAMT, txn->amount,
-1);
}
//if(txn->status == TXN_STATUS_REMIND)
if(txn->flags & OF_REMIND)
{
gtk_tree_store_insert_with_values(GTK_TREE_STORE(model2), &iter, NULL, -1,
MODEL_TXN_POINTER, txn,
MODEL_TXN_SPLITAMT, txn->amount,
-1);
}
lnk_txn = g_list_next(lnk_txn);
}
next_acc:
lnk_acc = g_list_next(lnk_acc);
}
g_list_free(lst_acc);
}
static void ui_hub_transaction_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
struct hbfile_data *data = userdata;
Transaction *active_txn;
gboolean result;
DB( g_print ("\n[ui_hub_txn] row double-clicked\n") );
active_txn = list_txn_get_active_transaction(treeview);
if(active_txn)
{
Transaction *old_txn, *new_txn;
old_txn = da_transaction_clone (active_txn);
new_txn = active_txn;
result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
if(result == GTK_RESPONSE_ACCEPT)
{
//#1640885
GLOBALS->changes_count++;
ui_hub_transaction_populate(data);
//#1824515 when amount change update acc panel
//#1845839 every change should update
//if( old_txn->amount != new_txn->amount )
ui_hub_account_compute(GLOBALS->mainwindow, NULL);
//#1830880 update mainwindow
ui_wallet_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
}
da_transaction_free (old_txn);
}
}
GtkWidget *ui_hub_transaction_create(struct hbfile_data *data, HbHubTxnType type)
{
GtkWidget *hub, *vbox, *scrollwin, *treeview;
DB( g_print("\n[ui_hub_txn] create %d\n", type) );
if( type > HUB_TXN_TYPE_REMIND )
return NULL;
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hb_widget_set_margin(GTK_WIDGET(hub), SPACING_SMALL);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbtk_box_prepend (GTK_BOX (hub), vbox);
scrollwin = make_scrolled_window(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
hbtk_box_prepend (GTK_BOX (vbox), scrollwin);
treeview = (GtkWidget *)create_list_transaction(LIST_TXN_TYPE_OTHER, PREFS->lst_ope_columns);
list_txn_set_column_acc_visible(GTK_TREE_VIEW(treeview), TRUE);
data->LV_txn[type] = treeview;
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(scrollwin), treeview);
g_signal_connect (GTK_TREE_VIEW(data->LV_txn[type]), "row-activated", G_CALLBACK (ui_hub_transaction_onRowActivated), data);
return hub;
}
homebank-5.9.7/NEWS 0000644 0001750 0001750 00000000247 11023413024 013302 0 ustar franam franam HomeBank -- Free, easy, personal accounting for everyone.
---------------------------------------------------------
Every changes can be read in the ChangeLog file.
homebank-5.9.7/INSTALL 0000664 0001750 0001750 00000022450 11413604274 013652 0 ustar franam franam Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
6. Often, you can also type `make uninstall' to remove the installed
files again.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
homebank-5.9.7/images/ 0000775 0001750 0001750 00000000000 15123472257 014071 5 ustar franam franam homebank-5.9.7/images/splash.png 0000644 0001750 0001750 00000223043 11023413026 016053 0 ustar franam franam PNG
IHDR ) O! sBIT|d pHYs
oy tEXtSoftware www.inkscape.org< IDATxkh)9N'S;=y؎86_N-$mQӳV;>ص>}p>})sRW9\}/:2<ڣ][> wqm)}k;߭e})s{Uʹ'c]u
i%]'lc2Oģ鿐/'c]{s)-&GyboH!}%}k!}vQ/'13O!])|v/Z!ģp>>ص>_HߵQ_Ϳ.6J!}%}k!}WZ_LJP?~kq|R?صk=j_%'.¿|Tv_u=jOQҿڿz{v }ADknmH+MBҿڿz{vq>E
ZJhE_HkQ|R?صw@:̷O~`>avע?w_I!ͩD@zdč~!,J<=>
DGXF]_H+}cE~*'|+h3Bf'Bop/V?
`>5F}.sO.-v>',g>,6?"-)_έ_H1^_z/K"碆wC10\%\f5*mH+Oαۯ/]=wZoAC%qf78G{/~k{dŏ"cw@˲ו1}<j-?v4*wI9<#r0*S@7>ص=BflA_s
_X1=r
? ^|9q{G]v w m|䱿`~۵>76kHWԯvi\89E{o³N@h~O%th/rrό+
hg/sMA ~{!}k3/pdM[ل {曧7
Bd+C$a}ҫv;ݗ-/=:_Hg)~ģViUe FYzkF\PۧkN~"k;̵ܽ)ŜYp SP_^t:R73o./~XK
߳:ղ