    #include "importmidi_model.h"
    #include "importmidi_inner.h"
    #include "importmidi_clef.h"
    #include "mscore/preferences.h"
    #include "libmscore/instrtemplate.h"

    namespace Ms {

    class TracksModel::Column
          {
       public:
          explicit Column(MidiOperations::Opers &opers) : _opers(opers) {}
          virtual ~Column() {}

          virtual QVariant value(int trackIndex) const = 0;
          virtual void setValue(const QVariant &value, int trackIndex) = 0;
          virtual QString headerName() const = 0;
          virtual bool isVisible(int /*trackIndex*/) const { return true; }
          virtual QStringList valueList(int /*trackIndex*/) const { return _values; }
          virtual int width() const { return -1; }
          virtual bool isEditable(int /*trackIndex*/) const { return true; }
          virtual bool isForAllTracksOnly() const { return false; }

       protected:
          MidiOperations::Opers &_opers;
          QStringList _values;
          };

    TracksModel::TracksModel()
          : _trackCount(0)
          , _frozenColCount(0)
          , _isAllApplied(true)
          {
          }

    TracksModel::~TracksModel()
          {
          }

    void TracksModel::reset(const MidiOperations::Opers &opers,
                            const QList<std::string> &lyricsList,
                            int trackCount,
                            const QString &midiFile,
                            bool hasHumanBeats,
                            bool hasTempoText,
                            bool hasChordNames)
          {
          beginResetModel();
          _trackOpers = opers;
          _columns.clear();
          _trackCount = trackCount;
          _frozenColCount = 0;
          _midiFile = midiFile;
          _isAllApplied = true;
          if (trackCount == 0)
                return;

          //-----------------------------------------------------------------------
          struct Import : Column {
                Import(MidiOperations::Opers &opers) : Column(opers) {}

                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Import"); }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.doImport.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.doImport.setValue(trackIndex, value.toBool());
                      }
                };
          ++_frozenColCount;
          _columns.push_back(std::unique_ptr<Column>(new Import(_trackOpers)));

          //-----------------------------------------------------------------------
          struct Channel : Column {
                Channel(MidiOperations::Opers &opers) : Column(opers) {}
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Channel"); }
                bool isEditable(int /*trackIndex*/) const override { return false; }
                QVariant value(int trackIndex) const override
                      {
                      return QString::number(_opers.channel.value(trackIndex));
                      }
                void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}
                };
          ++_frozenColCount;
          _columns.push_back(std::unique_ptr<Column>(new Channel(_trackOpers)));

          //-----------------------------------------------------------------------
          bool hasStaffName = false;
          for (int i = 0; i != _trackCount; ++i) {
                if (_trackOpers.staffName.value(i) != "") {
                      hasStaffName = true;
                      break;
                      }
                }
          if (hasStaffName) {
                struct StaffName : Column {
                      StaffName(MidiOperations::Opers &opers, const QString &midiFile)
                            : Column(opers), _midiFile(midiFile)
                            {
                            }
                      int width() const override { return 180; }
                      QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Staff name"); }
                      bool isEditable(int /*trackIndex*/) const override { return false; }
                      QVariant value(int trackIndex) const override
                            {
                            MidiOperations::Data &opers = preferences.midiImportOperations;
                            MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);

                            return MidiCharset::convertToCharset(_opers.staffName.value(trackIndex));
                            }
                      void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}

                   private:
                      QString _midiFile;
                      };
                ++_frozenColCount;
                _columns.push_back(std::unique_ptr<Column>(new StaffName(_trackOpers, _midiFile)));
                }

          //-----------------------------------------------------------------------
          struct MidiInstrumentName : Column {
                MidiInstrumentName(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                int width() const override { return 130; }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Sound"); }
                bool isEditable(int /*trackIndex*/) const override { return false; }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.midiInstrName.value(trackIndex);
                      }
                void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}
                };
          ++_frozenColCount;
          _columns.push_back(std::unique_ptr<Column>(new MidiInstrumentName(_trackOpers)));

          //-----------------------------------------------------------------------
          bool hasMsInstrument = false;
          for (int i = 0; i != _trackCount; ++i) {
                if (!_trackOpers.msInstrList.value(i).empty()) {
                      hasMsInstrument = true;
                      break;
                      }
                }
          if (hasMsInstrument) {
                struct MsInstrument : Column {
                      MsInstrument(MidiOperations::Opers &opers) : Column(opers)
                            {
                            }
                      int width() const override { return 220; }
                      QString headerName() const override { return QCoreApplication::translate(
                                              "MIDI import operations", "MuseScore instrument"); }
                      bool isEditable(int trackIndex) const override
                            {
                            return _opers.msInstrList.value(trackIndex).size() > 1;
                            }
                      QVariant value(int trackIndex) const override
                            {
                            const int instrIndex = _opers.msInstrIndex.value(trackIndex);
                            const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
                            const InstrumentTemplate *instr = (trackInstrList.empty())
                                        ? nullptr : trackInstrList[instrIndex];
                            return instrName(instr);
                            }
                      void setValue(const QVariant &value, int trackIndex) override
                            {
                            _opers.msInstrIndex.setValue(trackIndex, value.toInt());
                            }
                      QStringList valueList(int trackIndex) const override
                            {
                            auto list = QStringList();
                            const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
                            for (const InstrumentTemplate *instr: trackInstrList)
                                  list.append(instrName(instr));
                            return list;
                            }

                   private:
                      static QString instrName(const InstrumentTemplate *instr)
                            {
                            if (!instr)
                                  return "-";
                            if (!instr->trackName.isEmpty())
                                  return instr->trackName;
                            if (instr->longNames.isEmpty())
                                  return instr->id;
                            return instr->longNames.front().name();
                            }
                      };
                _columns.push_back(std::unique_ptr<Column>(new MsInstrument(_trackOpers)));
                }

          //-----------------------------------------------------------------------
          if (!lyricsList.isEmpty()) {
                struct Lyrics : Column {
                      Lyrics(MidiOperations::Opers &opers,
                             const QList<std::string> &lyricsList,
                             const QString &midiFile)
                            : Column(opers), _lyricsList(lyricsList), _midiFile(midiFile)
                            {
                            }
                      int width() const override { return 185; }
                      QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Lyrics"); }
                      QVariant value(int trackIndex) const override
                            {
                            int index = _opers.lyricTrackIndex.value(trackIndex);
                            if (index >= 0) {
                                  MidiOperations::Data &opers = preferences.midiImportOperations;
                                  MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);

                                  return MidiCharset::convertToCharset(_lyricsList[index]);
                                  }
                            return "";
                            }
                      void setValue(const QVariant &value, int trackIndex) override
                            {
                                        // GUI lyrics list always have "" row, so: (index - 1)
                            _opers.lyricTrackIndex.setValue(trackIndex, value.toInt() - 1);
                            }
                      QStringList valueList(int /*trackIndex*/) const override
                            {
                            MidiOperations::Data &opers = preferences.midiImportOperations;
                            MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);

                            auto list = QStringList("");
                            for (const auto &lyric: _lyricsList)
                                  list.append(MidiCharset::convertToCharset(lyric));
                            return list;
                            }
                   private:
                      QList<std::string> _lyricsList;
                      QString _midiFile;
                      };
                _columns.push_back(std::unique_ptr<Column>(new Lyrics(_trackOpers, lyricsList, _midiFile)));
                }

          //-----------------------------------------------------------------------
          struct QuantValue : Column {
                QuantValue(MidiOperations::Opers &opers) : Column(opers)
                      {
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "Quarter"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "Eighth"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "16th"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "32nd"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "64th"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "128th"));
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                    "MIDI import operations", "Max. quantization"); }
                QVariant value(int trackIndex) const override
                      {
                      return _values[(int)_opers.quantValue.value(trackIndex)];
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.quantValue.setValue(trackIndex, (MidiOperations::QuantValue)value.toInt());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new QuantValue(_trackOpers)));

          //-----------------------------------------------------------------------
          struct VoiceCount : Column {
                VoiceCount(MidiOperations::Opers &opers) : Column(opers)
                      {
                      _values.push_back("1");
                      _values.push_back("2");
                      _values.push_back("3");
                      _values.push_back("4");
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Max. voices"); }
                QVariant value(int trackIndex) const override
                      {
                      return _values[(int)_opers.maxVoiceCount.value(trackIndex)];
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.maxVoiceCount.setValue(trackIndex, (MidiOperations::VoiceCount)value.toInt());
                      }
                bool isVisible(int trackIndex) const override
                      {
                      if (_opers.isDrumTrack.value(trackIndex))
                            return false;
                      return true;
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new VoiceCount(_trackOpers)));

          //-----------------------------------------------------------------------
          struct Tuplets : Column {
                Tuplets(MidiOperations::Opers &opers, int trackCount)
                      : Column(opers), _trackCount(trackCount)
                      {
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "2-plets"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "3-plets"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "4-plets"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "5-plets"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "7-plets"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "9-plets"));
                      }
                int width() const override { return 140; }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Tuplets"); }
                QVariant value(int trackIndex) const override
                      {
                      QString val;
                      if (_opers.search2plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "2";
                            }
                      if (_opers.search3plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "3";
                            }
                      if (_opers.search4plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "4";
                            }
                      if (_opers.search5plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "5";
                            }
                      if (_opers.search7plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "7";
                            }
                      if (_opers.search9plets.value(trackIndex)) {
                            if (val != "")
                                  val += ", ";
                            val += "9";
                            }
                      return val;
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      const QStringList list = value.toStringList();

                      Q_ASSERT_X(list.size() > 5, "Midi import operations",
                                 "Invalid size of the tuplets value list");

                      bool searchTuplets = false;
                      if (list[0] != "undefined") {
                            const bool doSearch = (list[0] == "true");
                            _opers.search2plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      if (list[1] != "undefined") {
                            const bool doSearch = (list[1] == "true");
                            _opers.search3plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      if (list[2] != "undefined") {
                            const bool doSearch = (list[2] == "true");
                            _opers.search4plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      if (list[3] != "undefined") {
                            const bool doSearch = (list[3] == "true");
                            _opers.search5plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      if (list[4] != "undefined") {
                            const bool doSearch = (list[4] == "true");
                            _opers.search7plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      if (list[5] != "undefined") {
                            const bool doSearch = (list[5] == "true");
                            _opers.search9plets.setValue(trackIndex, doSearch);
                            if (!searchTuplets && doSearch)
                                  searchTuplets = true;
                            }
                      _opers.searchTuplets.setValue(trackIndex, searchTuplets);
                      }
                QStringList valueList(int trackIndex) const override
                      {
                      auto list = QStringList("__MultiValue__");

                      list.append(_values[0]);
                      list.append(checkBoxValue(trackIndex, _opers.search2plets));
                      list.append(_values[1]);
                      list.append(checkBoxValue(trackIndex, _opers.search3plets));
                      list.append(_values[2]);
                      list.append(checkBoxValue(trackIndex, _opers.search4plets));
                      list.append(_values[3]);
                      list.append(checkBoxValue(trackIndex, _opers.search5plets));
                      list.append(_values[4]);
                      list.append(checkBoxValue(trackIndex, _opers.search7plets));
                      list.append(_values[5]);
                      list.append(checkBoxValue(trackIndex, _opers.search9plets));

                      return list;
                      }

             private:
                QString checkBoxValue(int trackIndex,
                                      const MidiOperations::TrackOp<bool> &operation) const
                      {
                      if (trackIndex == -1) {       // symbolizes all tracks
                            const bool firstValue = operation.value(0);
                            for (int i = 1; i < _trackCount; ++i) {
                                  if (operation.value(i) != firstValue)
                                        return "undefined";
                                  }
                            trackIndex = 0;   // to pick the first track value on return
                            }
                      return operation.value(trackIndex) ? "true" : "false";
                      }

                int _trackCount;
                };
          _columns.push_back(std::unique_ptr<Column>(new Tuplets(_trackOpers, _trackCount)));

          //-----------------------------------------------------------------------
          if (hasHumanBeats) {
                struct TimeSig : Column {
                      TimeSig(MidiOperations::Opers &opers) : Column(opers)
                            {
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "3"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "5"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "6"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "7"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "9"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "12"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "15"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "21"));
                            _numeratorCount = _values.size();

                            _values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "8"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "16"));
                            _values.push_back(QCoreApplication::translate("MIDI import operations", "32"));
                            }
                      QString headerName() const override { return QCoreApplication::translate(
                                                                "MIDI import operations", "Time signature"); }
                      bool isForAllTracksOnly() const override { return true; }
                      QVariant value(int /*trackIndex*/) const override
                            {
                            const int numeratorIndex = (int)_opers.timeSigNumerator.value();
                            const int denominatorIndex = (int)_opers.timeSigDenominator.value();

                            return _values[numeratorIndex] + " / " + _values[_numeratorCount + denominatorIndex];
                            }
                      void setValue(const QVariant &value, int /*trackIndex*/) override
                            {
                            const QStringList list = value.toStringList();

                            Q_ASSERT_X(list.size() == 2, "Midi import operations",
                                       "Invalid size of the time signature value list");

                            bool ok = false;
                            _opers.timeSigNumerator.setValue((MidiOperations::TimeSigNumerator)list[0].toInt(&ok));

                            Q_ASSERT_X(ok, "Midi import operations", "Invalid numerator value");

                            ok = false;
                            _opers.timeSigDenominator.setValue((MidiOperations::TimeSigDenominator)list[1].toInt(&ok));

                            Q_ASSERT_X(ok, "Midi import operations", "Invalid denominator value");

                            }
                      QStringList valueList(int /*trackIndex*/) const override
                            {
                            auto list = QStringList("__TimeSig__");
                            list.append("__Numerator__");
                            list.append(QString::number((int)_opers.timeSigNumerator.value()));
                            for (int i = 0; i != _numeratorCount; ++i)
                                  list.append(_values[i]);
                            list.append("__Denominator__");
                            list.append(QString::number((int)_opers.timeSigDenominator.value()));
                            for (int i = _numeratorCount; i != _values.size(); ++i)
                                  list.append(_values[i]);
                            return list;
                            }
                   private:
                      int _numeratorCount;
                      };
                _columns.push_back(std::unique_ptr<Column>(new TimeSig(_trackOpers)));

                //-----------------------------------------------------------------------
                struct MeasureCount2xLess : Column {
                      MeasureCount2xLess(MidiOperations::Opers &opers) : Column(opers)
                            {
                            }
                      QString headerName() const override { return QCoreApplication::translate(
                                              "MIDI import operations", "2x less\nmeasure count"); }
                      bool isForAllTracksOnly() const override { return true; }
                      QVariant value(int /*trackIndex*/) const override
                            {
                            return _opers.measureCount2xLess.value();
                            }
                      void setValue(const QVariant &value, int /*trackIndex*/) override
                            {
                            _opers.measureCount2xLess.setValue(value.toBool());
                            }
                      };
                _columns.push_back(std::unique_ptr<Column>(new MeasureCount2xLess(_trackOpers)));
                }

          //-----------------------------------------------------------------------
          struct Human : Column {
                Human(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Is human\nperformance"); }
                bool isForAllTracksOnly() const override { return true; }
                QVariant value(int /*trackIndex*/) const override
                      {
                      return _opers.isHumanPerformance.value();
                      }
                void setValue(const QVariant &value, int /*trackIndex*/) override
                      {
                      _opers.isHumanPerformance.setValue(value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new Human(_trackOpers)));

          //-----------------------------------------------------------------------
          struct StaffSplit : Column {
                StaffSplit(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Split staff"); }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.doStaffSplit.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.doStaffSplit.setValue(trackIndex, value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new StaffSplit(_trackOpers)));

          //-----------------------------------------------------------------------
          struct ClefChanges : Column {
                ClefChanges(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Clef\nchanges"); }
                bool isEditable(int trackIndex) const override
                      {
                      if (_opers.isDrumTrack.value(trackIndex))
                            return false;
                      const int instrIndex = _opers.msInstrIndex.value(trackIndex);
                      const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
                      const InstrumentTemplate *instr = (trackInstrList.empty())
                                              ? nullptr : trackInstrList[instrIndex];
                      if (instr && !MidiClef::hasGFclefs(instr))
                            return false;
                      return true;
                      }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.changeClef.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.changeClef.setValue(trackIndex, value.toBool());
                      }
                bool isVisible(int trackIndex) const override
                      {
                      return isEditable(trackIndex);
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new ClefChanges(_trackOpers)));

          //-----------------------------------------------------------------------
          struct Simplify : Column {
                Simplify(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Simplify\ndurations"); }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.simplifyDurations.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.simplifyDurations.setValue(trackIndex, value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new Simplify(_trackOpers)));

          //-----------------------------------------------------------------------
          struct ShowStaccato : Column {
                ShowStaccato(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Show\nstaccato"); }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.showStaccato.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.showStaccato.setValue(trackIndex, value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new ShowStaccato(_trackOpers)));

          //-----------------------------------------------------------------------
          struct DottedNotes : Column {
                DottedNotes(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                          "MIDI import operations", "Dotted\nnotes"); }
                QVariant value(int trackIndex) const override
                      {
                      return _opers.useDots.value(trackIndex);
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.useDots.setValue(trackIndex, value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new DottedNotes(_trackOpers)));

          //-----------------------------------------------------------------------
          if (hasTempoText) {
                struct ShowTempoText : Column {
                      ShowTempoText(MidiOperations::Opers &opers) : Column(opers)
                            {
                            }
                      QString headerName() const override { return QCoreApplication::translate(
                                              "MIDI import operations", "Show\ntempo text"); }
                      bool isForAllTracksOnly() const override { return true; }
                      QVariant value(int /*trackIndex*/) const override
                            {
                            return _opers.showTempoText.value();
                            }
                      void setValue(const QVariant &value, int /*trackIndex*/) override
                            {
                            _opers.showTempoText.setValue(value.toBool());
                            }
                      };
                _columns.push_back(std::unique_ptr<Column>(new ShowTempoText(_trackOpers)));
                }

          //-----------------------------------------------------------------------
          if (hasChordNames) {
                struct ShowChordNames : Column {
                      ShowChordNames(MidiOperations::Opers &opers) : Column(opers)
                            {
                            }
                      QString headerName() const override { return QCoreApplication::translate(
                                              "MIDI import operations", "Show\nchord symbols"); }
                      bool isForAllTracksOnly() const override { return true; }
                      QVariant value(int /*trackIndex*/) const override
                            {
                            return _opers.showChordNames.value();
                            }
                      void setValue(const QVariant &value, int /*trackIndex*/) override
                            {
                            _opers.showChordNames.setValue(value.toBool());
                            }
                      };
                _columns.push_back(std::unique_ptr<Column>(new ShowChordNames(_trackOpers)));
                }

          //-----------------------------------------------------------------------
          struct PickupBar : Column {
                PickupBar(MidiOperations::Opers &opers) : Column(opers)
                      {
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                               "MIDI import operations", "Recognize\npickup measure"); }
                bool isForAllTracksOnly() const override { return true; }
                QVariant value(int /*trackIndex*/) const override
                      {
                      return _opers.searchPickupMeasure.value();
                      }
                void setValue(const QVariant &value, int /*trackIndex*/) override
                      {
                      _opers.searchPickupMeasure.setValue(value.toBool());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new PickupBar(_trackOpers)));

          //-----------------------------------------------------------------------
          struct Swing : Column {
                Swing(MidiOperations::Opers &opers) : Column(opers)
                      {
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "None (1:1)"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "Swing (2:1)"));
                      _values.push_back(QCoreApplication::translate("MIDI import operations", "Shuffle (3:1)"));
                      }
                QString headerName() const override { return QCoreApplication::translate(
                                                           "MIDI import operations", "Detect swing"); }
                int width() const override { return 130; }
                QVariant value(int trackIndex) const override
                      {
                      return _values[(int)_opers.swing.value(trackIndex)];
                      }
                void setValue(const QVariant &value, int trackIndex) override
                      {
                      _opers.swing.setValue(trackIndex, (MidiOperations::Swing)value.toInt());
                      }
                };
          _columns.push_back(std::unique_ptr<Column>(new Swing(_trackOpers)));

          endResetModel();
          }

    void TracksModel::clear()
          {
          beginResetModel();
          _trackCount = 0;
          _frozenColCount = 0;
          _trackOpers = MidiOperations::Opers();
          _columns.clear();
          _isAllApplied = true;
          endResetModel();
          }

    const MidiOperations::Opers& TracksModel::trackOpers() const
          {
          return _trackOpers;
          }

    void TracksModel::updateCharset()
          {
          _isAllApplied = false;
          forceAllChanged();
          }

    void TracksModel::notifyAllApplied()
          {
          _isAllApplied = true;
          }

    int TracksModel::rowFromTrackIndex(int trackIndex) const
          {
                      // first row reserved for all tracks if track count > 1
          return (_trackCount > 1) ? trackIndex + 1 : trackIndex;
          }

    int TracksModel::trackIndexFromRow(int row) const
          {
                      // first row reserved for all tracks if track count > 1
                      // return -1 if row is all tracks row
          return (_trackCount > 1) ? row - 1 : row;
          }

    int TracksModel::trackCountForImport() const
          {
          int count = 0;
          for (int i = 0; i != _trackCount; ++i) {
                if (_trackOpers.doImport.value(i))
                      ++count;
                }
          return count;
          }

    int TracksModel::frozenRowCount() const
          {
          if (_trackCount > 1)
                return 1;
          return 0;
          }

    int TracksModel::frozenColCount() const
          {
          return _frozenColCount;
          }

    int TracksModel::rowCount(const QModelIndex &/*parent*/) const
          {
          return (_trackCount > 1) ? _trackCount + 1 : _trackCount;
          }

    int TracksModel::columnCount(const QModelIndex &/*parent*/) const
          {
          return _columns.size();
          }

    bool TracksModel::editableSingleTrack(int trackIndex, int column) const
          {
          return !(trackIndex >= 0 && _trackCount != 1 && _columns[column]->isForAllTracksOnly());
          }

    QVariant TracksModel::data(const QModelIndex &index, int role) const
          {
          if (!index.isValid())
                return QVariant();
          const int trackIndex = trackIndexFromRow(index.row());
          if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
                return QVariant();

          switch (role) {
                case Qt::DisplayRole:
                      if (trackIndex == -1) {       // all tracks
                            if (_columns[index.column()]->isEditable(-1)) {
                                  QVariant value = _columns[index.column()]->value(0);
                                  if (value.type() == QVariant::String) {
                                        value = QVariant();
                                        if (!_columns[index.column()]->isForAllTracksOnly()) {
                                              for (int i = 0; i < _trackCount; ++i) {
                                                    if (!_columns[index.column()]->isVisible(i))
                                                          continue;
                                                    const auto newValue
                                                                = _columns[index.column()]->value(i);
                                                    if (!value.isValid()) {       // value to compare with
                                                          value = newValue;
                                                          continue;
                                                          }
                                                    if (newValue.toString() != value.toString())
                                                          return "...";
                                                    }
                                              }
                                        else {
                                              value = _columns[index.column()]->value(0);
                                              }
                                        if (value.isValid())
                                              return value.toString();
                                        }
                                  }
                            }
                      else if (editableSingleTrack(trackIndex, index.column())
                               && _columns[index.column()]->isVisible(trackIndex)) {
                            QVariant value = _columns[index.column()]->value(trackIndex);
                            if (value.type() == QVariant::String)
                                  return value.toString();
                            }
                      break;
                case Qt::EditRole:
                      if (_columns[index.column()]->isEditable(trackIndex)
                                  && editableSingleTrack(trackIndex, index.column())
                                  && _columns[index.column()]->isVisible(trackIndex)) {
                            const auto list = _columns[index.column()]->valueList(trackIndex);
                            if (!list.isEmpty())
                                  return list;
                            }
                      break;
                case Qt::CheckStateRole:
                      if (trackIndex == -1) {
                            QVariant value = _columns[index.column()]->value(0);
                            if (value.type() == QVariant::Bool) {
                                  value = QVariant();
                                  if (!_columns[index.column()]->isForAllTracksOnly()) {
                                        for (int i = 0; i < _trackCount; ++i) {
                                              if (!_columns[index.column()]->isVisible(i))
                                                    continue;
                                              const auto newValue
                                                          = _columns[index.column()]->value(i);
                                              if (!value.isValid()) {       // value to compare with
                                                    value = newValue;
                                                    continue;
                                                    }
                                              if (newValue.toBool() != value.toBool()) {
                                                    return Qt::PartiallyChecked;
                                                    }
                                              }
                                        }
                                  else {
                                        value = _columns[index.column()]->value(0);
                                        }
                                  if (value.isValid())
                                        return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
                                  }
                            }
                      else if (editableSingleTrack(trackIndex, index.column())
                               && _columns[index.column()]->isVisible(trackIndex)) {
                            QVariant value = _columns[index.column()]->value(trackIndex);
                            if (value.type() == QVariant::Bool)
                                  return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
                            }
                      break;
                case Qt::TextAlignmentRole:
                      return Qt::AlignCenter;
                      break;
                case Qt::ToolTipRole:
                      if (trackIndex != -1 && _columns[index.column()]->isVisible(trackIndex)) {
                            QVariant value = _columns[index.column()]->value(trackIndex);
                            if (value.type() == QVariant::String
                                        && _columns[index.column()]->valueList(trackIndex).empty()) {
                                  MidiOperations::Data &opers = preferences.midiImportOperations;
                                  MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);

                                  return MidiCharset::convertToCharset(value.toString().toStdString());
                                  }
                            }
                      break;
                case Qt::SizeHintRole:
                      return QSize(_columns[index.column()]->width(), -1);
                      break;
                default:
                      break;
                }
          return QVariant();
          }

    Qt::ItemFlags TracksModel::editableFlags(int row, int col) const
          {
          Qt::ItemFlags flags;
          const int trackIndex = trackIndexFromRow(row);

          if (_columns[col]->isVisible(trackIndex)) {
                if (_columns[col]->value(0).type() == QVariant::Bool) {
                      flags |= Qt::ItemIsUserCheckable;
                      }
                else if (_columns[col]->isEditable(trackIndex)) {
                      if (trackIndex == -1) {
                            flags |= Qt::ItemIsEditable;
                            }
                      else if (editableSingleTrack(trackIndex, col)) {
                            QVariant value = _columns[col]->value(0);
                            if (value.type() != QVariant::Bool)       // not checkboxes
                                  flags |= Qt::ItemIsEditable;
                            }
                      }
                }
          return flags;
          }

    Qt::ItemFlags TracksModel::flags(const QModelIndex &index) const
          {
          if (!index.isValid())
                return 0;

          Qt::ItemFlags flags = Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
          const int trackIndex = trackIndexFromRow(index.row());

          if (trackIndex == -1) {       // all tracks row
                if (!_columns[index.column()]->isForAllTracksOnly()
                            && _columns[index.column()]->isEditable(-1)) {
                      for (int i = 0; i < _trackCount; ++i) {
                            const auto newFlags = editableFlags(rowFromTrackIndex(i), index.column());
                            if (newFlags) {
                                  flags |= newFlags;
                                  break;
                                  }
                            }
                      }
                else {
                      flags |= editableFlags(index.row(), index.column());
                      }
                }
          else {
                flags |= editableFlags(index.row(), index.column());
                }

          return flags;
          }

    void TracksModel::forceRowDataChanged(int row)
          {
          const auto begIndex = this->index(row, 0);
          const auto endIndex = this->index(row, columnCount(QModelIndex()));
          emit dataChanged(begIndex, endIndex);
          }

    void TracksModel::forceColumnDataChanged(int col)
          {
          const auto begIndex = this->index(0, col);
          const auto endIndex = this->index(rowCount(QModelIndex()), col);
          emit dataChanged(begIndex, endIndex);
          }

    void TracksModel::forceAllChanged()
          {
          const auto begIndex = this->index(0, 0);
          const auto endIndex = this->index(rowCount(QModelIndex()), columnCount(QModelIndex()));
          emit dataChanged(begIndex, endIndex);
          }

    bool TracksModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/)
          {
          const int trackIndex = trackIndexFromRow(index.row());
          if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
                return false;

          if (trackIndex == -1) {   // all tracks row
                if (!_columns[index.column()]->isForAllTracksOnly()) {
                      for (int i = 0; i != _trackCount; ++i) {
                            if (_columns[index.column()]->isVisible(i))
                                  _columns[index.column()]->setValue(value, i);
                            }
                      forceColumnDataChanged(index.column());
                      }
                else {
                      _columns[index.column()]->setValue(value, 0);
                      }
                }
          else if (editableSingleTrack(trackIndex, index.column())
                   && _columns[index.column()]->isVisible(trackIndex)) {
                _columns[index.column()]->setValue(value, trackIndex);
                emit dataChanged(index, index);
                if (_trackCount > 1)    // update 'all tracks' row
                      forceRowDataChanged(0);
                }

          _isAllApplied = false;
          return true;
          }

    QVariant TracksModel::headerData(int section, Qt::Orientation orientation, int role) const
          {
          if ((orientation == Qt::Vertical && !isRowValid(section))
                      || (orientation == Qt::Horizontal && !isColumnValid(section))) {
                return QVariant();
                }

          if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
                if (!_columns.empty()) {
                      return QCoreApplication::translate("MIDI import: tracks model",
                                          _columns[section]->headerName().toStdString().c_str());
                      }
                }
          else if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
                if (_trackCount > 1) {
                      if (section == 0)
                            return QCoreApplication::translate("MIDI import: tracks model", "All");
                      return section;
                      }
                return section + 1;
                }
          return QVariant();
          }

    bool TracksModel::isTrackIndexValid(int trackIndex) const
          {
          return trackIndex >= -1 && trackIndex < _trackCount;
          }

    bool TracksModel::isRowValid(int row) const
          {
          return row >= 0 && ((_trackCount == 1) ? row < _trackCount : row <= _trackCount);
          }

    bool TracksModel::isColumnValid(int column) const
          {
          return column >= 0 && column < (int)_columns.size();
          }

    } // namespace Ms

↑ V560 A part of conditional expression is always true: !searchTuplets.