mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-28 13:57:22 +00:00
parent
8badffee79
commit
1ac25bc3c5
161
src/mkvtoolnix-gui/forms/header_editor/attached_file_page.ui
Normal file
161
src/mkvtoolnix-gui/forms/header_editor/attached_file_page.ui
Normal file
@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>mtx::gui::HeaderEditor::AttachedFilePage</class>
|
||||
<widget class="QWidget" name="mtx::gui::HeaderEditor::AttachedFilePage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>546</width>
|
||||
<height>361</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="heading">
|
||||
<property name="text">
|
||||
<string>Attachment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="description"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="name"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Fi&le name:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>name</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="descriptionLabel">
|
||||
<property name="text">
|
||||
<string>&Description:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>description</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="size">
|
||||
<property name="text">
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="mimeType">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="sizeLabel">
|
||||
<property name="text">
|
||||
<string>Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="mimeTypeLabel">
|
||||
<property name="text">
|
||||
<string>&MIME type:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>mimeType</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&UID:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>uid</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="uid"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="reset">
|
||||
<property name="text">
|
||||
<string notr="true">&Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>name</tabstop>
|
||||
<tabstop>description</tabstop>
|
||||
<tabstop>mimeType</tabstop>
|
||||
<tabstop>uid</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
133
src/mkvtoolnix-gui/forms/header_editor/attachments_page.ui
Normal file
133
src/mkvtoolnix-gui/forms/header_editor/attachments_page.ui
Normal file
@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>mtx::gui::HeaderEditor::AttachmentsPage</class>
|
||||
<widget class="QWidget" name="mtx::gui::HeaderEditor::AttachmentsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>457</width>
|
||||
<height>472</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="heading">
|
||||
<property name="text">
|
||||
<string>Attachments</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>136</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="instructions">
|
||||
<property name="text">
|
||||
<string>You can add attachments by clicking on the button below, by right-clicking on a node in the tree and selecting "Add attachments" from the popup menu or by dragging & dropping files here or onto the tree.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addAttachments">
|
||||
<property name="text">
|
||||
<string>&Add attachments</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../qt_resources.qrc">
|
||||
<normaloff>:/icons/16x16/list-add.png</normaloff>:/icons/16x16/list-add.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>135</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../qt_resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
@ -85,7 +85,7 @@
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QTreeView" name="elements">
|
||||
<widget class="mtx::gui::Util::BasicTreeView" name="elements">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
@ -99,7 +99,10 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
@ -130,6 +133,11 @@
|
||||
<extends>QLabel</extends>
|
||||
<header>mkvtoolnix-gui/util/elide_label.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>mtx::gui::Util::BasicTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>mkvtoolnix-gui/util/basic_tree_view.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
229
src/mkvtoolnix-gui/header_editor/attached_file_page.cpp
Normal file
229
src/mkvtoolnix-gui/header_editor/attached_file_page.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QSaveFile>
|
||||
|
||||
#include <matroska/KaxAttached.h>
|
||||
|
||||
#include "common/ebml.h"
|
||||
#include "common/extern_data.h"
|
||||
#include "common/qt.h"
|
||||
#include "common/strings/formatting.h"
|
||||
#include "mkvtoolnix-gui/forms/header_editor/attached_file_page.h"
|
||||
#include "mkvtoolnix-gui/forms/header_editor/tab.h"
|
||||
#include "mkvtoolnix-gui/header_editor/attached_file_page.h"
|
||||
#include "mkvtoolnix-gui/util/file_dialog.h"
|
||||
#include "mkvtoolnix-gui/util/message_box.h"
|
||||
#include "mkvtoolnix-gui/util/settings.h"
|
||||
#include "mkvtoolnix-gui/util/widget.h"
|
||||
|
||||
namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
using namespace mtx::gui;
|
||||
|
||||
AttachedFilePage::AttachedFilePage(Tab &parent,
|
||||
PageBase &topLevelPage,
|
||||
KaxAttachedPtr const &attachment)
|
||||
: PageBase{parent, "todo"}
|
||||
, ui{new Ui::AttachedFilePage}
|
||||
, m_filesDDHandler{Util::FilesDragDropHandler::Mode::Remember}
|
||||
, m_topLevelPage(topLevelPage)
|
||||
, m_attachment{attachment}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->reset, &QPushButton::clicked, this, &AttachedFilePage::setControlsFromAttachment);
|
||||
}
|
||||
|
||||
AttachedFilePage::~AttachedFilePage() {
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::retranslateUi() {
|
||||
ui->retranslateUi(this);
|
||||
|
||||
ui->size->setText(formatSize());
|
||||
|
||||
Util::setToolTip(ui->name, Q("%1 %2").arg(QY("Other parts of the file (e.g. a subtitle track) may refer to this attachment via this name.")).arg(QY("The name must not be left empty.")));
|
||||
Util::setToolTip(ui->description, Q("%1 %2").arg(QY("An arbitrary description meant for the user.")).arg(QY("The description can be left empty.")));
|
||||
Util::setToolTip(ui->mimeType, Q("%1 %2").arg(QY("The MIME type determines which program can be used for handling its content.")).arg(QY("The MIME type must not be left empty.")));
|
||||
Util::setToolTip(ui->uid, Q("%1 %2").arg(QY("A unique, positive number unambiguously identifying the attachment within the Matroska file.")).arg(QY("The UID must not be left empty.")));
|
||||
Util::setToolTip(ui->reset, QY("Reset the attachment values on this page to how they're saved in the file."));
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::init() {
|
||||
for (auto &mimeType : mime_types)
|
||||
ui->mimeType->addItem(Q(mimeType.name), Q(mimeType.name));
|
||||
|
||||
retranslateUi();
|
||||
|
||||
setControlsFromAttachment();
|
||||
|
||||
m_parent.appendPage(this, m_topLevelPage.m_pageIdx);
|
||||
|
||||
m_topLevelPage.m_children << this;
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::setControlsFromAttachment() {
|
||||
auto mimeType = Q(FindChildValue<KaxMimeType>(*m_attachment));
|
||||
|
||||
ui->name->setText(Q(FindChildValue<KaxFileName>(*m_attachment)));
|
||||
ui->description->setText(Q(FindChildValue<KaxFileDescription>(*m_attachment)));
|
||||
ui->mimeType->setEditText(mimeType);
|
||||
ui->uid->setText(QString::number(FindChildValue<KaxFileUID>(*m_attachment)));
|
||||
|
||||
Util::setComboBoxTextByData(ui->mimeType, mimeType);
|
||||
|
||||
ui->size->setText(formatSize());
|
||||
|
||||
m_newFileContent.reset();
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::dragEnterEvent(QDragEnterEvent *event) {
|
||||
m_filesDDHandler.handle(event, false);
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::dropEvent(QDropEvent *event) {
|
||||
if (m_filesDDHandler.handle(event, true))
|
||||
emit filesDropped(m_filesDDHandler.fileNames());
|
||||
}
|
||||
|
||||
QString
|
||||
AttachedFilePage::title()
|
||||
const {
|
||||
return Q(FindChildValue<KaxFileName>(*m_attachment, to_wide(Y("<unnamed>"))));
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::setItems(QList<QStandardItem *> const &items)
|
||||
const {
|
||||
PageBase::setItems(items);
|
||||
|
||||
items.at(1)->setText(Q(FindChildValue<KaxMimeType>(*m_attachment)));
|
||||
items.at(3)->setText(Q(FindChildValue<KaxFileDescription>(*m_attachment)));
|
||||
items.at(4)->setText(QString::number(FindChildValue<KaxFileUID>(*m_attachment)));
|
||||
items.at(7)->setText(formatSize());
|
||||
}
|
||||
|
||||
QString
|
||||
AttachedFilePage::formatSize()
|
||||
const {
|
||||
if (m_newFileContent)
|
||||
return QNY("%1 byte (%2)", "%1 bytes (%2)", m_newFileContent->get_size()).arg(m_newFileContent->get_size()).arg(Q(format_file_size(m_newFileContent->get_size())));
|
||||
|
||||
auto content = FindChild<KaxFileData>(*m_attachment);
|
||||
if (content)
|
||||
return QNY("%1 byte (%2)", "%1 bytes (%2)", content->GetSize()).arg(content->GetSize()).arg(Q(format_file_size(content->GetSize())));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool
|
||||
AttachedFilePage::hasThisBeenModified()
|
||||
const {
|
||||
return m_newFileContent
|
||||
|| (Q(FindChildValue<KaxFileName>(*m_attachment)) != ui->name->text())
|
||||
|| (Q(FindChildValue<KaxFileDescription>(*m_attachment)) != ui->description->text())
|
||||
|| (Q(FindChildValue<KaxMimeType>(*m_attachment)) != ui->mimeType->currentText())
|
||||
|| (QString::number(FindChildValue<KaxFileUID>(*m_attachment)) != ui->uid->text());
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::modifyThis() {
|
||||
auto description = ui->description->text();
|
||||
|
||||
GetChild<KaxFileName>(*m_attachment).SetValueUTF8(to_utf8(ui->name->text()));
|
||||
GetChild<KaxMimeType>(*m_attachment).SetValue(to_utf8(ui->mimeType->currentText()));
|
||||
GetChild<KaxFileUID>(*m_attachment).SetValue(ui->uid->text().toULongLong());
|
||||
|
||||
if (description.isEmpty())
|
||||
DeleteChildren<KaxFileDescription>(*m_attachment);
|
||||
else
|
||||
GetChild<KaxFileDescription>(*m_attachment).SetValueUTF8(to_utf8(description));
|
||||
|
||||
if (m_newFileContent)
|
||||
GetChild<KaxFileData>(*m_attachment).CopyBuffer(m_newFileContent->get_buffer(), m_newFileContent->get_size());
|
||||
}
|
||||
|
||||
bool
|
||||
AttachedFilePage::validateThis()
|
||||
const {
|
||||
auto ok = false;
|
||||
|
||||
ui->uid->text().toULongLong(&ok);
|
||||
|
||||
ok = ok
|
||||
&& !ui->name->text().isEmpty()
|
||||
&& !ui->mimeType->currentText().isEmpty();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::saveContent() {
|
||||
auto content = FindChild<KaxFileData>(*m_attachment);
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
auto &settings = Util::Settings::get();
|
||||
auto fileName = Util::getSaveFileName(this, QY("Save attachment"), Q("%1/%2").arg(Util::dirPath(settings.m_lastOutputDir.path())).arg(ui->name->text()), QY("All files") + Q(" (*)"));
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
settings.m_lastOutputDir = QFileInfo{ fileName }.absoluteDir();
|
||||
settings.save();
|
||||
|
||||
QSaveFile file{fileName};
|
||||
auto ok = true;
|
||||
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(reinterpret_cast<char *>(content->GetBuffer()), content->GetSize());
|
||||
ok = file.commit();
|
||||
} else
|
||||
ok = false;
|
||||
|
||||
if (!ok)
|
||||
Util::MessageBox::critical(this)->title(QY("Saving failed")).text(QY("Creating the file failed. Check to make sure you have permission to write to that directory and that the drive is not full.")).exec();
|
||||
}
|
||||
|
||||
void
|
||||
AttachedFilePage::replaceContent(bool deriveNameAndMimeType) {
|
||||
auto &settings = Util::Settings::get();
|
||||
auto fileName = Util::getOpenFileName(this, QY("Replace attachment"), Util::dirPath(settings.m_lastOpenDir.path()), QY("All files") + Q(" (*)"));
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
auto fileInfo = QFileInfo{ fileName };
|
||||
settings.m_lastOpenDir = fileInfo.absoluteDir();
|
||||
settings.save();
|
||||
|
||||
QFile file{fileName};
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
Util::MessageBox::critical(this)->title(QY("Reading failed")).text(QY("The file you tried to open (%1) could not be read successfully.").arg(fileName)).exec();
|
||||
return;
|
||||
}
|
||||
|
||||
auto newContent = file.readAll();
|
||||
m_newFileContent = memory_c::clone(newContent.data(), newContent.count());
|
||||
|
||||
ui->size->setText(formatSize());
|
||||
|
||||
if (!deriveNameAndMimeType)
|
||||
return;
|
||||
|
||||
auto mimeType = Q(guess_mime_type(to_utf8(fileName), true));
|
||||
|
||||
ui->name->setText(fileInfo.fileName());
|
||||
ui->mimeType->setEditText(mimeType);
|
||||
Util::setComboBoxTextByData(ui->mimeType, mimeType);
|
||||
}
|
||||
|
||||
}}}
|
62
src/mkvtoolnix-gui/header_editor/attached_file_page.h
Normal file
62
src/mkvtoolnix-gui/header_editor/attached_file_page.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHED_FILE_PAGE_H
|
||||
#define MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHED_FILE_PAGE_H
|
||||
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include "mkvtoolnix-gui/header_editor/page_base.h"
|
||||
#include "mkvtoolnix-gui/util/files_drag_drop_handler.h"
|
||||
|
||||
namespace libmatroska {
|
||||
class KaxAttached;
|
||||
};
|
||||
|
||||
namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
namespace Ui {
|
||||
class AttachedFilePage;
|
||||
}
|
||||
|
||||
using KaxAttachedPtr = std::shared_ptr<KaxAttached>;
|
||||
|
||||
class AttachedFilePage: public PageBase {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
std::unique_ptr<Ui::AttachedFilePage> ui;
|
||||
mtx::gui::Util::FilesDragDropHandler m_filesDDHandler;
|
||||
PageBase &m_topLevelPage;
|
||||
KaxAttachedPtr m_attachment;
|
||||
memory_cptr m_newFileContent;
|
||||
|
||||
public:
|
||||
AttachedFilePage(Tab &parent, PageBase &topLevelPage, KaxAttachedPtr const &attachment);
|
||||
virtual ~AttachedFilePage();
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual void retranslateUi() override;
|
||||
|
||||
virtual QString title() const override;
|
||||
virtual void setItems(QList<QStandardItem *> const &items) const override;
|
||||
|
||||
virtual bool hasThisBeenModified() const;
|
||||
virtual void modifyThis();
|
||||
virtual bool validateThis() const;
|
||||
|
||||
virtual void setControlsFromAttachment();
|
||||
virtual void saveContent();
|
||||
virtual void replaceContent(bool deriveNameAndMimeType);
|
||||
|
||||
signals:
|
||||
void filesDropped(QStringList const &fileNames);
|
||||
|
||||
protected:
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
virtual void dropEvent(QDropEvent *event) override;
|
||||
|
||||
virtual QString formatSize() const;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif // MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHED_FILE_PAGE_H
|
79
src/mkvtoolnix-gui/header_editor/attachments_page.cpp
Normal file
79
src/mkvtoolnix-gui/header_editor/attachments_page.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDropEvent>
|
||||
|
||||
#include <matroska/KaxAttached.h>
|
||||
|
||||
#include "common/qt.h"
|
||||
#include "mkvtoolnix-gui/forms/header_editor/attachments_page.h"
|
||||
#include "mkvtoolnix-gui/forms/header_editor/tab.h"
|
||||
#include "mkvtoolnix-gui/header_editor/attached_file_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/attachments_page.h"
|
||||
|
||||
namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
using namespace mtx::gui;
|
||||
|
||||
AttachmentsPage::AttachmentsPage(Tab &parent,
|
||||
KaxAttachedList const &attachments)
|
||||
: TopLevelPage{parent, YT("Attachments"), true}
|
||||
, ui{new Ui::AttachmentsPage}
|
||||
, m_filesDDHandler{Util::FilesDragDropHandler::Mode::Remember}
|
||||
, m_initialAttachments{attachments}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->addAttachments, &QPushButton::clicked, &parent, &Tab::selectAttachmentsAndAdd);
|
||||
connect(this, &AttachmentsPage::filesDropped, &parent, &Tab::addAttachments);
|
||||
}
|
||||
|
||||
AttachmentsPage::~AttachmentsPage() {
|
||||
}
|
||||
|
||||
void
|
||||
AttachmentsPage::init() {
|
||||
TopLevelPage::init();
|
||||
|
||||
for (auto const &attachment : m_initialAttachments)
|
||||
m_parent.addAttachment(attachment);
|
||||
}
|
||||
|
||||
bool
|
||||
AttachmentsPage::hasThisBeenModified()
|
||||
const {
|
||||
auto numChildren = m_children.count();
|
||||
|
||||
if (m_initialAttachments.count() != numChildren)
|
||||
return true;
|
||||
|
||||
for (auto idx = 0; idx < numChildren; ++idx)
|
||||
if (m_initialAttachments[idx] != dynamic_cast<AttachedFilePage &>(*m_children[idx]).m_attachment)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
AttachmentsPage::retranslateUi() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void
|
||||
AttachmentsPage::dragEnterEvent(QDragEnterEvent *event) {
|
||||
m_filesDDHandler.handle(event, false);
|
||||
}
|
||||
|
||||
void
|
||||
AttachmentsPage::dropEvent(QDropEvent *event) {
|
||||
if (m_filesDDHandler.handle(event, true))
|
||||
emit filesDropped(m_filesDDHandler.fileNames());
|
||||
}
|
||||
|
||||
QString
|
||||
AttachmentsPage::internalIdentifier()
|
||||
const {
|
||||
return Q("attachments");
|
||||
}
|
||||
|
||||
}}}
|
51
src/mkvtoolnix-gui/header_editor/attachments_page.h
Normal file
51
src/mkvtoolnix-gui/header_editor/attachments_page.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHMENTS_PAGE_H
|
||||
#define MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHMENTS_PAGE_H
|
||||
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include "mkvtoolnix-gui/header_editor/top_level_page.h"
|
||||
#include "mkvtoolnix-gui/util/files_drag_drop_handler.h"
|
||||
|
||||
namespace libmatroska {
|
||||
class KaxAttached;
|
||||
};
|
||||
|
||||
namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
namespace Ui {
|
||||
class AttachmentsPage;
|
||||
}
|
||||
|
||||
using KaxAttachedPtr = std::shared_ptr<KaxAttached>;
|
||||
using KaxAttachedList = QList< std::shared_ptr<KaxAttached> >;
|
||||
|
||||
class AttachmentsPage: public TopLevelPage {
|
||||
Q_OBJECT;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Ui::AttachmentsPage> ui;
|
||||
mtx::gui::Util::FilesDragDropHandler m_filesDDHandler;
|
||||
KaxAttachedList m_initialAttachments;
|
||||
|
||||
public:
|
||||
AttachmentsPage(Tab &parent, KaxAttachedList const &attachments);
|
||||
virtual ~AttachmentsPage();
|
||||
|
||||
virtual void init() override;
|
||||
virtual bool hasThisBeenModified() const override;
|
||||
virtual QString internalIdentifier() const override;
|
||||
|
||||
signals:
|
||||
void filesDropped(QStringList const &fileNames);
|
||||
|
||||
public slots:
|
||||
virtual void retranslateUi() override;
|
||||
|
||||
protected:
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
virtual void dropEvent(QDropEvent *event) override;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif // MTX_MKVTOOLNIX_GUI_HEADER_EDITOR_ATTACHMENTS_PAGE_H
|
@ -96,17 +96,25 @@ PageModel::itemsForIndex(QModelIndex const &idx) {
|
||||
return items;
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
PageModel::indexFromPage(PageBase *page)
|
||||
const {
|
||||
return Util::findIndex(*this, [this, page](QModelIndex const &idx) -> bool {
|
||||
return selectedPage(idx) == page;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
PageModel::retranslateUi() {
|
||||
Util::setDisplayableAndSymbolicColumnNames(*this, {
|
||||
{ QY("Type"), Q("type") },
|
||||
{ QY("Codec"), Q("codec") },
|
||||
{ QY("Language"), Q("language") },
|
||||
{ QY("Name"), Q("name") },
|
||||
{ QY("UID"), Q("uid") },
|
||||
{ QY("Default track"), Q("defaultTrackFlag") },
|
||||
{ QY("Forced track"), Q("forcedTrackFlag") },
|
||||
{ QY("Properties"), Q("properties") },
|
||||
{ QY("Type"), Q("type") },
|
||||
{ QY("Codec/MIME type"), Q("codec") },
|
||||
{ QY("Language"), Q("language") },
|
||||
{ QY("Name/Description"), Q("name") },
|
||||
{ QY("UID"), Q("uid") },
|
||||
{ QY("Default track"), Q("defaultTrackFlag") },
|
||||
{ QY("Forced track"), Q("forcedTrackFlag") },
|
||||
{ QY("Properties"), Q("properties") },
|
||||
});
|
||||
|
||||
horizontalHeaderItem(4)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
void retranslateUi();
|
||||
|
||||
QList<QStandardItem *> itemsForIndex(QModelIndex const &idx);
|
||||
QModelIndex indexFromPage(PageBase *page) const;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
@ -1,17 +1,25 @@
|
||||
#include "common/common_pch.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <matroska/KaxAttached.h>
|
||||
#include <matroska/KaxAttachments.h>
|
||||
#include <matroska/KaxInfoData.h>
|
||||
#include <matroska/KaxSemantic.h>
|
||||
|
||||
#include "common/construct.h"
|
||||
#include "common/ebml.h"
|
||||
#include "common/extern_data.h"
|
||||
#include "common/qt.h"
|
||||
#include "common/segmentinfo.h"
|
||||
#include "common/segment_tracks.h"
|
||||
#include "common/unique_numbers.h"
|
||||
#include "mkvtoolnix-gui/forms/header_editor/tab.h"
|
||||
#include "mkvtoolnix-gui/header_editor/ascii_string_value_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/attached_file_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/attachments_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/bit_value_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/bool_value_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/float_value_page.h"
|
||||
@ -23,6 +31,8 @@
|
||||
#include "mkvtoolnix-gui/header_editor/track_type_page.h"
|
||||
#include "mkvtoolnix-gui/header_editor/unsigned_integer_value_page.h"
|
||||
#include "mkvtoolnix-gui/main_window/main_window.h"
|
||||
#include "mkvtoolnix-gui/util/basic_tree_view.h"
|
||||
#include "mkvtoolnix-gui/util/file_dialog.h"
|
||||
#include "mkvtoolnix-gui/util/header_view_manager.h"
|
||||
#include "mkvtoolnix-gui/util/model.h"
|
||||
#include "mkvtoolnix-gui/util/message_box.h"
|
||||
@ -39,8 +49,14 @@ Tab::Tab(QWidget *parent,
|
||||
, ui{new Ui::Tab}
|
||||
, m_fileName{fileName}
|
||||
, m_model{new PageModel{this}}
|
||||
, m_treeContextMenu{new QMenu{this}}
|
||||
, m_expandAllAction{new QAction{this}}
|
||||
, m_collapseAllAction{new QAction{this}}
|
||||
, m_addAttachmentsAction{new QAction{this}}
|
||||
, m_removeAttachmentAction{new QAction{this}}
|
||||
, m_saveAttachmentContentAction{new QAction{this}}
|
||||
, m_replaceAttachmentContentAction{new QAction{this}}
|
||||
, m_replaceAttachmentContentSetValuesAction{new QAction{this}}
|
||||
{
|
||||
// Setup UI controls.
|
||||
ui->setupUi(this);
|
||||
@ -75,7 +91,7 @@ Tab::load() {
|
||||
auto expansionStatus = QHash<QString, bool>{};
|
||||
|
||||
for (auto const &page : m_model->topLevelPages()) {
|
||||
auto key = page == m_segmentinfoPage ? Q("segmentinfo") : QString::number(static_cast<TrackTypePage &>(*page).m_trackNumber);
|
||||
auto key = dynamic_cast<TopLevelPage &>(*page).internalIdentifier();
|
||||
expansionStatus[key] = ui->elements->isExpanded(page->m_pageIdx);
|
||||
}
|
||||
|
||||
@ -102,7 +118,7 @@ Tab::load() {
|
||||
m_analyzer->close_file();
|
||||
|
||||
for (auto const &page : m_model->topLevelPages()) {
|
||||
auto key = page == m_segmentinfoPage ? Q("segmentinfo") : QString::number(static_cast<TrackTypePage &>(*page).m_trackNumber);
|
||||
auto key = dynamic_cast<TopLevelPage &>(*page).internalIdentifier();
|
||||
ui->elements->setExpanded(page->m_pageIdx, expansionStatus[key]);
|
||||
}
|
||||
|
||||
@ -115,7 +131,8 @@ Tab::load() {
|
||||
if (-1 != selected2ndLevelRow)
|
||||
selectedIdx = m_model->index(selected2ndLevelRow, 0, selectedIdx);
|
||||
|
||||
ui->elements->selectionModel()->select(selectedIdx, QItemSelectionModel::ClearAndSelect);
|
||||
auto selection = QItemSelection{selectedIdx, selectedIdx.sibling(selectedIdx.row(), m_model->columnCount() - 1)};
|
||||
ui->elements->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
|
||||
selectionChanged(selectedIdx, QModelIndex{});
|
||||
}
|
||||
|
||||
@ -123,6 +140,7 @@ void
|
||||
Tab::save() {
|
||||
auto segmentinfoModified = false;
|
||||
auto tracksModified = false;
|
||||
auto attachmentsModified = false;
|
||||
|
||||
for (auto const &page : m_model->topLevelPages()) {
|
||||
if (!page->hasBeenModified())
|
||||
@ -130,11 +148,15 @@ Tab::save() {
|
||||
|
||||
if (page == m_segmentinfoPage)
|
||||
segmentinfoModified = true;
|
||||
|
||||
else if (page == m_attachmentsPage)
|
||||
attachmentsModified = true;
|
||||
|
||||
else
|
||||
tracksModified = true;
|
||||
}
|
||||
|
||||
if (!segmentinfoModified && !tracksModified) {
|
||||
if (!segmentinfoModified && !tracksModified && !attachmentsModified) {
|
||||
Util::MessageBox::information(this)->title(QY("File has not been modified")).text(QY("The header values have not been modified. There is nothing to save.")).exec();
|
||||
return;
|
||||
}
|
||||
@ -167,6 +189,21 @@ Tab::save() {
|
||||
QtKaxAnalyzer::displayUpdateElementResult(this, result, QY("Saving the modified track headers failed."));
|
||||
}
|
||||
|
||||
if (attachmentsModified) {
|
||||
auto attachments = std::make_shared<KaxAttachments>();
|
||||
|
||||
for (auto const &attachedFilePage : m_attachmentsPage->m_children)
|
||||
attachments->PushElement(*dynamic_cast<AttachedFilePage &>(*attachedFilePage).m_attachment.get());
|
||||
|
||||
auto result = attachments->ListSize() ? m_analyzer->update_element(attachments.get(), true)
|
||||
: m_analyzer->remove_elements(KaxAttachments::ClassInfos.GlobalId);
|
||||
|
||||
attachments->RemoveAll();
|
||||
|
||||
if (kax_analyzer_c::uer_success != result)
|
||||
QtKaxAnalyzer::displayUpdateElementResult(this, result, QY("Saving the modified attachments failed."));
|
||||
}
|
||||
|
||||
m_analyzer->close_file();
|
||||
|
||||
load();
|
||||
@ -183,15 +220,21 @@ Tab::setupUi() {
|
||||
ui->directory->setText(info.path());
|
||||
|
||||
ui->elements->setModel(m_model);
|
||||
ui->elements->addAction(m_expandAllAction);
|
||||
ui->elements->addAction(m_collapseAllAction);
|
||||
ui->elements->acceptDroppedFiles(true);
|
||||
|
||||
Util::HeaderViewManager::create(*ui->elements, "HeaderEditor::Elements");
|
||||
Util::preventScrollingWithoutFocus(this);
|
||||
|
||||
connect(ui->elements->selectionModel(), &QItemSelectionModel::currentChanged, this, &Tab::selectionChanged);
|
||||
connect(m_expandAllAction, &QAction::triggered, this, &Tab::expandAll);
|
||||
connect(m_collapseAllAction, &QAction::triggered, this, &Tab::collapseAll);
|
||||
connect(ui->elements, &Util::BasicTreeView::customContextMenuRequested, this, &Tab::showTreeContextMenu);
|
||||
connect(ui->elements, &Util::BasicTreeView::filesDropped, this, &Tab::addAttachments);
|
||||
connect(ui->elements->selectionModel(), &QItemSelectionModel::currentChanged, this, &Tab::selectionChanged);
|
||||
connect(m_expandAllAction, &QAction::triggered, this, &Tab::expandAll);
|
||||
connect(m_collapseAllAction, &QAction::triggered, this, &Tab::collapseAll);
|
||||
connect(m_addAttachmentsAction, &QAction::triggered, this, &Tab::selectAttachmentsAndAdd);
|
||||
connect(m_removeAttachmentAction, &QAction::triggered, this, &Tab::removeSelectedAttachment);
|
||||
connect(m_saveAttachmentContentAction, &QAction::triggered, this, &Tab::saveAttachmentContent);
|
||||
connect(m_replaceAttachmentContentAction, &QAction::triggered, [this]() { replaceAttachmentContent(false); });
|
||||
connect(m_replaceAttachmentContentSetValuesAction, &QAction::triggered, [this]() { replaceAttachmentContent(true); });
|
||||
}
|
||||
|
||||
void
|
||||
@ -207,6 +250,12 @@ Tab::model()
|
||||
return m_model;
|
||||
}
|
||||
|
||||
PageBase *
|
||||
Tab::currentlySelectedPage()
|
||||
const {
|
||||
return m_model->selectedPage(ui->elements->selectionModel()->currentIndex());
|
||||
}
|
||||
|
||||
void
|
||||
Tab::retranslateUi() {
|
||||
ui->fileNameLabel->setText(QY("File name:"));
|
||||
@ -214,6 +263,11 @@ Tab::retranslateUi() {
|
||||
|
||||
m_expandAllAction->setText(QY("&Expand all"));
|
||||
m_collapseAllAction->setText(QY("&Collapse all"));
|
||||
m_addAttachmentsAction->setText(QY("&Add attachments"));
|
||||
m_removeAttachmentAction->setText(QY("&Remove selected attachment"));
|
||||
m_saveAttachmentContentAction->setText(QY("Save attachment content to a &file"));
|
||||
m_replaceAttachmentContentAction->setText(QY("Replace attachment with a new &file"));
|
||||
m_replaceAttachmentContentSetValuesAction->setText(QY("Replace attachment with new a file and &derive name && MIME type from it"));
|
||||
|
||||
auto &pages = m_model->pages();
|
||||
for (auto const &page : pages)
|
||||
@ -233,6 +287,8 @@ Tab::populateTree() {
|
||||
m_analyzer->with_elements(KaxTracks::ClassInfos.GlobalId, [this](kax_analyzer_data_c const &data) {
|
||||
handleTracks(data);
|
||||
});
|
||||
|
||||
handleAttachments();
|
||||
}
|
||||
|
||||
void
|
||||
@ -288,8 +344,9 @@ Tab::handleSegmentInfo(kax_analyzer_data_c const &data) {
|
||||
if (!m_eSegmentInfo)
|
||||
return;
|
||||
|
||||
auto &info = static_cast<KaxInfo &>(*m_eSegmentInfo.get());
|
||||
auto &info = dynamic_cast<KaxInfo &>(*m_eSegmentInfo.get());
|
||||
auto page = new TopLevelPage{*this, YT("Segment information")};
|
||||
page->setInternalIdentifier("segmentInfo");
|
||||
page->init();
|
||||
|
||||
(new StringValuePage{*this, *page, info, KaxTitle::ClassInfos, YT("Title"), YT("The title for the whole movie.")})->init();
|
||||
@ -311,7 +368,7 @@ Tab::handleTracks(kax_analyzer_data_c const &data) {
|
||||
|
||||
auto trackIdxMkvmerge = 0u;
|
||||
|
||||
for (auto const &element : static_cast<EbmlMaster &>(*m_eTracks)) {
|
||||
for (auto const &element : dynamic_cast<EbmlMaster &>(*m_eTracks)) {
|
||||
auto kTrackEntry = dynamic_cast<KaxTrackEntry *>(element);
|
||||
if (!kTrackEntry)
|
||||
continue;
|
||||
@ -426,9 +483,34 @@ Tab::handleTracks(kax_analyzer_data_c const &data) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Tab::handleAttachments() {
|
||||
auto attachments = KaxAttachedList{};
|
||||
|
||||
m_analyzer->with_elements(KaxAttachments::ClassInfos.GlobalId, [this, &attachments](kax_analyzer_data_c const &data) {
|
||||
auto master = std::dynamic_pointer_cast<KaxAttachments>(m_analyzer->read_element(data));
|
||||
if (!master)
|
||||
return;
|
||||
|
||||
auto idx = 0u;
|
||||
while (idx < master->ListSize()) {
|
||||
auto attached = dynamic_cast<KaxAttached *>((*master)[idx]);
|
||||
if (attached) {
|
||||
attachments << KaxAttachedPtr{attached};
|
||||
master->Remove(idx);
|
||||
} else
|
||||
++idx;
|
||||
}
|
||||
});
|
||||
|
||||
m_attachmentsPage = new AttachmentsPage{*this, attachments};
|
||||
m_attachmentsPage->init();
|
||||
}
|
||||
|
||||
void
|
||||
Tab::validate() {
|
||||
auto pageIdx = m_model->validate();
|
||||
// TODO: Tab::validate: handle attachments
|
||||
|
||||
if (!pageIdx.isValid()) {
|
||||
Util::MessageBox::information(this)->title(QY("Header validation")).text(QY("All header values are OK.")).exec();
|
||||
@ -467,4 +549,121 @@ Tab::expandCollapseAll(bool expand) {
|
||||
ui->elements->setExpanded(page->m_pageIdx, expand);
|
||||
}
|
||||
|
||||
void
|
||||
Tab::showTreeContextMenu(QPoint const &pos) {
|
||||
auto selectedPage = currentlySelectedPage();
|
||||
auto isAttachmentsPage = !!dynamic_cast<AttachmentsPage *>(selectedPage);
|
||||
auto isAttachedFilePage = !!dynamic_cast<AttachedFilePage *>(selectedPage);
|
||||
auto isAttachments = isAttachmentsPage || isAttachedFilePage;
|
||||
auto actions = m_treeContextMenu->actions();
|
||||
|
||||
for (auto const &action : actions)
|
||||
if (!action->isSeparator())
|
||||
m_treeContextMenu->removeAction(action);
|
||||
|
||||
m_treeContextMenu->clear();
|
||||
|
||||
m_treeContextMenu->addAction(m_expandAllAction);
|
||||
m_treeContextMenu->addAction(m_collapseAllAction);
|
||||
m_treeContextMenu->addSeparator();
|
||||
m_treeContextMenu->addAction(m_addAttachmentsAction);
|
||||
|
||||
if (isAttachments) {
|
||||
m_treeContextMenu->addAction(m_removeAttachmentAction);
|
||||
m_treeContextMenu->addSeparator();
|
||||
m_treeContextMenu->addAction(m_saveAttachmentContentAction);
|
||||
m_treeContextMenu->addAction(m_replaceAttachmentContentAction);
|
||||
m_treeContextMenu->addAction(m_replaceAttachmentContentSetValuesAction);
|
||||
|
||||
m_removeAttachmentAction->setEnabled(isAttachedFilePage);
|
||||
m_saveAttachmentContentAction->setEnabled(isAttachedFilePage);
|
||||
m_replaceAttachmentContentAction->setEnabled(isAttachedFilePage);
|
||||
m_replaceAttachmentContentSetValuesAction->setEnabled(isAttachedFilePage);
|
||||
}
|
||||
|
||||
m_treeContextMenu->exec(ui->elements->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void
|
||||
Tab::selectAttachmentsAndAdd() {
|
||||
auto &settings = Util::Settings::get();
|
||||
auto fileNames = Util::getOpenFileNames(this, QY("Add attachments"), Util::dirPath(settings.lastOpenDirPath()), QY("All files") + Q(" (*)"));
|
||||
|
||||
if (fileNames.isEmpty())
|
||||
return;
|
||||
|
||||
settings.m_lastOpenDir = QFileInfo{fileNames[0]}.path();
|
||||
settings.save();
|
||||
|
||||
addAttachments(fileNames);
|
||||
}
|
||||
|
||||
void
|
||||
Tab::addAttachment(KaxAttachedPtr const &attachment) {
|
||||
if (!attachment)
|
||||
return;
|
||||
|
||||
auto page = new AttachedFilePage{*this, *m_attachmentsPage, attachment};
|
||||
page->init();
|
||||
}
|
||||
|
||||
void
|
||||
Tab::addAttachments(QStringList const &fileNames) {
|
||||
for (auto const &fileName : fileNames)
|
||||
addAttachment(createAttachmentFromFile(fileName));
|
||||
|
||||
ui->elements->setExpanded(m_attachmentsPage->m_pageIdx, true);
|
||||
}
|
||||
|
||||
void
|
||||
Tab::removeSelectedAttachment() {
|
||||
auto selectedPage = currentlySelectedPage();
|
||||
if (!selectedPage)
|
||||
return;
|
||||
|
||||
auto idx = m_model->indexFromPage(selectedPage);
|
||||
if (idx.isValid())
|
||||
m_model->removeRow(idx.row(), idx.parent());
|
||||
|
||||
m_attachmentsPage->m_children.removeAll(selectedPage);
|
||||
|
||||
delete selectedPage;
|
||||
}
|
||||
|
||||
KaxAttachedPtr
|
||||
Tab::createAttachmentFromFile(QString const &fileName) {
|
||||
QByteArray content;
|
||||
QFile file{fileName};
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
Util::MessageBox::critical(this)->title(QY("Reading failed")).text(QY("The file you tried to open (%1) could not be read successfully.").arg(fileName)).exec();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto mimeType = guess_mime_type(to_utf8(fileName), true);
|
||||
auto contentAsMem = std::make_shared<memory_c>(content.data(), content.count(), false);
|
||||
auto uid = create_unique_number(UNIQUE_ATTACHMENT_IDS);
|
||||
|
||||
return KaxAttachedPtr{
|
||||
mtx::construct::cons<KaxAttached>(new KaxFileName, to_wide(QFileInfo{fileName}.fileName()),
|
||||
new KaxMimeType, mimeType,
|
||||
new KaxFileUID, uid,
|
||||
new KaxFileData, contentAsMem)
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
Tab::saveAttachmentContent() {
|
||||
auto page = dynamic_cast<AttachedFilePage *>(currentlySelectedPage());
|
||||
if (page)
|
||||
page->saveContent();
|
||||
}
|
||||
|
||||
void
|
||||
Tab::replaceAttachmentContent(bool deriveNameAndMimeType) {
|
||||
auto page = dynamic_cast<AttachedFilePage *>(currentlySelectedPage());
|
||||
if (page)
|
||||
page->replaceContent(deriveNameAndMimeType);
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mkvtoolnix-gui/header_editor/page_model.h"
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
||||
namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
@ -16,6 +17,10 @@ namespace Ui {
|
||||
class Tab;
|
||||
}
|
||||
|
||||
using KaxAttachedPtr = std::shared_ptr<KaxAttached>;
|
||||
|
||||
class AttachmentsPage;
|
||||
|
||||
class Tab : public QWidget {
|
||||
Q_OBJECT;
|
||||
|
||||
@ -29,8 +34,10 @@ protected:
|
||||
|
||||
PageModel *m_model;
|
||||
PageBase *m_segmentinfoPage{};
|
||||
AttachmentsPage *m_attachmentsPage{};
|
||||
|
||||
QAction *m_expandAllAction, *m_collapseAllAction;
|
||||
QMenu *m_treeContextMenu;
|
||||
QAction *m_expandAllAction, *m_collapseAllAction, *m_addAttachmentsAction, *m_removeAttachmentAction, *m_saveAttachmentContentAction, *m_replaceAttachmentContentAction, *m_replaceAttachmentContentSetValuesAction;
|
||||
|
||||
std::shared_ptr<EbmlElement> m_eSegmentInfo, m_eTracks;
|
||||
|
||||
@ -46,26 +53,38 @@ public:
|
||||
virtual QString const &fileName() const;
|
||||
virtual QString title() const;
|
||||
virtual void validate();
|
||||
virtual void addAttachment(KaxAttachedPtr const &attachment);
|
||||
|
||||
signals:
|
||||
void removeThisTab();
|
||||
|
||||
public slots:
|
||||
virtual void showTreeContextMenu(QPoint const &pos);
|
||||
virtual void selectionChanged(QModelIndex const ¤t, QModelIndex const &previous);
|
||||
virtual void load();
|
||||
virtual void save();
|
||||
virtual void expandAll();
|
||||
virtual void collapseAll();
|
||||
virtual void selectAttachmentsAndAdd();
|
||||
virtual void addAttachments(QStringList const &fileNames);
|
||||
virtual void removeSelectedAttachment();
|
||||
virtual void saveAttachmentContent();
|
||||
virtual void replaceAttachmentContent(bool deriveNameAndMimeType);
|
||||
|
||||
protected:
|
||||
void setupUi();
|
||||
void handleSegmentInfo(kax_analyzer_data_c const &data);
|
||||
void handleTracks(kax_analyzer_data_c const &data);
|
||||
void handleAttachments();
|
||||
void populateTree();
|
||||
void resetData();
|
||||
void doModifications();
|
||||
void expandCollapseAll(bool expand);
|
||||
void reportValidationFailure(bool isCritical, QModelIndex const &pageIdx);
|
||||
|
||||
PageBase *currentlySelectedPage() const;
|
||||
|
||||
KaxAttachedPtr createAttachmentFromFile(QString const &fileName);
|
||||
};
|
||||
|
||||
}}}
|
||||
|
@ -21,4 +21,15 @@ TopLevelPage::init() {
|
||||
m_parent.appendPage(this);
|
||||
}
|
||||
|
||||
QString
|
||||
TopLevelPage::internalIdentifier()
|
||||
const {
|
||||
return m_internalIdentifier;
|
||||
}
|
||||
|
||||
void
|
||||
TopLevelPage::setInternalIdentifier(QString const &identifier) {
|
||||
m_internalIdentifier = identifier;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -9,11 +9,15 @@ namespace mtx { namespace gui { namespace HeaderEditor {
|
||||
|
||||
class TopLevelPage: public EmptyPage {
|
||||
Q_OBJECT;
|
||||
QString m_internalIdentifier;
|
||||
|
||||
public:
|
||||
TopLevelPage(Tab &parent, translatable_string_c const &title, bool customLayout = false);
|
||||
virtual ~TopLevelPage();
|
||||
|
||||
virtual void init();
|
||||
virtual QString internalIdentifier() const;
|
||||
virtual void setInternalIdentifier(QString const &identifier);
|
||||
};
|
||||
|
||||
}}}
|
||||
|
@ -135,4 +135,10 @@ TrackTypePage::summarizeProperties() {
|
||||
m_properties = properties.join(Q(", "));
|
||||
}
|
||||
|
||||
QString
|
||||
TrackTypePage::internalIdentifier()
|
||||
const {
|
||||
return Q("track %1").arg(m_trackIdxMkvmerge);
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -29,6 +29,8 @@ public:
|
||||
TrackTypePage(Tab &parent, EbmlMaster &master, uint64_t trackIdxMkvmerge);
|
||||
virtual ~TrackTypePage();
|
||||
|
||||
virtual QString internalIdentifier() const override;
|
||||
|
||||
protected:
|
||||
virtual void setItems(QList<QStandardItem *> const &items) const override;
|
||||
virtual void summarizeProperties();
|
||||
|
@ -40,7 +40,9 @@ FORMS += \
|
||||
forms/watch_jobs/tab.ui \
|
||||
forms/watch_jobs/tool.ui \
|
||||
forms/merge/executable_location_dialog.ui \
|
||||
forms/main_window/prefs_run_program_widget.ui
|
||||
forms/main_window/prefs_run_program_widget.ui \
|
||||
forms/header_editor/attachments_page.ui \
|
||||
forms/header_editor/attached_file_page.ui
|
||||
|
||||
RESOURCES += \
|
||||
qt_resources.qrc
|
||||
|
@ -128,4 +128,20 @@ walkTree(QAbstractItemModel &model,
|
||||
walkTree(model, model.index(row, 0, idx), worker);
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
findIndex(QAbstractItemModel const &model,
|
||||
std::function<bool(QModelIndex const &)> const &predicate,
|
||||
QModelIndex const &idx) {
|
||||
if (idx.isValid() && predicate(idx))
|
||||
return idx;
|
||||
|
||||
for (auto row = 0, numRows = model.rowCount(idx); row < numRows; ++row) {
|
||||
auto result = findIndex(model, predicate, model.index(row, 0, idx));
|
||||
if (result.isValid())
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}}}
|
||||
|
@ -36,6 +36,7 @@ void withSelectedIndexes(QAbstractItemView *view, std::function<void(QModelIndex
|
||||
void selectRow(QAbstractItemView *view, int row, QModelIndex const &parentIdx = QModelIndex{});
|
||||
QModelIndex toTopLevelIdx(QModelIndex const &idx);
|
||||
void walkTree(QAbstractItemModel &model, QModelIndex const &idx, std::function<void(QModelIndex const &)> const &worker);
|
||||
QModelIndex findIndex(QAbstractItemModel const &model, std::function<bool(QModelIndex const &)> const &predicate, QModelIndex const &idx = QModelIndex{});
|
||||
|
||||
}}}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user