added FileInfoModel as alternative to webview-based file info

This commit is contained in:
Martchus 2016-04-24 21:07:36 +02:00
parent 0ac33e333c
commit 6f6b410a8c
13 changed files with 3861 additions and 2137 deletions

View File

@ -49,6 +49,7 @@ set(WIDGETS_HEADER_FILES
gui/tagedit.h
gui/tagfieldedit.h
gui/tageditorwidget.h
gui/fileinfomodel.h
dbquery/dbquery.h
gui/dbquerywidget.h
misc/networkaccessmanager.h
@ -78,6 +79,7 @@ set(WIDGETS_SRC_FILES
gui/tagedit.cpp
gui/tagfieldedit.cpp
gui/tageditorwidget.cpp
gui/fileinfomodel.cpp
dbquery/dbquery.cpp
gui/dbquerywidget.cpp
misc/networkaccessmanager.cpp

View File

@ -7,10 +7,7 @@
#elif defined(GUI_QTQUICK)
#endif
// include configuration from separate header file when building with CMake
#ifndef APP_METADATA_AVAIL
#include "resources/config.h"
#endif
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# include <qtutilities/resources/qtconfigarguments.h>

593
gui/fileinfomodel.cpp Normal file
View File

@ -0,0 +1,593 @@
#include "./fileinfomodel.h"
#include <tagparser/signature.h>
#include <tagparser/mediafileinfo.h>
#include <tagparser/abstractcontainer.h>
#include <tagparser/matroska/matroskacontainer.h>
#include <tagparser/matroska/matroskaeditionentry.h>
#include <tagparser/mp4/mp4container.h>
#include <tagparser/abstracttrack.h>
#include <tagparser/abstractattachment.h>
#include <tagparser/notification.h>
#include <c++utilities/chrono/datetime.h>
#include <c++utilities/conversion/stringconversion.h>
#if defined(GUI_QTWIDGETS)
# include <QApplication>
# include <QStyle>
# include <QIcon>
#elif defined(GUI_QTQUICK)
# include <QGuiApplication>
# include <QIcon>
#elif !defined(GUI_NONE)
# define GUI_NONE
#endif
#include <QStringBuilder>
using namespace std;
using namespace ChronoUtilities;
using namespace ConversionUtilities;
using namespace Media;
namespace QtGui {
/*!
* \cond
*/
// helper functions and methods
QStandardItem *defaultItem(const QString &text)
{
auto *item = new QStandardItem(text);
item->setEditable(false);
return item;
}
class ItemHelper
{
public:
ItemHelper(QStandardItem *item) :
m_item(item)
{
m_item->setEditable(false);
}
void appendRow(const QString &label, const QString &text)
{
if(!text.isEmpty()) {
m_item->appendRow(QList<QStandardItem *>()
<< defaultItem(label)
<< defaultItem(text));
}
}
void appendRow(const QString &label, const char *text)
{
appendRow(label, QString::fromLocal8Bit(text));
}
void appendRow(const QString &label, const string &text)
{
if(!text.empty()) {
appendRow(label, text.data());
}
}
void appendRow(const QString &label, DateTime dateTime)
{
if(!dateTime.isNull()) {
appendRow(label, dateTime.toString());
}
}
void appendRow(const QString &label, TimeSpan timeSpan)
{
if(!timeSpan.isNull()) {
appendRow(label, timeSpan.toString(TimeSpanOutputFormat::WithMeasures));
}
}
void appendRow(const QString &label, uint64 number)
{
if(number) {
appendRow(label, QString::number(number));
}
}
void appendRow(const QString &label, const Margin &margin)
{
if(!margin.isNull()) {
appendRow(label, margin.toString());
}
}
void appendRow(const QString &label, const Size &size)
{
if(!size.isNull()) {
appendRow(label, size.toString());
}
}
private:
QStandardItem *m_item;
};
void addNotifications(Media::NotificationList *notifications, QStandardItem *parent)
{
Notification::sortByTime(*notifications);
for(Notification &notification : *notifications) {
QList<QStandardItem *> notificationRow;
notificationRow.reserve(3);
auto *firstItem = defaultItem(QString::fromLocal8Bit(notification.creationTime().toString().data()));
switch(notification.type()) {
case NotificationType::Critical:
firstItem->setIcon(FileInfoModel::errorIcon());
break;
case NotificationType::Warning:
firstItem->setIcon(FileInfoModel::warningIcon());
break;
case NotificationType::Information:
firstItem->setIcon(FileInfoModel::informationIcon());
break;
case NotificationType::Debug:
firstItem->setIcon(FileInfoModel::debugIcon());
break;
}
parent->appendRow(QList<QStandardItem *>()
<< firstItem
<< defaultItem(QString::fromLocal8Bit(notification.context().data()))
<< defaultItem(QString::fromLocal8Bit(notification.message().data())));
}
}
template<class ElementType, bool isAdditional = false> void addElementNode(ElementType *element, QStandardItem *parent)
{
while(element) {
if(element->isParsed()) {
auto *firstItem = defaultItem(QString::fromLatin1(element->idToString().data()));
parent->appendRow(QList<QStandardItem *>()
<< firstItem
<< defaultItem(QStringLiteral("offset: 0x") + QString::number(element->startOffset(), 16))
<< defaultItem(QStringLiteral("size: 0x") + QString::number(element->totalSize(), 16)));
if(element->firstChild()) {
addElementNode(element->firstChild(), firstItem);
}
element = element->nextSibling();
} else {
if(!isAdditional) {
auto *notAnalyzedItem = defaultItem(QStringLiteral("not analyzed"));
notAnalyzedItem->setForeground(QBrush(QColor(Qt::red)));
parent->appendRow(notAnalyzedItem);
}
break;
}
}
}
/*!
* \endcond
*/
/*!
* \class FileInfoModel
* \brief The FileInfoModel displays overall information from a Media::MediaFileInfo instance.
*
* The model assumes that the specified Media::MediaFileInfo instance has been parsed already.
* The model is not updated automatically when the state of the Media::MediaFileInfo changes.
* To update the model, just call setFileInfo() again.
*/
/*!
* \brief Constructs a new instance with the specified \a fileInfo which might be nullptr.
*/
FileInfoModel::FileInfoModel(Media::MediaFileInfo *fileInfo, QObject *parent) :
QStandardItemModel(parent)
{
setFileInfo(fileInfo);
}
QVariant FileInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(orientation) {
case Qt::Horizontal:
switch(role) {
case Qt::DisplayRole:
switch(section) {
case 0:
tr("Property");
break;
case 1:
tr("Value");
break;
default:
;
}
break;
}
break;
default:
;
}
return QVariant();
}
/*!
* \brief Returns the currently assigned Media::MediaFileInfo.
*/
const MediaFileInfo *FileInfoModel::fileInfo() const
{
return m_file;
}
/*!
* \brief Assigns a Media::MediaFileInfo.
* \remarks Causes updating the internal cache and resets the model.
*/
void FileInfoModel::setFileInfo(MediaFileInfo *fileInfo, Media::NotificationList *originalNotifications)
{
m_file = fileInfo;
m_originalNotifications = originalNotifications;
updateCache();
}
#if defined(GUI_QTWIDGETS)
const QIcon &FileInfoModel::informationIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation);
return icon;
}
const QIcon &FileInfoModel::warningIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
return icon;
}
const QIcon &FileInfoModel::errorIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
return icon;
}
const QIcon &FileInfoModel::debugIcon()
{
static const QIcon icon = QIcon(QStringLiteral("/images/bug"));
return icon;
}
/*!
* \brief Internally called to create to cache the items.
*/
void FileInfoModel::updateCache()
{
beginResetModel();
clear();
if(m_file) {
// get container
AbstractContainer *container = m_file->container();
// get root item from model
QStandardItem *rootItem = invisibleRootItem();
ItemHelper rootHelper(rootItem);
// add general information
rootHelper.appendRow(tr("Path"), m_file->path());
rootHelper.appendRow(tr("Size"), dataSizeToString(m_file->size()));
const TimeSpan duration = m_file->duration();
if(!duration.isNull()) {
rootHelper.appendRow(tr("Duration"), duration);
rootHelper.appendRow(tr("Overall avg. bitrate"), bitrateToString(0.0078125 * m_file->size() / duration.totalSeconds()));
}
const char *const mimeType = m_file->mimeType();
if(*mimeType) {
rootHelper.appendRow(tr("Mime-type"), mimeType);
}
// 3 columns
setItem(0, 2, new QStandardItem);
int currentRow;
// add container item (last top-level-item which is always present)
auto *containerItem = defaultItem(tr("Container"));
ItemHelper containerHelper(containerItem);
setItem(currentRow = rowCount(), containerItem);
// -> add container name
QString containerName;
const char *const subversion = m_file->containerFormatSubversion();
if(*subversion) {
containerName = QString::fromLocal8Bit(m_file->containerFormatName()) % QChar(' ') % QString::fromLocal8Bit(m_file->containerFormatSubversion());
} else {
containerName = QString::fromLocal8Bit(m_file->containerFormatName());
}
setItem(currentRow, 1, defaultItem(containerName));
// container details
if(container) {
size_t segmentIndex = 0;
for(const auto &title : container->titles()) {
if(segmentIndex) {
containerHelper.appendRow(tr("Title (segment %1)").arg(++segmentIndex), title);
} else {
++segmentIndex;
containerHelper.appendRow(tr("Title"), title);
}
}
containerHelper.appendRow(tr("Version"), container->version());
containerHelper.appendRow(tr("Read version"), container->readVersion());
containerHelper.appendRow(tr("Document type"), container->documentType());
containerHelper.appendRow(tr("Document version"), container->doctypeVersion());
containerHelper.appendRow(tr("Document read version"), container->doctypeReadVersion());
}
containerHelper.appendRow(tr("Padding size"), m_file->paddingSize());
// tags
{
const auto tags = m_file->tags();
if(!tags.empty()) {
auto *tagsItem = defaultItem(tr("Tags"));
setItem(++currentRow, tagsItem);
setItem(currentRow, 1, defaultItem(tr("%1 tag(s) assigned", 0, tags.size()).arg(tags.size())));
for(const Tag *tag : tags) {
auto *tagItem = defaultItem(tag->typeName());
ItemHelper tagHelper(tagItem);
tagHelper.appendRow(tr("Version"), tag->version());
tagHelper.appendRow(tr("Target level"), tag->target().toString());
tagHelper.appendRow(tr("Size"), dataSizeToString(tag->size()));
tagHelper.appendRow(tr("Field count"), tag->fieldCount());
tagsItem->appendRow(tagItem);
}
}
}
// tracks
{
const auto tracks = m_file->tracks();
if(!tracks.empty()) {
auto *tracksItem = defaultItem(tr("Tracks"));
setItem(++currentRow, tracksItem);
setItem(currentRow, 1, defaultItem(tr("%1 track(s) contained", 0, tracks.size()).arg(tracks.size())));
size_t number = 0;
for(const AbstractTrack *track : tracks) {
auto *trackItem = defaultItem(tr("Track #%1").arg(++number));
ItemHelper trackHelper(trackItem);
trackHelper.appendRow(tr("ID"), track->id());
trackHelper.appendRow(tr("Number"), track->trackNumber());
trackHelper.appendRow(tr("Name"), track->name());
trackHelper.appendRow(tr("Type"), track->mediaTypeName());
const char *fmtName = track->formatName(), *fmtAbbr = track->formatAbbreviation();
trackHelper.appendRow(tr("Format"), fmtName);
if(strcmp(fmtName, fmtAbbr)) { // format name and abbreviation differ
trackHelper.appendRow(tr("Abbreviation"), fmtAbbr);
}
if(track->version()) {
switch(track->format().general) {
case GeneralMediaFormat::Mpeg4Video:
case GeneralMediaFormat::Avc:
trackHelper.appendRow(tr("Level"), QChar('L') + QString::number(track->version()));
break;
default:
trackHelper.appendRow(tr("Version"), QString::number(track->version()));
}
}
fmtName = track->format().extensionName();
trackHelper.appendRow(tr("Extension"), fmtName);
trackHelper.appendRow(tr("Format/codec ID"), track->formatId());
trackHelper.appendRow(tr("Size"), track->size());
trackHelper.appendRow(tr("Duration"), track->duration());
trackHelper.appendRow(tr("Avg. bitrate"), bitrateToString(track->bitrate()));
trackHelper.appendRow(tr("Max. bitrate"), bitrateToString(track->maxBitrate()));
trackHelper.appendRow(tr("Creation time"), track->creationTime());
trackHelper.appendRow(tr("Modification time"), track->modificationTime());
trackHelper.appendRow(tr("Language"), track->language());
trackHelper.appendRow(tr("Compressor name"), track->compressorName());
if(track->samplingFrequency()) {
trackHelper.appendRow(tr("Sampling frequency"), track->extensionSamplingFrequency()
? QString::number(track->extensionSamplingFrequency()) % QStringLiteral(" Hz / ") % QString::number(track->samplingFrequency()) % QStringLiteral(" Hz")
: QString::number(track->samplingFrequency()) + QStringLiteral(" Hz"));
}
trackHelper.appendRow(tr("Sample count"), track->sampleCount());
trackHelper.appendRow(tr("Bits per sample"), track->bitsPerSample());
trackHelper.appendRow(tr("Quality"), track->quality());
trackHelper.appendRow(tr("Pixel size"), track->pixelSize());
trackHelper.appendRow(tr("Display size"), track->displaySize());
if(track->pixelAspectRatio().isValid()) {
trackHelper.appendRow(tr("Pixel Aspect Ratio"), QString::number(track->pixelAspectRatio().numerator) % QStringLiteral(" : ") % QString::number(track->pixelAspectRatio().denominator));
}
trackHelper.appendRow(tr("Cropping"), track->cropping());
trackHelper.appendRow(tr("Resolution"), track->resolution());
if(track->channelConfigString()) {
trackHelper.appendRow(tr("Channel config"), track->extensionChannelConfigString()
? QString::fromLocal8Bit(track->extensionChannelConfigString()) % QStringLiteral(" / ") % QString::fromLocal8Bit(track->channelConfigString())
: QString::fromLocal8Bit(track->channelConfigString()));
} else {
trackHelper.appendRow(tr("Channel count"), track->channelCount());
}
trackHelper.appendRow(tr("Bit depth"), track->depth());
trackHelper.appendRow(tr("Frames per second"), track->fps());
trackHelper.appendRow(tr("Chroma format"), track->chromaFormat());
QStringList labels;
if(track->isInterlaced()) {
labels << tr("interlaced");
}
if(!track->isEnabled()) {
labels << tr("disabled");
}
if(track->isDefault()) {
labels << tr("default");
}
if(track->isForced()) {
labels << tr("forced");
}
if(track->hasLacing()) {
labels << tr("has lacing");
}
if(track->isEncrypted()) {
labels << tr("encrypted");
}
if(!labels.isEmpty()) {
trackHelper.appendRow(tr("Labeled as"), labels.join(QStringLiteral(", ")));
}
tracksItem->appendRow(trackItem);
}
}
}
// attachments
{
const auto attachments = m_file->attachments();
if(!attachments.empty()) {
auto *attachmentsItem = defaultItem(tr("Attachments"));
setItem(++currentRow, attachmentsItem);
setItem(currentRow, 1, defaultItem(tr("%1 attachment(s) present", 0, attachments.size()).arg(attachments.size())));
size_t number = 0;
for(const AbstractAttachment *attachment : attachments) {
auto *attachmentItem = defaultItem(tr("Attachment #%1").arg(++number));
ItemHelper attachHelper(attachmentItem);
attachHelper.appendRow(tr("ID"), attachment->id());
attachHelper.appendRow(tr("Name"), attachment->name());
attachHelper.appendRow(tr("Size"), dataSizeToString(attachment->data()->size()));
attachHelper.appendRow(tr("Mime-type"), attachment->mimeType());
attachHelper.appendRow(tr("Description"), attachment->description());
attachmentsItem->appendRow(attachmentItem);
}
}
}
// chapters/editions
{
size_t number = 0;
function<void(const AbstractChapter *, QStandardItem *)> addChapter;
addChapter = [&addChapter, &number] (const AbstractChapter *chapter, QStandardItem *parent) {
auto *chapterItem = defaultItem(tr("Chapter #%1").arg(++number));
ItemHelper chapterHelper(chapterItem);
chapterHelper.appendRow(tr("ID"), chapter->id());
for(const LocaleAwareString &name : chapter->names()) {
static const string delim(", ");
const string locale = joinStrings(initializer_list<string>{joinStrings(name.languages(), delim, true), joinStrings(name.countries(), delim, true)}, delim, true);
chapterHelper.appendRow(tr("Name (%1)").arg(QString::fromLocal8Bit(locale.data())), name);
}
chapterHelper.appendRow(tr("Start time"), chapter->startTime());
chapterHelper.appendRow(tr("End time"), chapter->endTime());
QStringList labels;
if(chapter->isHidden()) {
labels << tr("hidden");
}
if(!chapter->isEnabled()) {
labels << tr("disabled");
}
if(!labels.empty()) {
chapterHelper.appendRow(tr("Labeled as"), labels.join(QStringLiteral(", ")));
}
if(!chapter->tracks().empty()) {
QStringList trackIds;
for(const uint64 id : chapter->tracks()) {
trackIds << QString::number(id);
}
chapterHelper.appendRow(tr("Tracks"), trackIds.join(QStringLiteral(", ")));
}
for(size_t i = 0, nestedChapters = chapter->nestedChapterCount(); i < nestedChapters; ++i) {
addChapter(chapter->nestedChapter(i), chapterItem);
}
parent->appendRow(chapterItem);
};
if(m_file->containerFormat() == ContainerFormat::Matroska) {
const auto &editionEntries = static_cast<const MatroskaContainer *>(container)->editionEntires();
if(!editionEntries.empty()) {
auto *editionsItem = defaultItem(tr("Editions"));
setItem(++currentRow, editionsItem);
setItem(currentRow, 1, defaultItem(tr("%1 edition(s) present", 0, editionEntries.size()).arg(editionEntries.size())));
size_t editionNumber = 0;
for(const auto &edition : editionEntries) {
auto *editionItem = defaultItem(tr("Edition #%1").arg(++editionNumber));
ItemHelper editionHelper(editionItem);
editionHelper.appendRow(tr("ID"), edition->id());
QStringList labels;
if(edition->isHidden()) {
labels << tr("hidden");
}
if(edition->isDefault()) {
labels << tr("default");
}
if(edition->isOrdered()) {
labels << tr("ordered");
}
if(!labels.isEmpty()) {
editionHelper.appendRow(tr("Labeled as"), labels.join(QStringLiteral(", ")));
}
for(const auto &chapter : edition->chapters()) {
addChapter(chapter.get(), editionItem);
}
editionsItem->appendRow(editionItem);
}
}
} else {
const auto chapters = m_file->chapters();
if(!chapters.empty()) {
auto *chaptersItem = defaultItem(tr("Chapters"));
setItem(++currentRow, chaptersItem);
setItem(currentRow, 1, defaultItem(tr("%1 chapter(s) present", 0, chapters.size()).arg(chapters.size())));
for(const AbstractChapter *chapter : chapters) {
addChapter(chapter, chaptersItem);
}
}
}
}
// structure
switch(m_file->containerFormat()) {
case ContainerFormat::Mp4:
case ContainerFormat::QuickTime:
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
case ContainerFormat::Ebml: {
auto *structureItem = defaultItem(tr("Structure"));
switch(m_file->containerFormat()) {
case ContainerFormat::Mp4:
case ContainerFormat::QuickTime:
addElementNode(static_cast<Mp4Container *>(container)->firstElement(), structureItem);
break;
case ContainerFormat::Matroska:
case ContainerFormat::Webm:
case ContainerFormat::Ebml:
addElementNode(static_cast<MatroskaContainer *>(container)->firstElement(), structureItem);
break;
default:
;
}
setItem(++currentRow, structureItem);
}
break;
default:
;
}
// notifications
auto currentNotifications = m_file->gatherRelatedNotifications();
if(!currentNotifications.empty()) {
auto *notificationsItem= defaultItem(m_originalNotifications ? tr("Notifications (reparsing after saving)") : tr("Notifications"));
addNotifications(&currentNotifications, notificationsItem);
setItem(++currentRow, notificationsItem);
}
if(m_originalNotifications && !m_originalNotifications->empty()) {
auto *notificationsItem = defaultItem(tr("Notifications"));
addNotifications(m_originalNotifications, notificationsItem);
setItem(++currentRow, notificationsItem);
}
}
endResetModel();
}
#endif
}

