#include "./tagfieldedit.h" #include "./picturepreviewselection.h" #include "../application/settings.h" #include "../application/knownfieldmodel.h" #include "../misc/utility.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Models; using namespace Widgets; using namespace Media; 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() */ TagFieldEdit::TagFieldEdit(const QList &tags, Media::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(nullptr, nullptr)), m_pictureSelection(nullptr), m_plainTextEdit(nullptr), m_descriptionLineEdit(nullptr), m_restoreButton(nullptr) { 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. */ void TagFieldEdit::setTagField(const QList &tags, Media::KnownField field, PreviousValueHandling previousValueHandling, bool preventUiUpdate) { bool uiRebuildingRequired = false; m_tags = &tags; if(m_field != field) { m_field = field; uiRebuildingRequired = true; } if(tags.size()) { TagDataType proposedDataType = determineDataType(); if(proposedDataType != m_dataType) { m_dataType = proposedDataType; uiRebuildingRequired = true; } } if(!preventUiUpdate) { if(uiRebuildingRequired) { setupUi(); } updateValue(previousValueHandling); } } /*! * \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; switch(m_dataType) { case TagDataType::Text: case TagDataType::TimeSpan: case TagDataType::DateTime: switch(m_field) { case KnownField::Genre: if(m_comboBox) { value.assignText(Utility::qstringToString(m_comboBox->currentText(), encoding), encoding); } break; case KnownField::Lyrics: if(m_plainTextEdit) { value.assignText(Utility::qstringToString(m_plainTextEdit->toPlainText(), encoding), encoding); } break; default: if(m_lineEdit) { value.assignText(Utility::qstringToString(m_lineEdit->text(), encoding), encoding); } } break; case TagDataType::Integer: if(m_spinBoxes.first && m_spinBoxes.first->value()) { value.assignInteger(m_spinBoxes.first->value()); } break; case TagDataType::PositionInSet: if(m_spinBoxes.first && m_spinBoxes.second) { value.assignPosition(PositionInSet(m_spinBoxes.first->value(), m_spinBoxes.second->value())); } break; case TagDataType::StandardGenreIndex: if(m_comboBox) { value.assignText(Utility::qstringToString(m_comboBox->currentText(), encoding), encoding); } break; default: ; } if(m_descriptionLineEdit && includeDescription) { // setup description line edit value.setDescription(Utility::qstringToString(m_descriptionLineEdit->text(), encoding), encoding); } return value; } /*! * \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); if(m_pictureSelection) { m_pictureSelection->setValue(value, previousValueHandling); } return true; } /*! * \brief Returns whether at least one of the assigned tags supports a description for the current field. */ bool TagFieldEdit::hasDescription() const { for(Tag *tag : tags()) { if(tag->supportsDescription(m_field)) { return true; } } return false; } /*! * \brief Returns an indication whether the specified \a field can be applied. */ bool TagFieldEdit::canApply(KnownField field) const { for(Tag *tag : tags()) { switch(tag->type()) { case TagType::Id3v1Tag: if(Settings::id3v1usage() == TagUsage::Never) { continue; } break; case TagType::Id3v2Tag: if(Settings::id3v2usage() == TagUsage::Never) { continue; } break; default: ; } if(tag->supportsField(field)) { return true; } } return false; } /*! * \brief Sets whether the cover buttons are hidden. */ void TagFieldEdit::setCoverButtonsHidden(bool hideCoverButtons) { if(m_pictureSelection) { m_pictureSelection->setCoverButtonsHidden(hideCoverButtons); } } /*! * \brief Internally called to determine the data type of the current tag field. */ TagDataType TagFieldEdit::determineDataType() { TagDataType proposedDataType = TagDataType::Undefined; for(Tag *tag : tags()) { TagDataType type = tag->proposedDataType(m_field); if(proposedDataType == TagDataType::Undefined) { proposedDataType = type; } else if((proposedDataType == TagDataType::PositionInSet && type == TagDataType::Integer) || (type == TagDataType::PositionInSet && proposedDataType == TagDataType::Integer)) { proposedDataType = TagDataType::PositionInSet; // PositionInSet and Number can be considered as compatible } else if(type == TagDataType::Undefined || type != proposedDataType) { 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 switch(m_dataType) { case TagDataType::Text: case TagDataType::TimeSpan: case TagDataType::DateTime: switch(m_field) { 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(); ; } if(m_dataType != TagDataType::Picture && hasDescription()) { // setup description line edit 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(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); if(QLineEdit *lineEdit = m_comboBox->lineEdit()) { lineEdit->setPlaceholderText(tr("empty")); } m_comboBox->addItems(QStringList() << 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")); m_comboBox->setCurrentIndex(0); m_comboBox->setClearButtonEnabled(true); m_comboBox->insertCustomButton(0, setupRestoreButton()); m_comboBox->installEventFilter(this); m_comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 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(&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 &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(&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(&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(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; } /*! * \brief Updates the currently shown value manually. * \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 i(tags()); i.toBack(); bool pictureSelectionUpdated = false; while(i.hasPrevious()) { Tag *tag = i.previous(); const TagValue &value = tag->value(m_field); if(!value.isEmpty()) { updateValue(value, previousValueHandling); if(m_pictureSelection) { m_pictureSelection->setTagField(tag, m_field, previousValueHandling); pictureSelectionUpdated = true; } return; } } updateValue(TagValue::empty(), previousValueHandling); if(m_pictureSelection && !pictureSelectionUpdated) { if(m_tags->size()) { m_pictureSelection->setTagField(m_tags->last(), m_field, previousValueHandling); } else { m_pictureSelection->setTagField(nullptr, m_field, previousValueHandling); } } } /*! * \brief Updates the currently shown value manually. * \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) { if(tag) { updateValue(tag->value(m_field), previousValueHandling); } else { updateValue(TagValue::empty(), previousValueHandling); } if(m_pictureSelection) { m_pictureSelection->setTagField(tag, m_field, previousValueHandling); } } /*! * \brief Updates the currently shown value manually. * \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. */ void TagFieldEdit::updateValue(const TagValue &value, PreviousValueHandling previousValueHandling, bool updateRestoreButton) { bool conversionError = false; bool updated = false; concretizePreviousValueHandling(previousValueHandling); if(m_lineEdit || m_comboBox || m_plainTextEdit) { QString text; try { text = Utility::tagValueToQString(value); } catch (const ConversionException &) { conversionError = true; } applyAutoCorrection(text); if(previousValueHandling == PreviousValueHandling::Clear || !text.isEmpty()) { if(m_lineEdit && (previousValueHandling != PreviousValueHandling::Keep || m_lineEdit->isCleared())) { m_lineEdit->setText(text); updated = true; } if(m_comboBox && (previousValueHandling != PreviousValueHandling::Keep || m_comboBox->isCleared())) { m_comboBox->setCurrentText(text); updated = true; } if(m_plainTextEdit && (previousValueHandling != PreviousValueHandling::Keep || m_plainTextEdit->isCleared())) { m_plainTextEdit->setPlainText(text); updated = true; } } } if(m_spinBoxes.first) { if(m_spinBoxes.second) { PositionInSet pos; try { pos = value.toPositionInSet(); } catch(const ConversionException &) { conversionError = true; } if(previousValueHandling == PreviousValueHandling::Clear || pos.position()) { if(previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.first->isCleared()) { m_spinBoxes.first->setValue(pos.position()); updated = true; } } else if(previousValueHandling == PreviousValueHandling::IncrementUpdate && !m_spinBoxes.first->isCleared()) { m_spinBoxes.first->setValue(m_spinBoxes.first->value() + 1); updated = true; } if(previousValueHandling == PreviousValueHandling::Clear || pos.total()) { if(previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.second->isCleared()) { m_spinBoxes.second->setValue(pos.total()); updated = true; } } } else { int num; try { num = value.toInteger(); } catch(const ConversionException &) { conversionError = true; num = 0; } if(previousValueHandling == PreviousValueHandling::Clear || num) { if(previousValueHandling != PreviousValueHandling::Keep || m_spinBoxes.first->isCleared()) { m_spinBoxes.first->setValue(num); updated = true; } } else if(previousValueHandling == PreviousValueHandling::IncrementUpdate && !m_spinBoxes.first->isCleared()) { m_spinBoxes.first->setValue(m_spinBoxes.first->value() + 1); updated = true; } } } if(m_descriptionLineEdit && (previousValueHandling != PreviousValueHandling::Keep || m_descriptionLineEdit->isCleared())) { try { QString desc = Utility::stringToQString(value.description(), value.descriptionEncoding()); applyAutoCorrection(desc); m_descriptionLineEdit->setText(desc); } catch(const ConversionException &) { conversionError = true; m_descriptionLineEdit->clear(); } } if(updateRestoreButton && m_restoreButton) { m_restoreButton->setVisible((!updated && m_restoreButton->isVisible()) || m_tags->size() > 1); } const initializer_list widgets = {m_lineEdit, m_comboBox, m_spinBoxes.first, m_spinBoxes.second}; bool canApply = this->canApply(m_field); if(conversionError || !canApply) { QPixmap pixmap = style()->standardPixmap(QStyle::SP_MessageBoxCritical).scaled(16, 16); QString text; if(conversionError) { text = tr("The value of this field could not be read from the file because it couldn't be converted proberly."); if(!canApply) { text += "\n"; } } if(!canApply) { text += tr("The field can not be applied when saving the file and will be lost."); } for(ButtonOverlay *overlay : widgets) { if(overlay) { overlay->enableInfoButton(pixmap, text); } } } else { for(ButtonOverlay *overlay : widgets) { if(overlay) { overlay->disableInfoButton(); } } } } /*! * \brief Internally called by the other setup methods to create the "restore button". */ IconButton *TagFieldEdit::setupRestoreButton() { if(!m_restoreButton) { // setup restore button m_restoreButton = new IconButton(this); m_restoreButton->setPixmap(/*QIcon::fromTheme(QStringLiteral("edit-undo"), */QIcon(QStringLiteral(":/qtutilities/icons/hicolor/16x16/actions/edit-menu.png")/*)*/).pixmap(16)); m_restoreButton->setToolTip(tr("restore")); 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() { if(m_restoreButton) { m_restoreButton->setVisible(m_tags->size()); } } /*! * \brief Applies auto correction (according to the settings) for the specified \a textValue. */ void TagFieldEdit::applyAutoCorrection(QString &textValue) { auto &fields = Settings::autoCorrectionFields().items(); auto i = find_if(fields.constBegin(), fields.constEnd(), [this] (const ChecklistItem &item) { bool ok; return (item.id().toInt(&ok) == static_cast(this->field())) && ok; }); // if current field is in the list of auto correction fields and auto correction should be applied if(i != fields.constEnd() && i->isChecked()) { if(Settings::trimWhitespaces()) { textValue = textValue.trimmed(); } if(Settings::fixUmlauts()) { textValue = Utility::fixUmlauts(textValue); } if(Settings::formatNames()) { textValue = Utility::formatName(textValue); } } } /*! * \brief If \a previousValueHandling is PreviousValueHandling::Auto it will be set to a concrete value. */ void TagFieldEdit::concretizePreviousValueHandling(PreviousValueHandling &previousValueHandling) { switch(previousValueHandling) { case PreviousValueHandling::Auto: switch(m_field) { // these differ for each song -> always clear previous value case KnownField::Title: case KnownField::Lyrics: previousValueHandling = PreviousValueHandling::Clear; break; // these will be incremented case KnownField::TrackPosition: case KnownField::PartNumber: previousValueHandling = PreviousValueHandling::IncrementUpdate; break; default: previousValueHandling = PreviousValueHandling::Update; } default: ; } } /*! * \brief Restores the current value to the initial value. */ void TagFieldEdit::restore() { updateValue(PreviousValueHandling::Clear); } /*! * \brief Clears the current value. */ void TagFieldEdit::clear() { if(m_lineEdit) { m_lineEdit->clear(); } if(m_comboBox) { m_comboBox->setCurrentText(QString()); } if(m_plainTextEdit) { m_plainTextEdit->clear(); } if(m_spinBoxes.first) { m_spinBoxes.first->setValue(0); } if(m_spinBoxes.second) { m_spinBoxes.second->setValue(0); } if(m_pictureSelection) { m_pictureSelection->clear(); } if(m_descriptionLineEdit) { m_descriptionLineEdit->clear(); } } /*! * \brief Applies the current value to the assigned tag. */ void TagFieldEdit::apply() { for(Tag *tag : *m_tags) { if(m_dataType == TagDataType::Picture) { if(m_pictureSelection) { m_pictureSelection->apply(); } } else { TagTextEncoding encoding = Settings::preferredEncoding(); if(!tag->canEncodingBeUsed(encoding)) { encoding = tag->proposedTextEncoding(); } tag->setValue(m_field, value(encoding, tag->supportsDescription(m_field))); } } } /*! * \brief Handles different key events. */ bool TagFieldEdit::eventFilter(QObject *obj, QEvent *event) { switch(event->type()) { case QEvent::KeyRelease: { auto *keyEvent = static_cast(event); int key = keyEvent->key(); switch(key) { case Qt::Key_Return: emit returnPressed(); break; case Qt::Key_Shift: if(keyEvent->modifiers() & Qt::ControlModifier) { if(QLineEdit *le = qobject_cast(obj)) { le->setText(Utility::formatName(le->text())); return true; } } break; default: ; } break; } default: ; } return QWidget::eventFilter(obj, event); } /*! * \brief Called when the restore button has been clicked. */ void TagFieldEdit::handleRestoreButtonClicked() { if(tags().size()) { QMenu menu; int i = 0; for(Tag *tag : tags()) { ++i; QAction *action = menu.addAction(tr("restore to value from %1 (%2)").arg(tag->typeName()).arg(i)); connect(action, &QAction::triggered, std::bind(static_cast(&TagFieldEdit::updateValue), this, tag, PreviousValueHandling::Clear)); } menu.exec(QCursor::pos()); } else { restore(); } } /*! * \brief Sets m_restoreButton to nullptr when the restore button has been destroyed. */ void TagFieldEdit::handleRestoreButtonDestroyed(QObject *obj) { if(obj == m_restoreButton) { m_restoreButton = nullptr; } } }