Merge branch 'html-set-cell'

Make wxHTML API more flexible by allowing to operate on individual
wxHtmlContainerCell instead of only the entire HTML document text.

See https://github.com/wxWidgets/wxWidgets/pull/899
This commit is contained in:
Vadim Zeitlin
2018-08-27 02:17:10 +02:00
7 changed files with 174 additions and 46 deletions

View File

@@ -445,7 +445,7 @@ protected:
class WXDLLIMPEXP_HTML wxHtmlContainerCell : public wxHtmlCell class WXDLLIMPEXP_HTML wxHtmlContainerCell : public wxHtmlCell
{ {
public: public:
wxHtmlContainerCell(wxHtmlContainerCell *parent); explicit wxHtmlContainerCell(wxHtmlContainerCell *parent);
virtual ~wxHtmlContainerCell(); virtual ~wxHtmlContainerCell();
virtual void Layout(int w) wxOVERRIDE; virtual void Layout(int w) wxOVERRIDE;
@@ -459,6 +459,11 @@ public:
// insert cell at the end of m_Cells list // insert cell at the end of m_Cells list
void InsertCell(wxHtmlCell *cell); void InsertCell(wxHtmlCell *cell);
// Detach a child cell. After calling this method, it's the caller
// responsibility to destroy this cell (possibly by calling InsertCell()
// with it to attach it elsewhere).
void Detach(wxHtmlCell *cell);
// sets horizontal/vertical alignment // sets horizontal/vertical alignment
void SetAlignHor(int al) {m_AlignHor = al; m_LastLayout = -1;} void SetAlignHor(int al) {m_AlignHor = al; m_LastLayout = -1;}
int GetAlignHor() const {return m_AlignHor;} int GetAlignHor() const {return m_AlignHor;}

View File

@@ -54,6 +54,11 @@ public:
// (see wxFileSystem for detailed explanation) // (see wxFileSystem for detailed explanation)
void SetHtmlText(const wxString& html, const wxString& basepath = wxEmptyString, bool isdir = true); void SetHtmlText(const wxString& html, const wxString& basepath = wxEmptyString, bool isdir = true);
// Sets the HTML cell that will be rendered: this is more efficient than
// using text as it allows to parse it only once. Note that the cell will
// be modified by this call.
void SetHtmlCell(wxHtmlContainerCell& cell);
// Sets fonts to be used when displaying HTML page. (if size null then default sizes used). // Sets fonts to be used when displaying HTML page. (if size null then default sizes used).
void SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes = NULL); void SetFonts(const wxString& normal_face, const wxString& fixed_face, const int *sizes = NULL);
@@ -79,11 +84,14 @@ public:
int GetTotalHeight() const; int GetTotalHeight() const;
private: private:
void DoSetHtmlCell(wxHtmlContainerCell* cell);
wxDC *m_DC; wxDC *m_DC;
wxHtmlWinParser *m_Parser; wxFileSystem m_FS;
wxFileSystem *m_FS; wxHtmlWinParser m_Parser;
wxHtmlContainerCell *m_Cells; wxHtmlContainerCell *m_Cells;
int m_Width, m_Height; int m_Width, m_Height;
bool m_ownsCells;
wxDECLARE_NO_COPY_CLASS(wxHtmlDCRenderer); wxDECLARE_NO_COPY_CLASS(wxHtmlDCRenderer);
}; };

View File

@@ -471,7 +471,21 @@ public:
/** /**
Constructor. @a parent is pointer to parent container or @NULL. Constructor. @a parent is pointer to parent container or @NULL.
*/ */
wxHtmlContainerCell(wxHtmlContainerCell* parent); explicit wxHtmlContainerCell(wxHtmlContainerCell* parent);
/**
Detach a child cell.
Detaching a cell removes it from this container and allows to reattach
it to another one by using InsertCell(). Alternatively, this method can
be used to selectively remove some elements of the HTML document tree
by deleting the cell after calling it.
@param cell Must be non-null and an immediate child of this cell.
@since 3.1.2
*/
void Detach(wxHtmlCell* cell);
/** /**
Returns container's horizontal alignment. Returns container's horizontal alignment.
@@ -506,6 +520,9 @@ public:
/** /**
Inserts a new cell into the container. Inserts a new cell into the container.
Note that the container takes ownership of the cell and will delete it
when it itself is destroyed.
*/ */
void InsertCell(wxHtmlCell* cell); void InsertCell(wxHtmlCell* cell);