44
gui/fileinfomodel.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef FILEINFOMODEL_H
#define FILEINFOMODEL_H
#include <QStandardItemModel>
#include <list>
namespace Media {
class MediaFileInfo;
class Notification;
typedef std::list<Notification> NotificationList;
}
namespace QtGui {
class FileInfoModel : public QStandardItemModel
{
Q_OBJECT
public:
explicit FileInfoModel(Media::MediaFileInfo *fileInfo = nullptr, QObject *parent = nullptr);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
const Media::MediaFileInfo *fileInfo() const;
void setFileInfo(Media::MediaFileInfo *fileInfo, Media::NotificationList *originalNotifications = nullptr);
#if defined(GUI_QTWIDGETS)
static const QIcon &informationIcon();
static const QIcon &warningIcon();
static const QIcon &errorIcon();
static const QIcon &debugIcon();
#endif
private:
void updateCache();
private:
Media::MediaFileInfo *m_file;
Media::NotificationList *m_originalNotifications;
};
}
#endif // FILEINFOMODEL_H

View File

@ -4,10 +4,7 @@
#include "../application/settings.h"
// include configuration from separate header file when building with CMake
#ifndef APP_METADATA_AVAIL
# include "resources/config.h"
#endif
#include "resources/config.h"
#include <qtutilities/resources/qtconfigarguments.h>
#include <qtutilities/resources/resources.h>

