#include "./htmlinfo.h"
#include "./utility.h"
#include "../gui/fileinfomodel.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(TAGEDITOR_GUI_QTWIDGETS)
#include
#include
#elif defined(TAGEDITOR_GUI_QTQUICK)
#include
#endif
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
#include
#include
#include
#endif
#include
#include
#include
#include
#include
#include
#ifdef QT_DEBUG
#include
#endif
#include
#if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
#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 CppUtilities;
using namespace QtUtilities;
using namespace TagParser;
using namespace Utility;
namespace HtmlInfo {
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, ElementPosition elementPosition, bool head = true)
{
const auto asString(Utility::elementPositionToQString(elementPosition));
if (!asString.isEmpty()) {
mkRow(label, asString, head);
}
}
void mkRow(const QString &label, const QString &helpText, const QString &text)
{
startRow(label, helpText);
writer.writeCharacters(text);
endRow();
}
void startSubTab(const QString &subLabel, size_t 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(QtGui::FileInfoModel::informationIcon());
return data;
}
const QByteArray &mkWarningIconData()
{
static const QByteArray data = mkBase64(QtGui::FileInfoModel::warningIcon());
return data;
}
const QByteArray &mkErrorIconData()
{
static const QByteArray data = mkBase64(QtGui::FileInfoModel::errorIcon());
return data;
}
const QByteArray &mkDebugIconData()
{
static const QByteArray data = mkBase64(QtGui::FileInfoModel::debugIcon());
return data;
}
template void mkElementContent(QXmlStreamWriter &, ElementType *)
{
}
template <> void mkElementContent(QXmlStreamWriter &writer, EbmlElement *element)
{
switch (element->id()) {
case MatroskaIds::SeekID: {
const std::uint64_t seekId = element->readUInteger();
writer.writeCharacters(QStringLiteral(", denoted type: 0x"));
writer.writeCharacters(QString::number(seekId, 16));
if (seekId <= numeric_limits::max()) {
const auto seekIdName = matroskaIdName(static_cast(seekId));
if (!seekIdName.empty()) {
writer.writeCharacters(QStringLiteral(" \""));
writer.writeCharacters(qstringFromStdStringView(seekIdName));
writer.writeCharacters(QStringLiteral("\""));
}
}
break;
}
case MatroskaIds::SeekPosition: {
writer.writeCharacters(QStringLiteral(", denoted position: "));
const std::uint64_t 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();
break;
}
default:;
}
}
class Generator {
public:
Generator(const MediaFileInfo &file, Diagnostics &diag, Diagnostics &diagReparsing)
: m_writer(&m_res)
, m_rowMaker(m_writer)
, m_file(file)
, m_diag(diag)
, m_diagReparsing(diagReparsing)
{
}
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::Window).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, td.debug {"
"width: 18px;"
"background-repeat: no-repeat;"
"background-position:center;"
"}"
".has-helptext {"
"cursor: help;"
"}"
"#structure_links a {"
"margin-right: 5px;"
"}"));
#if defined(TAGEDITOR_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(");}td.debug {background-image: url(data:image/png;base64,"));
res.append(mkDebugIconData());
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 mkBreak()
{
m_writer.writeStartElement(QStringLiteral("br"));
m_writer.writeEndElement();
}
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()));
auto 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 (track->format() != GeneralMediaFormat::Unknown && fmtName != fmtAbbr) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Abbreviation"),
QCoreApplication::translate("HtmlInfo", "The abbreviated name of the track's format."), qstr(fmtAbbr));
}
if (track->version() != 0.0) {
switch (track->format().general) {
case GeneralMediaFormat::Mpeg4Video:
case GeneralMediaFormat::Avc:
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Level"),
QCoreApplication::translate("HtmlInfo", "The version/level of the track's format."),
QChar('L') + QString::number(track->version()));
break;
default:
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Version"),
QCoreApplication::translate("HtmlInfo", "The version/level of the track's format."), QString::number(track->version()));
}
}
fmtName = track->format().extensionName();
if (!fmtName.empty()) {
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->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)) % QStringLiteral(" (")
% QString::number(track->duration().totalTicks()) % QChar(')'));
}
if (track->bitrate() > 0.0) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Avg. bitrate"), qstr(bitrateToString(track->bitrate())));
}
if (track->maxBitrate() > 0.0) {
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 (const auto &language = track->locale().fullOrSomeAbbreviatedName(); !language.empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Language"), qstr(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"),
QString::number(track->extensionSamplingFrequency()) % QStringLiteral(" Hz / ") % QString::number(track->samplingFrequency())
% QStringLiteral(" Hz"));
} else {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Sampling frequency"),
QString::number(track->samplingFrequency()) + QStringLiteral(" Hz"));
}
}
if (track->sampleCount()) {
rowMaker.mkRow(track->mediaType() == MediaType::Video ? QCoreApplication::translate("HtmlInfo", "Frame count")
: 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->pixelAspectRatio().isValid()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Pixel Aspect Ratio"), qstr(track->pixelAspectRatio().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 (const auto cc = track->channelConfigString(); !cc.empty()) {
if (const auto ecc = track->extensionChannelConfigString(); !ecc.empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Channel config"),
QCoreApplication::translate("HtmlInfo", "Channel configuration"), QStringLiteral("%1 / %2").arg(qstr(ecc), qstr(cc)));
} else {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Channel config"),
QCoreApplication::translate("HtmlInfo", "Channel configuration"), qstr(cc));
}
} 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()));
}
if (auto cf = track->chromaFormat(); !cf.empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Chroma format"), qstr(cf));
}
QStringList labels;
if (track->isInterlaced()) {
labels << QCoreApplication::translate("HtmlInfo", "interlaced");
}
if (!track->isEnabled()) {
labels << QCoreApplication::translate("HtmlInfo", "disabled");
}
if (track->isDefault()) {
labels << QCoreApplication::translate("HtmlInfo", "default");
}
if (track->isForced()) {
labels << QCoreApplication::translate("HtmlInfo", "forced");
}
if (track->hasLacing()) {
labels << QCoreApplication::translate("HtmlInfo", "has lacing");
}
if (track->isEncrypted()) {
labels << QCoreApplication::translate("HtmlInfo", "encrypted");
}
if (!labels.isEmpty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), labels.join(QStringLiteral(", ")));
}
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(static_cast(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, size_t 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()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Name (%1)").arg(qstr(name.locale().toString())), qstr(name));
}
if (!chapter.startTime().isNegative()) {
rowMaker.mkRow(
QCoreApplication::translate("HtmlInfo", "Start time"), qstr(chapter.startTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
if (!chapter.endTime().isNegative()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "End time"), qstr(chapter.endTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
QStringList labels;
if (chapter.isHidden()) {
labels << QCoreApplication::translate("HtmlInfo", "hidden");
}
if (!chapter.isEnabled()) {
labels << QCoreApplication::translate("HtmlInfo", "disabled");
}
if (!labels.empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), labels.join(QStringLiteral(", ")));
}
if (!chapter.tracks().empty()) {
QStringList trackIds;
for (std::uint64_t 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()));
}
QStringList labels;
if (edition.isHidden()) {
labels << QCoreApplication::translate("HtmlInfo", "hidden");
}
if (edition.isDefault()) {
labels << QCoreApplication::translate("HtmlInfo", "default");
}
if (edition.isOrdered()) {
labels << QCoreApplication::translate("HtmlInfo", "ordered");
}
if (!labels.isEmpty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), labels.join(QStringLiteral(", ")));
}
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Chapters"),
QCoreApplication::translate("HtmlInfo", "edition contains %1 chapter(s)", nullptr, static_cast(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);"));
}
string idString = element->idToString();
if (!idString.empty()) {
m_writer.writeTextElement(QStringLiteral("em"), QString::fromLatin1(idString.data(), static_cast(idString.size())));
}
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)
{
if (container->firstElement()) {
mkElementNode(container->firstElement());
}
for (auto &element : container->additionalElements()) {
mkElementNode(element.get());
}
}
void mkNotifications(Diagnostics &diag, bool reparsing = false)
{
if (diag.empty()) {
return;
}
startTableSection();
const QString moreId(reparsing ? QStringLiteral("notificationsReparsingMore") : QStringLiteral("notificationsMore"));
m_rowMaker.startRow(reparsing ? QCoreApplication::translate("HtmlInfo", "Notifications (reparsing after saving)")
: QCoreApplication::translate("HtmlInfo", "Notifications"));
m_writer.writeCharacters(
QCoreApplication::translate("HtmlInfo", "%1 notification(s) available", nullptr, trQuandity(diag.size())).arg(diag.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") }));
for (const auto &msg : diag) {
m_writer.writeStartElement(QStringLiteral("tr"));
m_writer.writeEmptyElement(QStringLiteral("td"));
m_writer.writeAttribute(QStringLiteral("class"), qstr(msg.levelName()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.context()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.message()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.creationTime().toString(DateTimeOutputFormat::DateAndTime, false)));
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())));
//