1 #include "importmidi_model.h"
2 #include "importmidi_inner.h"
3 #include "importmidi_clef.h"
4 #include "mscore/preferences.h"
5 #include "libmscore/instrtemplate.h"
6 
7 
8 namespace Ms {
9 
10 class TracksModel::Column
11       {
12    public:
Column(MidiOperations::Opers & opers)13       explicit Column(MidiOperations::Opers &opers) : _opers(opers) {}
~Column()14       virtual ~Column() {}
15 
16       virtual QVariant value(int trackIndex) const = 0;
17       virtual void setValue(const QVariant &value, int trackIndex) = 0;
18       virtual QString headerName() const = 0;
isVisible(int) const19       virtual bool isVisible(int /*trackIndex*/) const { return true; }
valueList(int) const20       virtual QStringList valueList(int /*trackIndex*/) const { return _values; }
width() const21       virtual int width() const { return -1; }
isEditable(int) const22       virtual bool isEditable(int /*trackIndex*/) const { return true; }
isForAllTracksOnly() const23       virtual bool isForAllTracksOnly() const { return false; }
24 
25    protected:
26       MidiOperations::Opers &_opers;
27       QStringList _values;
28       };
29 
30 
TracksModel()31 TracksModel::TracksModel()
32       : _trackCount(0)
33       , _frozenColCount(0)
34       , _isAllApplied(true)
35       {
36       }
37 
~TracksModel()38 TracksModel::~TracksModel()
39       {
40       }
41 
reset(const MidiOperations::Opers & opers,const QList<std::string> & lyricsList,int trackCount,const QString & midiFile,bool hasHumanBeats,bool hasTempoText,bool hasChordNames)42 void TracksModel::reset(const MidiOperations::Opers &opers,
43                         const QList<std::string> &lyricsList,
44                         int trackCount,
45                         const QString &midiFile,
46                         bool hasHumanBeats,
47                         bool hasTempoText,
48                         bool hasChordNames)
49       {
50       beginResetModel();
51       _trackOpers = opers;
52       _columns.clear();
53       _trackCount = trackCount;
54       _frozenColCount = 0;
55       _midiFile = midiFile;
56       _isAllApplied = true;
57       if (trackCount == 0)
58             return;
59 
60       //-----------------------------------------------------------------------
61       struct Import : Column {
62             Import(MidiOperations::Opers &opers) : Column(opers) {}
63 
64             QString headerName() const override { return QCoreApplication::translate(
65                                                       "MIDI import operations", "Import"); }
66             QVariant value(int trackIndex) const override
67                   {
68                   return _opers.doImport.value(trackIndex);
69                   }
70             void setValue(const QVariant &value, int trackIndex) override
71                   {
72                   _opers.doImport.setValue(trackIndex, value.toBool());
73                   }
74             };
75       ++_frozenColCount;
76       _columns.push_back(std::unique_ptr<Column>(new Import(_trackOpers)));
77 
78       //-----------------------------------------------------------------------
79       struct Channel : Column {
80             Channel(MidiOperations::Opers &opers) : Column(opers) {}
81             QString headerName() const override { return QCoreApplication::translate(
82                                                       "MIDI import operations", "Channel"); }
83             bool isEditable(int /*trackIndex*/) const override { return false; }
84             QVariant value(int trackIndex) const override
85                   {
86                   return QString::number(_opers.channel.value(trackIndex));
87                   }
88             void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}
89             };
90       ++_frozenColCount;
91       _columns.push_back(std::unique_ptr<Column>(new Channel(_trackOpers)));
92 
93       //-----------------------------------------------------------------------
94       bool hasStaffName = false;
95       for (int i = 0; i != _trackCount; ++i) {
96             if (_trackOpers.staffName.value(i) != "") {
97                   hasStaffName = true;
98                   break;
99                   }
100             }
101       if (hasStaffName) {
102             struct StaffName : Column {
103                   StaffName(MidiOperations::Opers &opers, const QString &midiFile)
104                         : Column(opers), _midiFile(midiFile)
105                         {
106                         }
107                   int width() const override { return 180; }
108                   QString headerName() const override { return QCoreApplication::translate(
109                                                       "MIDI import operations", "Staff name"); }
110                   bool isEditable(int /*trackIndex*/) const override { return false; }
111                   QVariant value(int trackIndex) const override
112                         {
113                         MidiOperations::Data &opers = midiImportOperations;
114                         MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);
115 
116                         return MidiCharset::convertToCharset(_opers.staffName.value(trackIndex));
117                         }
118                   void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}
119 
120                private:
121                   QString _midiFile;
122                   };
123             ++_frozenColCount;
124             _columns.push_back(std::unique_ptr<Column>(new StaffName(_trackOpers, _midiFile)));
125             }
126 
127       //-----------------------------------------------------------------------
128       struct MidiInstrumentName : Column {
129             MidiInstrumentName(MidiOperations::Opers &opers) : Column(opers)
130                   {
131                   }
132             int width() const override { return 130; }
133             QString headerName() const override { return QCoreApplication::translate(
134                                                       "MIDI import operations", "Sound"); }
135             bool isEditable(int /*trackIndex*/) const override { return false; }
136             QVariant value(int trackIndex) const override
137                   {
138                   return _opers.midiInstrName.value(trackIndex);
139                   }
140             void setValue(const QVariant &/*value*/, int /*trackIndex*/) override {}
141             };
142       ++_frozenColCount;
143       _columns.push_back(std::unique_ptr<Column>(new MidiInstrumentName(_trackOpers)));
144 
145       //-----------------------------------------------------------------------
146       bool hasMsInstrument = false;
147       for (int i = 0; i != _trackCount; ++i) {
148             if (!_trackOpers.msInstrList.value(i).empty()) {
149                   hasMsInstrument = true;
150                   break;
151                   }
152             }
153       if (hasMsInstrument) {
154             struct MsInstrument : Column {
155                   MsInstrument(MidiOperations::Opers &opers) : Column(opers)
156                         {
157                         }
158                   int width() const override { return 220; }
159                   QString headerName() const override { return QCoreApplication::translate(
160                                           "MIDI import operations", "MuseScore instrument"); }
161                   bool isEditable(int trackIndex) const override
162                         {
163                         return _opers.msInstrList.value(trackIndex).size() > 1;
164                         }
165                   QVariant value(int trackIndex) const override
166                         {
167                         const int instrIndex = _opers.msInstrIndex.value(trackIndex);
168                         const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
169                         const InstrumentTemplate *instr = (trackInstrList.empty())
170                                     ? nullptr : trackInstrList[instrIndex];
171                         return instrName(instr);
172                         }
173                   void setValue(const QVariant &value, int trackIndex) override
174                         {
175                         _opers.msInstrIndex.setValue(trackIndex, value.toInt());
176                         }
177                   QStringList valueList(int trackIndex) const override
178                         {
179                         auto list = QStringList();
180                         const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
181                         for (const InstrumentTemplate *instr: trackInstrList)
182                               list.append(instrName(instr));
183                         return list;
184                         }
185 
186                private:
187                   static QString instrName(const InstrumentTemplate *instr)
188                         {
189                         if (!instr)
190                               return "-";
191                         if (!instr->trackName.isEmpty())
192                               return instr->trackName;
193                         if (instr->longNames.isEmpty())
194                               return instr->id;
195                         return instr->longNames.front().name();
196                         }
197                   };
198             _columns.push_back(std::unique_ptr<Column>(new MsInstrument(_trackOpers)));
199             }
200 
201       //-----------------------------------------------------------------------
202       if (!lyricsList.isEmpty()) {
203             struct Lyrics : Column {
204                   Lyrics(MidiOperations::Opers &opers,
205                          const QList<std::string> &lyricsList,
206                          const QString &midiFile)
207                         : Column(opers), _lyricsList(lyricsList), _midiFile(midiFile)
208                         {
209                         }
210                   int width() const override { return 185; }
211                   QString headerName() const override { return QCoreApplication::translate(
212                                                       "MIDI import operations", "Lyrics"); }
213                   QVariant value(int trackIndex) const override
214                         {
215                         int index = _opers.lyricTrackIndex.value(trackIndex);
216                         if (index >= 0) {
217                               MidiOperations::Data &opers = midiImportOperations;
218                               MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);
219 
220                               return MidiCharset::convertToCharset(_lyricsList[index]);
221                               }
222                         return "";
223                         }
224                   void setValue(const QVariant &value, int trackIndex) override
225                         {
226                                     // GUI lyrics list always have "" row, so: (index - 1)
227                         _opers.lyricTrackIndex.setValue(trackIndex, value.toInt() - 1);
228                         }
229                   QStringList valueList(int /*trackIndex*/) const override
230                         {
231                         MidiOperations::Data &opers = midiImportOperations;
232                         MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);
233 
234                         auto list = QStringList("");
235                         for (const auto &lyric: _lyricsList)
236                               list.append(MidiCharset::convertToCharset(lyric));
237                         return list;
238                         }
239                private:
240                   QList<std::string> _lyricsList;
241                   QString _midiFile;
242                   };
243             _columns.push_back(std::unique_ptr<Column>(new Lyrics(_trackOpers, lyricsList, _midiFile)));
244             }
245 
246       //-----------------------------------------------------------------------
247       struct QuantValue : Column {
248             QuantValue(MidiOperations::Opers &opers) : Column(opers)
249                   {
250                   _values.push_back(QCoreApplication::translate("MIDI import operations", "Quarter"));
251                   _values.push_back(QCoreApplication::translate("MIDI import operations", "Eighth"));
252                   _values.push_back(QCoreApplication::translate("MIDI import operations", "16th"));
253                   _values.push_back(QCoreApplication::translate("MIDI import operations", "32nd"));
254                   _values.push_back(QCoreApplication::translate("MIDI import operations", "64th"));
255                   _values.push_back(QCoreApplication::translate("MIDI import operations", "128th"));
256                   }
257             QString headerName() const override { return QCoreApplication::translate(
258                                                 "MIDI import operations", "Max. quantization"); }
259             QVariant value(int trackIndex) const override
260                   {
261                   return _values[(int)_opers.quantValue.value(trackIndex)];
262                   }
263             void setValue(const QVariant &value, int trackIndex) override
264                   {
265                   _opers.quantValue.setValue(trackIndex, (MidiOperations::QuantValue)value.toInt());
266                   }
267             };
268       _columns.push_back(std::unique_ptr<Column>(new QuantValue(_trackOpers)));
269 
270       //-----------------------------------------------------------------------
271       struct VoiceCount : Column {
272             VoiceCount(MidiOperations::Opers &opers) : Column(opers)
273                   {
274                   _values.push_back("1");
275                   _values.push_back("2");
276                   _values.push_back("3");
277                   _values.push_back("4");
278                   }
279             QString headerName() const override { return QCoreApplication::translate(
280                                                       "MIDI import operations", "Max. voices"); }
281             QVariant value(int trackIndex) const override
282                   {
283                   return _values[(int)_opers.maxVoiceCount.value(trackIndex)];
284                   }
285             void setValue(const QVariant &value, int trackIndex) override
286                   {
287                   _opers.maxVoiceCount.setValue(trackIndex, (MidiOperations::VoiceCount)value.toInt());
288                   }
289             bool isVisible(int trackIndex) const override
290                   {
291                   if (_opers.isDrumTrack.value(trackIndex))
292                         return false;
293                   return true;
294                   }
295             };
296       _columns.push_back(std::unique_ptr<Column>(new VoiceCount(_trackOpers)));
297 
298       //-----------------------------------------------------------------------
299       struct Tuplets : Column {
300             Tuplets(MidiOperations::Opers &opers, int trackCount)
301                   : Column(opers), _trackCount(trackCount)
302                   {
303                   _values.push_back(QCoreApplication::translate("MIDI import operations", "2-plets"));
304                   _values.push_back(QCoreApplication::translate("MIDI import operations", "3-plets"));
305                   _values.push_back(QCoreApplication::translate("MIDI import operations", "4-plets"));
306                   _values.push_back(QCoreApplication::translate("MIDI import operations", "5-plets"));
307                   _values.push_back(QCoreApplication::translate("MIDI import operations", "7-plets"));
308                   _values.push_back(QCoreApplication::translate("MIDI import operations", "9-plets"));
309                   }
310             int width() const override { return 140; }
311             QString headerName() const override { return QCoreApplication::translate(
312                                                       "MIDI import operations", "Tuplets"); }
313             QVariant value(int trackIndex) const override
314                   {
315                   QString val;
316                   if (_opers.search2plets.value(trackIndex)) {
317                         if (val != "")
318                               val += ", ";
319                         val += "2";
320                         }
321                   if (_opers.search3plets.value(trackIndex)) {
322                         if (val != "")
323                               val += ", ";
324                         val += "3";
325                         }
326                   if (_opers.search4plets.value(trackIndex)) {
327                         if (val != "")
328                               val += ", ";
329                         val += "4";
330                         }
331                   if (_opers.search5plets.value(trackIndex)) {
332                         if (val != "")
333                               val += ", ";
334                         val += "5";
335                         }
336                   if (_opers.search7plets.value(trackIndex)) {
337                         if (val != "")
338                               val += ", ";
339                         val += "7";
340                         }
341                   if (_opers.search9plets.value(trackIndex)) {
342                         if (val != "")
343                               val += ", ";
344                         val += "9";
345                         }
346                   return val;
347                   }
348             void setValue(const QVariant &value, int trackIndex) override
349                   {
350                   const QStringList list = value.toStringList();
351 
352                   Q_ASSERT_X(list.size() > 5, "Midi import operations",
353                              "Invalid size of the tuplets value list");
354 
355                   bool searchTuplets = false;
356                   if (list[0] != "undefined") {
357                         const bool doSearch = (list[0] == "true");
358                         _opers.search2plets.setValue(trackIndex, doSearch);
359                         if (!searchTuplets && doSearch)
360                               searchTuplets = true;
361                         }
362                   if (list[1] != "undefined") {
363                         const bool doSearch = (list[1] == "true");
364                         _opers.search3plets.setValue(trackIndex, doSearch);
365                         if (!searchTuplets && doSearch)
366                               searchTuplets = true;
367                         }
368                   if (list[2] != "undefined") {
369                         const bool doSearch = (list[2] == "true");
370                         _opers.search4plets.setValue(trackIndex, doSearch);
371                         if (!searchTuplets && doSearch)
372                               searchTuplets = true;
373                         }
374                   if (list[3] != "undefined") {
375                         const bool doSearch = (list[3] == "true");
376                         _opers.search5plets.setValue(trackIndex, doSearch);
377                         if (!searchTuplets && doSearch)
378                               searchTuplets = true;
379                         }
380                   if (list[4] != "undefined") {
381                         const bool doSearch = (list[4] == "true");
382                         _opers.search7plets.setValue(trackIndex, doSearch);
383                         if (!searchTuplets && doSearch)
384                               searchTuplets = true;
385                         }
386                   if (list[5] != "undefined") {
387                         const bool doSearch = (list[5] == "true");
388                         _opers.search9plets.setValue(trackIndex, doSearch);
389                         if (!searchTuplets && doSearch)
390                               searchTuplets = true;
391                         }
392                   _opers.searchTuplets.setValue(trackIndex, searchTuplets);
393                   }
394             QStringList valueList(int trackIndex) const override
395                   {
396                   auto list = QStringList("__MultiValue__");
397 
398                   list.append(_values[0]);
399                   list.append(checkBoxValue(trackIndex, _opers.search2plets));
400                   list.append(_values[1]);
401                   list.append(checkBoxValue(trackIndex, _opers.search3plets));
402                   list.append(_values[2]);
403                   list.append(checkBoxValue(trackIndex, _opers.search4plets));
404                   list.append(_values[3]);
405                   list.append(checkBoxValue(trackIndex, _opers.search5plets));
406                   list.append(_values[4]);
407                   list.append(checkBoxValue(trackIndex, _opers.search7plets));
408                   list.append(_values[5]);
409                   list.append(checkBoxValue(trackIndex, _opers.search9plets));
410 
411                   return list;
412                   }
413 
414          private:
415             QString checkBoxValue(int trackIndex,
416                                   const MidiOperations::TrackOp<bool> &operation) const
417                   {
418                   if (trackIndex == -1) {       // symbolizes all tracks
419                         const bool firstValue = operation.value(0);
420                         for (int i = 1; i < _trackCount; ++i) {
421                               if (operation.value(i) != firstValue)
422                                     return "undefined";
423                               }
424                         trackIndex = 0;   // to pick the first track value on return
425                         }
426                   return operation.value(trackIndex) ? "true" : "false";
427                   }
428 
429             int _trackCount;
430             };
431       _columns.push_back(std::unique_ptr<Column>(new Tuplets(_trackOpers, _trackCount)));
432 
433       //-----------------------------------------------------------------------
434       if (hasHumanBeats) {
435             struct TimeSig : Column {
436                   TimeSig(MidiOperations::Opers &opers) : Column(opers)
437                         {
438                         _values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
439                         _values.push_back(QCoreApplication::translate("MIDI import operations", "3"));
440                         _values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
441                         _values.push_back(QCoreApplication::translate("MIDI import operations", "5"));
442                         _values.push_back(QCoreApplication::translate("MIDI import operations", "6"));
443                         _values.push_back(QCoreApplication::translate("MIDI import operations", "7"));
444                         _values.push_back(QCoreApplication::translate("MIDI import operations", "9"));
445                         _values.push_back(QCoreApplication::translate("MIDI import operations", "12"));
446                         _values.push_back(QCoreApplication::translate("MIDI import operations", "15"));
447                         _values.push_back(QCoreApplication::translate("MIDI import operations", "21"));
448                         _numeratorCount = _values.size();
449 
450                         _values.push_back(QCoreApplication::translate("MIDI import operations", "2"));
451                         _values.push_back(QCoreApplication::translate("MIDI import operations", "4"));
452                         _values.push_back(QCoreApplication::translate("MIDI import operations", "8"));
453                         _values.push_back(QCoreApplication::translate("MIDI import operations", "16"));
454                         _values.push_back(QCoreApplication::translate("MIDI import operations", "32"));
455                         }
456                   QString headerName() const override { return QCoreApplication::translate(
457                                                             "MIDI import operations", "Time signature"); }
458                   bool isForAllTracksOnly() const override { return true; }
459                   QVariant value(int /*trackIndex*/) const override
460                         {
461                         const int numeratorIndex = (int)_opers.timeSigNumerator.value();
462                         const int denominatorIndex = (int)_opers.timeSigDenominator.value();
463 
464                         return _values[numeratorIndex] + " / " + _values[_numeratorCount + denominatorIndex];
465                         }
466                   void setValue(const QVariant &value, int /*trackIndex*/) override
467                         {
468                         const QStringList list = value.toStringList();
469 
470                         Q_ASSERT_X(list.size() == 2, "Midi import operations",
471                                    "Invalid size of the time signature value list");
472 
473                         bool ok = false;
474                         _opers.timeSigNumerator.setValue((MidiOperations::TimeSigNumerator)list[0].toInt(&ok));
475 
476                         Q_ASSERT_X(ok, "Midi import operations", "Invalid numerator value");
477 
478                         ok = false;
479                         _opers.timeSigDenominator.setValue((MidiOperations::TimeSigDenominator)list[1].toInt(&ok));
480 
481                         Q_ASSERT_X(ok, "Midi import operations", "Invalid denominator value");
482 
483                         }
484                   QStringList valueList(int /*trackIndex*/) const override
485                         {
486                         auto list = QStringList("__TimeSig__");
487                         list.append("__Numerator__");
488                         list.append(QString::number((int)_opers.timeSigNumerator.value()));
489                         for (int i = 0; i != _numeratorCount; ++i)
490                               list.append(_values[i]);
491                         list.append("__Denominator__");
492                         list.append(QString::number((int)_opers.timeSigDenominator.value()));
493                         for (int i = _numeratorCount; i != _values.size(); ++i)
494                               list.append(_values[i]);
495                         return list;
496                         }
497                private:
498                   int _numeratorCount;
499                   };
500             _columns.push_back(std::unique_ptr<Column>(new TimeSig(_trackOpers)));
501 
502             //-----------------------------------------------------------------------
503             struct MeasureCount2xLess : Column {
504                   MeasureCount2xLess(MidiOperations::Opers &opers) : Column(opers)
505                         {
506                         }
507                   QString headerName() const override { return QCoreApplication::translate(
508                                           "MIDI import operations", "Halving the\nmeasure count"); }
509                   bool isForAllTracksOnly() const override { return true; }
510                   QVariant value(int /*trackIndex*/) const override
511                         {
512                         return _opers.measureCount2xLess.value();
513                         }
514                   void setValue(const QVariant &value, int /*trackIndex*/) override
515                         {
516                         _opers.measureCount2xLess.setValue(value.toBool());
517                         }
518                   };
519             _columns.push_back(std::unique_ptr<Column>(new MeasureCount2xLess(_trackOpers)));
520             }
521 
522       //-----------------------------------------------------------------------
523       struct Human : Column {
524             Human(MidiOperations::Opers &opers) : Column(opers)
525                   {
526                   }
527             QString headerName() const override { return QCoreApplication::translate(
528                                                       "MIDI import operations", "Is human\nperformance"); }
529             bool isForAllTracksOnly() const override { return true; }
530             QVariant value(int /*trackIndex*/) const override
531                   {
532                   return _opers.isHumanPerformance.value();
533                   }
534             void setValue(const QVariant &value, int /*trackIndex*/) override
535                   {
536                   _opers.isHumanPerformance.setValue(value.toBool());
537                   }
538             };
539       _columns.push_back(std::unique_ptr<Column>(new Human(_trackOpers)));
540 
541       //-----------------------------------------------------------------------
542       struct StaffSplit : Column {
543             StaffSplit(MidiOperations::Opers &opers) : Column(opers)
544                   {
545                   }
546             QString headerName() const override { return QCoreApplication::translate(
547                                                       "MIDI import operations", "Split staff"); }
548             QVariant value(int trackIndex) const override
549                   {
550                   return _opers.doStaffSplit.value(trackIndex);
551                   }
552             void setValue(const QVariant &value, int trackIndex) override
553                   {
554                   _opers.doStaffSplit.setValue(trackIndex, value.toBool());
555                   }
556             };
557       _columns.push_back(std::unique_ptr<Column>(new StaffSplit(_trackOpers)));
558 
559       //-----------------------------------------------------------------------
560       struct ClefChanges : Column {
561             ClefChanges(MidiOperations::Opers &opers) : Column(opers)
562                   {
563                   }
564             QString headerName() const override { return QCoreApplication::translate(
565                                                       "MIDI import operations", "Clef\nchanges"); }
566             bool isEditable(int trackIndex) const override
567                   {
568                   if (_opers.isDrumTrack.value(trackIndex))
569                         return false;
570                   const int instrIndex = _opers.msInstrIndex.value(trackIndex);
571                   const auto &trackInstrList = _opers.msInstrList.value(trackIndex);
572                   const InstrumentTemplate *instr = (trackInstrList.empty())
573                                           ? nullptr : trackInstrList[instrIndex];
574                   if (instr && !MidiClef::hasGFclefs(instr))
575                         return false;
576                   return true;
577                   }
578             QVariant value(int trackIndex) const override
579                   {
580                   return _opers.changeClef.value(trackIndex);
581                   }
582             void setValue(const QVariant &value, int trackIndex) override
583                   {
584                   _opers.changeClef.setValue(trackIndex, value.toBool());
585                   }
586             bool isVisible(int trackIndex) const override
587                   {
588                   return isEditable(trackIndex);
589                   }
590             };
591       _columns.push_back(std::unique_ptr<Column>(new ClefChanges(_trackOpers)));
592 
593       //-----------------------------------------------------------------------
594       struct Simplify : Column {
595             Simplify(MidiOperations::Opers &opers) : Column(opers)
596                   {
597                   }
598             QString headerName() const override { return QCoreApplication::translate(
599                                                       "MIDI import operations", "Simplify\ndurations"); }
600             QVariant value(int trackIndex) const override
601                   {
602                   return _opers.simplifyDurations.value(trackIndex);
603                   }
604             void setValue(const QVariant &value, int trackIndex) override
605                   {
606                   _opers.simplifyDurations.setValue(trackIndex, value.toBool());
607                   }
608             };
609       _columns.push_back(std::unique_ptr<Column>(new Simplify(_trackOpers)));
610 
611       //-----------------------------------------------------------------------
612       struct ShowStaccato : Column {
613             ShowStaccato(MidiOperations::Opers &opers) : Column(opers)
614                   {
615                   }
616             QString headerName() const override { return QCoreApplication::translate(
617                                                       "MIDI import operations", "Show\nstaccato"); }
618             QVariant value(int trackIndex) const override
619                   {
620                   return _opers.showStaccato.value(trackIndex);
621                   }
622             void setValue(const QVariant &value, int trackIndex) override
623                   {
624                   _opers.showStaccato.setValue(trackIndex, value.toBool());
625                   }
626             };
627       _columns.push_back(std::unique_ptr<Column>(new ShowStaccato(_trackOpers)));
628 
629       //-----------------------------------------------------------------------
630       struct DottedNotes : Column {
631             DottedNotes(MidiOperations::Opers &opers) : Column(opers)
632                   {
633                   }
634             QString headerName() const override { return QCoreApplication::translate(
635                                                       "MIDI import operations", "Dotted\nnotes"); }
636             QVariant value(int trackIndex) const override
637                   {
638                   return _opers.useDots.value(trackIndex);
639                   }
640             void setValue(const QVariant &value, int trackIndex) override
641                   {
642                   _opers.useDots.setValue(trackIndex, value.toBool());
643                   }
644             };
645       _columns.push_back(std::unique_ptr<Column>(new DottedNotes(_trackOpers)));
646 
647       //-----------------------------------------------------------------------
648       if (hasTempoText) {
649             struct ShowTempoText : Column {
650                   ShowTempoText(MidiOperations::Opers &opers) : Column(opers)
651                         {
652                         }
653                   QString headerName() const override { return QCoreApplication::translate(
654                                           "MIDI import operations", "Show\ntempo text"); }
655                   bool isForAllTracksOnly() const override { return true; }
656                   QVariant value(int /*trackIndex*/) const override
657                         {
658                         return _opers.showTempoText.value();
659                         }
660                   void setValue(const QVariant &value, int /*trackIndex*/) override
661                         {
662                         _opers.showTempoText.setValue(value.toBool());
663                         }
664                   };
665             _columns.push_back(std::unique_ptr<Column>(new ShowTempoText(_trackOpers)));
666             }
667 
668       //-----------------------------------------------------------------------
669       if (hasChordNames) {
670             struct ShowChordNames : Column {
671                   ShowChordNames(MidiOperations::Opers &opers) : Column(opers)
672                         {
673                         }
674                   QString headerName() const override { return QCoreApplication::translate(
675                                           "MIDI import operations", "Show\nchord symbols"); }
676                   bool isForAllTracksOnly() const override { return true; }
677                   QVariant value(int /*trackIndex*/) const override
678                         {
679                         return _opers.showChordNames.value();
680                         }
681                   void setValue(const QVariant &value, int /*trackIndex*/) override
682                         {
683                         _opers.showChordNames.setValue(value.toBool());
684                         }
685                   };
686             _columns.push_back(std::unique_ptr<Column>(new ShowChordNames(_trackOpers)));
687             }
688 
689       //-----------------------------------------------------------------------
690       struct PickupBar : Column {
691             PickupBar(MidiOperations::Opers &opers) : Column(opers)
692                   {
693                   }
694             QString headerName() const override { return QCoreApplication::translate(
695                                            "MIDI import operations", "Recognize\npickup measure"); }
696             bool isForAllTracksOnly() const override { return true; }
697             QVariant value(int /*trackIndex*/) const override
698                   {
699                   return _opers.searchPickupMeasure.value();
700                   }
701             void setValue(const QVariant &value, int /*trackIndex*/) override
702                   {
703                   _opers.searchPickupMeasure.setValue(value.toBool());
704                   }
705             };
706       _columns.push_back(std::unique_ptr<Column>(new PickupBar(_trackOpers)));
707 
708       //-----------------------------------------------------------------------
709       struct Swing : Column {
710             Swing(MidiOperations::Opers &opers) : Column(opers)
711                   {
712                   _values.push_back(QCoreApplication::translate("MIDI import operations", "None (1:1)"));
713                   _values.push_back(QCoreApplication::translate("MIDI import operations", "Swing (2:1)"));
714                   _values.push_back(QCoreApplication::translate("MIDI import operations", "Shuffle (3:1)"));
715                   }
716             QString headerName() const override { return QCoreApplication::translate(
717                                                        "MIDI import operations", "Detect swing"); }
718             int width() const override { return 130; }
719             QVariant value(int trackIndex) const override
720                   {
721                   return _values[(int)_opers.swing.value(trackIndex)];
722                   }
723             void setValue(const QVariant &value, int trackIndex) override
724                   {
725                   _opers.swing.setValue(trackIndex, (MidiOperations::Swing)value.toInt());
726                   }
727             };
728       _columns.push_back(std::unique_ptr<Column>(new Swing(_trackOpers)));
729 
730       endResetModel();
731       }
732 
clear()733 void TracksModel::clear()
734       {
735       beginResetModel();
736       _trackCount = 0;
737       _frozenColCount = 0;
738       _trackOpers = MidiOperations::Opers();
739       _columns.clear();
740       _isAllApplied = true;
741       endResetModel();
742       }
743 
trackOpers() const744 const MidiOperations::Opers& TracksModel::trackOpers() const
745       {
746       return _trackOpers;
747       }
748 
updateCharset()749 void TracksModel::updateCharset()
750       {
751       _isAllApplied = false;
752       forceAllChanged();
753       }
754 
notifyAllApplied()755 void TracksModel::notifyAllApplied()
756       {
757       _isAllApplied = true;
758       }
759 
rowFromTrackIndex(int trackIndex) const760 int TracksModel::rowFromTrackIndex(int trackIndex) const
761       {
762                   // first row reserved for all tracks if track count > 1
763       return (_trackCount > 1) ? trackIndex + 1 : trackIndex;
764       }
765 
trackIndexFromRow(int row) const766 int TracksModel::trackIndexFromRow(int row) const
767       {
768                   // first row reserved for all tracks if track count > 1
769                   // return -1 if row is all tracks row
770       return (_trackCount > 1) ? row - 1 : row;
771       }
772 
trackCountForImport() const773 int TracksModel::trackCountForImport() const
774       {
775       int count = 0;
776       for (int i = 0; i != _trackCount; ++i) {
777             if (_trackOpers.doImport.value(i))
778                   ++count;
779             }
780       return count;
781       }
782 
frozenRowCount() const783 int TracksModel::frozenRowCount() const
784       {
785       if (_trackCount > 1)
786             return 1;
787       return 0;
788       }
789 
frozenColCount() const790 int TracksModel::frozenColCount() const
791       {
792       return _frozenColCount;
793       }
794 
rowCount(const QModelIndex &) const795 int TracksModel::rowCount(const QModelIndex &/*parent*/) const
796       {
797       return (_trackCount > 1) ? _trackCount + 1 : _trackCount;
798       }
799 
columnCount(const QModelIndex &) const800 int TracksModel::columnCount(const QModelIndex &/*parent*/) const
801       {
802       return int(_columns.size());
803       }
804 
editableSingleTrack(int trackIndex,int column) const805 bool TracksModel::editableSingleTrack(int trackIndex, int column) const
806       {
807       return !(trackIndex >= 0 && _trackCount != 1 && _columns[column]->isForAllTracksOnly());
808       }
809 
data(const QModelIndex & index,int role) const810 QVariant TracksModel::data(const QModelIndex &index, int role) const
811       {
812       if (!index.isValid())
813             return QVariant();
814       const int trackIndex = trackIndexFromRow(index.row());
815       if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
816             return QVariant();
817 
818       switch (role) {
819             case Qt::DisplayRole:
820                   if (trackIndex == -1) {       // all tracks
821                         if (_columns[index.column()]->isEditable(-1)) {
822                               QVariant value = _columns[index.column()]->value(0);
823                               if (value.type() == QVariant::String) {
824                                     value = QVariant();
825                                     if (!_columns[index.column()]->isForAllTracksOnly()) {
826                                           for (int i = 0; i < _trackCount; ++i) {
827                                                 if (!_columns[index.column()]->isVisible(i))
828                                                       continue;
829                                                 const auto newValue
830                                                             = _columns[index.column()]->value(i);
831                                                 if (!value.isValid()) {       // value to compare with
832                                                       value = newValue;
833                                                       continue;
834                                                       }
835                                                 if (newValue.toString() != value.toString())
836                                                       return "…";
837                                                 }
838                                           }
839                                     else {
840                                           value = _columns[index.column()]->value(0);
841                                           }
842                                     if (value.isValid())
843                                           return value.toString();
844                                     }
845                               }
846                         }
847                   else if (editableSingleTrack(trackIndex, index.column())
848                            && _columns[index.column()]->isVisible(trackIndex)) {
849                         QVariant value = _columns[index.column()]->value(trackIndex);
850                         if (value.type() == QVariant::String)
851                               return value.toString();
852                         }
853                   break;
854             case Qt::EditRole:
855                   if (_columns[index.column()]->isEditable(trackIndex)
856                               && editableSingleTrack(trackIndex, index.column())
857                               && _columns[index.column()]->isVisible(trackIndex)) {
858                         const auto list = _columns[index.column()]->valueList(trackIndex);
859                         if (!list.isEmpty())
860                               return list;
861                         }
862                   break;
863             case Qt::CheckStateRole:
864                   if (trackIndex == -1) {
865                         QVariant value = _columns[index.column()]->value(0);
866                         if (value.type() == QVariant::Bool) {
867                               value = QVariant();
868                               if (!_columns[index.column()]->isForAllTracksOnly()) {
869                                     for (int i = 0; i < _trackCount; ++i) {
870                                           if (!_columns[index.column()]->isVisible(i))
871                                                 continue;
872                                           const auto newValue
873                                                       = _columns[index.column()]->value(i);
874                                           if (!value.isValid()) {       // value to compare with
875                                                 value = newValue;
876                                                 continue;
877                                                 }
878                                           if (newValue.toBool() != value.toBool()) {
879                                                 return Qt::PartiallyChecked;
880                                                 }
881                                           }
882                                     }
883                               else {
884                                     value = _columns[index.column()]->value(0);
885                                     }
886                               if (value.isValid())
887                                     return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
888                               }
889                         }
890                   else if (editableSingleTrack(trackIndex, index.column())
891                            && _columns[index.column()]->isVisible(trackIndex)) {
892                         QVariant value = _columns[index.column()]->value(trackIndex);
893                         if (value.type() == QVariant::Bool)
894                               return (value.toBool()) ? Qt::Checked : Qt::Unchecked;
895                         }
896                   break;
897             case Qt::TextAlignmentRole:
898                   return Qt::AlignCenter;
899                   break;
900             case Qt::ToolTipRole:
901                   if (trackIndex != -1 && _columns[index.column()]->isVisible(trackIndex)) {
902                         QVariant value = _columns[index.column()]->value(trackIndex);
903                         if (value.type() == QVariant::String
904                                     && _columns[index.column()]->valueList(trackIndex).empty()) {
905                               MidiOperations::Data &opers = midiImportOperations;
906                               MidiOperations::CurrentMidiFileSetter setCurrentMidiFile(opers, _midiFile);
907 
908                               return MidiCharset::convertToCharset(value.toString().toUtf8().constData());
909                               }
910                         }
911                   break;
912             case Qt::SizeHintRole:
913                   return QSize(_columns[index.column()]->width(), -1);
914                   break;
915             default:
916                   break;
917             }
918       return QVariant();
919       }
920 
editableFlags(int row,int col) const921 Qt::ItemFlags TracksModel::editableFlags(int row, int col) const
922       {
923       Qt::ItemFlags flags;
924       const int trackIndex = trackIndexFromRow(row);
925 
926       if (_columns[col]->isVisible(trackIndex)) {
927             if (_columns[col]->value(0).type() == QVariant::Bool) {
928                   flags |= Qt::ItemIsUserCheckable;
929                   }
930             else if (_columns[col]->isEditable(trackIndex)) {
931                   if (trackIndex == -1) {
932                         flags |= Qt::ItemIsEditable;
933                         }
934                   else if (editableSingleTrack(trackIndex, col)) {
935                         QVariant value = _columns[col]->value(0);
936                         if (value.type() != QVariant::Bool)       // not checkboxes
937                               flags |= Qt::ItemIsEditable;
938                         }
939                   }
940             }
941       return flags;
942       }
943 
flags(const QModelIndex & index) const944 Qt::ItemFlags TracksModel::flags(const QModelIndex &index) const
945       {
946       if (!index.isValid())
947             return {};
948 
949       Qt::ItemFlags flags = Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
950       const int trackIndex = trackIndexFromRow(index.row());
951 
952       if (trackIndex == -1) {       // all tracks row
953             if (!_columns[index.column()]->isForAllTracksOnly()
954                         && _columns[index.column()]->isEditable(-1)) {
955                   for (int i = 0; i < _trackCount; ++i) {
956                         const auto newFlags = editableFlags(rowFromTrackIndex(i), index.column());
957                         if (newFlags) {
958                               flags |= newFlags;
959                               break;
960                               }
961                         }
962                   }
963             else {
964                   flags |= editableFlags(index.row(), index.column());
965                   }
966             }
967       else {
968             flags |= editableFlags(index.row(), index.column());
969             }
970 
971       return flags;
972       }
973 
forceRowDataChanged(int row)974 void TracksModel::forceRowDataChanged(int row)
975       {
976       const auto begIndex = this->index(row, 0);
977       const auto endIndex = this->index(row, columnCount(QModelIndex()));
978       emit dataChanged(begIndex, endIndex);
979       }
980 
forceColumnDataChanged(int col)981 void TracksModel::forceColumnDataChanged(int col)
982       {
983       const auto begIndex = this->index(0, col);
984       const auto endIndex = this->index(rowCount(QModelIndex()), col);
985       emit dataChanged(begIndex, endIndex);
986       }
987 
forceAllChanged()988 void TracksModel::forceAllChanged()
989       {
990       const auto begIndex = this->index(0, 0);
991       const auto endIndex = this->index(rowCount(QModelIndex()), columnCount(QModelIndex()));
992       emit dataChanged(begIndex, endIndex);
993       }
994 
setData(const QModelIndex & index,const QVariant & value,int)995 bool TracksModel::setData(const QModelIndex &index, const QVariant &value, int /*role*/)
996       {
997       const int trackIndex = trackIndexFromRow(index.row());
998       if (!isTrackIndexValid(trackIndex) || !isColumnValid(index.column()))
999             return false;
1000 
1001       if (trackIndex == -1) {   // all tracks row
1002             if (!_columns[index.column()]->isForAllTracksOnly()) {
1003                   for (int i = 0; i != _trackCount; ++i) {
1004                         if (_columns[index.column()]->isVisible(i))
1005                               _columns[index.column()]->setValue(value, i);
1006                         }
1007                   forceColumnDataChanged(index.column());
1008                   }
1009             else {
1010                   _columns[index.column()]->setValue(value, 0);
1011                   }
1012             }
1013       else if (editableSingleTrack(trackIndex, index.column())
1014                && _columns[index.column()]->isVisible(trackIndex)) {
1015             _columns[index.column()]->setValue(value, trackIndex);
1016             emit dataChanged(index, index);
1017             if (_trackCount > 1)    // update 'all tracks' row
1018                   forceRowDataChanged(0);
1019             }
1020 
1021       _isAllApplied = false;
1022       return true;
1023       }
1024 
headerData(int section,Qt::Orientation orientation,int role) const1025 QVariant TracksModel::headerData(int section, Qt::Orientation orientation, int role) const
1026       {
1027       if ((orientation == Qt::Vertical && !isRowValid(section))
1028                   || (orientation == Qt::Horizontal && !isColumnValid(section))) {
1029             return QVariant();
1030             }
1031 
1032       if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
1033             if (!_columns.empty()) {
1034                   return QCoreApplication::translate("MIDI import: tracks model",
1035                                       _columns[section]->headerName().toUtf8().constData());
1036                   }
1037             }
1038       else if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
1039             if (_trackCount > 1) {
1040                   if (section == 0)
1041                         return QCoreApplication::translate("MIDI import: tracks model", "All");
1042                   return section;
1043                   }
1044             return section + 1;
1045             }
1046       return QVariant();
1047       }
1048 
isTrackIndexValid(int trackIndex) const1049 bool TracksModel::isTrackIndexValid(int trackIndex) const
1050       {
1051       return trackIndex >= -1 && trackIndex < _trackCount;
1052       }
1053 
isRowValid(int row) const1054 bool TracksModel::isRowValid(int row) const
1055       {
1056       return row >= 0 && ((_trackCount == 1) ? row < _trackCount : row <= _trackCount);
1057       }
1058 
isColumnValid(int column) const1059 bool TracksModel::isColumnValid(int column) const
1060       {
1061       return column >= 0 && column < (int)_columns.size();
1062       }
1063 
1064 } // namespace Ms
1065