[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [6258] Implement new "group premium quote" report
From: |
Greg Chicares |
Subject: |
[lmi-commits] [6258] Implement new "group premium quote" report |
Date: |
Wed, 19 Aug 2015 17:02:28 +0000 |
Revision: 6258
http://svn.sv.gnu.org/viewvc/?view=rev&root=lmi&revision=6258
Author: chicares
Date: 2015-08-19 17:02:28 +0000 (Wed, 19 Aug 2015)
Log Message:
-----------
Implement new "group premium quote" report
Modified Paths:
--------------
lmi/trunk/ChangeLog
lmi/trunk/Makefile.am
lmi/trunk/census_view.cpp
lmi/trunk/census_view.hpp
lmi/trunk/emit_ledger.cpp
lmi/trunk/emit_ledger.hpp
lmi/trunk/gpt_view.cpp
lmi/trunk/ihs_acctval.cpp
lmi/trunk/illustration_view.cpp
lmi/trunk/main_wx.cpp
lmi/trunk/main_wx_test.cpp
lmi/trunk/mc_enum_type_enums.hpp
lmi/trunk/mc_enum_types.cpp
lmi/trunk/mc_enum_types_aux.cpp
lmi/trunk/mec_view.cpp
lmi/trunk/menus.xrc
lmi/trunk/objects.make
lmi/trunk/skeleton.cpp
lmi/trunk/toolbar.xrc
lmi/trunk/workhorse.make
Added Paths:
-----------
lmi/trunk/group_quote_pdf_gen.cpp
lmi/trunk/group_quote_pdf_gen.hpp
lmi/trunk/group_quote_pdf_gen_wx.cpp
lmi/trunk/wx_table_generator.cpp
lmi/trunk/wx_table_generator.hpp
Modified: lmi/trunk/ChangeLog
===================================================================
--- lmi/trunk/ChangeLog 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/ChangeLog 2015-08-19 17:02:28 UTC (rev 6258)
@@ -36630,3 +36630,37 @@
ihs_basicval.cpp
Remove an ugly disused data member.
+20150818T1840Z <address@hidden> [477]
+
+ workhorse.make
+ wx_pdfdoc_test.cpp [expunged]
+Expunge a temporary test that has served its purpose.
+
+20150819T1702Z <address@hidden> [477]
+
+ Makefile.am
+ census_view.cpp
+ census_view.hpp
+ emit_ledger.cpp
+ emit_ledger.hpp
+ gpt_view.cpp
+ group_quote_pdf_gen.cpp [new file]
+ group_quote_pdf_gen.hpp [new file]
+ group_quote_pdf_gen_wx.cpp [new file]
+ ihs_acctval.cpp
+ illustration_view.cpp
+ main_wx.cpp
+ main_wx_test.cpp
+ mc_enum_type_enums.hpp
+ mc_enum_types.cpp
+ mc_enum_types_aux.cpp
+ mec_view.cpp
+ menus.xrc
+ objects.make
+ skeleton.cpp
+ toolbar.xrc
+ workhorse.make
+ wx_table_generator.cpp [new file]
+ wx_table_generator.hpp [new file]
+Implement new "group premium quote" report.
+
Modified: lmi/trunk/Makefile.am
===================================================================
--- lmi/trunk/Makefile.am 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/Makefile.am 2015-08-19 17:02:28 UTC (rev 6258)
@@ -167,6 +167,7 @@
file_command_wx.cpp \
gpt_document.cpp \
gpt_view.cpp \
+ group_quote_pdf_generator_wx.cpp \
icon_monger.cpp \
illustration_document.cpp \
illustration_view.cpp \
@@ -198,6 +199,7 @@
tier_view_editor.cpp \
transferor.cpp \
view_ex.cpp \
+ wx_table_generator.cpp \
wx_utility.cpp
# main program executables
@@ -310,6 +312,7 @@
getopt.cpp \
global_settings.cpp \
group_values.cpp \
+ group_quote_pdf_generator.cpp \
illustrator.cpp \
input.cpp \
input_harmonization.cpp \
@@ -1103,6 +1106,7 @@
gpt_state.hpp \
gpt_view.hpp \
gpt_xml_document.hpp \
+ group_quote_pdf_gen.hpp \
group_values.hpp \
handle_exceptions.hpp \
icon_monger.hpp \
@@ -1228,6 +1232,7 @@
view_ex.hpp \
view_ex.tpp \
wx_new.hpp \
+ wx_table_generator.hpp \
wx_utility.hpp \
wx_workarounds.hpp \
xml_lmi.hpp \
Modified: lmi/trunk/census_view.cpp
===================================================================
--- lmi/trunk/census_view.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/census_view.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -810,6 +810,7 @@
EVT_MENU(XRCID("print_case_to_disk"
),CensusView::UponPrintCaseToDisk )
EVT_MENU(XRCID("print_spreadsheet"
),CensusView::UponRunCaseToSpreadsheet )
EVT_MENU(XRCID("print_group_roster"
),CensusView::UponRunCaseToGroupRoster )
+ EVT_MENU(XRCID("print_group_quote"
),CensusView::UponRunCaseToGroupQuote )
EVT_MENU(XRCID("paste_census" ),CensusView::UponPasteCensus
)
EVT_MENU(XRCID("add_cell" ),CensusView::UponAddCell
)
EVT_MENU(XRCID("delete_cells" ),CensusView::UponDeleteCells
)
@@ -825,6 +826,7 @@
EVT_UPDATE_UI(XRCID("print_case_to_disk"
),CensusView::UponUpdateAlwaysEnabled )
EVT_UPDATE_UI(XRCID("print_spreadsheet"
),CensusView::UponUpdateAlwaysEnabled )
EVT_UPDATE_UI(XRCID("print_group_roster"
),CensusView::UponUpdateAlwaysEnabled )
+ EVT_UPDATE_UI(XRCID("print_group_quote"
),CensusView::UponUpdateAlwaysEnabled )
EVT_UPDATE_UI(XRCID("paste_census"
),CensusView::UponUpdateAlwaysEnabled )
EVT_UPDATE_UI(XRCID("add_cell"
),CensusView::UponUpdateAlwaysEnabled )
EVT_UPDATE_UI(XRCID("delete_cells"
),CensusView::UponUpdateNonemptySelection)
@@ -1540,6 +1542,13 @@
DoAllCells(mce_emit_group_roster);
}
+/// Print group quote to PDF file.
+
+void CensusView::UponRunCaseToGroupQuote(wxCommandEvent&)
+{
+ DoAllCells(mce_emit_group_quote);
+}
+
/// Paste a census from the clipboard.
///
/// See unit tests in Skeleton::UponTestPasting().
Modified: lmi/trunk/census_view.hpp
===================================================================
--- lmi/trunk/census_view.hpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/census_view.hpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -84,6 +84,7 @@
void UponRunCase (wxCommandEvent&);
void UponRunCaseToSpreadsheet (wxCommandEvent&);
void UponRunCaseToGroupRoster (wxCommandEvent&);
+ void UponRunCaseToGroupQuote (wxCommandEvent&);
void UponUpdateAlwaysDisabled (wxUpdateUIEvent&);
void UponUpdateAlwaysEnabled (wxUpdateUIEvent&);
void UponUpdateSingleSelection (wxUpdateUIEvent&);
Modified: lmi/trunk/emit_ledger.cpp
===================================================================
--- lmi/trunk/emit_ledger.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/emit_ledger.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -32,6 +32,7 @@
#include "custom_io_0.hpp"
#include "custom_io_1.hpp"
#include "file_command.hpp"
+#include "group_quote_pdf_gen.hpp"
#include "ledger.hpp"
#include "ledger_text_formats.hpp"
#include "ledger_xsl.hpp"
@@ -87,6 +88,13 @@
std::remove(spreadsheet_filename.c_str());
PrintRosterHeaders(spreadsheet_filename);
}
+ if(emission_ & mce_emit_group_quote)
+ {
+ LMI_ASSERT(!case_filepath_.empty());
+ std::string const pdf_filename = case_filepath_.string() +
".quote.pdf";
+ std::remove(pdf_filename.c_str());
+ group_quote_gen_ = group_quote_pdf_generator::create();
+ }
return timer.stop().elapsed_seconds();
}
@@ -145,6 +153,10 @@
+ configurable_settings::instance().spreadsheet_file_extension()
);
}
+ if(emission_ & mce_emit_group_quote)
+ {
+ group_quote_gen_->add_ledger(ledger);
+ }
if(emission_ & mce_emit_text_stream)
{
PrintLedgerFlatText(ledger, std::cout);
@@ -180,7 +192,12 @@
{
Timer timer;
- ; // Nothing to do for now.
+ if(emission_ & mce_emit_group_quote)
+ {
+ LMI_ASSERT(!case_filepath_.empty());
+ std::string const pdf_filename = case_filepath_.string() +
".quote.pdf";
+ group_quote_gen_->save(pdf_filename);
+ }
return timer.stop().elapsed_seconds();
}
Modified: lmi/trunk/emit_ledger.hpp
===================================================================
--- lmi/trunk/emit_ledger.hpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/emit_ledger.hpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -32,7 +32,9 @@
#include "uncopyable_lmi.hpp"
#include <boost/filesystem/path.hpp>
+#include <boost/shared_ptr.hpp>
+class group_quote_pdf_generator;
class Ledger;
/// Emit a group of ledgers in various guises.
@@ -55,6 +57,9 @@
private:
fs::path const& case_filepath_;
mcenum_emission emission_;
+
+ // Used only if emission includes mce_emit_group_quote; empty otherwise.
+ boost::shared_ptr<group_quote_pdf_generator> group_quote_gen_;
};
double LMI_SO emit_ledger
Modified: lmi/trunk/gpt_view.cpp
===================================================================
--- lmi/trunk/gpt_view.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/gpt_view.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -84,6 +84,7 @@
EVT_UPDATE_UI(XRCID("print_case_to_disk"
),gpt_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("print_spreadsheet"
),gpt_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("print_group_roster"
),gpt_view::UponUpdateInapplicable)
+ EVT_UPDATE_UI(XRCID("print_group_quote"
),gpt_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("paste_census"
),gpt_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("add_cell"
),gpt_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("delete_cells"
),gpt_view::UponUpdateInapplicable)
Added: lmi/trunk/group_quote_pdf_gen.cpp
===================================================================
--- lmi/trunk/group_quote_pdf_gen.cpp (rev 0)
+++ lmi/trunk/group_quote_pdf_gen.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -0,0 +1,55 @@
+// Generate group premium quote PDF file.
+//
+// Copyright (C) 2015 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifdef __BORLANDC__
+# include "pchfile.hpp"
+# pragma hdrstop
+#endif // __BORLANDC__
+
+#include "group_quote_pdf_gen.hpp"
+
+#include "callback.hpp"
+
+namespace
+{
+callback<group_quote_pdf_generator::creator_type>
+ group_quote_pdf_generator_create_callback;
+} // Unnnamed namespace.
+
+typedef group_quote_pdf_generator::creator_type FunctionPointer;
+template<> FunctionPointer callback<FunctionPointer>::function_pointer_ = 0;
+
+bool group_quote_pdf_generator::set_creator(creator_type f)
+{
+ group_quote_pdf_generator_create_callback.initialize(f);
+ return true;
+}
+
+boost::shared_ptr<group_quote_pdf_generator>
group_quote_pdf_generator::create()
+{
+ return group_quote_pdf_generator_create_callback()();
+}
+
+group_quote_pdf_generator::~group_quote_pdf_generator()
+{
+}
Property changes on: lmi/trunk/group_quote_pdf_gen.cpp
___________________________________________________________________
Added: svn:keywords
+ Id
Added: lmi/trunk/group_quote_pdf_gen.hpp
===================================================================
--- lmi/trunk/group_quote_pdf_gen.hpp (rev 0)
+++ lmi/trunk/group_quote_pdf_gen.hpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -0,0 +1,64 @@
+// Generate group premium quote PDF file.
+//
+// Copyright (C) 2015 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifndef group_quote_pdf_gen_hpp
+#define group_quote_pdf_gen_hpp
+
+#include "config.hpp"
+
+#include "so_attributes.hpp"
+#include "uncopyable_lmi.hpp"
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+
+class Ledger;
+
+/// Abstract base class for generating group premium quote PDFs.
+///
+/// Although there is currently only a single concrete implementation of this
+/// abstract base class and no other implementations are planned, splitting the
+/// PDF generation functionality into an abstract base and the concrete derived
+/// class is still needed because the former is part of liblmi while the latter
+/// uses wxPdfDocument and other wx facilities and is only part of libskeleton.
+
+class LMI_SO group_quote_pdf_generator
+ :private lmi::uncopyable<group_quote_pdf_generator>
+{
+ public:
+ typedef boost::shared_ptr<group_quote_pdf_generator> (*creator_type)();
+
+ static bool set_creator(creator_type);
+ static boost::shared_ptr<group_quote_pdf_generator> create();
+
+ virtual ~group_quote_pdf_generator();
+
+ virtual void add_ledger(Ledger const& ledger) = 0;
+ virtual void save(std::string const& output_filename) = 0;
+
+ protected:
+ group_quote_pdf_generator() {}
+};
+
+#endif // group_quote_pdf_gen_hpp
Property changes on: lmi/trunk/group_quote_pdf_gen.hpp
___________________________________________________________________
Added: svn:keywords
+ Id
Added: lmi/trunk/group_quote_pdf_gen_wx.cpp
===================================================================
--- lmi/trunk/group_quote_pdf_gen_wx.cpp (rev 0)
+++ lmi/trunk/group_quote_pdf_gen_wx.cpp 2015-08-19 17:02:28 UTC (rev
6258)
@@ -0,0 +1,903 @@
+// Generate group premium quote PDF file.
+//
+// Copyright (C) 2015 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifdef __BORLANDC__
+# include "pchfile.hpp"
+# pragma hdrstop
+#endif // __BORLANDC__
+
+#include "group_quote_pdf_gen.hpp"
+
+#include "alert.hpp"
+#include "assert_lmi.hpp"
+#include "calendar_date.hpp" // jdn_t()
+#include "force_linking.hpp"
+#include "ledger.hpp"
+#include "ledger_invariant.hpp"
+#include "ledger_text_formats.hpp" // ledger_format()
+#include "oecumenic_enumerations.hpp" // oenum_format_style
+#include "wx_table_generator.hpp"
+#include "wx_utility.hpp" // ConvertDateToWx()
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/static_assert.hpp>
+
+#include <wx/datetime.h>
+#include <wx/html/htmlcell.h>
+#include <wx/html/winpars.h>
+#include <wx/image.h>
+#include <wx/pdfdc.h>
+
+#include <limits>
+#include <utility> // std::pair
+#include <vector>
+
+LMI_FORCE_LINKING_IN_SITU(group_quote_pdf_generator_wx)
+
+namespace
+{
+
+enum enum_output_mode
+ {e_output_normal
+ ,e_output_measure_only
+ };
+
+/// Load the image from the given file. Throw on failure.
+
+wxImage load_image(char const* file)
+{
+ wxImage image(file);
+ if(!image.IsOk())
+ {
+ fatal_error()
+ << "File '"
+ << file
+ << "' is required but could not be found. Try reinstalling."
+ << LMI_FLUSH
+ ;
+ }
+
+ return image;
+}
+
+/// Render, or just pretend rendering in order to measure it, the given HTML
+/// contents at the specified position wrapping it at the given width.
+/// Return the height of the output (using this width).
+
+int output_html
+ (wxHtmlWinParser& html_parser
+ ,int x
+ ,int y
+ ,int width
+ ,wxString const& html
+ ,enum_output_mode output_mode = e_output_normal
+ )
+{
+ boost::scoped_ptr<wxHtmlContainerCell> const cell
+ (static_cast<wxHtmlContainerCell*>(html_parser.Parse(html))
+ );
+ LMI_ASSERT(cell);
+
+ cell->Layout(width);
+ switch(output_mode)
+ {
+ case e_output_normal:
+ {
+ wxHtmlRenderingInfo rendering_info;
+ cell->Draw
+ (*html_parser.GetDC()
+ ,x
+ ,y
+ ,0
+ ,std::numeric_limits<int>::max()
+ ,rendering_info
+ );
+ }
+ break;
+ case e_output_measure_only:
+ // Nothing else to do.
+ break;
+ }
+
+ return cell->GetHeight();
+}
+
+enum enum_group_quote_columns
+ {e_col_number
+ ,e_col_name
+ ,e_col_age
+ ,e_col_dob
+ ,e_col_salary
+ ,e_col_face_amount
+ ,e_col_premium
+ ,e_col_premium_with_waiver
+ ,e_col_premium_with_adb
+ ,e_col_premium_with_waiver_and_adb
+ ,e_col_max
+ };
+
+struct column_definition
+{
+ char const* const header_;
+ char const* const widest_text_; // Empty string means variable width.
+};
+
+column_definition const column_definitions[] =
+ {{"Part#" , "9999" }
+ ,{"Participant" , "" }
+ ,{"Issue Age" , "999" }
+ ,{"Date of Birth" , "9999-99-99" }
+ ,{"Income" , "$99,999,999" }
+ ,{"Face Amount" , "$99,999,999.00" }
+ // All the subsequent columns use dynamically determined "premium mode" in
+ // their title, so their labels are actually format strings.
+ ,{"%s\nPremium" , "$999,999.00" }
+ ,{"%s\nPremium with\nWaiver" , "$999,999.00" }
+ ,{"%s\nPremium with\nADB" , "$999,999.00" }
+ ,{"%s\nPremium with\nWaiver &\nADB" , "$999,999.00" }
+ };
+
+BOOST_STATIC_ASSERT(sizeof column_definitions / sizeof(column_definitions[0])
== e_col_max);
+
+class group_quote_pdf_generator_wx
+ :public group_quote_pdf_generator
+{
+ public:
+ static boost::shared_ptr<group_quote_pdf_generator> do_create()
+ {
+ return boost::shared_ptr<group_quote_pdf_generator>
+ (new group_quote_pdf_generator_wx()
+ );
+ }
+
+ virtual void add_ledger(Ledger const& ledger);
+ virtual void save(std::string const& output_filename);
+
+ private:
+ // These margins are arbitrary and can be changed to conform to subjective
+ // preferences.
+ static int const horz_margin = 24;
+ static int const vert_margin = 36;
+ static int const vert_skip = 12;
+
+ // Ctor is private as it is only used by do_create().
+ group_quote_pdf_generator_wx();
+
+ // Generate the PDF once we have all the data.
+ void do_generate_pdf(wxPdfDC& pdf_dc);
+
+ // Compute the number of pages needed by the table rows in the output given
+ // the space remaining on the first page, the heights of the header, one
+ // table row and the footer and the last row position.
+ // Remaining space contains the space on the first page on input and is
+ // updated with the space remaining on the last page on output.
+ int compute_pages_for_table_rows
+ (int* remaining_space
+ ,int header_height
+ ,int row_height
+ ,int last_row_y
+ );
+
+ void output_page_number
+ (wxPdfDC& pdf_dc
+ ,int total_pages
+ ,int current_page
+ );
+ void output_image_header
+ (wxPdfDC& pdf_dc
+ ,int* pos_y
+ );
+ void output_document_header
+ (wxPdfDC& pdf_dc
+ ,wxHtmlWinParser& html_parser
+ ,int* pos_y
+ );
+ void output_table_totals
+ (wxPdfDC& pdf_dc
+ ,wx_table_generator& table_gen
+ ,int* pos_y
+ );
+ void output_footer
+ (wxPdfDC& pdf_dc
+ ,wxHtmlWinParser& html_parser
+ ,int* pos_y
+ ,enum_output_mode output_mode = e_output_normal
+ );
+
+ struct header_data
+ {
+ // Extract header fields from a ledger.
+ void fill_header_data(LedgerInvariant const& ledger);
+
+ std::string company_;
+ std::string prepared_by_;
+ std::string guarantee_issue_max_;
+ std::string product_;
+ std::string available_riders_;
+ std::string plan_type_;
+ std::string premium_mode_;
+ std::string contract_state_;
+ };
+ header_data header_;
+
+ struct row_data
+ {
+ std::string values[e_col_max];
+ };
+ std::vector<row_data> rows_;
+
+ class totals_data
+ {
+ public:
+ totals_data()
+ {
+ for(int col = e_col_face_amount; col < e_col_max; ++col)
+ {
+ value(col) = 0.0;
+ }
+ }
+
+ void total(int col, double d)
+ {
+ value(col) = d;
+ }
+
+ double total(int col) const
+ {
+ return const_cast<totals_data*>(this)->value(col);
+ }
+
+ private:
+ double& value(int col) { return values_[col - e_col_face_amount]; }
+
+ double values_[e_col_max - e_col_face_amount];
+ };
+ totals_data totals_;
+
+ struct page_metrics
+ {
+ page_metrics()
+ :width_(0)
+ {
+ }
+
+ void initialize(wxDC const& dc)
+ {
+ total_size_ = dc.GetSize();
+ width_ = total_size_.x - 2 * horz_margin;
+ }
+
+ wxSize total_size_;
+ int width_;
+ };
+ page_metrics page_;
+
+ int row_num_;
+};
+
+group_quote_pdf_generator_wx::group_quote_pdf_generator_wx()
+ :row_num_(0)
+{
+}
+
+void group_quote_pdf_generator_wx::header_data::fill_header_data
+ (LedgerInvariant const& ledger
+ )
+{
+ company_ = ledger.CorpName;
+
+ prepared_by_ = ledger.ProducerName;
+ guarantee_issue_max_ = "$500,000"; // FIXME
+ product_ = ledger.ProductName;
+ available_riders_ = "Waiver, ADB, ABR, Spouse or Child"; // FIXME
+ plan_type_ = "Mandatory"; // FIXME
+ premium_mode_ = ledger.ErMode.at(0).str();
+ contract_state_ = ledger.GetStatePostalAbbrev();
+}
+
+void group_quote_pdf_generator_wx::add_ledger(Ledger const& ledger)
+{
+ LedgerInvariant const& Invar = ledger.GetLedgerInvariant();
+
+ // We suppose that header data is the same for all ledgers, so only fill it
+ // once.
+ if(header_.company_.empty())
+ {
+ header_.fill_header_data(Invar);
+ }
+
+ int const year = 0;
+
+ std::pair<int, oenum_format_style> const f0(0, oe_format_normal);
+ std::pair<int, oenum_format_style> const f2(2, oe_format_normal);
+
+ bool const is_composite = ledger.GetIsComposite();
+
+ row_data rd;
+ for(int col = 0; col < e_col_max; ++col)
+ {
+ // The cast is only used to ensure that if any new elements are added
+ // to the enum, the compiler would warn about their values not being
+ // present in this switch.
+ switch(static_cast<enum_group_quote_columns>(col))
+ {
+ case e_col_number:
+ rd.values[col] = wxString::Format("%d",
++row_num_).ToStdString();
+ continue;
+ case e_col_name:
+ rd.values[col] = Invar.Insured1;
+ continue;
+ case e_col_age:
+ rd.values[col] = wxString::Format("%.0f",
Invar.Age).ToStdString();
+ continue;
+ case e_col_dob:
+ rd.values[col] = ConvertDateToWx
+ (jdn_t(static_cast<int>(Invar.DateOfBirthJdn))
+ ).FormatDate();
+ continue;
+ case e_col_salary:
+ rd.values[col] = '$' + ledger_format(Invar.Salary.at(year),
f0);
+ continue;
+ case e_col_face_amount:
+ {
+ double const z = Invar.SpecAmt.at(year);
+ rd.values[col] = '$' + ledger_format(z, f0);
+ if(is_composite)
+ {
+ totals_.total(col, z);
+ }
+ }
+ continue;
+ case e_col_premium:
+ {
+ double const z = Invar.InitModalPrem00;
+ rd.values[col] = '$' + ledger_format(z, f2);
+ if(is_composite)
+ {
+ totals_.total(col, z);
+ }
+ }
+ continue;
+ case e_col_premium_with_waiver:
+ {
+ double const z = Invar.InitModalPrem01;
+ rd.values[col] = '$' + ledger_format(z, f2);
+ if(is_composite)
+ {
+ totals_.total(col, z);
+ }
+ }
+ continue;
+ case e_col_premium_with_adb:
+ {
+ double const z = Invar.InitModalPrem10;
+ rd.values[col] = '$' + ledger_format(z, f2);
+ if(is_composite)
+ {
+ totals_.total(col, z);
+ }
+ }
+ continue;
+ case e_col_premium_with_waiver_and_adb:
+ {
+ double const z = Invar.InitModalPrem11;
+ rd.values[col] = '$' + ledger_format(z, f2);
+ if(is_composite)
+ {
+ totals_.total(col, z);
+ }
+ }
+ continue;
+ case e_col_max:
+ break;
+ }
+
+ LMI_ASSERT(!"Unknown group premium quote column.");
+ }
+
+ // The last, composite, ledger is only used for the totals, it shouldn't be
+ // shown in the main table.
+ if(!is_composite)
+ {
+ rows_.push_back(rd);
+ }
+}
+
+void group_quote_pdf_generator_wx::save(std::string const& output_filename)
+{
+ // Create a wxPrintData object just to describe the paper to use.
+ wxPrintData print_data;
+ print_data.SetOrientation(wxLANDSCAPE);
+ print_data.SetPaperId(wxPAPER_LETTER);
+
+ wxPdfDC pdf_dc(print_data);
+
+ page_.initialize(pdf_dc);
+
+ do_generate_pdf(pdf_dc);
+
+ // This is pretty baroque: to specify the name of the file after wxPdfDC
+ // creation, we need to poke print data stored inside it.
+ wxPdfDCImpl* const impl = dynamic_cast<wxPdfDCImpl*>(pdf_dc.GetImpl());
+ LMI_ASSERT(impl);
+
+ impl->GetPrintData().SetFilename(output_filename);
+
+ // After the above, EndDoc() will produce output in the specified file.
+ pdf_dc.EndDoc();
+}
+
+void group_quote_pdf_generator_wx::do_generate_pdf(wxPdfDC& pdf_dc)
+{
+ // Ensure that the output is independent of the current display resolution:
+ // it seems that this is only the case with the PDF map mode and wxDC mode
+ // different from wxMM_TEXT.
+ pdf_dc.SetMapModeStyle(wxPDF_MAPMODESTYLE_PDF);
+
+ // For simplicity, use points for everything: font sizers are expressed in
+ // them anyhow, so it's convenient to use them for everything else too.
+ pdf_dc.SetMapMode(wxMM_POINTS);
+
+ pdf_dc.StartDoc(wxString()); // Argument is not used.
+ pdf_dc.StartPage();
+
+ // Use a standard PDF Helvetica font (without embedding any custom fonts in
+ // the generated file, the only other realistic choice is Times New Roman).
+ pdf_dc.SetFont
+ (wxFontInfo(8).Family(wxFONTFAMILY_SWISS).FaceName("Helvetica")
+ );
+
+ // Create an HTML parser to allow easily adding HTML contents to the
output.
+ wxHtmlWinParser html_parser(NULL);
+ html_parser.SetDC(&pdf_dc);
+ html_parser.SetStandardFonts
+ (pdf_dc.GetFont().GetPointSize()
+ ,"Helvetica"
+ ,"Courier"
+ );
+
+ int pos_y = 0;
+
+ output_image_header(pdf_dc, &pos_y);
+ pos_y += 2 * vert_skip;
+
+ output_document_header(pdf_dc, html_parser, &pos_y);
+ pos_y += 2 * vert_skip;
+
+ wx_table_generator table_gen
+ (pdf_dc
+ ,horz_margin
+ ,page_.width_
+ );
+
+ for(int col = 0; col < e_col_max; ++col)
+ {
+ column_definition const& cd = column_definitions[col];
+ std::string header(cd.header_);
+
+ // The cast is only used to ensure that if any new elements are added
+ // to the enum, the compiler would warn about their values not being
+ // present in this switch.
+ switch(static_cast<enum_group_quote_columns>(col))
+ {
+ case e_col_number:
+ case e_col_name:
+ case e_col_age:
+ case e_col_dob:
+ case e_col_salary:
+ case e_col_face_amount:
+ // Nothing to do for these columns, their labels are literal.
+ break;
+
+ case e_col_premium:
+ case e_col_premium_with_waiver:
+ case e_col_premium_with_adb:
+ case e_col_premium_with_waiver_and_adb:
+ {
+ // Labels of these columns are format strings as they need to
+ // be constructed dynamically.
+ LMI_ASSERT(header.find("%s") != std::string::npos);
+
+ header = wxString::Format
+ (wxString(header), header_.premium_mode_
+ ).ToStdString();
+ }
+ break;
+
+ case e_col_max:
+ LMI_ASSERT(!"unreachable");
+ }
+
+ table_gen.add_column(header.c_str(), cd.widest_text_);
+ }
+
+ output_table_totals(pdf_dc, table_gen, &pos_y);
+
+ int const y_before_header = pos_y;
+ table_gen.output_header(&pos_y);
+ int const header_height = pos_y - y_before_header;
+
+ int y_after_footer = pos_y;
+ output_footer(pdf_dc, html_parser, &y_after_footer, e_output_measure_only);
+ int const footer_height = y_after_footer - pos_y;
+
+ int const last_row_y = page_.total_size_.y - vert_margin;
+ int remaining_space = last_row_y - pos_y;
+
+ int total_pages = compute_pages_for_table_rows
+ (&remaining_space
+ ,header_height
+ ,table_gen.row_height()
+ ,last_row_y
+ );
+
+ // Check if the footer fits into the same page or if it needs a new one (we
+ // never want to have a page break in the footer).
+ bool const footer_on_its_own_page
+ = remaining_space < (footer_height + 2 * vert_skip);
+ if(footer_on_its_own_page)
+ {
+ total_pages++;
+ }
+
+ int current_page = 1;
+
+ typedef std::vector<row_data>::const_iterator rdci;
+ for(rdci i = rows_.begin(); i != rows_.end(); ++i)
+ {
+ table_gen.output_row(&pos_y, i->values);
+
+ if(pos_y >= last_row_y)
+ {
+ output_page_number(pdf_dc, total_pages, current_page);
+
+ current_page++;
+ pdf_dc.StartPage();
+
+ pos_y = vert_margin;
+ table_gen.output_header(&pos_y);
+ }
+ }
+
+ if(footer_on_its_own_page)
+ {
+ output_page_number(pdf_dc, total_pages, current_page);
+
+ current_page++;
+ pdf_dc.StartPage();
+
+ pos_y = vert_margin;
+ }
+ else
+ {
+ pos_y += 2 * vert_skip;
+ }
+
+ output_footer(pdf_dc, html_parser, &pos_y);
+
+ LMI_ASSERT(current_page == total_pages);
+ output_page_number(pdf_dc, total_pages, current_page);
+}
+
+int group_quote_pdf_generator_wx::compute_pages_for_table_rows
+ (int* remaining_space
+ ,int header_height
+ ,int row_height
+ ,int last_row_y
+ )
+{
+ int total_pages = 1;
+
+ int const max_rows_on_first_page = (*remaining_space) / row_height;
+ int remaining_rows = static_cast<int>(rows_.size());
+ if(max_rows_on_first_page < remaining_rows)
+ {
+ // All rows don't fit on the first page, so add enough pages for the
+ // rest of them.
+ remaining_rows -= max_rows_on_first_page;
+
+ int const page_area_y = last_row_y - vert_margin - header_height;
+ int const rows_per_page = page_area_y / row_height;
+ total_pages += (remaining_rows + rows_per_page - 1) / rows_per_page;
+ *remaining_space = page_area_y;
+ remaining_rows %= rows_per_page;
+ }
+
+ *remaining_space -= remaining_rows * row_height;
+
+ return total_pages;
+}
+
+void group_quote_pdf_generator_wx::output_page_number
+ (wxPdfDC& pdf_dc
+ ,int total_pages
+ ,int current_page
+ )
+{
+ pdf_dc.DrawLabel
+ (wxString::Format("Page %d of %d", current_page, total_pages)
+ ,wxRect
+ (horz_margin
+ ,page_.total_size_.y - vert_margin
+ ,page_.width_
+ ,vert_margin
+ )
+ ,wxALIGN_RIGHT | wxALIGN_BOTTOM
+ );
+}
+
+void group_quote_pdf_generator_wx::output_image_header
+ (wxPdfDC& pdf_dc
+ ,int* pos_y
+ )
+{
+ wxImage background_image(load_image("background.png"));
+ if(!background_image.IsOk())
+ {
+ return;
+ }
+
+ // Use wxPdfDocument API directly as wxDC doesn't provide a way to set the
+ // image scale at PDF level and also because passing via wxDC wastefully
+ // converts wxImage to wxBitmap only to convert it back to wxImage when
+ // embedding it into the PDF.
+ wxPdfDocument* const pdf_doc = pdf_dc.GetPdfDocument();
+ LMI_ASSERT(pdf_doc);
+
+ wxSize const image_size = background_image.GetSize();
+
+ // Set the scale to fit the image to the document width.
+ pdf_doc->SetImageScale
+ (static_cast<double>(image_size.x) / page_.total_size_.x
+ );
+ pdf_doc->Image("background", background_image, 0, *pos_y);
+
+ int const y = wxRound(image_size.y / pdf_doc->GetImageScale());
+
+ pdf_doc->SetImageScale(1);
+
+ wxDCFontChanger set_bigger_font(pdf_dc, pdf_dc.GetFont().Scaled(1.5));
+ wxDCTextColourChanger set_white_text(pdf_dc, *wxWHITE);
+
+ wxString const image_text
+ (header_.company_
+ + "\nPremium & Benefit Summary"
+ );
+
+ pdf_dc.DrawLabel
+ (image_text
+ ,wxRect
+ (wxPoint(horz_margin, *pos_y + y / 2),
+ pdf_dc.GetMultiLineTextExtent(image_text)
+ )
+ ,wxALIGN_CENTER_HORIZONTAL
+ );
+
+ *pos_y += y;
+}
+
+void group_quote_pdf_generator_wx::output_document_header
+ (wxPdfDC& pdf_dc
+ ,wxHtmlWinParser& html_parser
+ ,int* pos_y
+ )
+{
+ wxString const title_html = wxString::Format
+ ("<table width=\"100%%\">"
+ "<tr>"
+ "<td align=\"center\"><i><font size=\"+1\">%s</font></i></td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"center\"><i>Prepared Date: %s</i></td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"center\"><i>Prepared By: %s</i></td>"
+ "</tr>"
+ "</table>"
+ ,header_.company_
+ ,wxDateTime::Today().FormatDate()
+ ,header_.prepared_by_
+ );
+
+ output_html(html_parser, horz_margin, *pos_y, page_.width_ / 2,
title_html);
+
+ wxString const summary_html = wxString::Format
+ ("<table width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\">"
+ // This extra top empty row works around a bug in wxHTML
+ // table positioning code: it uses the provided ordinate
+ // coordinate as a base line of the first table line and
+ // not as its top, as it ought to, so without this line
+ // the rectangle drawn below wouldn't contain the header.
+ "<tr>"
+ "<td align=\"center\" colspan=\"4\"> </td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"center\" colspan=\"4\"><font size=\"+1\">Plan Details
Summary</font></td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"right\"><b>Effective
Data: </b></td><td>%s</td>"
+ "<td align=\"right\"><b>Plan Type(s): </b></td><td>%s</td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"right\"><b>Guarantee Issue
Max: </b></td><td>%s</td>"
+ "<td align=\"right\"><b>Premium Mode: </b></td><td>%s</td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"right\"><b>Product(s): </b></td><td>%s</td>"
+ "<td align=\"right\"><b>Contract
State: </b></td><td>%s</td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"right\"><b>Available
Riders: </b></td><td>%s</td>"
+ "</tr>"
+ "<tr>"
+ "<td align=\"right\"><b>Number of
eligibles: </b></td><td>%d</td>"
+ "</tr>"
+ "</table>"
+ ,wxDateTime::Today().FormatDate()
+ ,header_.plan_type_
+ ,header_.guarantee_issue_max_
+ ,header_.premium_mode_
+ ,header_.product_
+ ,header_.contract_state_
+ ,header_.available_riders_
+ ,row_num_
+ );
+
+ int const summary_height = output_html
+ (html_parser
+ ,horz_margin + page_.width_ / 2
+ ,*pos_y
+ ,page_.width_ / 2
+ ,summary_html
+ );
+
+ // wxHTML tables don't support "frame" attribute, so draw the border around
+ // the table manually.
+ pdf_dc.SetBrush(*wxTRANSPARENT_BRUSH);
+ pdf_dc.DrawRectangle
+ (horz_margin + page_.width_ / 2
+ ,*pos_y
+ ,page_.width_ / 2
+ ,summary_height
+ );
+
+ *pos_y += summary_height;
+}
+
+void group_quote_pdf_generator_wx::output_table_totals
+ (wxPdfDC& pdf_dc
+ ,wx_table_generator& table_gen
+ ,int* pos_y
+ )
+{
+ int& y = *pos_y;
+
+ table_gen.output_horz_separator(e_col_face_amount, e_col_max, y);
+ table_gen.output_vert_separator(e_col_face_amount, y);
+ table_gen.output_vert_separator(e_col_max, y);
+
+ y += table_gen.row_height();
+
+ table_gen.output_vert_separator(e_col_number, y);
+
+ int const cell_margin_x = pdf_dc.GetCharWidth();
+ int const y_text = y + pdf_dc.GetCharHeight();
+
+ // Render "Census" in bold.
+ wxDCFontChanger set_bold_font(pdf_dc, pdf_dc.GetFont().Bold());
+ pdf_dc.DrawLabel
+ ("Census"
+ ,table_gen.cell_rect(e_col_name, y_text).Deflate(cell_margin_x, 0)
+ ,wxALIGN_LEFT
+ );
+
+ // And the totals in bold italic: notice that there is no need to create
+ // another wxDCFontChanger here, the original font will be restored by the
+ // one just above anyhow.
+ pdf_dc.SetFont(pdf_dc.GetFont().Italic());
+
+ pdf_dc.DrawLabel
+ ("Totals:"
+ ,table_gen.cell_rect(e_col_salary, y_text).Deflate(cell_margin_x, 0)
+ ,wxALIGN_RIGHT
+ );
+
+ std::pair<int, oenum_format_style> const f2(2, oe_format_normal);
+ for(int col = e_col_face_amount; col < e_col_max; ++col)
+ {
+ wxRect const cell_rect = table_gen.cell_rect(col, y);
+ {
+ wxDCPenChanger set_transparent_pen(pdf_dc, *wxTRANSPARENT_PEN);
+ wxDCBrushChanger set_grey_brush(pdf_dc, *wxLIGHT_GREY_BRUSH);
+ pdf_dc.DrawRectangle(cell_rect);
+ }
+
+ wxRect const text_rect
+ (cell_rect.x + cell_margin_x
+ ,y_text
+ ,cell_rect.width - 2 * cell_margin_x
+ ,cell_rect.height
+ );
+
+ pdf_dc.DrawLabel
+ ("$"
+ ,text_rect
+ ,wxALIGN_LEFT
+ );
+ pdf_dc.DrawLabel
+ (ledger_format(totals_.total(col), f2)
+ ,text_rect
+ ,wxALIGN_RIGHT
+ );
+
+ table_gen.output_vert_separator(col, y);
+ }
+
+ table_gen.output_vert_separator(e_col_max, y);
+ table_gen.output_horz_separator(e_col_number, e_col_max, y);
+
+ y += table_gen.row_height();
+}
+
+void group_quote_pdf_generator_wx::output_footer
+ (wxPdfDC& pdf_dc
+ ,wxHtmlWinParser& html_parser
+ ,int* pos_y
+ ,enum_output_mode output_mode
+ )
+{
+ wxImage logo_image(load_image("logo.png"));
+ if(logo_image.IsOk())
+ {
+ switch(output_mode)
+ {
+ case e_output_normal:
+ pdf_dc.DrawBitmap(logo_image, horz_margin, *pos_y);
+ break;
+ case e_output_measure_only:
+ break;
+ }
+ *pos_y += logo_image.GetSize().y + vert_skip;
+ }
+
+ wxString const footer_html =
+ "<p>"
+ "...footnotes such as LedgerInvariant::MarketingNameFootnote..."
+ "</p>"
+ ;
+
+ *pos_y += output_html
+ (html_parser
+ ,horz_margin
+ ,*pos_y
+ ,page_.width_
+ ,footer_html
+ ,output_mode
+ );
+}
+
+volatile bool ensure_setup = group_quote_pdf_generator_wx::set_creator
+ (group_quote_pdf_generator_wx::do_create
+ );
+
+} // Unnamed namespace.
Property changes on: lmi/trunk/group_quote_pdf_gen_wx.cpp
___________________________________________________________________
Added: svn:keywords
+ Id
Modified: lmi/trunk/ihs_acctval.cpp
===================================================================
--- lmi/trunk/ihs_acctval.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/ihs_acctval.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -1081,7 +1081,7 @@
/// not inhibited here: all input is taken as deliberate, as an end
/// user might reasonably wish to show the effect of other riders; if
/// assertions as to input are to be made at all, then they should be
-/// made in the function that creates the group premium report.
+/// made in the function that creates the group premium quote report.
double AccountValue::SuppositiveModalPremium
(int year
Modified: lmi/trunk/illustration_view.cpp
===================================================================
--- lmi/trunk/illustration_view.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/illustration_view.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -91,6 +91,7 @@
EVT_UPDATE_UI(XRCID("print_case_to_disk"
),IllustrationView::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("print_spreadsheet"
),IllustrationView::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("print_group_roster"
),IllustrationView::UponUpdateInapplicable )
+ EVT_UPDATE_UI(XRCID("print_group_quote"
),IllustrationView::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("paste_census"
),IllustrationView::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("add_cell"
),IllustrationView::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("delete_cells"
),IllustrationView::UponUpdateInapplicable )
Modified: lmi/trunk/main_wx.cpp
===================================================================
--- lmi/trunk/main_wx.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/main_wx.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -50,6 +50,7 @@
#include <string>
LMI_FORCE_LINKING_EX_SITU(file_command_wx)
+LMI_FORCE_LINKING_EX_SITU(group_quote_pdf_generator_wx)
LMI_FORCE_LINKING_EX_SITU(progress_meter_wx)
LMI_FORCE_LINKING_EX_SITU(system_command_wx)
Modified: lmi/trunk/main_wx_test.cpp
===================================================================
--- lmi/trunk/main_wx_test.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/main_wx_test.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -65,6 +65,7 @@
#include <vector>
LMI_FORCE_LINKING_EX_SITU(file_command_wx)
+LMI_FORCE_LINKING_EX_SITU(group_quote_pdf_generator_wx)
LMI_FORCE_LINKING_EX_SITU(progress_meter_wx)
LMI_FORCE_LINKING_EX_SITU(system_command_wx)
Modified: lmi/trunk/mc_enum_type_enums.hpp
===================================================================
--- lmi/trunk/mc_enum_type_enums.hpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/mc_enum_type_enums.hpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -54,6 +54,7 @@
,mce_emit_text_stream = 512
,mce_emit_custom_0 = 1024
,mce_emit_custom_1 = 2048
+ ,mce_emit_group_quote = 4096
};
/// Rounding styles.
Modified: lmi/trunk/mc_enum_types.cpp
===================================================================
--- lmi/trunk/mc_enum_types.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/mc_enum_types.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -71,6 +71,7 @@
,mce_emit_text_stream
,mce_emit_custom_0
,mce_emit_custom_1
+ ,mce_emit_group_quote
};
extern char const*const emission_strings[] =
{"emit_nothing"
@@ -86,9 +87,10 @@
,"emit_text_stream"
,"emit_custom_0"
,"emit_custom_1"
+ ,"emit_group_quote"
};
template<> struct mc_enum_key<mcenum_emission>
- :public mc_enum_data<mcenum_emission, 13, emission_enums, emission_strings>
{};
+ :public mc_enum_data<mcenum_emission, 14, emission_enums, emission_strings>
{};
template class mc_enum<mcenum_emission>;
extern rounding_style const rounding_style_enums[] =
Modified: lmi/trunk/mc_enum_types_aux.cpp
===================================================================
--- lmi/trunk/mc_enum_types_aux.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/mc_enum_types_aux.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -44,6 +44,7 @@
{
z.allow(z.ordinal("emit_pdf_to_printer"), false);
z.allow(z.ordinal("emit_pdf_to_viewer" ), false);
+ z.allow(z.ordinal("emit_group_quote" ), false);
}
/// Validate mc_n_gen_bases, mc_n_sep_bases, and mc_n_rate_periods.
Modified: lmi/trunk/mec_view.cpp
===================================================================
--- lmi/trunk/mec_view.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/mec_view.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -84,6 +84,7 @@
EVT_UPDATE_UI(XRCID("print_case_to_disk"
),mec_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("print_spreadsheet"
),mec_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("print_group_roster"
),mec_view::UponUpdateInapplicable)
+ EVT_UPDATE_UI(XRCID("print_group_quote"
),mec_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("paste_census"
),mec_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("add_cell"
),mec_view::UponUpdateInapplicable)
EVT_UPDATE_UI(XRCID("delete_cells"
),mec_view::UponUpdateInapplicable)
Modified: lmi/trunk/menus.xrc
===================================================================
--- lmi/trunk/menus.xrc 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/menus.xrc 2015-08-19 17:02:28 UTC (rev 6258)
@@ -350,10 +350,17 @@
<bitmap platform="win" stock_id="write-spreadsheet"/>
<help>Run and print all cells to a spreadsheet file</help>
</object>
- <object class="wxMenuItem" name="print_group_roster">
- <label>Print r_oster to spreadsheet\tCtrl-Shift-O</label>
- <bitmap platform="win" stock_id="roster"/>
- <help>Run and print group roster to a spreadsheet file</help>
+ <object class="wxMenu">
+ <label>Print r_oster</label>
+ <object class="wxMenuItem" name="print_group_roster">
+ <label>Print r_oster to spreadsheet\tCtrl-Shift-O</label>
+ <bitmap platform="win" stock_id="roster"/>
+ <help>Run and print group roster to a spreadsheet file</help>
+ </object>
+ <object class="wxMenuItem" name="print_group_quote">
+ <label>Print group premium _quote\tCtrl-Shift-Q</label>
+ <help>Run and print group premium quote to a PDF file</help>
+ </object>
</object>
<object class="separator"/>
<object class="wxMenuItem" name="paste_census">
Modified: lmi/trunk/objects.make
===================================================================
--- lmi/trunk/objects.make 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/objects.make 2015-08-19 17:02:28 UTC (rev 6258)
@@ -205,6 +205,7 @@
file_command.o \
getopt.o \
global_settings.o \
+ group_quote_pdf_gen.o \
group_values.o \
illustrator.o \
input.o \
@@ -317,6 +318,7 @@
file_command_wx.o \
gpt_document.o \
gpt_view.o \
+ group_quote_pdf_gen_wx.o \
icon_monger.o \
illustration_document.o \
illustration_view.o \
@@ -349,6 +351,7 @@
transferor.o \
view_ex.o \
wx_checks.o \
+ wx_table_generator.o \
wx_utility.o \
lmi_wx_objects := \
Modified: lmi/trunk/skeleton.cpp
===================================================================
--- lmi/trunk/skeleton.cpp 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/skeleton.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -159,6 +159,7 @@
EVT_UPDATE_UI(XRCID("print_case_to_disk"
),Skeleton::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("print_spreadsheet"
),Skeleton::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("print_group_roster"
),Skeleton::UponUpdateInapplicable )
+ EVT_UPDATE_UI(XRCID("print_group_quote"
),Skeleton::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("paste_census"
),Skeleton::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("add_cell"
),Skeleton::UponUpdateInapplicable )
EVT_UPDATE_UI(XRCID("delete_cells"
),Skeleton::UponUpdateInapplicable )
Modified: lmi/trunk/toolbar.xrc
===================================================================
--- lmi/trunk/toolbar.xrc 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/toolbar.xrc 2015-08-19 17:02:28 UTC (rev 6258)
@@ -115,10 +115,21 @@
<bitmap stock_id="write-spreadsheet"/>
<longhelp>Run and print all cells to a spreadsheet file</longhelp>
</object>
- <object class="tool" name="print_group_roster">
- <tooltip>Print roster to spreadsheet</tooltip>
+ <object class="tool" name="print_group">
+ <tooltip>Print roster</tooltip>
<bitmap stock_id="roster"/>
- <longhelp>Run and print group roster to a spreadsheet file</longhelp>
+ <dropdown>
+ <object class="wxMenu">
+ <object class="wxMenuItem" name="print_group_roster">
+ <label>Print r_oster to spreadsheet</label>
+ <help>Run and print group roster to a spreadsheet
file</help>
+ </object>
+ <object class="wxMenuItem" name="print_group_quote">
+ <label>Print group premium _quote</label>
+ <help>Run and print group premium quote to a PDF
file</help>
+ </object>
+ </object>
+ </dropdown>
</object>
<object class="separator"/>
<object class="tool" name="paste_census">
Modified: lmi/trunk/workhorse.make
===================================================================
--- lmi/trunk/workhorse.make 2015-08-18 18:40:15 UTC (rev 6257)
+++ lmi/trunk/workhorse.make 2015-08-19 17:02:28 UTC (rev 6258)
@@ -835,7 +835,7 @@
# source files that are unrelated to wx, and that are therefore not
# part of $(skeleton_objects).
skeleton$(SHREXT): lmi_so_attributes := -DLMI_USE_SO
-skeleton$(SHREXT): EXTRA_LDFLAGS := $(wx_ldflags)
+skeleton$(SHREXT): EXTRA_LDFLAGS := $(wx_pdfdoc_ldflags) $(wx_ldflags)
skeleton$(SHREXT): $(skeleton_objects) liblmi$(SHREXT) wx_new$(SHREXT)
lmi_wx_shared$(EXEEXT): lmi_so_attributes := -DLMI_USE_SO
Added: lmi/trunk/wx_table_generator.cpp
===================================================================
--- lmi/trunk/wx_table_generator.cpp (rev 0)
+++ lmi/trunk/wx_table_generator.cpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -0,0 +1,352 @@
+// Generate a table using wxDC.
+//
+// Copyright (C) 2015 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifdef __BORLANDC__
+# include "pchfile.hpp"
+# pragma hdrstop
+#endif // __BORLANDC__
+
+#include "wx_table_generator.hpp"
+
+#include "alert.hpp"
+#include "assert_lmi.hpp"
+
+#include <algorithm> // std::count()
+
+namespace
+{
+
+// Return the number of lines in a possibly multiline string.
+std::size_t count_lines(std::string const& s)
+{
+ return 1u + std::count(s.begin(), s.end(), '\n');
+}
+
+// Split a string into lines separated by new line characters.
+std::vector<std::string> split_in_lines(std::string const& s)
+{
+ // BOOST !! Unfortunately boost::split() can't be easily used with the
+ // current ancient version of the library (1.33), so we reimplement it
+ // here.
+ std::vector<std::string> lines;
+ std::string line;
+ for(std::string::const_iterator i = s.begin(); i != s.end(); ++i)
+ {
+ if('\n' == *i)
+ {
+ lines.push_back(line);
+ line.clear();
+ }
+ else
+ {
+ line += *i;
+ }
+ }
+ return lines;
+}
+
+// Increase the first argument to the second one if it's smaller.
+template<typename T>
+void increase_to_if_smaller(T& first, T second)
+{
+ if(first < second)
+ {
+ first = second;
+ }
+}
+
+} // Unnamed namespace.
+
+wx_table_generator::wx_table_generator
+ (wxDC& dc_
+ ,int left_margin
+ ,int total_width
+ )
+ :dc_(dc_)
+ ,left_margin_(left_margin)
+ ,total_width_(total_width)
+ ,char_height_(dc_.GetCharHeight())
+ ,row_height_((4 * char_height_ + 2) / 3) // Arbitrarily use 1.333 line
spacing.
+ ,has_column_widths_(false)
+ ,max_header_lines_(1)
+{
+ // Set a pen with 0 width to get the thin lines and butt cap style for the
+ // different segments drawn in do_output_values() to seamlessly combine
+ // into a single line.
+ wxPen pen(*wxBLACK, 0);
+ pen.SetCap(wxCAP_ROUND);
+ dc_.SetPen(pen);
+}
+
+void wx_table_generator::add_column
+ (char const* header
+ ,char const* widest_text
+ )
+{
+ wxDCFontChanger set_header_font(dc_, get_header_font());
+
+ // Set width to the special value of 0 for the variable width columns.
+ int width = widest_text[0] ? dc_.GetTextExtent(widest_text).x : 0;
+
+ // Keep track of the maximal number of lines in a header as this determines
+ // the number of lines used for all of them.
+ increase_to_if_smaller(max_header_lines_, count_lines(header));
+
+ // Also increase the column width to be sufficiently wide to fit
+ // this header line (with roughly 1 em margins on either side) if it has
+ // fixed width.
+ if(0 != width)
+ {
+ increase_to_if_smaller
+ (width
+ ,dc_.GetMultiLineTextExtent(header).x + dc_.GetTextExtent("MM").x
+ );
+ }
+
+ columns_.push_back(column_info(header, width));
+}
+
+wxFont wx_table_generator::get_header_font() const
+{
+ return dc_.GetFont().Bold();
+}
+
+void wx_table_generator::do_output_horz_separator(int x1, int x2, int y)
+{
+ dc_.DrawLine(x1, y, x2, y);
+}
+
+void wx_table_generator::do_output_vert_separator(int x, int y1, int y2)
+{
+ // TODO: add a possibility to have a thick border between the columns.
+ dc_.DrawLine(x, y1, x, y2);
+}
+
+int wx_table_generator::do_get_cell_x(std::size_t column)
+{
+ do_compute_column_widths_if_necessary();
+
+ int x = left_margin_;
+ for(std::size_t col = 0; col < column; ++col)
+ {
+ x += columns_.at(col).width_;
+ }
+
+ return x;
+}
+
+wxRect wx_table_generator::cell_rect(std::size_t column, int y)
+{
+ LMI_ASSERT(column < columns_.size());
+
+ // Note: call do_get_cell_x() here and not from the wxRect ctor arguments
+ // list to ensure that the column width is initialized before it is used
+ // below.
+ int const x = do_get_cell_x(column);
+
+ return wxRect(x, y, columns_.at(column).width_, row_height_);
+}
+
+void wx_table_generator::do_compute_column_widths_if_necessary()
+{
+ if(has_column_widths_)
+ {
+ return;
+ }
+
+ int num_expand = 0;
+ int total_fixed = 0;
+
+ typedef std::vector<column_info>::const_iterator cici;
+ for(cici i = columns_.begin(); i != columns_.end(); ++i)
+ {
+ if(0 == i->width_)
+ {
+ num_expand++;
+ }
+ else
+ {
+ total_fixed += i->width_;
+ }
+ }
+
+ if(total_width_ < total_fixed)
+ {
+ warning()
+ << "Not enough space for all fixed columns "
+ "in group premium quote."
+ << LMI_FLUSH
+ ;
+ return;
+ }
+
+ if(num_expand)
+ {
+ int const per_expand
+ = (total_width_ - total_fixed + num_expand - 1)/num_expand;
+
+ typedef std::vector<column_info>::iterator cii;
+ for(cii i = columns_.begin(); i != columns_.end(); ++i)
+ {
+ if(0 == i->width_)
+ {
+ i->width_ = per_expand;
+ }
+ }
+ }
+
+ has_column_widths_ = true;
+}
+
+void wx_table_generator::do_output_values
+ (int& x
+ ,int& y
+ ,std::string const* values
+ )
+{
+ int const y_top = y;
+
+ int const y_text = y + char_height_;
+ y += row_height_;
+
+ do_output_vert_separator(x, y_top, y);
+
+ std::size_t const num_columns = columns_.size();
+ for(std::size_t col = 0; col < num_columns; ++col)
+ {
+ int const width = columns_.at(col).width_;
+
+ std::string const& s = values[col];
+ if(!s.empty())
+ {
+ int x_text = x;
+ if(columns_.at(col).is_centered_)
+ {
+ // Centre the text for the columns configured to do it.
+ x_text += (width - dc_.GetTextExtent(s).x) / 2;
+ }
+ else
+ {
+ // Otherwise just offset it by ~1 em.
+ x_text += dc_.GetTextExtent("M").x;
+ }
+
+ dc_.DrawText(s, x_text, y_text);
+ }
+ x += width;
+ do_output_vert_separator(x, y_top, y);
+ }
+}
+
+void wx_table_generator::output_vert_separator
+ (std::size_t before_column
+ ,int y
+ )
+{
+ LMI_ASSERT(before_column <= columns_.size());
+
+ do_output_vert_separator
+ (do_get_cell_x(before_column)
+ ,y
+ ,y + row_height_
+ );
+}
+
+void wx_table_generator::output_horz_separator
+ (std::size_t begin_column
+ ,std::size_t end_column
+ ,int y
+ )
+{
+ LMI_ASSERT(begin_column < end_column);
+ LMI_ASSERT(end_column <= columns_.size());
+
+ do_compute_column_widths_if_necessary();
+
+ int const x1 = do_get_cell_x(begin_column);
+
+ int x2 = x1;
+ for(std::size_t col = begin_column; col < end_column; ++col)
+ {
+ x2 += columns_.at(col).width_;
+ }
+
+ do_output_horz_separator(x1, x2, y);
+}
+
+void wx_table_generator::output_header(int* pos_y)
+{
+ do_compute_column_widths_if_necessary();
+
+ wxDCFontChanger set_header_font(dc_, get_header_font());
+
+ // Split headers in single lines and fill up the entire columns*lines 2D
+ // matrix, using empty strings for the headers with less than the maximal
+ // number of lines.
+ std::size_t const num_columns = columns_.size();
+ std::vector<std::string> headers_by_line(max_header_lines_ * num_columns);
+ for(std::size_t col = 0; col < num_columns; ++col)
+ {
+ column_info const& ci = columns_.at(col);
+ std::vector<std::string> const lines(split_in_lines(ci.header_));
+
+ // Fill the elements from the bottom line to the top one, so that a
+ // single line header is shown on the last line, as per the spec.
+ std::size_t const first_line = max_header_lines_ - lines.size();
+ for(std::size_t line = 0; line < lines.size(); ++line)
+ {
+ headers_by_line.at
+ ((first_line + line) * num_columns + col
+ ) = lines.at(line);
+ }
+ }
+
+ // And output all lines of all column headers.
+ int y_top = *pos_y;
+ int x = 0;
+ for(std::size_t line = 0; line < max_header_lines_; ++line)
+ {
+ x = left_margin_;
+ do_output_values
+ (x
+ ,*pos_y
+ ,&headers_by_line.at(line * num_columns)
+ );
+ }
+
+ // Finally draw the separators above and (a double one) below them.
+ do_output_horz_separator(left_margin_, x, y_top );
+ do_output_horz_separator(left_margin_, x, *pos_y - 1);
+ do_output_horz_separator(left_margin_, x, *pos_y );
+}
+
+void wx_table_generator::output_row
+ (int* pos_y
+ ,std::string const* values
+ )
+{
+ int x = left_margin_;
+ do_output_values(x, *pos_y, values);
+
+ do_output_horz_separator(left_margin_, x, *pos_y);
+}
Property changes on: lmi/trunk/wx_table_generator.cpp
___________________________________________________________________
Added: svn:keywords
+ Id
Added: lmi/trunk/wx_table_generator.hpp
===================================================================
--- lmi/trunk/wx_table_generator.hpp (rev 0)
+++ lmi/trunk/wx_table_generator.hpp 2015-08-19 17:02:28 UTC (rev 6258)
@@ -0,0 +1,142 @@
+// Generate a table using wxDC.
+//
+// Copyright (C) 2015 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// $Id$
+
+#ifndef wx_table_generator_hpp
+#define wx_table_generator_hpp
+
+#include "config.hpp"
+
+#include <wx/dc.h>
+#include <wx/font.h>
+
+#include <cstddef> // std::size_t
+#include <string>
+#include <vector>
+
+/// Simplifies outputting tabular data on wxDC.
+///
+/// To create a table, columns must be initialized first by calling
+/// set_column() for each of them once. After this, output_header() and
+/// output_row() can be called reusing the same pos_y argument which contains
+/// the coordinate of the top of the header or row to output and is updated to
+/// correspond to the value for the next row by these functions.
+
+class wx_table_generator
+{
+ public:
+ // The life time of the specified wxDC must be greater than the life time
+ // of this object itself and nothing should be using it while this object
+ // does (as it changes its attributes).
+ //
+ // The table has the given total width and starts at the left margin.
+ wx_table_generator(wxDC& dc, int left_margin, int total_width);
+
+ // Each column must either have a fixed width, specified as the width of
+ // the longest text that may appear in this column, or be expandable
+ // meaning that the rest of the page width is allocated to it which will be
+ // the case if widest_text is empty (but it shouldn't be null).
+ // Notice that column headers may be multiline strings.
+ void add_column(char const* header, char const* widest_text);
+
+ // Render the headers at the given position and update it.
+ void output_header(int* pos_y);
+
+ // Render a row with the given values at the given position and update it.
+ // The values here can be single-line only and there must be exactly the
+ // same number of them as the number of columns.
+ void output_row(int* pos_y, std::string const* values);
+
+ // Return the height of a single table row.
+ int row_height() const {return row_height_;}
+
+ // Return the rectangle containing the cell area.
+ wxRect cell_rect(std::size_t column, int y);
+
+ // Output a horizontal separator line across the specified columns,
+ // using the usual C++ close/open interval convention.
+ void output_horz_separator
+ (std::size_t begin_column
+ ,std::size_t end_column
+ ,int y
+ );
+
+ // Output a vertical separator line before the given column. Notice that
+ // the column index here may be equal to the number of columns in order to
+ // output a separator after the last column.
+ void output_vert_separator(std::size_t before_column, int y);
+
+ private:
+ // Return the font used for the headers.
+ wxFont get_header_font() const;
+
+ int do_get_cell_x(std::size_t column);
+
+ void do_output_horz_separator(int x1, int x2, int y );
+ void do_output_vert_separator(int x , int y1, int y2);
+
+ void do_compute_column_widths_if_necessary();
+
+ void do_output_values
+ (int& pos_x
+ ,int& pos_y
+ ,std::string const* values
+ );
+
+ wxDC& dc_;
+
+ int left_margin_;
+ int total_width_;
+
+ // These values could be recomputed, but cache them for performance.
+ int const char_height_;
+ int const row_height_;
+
+ struct column_info
+ {
+ column_info(char const* header, int width)
+ :header_(header)
+ ,width_(width)
+ // Fixed width columns are centered by default, variable width ones
+ // are not as long strings look better with the default left
+ // alignment.
+ ,is_centered_(width != 0)
+ {
+ }
+
+ std::string header_;
+ int width_;
+ bool is_centered_;
+ };
+
+ std::vector<column_info> columns_;
+
+ // Initially false, set to true after do_compute_column_widths() call
+ // meaning that all column_info::width_ values are now valid.
+ bool has_column_widths_;
+
+ // Maximal number of lines in any column header, initially 1 but can be
+ // higher if multiline headers are used.
+ std::size_t max_header_lines_;
+};
+
+#endif // wx_table_generator_hpp
Property changes on: lmi/trunk/wx_table_generator.hpp
___________________________________________________________________
Added: svn:keywords
+ Id
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [6258] Implement new "group premium quote" report,
Greg Chicares <=