View File

@@ -168,6 +168,19 @@ public:
const wxString& basepath = wxEmptyString, const wxString& basepath = wxEmptyString,
bool isdir = true); bool isdir = true);
/**
Associate the given HTML contents to the renderer.
This is similar to SetHtmlText(), but is more efficient as the text can
be parsed only once, using wxHtmlParser::Parse(), and then passed to
wxHtmlDCRenderer multiple times or already reused for other purposes.
Note that @a cell will be modified (e.g. laid out) by this function.
@since 3.1.2
*/
void SetHtmlCell(wxHtmlContainerCell& cell);
/** /**
Set size of output rectangle, in pixels. Note that you @b can't change Set size of output rectangle, in pixels. Note that you @b can't change
width of the rectangle between calls to Render() ! width of the rectangle between calls to Render() !

View File

@@ -1159,6 +1159,43 @@ void wxHtmlContainerCell::InsertCell(wxHtmlCell *f)
void wxHtmlContainerCell::Detach(wxHtmlCell *cell)
{
wxHtmlCell* const firstChild = GetFirstChild();
if ( cell == firstChild )
{
m_Cells = cell->GetNext();
if ( m_LastCell == cell )
m_LastCell = NULL;
}
else // Not the first child.
{
for ( wxHtmlCell* prev = firstChild;; )
{
wxHtmlCell* const next = prev->GetNext();
// We can't reach the end of the children list without finding this
// cell, normally.
wxCHECK_RET( next, "Detaching cell which is not our child" );
if ( cell == next )
{
prev->SetNext(cell->GetNext());
if ( m_LastCell == cell )
m_LastCell = prev;
break;
}
prev = next;
}
}
cell->SetParent(NULL);
cell->SetNext(NULL);
}
void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag) void wxHtmlContainerCell::SetAlign(const wxHtmlTag& tag)
{ {
wxString alg; wxString alg;

View File

@@ -70,9 +70,8 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject()
m_DC = NULL; m_DC = NULL;
m_Width = m_Height = 0; m_Width = m_Height = 0;
m_Cells = NULL; m_Cells = NULL;
m_Parser = new wxHtmlWinParser(); m_ownsCells = false;
m_FS = new wxFileSystem(); m_Parser.SetFS(&m_FS);
m_Parser->SetFS(m_FS);
SetStandardFonts(DEFAULT_PRINT_FONT_SIZE); SetStandardFonts(DEFAULT_PRINT_FONT_SIZE);
} }
@@ -80,9 +79,8 @@ wxHtmlDCRenderer::wxHtmlDCRenderer() : wxObject()
wxHtmlDCRenderer::~wxHtmlDCRenderer() wxHtmlDCRenderer::~wxHtmlDCRenderer()
{ {
if (m_Cells) delete m_Cells; if ( m_ownsCells )
if (m_Parser) delete m_Parser; delete m_Cells;
if (m_FS) delete m_FS;
} }
@@ -90,7 +88,7 @@ wxHtmlDCRenderer::~wxHtmlDCRenderer()
void wxHtmlDCRenderer::SetDC(wxDC *dc, double pixel_scale, double font_scale) void wxHtmlDCRenderer::SetDC(wxDC *dc, double pixel_scale, double font_scale)
{ {
m_DC = dc; m_DC = dc;
m_Parser->SetDC(m_DC, pixel_scale, font_scale); m_Parser.SetDC(m_DC, pixel_scale, font_scale);
} }
@@ -110,19 +108,38 @@ void wxHtmlDCRenderer::SetHtmlText(const wxString& html, const wxString& basepat
wxCHECK_RET( m_DC, "SetDC() must be called before SetHtmlText()" ); wxCHECK_RET( m_DC, "SetDC() must be called before SetHtmlText()" );
wxCHECK_RET( m_Width, "SetSize() must be called before SetHtmlText()" ); wxCHECK_RET( m_Width, "SetSize() must be called before SetHtmlText()" );
wxDELETE(m_Cells); m_FS.ChangePathTo(basepath, isdir);
m_FS->ChangePathTo(basepath, isdir); wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser.Parse(html);
m_Cells = (wxHtmlContainerCell*) m_Parser->Parse(html); wxCHECK_RET( cell, "Failed to parse HTML" );
DoSetHtmlCell(cell);
m_ownsCells = true;
}
void wxHtmlDCRenderer::DoSetHtmlCell(wxHtmlContainerCell* cell)
{
if ( m_ownsCells )
delete m_Cells;
m_Cells = cell;
m_Cells->SetIndent(0, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS); m_Cells->SetIndent(0, wxHTML_INDENT_ALL, wxHTML_UNITS_PIXELS);
m_Cells->Layout(m_Width); m_Cells->Layout(m_Width);
} }
void wxHtmlDCRenderer::SetHtmlCell(wxHtmlContainerCell& cell)
{
DoSetHtmlCell(&cell);
m_ownsCells = false;
}
void wxHtmlDCRenderer::SetFonts(const wxString& normal_face, const wxString& fixed_face, void wxHtmlDCRenderer::SetFonts(const wxString& normal_face, const wxString& fixed_face,
const int *sizes) const int *sizes)
{ {
m_Parser->SetFonts(normal_face, fixed_face, sizes); m_Parser.SetFonts(normal_face, fixed_face, sizes);
if ( m_Cells ) if ( m_Cells )
m_Cells->Layout(m_Width); m_Cells->Layout(m_Width);
@@ -133,7 +150,7 @@ void wxHtmlDCRenderer::SetStandardFonts(int size,
const wxString& normal_face, const wxString& normal_face,
const wxString& fixed_face) const wxString& fixed_face)
{ {
m_Parser->SetStandardFonts(size, normal_face, fixed_face); m_Parser.SetStandardFonts(size, normal_face, fixed_face);
if ( m_Cells ) if ( m_Cells )
m_Cells->Layout(m_Width); m_Cells->Layout(m_Width);

View File

@@ -24,37 +24,8 @@
#include "wx/html/winpars.h" #include "wx/html/winpars.h"
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class HtmlParserTestCase : public CppUnit::TestCase
{
public:
HtmlParserTestCase() { }
private:
CPPUNIT_TEST_SUITE( HtmlParserTestCase );
CPPUNIT_TEST( Invalid );
CPPUNIT_TEST_SUITE_END();
void Invalid();
wxDECLARE_NO_COPY_CLASS(HtmlParserTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( HtmlParserTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HtmlParserTestCase, "HtmlParserTestCase" );
// ----------------------------------------------------------------------------
// tests themselves
// ----------------------------------------------------------------------------
// Test that parsing invalid HTML simply fails but doesn't crash for example. // Test that parsing invalid HTML simply fails but doesn't crash for example.
void HtmlParserTestCase::Invalid() TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
{ {
class NullParser : public wxHtmlWinParser class NullParser : public wxHtmlWinParser
{ {
@@ -75,4 +46,64 @@ void HtmlParserTestCase::Invalid()
p.Parse("<!---"); p.Parse("<!---");
} }
TEST_CASE("wxHtmlCell::Detach", "[html][cell]")
{
wxMemoryDC dc;
wxHtmlContainerCell* const top = new wxHtmlContainerCell(NULL);
wxHtmlContainerCell* const cont = new wxHtmlContainerCell(NULL);
wxHtmlCell* const cell1 = new wxHtmlWordCell("Hello", dc);
wxHtmlCell* const cell2 = new wxHtmlColourCell(*wxRED);
wxHtmlCell* const cell3 = new wxHtmlWordCell("world", dc);
cont->InsertCell(cell1);
cont->InsertCell(cell2);
cont->InsertCell(cell3);
top->InsertCell(cont);
SECTION("container")
{
top->Detach(cont);
CHECK( top->GetFirstChild() == NULL );
delete cont;
}
SECTION("first-child")
{
cont->Detach(cell1);
CHECK( cont->GetFirstChild() == cell2 );
delete cell1;
}
SECTION("middle-child")
{
cont->Detach(cell2);
CHECK( cont->GetFirstChild() == cell1 );
CHECK( cell1->GetNext() == cell3 );
delete cell2;
}
SECTION("last-child")
{
cont->Detach(cell3);
CHECK( cont->GetFirstChild() == cell1 );
CHECK( cell1->GetNext() == cell2 );
CHECK( cell2->GetNext() == NULL );
delete cell3;
}
SECTION("invalid")
{
WX_ASSERT_FAILS_WITH_ASSERT_MESSAGE
(
"Expected assertion for detaching non-child",
top->Detach(cell1);
);
}
}
#endif //wxUSE_HTML #endif //wxUSE_HTML