|
|
|
@ -22,8 +22,6 @@
|
|
|
|
|
#include <tagparser/ogg/oggcontainer.h>
|
|
|
|
|
|
|
|
|
|
#include <qtutilities/misc/dialogutils.h>
|
|
|
|
|
#include <qtutilities/misc/trylocker.h>
|
|
|
|
|
#include <qtutilities/misc/adoptlocker.h>
|
|
|
|
|
#include <qtutilities/widgets/clearlineedit.h>
|
|
|
|
|
|
|
|
|
|
#include <c++utilities/conversion/stringconversion.h>
|
|
|
|
@ -56,7 +54,6 @@ using namespace std::placeholders;
|
|
|
|
|
using namespace Utility;
|
|
|
|
|
using namespace Dialogs;
|
|
|
|
|
using namespace Widgets;
|
|
|
|
|
using namespace ThreadingUtils;
|
|
|
|
|
using namespace Media;
|
|
|
|
|
using namespace Models;
|
|
|
|
|
|
|
|
|
@ -90,7 +87,8 @@ TagEditorWidget::TagEditorWidget(QWidget *parent) :
|
|
|
|
|
m_infoTreeView(nullptr),
|
|
|
|
|
m_nextFileAfterSaving(false),
|
|
|
|
|
m_makingResultsAvailable(false),
|
|
|
|
|
m_abortClicked(false)
|
|
|
|
|
m_abortClicked(false),
|
|
|
|
|
m_fileOperationOngoing(false)
|
|
|
|
|
{
|
|
|
|
|
// setup UI
|
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
@ -723,82 +721,80 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
|
|
|
|
|
if(!forceRefresh && sameFile) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
TryLocker<> locker(fileOperationMutex());
|
|
|
|
|
if(locker) {
|
|
|
|
|
// clear previous results and status
|
|
|
|
|
m_tags.clear();
|
|
|
|
|
m_fileInfo.clearParsingResults();
|
|
|
|
|
m_fileInfo.invalidateStatus();
|
|
|
|
|
m_fileInfo.invalidateNotifications();
|
|
|
|
|
if(!sameFile) {
|
|
|
|
|
// close last file if possibly open
|
|
|
|
|
m_fileInfo.close();
|
|
|
|
|
// set path of file info
|
|
|
|
|
m_currentPath = path;
|
|
|
|
|
m_fileInfo.setSaveFilePath(string());
|
|
|
|
|
m_fileInfo.setPath(path.toLocal8Bit().data());
|
|
|
|
|
// update directory
|
|
|
|
|
m_lastDir = m_currentDir;
|
|
|
|
|
m_currentDir = QString::fromLocal8Bit(m_fileInfo.containingDirectory().c_str());
|
|
|
|
|
}
|
|
|
|
|
// update availability of making results
|
|
|
|
|
m_makingResultsAvailable &= sameFile;
|
|
|
|
|
if(!m_makingResultsAvailable) {
|
|
|
|
|
m_originalNotifications.clear();
|
|
|
|
|
}
|
|
|
|
|
// show filename
|
|
|
|
|
m_ui->fileNameLabel->setText(QString::fromLocal8Bit(m_fileInfo.fileName().c_str()));
|
|
|
|
|
// define function to parse the file
|
|
|
|
|
auto startThread = [this] {
|
|
|
|
|
m_fileOperationMutex.lock();
|
|
|
|
|
char result;
|
|
|
|
|
try { // credits for this nesting go to GCC regression 66145
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage(tr("Unable to load the selected file \"%1\" because the current process hasn't finished yet.").arg(path));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear previous results and status
|
|
|
|
|
m_tags.clear();
|
|
|
|
|
m_fileInfo.clearParsingResults();
|
|
|
|
|
m_fileInfo.invalidateStatus();
|
|
|
|
|
m_fileInfo.invalidateNotifications();
|
|
|
|
|
if(!sameFile) {
|
|
|
|
|
// close last file if possibly open
|
|
|
|
|
m_fileInfo.close();
|
|
|
|
|
// set path of file info
|
|
|
|
|
m_currentPath = path;
|
|
|
|
|
m_fileInfo.setSaveFilePath(string());
|
|
|
|
|
m_fileInfo.setPath(path.toLocal8Bit().data());
|
|
|
|
|
// update directory
|
|
|
|
|
m_lastDir = m_currentDir;
|
|
|
|
|
m_currentDir = QString::fromLocal8Bit(m_fileInfo.containingDirectory().c_str());
|
|
|
|
|
}
|
|
|
|
|
// update availability of making results
|
|
|
|
|
m_makingResultsAvailable &= sameFile;
|
|
|
|
|
if(!m_makingResultsAvailable) {
|
|
|
|
|
m_originalNotifications.clear();
|
|
|
|
|
}
|
|
|
|
|
// show filename
|
|
|
|
|
m_ui->fileNameLabel->setText(QString::fromLocal8Bit(m_fileInfo.fileName().c_str()));
|
|
|
|
|
// define function to parse the file
|
|
|
|
|
auto startThread = [this] {
|
|
|
|
|
char result;
|
|
|
|
|
try { // credits for this nesting go to GCC regression 66145
|
|
|
|
|
try {
|
|
|
|
|
try {
|
|
|
|
|
try {
|
|
|
|
|
// try to open with write access
|
|
|
|
|
m_fileInfo.reopen(false);
|
|
|
|
|
} catch(...) {
|
|
|
|
|
::IoUtilities::catchIoFailure();
|
|
|
|
|
// try to open read-only if opening with write access failed
|
|
|
|
|
m_fileInfo.reopen(true);
|
|
|
|
|
}
|
|
|
|
|
m_fileInfo.setForceFullParse(Settings::values().editor.forceFullParse);
|
|
|
|
|
m_fileInfo.parseEverything();
|
|
|
|
|
result = ParsingSuccessful;
|
|
|
|
|
} catch(const Failure &) {
|
|
|
|
|
// the file has been opened; parsing notifications will be shown in the info box
|
|
|
|
|
result = FatalParsingError;
|
|
|
|
|
// try to open with write access
|
|
|
|
|
m_fileInfo.reopen(false);
|
|
|
|
|
} catch(...) {
|
|
|
|
|
::IoUtilities::catchIoFailure();
|
|
|
|
|
// the file could not be opened because an IO error occured
|
|
|
|
|
m_fileInfo.close(); // ensure file is closed
|
|
|
|
|
result = IoError;
|
|
|
|
|
// try to open read-only if opening with write access failed
|
|
|
|
|
m_fileInfo.reopen(true);
|
|
|
|
|
}
|
|
|
|
|
} catch(const exception &e) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "parsing");
|
|
|
|
|
m_fileInfo.setForceFullParse(Settings::values().editor.forceFullParse);
|
|
|
|
|
m_fileInfo.parseEverything();
|
|
|
|
|
result = ParsingSuccessful;
|
|
|
|
|
} catch(const Failure &) {
|
|
|
|
|
// the file has been opened; parsing notifications will be shown in the info box
|
|
|
|
|
result = FatalParsingError;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "parsing");
|
|
|
|
|
result = FatalParsingError;
|
|
|
|
|
::IoUtilities::catchIoFailure();
|
|
|
|
|
// the file could not be opened because an IO error occured
|
|
|
|
|
m_fileInfo.close(); // ensure file is closed
|
|
|
|
|
result = IoError;
|
|
|
|
|
}
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
QMetaObject::invokeMethod(this, "showFile", Qt::QueuedConnection, Q_ARG(char, result));
|
|
|
|
|
// showFile() will unlock the mutex!
|
|
|
|
|
};
|
|
|
|
|
} catch(const exception &e) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "parsing");
|
|
|
|
|
result = FatalParsingError;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "parsing");
|
|
|
|
|
result = FatalParsingError;
|
|
|
|
|
}
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
// perform the operation concurrently
|
|
|
|
|
QtConcurrent::run(startThread);
|
|
|
|
|
// inform user
|
|
|
|
|
static const QString statusMsg(tr("The file is beeing parsed ..."));
|
|
|
|
|
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
m_ui->parsingNotificationWidget->setText(statusMsg);
|
|
|
|
|
m_ui->parsingNotificationWidget->setVisible(true); // ensure widget is visible!
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
emit statusMessage(tr("Unable to load the selected file \"%1\" because the current process hasn't finished yet.").arg(path));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
QMetaObject::invokeMethod(this, "showFile", Qt::QueuedConnection, Q_ARG(char, result));
|
|
|
|
|
};
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
m_fileOperationOngoing = true;
|
|
|
|
|
// perform the operation concurrently
|
|
|
|
|
QtConcurrent::run(startThread);
|
|
|
|
|
// inform user
|
|
|
|
|
static const QString statusMsg(tr("The file is beeing parsed ..."));
|
|
|
|
|
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
m_ui->parsingNotificationWidget->setText(statusMsg);
|
|
|
|
|
m_ui->parsingNotificationWidget->setVisible(true); // ensure widget is visible!
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -806,17 +802,13 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
|
|
|
|
|
*/
|
|
|
|
|
bool TagEditorWidget::reparseFile()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
if(!m_fileInfo.isOpen() || m_currentPath.isEmpty()) {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("Currently is not file opened."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
emit statusMessage(tr("Unable to reload the file because the current process hasn't finished yet."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage(tr("Unable to reload the file because the current process hasn't finished yet."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!m_fileInfo.isOpen() || m_currentPath.isEmpty()) {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("Currently is not file opened."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return startParsing(m_currentPath, true);
|
|
|
|
|
}
|
|
|
|
@ -826,11 +818,10 @@ bool TagEditorWidget::reparseFile()
|
|
|
|
|
* This private slot is invoked from the thread which performed the
|
|
|
|
|
* parsing operation using Qt::QueuedConnection.
|
|
|
|
|
* \param result Specifies whether the file could be load sucessfully.
|
|
|
|
|
* \remarks Expects m_fileOperationMutex to be locked!
|
|
|
|
|
*/
|
|
|
|
|
void TagEditorWidget::showFile(char result)
|
|
|
|
|
{
|
|
|
|
|
AdoptLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
m_fileOperationOngoing = false;
|
|
|
|
|
if(result == IoError) {
|
|
|
|
|
// update status
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
@ -941,43 +932,39 @@ void TagEditorWidget::saveAndShowNextFile()
|
|
|
|
|
*/
|
|
|
|
|
bool TagEditorWidget::applyEntriesAndSaveChangings()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Information);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
|
|
|
|
|
m_ui->makingNotificationWidget->setHidden(false);
|
|
|
|
|
m_makingResultsAvailable = true;
|
|
|
|
|
if(m_fileInfo.isOpen()) {
|
|
|
|
|
// apply titles
|
|
|
|
|
if(AbstractContainer *container = m_fileInfo.container()) {
|
|
|
|
|
if(container->supportsTitle()) {
|
|
|
|
|
QLayout *docTitleLayout = m_ui->docTitleWidget->layout();
|
|
|
|
|
for(int i = 0, count = min<int>(docTitleLayout->count() - 1, container->segmentCount()); i < count; ++i) {
|
|
|
|
|
container->setTitle(static_cast<ClearLineEdit *>(docTitleLayout->itemAt(i + 1)->widget())->text().toUtf8().data(), i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// apply all tags
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) {edit->apply();});
|
|
|
|
|
static const QString statusMsg(tr("Saving tags ..."));
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::None);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
} else {
|
|
|
|
|
static const QString statusMsg(tr("No file has been opened."));
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
QMessageBox::warning(this, QApplication::applicationName(), statusMsg);
|
|
|
|
|
return false;
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
static const QString statusMsg(tr("Unable to apply the entered tags to the file because the current process hasn't finished yet."));
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Information);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
|
|
|
|
|
m_ui->makingNotificationWidget->setHidden(false);
|
|
|
|
|
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("No file has been opened, so tags can not be saved."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_makingResultsAvailable = true;
|
|
|
|
|
|
|
|
|
|
// apply titles
|
|
|
|
|
if(AbstractContainer *container = m_fileInfo.container()) {
|
|
|
|
|
if(container->supportsTitle()) {
|
|
|
|
|
QLayout *docTitleLayout = m_ui->docTitleWidget->layout();
|
|
|
|
|
for(int i = 0, count = min<int>(docTitleLayout->count() - 1, container->segmentCount()); i < count; ++i) {
|
|
|
|
|
container->setTitle(static_cast<ClearLineEdit *>(docTitleLayout->itemAt(i + 1)->widget())->text().toUtf8().data(), i);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
static const QString statusMsg(tr("Unable to apply the entered tags to the file because the current process hasn't finished yet."));
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// apply all tags
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) {edit->apply();});
|
|
|
|
|
static const QString statusMsg(tr("Saving tags ..."));
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::None);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return startSaving();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -987,148 +974,148 @@ bool TagEditorWidget::applyEntriesAndSaveChangings()
|
|
|
|
|
*/
|
|
|
|
|
bool TagEditorWidget::deleteAllTagsAndSave()
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
if(Settings::values().editor.askBeforeDeleting) {
|
|
|
|
|
QMessageBox msgBox(this);
|
|
|
|
|
msgBox.setText(tr("Do you really want to delete all tags from the file?"));
|
|
|
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
|
|
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
|
|
|
|
msgBox.setDefaultButton(QMessageBox::No);
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
static const QString statusMsg(tr("Unable to delete all tags from the file because the current process hasn't been finished yet."));
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Information);
|
|
|
|
|
m_ui->makingNotificationWidget->setHidden(false);
|
|
|
|
|
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("No file has been opened, so no tags can be deleted."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!m_fileInfo.hasAnyTag()) {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("The selected file has no tag (at least no supported), so there is nothing to delete."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Settings::values().editor.askBeforeDeleting) {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("Do you really want to delete all tags from the file?"));
|
|
|
|
|
|
|
|
|
|
QMessageBox msgBox(this);
|
|
|
|
|
msgBox.setText(m_ui->makingNotificationWidget->text());
|
|
|
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
|
|
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
|
|
|
|
msgBox.setDefaultButton(QMessageBox::No);
|
|
|
|
|
#if QT_VERSION >= 0x050200
|
|
|
|
|
auto *checkBox = new QCheckBox(&msgBox);
|
|
|
|
|
checkBox->setText(tr("don't show this message again"));
|
|
|
|
|
msgBox.setCheckBox(checkBox);
|
|
|
|
|
auto *checkBox = new QCheckBox(&msgBox);
|
|
|
|
|
checkBox->setText(tr("Don't show this message again"));
|
|
|
|
|
msgBox.setCheckBox(checkBox);
|
|
|
|
|
#endif
|
|
|
|
|
int res = msgBox.exec();
|
|
|
|
|
int res = msgBox.exec();
|
|
|
|
|
#if QT_VERSION >= 0x050200
|
|
|
|
|
if(checkBox->isChecked()) {
|
|
|
|
|
Settings::values().editor.askBeforeDeleting = false;
|
|
|
|
|
}
|
|
|
|
|
if(checkBox->isChecked()) {
|
|
|
|
|
Settings::values().editor.askBeforeDeleting = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if(res != QMessageBox::Yes) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Information);
|
|
|
|
|
m_ui->makingNotificationWidget->setHidden(false);
|
|
|
|
|
m_makingResultsAvailable = true;
|
|
|
|
|
if(m_fileInfo.isOpen()) {
|
|
|
|
|
if(m_fileInfo.hasAnyTag()) {
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) {edit->clear();});
|
|
|
|
|
m_fileInfo.removeAllTags();
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::None);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
static const QString statusMsg(tr("Deleting all tags ..."));
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
} else {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("The selected file stores no tag (at least no supported), so there is nothing to delete."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("No file has been opened, so no tags can be deleted."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
static const QString statusMsg(tr("Unable to delete all tags from the file because the current process hasn't been finished yet."));
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
if(res != QMessageBox::Yes) {
|
|
|
|
|
m_ui->makingNotificationWidget->setText(tr("Deletion aborted."));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_makingResultsAvailable = true;
|
|
|
|
|
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) {edit->clear();});
|
|
|
|
|
m_fileInfo.removeAllTags();
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::None);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Progress);
|
|
|
|
|
static const QString statusMsg(tr("Deleting all tags ..."));
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
return startSaving();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Starts saving. This method is called by applyEntriesAndSaveChangings() and deleteAllTagsAndSave().
|
|
|
|
|
*
|
|
|
|
|
* The actual process is performed in another thread.
|
|
|
|
|
*
|
|
|
|
|
* \remarks Will start a new thread to perform the operation. Then showSavingResult() is called
|
|
|
|
|
* using Qt::QueuedConnection in the main thread. m_fileOperationMutex will remain locked when saving is
|
|
|
|
|
* finished and will be unlocked in showSavingResult(). This way any method which might be called after
|
|
|
|
|
* the operation thread ends and before the invokation of showSavingResult() will see a locked mutex and
|
|
|
|
|
* hence not mutate the current file.
|
|
|
|
|
* using Qt::QueuedConnection in the main thread.
|
|
|
|
|
*/
|
|
|
|
|
bool TagEditorWidget::startSaving()
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
// tags might get invalidated
|
|
|
|
|
m_tags.clear();
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) { edit->setTag(nullptr, false); });
|
|
|
|
|
// show abort button
|
|
|
|
|
m_ui->abortButton->setHidden(false);
|
|
|
|
|
m_ui->abortButton->setEnabled(true);
|
|
|
|
|
m_abortClicked = false;
|
|
|
|
|
// remove current path from file watcher
|
|
|
|
|
m_fileWatcher->removePath(m_currentPath);
|
|
|
|
|
// use current configuration
|
|
|
|
|
const auto &settings = Settings::values().tagPocessing.fileLayout;
|
|
|
|
|
m_fileInfo.setForceRewrite(settings.forceRewrite);
|
|
|
|
|
m_fileInfo.setTagPosition(settings.preferredTagPosition);
|
|
|
|
|
m_fileInfo.setForceTagPosition(settings.forceTagPosition);
|
|
|
|
|
m_fileInfo.setIndexPosition(settings.preferredIndexPosition);
|
|
|
|
|
m_fileInfo.setForceIndexPosition(settings.forceIndexPosition);
|
|
|
|
|
m_fileInfo.setMinPadding(settings.minPadding);
|
|
|
|
|
m_fileInfo.setMaxPadding(settings.maxPadding);
|
|
|
|
|
m_fileInfo.setPreferredPadding(settings.preferredPadding);
|
|
|
|
|
// define functions to show the saving progress and to actually applying the changes
|
|
|
|
|
auto showProgress = [this] (StatusProvider &sender) -> void {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setPercentage", Qt::QueuedConnection, Q_ARG(int, static_cast<int>(sender.currentPercentage() * 100.0)));
|
|
|
|
|
if(m_abortClicked) {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, tr("Cancelling ...")));
|
|
|
|
|
m_fileInfo.tryToAbort();
|
|
|
|
|
} else {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(sender.currentStatus())));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto startThread = [this] {
|
|
|
|
|
m_fileOperationMutex.lock();
|
|
|
|
|
bool processingError = false, ioError = false;
|
|
|
|
|
try {
|
|
|
|
|
try {
|
|
|
|
|
m_fileInfo.applyChanges();
|
|
|
|
|
} catch(const Failure &) {
|
|
|
|
|
processingError = true;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
::IoUtilities::catchIoFailure();
|
|
|
|
|
ioError = true;
|
|
|
|
|
}
|
|
|
|
|
} catch(const exception &e) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "making");
|
|
|
|
|
processingError = true;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "making");
|
|
|
|
|
processingError = true;
|
|
|
|
|
}
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
QMetaObject::invokeMethod(this, "showSavingResult", Qt::QueuedConnection, Q_ARG(bool, processingError), Q_ARG(bool, ioError));
|
|
|
|
|
// showSavingResult() will unlock the mutex!
|
|
|
|
|
};
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
m_fileInfo.registerCallback(showProgress);
|
|
|
|
|
// use another thread to perform the operation
|
|
|
|
|
QtConcurrent::run(startThread);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
static const QString errorMsg(tr("Unable to start saving process because there an other process hasn't finished yet."));
|
|
|
|
|
emit statusMessage(errorMsg);
|
|
|
|
|
QMessageBox::warning(this, QApplication::applicationName(), errorMsg);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tags might get invalidated
|
|
|
|
|
m_tags.clear();
|
|
|
|
|
foreachTagEdit([] (TagEdit *edit) { edit->setTag(nullptr, false); });
|
|
|
|
|
// show abort button
|
|
|
|
|
m_ui->abortButton->setHidden(false);
|
|
|
|
|
m_ui->abortButton->setEnabled(true);
|
|
|
|
|
m_abortClicked = false;
|
|
|
|
|
// remove current path from file watcher
|
|
|
|
|
m_fileWatcher->removePath(m_currentPath);
|
|
|
|
|
// use current configuration
|
|
|
|
|
const auto &settings = Settings::values().tagPocessing.fileLayout;
|
|
|
|
|
m_fileInfo.setForceRewrite(settings.forceRewrite);
|
|
|
|
|
m_fileInfo.setTagPosition(settings.preferredTagPosition);
|
|
|
|
|
m_fileInfo.setForceTagPosition(settings.forceTagPosition);
|
|
|
|
|
m_fileInfo.setIndexPosition(settings.preferredIndexPosition);
|
|
|
|
|
m_fileInfo.setForceIndexPosition(settings.forceIndexPosition);
|
|
|
|
|
m_fileInfo.setMinPadding(settings.minPadding);
|
|
|
|
|
m_fileInfo.setMaxPadding(settings.maxPadding);
|
|
|
|
|
m_fileInfo.setPreferredPadding(settings.preferredPadding);
|
|
|
|
|
// define functions to show the saving progress and to actually applying the changes
|
|
|
|
|
auto showProgress = [this] (StatusProvider &sender) -> void {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setPercentage", Qt::QueuedConnection, Q_ARG(int, static_cast<int>(sender.currentPercentage() * 100.0)));
|
|
|
|
|
if(m_abortClicked) {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, tr("Cancelling ...")));
|
|
|
|
|
m_fileInfo.tryToAbort();
|
|
|
|
|
} else {
|
|
|
|
|
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(sender.currentStatus())));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
auto startThread = [this] {
|
|
|
|
|
bool processingError = false, ioError = false;
|
|
|
|
|
try {
|
|
|
|
|
try {
|
|
|
|
|
m_fileInfo.applyChanges();
|
|
|
|
|
} catch(const Failure &) {
|
|
|
|
|
processingError = true;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
::IoUtilities::catchIoFailure();
|
|
|
|
|
ioError = true;
|
|
|
|
|
}
|
|
|
|
|
} catch(const exception &e) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "making");
|
|
|
|
|
processingError = true;
|
|
|
|
|
} catch(...) {
|
|
|
|
|
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "making");
|
|
|
|
|
processingError = true;
|
|
|
|
|
}
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
QMetaObject::invokeMethod(this, "showSavingResult", Qt::QueuedConnection, Q_ARG(bool, processingError), Q_ARG(bool, ioError));
|
|
|
|
|
};
|
|
|
|
|
m_fileInfo.unregisterAllCallbacks();
|
|
|
|
|
m_fileInfo.registerCallback(showProgress);
|
|
|
|
|
m_fileOperationOngoing = true;
|
|
|
|
|
// use another thread to perform the operation
|
|
|
|
|
QtConcurrent::run(startThread);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Shows the saving results.
|
|
|
|
|
*
|
|
|
|
|
* This private slot is invoked from the thread which performed the
|
|
|
|
|
* saving operation using Qt::QueuedConnection.
|
|
|
|
|
*
|
|
|
|
|
* \param sucess Specifies whether the file could be saved sucessfully.
|
|
|
|
|
* \remarks Expects m_fileOperationMutex to be locked!
|
|
|
|
|
*/
|
|
|
|
|
void TagEditorWidget::showSavingResult(bool processingError, bool ioError)
|
|
|
|
|
{
|
|
|
|
|
m_fileOperationOngoing = false;
|
|
|
|
|
m_ui->abortButton->setHidden(true);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationType(NotificationType::TaskComplete);
|
|
|
|
|
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
|
|
|
|
@ -1166,7 +1153,6 @@ void TagEditorWidget::showSavingResult(bool processingError, bool ioError)
|
|
|
|
|
}
|
|
|
|
|
m_ui->makingNotificationWidget->setText(statusMsg);
|
|
|
|
|
emit statusMessage(statusMsg);
|
|
|
|
|
m_fileOperationMutex.unlock();
|
|
|
|
|
// let the main window know that the current file has been saved
|
|
|
|
|
// -> the main window will ensure the current file is still selected
|
|
|
|
|
emit currentPathChanged(m_currentPath);
|
|
|
|
@ -1193,7 +1179,6 @@ void TagEditorWidget::showSavingResult(bool processingError, bool ioError)
|
|
|
|
|
// -> reset "save as path" in any case after fatal error
|
|
|
|
|
m_fileInfo.setSaveFilePath(string());
|
|
|
|
|
|
|
|
|
|
m_fileOperationMutex.unlock();
|
|
|
|
|
startParsing(m_currentPath, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1230,18 +1215,18 @@ void TagEditorWidget::fileChangedOnDisk(const QString &path)
|
|
|
|
|
*/
|
|
|
|
|
void TagEditorWidget::closeFile()
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
// close file
|
|
|
|
|
m_fileInfo.close();
|
|
|
|
|
// remove current path from file watcher
|
|
|
|
|
m_fileWatcher->removePath(m_currentPath);
|
|
|
|
|
// update ui
|
|
|
|
|
emit statusMessage("The file has been closed.");
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
} else {
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage("Unable to close the file because the current process hasn't been finished yet.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// close file
|
|
|
|
|
m_fileInfo.close();
|
|
|
|
|
// remove current path from file watcher
|
|
|
|
|
m_fileWatcher->removePath(m_currentPath);
|
|
|
|
|
// update ui
|
|
|
|
|
emit statusMessage("The file has been closed.");
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -1300,28 +1285,29 @@ void TagEditorWidget::applySettingsFromDialog()
|
|
|
|
|
*/
|
|
|
|
|
void TagEditorWidget::addTag(const function<Media::Tag *(Media::MediaFileInfo &)> &createTag)
|
|
|
|
|
{
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage("Unable to add a tag because no file is opened.");
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage("Unable to add a tag because the current process hasn't been finished yet.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage("Unable to add a tag because no file is opened.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Tag *tag = createTag(m_fileInfo)) {
|
|
|
|
|
if(std::find(m_tags.cbegin(), m_tags.cend(), tag) != m_tags.cend()) {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("A tag (with the selected target) already exists."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(Tag *tag = createTag(m_fileInfo)) {
|
|
|
|
|
if(std::find(m_tags.cbegin(), m_tags.cend(), tag) == m_tags.cend()) {
|
|
|
|
|
m_tags.push_back(tag);
|
|
|
|
|
updateTagEditsAndAttachmentEdits(true, m_tags.size() > 1 ? PreviousValueHandling::Keep : PreviousValueHandling::Auto);
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
insertTitleFromFilename();
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("A tag (with the selected target) already exists."));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("The tag can not be created."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_tags.push_back(tag);
|
|
|
|
|
updateTagEditsAndAttachmentEdits(true, m_tags.size() > 1 ? PreviousValueHandling::Keep : PreviousValueHandling::Auto);
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
insertTitleFromFilename();
|
|
|
|
|
} else {
|
|
|
|
|
emit statusMessage("Unable to add a tag because the current process hasn't been finished yet.");
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("The tag can not be created."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1333,53 +1319,51 @@ void TagEditorWidget::addTag(const function<Media::Tag *(Media::MediaFileInfo &)
|
|
|
|
|
void TagEditorWidget::removeTag(Tag *tag)
|
|
|
|
|
{
|
|
|
|
|
if(tag) {
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage(tr("Unable to remove the tag because no file is opened."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(m_fileInfo.isOpen()) {
|
|
|
|
|
m_fileInfo.removeTag(tag);
|
|
|
|
|
// remove tag from m_tags
|
|
|
|
|
m_tags.erase(remove(m_tags.begin(), m_tags.end(), tag), m_tags.end());
|
|
|
|
|
// remove tag from all TagEdit widgets
|
|
|
|
|
vector<TagEdit *> toRemove;
|
|
|
|
|
for(int index = 0, count = m_ui->stackedWidget->count(); index < count; ++index) {
|
|
|
|
|
TagEdit *edit = qobject_cast<TagEdit *>(m_ui->stackedWidget->widget(index));
|
|
|
|
|
if(edit && edit->tags().contains(tag)) {
|
|
|
|
|
QList<Tag *> tagsOfEdit = edit->tags();
|
|
|
|
|
tagsOfEdit.removeAll(tag);
|
|
|
|
|
if(tagsOfEdit.empty()) {
|
|
|
|
|
// no tags left in the edit
|
|
|
|
|
if(m_tags.empty()) {
|
|
|
|
|
// there are no other tag edits -> just disable the edit
|
|
|
|
|
edit->setTag(nullptr, false);
|
|
|
|
|
} else {
|
|
|
|
|
// there are still other tag edits -> remove the edit
|
|
|
|
|
toRemove.push_back(edit);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// there are still tags left, reassign remaining tags (keeping the previous values)
|
|
|
|
|
edit->setPreviousValueHandling(PreviousValueHandling::Keep);
|
|
|
|
|
edit->setTags(tagsOfEdit, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// remove TagEdit widgets
|
|
|
|
|
for(TagEdit *edit : toRemove) {
|
|
|
|
|
m_ui->tagSelectionComboBox->removeItem(m_ui->stackedWidget->indexOf(edit));
|
|
|
|
|
m_ui->stackedWidget->removeWidget(edit);
|
|
|
|
|
delete edit;
|
|
|
|
|
}
|
|
|
|
|
// update affected widgets
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage(tr("Unable to remove the tag because the current process hasn't been finished yet."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage(tr("Unable to remove the tag because no file is opened."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_fileInfo.removeTag(tag);
|
|
|
|
|
// remove tag from m_tags
|
|
|
|
|
m_tags.erase(remove(m_tags.begin(), m_tags.end(), tag), m_tags.end());
|
|
|
|
|
// remove tag from all TagEdit widgets
|
|
|
|
|
vector<TagEdit *> toRemove;
|
|
|
|
|
for(int index = 0, count = m_ui->stackedWidget->count(); index < count; ++index) {
|
|
|
|
|
TagEdit *edit = qobject_cast<TagEdit *>(m_ui->stackedWidget->widget(index));
|
|
|
|
|
if(edit && edit->tags().contains(tag)) {
|
|
|
|
|
QList<Tag *> tagsOfEdit = edit->tags();
|
|
|
|
|
tagsOfEdit.removeAll(tag);
|
|
|
|
|
if(tagsOfEdit.empty()) {
|
|
|
|
|
// no tags left in the edit
|
|
|
|
|
if(m_tags.empty()) {
|
|
|
|
|
// there are no other tag edits -> just disable the edit
|
|
|
|
|
edit->setTag(nullptr, false);
|
|
|
|
|
} else {
|
|
|
|
|
// there are still other tag edits -> remove the edit
|
|
|
|
|
toRemove.push_back(edit);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// there are still tags left, reassign remaining tags (keeping the previous values)
|
|
|
|
|
edit->setPreviousValueHandling(PreviousValueHandling::Keep);
|
|
|
|
|
edit->setTags(tagsOfEdit, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// remove TagEdit widgets
|
|
|
|
|
for(TagEdit *edit : toRemove) {
|
|
|
|
|
m_ui->tagSelectionComboBox->removeItem(m_ui->stackedWidget->indexOf(edit));
|
|
|
|
|
m_ui->stackedWidget->removeWidget(edit);
|
|
|
|
|
delete edit;
|
|
|
|
|
}
|
|
|
|
|
// update affected widgets
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
updateFileStatusStatus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1391,27 +1375,25 @@ void TagEditorWidget::removeTag(Tag *tag)
|
|
|
|
|
void TagEditorWidget::changeTarget(Tag *tag)
|
|
|
|
|
{
|
|
|
|
|
if(tag) {
|
|
|
|
|
TryLocker<> locker(m_fileOperationMutex);
|
|
|
|
|
if(locker) {
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage(tr("Unable to change the target because no file is opened."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(m_fileInfo.isOpen()) {
|
|
|
|
|
if(tag->supportsTarget()) {
|
|
|
|
|
EnterTargetDialog targetDlg(this);
|
|
|
|
|
targetDlg.setTarget(tag->target(), &m_fileInfo);
|
|
|
|
|
if(targetDlg.exec() == QDialog::Accepted) {
|
|
|
|
|
tag->setTarget(targetDlg.target());
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("Can not change the target of the selected tag because the tag does not support targets."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if(m_fileOperationOngoing) {
|
|
|
|
|
emit statusMessage(tr("Unable to change the target because the current process hasn't been finished yet."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!m_fileInfo.isOpen()) {
|
|
|
|
|
emit statusMessage(tr("Unable to change the target because no file is opened."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!tag->supportsTarget()) {
|
|
|
|
|
QMessageBox::warning(this, windowTitle(), tr("Can not change the target of the selected tag because the tag does not support targets."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EnterTargetDialog targetDlg(this);
|
|
|
|
|
targetDlg.setTarget(tag->target(), &m_fileInfo);
|
|
|
|
|
if(targetDlg.exec() == QDialog::Accepted) {
|
|
|
|
|
tag->setTarget(targetDlg.target());
|
|
|
|
|
updateTagSelectionComboBox();
|
|
|
|
|
updateTagManagementMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|