View File

@ -6,9 +6,7 @@
#include "../application/settings.h"
#include "../misc/utility.h"
#ifdef TAGEDITOR_NO_WEBVIEW
# include "../misc/htmlinfo.h"
#endif
#include "../misc/htmlinfo.h"
#include "ui_mainwindow.h"
@ -322,6 +320,7 @@ void MainWindow::showSettingsDlg()
}
if(m_settingsDlg->exec() == QDialog::Accepted) {
applySettingsFromDialog();
m_ui->tagEditorWidget->applySettingsFromDialog();
}
}
@ -444,10 +443,10 @@ void MainWindow::saveFileInformation()
TryLocker<> locker(fileOperationMutex());
if(locker) {
if(fileInfo().isOpen()) {
const QByteArray &htmlData =
const QByteArray htmlData =
#ifndef TAGEDITOR_NO_WEBVIEW
!Settings::noWebView() ?
m_ui->tagEditorWidget->fileInfoHtml().size() :
!m_ui->tagEditorWidget->fileInfoHtml().isEmpty() ?
m_ui->tagEditorWidget->fileInfoHtml() :
#endif
HtmlInfo::generateInfo(fileInfo(), m_ui->tagEditorWidget->originalNotifications());
if(!htmlData.isEmpty()) {

View File

@ -132,7 +132,7 @@ QWidget *EditorTempOptionPage::setupWidget()
{
auto *widget = EditorTempOptionPageBase::setupWidget();
QObject::connect(ui()->selectPushButton, &QPushButton::clicked, std::bind(&EditorTempOptionPage::showDirectorySelection, this));
ui()->notificationLabel->setText(QApplication::tr("Currently this directory must be on the same partition as the files you want to edit."));
ui()->notificationLabel->setText(QCoreApplication::translate("QtGui::EditorTempOptionPage", "Currently this directory must be on the same partition as the files you want to edit."));
ui()->notificationLabel->setNotificationType(NotificationType::Information);
return widget;
}
@ -142,7 +142,7 @@ void EditorTempOptionPage::showDirectorySelection()
QFileDialog dlg(parentWindow());
dlg.setModal(true);
dlg.setFileMode(QFileDialog::DirectoryOnly);
dlg.setWindowTitle(QApplication::translate("QtGui::EditorTempOptionPage", "Select directory to store temporary files"));
dlg.setWindowTitle(QCoreApplication::translate("QtGui::EditorTempOptionPage", "Select directory to store temporary files"));
dlg.setDirectory(ui()->pathLineEdit->text());
if(dlg.exec() == QFileDialog::Accepted && dlg.selectedFiles().size() == 1) {
ui()->pathLineEdit->setText(dlg.selectedFiles().front());

View File

@ -3,21 +3,21 @@
#include "./previousvaluehandling.h"
#include <c++utilities/application/global.h>
#include <QList>
#include <QMap>
#include <QWidget>
#include <initializer_list>
QT_BEGIN_NAMESPACE
class QFormLayout;
class QVBoxLayout;
QT_END_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QFormLayout)
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
namespace Media {
class Tag;
class TagValue;
enum class KnownField : unsigned int;
DECLARE_ENUM(KnownField, unsigned int)
}
namespace QtGui {

View File

@ -3,6 +3,7 @@
#include "./tagedit.h"
#include "./attachmentsedit.h"
#include "./entertargetdialog.h"
#include "./fileinfomodel.h"
#include "../application/settings.h"
#include "../misc/htmlinfo.h"
@ -38,6 +39,7 @@
#include <QFileSystemWatcher>
#include <QMenu>
#include <QCheckBox>
#include <QTreeView>
#include <QtConcurrent>
#if defined(TAGEDITOR_NO_WEBVIEW)
#elif defined(TAGEDITOR_USE_WEBENGINE)
@ -79,6 +81,11 @@ enum LoadingResult : char
TagEditorWidget::TagEditorWidget(QWidget *parent) :
QWidget(parent),
m_ui(new Ui::TagEditorWidget),
#ifndef TAGEDITOR_NO_WEBVIEW
m_infoWebView(nullptr),
#endif
m_infoModel(nullptr),
m_infoTreeView(nullptr),
m_nextFileAfterSaving(false),
m_makingResultsAvailable(false),
m_abortClicked(false)
@ -444,6 +451,9 @@ void TagEditorWidget::updateFileStatusStatus()
m_infoWebView->setEnabled(opened);
}
#endif
if(m_infoTreeView) {
m_infoTreeView->setEnabled(opened);
}
// inform the main window about the file status change as well
emit fileStatusChange(opened, hasTag);
}
@ -542,14 +552,34 @@ void TagEditorWidget::initInfoView()
{
#ifndef TAGEDITOR_NO_WEBVIEW
if(!Settings::noWebView() && !m_infoWebView) {
if(m_infoTreeView) {
m_infoTreeView->deleteLater();
m_infoTreeView = nullptr;
}
if(m_infoModel) {
m_infoModel->deleteLater();
m_infoModel = nullptr;
}
m_infoWebView = new WEB_VIEW_PROVIDER(m_ui->tagSplitter);
m_infoWebView->setAcceptDrops(false);
m_infoWebView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_infoWebView, &QWidget::customContextMenuRequested, this, &TagEditorWidget::showInfoWebViewContextMenu);
m_ui->tagSplitter->addWidget(m_infoWebView);
} else if(Settings::noWebView() && m_infoWebView) {
m_infoWebView->deleteLater();
m_infoWebView = nullptr;
} else if(Settings::noWebView() && !m_infoTreeView) {
if(m_infoWebView) {
m_infoWebView->deleteLater();
m_infoWebView = nullptr;
}
#endif
if(!m_infoTreeView) {
m_infoTreeView = new QTreeView(this);
m_ui->tagSplitter->addWidget(m_infoTreeView);
}
if(!m_infoModel) {
m_infoModel = new FileInfoModel(m_fileInfo.isOpen() ? &m_fileInfo : nullptr, this);
m_infoTreeView->setModel(m_infoModel);
}
#ifndef TAGEDITOR_NO_WEBVIEW
}
#endif
}
@ -570,6 +600,9 @@ void TagEditorWidget::updateInfoView()
}
}
#endif
if(m_infoModel) {
m_infoModel->setFileInfo(m_fileInfo.isOpen() ? &m_fileInfo : nullptr); // resets the model
}
}
#ifndef TAGEDITOR_NO_WEBVIEW

