tageditor/gui/tagfieldedit.cpp

853 lines
31 KiB
C++
Raw Normal View History

2015-09-06 20:20:00 +02:00
#include "./tagfieldedit.h"
#include "./picturepreviewselection.h"
2015-04-22 19:33:53 +02:00
2015-09-06 20:20:00 +02:00
#include "../application/knownfieldmodel.h"
2018-03-07 01:18:01 +01:00
#include "../application/settings.h"
2015-04-22 19:33:53 +02:00
2015-09-06 20:20:00 +02:00
#include "../misc/utility.h"
2015-04-22 19:33:53 +02:00
#include <tagparser/id3/id3v2tag.h>
#include <tagparser/mediafileinfo.h>
2018-03-07 01:18:01 +01:00
#include <tagparser/tag.h>
#include <tagparser/tagvalue.h>
2015-04-22 19:33:53 +02:00
#include <qtutilities/widgets/clearcombobox.h>
2018-03-07 01:18:01 +01:00
#include <qtutilities/widgets/clearlineedit.h>
2015-04-22 19:33:53 +02:00
#include <qtutilities/widgets/clearplaintextedit.h>
2018-03-07 01:18:01 +01:00
#include <qtutilities/widgets/clearspinbox.h>
2015-04-22 19:33:53 +02:00
#include <qtutilities/widgets/iconbutton.h>
#include <c++utilities/conversion/conversionexception.h>
2018-03-07 01:18:01 +01:00
#include <QAction>
#include <QCursor>
#include <QEvent>
#include <QFile>
2015-04-22 19:33:53 +02:00
#include <QGraphicsScene>
#include <QGraphicsTextItem>
2018-03-07 01:18:01 +01:00
#include <QGraphicsView>
#include <QHBoxLayout>
2015-04-22 19:33:53 +02:00
#include <QImage>
#include <QKeyEvent>
2018-03-07 01:18:01 +01:00
#include <QLabel>
2015-04-22 19:33:53 +02:00
#include <QListIterator>
#include <QMenu>
2018-03-07 01:18:01 +01:00
#include <QPlainTextEdit>
#include <QPushButton>
#include <QVBoxLayout>
2015-04-22 19:33:53 +02:00
#include <algorithm>
#include <functional>
2018-03-07 01:18:01 +01:00
#include <initializer_list>
2015-04-22 19:33:53 +02:00
#include <iostream>
using namespace std;
using namespace Models;
using namespace Widgets;
2018-03-06 23:10:13 +01:00
using namespace TagParser;
2015-04-22 19:33:53 +02:00
using namespace ConversionUtilities;
namespace QtGui {
/*!
* \class QtGui::TagFieldEdit
* \brief The TagFieldEdit widget allows the user to edit a specified tag field.
*/
/*!
* \brief Constructs a new TagFieldEdit.
* \sa setTagField()
*/
2018-03-07 01:18:01 +01:00
TagFieldEdit::TagFieldEdit(const QList<TagParser::Tag *> &tags, TagParser::KnownField field, QWidget *parent)
: QWidget(parent)
, m_layout(new QVBoxLayout(this))
, m_tags(&tags)
, m_field(field)
, m_dataType(determineDataType())
, m_lineEdit(nullptr)
, m_comboBox(nullptr)
, m_spinBoxes(QPair<Widgets::ClearSpinBox *, Widgets::ClearSpinBox *>(nullptr, nullptr))
, m_pictureSelection(nullptr)
, m_plainTextEdit(nullptr)
, m_descriptionLineEdit(nullptr)
, m_restoreButton(nullptr)
2015-04-22 19:33:53 +02:00
{
m_layout->setContentsMargins(QMargins());
setLayout(m_layout);
setupUi();
updateValue();
}
/*!
* \brief Assigns the specified \a tags and sets the specified \a fields using the given \a previousValueHandling.
*
* If \a preventUiUpdate is true, the UI will not be updated.
*/
2018-03-07 01:18:01 +01:00
void TagFieldEdit::setTagField(
const QList<Tag *> &tags, TagParser::KnownField field, PreviousValueHandling previousValueHandling, bool preventUiUpdate)
2015-04-22 19:33:53 +02:00
{
bool uiRebuildingRequired = false;
m_tags = &tags;
2018-03-07 01:18:01 +01:00
if (m_field != field) {
2015-04-22 19:33:53 +02:00
m_field = field;
uiRebuildingRequired = true;
}
2018-03-07 01:18:01 +01:00
if (tags.size()) {
2015-04-22 19:33:53 +02:00
TagDataType proposedDataType = determineDataType();
2018-03-07 01:18:01 +01:00
if (proposedDataType != m_dataType) {
2015-04-22 19:33:53 +02:00
m_dataType = proposedDataType;
uiRebuildingRequired = true;
}
}
2018-03-07 01:18:01 +01:00
if (!preventUiUpdate) {
if (uiRebuildingRequired) {
2015-04-22 19:33:53 +02:00
setupUi();
}
updateValue(previousValueHandling);
}
}
2016-03-03 22:21:15 +01:00
/*!
* \brief Returns the currently shown value.
* \remarks
* - The specified \a encoding is used to encode text values.
* - Does not work for values of the type picture.
*/
TagValue TagFieldEdit::value(TagTextEncoding encoding, bool includeDescription) const
{
TagValue value;
2018-03-07 01:18:01 +01:00
switch (m_dataType) {
2016-03-03 22:21:15 +01:00
case TagDataType::Text:
case TagDataType::TimeSpan:
case TagDataType::DateTime:
2018-03-07 01:18:01 +01:00
switch (m_field) {
2016-03-03 22:21:15 +01:00
case KnownField::Genre:
2018-03-07 01:18:01 +01:00
if (m_comboBox) {
2016-03-03 22:21:15 +01:00
value.assignText(Utility::qstringToString(m_comboBox->currentText(), encoding), encoding);
}
break;
case KnownField::Lyrics:
2018-03-07 01:18:01 +01:00
if (m_plainTextEdit) {
2016-03-03 22:21:15 +01:00
value.assignText(Utility::qstringToString(m_plainTextEdit->toPlainText(), encoding), encoding);
}
break;
default:
2018-03-07 01:18:01 +01:00
if (m_lineEdit) {
2016-03-03 22:21:15 +01:00
value.assignText(Utility::qstringToString(m_lineEdit->text(), encoding), encoding);
}
}
break;
case TagDataType::Integer:
2018-03-07 01:18:01 +01:00
if (m_spinBoxes.first && m_spinBoxes.first->value()) {
2016-03-03 22:21:15 +01:00
value.assignInteger(m_spinBoxes.first->value());
}
break;
case TagDataType::PositionInSet:
2018-03-07 01:18:01 +01:00
if (m_spinBoxes.first && m_spinBoxes.second) {
2016-03-03 22:21:15 +01:00
value.assignPosition(PositionInSet(m_spinBoxes.first->value(), m_spinBoxes.second->value()));
}
break;
case TagDataType::StandardGenreIndex:
2018-03-07 01:18:01 +01:00
if (m_comboBox) {
2016-03-03 22:21:15 +01:00
value.assignText(Utility::qstringToString(m_comboBox->currentText(), encoding), encoding);
}
break;
2018-03-07 01:18:01 +01:00
default:;
2016-03-03 22:21:15 +01:00
}
2018-03-07 01:18:01 +01:00
if (m_descriptionLineEdit && includeDescription) { // setup description line edit
2016-03-03 22:21:15 +01:00
value.setDescription(Utility::qstringToString(m_descriptionLineEdit->text(), encoding), encoding);
}
return value;
}
2015-04-22 19:33:53 +02:00
/*!
* \brief Sets the \a value of the current tag field manually using the given \a previousValueHandling.
*/
bool TagFieldEdit::setValue(const TagValue &value, PreviousValueHandling previousValueHandling)
{
updateValue(value, previousValueHandling, false);
2018-03-07 01:18:01 +01:00
if (m_pictureSelection) {
m_pictureSelection->setValue(value, previousValueHandling);
}
return true;
2015-04-22 19:33:53 +02:00
}
/*!
* \brief Returns whether at least one of the assigned tags supports a description for the current field.
*/
bool TagFieldEdit::hasDescription() const
{
for (const Tag *tag : tags()) {
2018-03-07 01:18:01 +01:00
if (tag->supportsDescription(m_field)) {
2015-04-22 19:33:53 +02:00
return true;
}
}
return false;
}
/*!
* \brief Returns an indication whether the specified \a field can be applied.
*/
bool TagFieldEdit::canApply(KnownField field) const
{
for (const Tag *tag : tags()) {
2018-03-07 01:18:01 +01:00
switch (tag->type()) {
2015-04-22 19:33:53 +02:00
case TagType::Id3v1Tag:
if (Settings::values().tagPocessing.creationSettings.id3v1usage == TagUsage::Never) {
2015-04-22 19:33:53 +02:00
continue;
}
break;
case TagType::Id3v2Tag:
if (Settings::values().tagPocessing.creationSettings.id3v2usage == TagUsage::Never) {
2015-04-22 19:33:53 +02:00
continue;
}
break;
2018-03-07 01:18:01 +01:00
default:;
2015-04-22 19:33:53 +02:00
}
2018-03-07 01:18:01 +01:00
if (tag->supportsField(field)) {
2015-04-22 19:33:53 +02:00
return true;
}
}
return false;
}
/*!
* \brief Sets whether the cover buttons are hidden.
*/
void TagFieldEdit::setCoverButtonsHidden(bool hideCoverButtons)
{
2018-03-07 01:18:01 +01:00
if (m_pictureSelection) {
m_pictureSelection->setCoverButtonsHidden(hideCoverButtons);
}
}
2015-04-22 19:33:53 +02:00
/*!
* \brief Internally called to determine the data type of the current tag field.
*/
TagDataType TagFieldEdit::determineDataType()
{
TagDataType proposedDataType = TagDataType::Undefined;
2018-03-07 01:18:01 +01:00
for (Tag *tag : tags()) {
2015-04-22 19:33:53 +02:00
TagDataType type = tag->proposedDataType(m_field);
2018-03-07 01:18:01 +01:00
if (proposedDataType == TagDataType::Undefined) {
2015-04-22 19:33:53 +02:00
proposedDataType = type;
2018-03-07 01:18:01 +01:00
} else if ((proposedDataType == TagDataType::PositionInSet && type == TagDataType::Integer)
|| (type == TagDataType::PositionInSet && proposedDataType == TagDataType::Integer)) {
2015-04-22 19:33:53 +02:00
proposedDataType = TagDataType::PositionInSet; // PositionInSet and Number can be considered as compatible
2018-03-07 01:18:01 +01:00
} else if (type == TagDataType::Undefined || type != proposedDataType) {
2015-04-22 19:33:53 +02:00
return TagDataType::Undefined; // undefined or different (incompatible) types proposed
}
}
return proposedDataType;
}
/*!
* \brief Internally called to setup the UI.
*
* Causes the previous UI widgets to be destroyed. Any unapplied changes are lost.
*/
void TagFieldEdit::setupUi()
{
// remove previous widgets
m_lineEdit = nullptr;
m_comboBox = nullptr;
m_spinBoxes.first = nullptr;
m_spinBoxes.second = nullptr;
m_pictureSelection = nullptr;
m_plainTextEdit = nullptr;
m_descriptionLineEdit = nullptr;
qDeleteAll(m_widgets);
m_widgets.clear();
// setup widgets
2018-03-07 01:18:01 +01:00
switch (m_dataType) {
2015-04-22 19:33:53 +02:00
case TagDataType::Text:
case TagDataType::TimeSpan:
case TagDataType::DateTime:
2018-03-07 01:18:01 +01:00
switch (m_field) {
2015-04-22 19:33:53 +02:00
case KnownField::Genre:
setupGenreComboBox();
break;
case KnownField::Lyrics:
setupPlainTextEdit();
break;
default:
setupLineEdit();
}
break;
case TagDataType::Picture:
setupPictureSelection();
break;
case TagDataType::Integer:
setupSpinBox();
break;
case TagDataType::PositionInSet:
setupPositionInSetSpinBoxes();
break;
case TagDataType::StandardGenreIndex:
setupGenreComboBox();
break;
case TagDataType::Binary:
setupFileSelection();
break;
default:
setupTypeNotSupportedLabel();
;
}
2018-03-07 01:18:01 +01:00
if (m_dataType != TagDataType::Picture && hasDescription()) { // setup description line edit
2015-04-22 19:33:53 +02:00
setupDescriptionLineEdit();
}
}
/*!
* \brief Internally called by setupUi() to setup a line edit.
*/
ClearLineEdit *TagFieldEdit::setupLineEdit()
{
m_lineEdit = new ClearLineEdit(this);
m_lineEdit->setPlaceholderText(tr("empty"));
static_cast<ButtonOverlay *>(m_lineEdit)->setClearButtonEnabled(true);
m_lineEdit->insertCustomButton(0, setupRestoreButton());
m_lineEdit->installEventFilter(this);
connect(m_lineEdit, &ClearLineEdit::textChanged, this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_lineEdit);
m_widgets << m_lineEdit;
return m_lineEdit;
}
/*!
* \brief Internally called by setupUi() to setup a plain text edit.
*/
ClearPlainTextEdit *TagFieldEdit::setupPlainTextEdit()
{
m_plainTextEdit = new ClearPlainTextEdit(this);
m_plainTextEdit->setClearButtonEnabled(true);
m_plainTextEdit->insertCustomButton(0, setupRestoreButton());
connect(m_plainTextEdit->document(), &QTextDocument::contentsChanged, this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_plainTextEdit);
m_widgets << m_plainTextEdit;
return m_plainTextEdit;
}
/*!
* \brief Internally called by setupUi() to setup a combo box.
*/
ClearComboBox *TagFieldEdit::setupGenreComboBox()
{
m_comboBox = new ClearComboBox(this);
m_comboBox->setEditable(true);
2018-03-07 01:18:01 +01:00
if (QLineEdit *lineEdit = m_comboBox->lineEdit()) {
2015-04-22 19:33:53 +02:00
lineEdit->setPlaceholderText(tr("empty"));
}
m_comboBox->addItems(QStringList()
2018-03-07 01:18:01 +01:00
<< QString() << tr("Blues") << tr("A capella") << tr("Abstract") << tr("Acid") << tr("Acid Jazz") << tr("Acid Punk") << tr("Acoustic")
<< tr("Alternative") << tr("Alternative Rock") << tr("Ambient") << tr("Anime") << tr("Art Rock") << tr("Audio Theatre") << tr("Audiobook")
<< tr("Avantgarde") << tr("Ballad") << tr("Baroque") << tr("Bass") << tr("Beat") << tr("Bebop") << tr("Bhangra") << tr("Big Band")
<< tr("Big Beat") << tr("Black Metal") << tr("Bluegrass") << tr("Booty Bass") << tr("Breakbeat") << tr("BritPop") << tr("Cabaret")
<< tr("Celtic") << tr("Chamber Music") << tr("Chanson") << tr("Chillout") << tr("Chorus") << tr("Christian Gangsta Rap")
<< tr("Christian Rap") << tr("Christian Rock") << tr("Classic Rock") << tr("Classical") << tr("Club") << tr("Club-House") << tr("Comedy")
<< tr("Contemporary Christian") << tr("Country") << tr("Crossover") << tr("Cult") << tr("Dance") << tr("Dance Hall") << tr("Darkwave")
<< tr("Death Metal") << tr("Disco") << tr("Downtempo") << tr("Dream") << tr("Drum & Bass") << tr("Drum Solo") << tr("Dub") << tr("Dubstep")
<< tr("Duet") << tr("Easy Listening") << tr("EBM") << tr("Eclectic") << tr("Electro") << tr("Electroclash") << tr("Electronic") << tr("Emo")
<< tr("Ethnic") << tr("Euro-House") << tr("Euro-Techno") << tr("Eurodance") << tr("Experimental") << tr("Fast Fusion") << tr("Folk")
<< tr("Folk-Rock") << tr("Folklore") << tr("Freestyle") << tr("Funk") << tr("Fusion") << tr("G-Funk") << tr("Game") << tr("Gangsta")
<< tr("Garage") << tr("Garage Rock") << tr("Global") << tr("Goa") << tr("Gospel") << tr("Gothic") << tr("Gothic Rock") << tr("Grunge")
<< tr("Hard Rock") << tr("Hardcore Techno") << tr("Heavy Metal") << tr("Hip-Hop") << tr("House") << tr("Humour") << tr("IDM")
<< tr("Illbient") << tr("Indie") << tr("Indie Rock") << tr("Industrial") << tr("Industro-Goth") << tr("Instrumental")
<< tr("Instrumental Pop") << tr("Instrumental Rock") << tr("Jam Band") << tr("Jazz") << tr("Jazz & Funk") << tr("Jpop") << tr("Jungle")
<< tr("Krautrock") << tr("Latin") << tr("Leftfield") << tr("Lo-Fi") << tr("Lounge") << tr("Math Rock") << tr("Meditative") << tr("Merengue")
<< tr("Metal") << tr("Musical") << tr("National Folk") << tr("Native US") << tr("Negerpunk") << tr("Neoclassical")
<< tr("Neue Deutsche Welle") << tr("New Age") << tr("New Romantic") << tr("New Wave") << tr("Noise") << tr("Nu-Breakz") << tr("Oldies")
<< tr("Opera") << tr("Podcast") << tr("Polka") << tr("Polsk Punk") << tr("Pop") << tr("Pop-Folk") << tr("Pop/Funk") << tr("Porn Groove")
<< tr("Post-Punk") << tr("Post-Rock") << tr("Power Ballad") << tr("Pranks") << tr("Primus") << tr("Progressive Rock") << tr("Psychedelic")
<< tr("Psychedelic Rock") << tr("Psytrance") << tr("Punk") << tr("Punk Rock") << tr("Rap") << tr("Rave") << tr("Reggae") << tr("Retro")
<< tr("Revival") << tr("Rhythmic Soul") << tr("Rock") << tr("Rock & Roll") << tr("Salsa") << tr("Samba") << tr("Satire") << tr("Shoegaze")
<< tr("Showtunes") << tr("Ska") << tr("Slow Jam") << tr("Slow Rock") << tr("Sonata") << tr("Soul") << tr("Sound Clip") << tr("Soundtrack")
<< tr("Southern Rock") << tr("Space") << tr("Space Rock") << tr("Speech") << tr("Swing") << tr("Symphonic Rock") << tr("Symphony")
<< tr("Synthpop") << tr("Tango") << tr("Techno") << tr("Techno-Industrial") << tr("Terror") << tr("Thrash Metal") << tr("Top 40")
<< tr("Trailer") << tr("Trance") << tr("Tribal") << tr("Trip-Hop") << tr("Trop Rock") << tr("Vocal") << tr("World Music"));
2015-04-22 19:33:53 +02:00
m_comboBox->setCurrentIndex(0);
m_comboBox->setClearButtonEnabled(true);
m_comboBox->insertCustomButton(0, setupRestoreButton());
m_comboBox->installEventFilter(this);
m_comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
2015-04-22 19:33:53 +02:00
connect(m_comboBox, &ClearComboBox::currentTextChanged, this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_comboBox);
m_widgets << m_comboBox;
return m_comboBox;
}
/*!
* \brief Internally called by setupUi() to setup a single spin box.
*/
ClearSpinBox *TagFieldEdit::setupSpinBox()
{
m_spinBoxes.first = new ClearSpinBox(this);
m_spinBoxes.first->setPlaceholderText(tr("empty"));
m_spinBoxes.first->setMinimumHidden(true);
m_spinBoxes.first->setClearButtonEnabled(true);
m_spinBoxes.first->insertCustomButton(0, setupRestoreButton());
m_spinBoxes.first->installEventFilter(this);
m_spinBoxes.first->setMaximum(32766);
connect(m_spinBoxes.first, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_spinBoxes.first);
m_widgets << m_spinBoxes.first;
return m_spinBoxes.first;
}
/*!
* \brief Internally called by setupUi() to setup a a pair of spin boxes.
*/
QPair<Widgets::ClearSpinBox *, Widgets::ClearSpinBox *> &TagFieldEdit::setupPositionInSetSpinBoxes()
{
QHBoxLayout *subLayout = new QHBoxLayout;
m_spinBoxes.first = new ClearSpinBox(this);
m_spinBoxes.first->setPlaceholderText(tr("empty"));
m_spinBoxes.first->setMinimumHidden(true);
m_spinBoxes.first->setClearButtonEnabled(true);
m_spinBoxes.first->installEventFilter(this);
m_spinBoxes.first->setMaximum(32766);
m_spinBoxes.first->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(m_spinBoxes.first, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &TagFieldEdit::showRestoreButton);
subLayout->addWidget(m_spinBoxes.first);
m_widgets << m_spinBoxes.first;
QLabel *label = new QLabel(tr("of"), this);
subLayout->addWidget(label);
m_widgets << label;
m_spinBoxes.second = new ClearSpinBox(this);
m_spinBoxes.second->setPlaceholderText(tr("empty"));
m_spinBoxes.second->setMinimumHidden(true);
m_spinBoxes.second->setClearButtonEnabled(true);
m_spinBoxes.second->installEventFilter(this);
m_spinBoxes.second->setMaximum(32766);
m_spinBoxes.second->insertCustomButton(0, setupRestoreButton());
m_spinBoxes.second->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(m_spinBoxes.second, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &TagFieldEdit::showRestoreButton);
subLayout->addWidget(m_spinBoxes.second);
m_widgets << m_spinBoxes.second;
m_layout->addLayout(subLayout);
return m_spinBoxes;
}
/*!
* \brief Internally called by setupUi() to setup a PicturePreviewSelection widget.
*/
PicturePreviewSelection *TagFieldEdit::setupPictureSelection()
{
m_pictureSelection = new PicturePreviewSelection(nullptr, KnownField::Invalid, this);
connect(m_pictureSelection, &PicturePreviewSelection::pictureChanged, this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_pictureSelection);
m_widgets << m_pictureSelection;
return m_pictureSelection;
}
/*!
* \brief Internally called by setupUi() to setup a file selection.
* \remarks TODO
*/
QWidget *TagFieldEdit::setupFileSelection()
{
return setupTypeNotSupportedLabel();
}
/*!
* \brief Internally called by setupUi() to setup a line edit for the description.
*/
ClearLineEdit *TagFieldEdit::setupDescriptionLineEdit()
{
QLabel *label = new QLabel(tr("Description"), this);
m_layout->addWidget(label);
m_widgets << label;
m_descriptionLineEdit = new ClearLineEdit(this);
m_descriptionLineEdit->setPlaceholderText(tr("empty"));
static_cast<ButtonOverlay *>(m_descriptionLineEdit)->setClearButtonEnabled(true);
m_descriptionLineEdit->installEventFilter(this);
connect(m_descriptionLineEdit, &ClearLineEdit::textChanged, this, &TagFieldEdit::showRestoreButton);
m_layout->addWidget(m_descriptionLineEdit);
m_widgets << m_descriptionLineEdit;
return m_descriptionLineEdit;
}
/*!
* \brief Internally called by setupUi() to setup a label indicating that the current tag field is not supported by the edit.
*/
QLabel *TagFieldEdit::setupTypeNotSupportedLabel()
{
QLabel *label = new QLabel(tr("editing widget for field type not supported"), this);
m_layout->addWidget(label);
m_widgets << label;
return label;
}
/*!
2016-03-03 22:21:15 +01:00
* \brief Updates the currently shown value manually.
2015-04-22 19:33:53 +02:00
* \param previousValueHandling Specifies how to deal with the previous value.
*
* The new value is read from the assigned tag(s).
*/
void TagFieldEdit::updateValue(PreviousValueHandling previousValueHandling)
{
QListIterator<Tag *> i(tags());
i.toBack();
bool pictureSelectionUpdated = false;
2018-03-07 01:18:01 +01:00
while (i.hasPrevious()) {
2015-04-22 19:33:53 +02:00
Tag *tag = i.previous();
const TagValue &value = tag->value(m_field);
2018-03-07 01:18:01 +01:00
if (!value.isEmpty()) {
2015-04-22 19:33:53 +02:00
updateValue(value, previousValueHandling);
2018-03-07 01:18:01 +01:00
if (m_pictureSelection) {
2015-04-22 19:33:53 +02:00
m_pictureSelection->setTagField(tag, m_field, previousValueHandling);
pictureSelectionUpdated = true;
}
return;
}
}
updateValue(TagValue::empty(), previousValueHandling);
2018-03-07 01:18:01 +01:00
if (m_pictureSelection && !pictureSelectionUpdated) {
if (m_tags->size()) {
2015-04-22 19:33:53 +02:00
m_pictureSelection->setTagField(m_tags->last(), m_field, previousValueHandling);
} else {
m_pictureSelection->setTagField(nullptr, m_field, previousValueHandling);
}
}
}
/*!
2016-03-03 22:21:15 +01:00
* \brief Updates the currently shown value manually.
2015-04-22 19:33:53 +02:00
* \param tag Specifies the tag to read the new value from.
* \param previousValueHandling Specifies how to deal with the previous value.
* \remarks If \a tag is nullptr, the new value is empty.
*/
void TagFieldEdit::updateValue(Tag *tag, PreviousValueHandling previousValueHandling)
{
2018-03-07 01:18:01 +01:00
if (tag) {
2015-04-22 19:33:53 +02:00
updateValue(tag->value(m_field), previousValueHandling);
} else {
updateValue(TagValue::empty(), previousValueHandling);
}
2018-03-07 01:18:01 +01:00
if (m_pictureSelection) {
2015-04-22 19:33:53 +02:00
m_pictureSelection->setTagField(tag, m_field, previousValueHandling);
}
}
/*!
2016-03-03 22:21:15 +01:00
* \brief Updates the currently shown value manually.
2015-04-22 19:33:53 +02:00
* \param value Specifies the new value.
* \param previousValueHandling Specifies how to deal with the previous value.
* \param updateRestoreButton Specifies whether the "restore button" should be updated.
* \remarks Does not update the picture preview selection.
2015-04-22 19:33:53 +02:00
*/
void TagFieldEdit::updateValue(const TagValue &value, PreviousValueHandling previousValueHandling, bool updateRestoreButton)
{
bool conversionError = false;
bool updated = false;
concretizePreviousValueHandling(previousValueHandling);
2018-03-07 01:18:01 +01:00
if (m_lineEdit || m_comboBox || m_plainTextEdit) {
2015-04-22 19:33:53 +02:00
QString text;
try {
text = Utility::tagValueToQString(value);
2016-03-05 17:03:17 +01:00
} catch (const ConversionException &) {
2015-04-22 19:33:53 +02:00
conversionError = true;
}
applyAutoCorrection(text);
2018-03-07 01:18:01 +01:00
if (previousValueHandling == PreviousValueHandling::Clear || !text.isEmpty()) {
if (m_lineEdit && (previousValueHandling != PreviousValueHandling::Keep || m_lineEdit->isCleared())) {
2015-04-22 19:33:53 +02:00
m_lineEdit->setText(text);
updated = true;
}
2018-03-07 01:18:01 +01:00
if (m_comboBox && (previousValueHandling != PreviousValueHandling::Keep || m_comboBox->isCleared())) {
2015-04-22 19:33:53 +02:00
m_comboBox->setCurrentText(text);
updated = true;
}
2018-03-07 01:18:01 +01:00
if (m_plainTextEdit && (previousValueHandling != PreviousValueHandling::Keep || m_plainTextEdit->isCleared())) {
2015-04-22 19:33:53 +02:00
m_plainTextEdit->setPlainText(text);
updated = true;
}
}
}
2018-03-07 01:18:01 +01:00
if (m_spinBoxes.first) {
if (m_spinBoxes.second) {
2015-04-22 19:33:53 +02:00
PositionInSet pos;
try {
2016-03-19 18:34:10 +01:00
pos = value.toPositionInSet();
2018-03-07 01:18:01 +01:00
} catch (const ConversionException &) {
2015-04-22 19:33:53 +02:00
conversionError = true;
}
2018-03-07 01:18:01 +01:00
if (previousValueHandling == PreviousValueHandling::Clear || pos.position()) {
if (previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.first->isCleared()) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.first->setValue(pos.position());
updated = true;
}
2018-03-07 01:18:01 +01:00
} else if (previousValueHandling == PreviousValueHandling::IncrementUpdate && !m_spinBoxes.first->isCleared()) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.first->setValue(m_spinBoxes.first->value() + 1);
updated = true;
}
2018-03-07 01:18:01 +01:00
if (previousValueHandling == PreviousValueHandling::Clear || pos.total()) {
if (previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.second->isCleared()) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.second->setValue(pos.total());
updated = true;
}
}
} else {
int num;
try {
num = value.toInteger();
2018-03-07 01:18:01 +01:00
} catch (const ConversionException &) {
2015-04-22 19:33:53 +02:00
conversionError = true;
num = 0;
}
2018-03-07 01:18:01 +01:00
if (previousValueHandling == PreviousValueHandling::Clear || num) {
if (previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.first->isCleared()) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.first->setValue(num);
updated = true;
}
2018-03-07 01:18:01 +01:00
} else if (previousValueHandling == PreviousValueHandling::IncrementUpdate && !m_spinBoxes.first->isCleared()) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.first->setValue(m_spinBoxes.first->value() + 1);
updated = true;
}
}
}
2018-03-07 01:18:01 +01:00
if (m_descriptionLineEdit && (previousValueHandling != PreviousValueHandling::Keep || m_descriptionLineEdit->isCleared())) {
2015-04-22 19:33:53 +02:00
try {
QString desc = Utility::stringToQString(value.description(), value.descriptionEncoding());
applyAutoCorrection(desc);
m_descriptionLineEdit->setText(desc);
2018-03-07 01:18:01 +01:00
} catch (const ConversionException &) {
2015-04-22 19:33:53 +02:00
conversionError = true;
m_descriptionLineEdit->clear();
}
}
2018-03-07 01:18:01 +01:00
if (updateRestoreButton && m_restoreButton) {
2015-04-22 19:33:53 +02:00
m_restoreButton->setVisible((!updated && m_restoreButton->isVisible()) || m_tags->size() > 1);
}
2018-03-07 01:18:01 +01:00
const initializer_list<ButtonOverlay *> widgets = { m_lineEdit, m_comboBox, m_spinBoxes.first, m_spinBoxes.second };
2015-04-22 19:33:53 +02:00
bool canApply = this->canApply(m_field);
2018-03-07 01:18:01 +01:00
if (conversionError || !canApply) {
2017-03-01 23:31:46 +01:00
const QPixmap pixmap(QIcon(QStringLiteral(":/qtutilities/icons/hicolor/48x48/actions/edit-error.png")).pixmap(16));
2015-04-22 19:33:53 +02:00
QString text;
2018-03-07 01:18:01 +01:00
if (conversionError) {
2018-01-31 21:05:06 +01:00
text = tr("The value of this field could not be read from the file because it couldn't be converted properly.");
2018-03-07 01:18:01 +01:00
if (!canApply) {
2016-11-30 20:58:50 +01:00
text += QChar('\n');
2015-04-22 19:33:53 +02:00
}
}
2018-03-07 01:18:01 +01:00
if (!canApply) {
2015-04-22 19:33:53 +02:00
text += tr("The field can not be applied when saving the file and will be lost.");
}
2018-03-07 01:18:01 +01:00
for (ButtonOverlay *overlay : widgets) {
if (overlay) {
2015-04-22 19:33:53 +02:00
overlay->enableInfoButton(pixmap, text);
}
}
} else {
2018-03-07 01:18:01 +01:00
for (ButtonOverlay *overlay : widgets) {
if (overlay) {
2015-04-22 19:33:53 +02:00
overlay->disableInfoButton();
}
}
}
}
/*!
* \brief Internally called by the other setup methods to create the "restore button".
*/
IconButton *TagFieldEdit::setupRestoreButton()
{
2018-03-07 01:18:01 +01:00
if (!m_restoreButton) { // setup restore button
2015-04-22 19:33:53 +02:00
m_restoreButton = new IconButton(this);
2018-03-07 01:18:01 +01:00
m_restoreButton->setPixmap(
/*QIcon::fromTheme(QStringLiteral("edit-undo"), */ QIcon(QStringLiteral(":/qtutilities/icons/hicolor/48x48/actions/edit-menu.png") /*)*/)
.pixmap(16));
2016-12-01 23:02:49 +01:00
m_restoreButton->setToolTip(tr("Restore value as it is currently present in the file"));
2015-04-22 19:33:53 +02:00
connect(m_restoreButton, &IconButton::clicked, this, &TagFieldEdit::handleRestoreButtonClicked);
// ownership might be transfered to a child widget/layout
connect(m_restoreButton, &IconButton::destroyed, this, &TagFieldEdit::handleRestoreButtonDestroyed);
}
return m_restoreButton;
}
/*!
* \brief Internally called to show the restore button (if there is one and at least one tag is assigned).
*/
void TagFieldEdit::showRestoreButton()
{
2018-03-07 01:18:01 +01:00
if (m_restoreButton) {
2015-04-22 19:33:53 +02:00
m_restoreButton->setVisible(m_tags->size());
}
}
/*!
* \brief Applies auto correction (according to the settings) for the specified \a textValue.
*/
void TagFieldEdit::applyAutoCorrection(QString &textValue)
{
2016-10-24 20:15:10 +02:00
const auto &settings = Settings::values().editor.autoCompletition;
auto &fields = settings.fields.items();
2018-03-07 01:18:01 +01:00
auto i = find_if(fields.constBegin(), fields.constEnd(), [this](const ChecklistItem &item) {
2015-04-22 19:33:53 +02:00
bool ok;
return (item.id().toInt(&ok) == static_cast<int>(this->field())) && ok;
});
// if current field is in the list of auto correction fields and auto correction should be applied
2018-03-07 01:18:01 +01:00
if (i != fields.constEnd() && i->isChecked()) {
if (settings.trimWhitespaces) {
2015-04-22 19:33:53 +02:00
textValue = textValue.trimmed();
}
2018-03-07 01:18:01 +01:00
if (settings.fixUmlauts) {
2015-04-22 19:33:53 +02:00
textValue = Utility::fixUmlauts(textValue);
}
2018-03-07 01:18:01 +01:00
if (settings.formatNames) {
2015-04-22 19:33:53 +02:00
textValue = Utility::formatName(textValue);
}
}
}
/*!
* \brief If \a previousValueHandling is PreviousValueHandling::Auto it will be set to a concrete value.
*/
void TagFieldEdit::concretizePreviousValueHandling(PreviousValueHandling &previousValueHandling)
{
2018-03-07 01:18:01 +01:00
switch (previousValueHandling) {
2015-04-22 19:33:53 +02:00
case PreviousValueHandling::Auto:
2018-03-07 01:18:01 +01:00
switch (m_field) {
2016-03-05 17:03:17 +01:00
// these differ for each song -> always clear previous value
2015-04-22 19:33:53 +02:00
case KnownField::Title:
2016-03-05 17:03:17 +01:00
case KnownField::Lyrics:
2015-04-22 19:33:53 +02:00
previousValueHandling = PreviousValueHandling::Clear;
break;
// these will be incremented
case KnownField::TrackPosition:
case KnownField::PartNumber:
previousValueHandling = PreviousValueHandling::IncrementUpdate;
break;
default:
previousValueHandling = PreviousValueHandling::Update;
}
2018-03-07 01:18:01 +01:00
default:;
2015-04-22 19:33:53 +02:00
}
}
/*!
* \brief Restores the current value to the initial value.
*/
void TagFieldEdit::restore()
{
updateValue(PreviousValueHandling::Clear);
}
/*!
* \brief Clears the current value.
*/
void TagFieldEdit::clear()
{
2018-03-07 01:18:01 +01:00
if (m_lineEdit) {
2015-04-22 19:33:53 +02:00
m_lineEdit->clear();
}
2018-03-07 01:18:01 +01:00
if (m_comboBox) {
2015-04-22 19:33:53 +02:00
m_comboBox->setCurrentText(QString());
}
2018-03-07 01:18:01 +01:00
if (m_plainTextEdit) {
2015-04-22 19:33:53 +02:00
m_plainTextEdit->clear();
}
2018-03-07 01:18:01 +01:00
if (m_spinBoxes.first) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.first->setValue(0);
}
2018-03-07 01:18:01 +01:00
if (m_spinBoxes.second) {
2015-04-22 19:33:53 +02:00
m_spinBoxes.second->setValue(0);
}
2018-03-07 01:18:01 +01:00
if (m_pictureSelection) {
2015-04-22 19:33:53 +02:00
m_pictureSelection->clear();
}
2018-03-07 01:18:01 +01:00
if (m_descriptionLineEdit) {
2015-04-22 19:33:53 +02:00
m_descriptionLineEdit->clear();
}
}
/*!
* \brief Applies the current value to the assigned tag.
*/
void TagFieldEdit::apply()
{
2018-03-07 01:18:01 +01:00
for (Tag *tag : *m_tags) {
if (m_dataType == TagDataType::Picture) {
if (m_pictureSelection) {
2015-04-22 19:33:53 +02:00
m_pictureSelection->apply();
}
} else {
2016-10-24 20:15:10 +02:00
TagTextEncoding encoding = Settings::values().tagPocessing.preferredEncoding;
2018-03-07 01:18:01 +01:00
if (!tag->canEncodingBeUsed(encoding)) {
2015-04-22 19:33:53 +02:00
encoding = tag->proposedTextEncoding();
}
2016-03-03 22:21:15 +01:00
tag->setValue(m_field, value(encoding, tag->supportsDescription(m_field)));
2015-04-22 19:33:53 +02:00
}
}
}
/*!
* \brief Handles different key events.
*/
bool TagFieldEdit::eventFilter(QObject *obj, QEvent *event)
{
2018-03-07 01:18:01 +01:00
switch (event->type()) {
2015-04-22 19:33:53 +02:00
case QEvent::KeyRelease: {
2016-03-03 22:21:15 +01:00
auto *keyEvent = static_cast<QKeyEvent *>(event);
2015-04-22 19:33:53 +02:00
int key = keyEvent->key();
2018-03-07 01:18:01 +01:00
switch (key) {
2015-04-22 19:33:53 +02:00
case Qt::Key_Return:
emit returnPressed();
break;
case Qt::Key_Shift:
2018-03-07 01:18:01 +01:00
if (keyEvent->modifiers() & Qt::ControlModifier) {
if (QLineEdit *le = qobject_cast<QLineEdit *>(obj)) {
2015-04-22 19:33:53 +02:00
le->setText(Utility::formatName(le->text()));
return true;
}
}
break;
2018-03-07 01:18:01 +01:00
default:;
2015-04-22 19:33:53 +02:00
}
break;
2018-03-07 01:18:01 +01:00
}
default:;
2015-04-22 19:33:53 +02:00
}
return QWidget::eventFilter(obj, event);
}
/*!
* \brief Called when the restore button has been clicked.
*/
void TagFieldEdit::handleRestoreButtonClicked()
{
2018-03-07 01:18:01 +01:00
if (tags().size()) {
2015-04-22 19:33:53 +02:00
QMenu menu;
int i = 0;
2018-03-07 01:18:01 +01:00
for (Tag *tag : tags()) {
2015-04-22 19:33:53 +02:00
++i;
QAction *action = menu.addAction(tr("restore to value from %1 (%2)").arg(tag->typeName()).arg(i));
2018-03-07 01:18:01 +01:00
connect(action, &QAction::triggered,
std::bind(static_cast<void (TagFieldEdit::*)(Tag *, PreviousValueHandling)>(&TagFieldEdit::updateValue), this, tag,
PreviousValueHandling::Clear));
2015-04-22 19:33:53 +02:00
}
menu.exec(QCursor::pos());
} else {
restore();
}
}
/*!
* \brief Sets m_restoreButton to nullptr when the restore button has been destroyed.
*/
void TagFieldEdit::handleRestoreButtonDestroyed(QObject *obj)
{
2018-03-07 01:18:01 +01:00
if (obj == m_restoreButton) {
2015-04-22 19:33:53 +02:00
m_restoreButton = nullptr;
}
}
2018-03-07 01:18:01 +01:00
} // namespace QtGui