#ifndef _FORMAT_H #define _FORMAT_H #include "ledger.h" #include "valexpr.h" #include "walk.h" namespace ledger { std::string truncated(const std::string& str, unsigned int width); std::string partial_account_name(const account_t * account, const unsigned int start_depth); struct element_t { enum kind_t { STRING, VALUE_EXPR, DATE_STRING, CLEARED, CODE, PAYEE, ACCOUNT_NAME, ACCOUNT_FULLNAME, OPT_AMOUNT, VALUE, TOTAL, SPACER }; bool align_left; unsigned int min_width; unsigned int max_width; kind_t type; std::string chars; node_t * val_expr; struct element_t * next; element_t() : align_left(false), min_width(0), max_width(0), type(STRING), val_expr(NULL), next(NULL) {} ~element_t() { if (val_expr) delete val_expr; if (next) delete next; // recursive, but not too deep } }; struct format_t { element_t * elements; static std::auto_ptr value_expr; static std::auto_ptr total_expr; format_t(const std::string& _format) : elements(NULL) { reset(_format); } ~format_t() { if (elements) delete elements; } void reset(const std::string& _format) { if (elements) delete elements; elements = parse_elements(_format); } static element_t * parse_elements(const std::string& fmt); void format_elements(std::ostream& out, const details_t& details) const; static void compute_value(balance_t& result, const details_t& details) { if (value_expr.get()) value_expr->compute(result, details); } static void compute_total(balance_t& result, const details_t& details) { if (total_expr.get()) total_expr->compute(result, details); } }; #define COLLAPSED_REGISTER 1 // support collapsed registers class format_transaction : public item_handler { std::ostream& output_stream; const format_t& first_line_format; const format_t& next_lines_format; #ifdef COLLAPSED_REGISTER const bool collapsed; #endif const bool inverted; item_predicate disp_pred; #ifdef COLLAPSED_REGISTER mutable balance_pair_t subtotal; mutable unsigned int count; #endif mutable entry_t * last_entry; mutable transaction_t * last_xact; public: format_transaction(std::ostream& _output_stream, const format_t& _first_line_format, const format_t& _next_lines_format, const std::string& display_predicate = NULL, #ifdef COLLAPSED_REGISTER const bool _collapsed = false, #endif const bool _inverted = false) : output_stream(_output_stream), first_line_format(_first_line_format), next_lines_format(_next_lines_format), #ifdef COLLAPSED_REGISTER collapsed(_collapsed), #endif inverted(_inverted), disp_pred(display_predicate), #ifdef COLLAPSED_REGISTER count(0), #endif last_entry(NULL), last_xact(NULL) {} #ifdef COLLAPSED_REGISTER virtual ~format_transaction() { if (subtotal) report_cumulative_subtotal(); } void report_cumulative_subtotal() const; #endif virtual void operator()(transaction_t * xact) const; }; class changed_value_filter : public item_handler { const item_handler& handler; mutable entry_t modified_entry; mutable transaction_t modified_xact; mutable transaction_t * last_xact; public: changed_value_filter(const item_handler& _handler) : handler(_handler), modified_xact(&modified_entry, NULL), last_xact(NULL) { modified_entry.payee = "Commodities revalued"; } virtual ~changed_value_filter() { (*this)(NULL); } void operator()(transaction_t * xact) const { if (last_xact) { balance_t prev_bal, cur_bal; format_t::compute_total(prev_bal, details_t(last_xact)); std::time_t current = xact ? xact->entry->date : std::time(NULL); std::time_t prev_date = last_xact->entry->date; last_xact->entry->date = current; format_t::compute_total(cur_bal, details_t(last_xact)); last_xact->entry->date = prev_date; if (balance_t diff = cur_bal - prev_bal) { modified_entry.date = current; // jww (2004-08-07): What if there are multiple commodities? assert(diff.amounts.size() == 1); modified_xact.amount = diff.amount(); modified_xact.total = diff; modified_xact.total.negate(); handler(&modified_xact); } } if (xact) handler(xact); last_xact = xact; } }; class format_account : public item_handler { std::ostream& output_stream; const format_t& format; item_predicate disp_pred; public: format_account(std::ostream& _output_stream, const format_t& _format, const std::string& display_predicate = NULL) : output_stream(_output_stream), format(_format), disp_pred(display_predicate) {} virtual ~format_account() {} static bool disp_subaccounts_p(const account_t * account, const item_predicate& disp_pred, const account_t *& to_show); static bool disp_subaccounts_p(const account_t * account) { const account_t * temp; return disp_subaccounts_p(account, item_predicate(NULL), temp); } static bool display_account(const account_t * account, const item_predicate& disp_pred); virtual void operator()(account_t * account) const { if (display_account(account, disp_pred)) { format.format_elements(output_stream, details_t(account)); account->dflags |= ACCOUNT_DISPLAYED; } } }; class format_equity : public item_handler { std::ostream& output_stream; const format_t& first_line_format; const format_t& next_lines_format; item_predicate disp_pred; mutable balance_t total; public: format_equity(std::ostream& _output_stream, const format_t& _first_line_format, const format_t& _next_lines_format, const std::string& display_predicate = NULL) : output_stream(_output_stream), first_line_format(_first_line_format), next_lines_format(_next_lines_format), disp_pred(display_predicate) { entry_t header_entry; header_entry.payee = "Opening Balances"; header_entry.date = std::time(NULL); first_line_format.format_elements(output_stream, details_t(&header_entry)); } virtual ~format_equity() { account_t summary(NULL, "Equity:Opening Balances"); summary.value = - total; next_lines_format.format_elements(output_stream, details_t(&summary)); } virtual void operator()(account_t * account) const { if (format_account::display_account(account, disp_pred)) { next_lines_format.format_elements(output_stream, details_t(account)); account->dflags |= ACCOUNT_DISPLAYED; total += account->value.quantity; } } }; } // namespace ledger #endif // _REPORT_H