vnote/src/vtableofcontent.cpp
2017-10-13 07:10:04 +08:00

177 lines
4.6 KiB
C++

#include "vtableofcontent.h"
#include "vconstants.h"
#include <QXmlStreamReader>
#include <QDebug>
VTableOfContent::VTableOfContent()
: m_file(NULL), m_type(VTableOfContentType::Anchor)
{
}
VTableOfContent::VTableOfContent(const VFile *p_file)
: m_file(p_file), m_type(VTableOfContentType::Anchor)
{
}
void VTableOfContent::update(const VFile *p_file,
const QVector<VTableOfContentItem> &p_table,
VTableOfContentType p_type)
{
m_file = p_file;
m_table = p_table;
m_type = p_type;
}
static bool parseTocUl(QXmlStreamReader &p_xml,
QVector<VTableOfContentItem> &p_table,
int p_level);
static bool parseTocLi(QXmlStreamReader &p_xml,
QVector<VTableOfContentItem> &p_table,
int p_level)
{
Q_ASSERT(p_xml.isStartElement() && p_xml.name() == "li");
if (p_xml.readNextStartElement()) {
if (p_xml.name() == "a") {
QString anchor = p_xml.attributes().value("href").toString().mid(1);
QString name;
if (p_xml.readNext()) {
if (p_xml.tokenString() == "Characters") {
name = p_xml.text().toString();
} else if (!p_xml.isEndElement()) {
qWarning() << "TOC HTML <a> should be ended by </a>" << p_xml.name();
return false;
}
VTableOfContentItem header(name, p_level, anchor, p_table.size());
p_table.append(header);
} else {
// Error
return false;
}
} else if (p_xml.name() == "ul") {
// Such as header 3 under header 1 directly
VTableOfContentItem header(c_emptyHeaderName, p_level, "", p_table.size());
p_table.append(header);
parseTocUl(p_xml, p_table, p_level + 1);
} else {
qWarning() << "TOC HTML <li> should contain <a> or <ul>" << p_xml.name();
return false;
}
}
while (p_xml.readNext()) {
if (p_xml.isEndElement()) {
if (p_xml.name() == "li") {
return true;
}
continue;
}
if (p_xml.name() == "ul") {
// Nested unordered list
if (!parseTocUl(p_xml, p_table, p_level + 1)) {
return false;
}
} else {
return false;
}
}
return true;
}
static bool parseTocUl(QXmlStreamReader &p_xml,
QVector<VTableOfContentItem> &p_table,
int p_level)
{
bool ret = true;
Q_ASSERT(p_xml.isStartElement() && p_xml.name() == "ul");
while (p_xml.readNextStartElement()) {
if (p_xml.name() == "li") {
if (!parseTocLi(p_xml, p_table, p_level)) {
ret = false;
break;
}
} else {
qWarning() << "TOC HTML <ul> should contain <li>" << p_xml.name();
ret = false;
break;
}
}
return ret;
}
bool VTableOfContent::parseTableFromHtml(const QString &p_html)
{
bool ret = true;
m_table.clear();
if (!p_html.isEmpty()) {
QXmlStreamReader xml(p_html);
if (xml.readNextStartElement()) {
if (xml.name() == "ul") {
ret = parseTocUl(xml, m_table, 1);
} else {
qWarning() << "TOC HTML does not start with <ul>" << p_html;
ret = false;
}
}
if (xml.hasError()) {
qWarning() << "fail to parse TOC in HTML" << p_html;
ret = false;
}
}
return ret;
}
int VTableOfContent::indexOfItemByAnchor(const QString &p_anchor) const
{
if (p_anchor.isEmpty()
|| isEmpty()
|| m_type != VTableOfContentType::Anchor) {
return -1;
}
for (int i = 0; i < m_table.size(); ++i) {
if (m_table[i].m_anchor == p_anchor) {
return i;
}
}
return -1;
}
int VTableOfContent::indexOfItemByBlockNumber(int p_blockNumber) const
{
if (p_blockNumber == -1
|| isEmpty()
|| m_type != VTableOfContentType::BlockNumber) {
return -1;
}
for (int i = m_table.size() - 1; i >= 0; --i) {
if (!m_table[i].isEmpty()
&& m_table[i].m_blockNumber <= p_blockNumber) {
return i;
}
}
return -1;
}
bool VTableOfContent::operator==(const VTableOfContent &p_outline) const
{
return m_file == p_outline.getFile()
&& m_type == p_outline.getType()
&& m_table == p_outline.getTable();
}