View File

@ -22,6 +22,7 @@
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher)
QT_FORWARD_DECLARE_CLASS(QMenu)
QT_FORWARD_DECLARE_CLASS(QTreeView)
#ifndef TAGEDITOR_NO_WEBVIEW
QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER)
#endif
@ -37,6 +38,7 @@ class TagEditorWidget;
}
class TagEdit;
class FileInfoModel;
class TagEditorWidget : public QWidget
{
@ -73,6 +75,8 @@ public slots:
bool applyEntriesAndSaveChangings();
bool deleteAllTagsAndSave();
void closeFile();
// misc
void applySettingsFromDialog();
signals:
/*!
@ -104,7 +108,6 @@ private slots:
void showFile(char result);
void handleReturnPressed();
void handleKeepPreviousValuesActionTriggered(QAction *action);
void applySettingsFromDialog();
void addTag(const std::function<Media::Tag *(Media::MediaFileInfo &)> &createTag);
void removeTag(Media::Tag *tag);
void changeTarget(Media::Tag *tag);
@ -139,6 +142,8 @@ private:
#ifndef TAGEDITOR_NO_WEBVIEW
WEB_VIEW_PROVIDER *m_infoWebView;
#endif
FileInfoModel *m_infoModel;
QTreeView *m_infoTreeView;
// tag, file, directory management
QString m_currentPath;
QFileSystemWatcher *m_fileWatcher;

View File

@ -17,17 +17,17 @@
#include <c++utilities/conversion/stringconversion.h>
#if defined(GUI_QTWIDGETS)
#include <QApplication>
#include <QStyle>
# include <QApplication>
# include <QStyle>
#elif defined(GUI_QTQUICK)
#include <QGuiApplication>
# include <QGuiApplication>
#elif !defined(GUI_NONE)
#define GUI_NONE
# define GUI_NONE
#endif
#ifndef GUI_NONE
#include <QFont>
#include <QFontMetrics>
#include <QIcon>
# include <QFont>
# include <QFontMetrics>
# include <QIcon>
#endif
#include <QString>
#include <QStringBuilder>
@ -36,7 +36,7 @@
#include <QByteArray>
#include <QXmlStreamWriter>
#ifdef QT_DEBUG
#include <QFile>
# include <QFile>
#endif
#include <list>
@ -512,8 +512,7 @@ public:
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
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));
}
if(track->version()) {
@ -579,7 +578,7 @@ public:
if(!track->displaySize().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Display size"), qstr(track->displaySize().toString()));
}
if(!track->pixelAspectRatio().isValid()) {
if(track->pixelAspectRatio().isValid()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Pixel Aspect Ratio"), QString::number(track->pixelAspectRatio().numerator) % QStringLiteral(" : ") % QString::number(track->pixelAspectRatio().denominator));
}
if(!track->cropping().isNull()) {
@ -606,27 +605,27 @@ public:
if(track->chromaFormat()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Chroma format"), qstr(track->chromaFormat()));
}
list<string> labels;
QStringList labels;
if(track->isInterlaced()) {
labels.push_back("interlaced");
labels << QCoreApplication::translate("HtmlInfo", "interlaced");
}
if(!track->isEnabled()) {
labels.push_back("disabled");
labels << QCoreApplication::translate("HtmlInfo", "disabled");
}
if(track->isDefault()) {
labels.push_back("default");
labels << QCoreApplication::translate("HtmlInfo", "default");
}
if(track->isForced()) {
labels.push_back("forced");
labels << QCoreApplication::translate("HtmlInfo", "forced");
}
if(track->hasLacing()) {
labels.push_back("has lacing");
labels << QCoreApplication::translate("HtmlInfo", "has lacing");
}
if(track->isEncrypted()) {
labels.push_back("encrypted");
labels << QCoreApplication::translate("HtmlInfo", "encrypted");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
if(!labels.isEmpty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), labels.join(QStringLiteral(", ")));
}
rowMaker.endSubTab();
}
@ -662,26 +661,26 @@ public:
}
for(const LocaleAwareString &name : chapter.names()) {
static const string delim(", ");
string locale = joinStrings(initializer_list<string>{joinStrings(name.languages(), delim, true), joinStrings(name.countries(), delim, true)}, delim, true);
const string locale = joinStrings(initializer_list<string>{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) {
if(!chapter.startTime().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Start time"), qstr(chapter.startTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
if(chapter.endTime().totalTicks() > 0) {
if(!chapter.endTime().isNull()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "End time"), qstr(chapter.endTime().toString(TimeSpanOutputFormat::WithMeasures)));
}
list<string> labels;
QStringList labels;
if(chapter.isHidden()) {
labels.push_back("hidden");
labels << QCoreApplication::translate("HtmlInfo", "hidden");
}
if(!chapter.isEnabled()) {
labels.push_back("disabled");
labels << QCoreApplication::translate("HtmlInfo", "disabled");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
if(!labels.empty()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), labels.join(QStringLiteral(", ")));
}
if(chapter.tracks().size()) {
if(!chapter.tracks().empty()) {
QStringList trackIds;
for(uint64 id : chapter.tracks()) {
trackIds << QString::number(id);
@ -702,18 +701,18 @@ public:
if(edition.id()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "ID"), QString::number(edition.id()));
}
list<string> labels;
QStringList labels;
if(edition.isHidden()) {
labels.push_back("hidden");
labels << QCoreApplication::translate("HtmlInfo", "hidden");
}
if(edition.isDefault()) {
labels.push_back("default");
labels << QCoreApplication::translate("HtmlInfo", "default");
}
if(edition.isOrdered()) {
labels.push_back("ordered");
labels << QCoreApplication::translate("HtmlInfo", "ordered");
}
if(labels.size()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Labeled as"), qstr(joinStrings(labels, ", ")));
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, edition.chapters().size()).arg(edition.chapters().size()));
@ -792,7 +791,7 @@ public:
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_rowMaker.startRow(reparsing ? QCoreApplication::translate("HtmlInfo", "Notifications (reparsing after saving)") : 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"));
@ -850,22 +849,22 @@ public:
m_writer.writeStartElement(QStringLiteral("body"));
startVerticalTable();
// general
// general information
startTableSection(QStringLiteral("general"));
m_rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Path"), qstr(m_file.path()));
m_rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Size"), qstr(dataSizeToString(m_file.size(), true)));
TimeSpan duration = m_file.duration();
const TimeSpan duration = m_file.duration();
if(!duration.isNull()) {
m_rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Duration"), qstr(duration.toString(TimeSpanOutputFormat::WithMeasures)));
m_rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Overall avg. bitrate"), qstr(bitrateToString(0.0078125 * m_file.size() / duration.totalSeconds())));
}
const char *mimeType = m_file.mimeType();
const char *const mimeType = m_file.mimeType();
if(*mimeType) {
m_rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Mime-type"), qstr(mimeType));
}
m_rowMaker.startRow(QCoreApplication::translate("HtmlInfo", "Container"));
m_writer.writeCharacters(qstr(m_file.containerFormatName()));
const char *subversion = m_file.containerFormatSubversion();
const char *const subversion = m_file.containerFormatSubversion();
if(*subversion) {
mkSpace();
m_writer.writeCharacters(qstr(subversion));
@ -946,7 +945,7 @@ public:
}
// tracks
auto tracks = m_file.tracks();
const auto tracks = m_file.tracks();
if(!tracks.empty()) {
startTableSection();
const QString moreId(QStringLiteral("tracksMore"));
@ -990,11 +989,12 @@ public:
// chapters
if(container) {
if(m_file.containerFormat() == ContainerFormat::Matroska) {
if(size_t editionCount = static_cast<const MatroskaContainer *>(container)->editionEntires().size()) {
const auto &editionEntries = static_cast<const MatroskaContainer *>(container)->editionEntires();
if(!editionEntries.empty()) {
startTableSection();
const QString moreId(QStringLiteral("editionsMore"));
m_rowMaker.startRow(QCoreApplication::translate("HtmlInfo", "Editions/chapters"));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "file has %1 edition(s)", 0, editionCount).arg(editionCount));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "file has %1 edition(s)", 0, editionEntries.size()).arg(editionEntries.size()));
mkSpace();
mkDetailsLink(moreId, QCoreApplication::translate("HtmlInfo", "show details"));
m_rowMaker.endRow();
@ -1105,8 +1105,6 @@ private:
NotificationList &originalNotifications;
};
/*!
* \brief Generates technical information for the specified \a file.
*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff