mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Merge branch 'maint'
* Bug 711440 - Tab labels have different background colour than containing gui element * Bug 434462 - register color don't work correct with system theme color * Add scrub function for (business) lots to eliminate unneeded lot links * Rewrite invoice payment logic to use lot links only when absolutely needed * Bug 684719 - Man pages for gnc-fq-* perl scripts
This commit is contained in:
commit
4ecc4cb7d0
@ -154,6 +154,7 @@ void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *pat
|
||||
GtkTreeViewColumn *column, PaymentWindow *pw);
|
||||
void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
|
||||
PaymentWindow *pw);
|
||||
void gnc_payment_window_fill_docs_list (PaymentWindow *pw);
|
||||
|
||||
|
||||
static void
|
||||
@ -161,6 +162,7 @@ gnc_payment_window_refresh_handler (GHashTable *changes, gpointer data)
|
||||
{
|
||||
PaymentWindow *pw = data;
|
||||
|
||||
gnc_payment_window_fill_docs_list (pw);
|
||||
pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
|
||||
}
|
||||
|
||||
@ -324,6 +326,8 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
|
||||
lot = (GNCLot *) g_value_get_pointer (&value);
|
||||
g_value_unset (&value);
|
||||
|
||||
if (!lot)
|
||||
continue; /* Lot has been deleted behind our back... */
|
||||
|
||||
invoice = gncInvoiceGetInvoiceFromLot (lot);
|
||||
if (!invoice)
|
||||
@ -340,7 +344,7 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
gnc_payment_window_fill_docs_list (PaymentWindow *pw)
|
||||
{
|
||||
GtkListStore *store;
|
||||
@ -1102,37 +1106,13 @@ gnc_ui_payment_new (GncOwner *owner, QofBook *book)
|
||||
}
|
||||
|
||||
// ////////////////////////////////////////////////////////////
|
||||
|
||||
static gboolean isAssetLiabType(GNCAccountType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ACCT_TYPE_RECEIVABLE:
|
||||
case ACCT_TYPE_PAYABLE:
|
||||
return FALSE;
|
||||
default:
|
||||
return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
|
||||
|| xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
|
||||
}
|
||||
}
|
||||
static gboolean isAPARType(GNCAccountType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ACCT_TYPE_RECEIVABLE:
|
||||
case ACCT_TYPE_PAYABLE:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
static void increment_if_asset_account (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
int *r = user_data;
|
||||
const Split *split = data;
|
||||
const Account *account = xaccSplitGetAccount(split);
|
||||
if (isAssetLiabType(xaccAccountGetType(account)))
|
||||
if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
|
||||
++(*r);
|
||||
}
|
||||
static int countAssetAccounts(SplitList* slist)
|
||||
@ -1147,7 +1127,7 @@ static gint predicate_is_asset_account(gconstpointer a,
|
||||
{
|
||||
const Split *split = a;
|
||||
const Account *account = xaccSplitGetAccount(split);
|
||||
if (isAssetLiabType(xaccAccountGetType(account)))
|
||||
if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
@ -1157,7 +1137,7 @@ static gint predicate_is_apar_account(gconstpointer a,
|
||||
{
|
||||
const Split *split = a;
|
||||
const Account *account = xaccSplitGetAccount(split);
|
||||
if (isAPARType(xaccAccountGetType(account)))
|
||||
if (xaccAccountIsAPARType(xaccAccountGetType(account)))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
|
@ -49,17 +49,6 @@ typedef enum
|
||||
GNCENTRY_NUM_REGISTER_TYPES
|
||||
} GncEntryLedgerType;
|
||||
|
||||
typedef struct entry_ledger_colors
|
||||
{
|
||||
guint32 header_bg_color;
|
||||
|
||||
guint32 primary_bg_color;
|
||||
guint32 secondary_bg_color;
|
||||
|
||||
guint32 primary_active_bg_color;
|
||||
guint32 secondary_active_bg_color;
|
||||
} GncEntryLedgerColors;
|
||||
|
||||
#define ENTRY_IACCT_CELL "inv-account"
|
||||
#define ENTRY_BACCT_CELL "bill-account"
|
||||
#define ENTRY_ACTN_CELL "action"
|
||||
|
@ -36,15 +36,6 @@
|
||||
#include "gncEntryLedgerP.h"
|
||||
#include "gncEntryLedgerModel.h"
|
||||
|
||||
static GncEntryLedgerColors reg_colors =
|
||||
{
|
||||
0x96B183,
|
||||
0xBFDEB9,
|
||||
0xF6FFDA,
|
||||
|
||||
0xFFEF98,
|
||||
0xFFEF98,
|
||||
};
|
||||
|
||||
/** Private Interfaces ***********************************************/
|
||||
|
||||
@ -926,43 +917,85 @@ static CellIOFlags get_qty_io_flags (VirtualLocation virt_loc, gpointer user_dat
|
||||
|
||||
/* GET BG_COLORS */
|
||||
|
||||
static guint32
|
||||
gnc_entry_ledger_get_color_internal (VirtualLocation virt_loc,
|
||||
GncEntryLedger *ledger,
|
||||
const guint32 *color_table,
|
||||
gboolean foreground)
|
||||
{
|
||||
const char *cursor_name;
|
||||
VirtualCell *vcell;
|
||||
gboolean is_current;
|
||||
guint32 colorbase = 0; /* By default return background colors */
|
||||
|
||||
if (foreground)
|
||||
colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
|
||||
|
||||
if (!ledger)
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
if (gnc_table_virtual_location_in_header (ledger->table, virt_loc))
|
||||
return color_table[colorbase + COLOR_HEADER_BG];
|
||||
|
||||
vcell = gnc_table_get_virtual_cell (ledger->table, virt_loc.vcell_loc);
|
||||
if (!vcell || !vcell->cellblock)
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
is_current = virt_cell_loc_equal (ledger->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc);
|
||||
|
||||
if (is_current)
|
||||
return vcell->start_primary_color ?
|
||||
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||
|
||||
return vcell->start_primary_color ?
|
||||
color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
|
||||
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_entry_ledger_get_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
{
|
||||
GncEntryLedger *ledger = user_data;
|
||||
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, TRUE);
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_entry_ledger_get_gtkrc_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
{
|
||||
GncEntryLedger *ledger = user_data;
|
||||
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, TRUE);
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_entry_ledger_get_bg_color (VirtualLocation virt_loc,
|
||||
gboolean *hatching, gpointer user_data)
|
||||
{
|
||||
GncEntryLedger *ledger = user_data;
|
||||
VirtualCell *vcell;
|
||||
guint32 bg_color;
|
||||
gboolean is_current;
|
||||
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
bg_color = 0xffffff; /* white */
|
||||
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, FALSE);
|
||||
}
|
||||
|
||||
if (!ledger) return bg_color;
|
||||
static guint32
|
||||
gnc_entry_ledger_get_gtkrc_bg_color (VirtualLocation virt_loc,
|
||||
gboolean *hatching, gpointer user_data)
|
||||
{
|
||||
GncEntryLedger *ledger = user_data;
|
||||
|
||||
if (gnc_table_virtual_location_in_header (ledger->table, virt_loc))
|
||||
return reg_colors.header_bg_color;
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
vcell = gnc_table_get_virtual_cell (ledger->table, virt_loc.vcell_loc);
|
||||
if (!vcell || !vcell->cellblock)
|
||||
return bg_color;
|
||||
|
||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||
return bg_color;
|
||||
|
||||
is_current = virt_cell_loc_equal
|
||||
(ledger->table->current_cursor_loc.vcell_loc, virt_loc.vcell_loc);
|
||||
|
||||
if (is_current)
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_active_bg_color :
|
||||
reg_colors.secondary_active_bg_color;
|
||||
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
|
||||
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, FALSE);
|
||||
}
|
||||
|
||||
/* SAVE CELLS */
|
||||
@ -1214,9 +1247,18 @@ static void gnc_entry_ledger_model_new_handlers (TableModel *model,
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
gnc_table_model_set_default_fg_color_handler
|
||||
(model, gnc_entry_ledger_get_fg_color);
|
||||
|
||||
gnc_table_model_set_fg_color_handler
|
||||
(model, gnc_entry_ledger_get_gtkrc_fg_color, "gtkrc");
|
||||
|
||||
gnc_table_model_set_default_bg_color_handler
|
||||
(model, gnc_entry_ledger_get_bg_color);
|
||||
|
||||
gnc_table_model_set_bg_color_handler
|
||||
(model, gnc_entry_ledger_get_gtkrc_bg_color, "gtkrc");
|
||||
|
||||
|
||||
for (i = 0; i < (sizeof(models) / sizeof(*models)); i++)
|
||||
{
|
||||
|
@ -4208,6 +4208,30 @@ xaccAccountTypesValid(void)
|
||||
return mask;
|
||||
}
|
||||
|
||||
gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ACCT_TYPE_RECEIVABLE:
|
||||
case ACCT_TYPE_PAYABLE:
|
||||
return FALSE;
|
||||
default:
|
||||
return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
|
||||
|| xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
|
||||
}
|
||||
}
|
||||
gboolean xaccAccountIsAPARType(GNCAccountType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ACCT_TYPE_RECEIVABLE:
|
||||
case ACCT_TYPE_PAYABLE:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccAccountIsPriced(const Account *acc)
|
||||
{
|
||||
|
@ -943,6 +943,16 @@ gboolean xaccAccountTypesCompatible (GNCAccountType parent_type,
|
||||
* root account types are stripped. */
|
||||
guint32 xaccAccountTypesValid(void);
|
||||
|
||||
/** Convenience function to check if the account is a valid
|
||||
* Asset or Liability type, but not a business account type
|
||||
* (meaning not an Accounts Payable/Accounts Receivable). */
|
||||
gboolean xaccAccountIsAssetLiabType(GNCAccountType t);
|
||||
|
||||
/** Convenience function to check if the account is a valid
|
||||
* business account type
|
||||
* (meaning an Accounts Payable/Accounts Receivable). */
|
||||
gboolean xaccAccountIsAPARType(GNCAccountType t);
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
|
@ -22,6 +22,7 @@ libgncmod_engine_la_SOURCES = \
|
||||
Scrub.c \
|
||||
Scrub2.c \
|
||||
Scrub3.c \
|
||||
ScrubBusiness.c \
|
||||
Split.c \
|
||||
TransLog.c \
|
||||
Transaction.c \
|
||||
@ -70,6 +71,7 @@ gncinclude_HEADERS = \
|
||||
Scrub.h \
|
||||
Scrub2.h \
|
||||
Scrub3.h \
|
||||
ScrubBusiness.h \
|
||||
Split.h \
|
||||
TransLog.h \
|
||||
Transaction.h \
|
||||
|
@ -529,7 +529,10 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
|
||||
/* Return immediately if things are balanced. */
|
||||
if (xaccTransIsBalanced (trans))
|
||||
{
|
||||
LEAVE ("transaction is balanced");
|
||||
return;
|
||||
}
|
||||
|
||||
currency = xaccTransGetCurrency (trans);
|
||||
|
||||
@ -626,7 +629,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
||||
imbal_list = xaccTransGetImbalance (trans);
|
||||
if (!imbal_list)
|
||||
{
|
||||
LEAVE("()");
|
||||
LEAVE("transaction is balanced");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ merge_splits (Split *sa, Split *sb)
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccScrubMergeSubSplits (Split *split)
|
||||
xaccScrubMergeSubSplits (Split *split, gboolean strict)
|
||||
{
|
||||
gboolean rc = FALSE;
|
||||
Transaction *txn;
|
||||
@ -352,7 +352,7 @@ xaccScrubMergeSubSplits (Split *split)
|
||||
GNCLot *lot;
|
||||
const GncGUID *guid;
|
||||
|
||||
if (FALSE == is_subsplit (split)) return FALSE;
|
||||
if (strict && (FALSE == is_subsplit (split))) return FALSE;
|
||||
|
||||
txn = split->parent;
|
||||
lot = xaccSplitGetLot (split);
|
||||
@ -392,7 +392,7 @@ restart:
|
||||
}
|
||||
|
||||
gboolean
|
||||
xaccScrubMergeLotSubSplits (GNCLot *lot)
|
||||
xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
|
||||
{
|
||||
gboolean rc = FALSE;
|
||||
SplitList *node;
|
||||
@ -404,7 +404,7 @@ restart:
|
||||
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
|
||||
{
|
||||
Split *s = node->data;
|
||||
if (!xaccScrubMergeSubSplits(s)) continue;
|
||||
if (!xaccScrubMergeSubSplits(s, strict)) continue;
|
||||
|
||||
rc = TRUE;
|
||||
goto restart;
|
||||
|
@ -53,7 +53,7 @@
|
||||
* not in a lot will be used to close the oldest open lot(s).
|
||||
* If there are no open lots, a new lot will be started.
|
||||
* By trying to close the oldest lots, this effectively
|
||||
* implements a FIFO acounting policy.
|
||||
* implements a FIFO accounting policy.
|
||||
*/
|
||||
void xaccAccountAssignLots (Account *acc);
|
||||
|
||||
@ -89,16 +89,26 @@ void xaccLotScrubDoubleBalance (GNCLot *lot);
|
||||
* the same lot, or in no lot. Note that, by definition, all
|
||||
* subsplits belong to the same transaction.
|
||||
*
|
||||
* There are two ways to find matching subsplits. The first
|
||||
* way will consider splits to be subsplits only if they
|
||||
* are explicitly marked as such while splitting the original
|
||||
* split. Set strict to TRUE for this matching algorhythm.
|
||||
*
|
||||
* The second way is more relaxed. It will consider any two
|
||||
* splits that happen to be part of the same lot and the
|
||||
* same transaction to be subsplits. Set strict to FALSE for
|
||||
* this matching algorhythm.
|
||||
*
|
||||
* The routine returns TRUE if a merger was performed, else
|
||||
* it returns FALSE.
|
||||
*
|
||||
* The xaccScrubMergeTransSubSplits() routine does the same, except
|
||||
* that it does it for all of the splits in the transaction.
|
||||
* The xaccScrubMergeLotSubSplits() routine does the same, except
|
||||
* that it does it for all of the splits in the lot.
|
||||
*/
|
||||
gboolean xaccScrubMergeSubSplits (Split *split);
|
||||
gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
|
||||
gboolean xaccScrubMergeSubSplits (Split *split, gboolean strict);
|
||||
|
||||
/** The xaccScrubMergeLotSubSplits() routine does the same as
|
||||
* the xaccScrubMergSubSplits, except that it does it
|
||||
* for all of the splits in the lot.
|
||||
*/
|
||||
gboolean xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict);
|
||||
|
||||
#endif /* XACC_SCRUB2_H */
|
||||
/** @} */
|
||||
|
@ -96,7 +96,7 @@ xaccScrubLot (GNCLot *lot)
|
||||
acc = gnc_lot_get_account (lot);
|
||||
pcy = gnc_account_get_policy(acc);
|
||||
xaccAccountBeginEdit(acc);
|
||||
xaccScrubMergeLotSubSplits (lot);
|
||||
xaccScrubMergeLotSubSplits (lot, TRUE);
|
||||
|
||||
/* If the lot balance is zero, we don't need to rebalance */
|
||||
lot_baln = gnc_lot_get_balance (lot);
|
||||
@ -132,7 +132,7 @@ rethin:
|
||||
xaccLotFill (lot);
|
||||
|
||||
/* Make sure there are no subsplits. */
|
||||
splits_deleted = xaccScrubMergeLotSubSplits (lot);
|
||||
splits_deleted = xaccScrubMergeLotSubSplits (lot, TRUE);
|
||||
}
|
||||
|
||||
/* Now re-compute cap gains, and then double-check that.
|
||||
|
@ -25,7 +25,7 @@
|
||||
@{ */
|
||||
|
||||
/** @file Scrub3.h
|
||||
* @brief Hiogh-Level API for imposing Lot constraints
|
||||
* @brief High-Level API for imposing Lot constraints
|
||||
* @author Created by Linas Vepstas Sept 2003
|
||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||
*/
|
||||
@ -44,7 +44,7 @@
|
||||
* self-consistent and properly balanced, and fixes it if its not.
|
||||
* This is an important routine to call if the amount of any split
|
||||
* in the lot is changed. That's because (obviously) changing
|
||||
* split values is gaurenteed to throw off lot balances.
|
||||
* split values is guaranteed to throw off lot balances.
|
||||
* This routine may end up closing the lot, or at least trying
|
||||
* to. It will also cause cap gains to be recomputed.
|
||||
*
|
||||
|
333
src/engine/ScrubBusiness.c
Normal file
333
src/engine/ScrubBusiness.c
Normal file
@ -0,0 +1,333 @@
|
||||
/********************************************************************\
|
||||
* ScrubBusiness.h -- Cleanup functions for the business objects. *
|
||||
* *
|
||||
* This program 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. *
|
||||
* *
|
||||
* This program 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
/** @file ScrubBusiness.h
|
||||
* @brief Cleanup functions for business objects
|
||||
* @author Created by Geert Janssens August 2014
|
||||
* @author Copyright (c) 2014 Geert Janssens <geert@kobaltwit.be>
|
||||
*
|
||||
* Provides the high-level API for checking and repairing ('scrubbing
|
||||
* clean') the various data objects used by the business functions.*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-lot.h"
|
||||
#include "policy-p.h"
|
||||
#include "Account.h"
|
||||
#include "gncInvoice.h"
|
||||
#include "Scrub2.h"
|
||||
#include "ScrubBusiness.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
static QofLogModule log_module = GNC_MOD_LOT;
|
||||
|
||||
// A helper function that takes two splits. If the splits are of opposite sign
|
||||
// it reduces the biggest split to have the same value (but with opposite sign)
|
||||
// of the smaller split.
|
||||
// To make sure everything still continues to balance in addition a "remainder" split
|
||||
// will be created that will be added to the same lot and transaction as the biggest
|
||||
// split.
|
||||
// The opposite sign restriction is because that's the only scenario that makes sense
|
||||
// in the context of scrubbing business lots below.
|
||||
// If we created new splits, return TRUE, otherwise FALSE
|
||||
static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
|
||||
{
|
||||
gnc_numeric valA = xaccSplitGetValue (splitA);
|
||||
gnc_numeric valB = xaccSplitGetValue (splitB);
|
||||
|
||||
if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) >= 0)
|
||||
return gncOwnerReduceSplitTo (splitA, gnc_numeric_neg (valB));
|
||||
else
|
||||
return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA));
|
||||
}
|
||||
|
||||
// Attempt to eliminate or reduce the lot link splits (ll_*_split)
|
||||
// between from_lot and to_lot. To do so this function will attempt
|
||||
// to move a payment split from from_lot to to_lot in order to
|
||||
// balance the lot link split that will be deleted.
|
||||
// To ensure everything remains balanced at most
|
||||
// min (val-ll-*-split, val-pay-split) (in absolute values) can be moved.
|
||||
// If any split involved has a larger value, it will be split in two
|
||||
// and only the part matching the other splits' value will be used.
|
||||
// The leftover splits are kept in the respective transactions/lots.
|
||||
// A future scrub action can still act on those if needed.
|
||||
//
|
||||
// Note that this function assumes that ll_from_split and ll_to_split are
|
||||
// of opposite sign. The calling function should check this.
|
||||
|
||||
static gboolean
|
||||
scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
|
||||
GNCLot *to_lot, Split *ll_to_split)
|
||||
{
|
||||
Split *real_from_split; // This refers to the split in the payment lot representing the payment itself
|
||||
gnc_numeric from_val, real_from_val, to_val;
|
||||
gboolean modified = FALSE;
|
||||
Transaction *ll_txn = xaccSplitGetParent (ll_to_split);
|
||||
|
||||
// Per iteration we can only scrub at most max (val-doc-split, val-pay-split)
|
||||
// So split the bigger one in two if needed and continue with the equal valued splits only
|
||||
// The remainder is added to the lot link transaction and the lot to keep everything balanced
|
||||
// and will be processed in a future iteration
|
||||
modified = reduce_biggest_split (ll_from_split, ll_to_split);
|
||||
|
||||
// Next we have to find the original payment split so we can
|
||||
// add (part of) it to the document lot
|
||||
real_from_split = gncOwnerFindOffsettingSplit (from_lot, xaccSplitGetValue (ll_from_split));
|
||||
if (!real_from_split)
|
||||
return modified; // No usable split in the payment lot
|
||||
|
||||
// Here again per iteration we can only scrub at most max (val-other-pay-split, val-pay-split)
|
||||
// So split the bigger one in two if needed and continue with the equal valued splits only
|
||||
// The remainder is added to the lot link transaction and the lot to keep everything balanced
|
||||
// and will be processed in a future iteration
|
||||
modified = reduce_biggest_split (real_from_split, ll_from_split);
|
||||
|
||||
// Once more check for max (val-doc-split, val-pay-split), and reduce if necessary.
|
||||
// It may have changed while looking for the real payment split
|
||||
modified = reduce_biggest_split (ll_from_split, ll_to_split);
|
||||
|
||||
// At this point ll_to_split and real_from_split should have the same value
|
||||
// If not, flag a warning and skip to the next iteration
|
||||
to_val = xaccSplitGetValue (ll_to_split);
|
||||
from_val = xaccSplitGetValue (ll_from_split);
|
||||
real_from_val = xaccSplitGetValue (real_from_split);
|
||||
if (!gnc_numeric_equal (real_from_val, to_val))
|
||||
{
|
||||
// This is unexpected - write a warning message and skip this split
|
||||
PWARN("real_from_val and to_val differ. "
|
||||
"This is unexpected! Skip scrubbing of real_from_split %p against ll_to_split %p.", real_from_split, ll_to_split);
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Now do the actual split dance
|
||||
// - move real payment split to doc lot
|
||||
// - delete both lot link splits from the lot link transaction
|
||||
gnc_lot_add_split (to_lot, real_from_split);
|
||||
xaccTransBeginEdit (ll_txn);
|
||||
xaccSplitDestroy (ll_to_split);
|
||||
xaccSplitDestroy (ll_from_split);
|
||||
xaccTransCommitEdit (ll_txn);
|
||||
|
||||
// Cleanup the lots
|
||||
xaccScrubMergeLotSubSplits (to_lot, FALSE);
|
||||
xaccScrubMergeLotSubSplits (from_lot, FALSE);
|
||||
|
||||
return TRUE; // We did change splits/transactions/lots...
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gncScrubLotLinks (GNCLot *scrub_lot)
|
||||
{
|
||||
gboolean modified = FALSE, restart_needed = FALSE;
|
||||
SplitList *sls_iter = NULL;
|
||||
|
||||
scrub_start:
|
||||
restart_needed = FALSE;
|
||||
|
||||
// Iterate over all splits in the lot
|
||||
for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
|
||||
{
|
||||
Split *sl_split = sls_iter->data;
|
||||
Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
|
||||
SplitList *lts_iter = NULL;
|
||||
|
||||
if (!sl_split)
|
||||
continue; // next scrub lot split
|
||||
|
||||
// Only lot link transactions need to be scrubbed
|
||||
ll_txn = xaccSplitGetParent (sl_split);
|
||||
|
||||
if (!ll_txn)
|
||||
{
|
||||
// Ooops - the split doesn't belong to any transaction !
|
||||
// This is not expected so issue a warning and continue with next split
|
||||
PWARN("Encountered a split in a business lot that's not part of any transaction. "
|
||||
"This is unexpected! Skipping split %p.", sl_split);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||
continue; // next scrub lot split
|
||||
|
||||
// Iterate over all splits in the lot link transaction
|
||||
for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
|
||||
{
|
||||
Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
|
||||
GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
|
||||
gboolean sl_is_doc_lot, rl_is_doc_lot;
|
||||
|
||||
if (!ll_txn_split)
|
||||
continue; // next lot link transaction split
|
||||
|
||||
// Skip the split in the lot we're currently scrubbing
|
||||
if (sl_split == ll_txn_split)
|
||||
continue; // next lot link transaction split
|
||||
|
||||
// Only splits of opposite sign can be scrubbed
|
||||
if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
|
||||
gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
|
||||
continue; // next lot link transaction split
|
||||
|
||||
// Find linked lot via split
|
||||
remote_lot = xaccSplitGetLot (ll_txn_split);
|
||||
if (!remote_lot)
|
||||
{
|
||||
// This is unexpected - write a warning message and skip this split
|
||||
PWARN("Encountered a Lot Link transaction with a split that's not in any lot. "
|
||||
"This is unexpected! Skipping split %p from transaction %p.", ll_txn_split, ll_txn);
|
||||
continue;
|
||||
}
|
||||
|
||||
sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
|
||||
rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);
|
||||
|
||||
// Depending on the type of lots we're comparing, we need different actions
|
||||
// - Two document lots (an invoice and a credit note):
|
||||
// Special treatment - look for all document lots linked via ll_txn
|
||||
// and update the memo to be of more use to the uses.
|
||||
// - Two payment lots:
|
||||
// (Part of) the link will be eliminated and instead (part of)
|
||||
// one payment will be added to the other lot to keep the balance.
|
||||
// If the payments are not equal in abs value part of the bigger payment
|
||||
// will be moved to the smaller payment's lot.
|
||||
// - A document and a payment lot:
|
||||
// (Part of) the link will be eliminated and instead (part of) the real
|
||||
// payment will be added to the document lot to handle the payment.
|
||||
if (sl_is_doc_lot && rl_is_doc_lot)
|
||||
gncOwnerSetLotLinkMemo (ll_txn);
|
||||
else if (!sl_is_doc_lot && !rl_is_doc_lot)
|
||||
{
|
||||
gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
|
||||
gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
|
||||
if (cmp >= 0)
|
||||
restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
|
||||
else
|
||||
restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
|
||||
}
|
||||
else
|
||||
{
|
||||
GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
|
||||
GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
|
||||
Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
|
||||
Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
|
||||
// Ok, let's try to move a payment from pay_lot to doc_lot
|
||||
restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
|
||||
}
|
||||
|
||||
// If we got here, the splits in our lot and ll_txn have been severely mixed up
|
||||
// And our iterator lists are probably no longer valid
|
||||
// So let's start over
|
||||
if (restart_needed)
|
||||
{
|
||||
modified = TRUE;
|
||||
goto scrub_start;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
gncScrubBusinessLot (GNCLot *lot)
|
||||
{
|
||||
gboolean splits_deleted = FALSE;
|
||||
Account *acc;
|
||||
gchar *lotname=NULL;
|
||||
|
||||
if (!lot) return FALSE;
|
||||
lotname = g_strdup (gnc_lot_get_title (lot));
|
||||
ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)");
|
||||
|
||||
acc = gnc_lot_get_account (lot);
|
||||
if (acc)
|
||||
xaccAccountBeginEdit(acc);
|
||||
|
||||
// Scrub lot links.
|
||||
// They should only remain when two document lots are linked together
|
||||
xaccScrubMergeLotSubSplits (lot, FALSE);
|
||||
splits_deleted = gncScrubLotLinks (lot);
|
||||
|
||||
// If lot is empty now, delete it
|
||||
if (0 == gnc_lot_count_splits (lot))
|
||||
{
|
||||
PINFO("All splits were removed from lot, deleting");
|
||||
gnc_lot_destroy (lot);
|
||||
}
|
||||
|
||||
if (acc)
|
||||
xaccAccountCommitEdit(acc);
|
||||
|
||||
LEAVE ("(lot=%s, deleted=%d)", lotname ? lotname : "(no lotname)", splits_deleted);
|
||||
g_free (lotname);
|
||||
|
||||
return splits_deleted;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
void
|
||||
gncScrubBusinessAccountLots (Account *acc)
|
||||
{
|
||||
LotList *lots, *node;
|
||||
if (!acc) return;
|
||||
if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
|
||||
|
||||
ENTER ("(acc=%s)", xaccAccountGetName(acc));
|
||||
xaccAccountBeginEdit(acc);
|
||||
|
||||
lots = xaccAccountGetLotList(acc);
|
||||
for (node = lots; node; node = node->next)
|
||||
{
|
||||
GNCLot *lot = node->data;
|
||||
if (lot)
|
||||
gncScrubBusinessLot (lot);
|
||||
}
|
||||
g_list_free(lots);
|
||||
xaccAccountCommitEdit(acc);
|
||||
LEAVE ("(acc=%s)", xaccAccountGetName(acc));
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static void
|
||||
lot_scrub_cb (Account *acc, gpointer data)
|
||||
{
|
||||
if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
|
||||
gncScrubBusinessAccountLots (acc);
|
||||
}
|
||||
|
||||
void
|
||||
gncScrubBusinessAccountTreeLots (Account *acc)
|
||||
{
|
||||
if (!acc) return;
|
||||
|
||||
gnc_account_foreach_descendant(acc, lot_scrub_cb, NULL);
|
||||
gncScrubBusinessAccountLots (acc);
|
||||
}
|
||||
|
||||
/* ========================== END OF FILE ========================= */
|
79
src/engine/ScrubBusiness.h
Normal file
79
src/engine/ScrubBusiness.h
Normal file
@ -0,0 +1,79 @@
|
||||
/********************************************************************\
|
||||
* ScrubBusiness.h -- Cleanup functions for the business objects. *
|
||||
* *
|
||||
* This program 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. *
|
||||
* *
|
||||
* This program 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, contact: *
|
||||
* *
|
||||
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||
\********************************************************************/
|
||||
|
||||
/** @addtogroup Engine
|
||||
@{ */
|
||||
/** @addtogroup Scrub
|
||||
@{ */
|
||||
|
||||
/** @file ScrubBusiness.h
|
||||
* @brief Cleanup functions for business objects
|
||||
* @author Created by Geert Janssens August 2014
|
||||
* @author Copyright (c) 2014 Geert Janssens <geert@kobaltwit.be>
|
||||
*/
|
||||
#ifndef GNC_SCRUBBUSINESS_H
|
||||
#define GNC_SCRUBBUSINESS_H
|
||||
|
||||
#include "gnc-engine.h"
|
||||
|
||||
/** @name Cleanup functions for business objects
|
||||
* Provides the high-level API for checking and repairing ('scrubbing
|
||||
* clean') the various data objects used by the business functions.
|
||||
@{ */
|
||||
|
||||
/** The gncScrubBusinessLot() function makes sure that the indicated
|
||||
* lot has all the correct properties required for a lot used in
|
||||
* the business features.
|
||||
*
|
||||
* Currently this function only does one thing: eliminate lot link
|
||||
* transactions between invoice lots and payment lots (which were
|
||||
* generated by GnuCash versions 2.6.0-2.6.3). Lot links
|
||||
* between invoices and credit notes will still remain.
|
||||
*
|
||||
* Scrubbing the lot may cause subsplits to be merged together,
|
||||
* i.e. for splits to be deleted. This routine returns true if
|
||||
* any splits were modified or deleted.
|
||||
*/
|
||||
gboolean gncScrubBusinessLot (GNCLot *lot);
|
||||
|
||||
/** The gncScrubBusinessAccountLots() function will call
|
||||
* gncScrubBusinessLot() on each lot in the given account.
|
||||
*
|
||||
* This routine is the primary routine for ensuring that the
|
||||
* lot structure of every lot of a business account is in good
|
||||
* order.
|
||||
*/
|
||||
void gncScrubBusinessAccountLots (Account *acc);
|
||||
|
||||
/** The gncScrubBusinessAccountTreeLots() function will call
|
||||
* gncScrubBusinessAccountLots() on each lot in the given account
|
||||
* and its sub accounts.
|
||||
*
|
||||
* This routine is the primary routine for ensuring that the
|
||||
* lot structure of every lot of a business account is in good
|
||||
* order.
|
||||
*/
|
||||
void gncScrubBusinessAccountTreeLots (Account *acc);
|
||||
|
||||
/** @} */
|
||||
#endif /* GNC_SCRUBBUSINESS_H */
|
||||
/** @} */
|
||||
/** @} */
|
@ -43,7 +43,6 @@
|
||||
#include "Split.h"
|
||||
#include "AccountP.h"
|
||||
#include "Scrub.h"
|
||||
#include "Scrub3.h"
|
||||
#include "TransactionP.h"
|
||||
#include "TransLog.h"
|
||||
#include "cap-gains.h"
|
||||
|
@ -213,7 +213,7 @@ void xaccTransRollbackEdit (Transaction *trans);
|
||||
|
||||
/** The xaccTransIsOpen() method returns TRUE if the transaction
|
||||
is open for editing. Otherwise, it returns false.
|
||||
XXX this routne should probably be deprecated. its, umm,
|
||||
XXX this routine should probably be deprecated. its, umm,
|
||||
hard to imagine legitimate uses (but it is used by
|
||||
the import/export code for reasons I can't understand.)
|
||||
*/
|
||||
|
@ -309,6 +309,8 @@ gnc_lot_free(GNCLot* lot)
|
||||
priv->is_closed = TRUE;
|
||||
/* qof_instance_release (&lot->inst); */
|
||||
g_object_unref (lot);
|
||||
|
||||
LEAVE();
|
||||
}
|
||||
|
||||
void
|
||||
@ -635,6 +637,7 @@ gnc_lot_remove_split (GNCLot *lot, Split *split)
|
||||
}
|
||||
gnc_lot_commit_edit(lot);
|
||||
qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
|
||||
LEAVE("removed from lot");
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
@ -44,14 +44,17 @@
|
||||
#include "gncVendorP.h"
|
||||
#include "gncInvoice.h"
|
||||
#include "gnc-commodity.h"
|
||||
#include "Transaction.h"
|
||||
#include "Scrub2.h"
|
||||
#include "Split.h"
|
||||
#include "Transaction.h"
|
||||
#include "engine-helpers.h"
|
||||
|
||||
#define _GNC_MOD_NAME GNC_ID_OWNER
|
||||
|
||||
#define GNC_OWNER_ID "gncOwner"
|
||||
|
||||
static QofLogModule log_module = GNC_MOD_ENGINE;
|
||||
|
||||
GncOwner * gncOwnerNew (void)
|
||||
{
|
||||
GncOwner *o;
|
||||
@ -834,9 +837,368 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
|
||||
return payment_lot;
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
is_equal = 8,
|
||||
is_more = 4,
|
||||
is_less = 2,
|
||||
is_pay_split = 1
|
||||
} split_flags;
|
||||
|
||||
Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_value)
|
||||
{
|
||||
SplitList *ls_iter = NULL;
|
||||
Split *best_split = NULL;
|
||||
gnc_numeric best_val = { 0, 1};
|
||||
gint best_flags = 0;
|
||||
|
||||
if (!lot)
|
||||
return NULL;
|
||||
|
||||
for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
|
||||
{
|
||||
Split *split = ls_iter->data;
|
||||
Transaction *txn;
|
||||
gnc_numeric split_value;
|
||||
gint new_flags = 0;
|
||||
gint val_cmp = 0;
|
||||
|
||||
if (!split)
|
||||
continue;
|
||||
|
||||
|
||||
txn = xaccSplitGetParent (split);
|
||||
if (!txn)
|
||||
{
|
||||
// Ooops - the split doesn't belong to any transaction !
|
||||
// This is not expected so issue a warning and continue with next split
|
||||
PWARN("Encountered a split in a payment lot that's not part of any transaction. "
|
||||
"This is unexpected! Skipping split %p.", split);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this split has the opposite sign of the target value we want to offset
|
||||
split_value = xaccSplitGetValue (split);
|
||||
if (gnc_numeric_positive_p (target_value) == gnc_numeric_positive_p (split_value))
|
||||
continue;
|
||||
|
||||
// Ok we have found a split that potentially can offset the target value
|
||||
// Let's see if it's better than what we have found already.
|
||||
val_cmp = gnc_numeric_compare (gnc_numeric_abs (split_value),
|
||||
gnc_numeric_abs (target_value));
|
||||
if (val_cmp == 0)
|
||||
new_flags += is_equal;
|
||||
else if (val_cmp > 0)
|
||||
new_flags += is_more;
|
||||
else
|
||||
new_flags += is_less;
|
||||
|
||||
if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
|
||||
new_flags += is_pay_split;
|
||||
|
||||
if ((new_flags >= best_flags) &&
|
||||
(gnc_numeric_compare (gnc_numeric_abs (split_value),
|
||||
gnc_numeric_abs (best_val)) > 0))
|
||||
{
|
||||
// The new split is a better match than what we found so far
|
||||
best_split = split;
|
||||
best_flags = new_flags;
|
||||
best_val = split_value;
|
||||
}
|
||||
}
|
||||
|
||||
return best_split;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value)
|
||||
{
|
||||
gnc_numeric split_val = xaccSplitGetValue (split);
|
||||
gnc_numeric rem_val;
|
||||
Split *rem_split;
|
||||
Transaction *txn;
|
||||
GNCLot *lot;
|
||||
|
||||
if (gnc_numeric_positive_p (split_val) != gnc_numeric_positive_p (target_value))
|
||||
return FALSE; // Split and target value have to be of the same sign
|
||||
|
||||
if (gnc_numeric_equal (split_val, target_value))
|
||||
return FALSE; // Split already has the target value
|
||||
|
||||
rem_val = gnc_numeric_sub (split_val, target_value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); // note: values are of opposite sign
|
||||
rem_split = xaccMallocSplit (xaccSplitGetBook (split));
|
||||
xaccSplitCopyOnto (split, rem_split);
|
||||
xaccSplitSetValue (rem_split, rem_val);
|
||||
|
||||
txn = xaccSplitGetParent (split);
|
||||
xaccTransBeginEdit (txn);
|
||||
xaccSplitSetValue (split, target_value);
|
||||
xaccSplitSetParent (rem_split, txn);
|
||||
xaccTransCommitEdit (txn);
|
||||
|
||||
lot = xaccSplitGetLot (split);
|
||||
gnc_lot_add_split (lot, rem_split);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gncOwnerSetLotLinkMemo (Transaction *ll_txn)
|
||||
{
|
||||
gchar *memo_prefix = _("Offset between documents: ");
|
||||
gchar *new_memo;
|
||||
SplitList *lts_iter;
|
||||
SplitList *splits = NULL, *siter;
|
||||
GList *titles = NULL, *titer;
|
||||
|
||||
if (!ll_txn)
|
||||
return;
|
||||
|
||||
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||
return;
|
||||
|
||||
// Find all splits in the lot link transaction that are also in a document lot
|
||||
for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
|
||||
{
|
||||
Split *split = lts_iter->data;
|
||||
GNCLot *lot;
|
||||
GncInvoice *invoice;
|
||||
gchar *title;
|
||||
|
||||
if (!split)
|
||||
continue;
|
||||
|
||||
lot = xaccSplitGetLot (split);
|
||||
if (!lot)
|
||||
continue;
|
||||
|
||||
invoice = gncInvoiceGetInvoiceFromLot (lot);
|
||||
if (!invoice)
|
||||
continue;
|
||||
|
||||
title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
|
||||
|
||||
titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
|
||||
splits = g_list_prepend (splits, split); // splits don't need to be sorted
|
||||
}
|
||||
|
||||
if (!titles)
|
||||
return; // We didn't find document lots
|
||||
|
||||
// Create the memo as we'd want it to be
|
||||
new_memo = g_strconcat (memo_prefix, titles->data, NULL);
|
||||
for (titer = titles->next; titer; titer = titer->next)
|
||||
{
|
||||
gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
|
||||
g_free (new_memo);
|
||||
new_memo = tmp_memo;
|
||||
}
|
||||
g_list_free_full (titles, g_free);
|
||||
|
||||
// Update the memos of all the splits we found previously (if needed)
|
||||
for (siter = splits; siter; siter = siter->next)
|
||||
{
|
||||
if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
|
||||
xaccSplitSetMemo (siter->data, new_memo);
|
||||
}
|
||||
|
||||
g_list_free (splits);
|
||||
g_free (new_memo);
|
||||
}
|
||||
|
||||
/* Find an existing lot link transaction in the given lot
|
||||
* Only use a lot link that already links at least two
|
||||
* documents (to avoid perpetuating the lot link proliferation
|
||||
* that happened in 2.6.0-2.6.3).
|
||||
*/
|
||||
static Transaction *
|
||||
get_ll_transaction_from_lot (GNCLot *lot)
|
||||
{
|
||||
SplitList *ls_iter;
|
||||
|
||||
/* This should really only be called on a document lot */
|
||||
if (!gncInvoiceGetInvoiceFromLot (lot))
|
||||
return NULL;
|
||||
|
||||
/* The given lot is a valid document lot. Now iterate over all
|
||||
* other lot links in this lot to find one more document lot.
|
||||
*/
|
||||
for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
|
||||
{
|
||||
Split *ls = ls_iter->data;
|
||||
Transaction *ll_txn = xaccSplitGetParent (ls);
|
||||
SplitList *ts_iter;
|
||||
|
||||
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||
continue;
|
||||
|
||||
for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
|
||||
{
|
||||
Split *ts = ts_iter->data;
|
||||
GNCLot *tslot = xaccSplitGetLot (ts);
|
||||
|
||||
if (!tslot)
|
||||
continue;
|
||||
|
||||
if (tslot == lot)
|
||||
continue;
|
||||
|
||||
if (gncInvoiceGetInvoiceFromLot (lot))
|
||||
return ll_txn; /* Got one more document lot - mission accomplished */
|
||||
}
|
||||
}
|
||||
|
||||
/* The lot doesn't have an ll_txn with the requested criteria... */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
|
||||
{
|
||||
const gchar *action = _("Lot Link");
|
||||
Account *acct = gnc_lot_get_account (from_lot);
|
||||
const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
|
||||
Transaction *ll_txn = NULL;
|
||||
gnc_numeric from_lot_bal, to_lot_bal;
|
||||
Timespec from_ts, to_ts;
|
||||
time64 time_posted;
|
||||
Split *split;
|
||||
|
||||
/* Sanity check */
|
||||
if (!gncInvoiceGetInvoiceFromLot (from_lot) ||
|
||||
!gncInvoiceGetInvoiceFromLot (to_lot))
|
||||
return;
|
||||
|
||||
/* Determine transaction date based on lot splits */
|
||||
from_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot)));
|
||||
to_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot)));
|
||||
if (timespecToTime64 (from_ts) >= timespecToTime64 (to_ts))
|
||||
time_posted = timespecToTime64 (from_ts);
|
||||
else
|
||||
time_posted = timespecToTime64 (to_ts);
|
||||
|
||||
/* Figure out how much we can offset between the lots */
|
||||
from_lot_bal = gnc_lot_get_balance (from_lot);
|
||||
to_lot_bal = gnc_lot_get_balance (to_lot);
|
||||
if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal),
|
||||
gnc_numeric_abs (to_lot_bal)) > 0)
|
||||
from_lot_bal = gnc_numeric_neg (to_lot_bal);
|
||||
else
|
||||
to_lot_bal = gnc_numeric_neg (from_lot_bal);
|
||||
|
||||
xaccAccountBeginEdit (acct);
|
||||
|
||||
/* Look for a pre-existing lot link we can extend */
|
||||
ll_txn = get_ll_transaction_from_lot (from_lot);
|
||||
|
||||
if (!ll_txn)
|
||||
ll_txn = get_ll_transaction_from_lot (to_lot);
|
||||
|
||||
if (!ll_txn)
|
||||
{
|
||||
/* No pre-existing lot link. Create one. */
|
||||
Timespec ts;
|
||||
|
||||
timespecFromTime64 (&ts, time_posted);
|
||||
|
||||
ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot));
|
||||
xaccTransBeginEdit (ll_txn);
|
||||
|
||||
xaccTransSetDescription (ll_txn, name ? name : "(Unknown)");
|
||||
xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct));
|
||||
xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL));
|
||||
xaccTransSetDatePostedTS (ll_txn, &ts);
|
||||
xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK);
|
||||
}
|
||||
else
|
||||
{
|
||||
Timespec ts = xaccTransRetDatePostedTS (ll_txn);
|
||||
xaccTransBeginEdit (ll_txn);
|
||||
|
||||
/* Maybe we need to update the post date of the transaction ? */
|
||||
if (time_posted > timespecToTime64 (ts))
|
||||
{
|
||||
timespecFromTime64 (&ts, time_posted);
|
||||
xaccTransSetDatePostedTS (ll_txn, &ts);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a split for the from_lot */
|
||||
split = xaccMallocSplit (gnc_lot_get_book (from_lot));
|
||||
/* set Action using utility function */
|
||||
gnc_set_num_action (NULL, split, NULL, action);
|
||||
xaccAccountInsertSplit (acct, split);
|
||||
xaccTransAppendSplit (ll_txn, split);
|
||||
/* To offset the lot balance, the split must be of the opposite sign */
|
||||
xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct));
|
||||
gnc_lot_add_split (from_lot, split);
|
||||
|
||||
/* Create a split for the to_lot */
|
||||
split = xaccMallocSplit (gnc_lot_get_book (to_lot));
|
||||
/* set Action using utility function */
|
||||
gnc_set_num_action (NULL, split, NULL, action);
|
||||
xaccAccountInsertSplit (acct, split);
|
||||
xaccTransAppendSplit (ll_txn, split);
|
||||
/* To offset the lot balance, the split must be of the opposite sign */
|
||||
xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct));
|
||||
gnc_lot_add_split (to_lot, split);
|
||||
|
||||
xaccTransCommitEdit (ll_txn);
|
||||
|
||||
|
||||
/* Do some post-cleaning on the lots
|
||||
* The above actions may have created splits that are
|
||||
* in the same transaction and lot. These can be merged.
|
||||
*/
|
||||
xaccScrubMergeLotSubSplits (to_lot, FALSE);
|
||||
xaccScrubMergeLotSubSplits (from_lot, FALSE);
|
||||
/* And finally set the same memo for all remaining splits
|
||||
* It's a convenience for the users to identify all documents
|
||||
* involved in the link.
|
||||
*/
|
||||
gncOwnerSetLotLinkMemo (ll_txn);
|
||||
xaccAccountCommitEdit (acct);
|
||||
}
|
||||
|
||||
static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
|
||||
{
|
||||
gnc_numeric target_offset;
|
||||
Split *split;
|
||||
|
||||
/* from lot should not be a document lot because we're removing a split from there ! */
|
||||
if (gncInvoiceGetInvoiceFromLot (from_lot))
|
||||
{
|
||||
PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get best matching split from from_lot to offset to_lot */
|
||||
target_offset = gnc_lot_get_balance (to_lot);
|
||||
if (gnc_numeric_zero_p (target_offset))
|
||||
return; // to_lot is already balanced, nothing more to do
|
||||
|
||||
split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
|
||||
if (!split)
|
||||
return; // No suitable offsetting split found, nothing more to do
|
||||
|
||||
/* If the offsetting split is bigger than the amount needed to balance
|
||||
* to_lot, reduce the split so its reduced value closes to_lot exactly.
|
||||
* Note the negation in the reduction function. The split must be of
|
||||
* opposite sign of to_lot's balance in order to be able to close it.
|
||||
*/
|
||||
if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (split)),
|
||||
gnc_numeric_abs (target_offset)) > 0)
|
||||
gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset));
|
||||
|
||||
/* Move the reduced split from from_lot to to_lot */
|
||||
gnc_lot_add_split (to_lot, split);
|
||||
|
||||
}
|
||||
|
||||
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
||||
{
|
||||
GList *base_iter;
|
||||
GList *left_iter;
|
||||
|
||||
/* General note: in the code below the term "payment" can
|
||||
* both mean a true payment or a document of
|
||||
@ -850,164 +1212,126 @@ void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
||||
if (!owner) return;
|
||||
if (!lots) return;
|
||||
|
||||
for (base_iter = lots; base_iter; base_iter = base_iter->next)
|
||||
for (left_iter = lots; left_iter; left_iter = left_iter->next)
|
||||
{
|
||||
GNCLot *base_lot = base_iter->data;
|
||||
QofBook *book;
|
||||
GNCLot *left_lot = left_iter->data;
|
||||
gnc_numeric left_lot_bal;
|
||||
gboolean left_lot_has_doc;
|
||||
gboolean left_modified = FALSE;
|
||||
Account *acct;
|
||||
const gchar *name;
|
||||
GList *lot_list, *lot_iter;
|
||||
Transaction *txn = NULL;
|
||||
gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 };
|
||||
gboolean base_bal_is_pos;
|
||||
const gchar *action, *memo;
|
||||
GList *right_iter;
|
||||
|
||||
/* Only attempt to apply payments to open lots.
|
||||
* Note that due to the iterative nature of this function lots
|
||||
* in the list may become closed before they are evaluated as
|
||||
* in the list may become empty/closed before they are evaluated as
|
||||
* base lot, so we should check this for each lot. */
|
||||
base_lot_bal = gnc_lot_get_balance (base_lot);
|
||||
if (gnc_numeric_zero_p (base_lot_bal))
|
||||
if (!left_lot)
|
||||
continue;
|
||||
if (gnc_lot_count_splits (left_lot) == 0)
|
||||
{
|
||||
gnc_lot_destroy (left_lot);
|
||||
left_iter->data = NULL;
|
||||
continue;
|
||||
}
|
||||
if (gnc_lot_is_closed (left_lot))
|
||||
continue;
|
||||
|
||||
book = gnc_lot_get_book (base_lot);
|
||||
acct = gnc_lot_get_account (base_lot);
|
||||
name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
|
||||
lot_list = base_iter->next;
|
||||
acct = gnc_lot_get_account (left_lot);
|
||||
xaccAccountBeginEdit (acct);
|
||||
|
||||
/* Strings used when creating splits later on. */
|
||||
action = _("Lot Link");
|
||||
memo = _("Internal link between invoice and payment lots");
|
||||
left_lot_bal = gnc_lot_get_balance (left_lot);
|
||||
left_lot_has_doc = (gncInvoiceGetInvoiceFromLot (left_lot) != NULL);
|
||||
|
||||
/* Note: to balance the lot the payment to assign
|
||||
* must have the opposite sign of the existing lot balance */
|
||||
val_to_pay = gnc_numeric_neg (base_lot_bal);
|
||||
base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal);
|
||||
|
||||
|
||||
/* Create splits in a linking transaction between lots until
|
||||
* - either the invoice lot is balanced
|
||||
* - or there are no more balancing lots.
|
||||
/* Attempt to offset left_lot with any of the remaining lots. To do so
|
||||
* iterate over the remaining lots adding lot links or moving payments
|
||||
* around.
|
||||
*/
|
||||
for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next)
|
||||
for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next)
|
||||
{
|
||||
gnc_numeric payment_lot_balance;
|
||||
Split *split;
|
||||
Account *bal_acct;
|
||||
gnc_numeric split_amt;
|
||||
|
||||
GNCLot *balancing_lot = lot_iter->data;
|
||||
GNCLot *right_lot = right_iter->data;
|
||||
gnc_numeric right_lot_bal;
|
||||
gboolean right_lot_has_doc;
|
||||
|
||||
/* Only attempt to use open lots to balance the base lot.
|
||||
* Note that due to the iterative nature of this function lots
|
||||
* in the list may become closed before they are evaluated as
|
||||
* in the list may become empty/closed before they are evaluated as
|
||||
* base lot, so we should check this for each lot. */
|
||||
if (gnc_lot_is_closed (balancing_lot))
|
||||
if (!right_lot)
|
||||
continue;
|
||||
if (gnc_lot_count_splits (right_lot) == 0)
|
||||
{
|
||||
gnc_lot_destroy (right_lot);
|
||||
right_iter->data = NULL;
|
||||
continue;
|
||||
}
|
||||
if (gnc_lot_is_closed (right_lot))
|
||||
continue;
|
||||
|
||||
/* Balancing transactions for invoice/payments can only happen
|
||||
* in the same account. */
|
||||
bal_acct = gnc_lot_get_account (balancing_lot);
|
||||
if (acct != bal_acct)
|
||||
if (acct != gnc_lot_get_account (right_lot))
|
||||
continue;
|
||||
|
||||
payment_lot_balance = gnc_lot_get_balance (balancing_lot);
|
||||
|
||||
/* Only attempt to balance if the base lot and balancing lot are
|
||||
* of the opposite sign. (Otherwise we would increase the balance
|
||||
* of the lot - Duh */
|
||||
if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance))
|
||||
right_lot_bal = gnc_lot_get_balance (right_lot);
|
||||
if (gnc_numeric_positive_p (left_lot_bal) == gnc_numeric_positive_p (right_lot_bal))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale.
|
||||
* Note that payment_value and balance are opposite in sign, so we have to compare absolute values here
|
||||
*
|
||||
* Otherwise, apply the balance, subtract that from the payment_value,
|
||||
* and move on to the next one.
|
||||
/* Ok we found two lots than can (partly) offset each other.
|
||||
* Depending on the lot types, a different action is needed to accomplish this.
|
||||
* 1. Both lots are document lots (invoices/credit notes)
|
||||
* -> Create a lot linking transaction between the lots
|
||||
* 2. Both lots are payment lots (lots without a document attached)
|
||||
* -> Use part of the bigger lot to the close the smaller lot
|
||||
* 3. One document lot with one payment lot
|
||||
* -> Use (part of) the payment to offset (part of) the document lot,
|
||||
* Which one will be closed depends on which is the bigger one
|
||||
*/
|
||||
if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0)
|
||||
right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL);
|
||||
if (left_lot_has_doc && right_lot_has_doc)
|
||||
gncOwnerCreateLotLink (left_lot, right_lot, owner);
|
||||
else if (!left_lot_has_doc && !right_lot_has_doc)
|
||||
{
|
||||
/* abs(val_to_pay) <= abs(balance) */
|
||||
split_amt = val_to_pay;
|
||||
gint cmp = gnc_numeric_compare (gnc_numeric_abs (left_lot_bal),
|
||||
gnc_numeric_abs (right_lot_bal));
|
||||
if (cmp >= 0)
|
||||
gncOwnerOffsetLots (left_lot, right_lot, owner);
|
||||
else
|
||||
gncOwnerOffsetLots (right_lot, left_lot, owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* abs(val_to_pay) > abs(balance)
|
||||
* Remember payment_value and balance are opposite in sign,
|
||||
* and we want a payment to neutralize the current balance
|
||||
* so we need to negate here */
|
||||
split_amt = payment_lot_balance;
|
||||
GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot;
|
||||
GNCLot *pay_lot = left_lot_has_doc ? right_lot : left_lot;
|
||||
// Ok, let's try to move a payment from pay_lot to doc_lot
|
||||
gncOwnerOffsetLots (pay_lot, doc_lot, owner);
|
||||
}
|
||||
|
||||
/* If not created yet, create a new transaction linking
|
||||
* the base lot and the balancing lot(s) */
|
||||
if (!txn)
|
||||
/* If we get here, then right_lot was modified
|
||||
* If the lot has a document, send an event for send an event for it as well
|
||||
* so it gets potentially updated as paid */
|
||||
|
||||
{
|
||||
Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot)));
|
||||
|
||||
xaccAccountBeginEdit (acct);
|
||||
|
||||
txn = xaccMallocTransaction (book);
|
||||
xaccTransBeginEdit (txn);
|
||||
|
||||
xaccTransSetDescription (txn, name ? name : "");
|
||||
xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct));
|
||||
xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
|
||||
xaccTransSetDatePostedTS (txn, &ts);
|
||||
xaccTransSetTxnType (txn, TXN_TYPE_LINK);
|
||||
}
|
||||
|
||||
/* Create the split for this link in current balancing lot */
|
||||
split = xaccMallocSplit (book);
|
||||
xaccSplitSetMemo (split, memo);
|
||||
/* set Action using utility function */
|
||||
gnc_set_num_action (NULL, split, NULL, action);
|
||||
xaccAccountInsertSplit (acct, split);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct));
|
||||
gnc_lot_add_split (balancing_lot, split);
|
||||
|
||||
/* If the balancing lot was linked to a document (invoice/credit note),
|
||||
* send an event for it as well so it gets potentially updated as paid */
|
||||
{
|
||||
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot);
|
||||
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot);
|
||||
if (this_invoice)
|
||||
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
||||
}
|
||||
|
||||
val_paid = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
||||
val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
||||
if (gnc_numeric_zero_p (val_to_pay))
|
||||
break;
|
||||
left_modified = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* If the above loop managed to create a transaction and some balancing splits,
|
||||
* create the final split for the link transaction in the base lot */
|
||||
if (txn)
|
||||
/* If left_lot was modified and the lot has a document,
|
||||
* send an event for send an event for it as well
|
||||
* so it gets potentially updated as paid */
|
||||
if (left_modified)
|
||||
{
|
||||
GncInvoice *this_invoice;
|
||||
Split *split = xaccMallocSplit (book);
|
||||
|
||||
xaccSplitSetMemo (split, memo);
|
||||
/* set Action with utiltity function */
|
||||
gnc_set_num_action (NULL, split, NULL, action);
|
||||
xaccAccountInsertSplit (acct, split);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct));
|
||||
gnc_lot_add_split (base_lot, split);
|
||||
|
||||
xaccTransCommitEdit (txn);
|
||||
xaccAccountCommitEdit (acct);
|
||||
|
||||
/* If the base lot was linked to a document (invoice/credit note),
|
||||
* send an event for it as well so it gets potentially updated as paid */
|
||||
this_invoice = gncInvoiceGetInvoiceFromLot(base_lot);
|
||||
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot);
|
||||
if (this_invoice)
|
||||
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
||||
|
||||
}
|
||||
xaccAccountCommitEdit (acct);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +263,34 @@ gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
|
||||
gnc_numeric amount, gnc_numeric exch, Timespec date,
|
||||
const char *memo, const char *num, gboolean auto_pay);
|
||||
|
||||
/** Helper function to find a split in lot that best offsets target_value
|
||||
* Obviously it should be of opposite sign.
|
||||
* If there are more splits of opposite sign the following
|
||||
* criteria are used in order of preference:
|
||||
* 1. exact match in abs value is preferred over larger abs value
|
||||
* 2. larger abs value is preferred over smaller abs value
|
||||
* 3. if previous and new candidate are in the same value category,
|
||||
* prefer real payment splits over lot link splits
|
||||
* 4. if previous and new candiate are of same split type
|
||||
* prefer biggest abs value.
|
||||
*/
|
||||
Split *gncOwnerFindOffsettingSplit (GNCLot *pay_lot, gnc_numeric target_value);
|
||||
|
||||
/** Helper function to reduce the value of a split to target_value. To make
|
||||
* sure the split's parent transaction remains balanced a second split
|
||||
* will be created with the remainder. Similarly if the split was part of a
|
||||
* (business) lot, the remainder split will be added to the same lot to
|
||||
* keep the lot's balance unchanged.
|
||||
*/
|
||||
gboolean gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value);
|
||||
|
||||
/** To help a user understand what a lot link transaction does,
|
||||
* we set the memo to name all documents involved in the link.
|
||||
* The function below calculates this memo and sets it for
|
||||
* all splits in the lot link transaction.
|
||||
*/
|
||||
void gncOwnerSetLotLinkMemo (Transaction *ll_txn);
|
||||
|
||||
/** Returns a GList of account-types based on the owner type */
|
||||
GList * gncOwnerGetAccountTypesList (const GncOwner *owner);
|
||||
|
||||
|
@ -130,7 +130,6 @@ static void gnc_main_window_destroy (GtkObject *object);
|
||||
|
||||
static void gnc_main_window_setup_window (GncMainWindow *window);
|
||||
static void gnc_window_main_window_init (GncWindowIface *iface);
|
||||
static gboolean main_window_find_tab_event (GncMainWindow *window, GncPluginPage *page, GtkWidget **event_p);
|
||||
#ifndef MAC_INTEGRATION
|
||||
static void gnc_main_window_update_all_menu_items (void);
|
||||
#endif
|
||||
@ -2009,31 +2008,11 @@ static void
|
||||
gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
|
||||
gpointer user_data)
|
||||
{
|
||||
GncMainWindow *window = user_data;
|
||||
GncMainWindowPrivate *priv;
|
||||
const gchar *color_string;
|
||||
GdkColor tab_color;
|
||||
GtkWidget *event_box;
|
||||
|
||||
ENTER("page %p", page);
|
||||
|
||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||
|
||||
/* Get the event box to update the tab */
|
||||
main_window_find_tab_event(window, page, &event_box);
|
||||
|
||||
color_string = gnc_plugin_page_get_page_color(page);
|
||||
if (color_string == NULL) color_string = "";
|
||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
||||
}
|
||||
main_window_update_page_color (page, color_string);
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
||||
@ -2142,7 +2121,7 @@ main_window_find_tab_items (GncMainWindow *window,
|
||||
GtkWidget **entry_p)
|
||||
{
|
||||
GncMainWindowPrivate *priv;
|
||||
GtkWidget *tab_hbox, *widget, *event_box;
|
||||
GtkWidget *tab_hbox, *widget, *tab_widget;
|
||||
GList *children, *tmp;
|
||||
|
||||
ENTER("window %p, page %p, label_p %p, entry_p %p",
|
||||
@ -2156,10 +2135,17 @@ main_window_find_tab_items (GncMainWindow *window,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||
tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||
page->notebook_page);
|
||||
|
||||
tab_hbox = gtk_bin_get_child(GTK_BIN(event_box));
|
||||
if (GTK_IS_EVENT_BOX (tab_widget))
|
||||
tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
|
||||
else if (GTK_IS_HBOX (tab_widget))
|
||||
tab_hbox = tab_widget;
|
||||
else
|
||||
{
|
||||
PWARN ("Unknown widget for tab label %p", tab_widget);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
|
||||
for (tmp = children; tmp; tmp = g_list_next(tmp))
|
||||
@ -2181,16 +2167,15 @@ main_window_find_tab_items (GncMainWindow *window,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
main_window_find_tab_event (GncMainWindow *window,
|
||||
GncPluginPage *page,
|
||||
GtkWidget **event_p)
|
||||
main_window_find_tab_widget (GncMainWindow *window,
|
||||
GncPluginPage *page,
|
||||
GtkWidget **widget_p)
|
||||
{
|
||||
GncMainWindowPrivate *priv;
|
||||
GtkWidget *event_box;
|
||||
|
||||
ENTER("window %p, page %p, event %p",
|
||||
window, page, event_p);
|
||||
*event_p = NULL;
|
||||
ENTER("window %p, page %p, widget %p",
|
||||
window, page, widget_p);
|
||||
*widget_p = NULL;
|
||||
|
||||
if (!page->notebook_page)
|
||||
{
|
||||
@ -2199,17 +2184,11 @@ main_window_find_tab_event (GncMainWindow *window,
|
||||
}
|
||||
|
||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||
event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||
*widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||
page->notebook_page);
|
||||
if (GTK_IS_EVENT_BOX(event_box))
|
||||
{
|
||||
*event_p = event_box;
|
||||
LEAVE("event %p", *event_p);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
LEAVE("event %p", *event_p);
|
||||
return (FALSE);
|
||||
LEAVE("widget %p", *widget_p);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2218,7 +2197,7 @@ main_window_update_page_name (GncPluginPage *page,
|
||||
{
|
||||
GncMainWindow *window;
|
||||
GncMainWindowPrivate *priv;
|
||||
GtkWidget *label, *entry, *event_box;
|
||||
GtkWidget *label, *entry;
|
||||
gchar *name, *old_page_name, *old_page_long_name;
|
||||
|
||||
ENTER(" ");
|
||||
@ -2264,14 +2243,15 @@ main_window_update_page_name (GncPluginPage *page,
|
||||
{
|
||||
gchar *new_page_long_name;
|
||||
gint string_position;
|
||||
GtkWidget *tab_widget;
|
||||
|
||||
string_position = strlen(old_page_long_name) - strlen(old_page_name);
|
||||
new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
|
||||
|
||||
gnc_plugin_page_set_page_long_name(page, new_page_long_name);
|
||||
|
||||
if (main_window_find_tab_event(window, page, &event_box))
|
||||
gtk_widget_set_tooltip_text(event_box, new_page_long_name);
|
||||
if (main_window_find_tab_widget(window, page, &tab_widget))
|
||||
gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
|
||||
|
||||
g_free(new_page_long_name);
|
||||
}
|
||||
@ -2300,44 +2280,55 @@ main_window_update_page_color (GncPluginPage *page,
|
||||
{
|
||||
GncMainWindow *window;
|
||||
GncMainWindowPrivate *priv;
|
||||
GtkWidget *event_box;
|
||||
GtkWidget *tab_widget;
|
||||
GdkColor tab_color;
|
||||
gchar *color_string;
|
||||
gchar *color_string = NULL;
|
||||
gboolean want_color = FALSE;
|
||||
|
||||
ENTER(" ");
|
||||
if ((color_in == NULL) || (*color_in == '\0'))
|
||||
{
|
||||
LEAVE("no string");
|
||||
return;
|
||||
}
|
||||
color_string = g_strstrip(g_strdup(color_in));
|
||||
if (color_in)
|
||||
color_string = g_strstrip(g_strdup(color_in));
|
||||
|
||||
/* Optimization, if the color hasn't changed, don't update. */
|
||||
if (*color_string == '\0' || 0 == g_strcmp0(color_string, gnc_plugin_page_get_page_color(page)))
|
||||
{
|
||||
g_free(color_string);
|
||||
LEAVE("empty string or color unchanged");
|
||||
return;
|
||||
}
|
||||
if (color_string && *color_string != '\0')
|
||||
want_color = TRUE;
|
||||
|
||||
/* Update the plugin */
|
||||
window = GNC_MAIN_WINDOW(page->window);
|
||||
gnc_plugin_page_set_page_color(page, color_string);
|
||||
|
||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||
if (want_color)
|
||||
gnc_plugin_page_set_page_color(page, color_string);
|
||||
else
|
||||
gnc_plugin_page_set_page_color(page, NULL);
|
||||
|
||||
/* Update the notebook tab */
|
||||
main_window_find_tab_event(window, page, &event_box);
|
||||
main_window_find_tab_widget (window, page, &tab_widget);
|
||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||
|
||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
||||
if (want_color && gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
||||
if (!GTK_IS_EVENT_BOX (tab_widget))
|
||||
{
|
||||
GtkWidget *event_box = gtk_event_box_new ();
|
||||
g_object_ref (tab_widget);
|
||||
gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
|
||||
page->notebook_page, event_box);
|
||||
gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
|
||||
g_object_unref (tab_widget);
|
||||
tab_widget = event_box;
|
||||
}
|
||||
gtk_widget_modify_bg(tab_widget, GTK_STATE_NORMAL, &tab_color);
|
||||
gtk_widget_modify_bg(tab_widget, GTK_STATE_ACTIVE, &tab_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
||||
if (GTK_IS_EVENT_BOX (tab_widget))
|
||||
{
|
||||
GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
|
||||
g_object_ref (tab_hbox);
|
||||
gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
|
||||
gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
|
||||
page->notebook_page, tab_hbox);
|
||||
g_object_unref (tab_hbox);
|
||||
}
|
||||
}
|
||||
g_free(color_string);
|
||||
LEAVE("done");
|
||||
@ -2882,7 +2873,7 @@ gnc_main_window_open_page (GncMainWindow *window,
|
||||
{
|
||||
GncMainWindowPrivate *priv;
|
||||
GtkWidget *tab_hbox;
|
||||
GtkWidget *label, *entry, *event_box;
|
||||
GtkWidget *label, *entry;
|
||||
const gchar *icon, *text, *color_string;
|
||||
GtkWidget *image;
|
||||
GList *tmp;
|
||||
@ -2955,27 +2946,10 @@ gnc_main_window_open_page (GncMainWindow *window,
|
||||
else
|
||||
gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
|
||||
|
||||
event_box = gtk_event_box_new();
|
||||
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), TRUE);
|
||||
gtk_widget_show(event_box);
|
||||
gtk_container_add(GTK_CONTAINER(event_box), tab_hbox);
|
||||
color_string = gnc_plugin_page_get_page_color(page);
|
||||
if (color_string == NULL) color_string = "";
|
||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
||||
}
|
||||
|
||||
text = gnc_plugin_page_get_page_long_name(page);
|
||||
if (text)
|
||||
{
|
||||
gtk_widget_set_tooltip_text(event_box, text);
|
||||
gtk_widget_set_tooltip_text(tab_hbox, text);
|
||||
}
|
||||
|
||||
entry = gtk_entry_new();
|
||||
@ -3029,7 +3003,10 @@ gnc_main_window_open_page (GncMainWindow *window,
|
||||
/*
|
||||
* Now install it all in the window.
|
||||
*/
|
||||
gnc_main_window_connect(window, page, event_box, label);
|
||||
gnc_main_window_connect(window, page, tab_hbox, label);
|
||||
|
||||
color_string = gnc_plugin_page_get_page_color(page);
|
||||
main_window_update_page_color (page, color_string);
|
||||
LEAVE("");
|
||||
}
|
||||
|
||||
|
@ -868,7 +868,8 @@ gnc_plugin_page_set_page_color (GncPluginPage *page, const gchar *color)
|
||||
priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
|
||||
if (priv->page_color)
|
||||
g_free(priv->page_color);
|
||||
priv->page_color = g_strdup(color);
|
||||
if (color)
|
||||
priv->page_color = g_strdup(color);
|
||||
}
|
||||
|
||||
|
||||
|
@ -259,7 +259,7 @@ gnc_tree_view_add_date_column (GncTreeView *view,
|
||||
* column used to determine the foreground color of any text in this
|
||||
* column. It should be used to display negative numbers in red.
|
||||
* Use GNC_TREE_VIEW_COLUMN_COLOR_NONE if the text in this column
|
||||
* should always be displayed in black.
|
||||
* should always be displayed in the default theme color for text.
|
||||
*
|
||||
* @param model_visibility_column The index of the GtkTreeModel data
|
||||
* column used to determine whether or not a checkbox for each row
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include "Recurrence.h"
|
||||
#include "Query.h"
|
||||
#include "Scrub.h"
|
||||
#include "Scrub3.h"
|
||||
#include "Transaction.h"
|
||||
#include "dialog-utils.h"
|
||||
#include "assistant-acct-period.h"
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "qof.h"
|
||||
#include "gnc-lot.h"
|
||||
#include "Scrub3.h"
|
||||
#include "ScrubBusiness.h"
|
||||
#include "Transaction.h"
|
||||
#include "engine-helpers.h"
|
||||
#include "gncInvoice.h"
|
||||
@ -750,14 +751,20 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
||||
case RESPONSE_SCRUB_LOT:
|
||||
if (NULL == lot)
|
||||
return;
|
||||
xaccScrubLot (lot);
|
||||
if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
|
||||
gncScrubBusinessLot (lot);
|
||||
else
|
||||
xaccScrubLot (lot);
|
||||
gnc_lot_viewer_fill (lv);
|
||||
lv_show_splits_in_lot (lv);
|
||||
break;
|
||||
|
||||
case RESPONSE_SCRUB_ACCOUNT:
|
||||
gnc_suspend_gui_refresh ();
|
||||
xaccAccountScrubLots (lv->account);
|
||||
if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
|
||||
gncScrubBusinessAccountLots (lv->account);
|
||||
else
|
||||
xaccAccountScrubLots (lv->account);
|
||||
gnc_resume_gui_refresh ();
|
||||
gnc_lot_viewer_fill (lv);
|
||||
lv_show_splits_free (lv);
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
#include "Scrub.h"
|
||||
#include "Scrub3.h"
|
||||
#include "ScrubBusiness.h"
|
||||
#include "Transaction.h"
|
||||
#include "dialog-account.h"
|
||||
#include "dialog-transfer.h"
|
||||
@ -1568,10 +1569,13 @@ gnc_plugin_page_account_tree_cmd_scrub (GtkAction *action, GncPluginPageAccountT
|
||||
xaccAccountScrubOrphans (account);
|
||||
xaccAccountScrubImbalance (account);
|
||||
|
||||
// XXX: Lots are disabled
|
||||
// XXX: Lots/capital gains scrubbing is disabled
|
||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||
xaccAccountScrubLots(account);
|
||||
|
||||
gncScrubBusinessAccountLots(account);
|
||||
|
||||
|
||||
gnc_resume_gui_refresh ();
|
||||
}
|
||||
|
||||
@ -1587,10 +1591,12 @@ gnc_plugin_page_account_tree_cmd_scrub_sub (GtkAction *action, GncPluginPageAcco
|
||||
xaccAccountTreeScrubOrphans (account);
|
||||
xaccAccountTreeScrubImbalance (account);
|
||||
|
||||
// XXX: Lots are disabled
|
||||
// XXX: Lots/capital gains scrubbing is disabled
|
||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||
xaccAccountTreeScrubLots(account);
|
||||
|
||||
gncScrubBusinessAccountTreeLots(account);
|
||||
|
||||
gnc_resume_gui_refresh ();
|
||||
}
|
||||
|
||||
@ -1603,10 +1609,12 @@ gnc_plugin_page_account_tree_cmd_scrub_all (GtkAction *action, GncPluginPageAcco
|
||||
|
||||
xaccAccountTreeScrubOrphans (root);
|
||||
xaccAccountTreeScrubImbalance (root);
|
||||
// XXX: Lots are disabled
|
||||
// XXX: Lots/capital gains scrubbing is disabled
|
||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||
xaccAccountTreeScrubLots(root);
|
||||
|
||||
gncScrubBusinessAccountTreeLots(root);
|
||||
|
||||
gnc_resume_gui_refresh ();
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@
|
||||
#include "gnucash-sheet.h"
|
||||
#include "dialog-lot-viewer.h"
|
||||
#include "Scrub.h"
|
||||
#include "ScrubBusiness.h"
|
||||
#include "qof.h"
|
||||
#include "window-reconcile.h"
|
||||
#include "window-autoclear.h"
|
||||
@ -3691,6 +3692,8 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
|
||||
Query *query;
|
||||
Account *root;
|
||||
Transaction *trans;
|
||||
Split *split;
|
||||
GNCLot *lot;
|
||||
SplitRegister *reg;
|
||||
|
||||
g_return_if_fail(GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
|
||||
@ -3717,6 +3720,11 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
|
||||
root = gnc_get_current_root_account();
|
||||
xaccTransScrubOrphans(trans);
|
||||
xaccTransScrubImbalance(trans, root, NULL);
|
||||
|
||||
split = gnc_split_register_get_current_split (reg);
|
||||
lot = xaccSplitGetLot (split);
|
||||
if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
|
||||
gncScrubBusinessLot (lot);
|
||||
gnc_resume_gui_refresh();
|
||||
LEAVE(" ");
|
||||
}
|
||||
@ -3729,6 +3737,7 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
|
||||
Query *query;
|
||||
Account *root;
|
||||
Transaction *trans;
|
||||
GNCLot *lot;
|
||||
Split *split;
|
||||
GList *node;
|
||||
|
||||
@ -3754,6 +3763,10 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
|
||||
|
||||
xaccTransScrubOrphans(trans);
|
||||
xaccTransScrubImbalance(trans, root, NULL);
|
||||
|
||||
lot = xaccSplitGetLot (split);
|
||||
if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
|
||||
gncScrubBusinessLot (lot);
|
||||
}
|
||||
|
||||
gnc_resume_gui_refresh();
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "Scrub.h"
|
||||
#include "Scrub3.h"
|
||||
#include "dialog-account.h"
|
||||
#include "dialog-transfer.h"
|
||||
#include "dialog-utils.h"
|
||||
|
@ -39,18 +39,6 @@
|
||||
#include "split-register-p.h"
|
||||
#include "engine-helpers.h"
|
||||
|
||||
|
||||
static SplitRegisterColors reg_colors =
|
||||
{
|
||||
0x96B183,
|
||||
0xBFDEB9,
|
||||
0xF6FFDA,
|
||||
0xFFEF98,
|
||||
0xFFEF98,
|
||||
0xEDE7D3,
|
||||
0xFFEF98,
|
||||
};
|
||||
|
||||
/* This static indicates the debugging module that this .o belongs to. */
|
||||
static QofLogModule log_module = GNC_MOD_LEDGER;
|
||||
|
||||
@ -513,116 +501,33 @@ get_trans_total_balance (SplitRegister *reg, Transaction *trans)
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_shares_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
gnc_split_register_get_color_internal (VirtualLocation virt_loc,
|
||||
SplitRegister *reg,
|
||||
const guint32 *color_table,
|
||||
gboolean foreground)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
const guint32 black = 0x000000;
|
||||
const guint32 red = 0xff0000;
|
||||
const char * cell_name;
|
||||
gboolean is_current;
|
||||
gnc_numeric shares;
|
||||
Split *split;
|
||||
|
||||
if (!use_red_for_negative)
|
||||
return black;
|
||||
|
||||
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
||||
if (!split)
|
||||
return black;
|
||||
|
||||
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
||||
|
||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc);
|
||||
|
||||
if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
|
||||
shares = get_trans_total_amount (reg, xaccSplitGetParent (split));
|
||||
else if (is_current)
|
||||
shares = gnc_price_cell_get_value
|
||||
((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
|
||||
SHRS_CELL));
|
||||
else
|
||||
shares = xaccSplitGetAmount (split);
|
||||
|
||||
if (gnc_numeric_negative_p (shares))
|
||||
return red;
|
||||
|
||||
return black;
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_balance_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
const guint32 black = 0x000000;
|
||||
const guint32 red = 0xff0000;
|
||||
const char * cell_name;
|
||||
gnc_numeric balance;
|
||||
Split *split;
|
||||
|
||||
if (!use_red_for_negative)
|
||||
return black;
|
||||
|
||||
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
||||
if (!split)
|
||||
return black;
|
||||
|
||||
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
||||
|
||||
if (gnc_cell_name_equal (cell_name, BALN_CELL))
|
||||
balance = xaccSplitGetBalance (split);
|
||||
else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
|
||||
balance = gnc_split_register_get_rbaln (virt_loc, user_data, TRUE);
|
||||
else
|
||||
balance = get_trans_total_balance (reg, xaccSplitGetParent (split));
|
||||
|
||||
{
|
||||
Account *account;
|
||||
|
||||
account = xaccSplitGetAccount (split);
|
||||
|
||||
if (gnc_reverse_balance (account))
|
||||
balance = gnc_numeric_neg (balance);
|
||||
}
|
||||
|
||||
if (gnc_numeric_negative_p (balance))
|
||||
return red;
|
||||
|
||||
return black;
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
||||
gboolean *hatching,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
const char *cursor_name;
|
||||
VirtualCell *vcell;
|
||||
guint32 bg_color;
|
||||
gboolean is_current;
|
||||
gboolean double_alternate_virt;
|
||||
guint32 colorbase = 0; /* By default return background colors */
|
||||
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
bg_color = 0xffffff; /* white */
|
||||
if (foreground)
|
||||
colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
|
||||
|
||||
if (!reg)
|
||||
return bg_color;
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
|
||||
return reg_colors.header_bg_color;
|
||||
return color_table[colorbase + COLOR_HEADER_BG];
|
||||
|
||||
vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
|
||||
if (!vcell || !vcell->cellblock)
|
||||
return bg_color;
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||
return bg_color;
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
|
||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc);
|
||||
@ -634,11 +539,11 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
||||
{
|
||||
if (is_current)
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_active_bg_color :
|
||||
reg_colors.secondary_active_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
|
||||
}
|
||||
|
||||
if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
|
||||
@ -652,119 +557,135 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
||||
{
|
||||
if (double_alternate_virt)
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_active_bg_color :
|
||||
reg_colors.secondary_active_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||
|
||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||
reg_colors.primary_active_bg_color :
|
||||
reg_colors.secondary_active_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||
}
|
||||
|
||||
if (double_alternate_virt)
|
||||
return vcell->start_primary_color ?
|
||||
reg_colors.primary_bg_color :
|
||||
reg_colors.secondary_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG];
|
||||
|
||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||
reg_colors.primary_bg_color :
|
||||
reg_colors.secondary_bg_color;
|
||||
color_table[colorbase + COLOR_PRIMARY_BG] :
|
||||
color_table[colorbase + COLOR_SECONDARY_BG];
|
||||
}
|
||||
|
||||
if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
|
||||
{
|
||||
if (is_current)
|
||||
return reg_colors.split_active_bg_color;
|
||||
return color_table[colorbase + COLOR_SPLIT_BG_ACTIVE];
|
||||
|
||||
return reg_colors.split_bg_color;
|
||||
return color_table[colorbase + COLOR_SPLIT_BG];
|
||||
}
|
||||
|
||||
PWARN ("Unexpected cursor: %s\n", cursor_name);
|
||||
|
||||
return bg_color;
|
||||
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_fg_color_internal (VirtualLocation virt_loc,
|
||||
SplitRegister *reg,
|
||||
const guint32 *color_table)
|
||||
{
|
||||
const guint32 red_color = color_table[COLOR_NEGATIVE];
|
||||
guint32 fg_color;
|
||||
const char * cell_name;
|
||||
gboolean is_current;
|
||||
gnc_numeric value;
|
||||
Split *split;
|
||||
|
||||
fg_color = gnc_split_register_get_color_internal (virt_loc, reg, color_table, TRUE);
|
||||
|
||||
if (!use_red_for_negative)
|
||||
return fg_color;
|
||||
|
||||
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
||||
if (!split)
|
||||
return fg_color;
|
||||
|
||||
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
||||
|
||||
if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
|
||||
value = get_trans_total_amount (reg, xaccSplitGetParent (split));
|
||||
else if (gnc_cell_name_equal (cell_name, SHRS_CELL))
|
||||
{
|
||||
if (virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc))
|
||||
value = gnc_price_cell_get_value
|
||||
((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
|
||||
SHRS_CELL));
|
||||
else
|
||||
value = xaccSplitGetAmount (split);
|
||||
}
|
||||
else if (gnc_cell_name_equal (cell_name, BALN_CELL))
|
||||
value = xaccSplitGetBalance (split);
|
||||
else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
|
||||
value = gnc_split_register_get_rbaln (virt_loc, reg, TRUE);
|
||||
else if (gnc_cell_name_equal (cell_name, TBALN_CELL))
|
||||
value = get_trans_total_balance (reg, xaccSplitGetParent (split));
|
||||
|
||||
if ((gnc_cell_name_equal (cell_name, BALN_CELL)) ||
|
||||
(gnc_cell_name_equal (cell_name, RBALN_CELL)) ||
|
||||
(gnc_cell_name_equal (cell_name, TBALN_CELL)))
|
||||
{
|
||||
Account *account = xaccSplitGetAccount (split);
|
||||
if (gnc_reverse_balance (account))
|
||||
value = gnc_numeric_neg (value);
|
||||
}
|
||||
|
||||
if (gnc_numeric_negative_p (value))
|
||||
return red_color;
|
||||
|
||||
return fg_color;
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_default);
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_gtkrc_fg_color (VirtualLocation virt_loc,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_gtkrc);
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
||||
gboolean *hatching,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_default, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static RegisterColor
|
||||
gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
|
||||
gboolean *hatching,
|
||||
gpointer user_data)
|
||||
{
|
||||
SplitRegister *reg = user_data;
|
||||
const char *cursor_name;
|
||||
VirtualCell *vcell;
|
||||
gboolean is_current;
|
||||
gboolean double_alternate_virt;
|
||||
|
||||
if (!reg)
|
||||
return COLOR_UNKNOWN;
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
|
||||
return COLOR_HEADER;
|
||||
|
||||
vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
|
||||
if (!vcell || !vcell->cellblock)
|
||||
return COLOR_UNKNOWN;
|
||||
|
||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||
return COLOR_UNKNOWN;
|
||||
|
||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||
virt_loc.vcell_loc);
|
||||
|
||||
cursor_name = vcell->cellblock->cursor_name;
|
||||
|
||||
if (g_strcmp0 (cursor_name, CURSOR_SINGLE_JOURNAL) == 0 ||
|
||||
g_strcmp0 (cursor_name, CURSOR_SINGLE_LEDGER) == 0)
|
||||
{
|
||||
if (is_current)
|
||||
return vcell->start_primary_color ?
|
||||
COLOR_PRIMARY_ACTIVE :
|
||||
COLOR_SECONDARY_ACTIVE;
|
||||
|
||||
return vcell->start_primary_color ?
|
||||
COLOR_PRIMARY : COLOR_SECONDARY;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
|
||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL_NUM_ACTN) == 0 ||
|
||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER) == 0 ||
|
||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER_NUM_ACTN) == 0)
|
||||
{
|
||||
double_alternate_virt = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
|
||||
GNC_PREF_ALT_COLOR_BY_TRANS);
|
||||
if (is_current)
|
||||
{
|
||||
if (double_alternate_virt)
|
||||
return vcell->start_primary_color ?
|
||||
COLOR_PRIMARY_ACTIVE :
|
||||
COLOR_SECONDARY_ACTIVE;
|
||||
|
||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||
COLOR_PRIMARY_ACTIVE :
|
||||
COLOR_SECONDARY_ACTIVE;
|
||||
}
|
||||
|
||||
if (double_alternate_virt)
|
||||
return vcell->start_primary_color ?
|
||||
COLOR_PRIMARY :
|
||||
COLOR_SECONDARY;
|
||||
|
||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||
COLOR_PRIMARY :
|
||||
COLOR_SECONDARY;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
|
||||
{
|
||||
if (is_current)
|
||||
return COLOR_SPLIT_ACTIVE;
|
||||
|
||||
return COLOR_SPLIT;
|
||||
}
|
||||
|
||||
PWARN ("Unexpected cursor: %s\n", cursor_name);
|
||||
|
||||
return COLOR_UNKNOWN;
|
||||
return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_gtkrc, FALSE);
|
||||
}
|
||||
|
||||
static guint32
|
||||
@ -2662,19 +2583,22 @@ gnc_split_register_model_new (void)
|
||||
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_shares_fg_color, SHRS_CELL);
|
||||
model, gnc_split_register_get_fg_color, SHRS_CELL);
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_shares_fg_color, TSHRS_CELL);
|
||||
model, gnc_split_register_get_fg_color, TSHRS_CELL);
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_balance_fg_color, BALN_CELL);
|
||||
model, gnc_split_register_get_fg_color, BALN_CELL);
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_balance_fg_color, TBALN_CELL);
|
||||
model, gnc_split_register_get_fg_color, TBALN_CELL);
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_balance_fg_color, RBALN_CELL);
|
||||
model, gnc_split_register_get_fg_color, RBALN_CELL);
|
||||
|
||||
gnc_table_model_set_fg_color_handler(
|
||||
model, gnc_split_register_get_gtkrc_fg_color, "gtkrc");
|
||||
|
||||
|
||||
gnc_table_model_set_default_bg_color_handler(
|
||||
|
@ -28,16 +28,4 @@
|
||||
TableModel * gnc_split_register_model_new (void);
|
||||
TableModel * gnc_template_register_model_new (void);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COLOR_UNKNOWN,
|
||||
COLOR_HEADER,
|
||||
COLOR_PRIMARY,
|
||||
COLOR_PRIMARY_ACTIVE,
|
||||
COLOR_SECONDARY,
|
||||
COLOR_SECONDARY_ACTIVE,
|
||||
COLOR_SPLIT,
|
||||
COLOR_SPLIT_ACTIVE,
|
||||
} RegisterColor;
|
||||
|
||||
#endif
|
||||
|
@ -238,20 +238,6 @@ typedef enum
|
||||
NUM_CURSOR_CLASSES
|
||||
} CursorClass;
|
||||
|
||||
typedef struct split_register_colors
|
||||
{
|
||||
guint32 header_bg_color;
|
||||
|
||||
guint32 primary_bg_color;
|
||||
guint32 secondary_bg_color;
|
||||
|
||||
guint32 primary_active_bg_color;
|
||||
guint32 secondary_active_bg_color;
|
||||
|
||||
guint32 split_bg_color;
|
||||
guint32 split_active_bg_color;
|
||||
} SplitRegisterColors;
|
||||
|
||||
|
||||
/** @brief A split register created with ::gnc_split_register_new */
|
||||
typedef struct split_register SplitRegister;
|
||||
@ -270,7 +256,7 @@ struct split_register
|
||||
or split action for number
|
||||
field in register */
|
||||
gboolean is_template;
|
||||
gboolean do_auto_complete; /**< whether to use auto-competion */
|
||||
gboolean do_auto_complete; /**< whether to use auto-completion */
|
||||
|
||||
SRInfo * sr_info; /**< private data; outsiders should not access this */
|
||||
};
|
||||
|
@ -345,19 +345,21 @@ gnc_table_get_label (Table *table, VirtualLocation virt_loc)
|
||||
return label;
|
||||
}
|
||||
|
||||
guint32
|
||||
gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
||||
static guint32
|
||||
gnc_table_get_fg_color_internal (Table *table, VirtualLocation virt_loc,
|
||||
gboolean want_gtkrc)
|
||||
{
|
||||
TableGetFGColorHandler fg_color_handler;
|
||||
const char *cell_name;
|
||||
const char *handler_name = "gtkrc";
|
||||
|
||||
if (!table || !table->model)
|
||||
return 0x0; /* black */
|
||||
|
||||
cell_name = gnc_table_get_cell_name (table, virt_loc);
|
||||
if (!want_gtkrc)
|
||||
handler_name = gnc_table_get_cell_name (table, virt_loc);
|
||||
|
||||
fg_color_handler = gnc_table_model_get_fg_color_handler (table->model,
|
||||
cell_name);
|
||||
handler_name);
|
||||
if (!fg_color_handler)
|
||||
return 0x0;
|
||||
|
||||
@ -365,11 +367,24 @@ gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
||||
}
|
||||
|
||||
guint32
|
||||
gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
gboolean *hatching)
|
||||
gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
||||
{
|
||||
return gnc_table_get_fg_color_internal (table, virt_loc, FALSE);
|
||||
}
|
||||
|
||||
guint32
|
||||
gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc)
|
||||
{
|
||||
return gnc_table_get_fg_color_internal (table, virt_loc, TRUE);
|
||||
}
|
||||
|
||||
static guint32
|
||||
gnc_table_get_bg_color_internal (Table *table, VirtualLocation virt_loc,
|
||||
gboolean *hatching,
|
||||
gboolean want_gtkrc)
|
||||
{
|
||||
TableGetBGColorHandler bg_color_handler;
|
||||
const char *cell_name;
|
||||
const char *handler_name = "gtkrc";
|
||||
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
@ -377,10 +392,11 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
if (!table || !table->model)
|
||||
return 0xffffff; /* white */
|
||||
|
||||
cell_name = gnc_table_get_cell_name (table, virt_loc);
|
||||
if (!want_gtkrc)
|
||||
handler_name = gnc_table_get_cell_name (table, virt_loc);
|
||||
|
||||
bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
|
||||
cell_name);
|
||||
handler_name);
|
||||
if (!bg_color_handler)
|
||||
return 0xffffff;
|
||||
|
||||
@ -388,25 +404,18 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
table->model->handler_user_data);
|
||||
}
|
||||
|
||||
guint32
|
||||
gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
gboolean *hatching)
|
||||
{
|
||||
return gnc_table_get_bg_color_internal (table, virt_loc, hatching, FALSE);
|
||||
}
|
||||
|
||||
guint32
|
||||
gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
gboolean *hatching)
|
||||
{
|
||||
TableGetBGColorHandler bg_color_handler;
|
||||
|
||||
if (hatching)
|
||||
*hatching = FALSE;
|
||||
|
||||
if (!table || !table->model)
|
||||
return 0xffffff; /* white */
|
||||
|
||||
bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
|
||||
"gtkrc");
|
||||
if (!bg_color_handler)
|
||||
return 0xffffff;
|
||||
|
||||
return bg_color_handler (virt_loc, hatching,
|
||||
table->model->handler_user_data);
|
||||
return gnc_table_get_bg_color_internal (table, virt_loc, hatching, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -156,6 +156,92 @@ struct table
|
||||
gpointer ui_data;
|
||||
};
|
||||
|
||||
/** Color definitions used for table elements */
|
||||
typedef enum
|
||||
{
|
||||
/* Colors used for background drawing */
|
||||
COLOR_UNKNOWN_BG,
|
||||
COLOR_HEADER_BG,
|
||||
COLOR_PRIMARY_BG,
|
||||
COLOR_PRIMARY_BG_ACTIVE,
|
||||
COLOR_SECONDARY_BG,
|
||||
COLOR_SECONDARY_BG_ACTIVE,
|
||||
COLOR_SPLIT_BG,
|
||||
COLOR_SPLIT_BG_ACTIVE,
|
||||
|
||||
/* Colors used for foreground drawing (text etc)
|
||||
* ATTENTION: the background and foreground lists should have
|
||||
* the same types (the same amount of entries) !
|
||||
* The code relies on this ! */
|
||||
COLOR_UNKNOWN_FG,
|
||||
COLOR_HEADER_FG,
|
||||
COLOR_PRIMARY_FG,
|
||||
COLOR_PRIMARY_FG_ACTIVE,
|
||||
COLOR_SECONDARY_FG,
|
||||
COLOR_SECONDARY_FG_ACTIVE,
|
||||
COLOR_SPLIT_FG,
|
||||
COLOR_SPLIT_FG_ACTIVE,
|
||||
|
||||
/* Other colors */
|
||||
COLOR_NEGATIVE, /* Color to use for negative numbers */
|
||||
} RegisterColor;
|
||||
|
||||
|
||||
|
||||
|
||||
/* Alternative color tables to use for the register.
|
||||
* The colors in this array are ordered according to the RegisterColor Enum
|
||||
* Be careful to respect this order !
|
||||
*/
|
||||
static const guint32 reg_colors_default [] =
|
||||
{
|
||||
0xFFFFFF, // COLOR_UNKNOWN_BG
|
||||
0x96B183, // COLOR_HEADER_BG
|
||||
0xBFDEB9, // COLOR_PRIMARY_BG
|
||||
0xFFEF98, // COLOR_PRIMARY_BG_ACTIVE
|
||||
0xF6FFDA, // COLOR_SECONDARY_BG
|
||||
0xFFEF98, // COLOR_SECONDARY_BG_ACTIVE
|
||||
0xEDE7D3, // COLOR_SPLIT_BG
|
||||
0xFFEF98, // COLOR_SPLIT_BG_ACTIVE
|
||||
|
||||
0x000000, // COLOR_UNKNOWN_FG
|
||||
0x000000, // COLOR_HEADER_FG
|
||||
0x000000, // COLOR_PRIMARY_FG
|
||||
0x000000, // COLOR_PRIMARY_FG_ACTIVE
|
||||
0x000000, // COLOR_SECONDARY_FG
|
||||
0x000000, // COLOR_SECONDARY_FG_ACTIVE
|
||||
0x000000, // COLOR_SPLIT_FG
|
||||
0x000000, // COLOR_SPLIT_FG_ACTIVE
|
||||
|
||||
0xFF0000, // COLOR_NEGATIVE
|
||||
};
|
||||
|
||||
/* The colors in this array are ordered according to the RegisterColor Enum
|
||||
* Be careful to respect this order !
|
||||
*/
|
||||
static const guint32 reg_colors_gtkrc [] =
|
||||
{
|
||||
COLOR_UNKNOWN_BG, // COLOR_UNKNOWN_BG
|
||||
COLOR_HEADER_BG, // COLOR_HEADER_BG
|
||||
COLOR_PRIMARY_BG, // COLOR_PRIMARY_BG
|
||||
COLOR_PRIMARY_BG_ACTIVE, // COLOR_PRIMARY_BG_ACTIVE
|
||||
COLOR_SECONDARY_BG, // COLOR_SECONDARY_BG
|
||||
COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
|
||||
COLOR_SPLIT_BG, // COLOR_SPLIT_BG
|
||||
COLOR_SPLIT_BG_ACTIVE, // COLOR_SPLIT_BG_ACTIVE
|
||||
|
||||
COLOR_UNKNOWN_FG, // COLOR_UNKNOWN_FG
|
||||
COLOR_HEADER_FG, // COLOR_HEADER_FG
|
||||
COLOR_PRIMARY_FG, // COLOR_PRIMARY_FG
|
||||
COLOR_PRIMARY_FG_ACTIVE, // COLOR_PRIMARY_FG_ACTIVE
|
||||
COLOR_SECONDARY_FG, // COLOR_SECONDARY_FG
|
||||
COLOR_SECONDARY_FG_ACTIVE, // COLOR_SECONDARY_FG_ACTIVE
|
||||
COLOR_SPLIT_FG, // COLOR_SPLIT_FG
|
||||
COLOR_SPLIT_FG_ACTIVE, // COLOR_SPLIT_FG_ACTIVE
|
||||
|
||||
COLOR_NEGATIVE, // COLOR_NEGATIVE
|
||||
};
|
||||
|
||||
|
||||
/* Set the default gui handlers used by new tables. */
|
||||
void gnc_table_set_default_gui_handlers (TableGUIHandlers *gui_handlers);
|
||||
@ -210,6 +296,8 @@ CellIOFlags gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc);
|
||||
|
||||
guint32 gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc);
|
||||
|
||||
guint32 gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc);
|
||||
|
||||
guint32 gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
gboolean *hatching);
|
||||
guint32 gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
||||
|
@ -605,17 +605,25 @@ draw_cell (GnucashGrid *grid,
|
||||
context = pango_layout_get_context (layout);
|
||||
font = pango_font_description_copy (pango_context_get_font_description (context));
|
||||
|
||||
argb = gnc_table_get_fg_color (table, virt_loc);
|
||||
#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
|
||||
// Are we in a read-only row? Then make the foreground color somewhat less black
|
||||
if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
|
||||
&& (table->model->dividing_row_upper >= 0)
|
||||
&& (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
|
||||
if (grid->sheet->use_theme_colors)
|
||||
{
|
||||
argb = inc_intensity_10percent(argb);
|
||||
color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
|
||||
fg_color = get_gtkrc_color(grid->sheet, color_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
argb = gnc_table_get_fg_color (table, virt_loc);
|
||||
#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
|
||||
// Are we in a read-only row? Then make the foreground color somewhat less black
|
||||
if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
|
||||
&& (table->model->dividing_row_upper >= 0)
|
||||
&& (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
|
||||
{
|
||||
argb = inc_intensity_10percent(argb);
|
||||
}
|
||||
#endif
|
||||
fg_color = gnucash_color_argb_to_gdk (argb);
|
||||
fg_color = gnucash_color_argb_to_gdk (argb);
|
||||
}
|
||||
|
||||
gdk_gc_set_foreground (grid->gc, fg_color);
|
||||
|
||||
|
@ -75,7 +75,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
||||
VirtualLocation virt_loc;
|
||||
VirtualCell *vcell;
|
||||
CellDimensions *cd;
|
||||
GdkColor *bg_color;
|
||||
GdkColor *bg_color, *fg_color;
|
||||
int xpaint, ypaint;
|
||||
const char *text;
|
||||
CellBlock *cb;
|
||||
@ -94,11 +94,15 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
||||
color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
|
||||
NULL);
|
||||
bg_color = get_gtkrc_color(header->sheet, color_type);
|
||||
color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
|
||||
fg_color = get_gtkrc_color(header->sheet, color_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
argb = gnc_table_get_bg_color (table, virt_loc, NULL);
|
||||
bg_color = gnucash_color_argb_to_gdk (argb);
|
||||
argb = gnc_table_get_fg_color (table, virt_loc);
|
||||
fg_color = gnucash_color_argb_to_gdk (argb);
|
||||
}
|
||||
|
||||
h = style->dimensions->height;
|
||||
@ -111,7 +115,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
||||
style->dimensions->width, h);
|
||||
|
||||
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
||||
gdk_gc_set_foreground (header->gc, &gn_black);
|
||||
gdk_gc_set_foreground (header->gc, fg_color);
|
||||
|
||||
gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y,
|
||||
style->dimensions->width, h);
|
||||
@ -121,7 +125,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
||||
|
||||
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
||||
gdk_gc_set_background (header->gc, &gn_white);
|
||||
gdk_gc_set_foreground (header->gc, &gn_black);
|
||||
gdk_gc_set_foreground (header->gc, fg_color);
|
||||
/*font = gnucash_register_font;*/
|
||||
|
||||
vcell = gnc_table_get_virtual_cell
|
||||
|
@ -196,16 +196,20 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
|
||||
item_edit->virt_loc,
|
||||
&hatching);
|
||||
info->bg_color = get_gtkrc_color(item_edit->sheet, color_type);
|
||||
color_type = gnc_table_get_gtkrc_fg_color (table,
|
||||
item_edit->virt_loc);
|
||||
info->fg_color = get_gtkrc_color(item_edit->sheet, color_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
argb = gnc_table_get_bg_color (table, item_edit->virt_loc,
|
||||
&hatching);
|
||||
info->bg_color = gnucash_color_argb_to_gdk (argb);
|
||||
argb = gnc_table_get_fg_color (table, item_edit->virt_loc);
|
||||
info->fg_color = gnucash_color_argb_to_gdk (argb);
|
||||
}
|
||||
|
||||
info->hatching = hatching;
|
||||
info->fg_color = &gn_black;
|
||||
|
||||
info->bg_color2 = &gn_dark_gray;
|
||||
info->fg_color2 = &gn_white;
|
||||
|
@ -2609,31 +2609,49 @@ get_gtkrc_color (GnucashSheet *sheet,
|
||||
{
|
||||
GtkWidget *widget = NULL;
|
||||
GtkStyle *style;
|
||||
GdkColor *white;
|
||||
GdkColor *white, *black, *red;
|
||||
GdkColor *color = NULL;
|
||||
|
||||
white = gnucash_color_argb_to_gdk (0xFFFFFF);
|
||||
black = gnucash_color_argb_to_gdk (0x000000);
|
||||
red = gnucash_color_argb_to_gdk (0xFF0000); /* Hardcoded...*/
|
||||
switch (field_type)
|
||||
{
|
||||
default:
|
||||
return white;
|
||||
|
||||
case COLOR_HEADER:
|
||||
case COLOR_UNKNOWN_BG:
|
||||
return white;
|
||||
|
||||
case COLOR_UNKNOWN_FG:
|
||||
return black;
|
||||
|
||||
case COLOR_NEGATIVE:
|
||||
return red;
|
||||
|
||||
case COLOR_HEADER_BG:
|
||||
case COLOR_HEADER_FG:
|
||||
widget = sheet->header_color;
|
||||
break;
|
||||
|
||||
case COLOR_PRIMARY:
|
||||
case COLOR_PRIMARY_ACTIVE:
|
||||
case COLOR_PRIMARY_BG:
|
||||
case COLOR_PRIMARY_BG_ACTIVE:
|
||||
case COLOR_PRIMARY_FG:
|
||||
case COLOR_PRIMARY_FG_ACTIVE:
|
||||
widget = sheet->primary_color;
|
||||
break;
|
||||
|
||||
case COLOR_SECONDARY:
|
||||
case COLOR_SECONDARY_ACTIVE:
|
||||
case COLOR_SECONDARY_BG:
|
||||
case COLOR_SECONDARY_BG_ACTIVE:
|
||||
case COLOR_SECONDARY_FG:
|
||||
case COLOR_SECONDARY_FG_ACTIVE:
|
||||
widget = sheet->secondary_color;
|
||||
break;
|
||||
|
||||
case COLOR_SPLIT:
|
||||
case COLOR_SPLIT_ACTIVE:
|
||||
case COLOR_SPLIT_BG:
|
||||
case COLOR_SPLIT_BG_ACTIVE:
|
||||
case COLOR_SPLIT_FG:
|
||||
case COLOR_SPLIT_FG_ACTIVE:
|
||||
widget = sheet->split_color;
|
||||
break;
|
||||
}
|
||||
@ -2647,18 +2665,31 @@ get_gtkrc_color (GnucashSheet *sheet,
|
||||
default:
|
||||
return white;
|
||||
|
||||
case COLOR_HEADER:
|
||||
case COLOR_PRIMARY:
|
||||
case COLOR_SECONDARY:
|
||||
case COLOR_SPLIT:
|
||||
case COLOR_HEADER_BG:
|
||||
case COLOR_PRIMARY_BG:
|
||||
case COLOR_SECONDARY_BG:
|
||||
case COLOR_SPLIT_BG:
|
||||
color = &style->base[GTK_STATE_NORMAL];
|
||||
break;
|
||||
|
||||
case COLOR_PRIMARY_ACTIVE:
|
||||
case COLOR_SECONDARY_ACTIVE:
|
||||
case COLOR_SPLIT_ACTIVE:
|
||||
case COLOR_PRIMARY_BG_ACTIVE:
|
||||
case COLOR_SECONDARY_BG_ACTIVE:
|
||||
case COLOR_SPLIT_BG_ACTIVE:
|
||||
color = &style->base[GTK_STATE_SELECTED];
|
||||
break;
|
||||
|
||||
case COLOR_HEADER_FG:
|
||||
case COLOR_PRIMARY_FG:
|
||||
case COLOR_SECONDARY_FG:
|
||||
case COLOR_SPLIT_FG:
|
||||
color = &style->text[GTK_STATE_NORMAL];
|
||||
break;
|
||||
|
||||
case COLOR_PRIMARY_FG_ACTIVE:
|
||||
case COLOR_SECONDARY_FG_ACTIVE:
|
||||
case COLOR_SPLIT_FG_ACTIVE:
|
||||
color = &style->text[GTK_STATE_SELECTED];
|
||||
break;
|
||||
}
|
||||
|
||||
gnucash_color_alloc_gdk(color);
|
||||
|
Loading…
Reference in New Issue
Block a user