#include "./htmlinfo.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(GUI_QTWIDGETS)
#include
#include
#elif defined(GUI_QTQUICK)
#include
#elif !defined(GUI_NONE)
#define GUI_NONE
#endif
#ifndef GUI_NONE
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#include
#ifdef QT_DEBUG
#include
#endif
#include
#ifndef GUI_NONE
# define APPEND_GUI_RULE(rule, fallback) \
if(ApplicationInstances::hasGuiApp()) { \
res.append(rule); \
} else { \
res.append(fallback); \
}
#else
# define APPEND_GUI_RULE(rule, fallback) \
res.append(fallback);
#endif
using namespace std;
using namespace ConversionUtilities;
using namespace ChronoUtilities;
using namespace Media;
namespace HtmlInfo {
inline QString qstr(const char *cstr)
{
return QString::fromLocal8Bit(cstr);
}
inline QString qstr(const string &stdstr)
{
return qstr(stdstr.c_str());
}
class RowMaker
{
public:
RowMaker(QXmlStreamWriter &writer) :
writer(writer),
m_even(true)
{}
void reset()
{
m_even = true;
}
void startRow(const QString &label, bool head = true)
{
writer.writeStartElement(QStringLiteral("tr"));
if((m_even = !m_even)) {
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("even"));
}
writer.writeTextElement(head ? QStringLiteral("th") : QStringLiteral("td"), label);
writer.writeStartElement(QStringLiteral("td"));
}
void startRow(const QString &label, const QString &helpText)
{
writer.writeStartElement(QStringLiteral("tr"));
if((m_even = !m_even)) {
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("even"));
}
writer.writeAttribute(QStringLiteral("title"), helpText);
writer.writeTextElement(QStringLiteral("th"), label);
writer.writeStartElement(QStringLiteral("td"));
}
void endRow()
{
writer.writeEndElement();
writer.writeEndElement();
}
void mkRow(const QString &label, const QString &text, bool head = true)
{
startRow(label, head);
writer.writeCharacters(text);
endRow();
}
void mkRow(const QString &label, const QString &helpText, const QString &text)
{
startRow(label, helpText);
writer.writeCharacters(text);
endRow();
}
void startSubTab(const QString &subLabel, unsigned int index, unsigned int level = 0)
{
writer.writeStartElement(QStringLiteral("tr"));
if((m_even = !m_even)) {
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("even"));
}
writer.writeStartElement(QStringLiteral("th"));
writer.writeCharacters(subLabel % QStringLiteral(" #") % QString::number(index));
if(level) {
writer.writeEmptyElement(QStringLiteral("br"));
writer.writeCharacters(QString::number(level));
}
writer.writeEndElement();
writer.writeStartElement(QStringLiteral("td"));
writer.writeStartElement(QStringLiteral("table"));
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("headervertical"));
writer.writeStartElement(QStringLiteral("tbody"));
}
void startSubTab(const QString &subLabel)
{
writer.writeStartElement(QStringLiteral("tr"));
if((m_even = !m_even)) {
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("even"));
}
writer.writeTextElement(QStringLiteral("th"), subLabel);
writer.writeStartElement(QStringLiteral("td"));
writer.writeStartElement(QStringLiteral("table"));
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("headervertical"));
writer.writeStartElement(QStringLiteral("tbody"));
}
void startHorizontalSubTab(const QString &subLabel, const QStringList &headerLabels)
{
writer.writeStartElement(QStringLiteral("tr"));
if((m_even = !m_even)) {
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("even"));
}
writer.writeTextElement(QStringLiteral("th"), subLabel);
writer.writeStartElement(QStringLiteral("td"));
writer.writeStartElement(QStringLiteral("table"));
writer.writeAttribute(QStringLiteral("class"), QStringLiteral("headerhorizontal"));
writer.writeStartElement(QStringLiteral("thead"));
writer.writeStartElement(QStringLiteral("tr"));
for(const auto &label : headerLabels) {
writer.writeTextElement(QStringLiteral("th"), label);
}
writer.writeEndElement();
writer.writeEndElement();
writer.writeStartElement(QStringLiteral("tbody"));
}
void endSubTab()
{
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndElement();
}
private:
QXmlStreamWriter &writer;
bool m_even;
};
QString mkFontStyle(const QFont &font)
{
QString style;
switch(font.style()) {
case QFont::StyleItalic:
style = QStringLiteral("italic");
break;
case QFont::StyleOblique:
style = QStringLiteral("oblique");
break;
default:
style = QStringLiteral("normal");
}
QString weight;
switch(font.weight()) {
case QFont::Light:
weight = QStringLiteral("light");
break;
case QFont::Bold:
weight = QStringLiteral("bold");
break;
case QFont::Black:
weight = QStringLiteral("bolder");
break;
default:
weight = QStringLiteral("normal");
}
QString size;
if(font.pixelSize() > 0) {
size = QStringLiteral("%1px").arg(font.pixelSize());
} else {
size = QStringLiteral("%1pt").arg(font.pointSize());
}
return QStringLiteral("font-family: \"%1\", sans-serif;"
"font-style: %2;"
"font-weight: %3;"
"font-size: %4;").arg(
font.family(), style, weight, size);
}
QByteArray mkBase64(const QIcon &icon)
{
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
icon.pixmap(16, 16).save(&buffer, "PNG");
return bytes.toBase64();
}
const QByteArray &mkInfoIconData()
{
static const QByteArray data = mkBase64(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation));
return data;
}
const QByteArray &mkWarningIconData()
{
static const QByteArray data = mkBase64(QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning));
return data;
}
const QByteArray &mkErrorIconData()
{
static const QByteArray data = mkBase64(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical));
return data;
}
const QByteArray &mkDebugIconData()
{
static const QByteArray data = mkBase64(QIcon(QStringLiteral(":/images/bug")));
return data;
}
template void mkElementContent(QXmlStreamWriter &, ElementType *)
{}
template<> void mkElementContent(QXmlStreamWriter &writer, EbmlElement *element)
{
switch(element->id()) {
case MatroskaIds::SeekID: {
const uint64 seekId = element->readUInteger();
writer.writeCharacters(QStringLiteral(", denoted type: 0x"));
writer.writeCharacters(QString::number(seekId, 16));
const char *seekIdName = matroskaIdName(seekId);
if(*seekIdName) {
writer.writeCharacters(QStringLiteral(" \""));
writer.writeCharacters(QString::fromLatin1(seekIdName));
writer.writeCharacters(QStringLiteral("\""));
}
break;
} case MatroskaIds::SeekPosition: {
writer.writeCharacters(QStringLiteral(", denoted position: "));
const uint64 seekPos = element->readUInteger();
const auto seekPosStr = QString::number(seekPos);
writer.writeStartElement(QStringLiteral("span"));
writer.writeAttribute(QStringLiteral("data-role"), QStringLiteral("offset"));
writer.writeAttribute(QStringLiteral("data-dec"), seekPosStr);
writer.writeAttribute(QStringLiteral("data-hex"), QString::number(seekPos, 16));
writer.writeCharacters(seekPosStr);
writer.writeEndElement();
} default:
;
}
}
class Generator
{
public:
Generator(const MediaFileInfo &file, NotificationList &originalNotifications) :
m_writer(&m_res),
m_rowMaker(m_writer),
m_file(file),
originalNotifications(originalNotifications)
{}
QString mkStyle()
{
QString res;
res.append(QStringLiteral("html, body {"
"padding: 1px;"
"margin: 0px;"));
#ifndef GUI_NONE
QPalette palette;
if(ApplicationInstances::hasGuiApp()) {
palette = QGuiApplication::palette();
res.append(mkFontStyle(QGuiApplication::font()));
res.append(QStringLiteral("background-color: %1;").arg(palette.color(QPalette::Base).name()));
res.append(QStringLiteral("color: %1;").arg(palette.color(QPalette::Text).name()));
} else {
#endif
res.append(QStringLiteral("background-color: #fff;"
"color: #000;"));
#ifndef GUI_NONE
}
#endif
res.append(QStringLiteral("}"
"a:link, #structure .parent-node {"));
APPEND_GUI_RULE(QStringLiteral("color: %1;").arg(palette.color(QPalette::Link).name()),
QStringLiteral("color: #337AB7;"))
res.append(QStringLiteral("text-decoration: none;"
"}"
"a:focus, a:hover {"));
APPEND_GUI_RULE(QStringLiteral("color: %1;").arg(palette.link().color().darker(palette.color(QPalette::Background).lightness() > palette.color(QPalette::Link).lightness() ? 150 : 50).name()),
QStringLiteral("color: #23527c;"))
res.append(QStringLiteral("text-decoration: underline;"
"} table {"
"border-collapse: collapse;"
"width: 100%;"
"} tr {"));
APPEND_GUI_RULE(QStringLiteral("background-color: %1;").arg(palette.color(QPalette::Base).name()),
QStringLiteral("background-color: #fff;"))
res.append(QStringLiteral("} tr.even {"));
APPEND_GUI_RULE(QStringLiteral("background-color: %1!important;").arg(palette.color(QPalette::AlternateBase).name()), QStringLiteral("background-color: #fafafa!important;"))
res.append(QStringLiteral("}"
"table.headervertical th, table.headervertical td {"
"padding: 2px 5px;"
"text-align: left;"
"vertical-align: top;"
"}"
"table.headervertical > tbody > tr > th {"
"text-align: right;"
"border: none;"
"width: 25%;"
"}"
"table.headerhorizontal > thead > tr > th {"
"text-align: center;"
"border: none;"
"}"
".more {"
"display: none;"
"}"
".more tr > th {"
"font-weight: normal;"
"}"
".nodecollapsed {"
"display: none;"
"}"
".nodeexpaned {"
"display: block;"
"}"
"table.headerhorizontal > thead > tr > th {"
"font-weight: bold;"
"}"
"td table.headervertical > thead > tr > th {"
"font-weight: normal;"
"border: none;"
"background-color: transparent;"
"}"
"th {"
"cursor: default;"
"}"
"td.warning, td.critical, td.information {"
"width: 18px;"
"background-repeat: no-repeat;"
"background-position:center;"
"}"
".has-helptext {"
"cursor: help;"
"}"
"#structure_links a {"
"margin-right: 5px;"
"}"));
#ifdef GUI_QTWIDGETS
if(ApplicationInstances::hasWidgetsApp()) {
res.append(QStringLiteral("td.warning {background-image: url(data:image/png;base64,"));
res.append(mkWarningIconData());
res.append(QStringLiteral(");}td.critical {background-image: url(data:image/png;base64,"));
res.append(mkErrorIconData());
res.append(QStringLiteral(");}td.information {background-image: url(data:image/png;base64,"));
res.append(mkInfoIconData());
res.append(QStringLiteral(");}"));
}
#endif
res.append(QStringLiteral("ul {"
"margin: 0px;"
"padding: 0px 0px 0px 15px;"
"list-style-type: none;"
"}"));
return res;
}
QString mkScript()
{
QString res;
res.append(QStringLiteral("function toggleVisibility(link, objid) {"
"var obj = document.getElementById(objid);"
"if(obj.style.display === \"none\" || (obj.style.display === \"\" && obj.className === \"more\")) {"
"if(obj.nodeName === \"TBODY\" || obj.nodeName === \"tbody\") {"
"obj.style.display = \"table-row-group\";"
"} else {"
"obj.style.display = \"block\";"
"}"
"link.defaultText = link.innerHTML;"
"link.innerHTML = \""));
res.append(QCoreApplication::translate("HtmlInfo", "hide"));
res.append(QStringLiteral("\";"
"} else {"
"obj.style.display = \"none\";"
"link.innerHTML = link.defaultText;"
"}}"
"function expandCollapse(parent) {"
"for(var children = parent.children, i = 0, c = children.length; i < c; ++i) {"
"if(children[i].tagName === 'UL' || children[i].tagName === 'ul') {"
"children[i].className = children[i].className === 'nodeexpanded' ? 'nodecollapsed' : 'nodeexpanded';"
"}}}"
"function expandAll(objid) {"
"var children = document.getElementById(objid).getElementsByTagName('ul');"
"for(var i = 0; i < children.length; ++i) {"
"children[i].className = 'nodeexpanded';"
"}}"
"function collapseAll(objid) {"
"var children = document.getElementById(objid).getElementsByTagName('ul');"
"for(var i = 0; i < children.length; ++i) {"
"if(children[i].parentNode.tagName === 'LI' || children[i].parentNode.tagName === 'li') {"
"children[i].className = 'nodecollapsed';"
"}}}"
"var baseHex = false;"
"function switchBase(objid) {"
"baseHex = !baseHex;"
"document.getElementById('toggle_hex_link').firstChild.data = baseHex ? 'decimal' : 'hex';"
"var elements = document.getElementById(objid).getElementsByTagName('span');"
"for(var i = 0; i < elements.length; ++i) {"
"var element = elements[i];"
"if(element.getAttribute('data-role') === 'offset') {"
"element.firstChild.data = element.getAttribute(baseHex ? 'data-hex' : 'data-dec');"
"}}}"));
return res;
}
void startVerticalTable()
{
m_writer.writeStartElement(QStringLiteral("table"));
m_writer.writeAttribute(QStringLiteral("class"), QStringLiteral("headervertical"));
}
void startTableSection(const QString &id)
{
m_writer.writeStartElement(QStringLiteral("tbody"));
m_writer.writeAttribute(QStringLiteral("id"), id);
}
void startTableSection()
{
m_writer.writeStartElement(QStringLiteral("tbody"));
}
void startExtendedTableSection(const QString &id)
{
startTableSection(id);
m_writer.writeAttribute(QStringLiteral("class"), QStringLiteral("more"));
}
void mkSpace()
{
m_writer.writeCharacters(QStringLiteral(" "));
}
void mkDetailsLink(const QString &id, const QString &text)
{
m_writer.writeStartElement(QStringLiteral("a"));
m_writer.writeAttribute(QStringLiteral("href"), QStringLiteral("#"));
m_writer.writeAttribute(QStringLiteral("onclick"), QStringLiteral("toggleVisibility(this, '") % id % QStringLiteral("'); return false;"));
m_writer.writeCharacters(text);
m_writer.writeEndElement();
}
void mkTrack(const AbstractTrack *track, unsigned int trackNumber)
{
RowMaker rowMaker(m_writer);
rowMaker.startSubTab(QCoreApplication::translate("HtmlInfo", "Track"), trackNumber);
if(track->id()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "ID"), QCoreApplication::translate("HtmlInfo", "The unique number used to identify the track in the container file."), QString::number(track->id()));
}
if(track->trackNumber()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Number"), QCoreApplication::translate("HtmlInfo", "The index of the track in the container file."), QString::number(track->trackNumber()));
}
if(!track->name().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Name"), qstr(track->name()));
}
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Type"), qstr(track->mediaTypeName()));
const char *fmtName = track->formatName(), *fmtAbbr = track->formatAbbreviation();
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Format"), QCoreApplication::translate("HtmlInfo", "The unabbreviated name of the track's format."), qstr(fmtName));
if(strcmp(fmtName, fmtAbbr)) {
// format name and abbreviation differ
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Abbreviation"), QCoreApplication::translate("HtmlInfo", "The abbreviated name of the track's format."), qstr(fmtAbbr));
}
fmtName = track->format().extensionName();
if(*fmtName) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Extension"), QCoreApplication::translate("HtmlInfo", "Used format extensions."), qstr(fmtName));
}
if(!track->formatId().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Format/codec ID"), QCoreApplication::translate("HtmlInfo", "The raw format/codec identifier extracted from the container."), qstr(track->formatId()));
}
if(track->version()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Version"), QString::number(track->version()));
}
if(track->size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Size"), qstr(dataSizeToString(track->size(), true)));
}
if(!track->duration().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Duration"), qstr(track->duration().toString(TimeSpanOutputFormat::WithMeasures)));
}
if(track->bitrate()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Avg. bitrate"), qstr(bitrateToString(track->bitrate())));
}
if(track->maxBitrate()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Maximum bitrate"), qstr(bitrateToString(track->maxBitrate())));
}
if(!track->creationTime().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Creation time"), qstr(track->creationTime().toString(DateTimeOutputFormat::DateAndTime, true)));
}
if(!track->modificationTime().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Modification time"), qstr(track->modificationTime().toString(DateTimeOutputFormat::DateAndTime, true)));
}
if(!track->language().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Language"), qstr(track->language()));
}
if(!track->compressorName().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Compressor name"), qstr(track->compressorName()));
}
if(track->samplingFrequency()) {
if(track->extensionSamplingFrequency()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Sampling frequency"), QStringLiteral("%1 Hz / %2 Hz").arg(track->extensionSamplingFrequency()).arg(track->samplingFrequency()));
} else {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Sampling frequency"), QStringLiteral("%1 Hz").arg(track->samplingFrequency()));
}
}
if(track->sampleCount()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Sample count"), QString::number(track->sampleCount()));
}
if(track->bitsPerSample()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Bits per sample"), QString::number(track->bitsPerSample()));
}
if(track->quality()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Quality"), QString::number(track->quality()));
}
if(!track->pixelSize().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Pixel size"), qstr(track->pixelSize().toString()));
}
if(!track->displaySize().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Display size"), qstr(track->displaySize().toString()));
}
if(!track->cropping().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Cropping"), qstr(track->cropping().toString()));
}
if(!track->resolution().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Resolution"), qstr(track->resolution().toString()));
}
if(track->channelConfigString()) {
if(track->extensionChannelConfigString()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Channel config"), QCoreApplication::translate("HtmlInfo", "Channel configuration"), QStringLiteral("%1 / %2").arg(QString::fromLocal8Bit(track->extensionChannelConfigString()), QString::fromLocal8Bit(track->channelConfigString())));
} else {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Channel config"), QCoreApplication::translate("HtmlInfo", "Channel configuration"), QString::fromLocal8Bit(track->channelConfigString()));
}
} else if(track->channelCount()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Channel count"), QString::number(track->channelCount()));
}
if(track->depth()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Bit depth"), QString::number(track->depth()));
}
if(track->fps()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Frames per second"), QString::number(track->fps()));
}
list labels;
if(track->isInterlaced()) {
labels.push_back("interlaced");
}
if(!track->isEnabled()) {
labels.push_back("disabled");
}
if(track->isDefault()) {
labels.push_back("default");
}
if(track->isForced()) {
labels.push_back("forced");
}
if(track->hasLacing()) {
labels.push_back("has lacing");
}
if(track->isEncrypted()) {
labels.push_back("encrypted");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
}
rowMaker.endSubTab();
}
void mkAttachment(const AbstractAttachment *attachment, unsigned int attachmentNumber)
{
RowMaker rowMaker(m_writer);
m_rowMaker.startSubTab(QCoreApplication::translate("HtmlInfo", "Attachment"), attachmentNumber);
if(attachment->id()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "ID"), QString::number(attachment->id()));
}
if(!attachment->name().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Name"), qstr(attachment->name()));
}
if(attachment->data()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Size"), qstr(dataSizeToString(attachment->data()->size(), true)));
}
if(!attachment->mimeType().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Mime-type"), qstr(attachment->mimeType()));
}
if(!attachment->description().empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Description"), qstr(attachment->description()));
}
rowMaker.endSubTab();
}
void mkChapter(const AbstractChapter &chapter, unsigned int chapterNumber, unsigned int level = 0)
{
RowMaker rowMaker(m_writer);
rowMaker.startSubTab(QCoreApplication::translate("HtmlInfo", "Chapter"), chapterNumber + 1, level);
if(chapter.id()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "ID"), QString::number(chapter.id()));
}
for(const LocaleAwareString &name : chapter.names()) {
static const string delim(", ");
string locale = joinStrings(initializer_list{joinStrings(name.languages(), delim, true), joinStrings(name.countries(), delim, true)}, delim, true);
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Name (%1)").arg(qstr(locale)), qstr(name));
}
if(chapter.startTime().totalTicks() > 0) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Start time"), qstr(chapter.startTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
if(chapter.endTime().totalTicks() > 0) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "End time"), qstr(chapter.endTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
list labels;
if(chapter.isHidden()) {
labels.push_back("hidden");
}
if(!chapter.isEnabled()) {
labels.push_back("disabled");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
}
if(chapter.tracks().size()) {
QStringList trackIds;
for(uint64 id : chapter.tracks()) {
trackIds << QString::number(id);
}
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Tracks"), trackIds.join(QStringLiteral(", ")));
}
rowMaker.endSubTab();
++level;
for(size_t i = 0, nestedChapters = chapter.nestedChapterCount(); i < nestedChapters; ++i) {
mkChapter(*chapter.nestedChapter(i), i, level);
}
}
void mkEdition(const MatroskaEditionEntry &edition, unsigned int editionNumber)
{
RowMaker rowMaker(m_writer);
rowMaker.startSubTab(QCoreApplication::translate("HtmlInfo", "Edition"), editionNumber + 1);
if(edition.id()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "ID"), QString::number(edition.id()));
}
list labels;
if(edition.isHidden()) {
labels.push_back("hidden");
}
if(edition.isDefault()) {
labels.push_back("default");
}
if(edition.isOrdered()) {
labels.push_back("ordered");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
}
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Chapters"),
QCoreApplication::translate("HtmlInfo", "edition contains %1 chapter(s)", nullptr, edition.chapters().size()).arg(edition.chapters().size()));
rowMaker.endSubTab();
unsigned int chapterNumber = 0;
for(const auto &chapter : edition.chapters()) {
mkChapter(*chapter, chapterNumber);
++chapterNumber;
}
}
template void mkElementNode(ElementType *element)
{
m_writer.writeStartElement(QStringLiteral("ul"));
m_writer.writeAttribute(QStringLiteral("class"), element && element->parent() ? QStringLiteral("nodecollapsed") : QStringLiteral("nodeexpanded"));
while(element) {
if(element->isParsed()) {
m_writer.writeStartElement(QStringLiteral("li"));
if(element->firstChild()) {
m_writer.writeStartElement(QStringLiteral("span"));
m_writer.writeAttribute(QStringLiteral("class"), QStringLiteral("parent-node"));
m_writer.writeAttribute(QStringLiteral("onclick"), QStringLiteral("expandCollapse(this.parentElement);"));
}
m_writer.writeTextElement(QStringLiteral("em"), QString::fromLatin1(element->idToString().c_str()));
m_writer.writeCharacters(QStringLiteral(" @"));
const auto startOffsetStr = QString::number(element->startOffset());
m_writer.writeStartElement(QStringLiteral("span"));
m_writer.writeAttribute(QStringLiteral("data-role"), QStringLiteral("offset"));
m_writer.writeAttribute(QStringLiteral("data-dec"), startOffsetStr);
m_writer.writeAttribute(QStringLiteral("data-hex"), QString::number(element->startOffset(), 16));
m_writer.writeCharacters(startOffsetStr);
m_writer.writeEndElement();
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", ", size: "));
const auto sizeStr = QString::number(element->totalSize());
m_writer.writeStartElement(QStringLiteral("span"));
m_writer.writeAttribute(QStringLiteral("data-role"), QStringLiteral("offset"));
m_writer.writeAttribute(QStringLiteral("data-dec"), sizeStr);
m_writer.writeAttribute(QStringLiteral("data-hex"), QString::number(element->totalSize(), 16));
m_writer.writeCharacters(sizeStr);
m_writer.writeEndElement();
mkElementContent(m_writer, element);
if(element->firstChild()) {
m_writer.writeEndElement();
mkElementNode(element->firstChild());
}
element = element->nextSibling();
m_writer.writeEndElement();
} else {
if(!isAdditional) {
m_writer.writeStartElement(QStringLiteral("li"));
m_writer.writeAttribute(QStringLiteral("style"), QStringLiteral("color: red;"));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "section has not been analyzed"));
m_writer.writeEndElement();
}
break;
}
}
m_writer.writeEndElement();
}
template void mkElementTree(ContainerType *container)
{
mkElementNode(container->firstElement());
for(auto &element : container->additionalElements()) {
mkElementNode(element.get());
}
}
void mkNotifications(NotificationList ¬ifications, bool reparsing = false)
{
if(notifications.size()) {
startTableSection();
const QString moreId(reparsing ? QStringLiteral("notificationsReparsingMore") : QStringLiteral("notificationsMore"));
m_rowMaker.startRow(reparsing ? QCoreApplication::translate("HtmlInfo", "Notifications (reparsing)") : QCoreApplication::translate("HtmlInfo", "Notifications"));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "%1 notification(s) available", 0, notifications.size()).arg(notifications.size()));
mkSpace();
mkDetailsLink(moreId, QCoreApplication::translate("HtmlInfo", "show notifications"));
m_rowMaker.endRow();
m_writer.writeEndElement();
startExtendedTableSection(moreId);
m_rowMaker.startHorizontalSubTab(QString(), QStringList() << QString() << QCoreApplication::translate("HtmlInfo", "Context") << QCoreApplication::translate("HtmlInfo", "Message") << QCoreApplication::translate("HtmlInfo", "Time"));
Notification::sortByTime(notifications);
for(const Notification ¬ification : notifications) {
m_writer.writeStartElement(QStringLiteral("tr"));
m_writer.writeEmptyElement(QStringLiteral("td"));
m_writer.writeAttribute(QStringLiteral("class"), qstr(notification.typeName()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.message()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.creationTime().toString(DateTimeOutputFormat::DateAndTime, false)));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.context()));
m_writer.writeEndElement();
}
m_rowMaker.endSubTab();
m_writer.writeEndElement();
}
}
void mkDoc()
{
#ifdef QT_DEBUG
m_writer.setAutoFormatting(true);
#endif
//
m_writer.writeStartDocument();
m_writer.writeDTD(QStringLiteral(""));
m_writer.writeStartElement(QStringLiteral("html"));
m_writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://www.w3.org/1999/xhtml"));
m_writer.writeStartElement(QStringLiteral("head"));
//
m_writer.writeTextElement(QStringLiteral("title"), QCoreApplication::translate("HtmlInfo", "Info for %1").arg(qstr(m_file.fileName())));
//