diff --git a/include/Track.h b/include/Track.h index 52d43f6e115..f4268da922d 100644 --- a/include/Track.h +++ b/include/Track.h @@ -197,6 +197,8 @@ class LMMS_EXPORT Track : public Model, public JournallingObject BoolModel* getMutedModel(); + //! returns a name that isn't used by any track and conatins `sourceName` + QString findUniqueName(const QString& sourceName) const; public slots: virtual void setName(const QString& newName); @@ -211,6 +213,9 @@ public slots: void saveTrack(QDomDocument& doc, QDomElement& element, bool presetMode); void loadTrack(const QDomElement& element, bool presetMode); + //! returns the number characters at the end of a string + static QString getNameNumberEnding(const QString& name, bool* isSeparatedWithWhiteSpace = nullptr); + private: TrackContainer* m_trackContainer; Type m_type; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index e44475d9374..cb8449fe633 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -167,6 +167,8 @@ Track* Track::clone() QDomElement parent = doc.createElement("clonedtrack"); saveState(doc, parent); Track* t = create(parent.firstChild().toElement(), m_trackContainer); + // giving different name to cloned track + t->setName(findUniqueName(name())); AutomationClip::resolveAllIDs(); return t; @@ -652,6 +654,75 @@ BoolModel *Track::getMutedModel() return &m_mutedModel; } +QString Track::findUniqueName(const QString& sourceName) const +{ + QString output = sourceName; + // removing number from `sourceName` + bool isSeparatedWithWhiteSpace = false; + size_t sourceNumberLength = Track::getNameNumberEnding(sourceName, &isSeparatedWithWhiteSpace).size(); + if (sourceNumberLength > 0) + { + // whitespace needs to be removed so we add + 1 to `sourceNumberLength` + sourceNumberLength = isSeparatedWithWhiteSpace ? sourceNumberLength + 1 : sourceNumberLength; + output.remove(output.size() - sourceNumberLength, sourceNumberLength); + } + + const TrackContainer::TrackList& trackList = m_trackContainer->tracks(); + + size_t maxNameCounter = 0; + bool found = false; + + for (const Track* it : trackList) + { + if (it->name().startsWith(output)) + { + size_t nameCount = Track::getNameNumberEnding(it->name()).toInt(); + maxNameCounter = maxNameCounter < nameCount ? nameCount : maxNameCounter; + found = true; + } + } + + if (found) + { + output = output + " " + QString::number(maxNameCounter + 1); + } + + return output; +} + +QString Track::getNameNumberEnding(const QString& name, bool* isSeparatedWithWhiteSpace) +{ + QString numberString = ""; + + //! `it` will point to where the numbers start in `name` + auto it = name.end(); + size_t digitCount = 0; + while (it != name.begin()) + { + it--; + if (it->isDigit() == false) + { + if (isSeparatedWithWhiteSpace != nullptr) + { + *isSeparatedWithWhiteSpace = *it == ' '; + } + // the last character was not a number + // increase `it` to account for this (and make it point to a digit) + it++; + break; + } + digitCount++; + } + + if (digitCount > 0) + { + numberString.resize(digitCount); + std::copy(it, name.end(), numberString.begin()); + } + + return numberString; +} + void Track::setName(const QString& newName) { if (m_name != newName) diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index 1ff30ac7680..8d9eb9d24ee 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -36,7 +36,7 @@ namespace lmms AutomationTrack::AutomationTrack( TrackContainer* tc, bool _hidden ) : Track( _hidden ? Type::HiddenAutomation : Type::Automation, tc ) { - setName( tr( "Automation track" ) ); + setName(findUniqueName(tr("Automation track"))); } bool AutomationTrack::play( const TimePos & time_start, const fpp_t _frames, diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 66992bc4f6d..5e56cef0b5e 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -104,7 +104,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : this, [this, i]{ processCCEvent(i); }, Qt::DirectConnection); } - setName( tr( "Default preset" ) ); + setName(findUniqueName(tr("Default preset"))); connect(&m_baseNoteModel, SIGNAL(dataChanged()), this, SLOT(updateBaseNote()), Qt::DirectConnection); connect(&m_pitchModel, SIGNAL(dataChanged()), this, SLOT(updatePitch()), Qt::DirectConnection); @@ -1056,7 +1056,7 @@ Instrument * InstrumentTrack::loadInstrument(const QString & _plugin_name, m_instrument = Instrument::instantiate(_plugin_name, this, key, keyFromDnd); unlock(); - setName(m_instrument->displayName()); + setName(findUniqueName(m_instrument->displayName())); emit instrumentChanged(); diff --git a/src/tracks/PatternTrack.cpp b/src/tracks/PatternTrack.cpp index 697a7c2a8fb..ede04948aa5 100644 --- a/src/tracks/PatternTrack.cpp +++ b/src/tracks/PatternTrack.cpp @@ -46,7 +46,7 @@ PatternTrack::PatternTrack(TrackContainer* tc) : int patternNum = s_infoMap.size(); s_infoMap[this] = patternNum; - setName(tr("Pattern %1").arg(patternNum)); + setName(findUniqueName(tr("Pattern"))); Engine::patternStore()->createClipsForPattern(patternNum); Engine::patternStore()->setCurrentPattern(patternNum); Engine::patternStore()->updateComboBox(); @@ -194,8 +194,6 @@ void PatternTrack::loadTrackSpecificSettings(const QDomElement& _this) { Clip::copyStateTo(track->getClip(src), track->getClip(dst)); } - setName( tr( "Clone of %1" ).arg( - _this.parentNode().toElement().attribute( "name" ) ) ); } else { diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 8ad75799dd0..c62b8bb9715 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -52,7 +52,7 @@ SampleTrack::SampleTrack(TrackContainer* tc) : m_audioPort(tr("Sample track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel), m_isPlaying(false) { - setName(tr("Sample track")); + setName(findUniqueName(tr("Sample track"))); m_panningModel.setCenterValue(DefaultPanning); m_mixerChannelModel.setRange(0, Engine::mixer()->numChannels()-1, 1);