#include "wx/fontmap.h"
#include "wx/html/htmldefs.h"
#include "wx/html/htmlpars.h"
+#include "wx/dynarray.h"
+#include "wx/arrimpl.cpp"
+//-----------------------------------------------------------------------------
+// wxHtmlParser helpers
+//-----------------------------------------------------------------------------
+
+class wxHtmlTextPiece
+{
+public:
+ wxHtmlTextPiece(int pos, int lng) : m_pos(pos), m_lng(lng) {}
+ int m_pos, m_lng;
+};
+
+WX_DECLARE_OBJARRAY(wxHtmlTextPiece, wxHtmlTextPieces);
+WX_DEFINE_OBJARRAY(wxHtmlTextPieces);
+struct wxHtmlParserState
+{
+ wxHtmlTag *m_curTag;
+ wxHtmlTag *m_tags;
+ wxHtmlTextPieces *m_textPieces;
+ int m_curTextPiece;
+ wxString m_source;
+ wxHtmlParserState *m_nextState;
+};
//-----------------------------------------------------------------------------
// wxHtmlParser
IMPLEMENT_ABSTRACT_CLASS(wxHtmlParser,wxObject)
wxHtmlParser::wxHtmlParser()
- : wxObject(), m_Cache(NULL), m_HandlersHash(wxKEY_STRING),
+ : wxObject(), m_HandlersHash(wxKEY_STRING),
m_FS(NULL), m_HandlersStack(NULL)
{
m_entitiesParser = new wxHtmlEntitiesParser;
+ m_Tags = NULL;
+ m_CurTag = NULL;
+ m_TextPieces = NULL;
+ m_CurTextPiece = 0;
+ m_SavedStates = NULL;
}
wxHtmlParser::~wxHtmlParser()
{
+ while (RestoreState())
+ DestroyDOMTree();
delete m_HandlersStack;
m_HandlersHash.Clear();
m_HandlersList.DeleteContents(TRUE);
void wxHtmlParser::DoneParser()
{
- delete m_Cache;
- m_Cache = NULL;
+ DestroyDOMTree();
}
void wxHtmlParser::SetSource(const wxString& src)
{
+ DestroyDOMTree();
m_Source = src;
- delete m_Cache;
- m_Cache = new wxHtmlTagsCache(m_Source);
+ CreateDOMTree();
+ m_CurTag = NULL;
+ m_CurTextPiece = 0;
}
-void wxHtmlParser::DoParsing(int begin_pos, int end_pos)
+void wxHtmlParser::CreateDOMTree()
{
- if (end_pos <= begin_pos) return;
-
- char c;
- char *temp = new char[end_pos - begin_pos + 1];
- int i;
- int templen;
+ wxHtmlTagsCache cache(m_Source);
+ m_TextPieces = new wxHtmlTextPieces;
+ CreateDOMSubTree(NULL, 0, m_Source.Length(), &cache);
+ m_CurTextPiece = 0;
+}
- templen = 0;
- i = begin_pos;
+void wxHtmlParser::CreateDOMSubTree(wxHtmlTag *cur,
+ int begin_pos, int end_pos,
+ wxHtmlTagsCache *cache)
+{
+ if (end_pos <= begin_pos) return;
+ wxChar c;
+ int i = begin_pos;
+ int textBeginning = begin_pos;
+
while (i < end_pos)
{
- c = m_Source[(unsigned int) i];
-
- // continue building word:
- if (c != '<')
- {
- temp[templen++] = c;
- i++;
- }
-
- else if (c == '<')
- {
- wxHtmlTag tag(m_Source, i, end_pos, m_Cache, m_entitiesParser);
+ c = m_Source.GetChar(i);
- if (templen)
+ if (c == wxT('<'))
+ {
+ // add text to m_TextPieces:
+ if (i - textBeginning > 0)
+ m_TextPieces->Add(
+ wxHtmlTextPiece(textBeginning, i - textBeginning));
+
+ // if it is a comment, skip it:
+ if (i < end_pos-6 && m_Source.GetChar(i+1) == wxT('!') &&
+ m_Source.GetChar(i+2) == wxT('-') &&
+ m_Source.GetChar(i+3) == wxT('-'))
+ {
+ // Comments begin with "<!--" and end with "--[ \t\r\n]*>"
+ // according to HTML 4.0
+ int dashes = 0;
+ i += 4;
+ while (i < end_pos)
+ {
+ c = m_Source.GetChar(i++);
+ if ((c == wxT(' ') || c == wxT('\n') ||
+ c == wxT('\r') || c == wxT('\t')) && dashes >= 2) {}
+ else if (c == wxT('>') && dashes >= 2)
+ {
+ textBeginning = i;
+ break;
+ }
+ else if (c == wxT('-'))
+ dashes++;
+ else
+ dashes = 0;
+ }
+ }
+
+ // add another tag to the tree:
+ else if (i < end_pos-1 && m_Source.GetChar(i+1) != wxT('/'))
{
- temp[templen] = 0;
- AddText(temp);
- templen = 0;
+ wxHtmlTag *chd;
+ if (cur)
+ chd = new wxHtmlTag(cur, m_Source,
+ i, end_pos, cache, m_entitiesParser);
+ else
+ {
+ chd = new wxHtmlTag(NULL, m_Source,
+ i, end_pos, cache, m_entitiesParser);
+ if (!m_Tags)
+ {
+ // if this is the first tag to be created make the root
+ // m_Tags point to it:
+ m_Tags = chd;
+ }
+ else
+ {
+ // if there is already a root tag add this tag as
+ // the last sibling:
+ chd->m_Prev = m_Tags->GetLastSibling();
+ chd->m_Prev->m_Next = chd;
+ }
+ }
+
+ if (chd->HasEnding())
+ {
+ CreateDOMSubTree(chd,
+ chd->GetBeginPos(), chd->GetEndPos1(),
+ cache);
+ i = chd->GetEndPos2();
+ }
+ else
+ i = chd->GetBeginPos();
+ textBeginning = i;
+ }
+
+ // ... or skip ending tag:
+ else
+ {
+ while (i < end_pos && m_Source.GetChar(i) != wxT('>')) i++;
+ textBeginning = i+1;
}
- AddTag(tag);
- if (tag.HasEnding()) i = tag.GetEndPos2();
- else i = tag.GetBeginPos();
}
+ else i++;
}
- if (templen)
- { // last word of block :-(
- temp[templen] = 0;
- AddText(temp);
+ // add remaining text to m_TextPieces:
+ if (end_pos - textBeginning > 0)
+ m_TextPieces->Add(
+ wxHtmlTextPiece(textBeginning, end_pos - textBeginning));
+}
+
+void wxHtmlParser::DestroyDOMTree()
+{
+ wxHtmlTag *t1, *t2;
+ t1 = m_Tags;
+ while (t1)
+ {
+ t2 = t1->GetNextSibling();
+ delete t1;
+ t1 = t2;
+ }
+ m_Tags = m_CurTag = NULL;
+
+ delete m_TextPieces;
+ m_TextPieces = NULL;
+}
+
+void wxHtmlParser::DoParsing()
+{
+ m_CurTag = m_Tags;
+ m_CurTextPiece = 0;
+ DoParsing(0, m_Source.Length());
+}
+
+void wxHtmlParser::DoParsing(int begin_pos, int end_pos)
+{
+ if (end_pos <= begin_pos) return;
+
+ wxHtmlTextPieces& pieces = *m_TextPieces;
+ size_t piecesCnt = pieces.GetCount();
+
+ while (begin_pos < end_pos)
+ {
+ while (m_CurTag && m_CurTag->GetBeginPos() < begin_pos)
+ m_CurTag = m_CurTag->GetNextTag();
+ while (m_CurTextPiece < piecesCnt &&
+ pieces[m_CurTextPiece].m_pos < begin_pos)
+ m_CurTextPiece++;
+
+ if (m_CurTextPiece < piecesCnt &&
+ (!m_CurTag ||
+ pieces[m_CurTextPiece].m_pos < m_CurTag->GetBeginPos()))
+ {
+ // Add text:
+ AddText(m_Source.Mid(pieces[m_CurTextPiece].m_pos,
+ pieces[m_CurTextPiece].m_lng));
+ begin_pos = pieces[m_CurTextPiece].m_pos +
+ pieces[m_CurTextPiece].m_lng;
+ m_CurTextPiece++;
+ }
+ else if (m_CurTag)
+ {
+ // Add tag:
+ if (m_CurTag)
+ {
+ if (m_CurTag->HasEnding())
+ begin_pos = m_CurTag->GetEndPos2();
+ else
+ begin_pos = m_CurTag->GetBeginPos();
+ }
+ wxHtmlTag *t = m_CurTag;
+ m_CurTag = m_CurTag->GetNextTag();
+ AddTag(*t);
+ }
+ else break;
}
- delete[] temp;
}
void wxHtmlParser::AddTag(const wxHtmlTag& tag)
void wxHtmlParser::AddTagHandler(wxHtmlTagHandler *handler)
{
wxString s(handler->GetSupportedTags());
- wxStringTokenizer tokenizer(s, ", ");
+ wxStringTokenizer tokenizer(s, wxT(", "));
while (tokenizer.HasMoreTokens())
m_HandlersHash.Put(tokenizer.NextToken(), handler);
void wxHtmlParser::PushTagHandler(wxHtmlTagHandler *handler, wxString tags)
{
- wxStringTokenizer tokenizer(tags, ", ");
+ wxStringTokenizer tokenizer(tags, wxT(", "));
wxString key;
if (m_HandlersStack == NULL)
m_HandlersStack->DeleteNode(first);
}
+void wxHtmlParser::SetSourceAndSaveState(const wxString& src)
+{
+ wxHtmlParserState *s = new wxHtmlParserState;
+
+ s->m_curTag = m_CurTag;
+ s->m_tags = m_Tags;
+ s->m_textPieces = m_TextPieces;
+ s->m_curTextPiece = m_CurTextPiece;
+ s->m_source = m_Source;
+
+ s->m_nextState = m_SavedStates;
+ m_SavedStates = s;
+
+ m_CurTag = NULL;
+ m_Tags = NULL;
+ m_TextPieces = NULL;
+ m_CurTextPiece = 0;
+ m_Source = wxEmptyString;
+
+ SetSource(src);
+}
+
+bool wxHtmlParser::RestoreState()
+{
+ if (!m_SavedStates) return FALSE;
+
+ wxHtmlParserState *s = m_SavedStates;
+ m_SavedStates = s->m_nextState;
+
+ m_CurTag = s->m_curTag;
+ m_Tags = s->m_tags;
+ m_TextPieces = s->m_textPieces;
+ m_CurTextPiece = s->m_curTextPiece;
+ m_Source = s->m_source;
+
+ delete s;
+ return TRUE;
+}
+
//-----------------------------------------------------------------------------
// wxHtmlTagHandler
//-----------------------------------------------------------------------------
(*c >= wxT('0') && *c <= wxT('9')) ||
*c == wxT('_') || *c == wxT('#'); c++) {}
entity.append(ent_s, c - ent_s);
- if (*c == wxT(';')) c++;
+ if (*c != wxT(';')) c--;
+ last = c+1;
output << GetEntityChar(entity);
- last = c;
}
}
if (*last != wxT('\0'))
IMPLEMENT_CLASS(wxHtmlTag,wxObject)
-wxHtmlTag::wxHtmlTag(const wxString& source, int pos, int end_pos,
+wxHtmlTag::wxHtmlTag(wxHtmlTag *parent,
+ const wxString& source, int pos, int end_pos,
wxHtmlTagsCache *cache,
wxHtmlEntitiesParser *entParser) : wxObject()
{
+ /* Setup DOM relations */
+
+ m_Next = NULL;
+ m_FirstChild = m_LastChild = NULL;
+ m_Parent = parent;
+ if (parent)
+ {
+ m_Prev = m_Parent->m_LastChild;
+ if (m_Prev == NULL)
+ m_Parent->m_FirstChild = this;
+ else
+ m_Prev->m_Next = this;
+ m_Parent->m_LastChild = this;
+ }
+ else
+ m_Prev = NULL;
+
+ /* Find parameters and their values: */
+
int i;
wxChar c;
// fill-in name, params and begin pos:
i = pos+1;
- if (source[i] == wxT('/'))
- { m_Ending = TRUE; i++; }
- else
- m_Ending = FALSE;
// find tag's name and convert it to uppercase:
while ((i < end_pos) &&
if (m_End2 > end_pos) m_End2 = end_pos;
}
+wxHtmlTag::~wxHtmlTag()
+{
+ for (wxHtmlTag *t = m_FirstChild; t; t = t->GetNextSibling())
+ delete t;
+}
+
bool wxHtmlTag::HasParam(const wxString& par) const
{
return (m_ParamNames.Index(par, FALSE) != wxNOT_FOUND);
return s;
}
+wxHtmlTag *wxHtmlTag::GetFirstSibling() const
+{
+ if (m_Parent)
+ return m_Parent->m_FirstChild;
+ else
+ {
+ wxHtmlTag *cur = (wxHtmlTag*)this;
+ while (cur->m_Prev)
+ cur = cur->m_Prev;
+ return cur;
+ }
+}
+
+wxHtmlTag *wxHtmlTag::GetLastSibling() const
+{
+ if (m_Parent)
+ return m_Parent->m_LastChild;
+ else
+ {
+ wxHtmlTag *cur = (wxHtmlTag*)this;
+ while (cur->m_Next)
+ cur = cur->m_Next;
+ return cur;
+ }
+}
+
+wxHtmlTag *wxHtmlTag::GetNextTag() const
+{
+ if (m_FirstChild) return m_FirstChild;
+ if (m_Next) return m_Next;
+ wxHtmlTag *cur = m_Parent;
+ if (!cur) return NULL;
+ while (cur->m_Parent && !cur->m_Next)
+ cur = cur->m_Parent;
+ return cur->m_Next;
+}
+
#endif
// List Item:
if (tag.GetName() == wxT("LI"))
{
- if (!tag.IsEnding())
+ m_WParser->GetContainer()->SetIndent(0, wxHTML_INDENT_TOP);
+ // this is to prevent indetation in <li><p> case
+ m_WParser->CloseContainer();
+ m_WParser->CloseContainer();
+
+ c = m_WParser->OpenContainer();
+ c->SetWidthFloat(2 * m_WParser->GetCharWidth(), wxHTML_UNITS_PIXELS);
+ c->SetAlignHor(wxHTML_ALIGN_RIGHT);
+ if (m_Numbering == 0)
+ c->InsertCell(new wxHtmlListmarkCell(m_WParser->GetDC(), m_WParser->GetActualColor()));
+ else
{
- m_WParser->GetContainer()->SetIndent(0, wxHTML_INDENT_TOP);
- // this is to prevent indetation in <li><p> case
- m_WParser->CloseContainer();
- m_WParser->CloseContainer();
+ wxString mark;
+ mark.Printf(wxT("%i."), m_Numbering);
+ c->InsertCell(new wxHtmlWordCell(mark, *(m_WParser->GetDC())));
+ }
+ m_WParser->CloseContainer();
- c = m_WParser->OpenContainer();
- c->SetWidthFloat(2 * m_WParser->GetCharWidth(), wxHTML_UNITS_PIXELS);
- c->SetAlignHor(wxHTML_ALIGN_RIGHT);
- if (m_Numbering == 0)
- c->InsertCell(new wxHtmlListmarkCell(m_WParser->GetDC(), m_WParser->GetActualColor()));
- else
- {
- wxString mark;
- mark.Printf(wxT("%i."), m_Numbering);
- c->InsertCell(new wxHtmlWordCell(mark, *(m_WParser->GetDC())));
- }
- m_WParser->CloseContainer();
+ c = m_WParser->OpenContainer();
+ c->SetIndent(m_WParser->GetCharWidth() / 4, wxHTML_INDENT_LEFT);
+ c->SetWidthFloat(-2 * m_WParser->GetCharWidth(), wxHTML_UNITS_PIXELS);
- c = m_WParser->OpenContainer();
- c->SetIndent(m_WParser->GetCharWidth() / 4, wxHTML_INDENT_LEFT);
- c->SetWidthFloat(-2 * m_WParser->GetCharWidth(), wxHTML_UNITS_PIXELS);
+ m_WParser->OpenContainer();
- m_WParser->OpenContainer();
+ if (m_Numbering != 0) m_Numbering++;
- if (m_Numbering != 0) m_Numbering++;
- }
return FALSE;
}
wxHtmlWinParser::wxHtmlWinParser(wxWindow *wnd) : wxHtmlParser()
{
+ m_tmpStrBuf = NULL;
+ m_tmpStrBufSize = 0;
m_Window = wnd;
m_Container = NULL;
m_DC = NULL;
if (m_FontsTable[i][j][k][l][m] != NULL)
delete m_FontsTable[i][j][k][l][m];
}
- if (m_EncConv) delete m_EncConv;
+ delete m_EncConv;
+ delete[] m_tmpStrBuf;
}
}
-
-void wxHtmlWinParser::AddText(const char* txt)
+void wxHtmlWinParser::AddText(const wxChar* txt)
{
wxHtmlCell *c;
- int i = 0, x, lng = strlen(txt);
- char temp[wxHTML_BUFLEN];
- register char d;
+ int i = 0, x, lng = wxStrlen(txt);
+ register wxChar d;
int templen = 0;
+
+ if (lng+1 > m_tmpStrBufSize)
+ {
+ delete[] m_tmpStrBuf;
+ m_tmpStrBuf = new wxChar[lng+1];
+ m_tmpStrBufSize = lng+1;
+ }
+ wxChar *temp = m_tmpStrBuf;
if (m_tmpLastWasSpace)
{
while ((i < lng) &&
- ((txt[i] == '\n') || (txt[i] == '\r') || (txt[i] == ' ') ||
- (txt[i] == '\t'))) i++;
+ ((txt[i] == wxT('\n')) || (txt[i] == wxT('\r')) || (txt[i] == wxT(' ')) ||
+ (txt[i] == wxT('\t')))) i++;
}
while (i < lng)
{
x = 0;
d = temp[templen++] = txt[i];
- if ((d == '\n') || (d == '\r') || (d == ' ') || (d == '\t'))
+ if ((d == wxT('\n')) || (d == wxT('\r')) || (d == wxT(' ')) || (d == wxT('\t')))
{
i++, x++;
- while ((i < lng) && ((txt[i] == '\n') || (txt[i] == '\r') ||
- (txt[i] == ' ') || (txt[i] == '\t'))) i++, x++;
+ while ((i < lng) && ((txt[i] == wxT('\n')) || (txt[i] == wxT('\r')) ||
+ (txt[i] == wxT(' ')) || (txt[i] == wxT('\t')))) i++, x++;
}
else i++;
if (x)
{
- temp[templen-1] = ' ';
+ temp[templen-1] = wxT(' ');
temp[templen] = 0;
templen = 0;
if (m_EncConv)
if (m_EncConv)
m_EncConv->Convert(temp);
c = new wxHtmlWordCell(GetEntitiesParser()->Parse(temp), *(GetDC()));
- if (m_UseLink)
+ if (m_UseLink)
c->SetLink(m_Link);
m_Container->InsertCell(c);
m_tmpLastWasSpace = FALSE;