1 /***************************************************************************
2 **                                                                        **
3 **  Polyphone, a soundfont editor                                         **
4 **  Copyright (C) 2013-2020 Davy Triponney                                **
5 **                2014      Andrea Celani                                 **
6 **                                                                        **
7 **  This program is free software: you can redistribute it and/or modify  **
8 **  it under the terms of the GNU General Public License as published by  **
9 **  the Free Software Foundation, either version 3 of the License, or     **
10 **  (at your option) any later version.                                   **
11 **                                                                        **
12 **  This program is distributed in the hope that it will be useful,       **
13 **  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
14 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          **
15 **  GNU General Public License for more details.                          **
16 **                                                                        **
17 **  You should have received a copy of the GNU General Public License     **
18 **  along with this program. If not, see http://www.gnu.org/licenses/.    **
19 **                                                                        **
20 ****************************************************************************
21 **           Author: Davy Triponney                                       **
22 **  Website/Contact: https://www.polyphone-soundfonts.com                 **
23 **             Date: 01.01.2013                                           **
24 ***************************************************************************/
25 
26 #include "pagetable.h"
27 #include "contextmanager.h"
28 #include "dialogselection.h"
29 #include "graphicsviewrange.h"
30 #include "envelopeditor.h"
31 #include "utils.h"
32 #include <QScrollBar>
33 #include <QMenu>
34 #include "pianokeybdcustom.h"
35 #include "modulatoreditor.h"
36 
PageTable(TypePage typePage,QWidget * parent)37 PageTable::PageTable(TypePage typePage, QWidget *parent) : Page(parent, typePage, typePage == PAGE_INST ? "page:inst" : "page:prst"),
38     _table(nullptr)
39 {
40     connect(ContextManager::configuration(), SIGNAL(divisionSortChanged()), this, SLOT(divisionSortChanged()));
41 }
42 
afficheTable(bool justSelection)43 void PageTable::afficheTable(bool justSelection)
44 {
45     int posV = _table->verticalScrollBar()->value();
46     _sortType = ContextManager::configuration()->getValue(ConfManager::SECTION_DISPLAY, "division_sort", 0).toInt();
47 
48     if (!justSelection)
49     {
50         // Clear the table and repopulate it
51         _table->blockSignals(true);
52         _table->clear();
53         _table->blockSignals(false);
54 
55         if (!_currentParentIds.isEmpty())
56         {
57             // Global division(s)
58             addGlobal(_currentParentIds);
59 
60             // Child divisions
61             if (_currentParentIds.count() == 1)
62                 addDivisions(_currentParentIds.first());
63 
64             // Table style
65             formatTable(_currentParentIds.count() > 1);
66         }
67     }
68 
69     // Mods
70     _modulatorEditor->setIds(_currentIds);
71     this->displayModInTable();
72 
73     // Fin de la préparation
74     this->reselect();
75     _table->verticalScrollBar()->setValue(posV);
76 }
77 
addGlobal(IdList listIds)78 void PageTable::addGlobal(IdList listIds)
79 {
80     bool multiGlobal = listIds.count() > 1;
81     int nbGlobal = 0;
82 
83     foreach (EltID id, listIds)
84     {
85         AttributeValue genValTmp;
86         int offsetStart = 0;
87         bool offsetStartDefined = false;
88         int offsetEnd = 0;
89         bool offsetEndDefined = false;
90         int offsetStartLoop = 0;
91         bool offsetStartLoopDefined = false;
92         int offsetEndLoop = 0;
93         bool offsetEndLoopDefined = false;
94         int row;
95 
96         // Column number
97         int numCol = 0;
98         for (int i = 0; i < nbGlobal; i++)
99         {
100             if (Utils::naturalOrder(_sf2->getQstr(id, champ_nameSort), _sf2->getQstr(_table->getID(i), champ_nameSort)) > 0)
101                 numCol++;
102             else
103                 break;
104         }
105 
106         _table->addColumn(numCol, multiGlobal ? _sf2->getQstr(id, champ_name) : tr("Global"), id);
107         nbGlobal++;
108         EltID idGen = id;
109         idGen.typeElement = this->contenantGen;
110         idGen.indexElt2 = 0;
111         foreach (int i, _sf2->getSiblings(idGen))
112         {
113             genValTmp = _sf2->get(id, static_cast<AttributeType>(i));
114             switch (i)
115             {
116             case champ_startAddrsOffset:
117                 offsetStartDefined = true;
118                 offsetStart += genValTmp.shValue;
119                 break;
120             case champ_startAddrsCoarseOffset:
121                 offsetStartDefined = true;
122                 offsetStart += 32768 * genValTmp.shValue;
123                 break;
124             case champ_endAddrsOffset:
125                 offsetEndDefined = true;
126                 offsetEnd += genValTmp.shValue;
127                 break;
128             case champ_endAddrsCoarseOffset:
129                 offsetEndDefined = true;
130                 offsetEnd += 32768 * genValTmp.shValue;
131                 break;
132             case champ_startloopAddrsOffset:
133                 offsetStartLoopDefined = true;
134                 offsetStartLoop += genValTmp.shValue;
135                 break;
136             case champ_startloopAddrsCoarseOffset:
137                 offsetStartLoopDefined = true;
138                 offsetStartLoop += 32768 * genValTmp.shValue;
139                 break;
140             case champ_endloopAddrsOffset:
141                 offsetEndLoopDefined = true;
142                 offsetEndLoop += genValTmp.shValue;
143                 break;
144             case champ_endloopAddrsCoarseOffset:
145                 offsetEndLoopDefined = true;
146                 offsetEndLoop += 32768 * genValTmp.shValue;
147                 break;
148             default:
149                 row = _table->getRow(static_cast<AttributeType>(i));
150                 if (row > -1)
151                 {
152                     if (i == champ_sampleModes)
153                         _table->setLoopModeImage(row, numCol, genValTmp.wValue);
154                     else
155                         _table->item(row, numCol)->setText(
156                                     Attribute::toString(static_cast<AttributeType>(i),
157                                                         _typePage == PAGE_PRST, genValTmp));
158                 }
159             }
160         }
161         if (offsetStartDefined && _table->getRow(champ_startAddrsOffset) > -1)
162         {
163             row = _table->getRow(champ_startAddrsOffset);
164             _table->item(row, numCol)->setText(QString::number(offsetStart));
165         }
166         if (offsetEndDefined && _table->getRow(champ_endAddrsOffset) > -1)
167         {
168             row = _table->getRow(champ_endAddrsOffset);
169             _table->item(row, numCol)->setText(QString::number(offsetEnd));
170         }
171         if (offsetStartLoopDefined && _table->getRow(champ_startloopAddrsOffset) > -1)
172         {
173             row = _table->getRow(champ_startloopAddrsOffset);
174             _table->item(row, numCol)->setText(QString::number(offsetStartLoop));
175         }
176         if (offsetEndLoopDefined && _table->getRow(champ_endloopAddrsOffset) > -1)
177         {
178             row = _table->getRow(champ_endloopAddrsOffset);
179             _table->item(row, numCol)->setText(QString::number(offsetEndLoop));
180         }
181     }
182 }
183 
addDivisions(EltID id)184 void PageTable::addDivisions(EltID id)
185 {
186     AttributeValue genValTmp;
187     int row;
188     EltID id2 = id;
189     EltID id3 = id;
190     int nbSmplInst = 0;
191     id.typeElement = this->lien;
192     id2.typeElement = this->lienGen;
193     id3.typeElement = this->contenu;
194 
195     foreach (int i, _sf2->getSiblings(id))
196     {
197         id.indexElt2 = i;
198         id2.indexElt2 = i;
199 
200         // Ajout d'un élément lié
201         int numCol = 1;
202 
203         // Détermination de la colonne
204         AttributeType cElementLie;
205         if (this->contenant == elementInst)
206             cElementLie = champ_sampleID;
207         else
208             cElementLie = champ_instrument;
209 
210         id3.indexElt = _sf2->get(id, cElementLie).wValue;
211 
212         QString strOrder = QString("%1-%2-%3")
213                 .arg(_sf2->get(id, champ_keyRange).rValue.byLo, 3, 10, QChar('0'))
214                 .arg(_sf2->get(id, champ_velRange).rValue.byLo, 3, 10, QChar('0'))
215                 .arg(_sf2->getQstr(id3, champ_name));
216         for (int j = 1; j < nbSmplInst + 1; j++)
217         {
218             if (Utils::sortDivisions(id, _table->getID(j), _sortType) > 0)
219                 numCol++;
220             else
221                 break;
222         }
223 
224         nbSmplInst++;
225         int offsetStart = 0;
226         bool offsetStartDefined = false;
227         int offsetEnd = 0;
228         bool offsetEndDefined = false;
229         int offsetStartLoop = 0;
230         bool offsetStartLoopDefined = false;
231         int offsetEndLoop = 0;
232         bool offsetEndLoopDefined = false;
233         _table->addColumn(numCol, _sf2->getQstr(id3, champ_name), id);
234         foreach (int champTmp, _sf2->getSiblings(id2))
235         {
236             genValTmp = _sf2->get(id, static_cast<AttributeType>(champTmp));
237             switch (champTmp)
238             {
239             case champ_startAddrsOffset:
240                 offsetStartDefined = true;
241                 offsetStart += genValTmp.shValue;
242                 break;
243             case champ_startAddrsCoarseOffset:
244                 offsetStartDefined = true;
245                 offsetStart += 32768 * genValTmp.shValue;
246                 break;
247             case champ_endAddrsOffset:
248                 offsetEndDefined = true;
249                 offsetEnd += genValTmp.shValue;
250                 break;
251             case champ_endAddrsCoarseOffset:
252                 offsetEndDefined = true;
253                 offsetEnd += 32768 * genValTmp.shValue;
254                 break;
255             case champ_startloopAddrsOffset:
256                 offsetStartLoopDefined = true;
257                 offsetStartLoop += genValTmp.shValue;
258                 break;
259             case champ_startloopAddrsCoarseOffset:
260                 offsetStartLoopDefined = true;
261                 offsetStartLoop += 32768 * genValTmp.shValue;
262                 break;
263             case champ_endloopAddrsOffset:
264                 offsetEndLoopDefined = true;
265                 offsetEndLoop += genValTmp.shValue;
266                 break;
267             case champ_endloopAddrsCoarseOffset:
268                 offsetEndLoopDefined = true;
269                 offsetEndLoop += 32768 * genValTmp.shValue;
270                 break;
271             default:
272                 row = _table->getRow(static_cast<AttributeType>(champTmp));
273                 if (row > -1)
274                 {
275                     if (champTmp == champ_sampleModes)
276                         _table->setLoopModeImage(row, numCol, genValTmp.wValue);
277                     else
278                         _table->item(row, numCol)->setText(
279                                     Attribute::toString(static_cast<AttributeType>(champTmp),
280                                                         _typePage == PAGE_PRST, genValTmp));
281                 }
282             }
283         }
284 
285         if (offsetStartDefined && _table->getRow(champ_startAddrsOffset) > -1)
286         {
287             row = _table->getRow(champ_startAddrsOffset);
288             _table->item(row, numCol)->setText(QString::number(offsetStart));
289         }
290         if (offsetEndDefined && _table->getRow(champ_endAddrsOffset) > -1)
291         {
292             row = _table->getRow(champ_endAddrsOffset);
293             _table->item(row, numCol)->setText(QString::number(offsetEnd));
294         }
295         if (offsetStartLoopDefined && _table->getRow(champ_startloopAddrsOffset) > -1)
296         {
297             row = _table->getRow(champ_startloopAddrsOffset);
298             _table->item(row, numCol)->setText(QString::number(offsetStartLoop));
299         }
300         if (offsetEndLoopDefined && _table->getRow(champ_endloopAddrsOffset) > -1)
301         {
302             row = _table->getRow(champ_endloopAddrsOffset);
303             _table->item(row, numCol)->setText(QString::number(offsetEndLoop));
304         }
305 
306         if (this->contenant == elementInst)
307         {
308             // Data about linked samples
309             int row = _table->getRow(champ_byOriginalPitch);
310             if (row > -1)
311             {
312                 unsigned char pitch = _sf2->get(id3, champ_byOriginalPitch).bValue;
313                 _table->item(row, numCol)->setText(QString::number(pitch));
314             }
315 
316             row = _table->getRow(champ_dwLength);
317             if (row > -1)
318             {
319                 unsigned int length = _sf2->get(id3, champ_dwLength).dwValue;
320                 _table->item(row, numCol)->setText(QString::number(length));
321             }
322 
323             row = _table->getRow(champ_dwStartLoop);
324             if (row > -1)
325             {
326                 unsigned int start = _sf2->get(id3, champ_dwStartLoop).dwValue;
327                 unsigned int end = _sf2->get(id3, champ_dwEndLoop).dwValue;
328                 _table->item(row, numCol)->setText(QString::number(start) + "-" + QString::number(end));
329             }
330         }
331     }
332 }
333 
formatTable(bool multiGlobal)334 void PageTable::formatTable(bool multiGlobal)
335 {
336     QColor color = this->palette().color(QPalette::Base);
337     QColor alternateColor = this->palette().color(QPalette::AlternateBase);
338     QBrush brush1(TableWidget::getPixMap(color, alternateColor));
339     QBrush brush2(TableWidget::getPixMap(alternateColor, color));
340     if (this->contenant == elementInst)
341     {
342         // Rows with the alternate color
343         for (int i = multiGlobal ? 0 : 1; i < _table->columnCount(); i++)
344         {
345             for (int j = 0; j < _table->rowCount(); j++)
346             {
347                 if (j < 6) {}
348                 else if (j < 10)
349                     _table->item(j, i)->setBackground(alternateColor);
350                 else if (j < 13) {}
351                 else if (j < 21)
352                     _table->item(j, i)->setBackground(alternateColor);
353                 else if (j < 31) {}
354                 else if (j < 39)
355                     _table->item(j, i)->setBackground(alternateColor);
356                 else if (j < 44) {}
357                 else
358                     _table->item(j, i)->setBackground(alternateColor);
359                 _table->item(j, i)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
360             }
361         }
362 
363         if (multiGlobal)
364         {
365             // Hide rows champ_byOriginalPitch, champ_dwLength and champ_dwStartLoop
366             _table->hideRow(6);
367             _table->hideRow(44);
368             _table->hideRow(47);
369         }
370         else
371         {
372             // First column with a hatching pattern
373             for (int j = 0; j < _table->rowCount(); j++)
374             {
375                 if (j < 6)
376                     _table->item(j, 0)->setBackground(brush1);
377                 else if (j < 10)
378                     _table->item(j, 0)->setBackground(brush2);
379                 else if (j < 13)
380                     _table->item(j, 0)->setBackground(brush1);
381                 else if (j < 21)
382                     _table->item(j, 0)->setBackground(brush2);
383                 else if (j < 31)
384                     _table->item(j, 0)->setBackground(brush1);
385                 else if (j < 39)
386                     _table->item(j, 0)->setBackground(brush2);
387                 else if (j < 44)
388                     _table->item(j, 0)->setBackground(brush1);
389                 else
390                     _table->item(j, 0)->setBackground(brush2);
391                 _table->item(j, 0)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
392             }
393 
394             // Style of rows champ_byOriginalPitch, champ_dwLength and champ_dwStartLoop
395             styleFixedRow(6);
396             styleFixedRow(44);
397             styleFixedRow(47);
398         }
399     }
400     else
401     {
402         // First column with a hatching pattern
403         if (!multiGlobal)
404         {
405             for (int j = 0; j < _table->rowCount(); j++)
406             {
407                 if (j < 5)
408                     _table->item(j, 0)->setBackground(brush1);
409                 else if (j < 8)
410                     _table->item(j, 0)->setBackground(brush2);
411                 else if (j < 10)
412                     _table->item(j, 0)->setBackground(brush1);
413                 else if (j < 18)
414                     _table->item(j, 0)->setBackground(brush2);
415                 else if (j < 28)
416                     _table->item(j, 0)->setBackground(brush1);
417                 else if (j < 36)
418                     _table->item(j, 0)->setBackground(brush2);
419                 else
420                     _table->item(j, 0)->setBackground(brush1);
421                 _table->item(j, 0)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
422             }
423         }
424 
425         // Yellow rows
426         for (int i = multiGlobal ? 0 : 1; i < _table->columnCount(); i++)
427         {
428             for (int j = 0; j < _table->rowCount(); j++)
429             {
430                 if (j < 5) {}
431                 else if (j < 8)
432                     _table->item(j, i)->setBackground(alternateColor);
433                 else if (j < 10) {}
434                 else if (j < 18)
435                     _table->item(j, i)->setBackground(alternateColor);
436                 else if (j < 28) {}
437                 else if (j < 36)
438                     _table->item(j, i)->setBackground(alternateColor);
439                 _table->item(j, i)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
440             }
441         }
442     }
443     _table->hideRow(0);
444 }
445 
styleFixedRow(int numRow)446 void PageTable::styleFixedRow(int numRow)
447 {
448     // Color, font
449     QFont font(this->font().family(), 4 * this->font().pointSize() / 5, QFont::Normal, true);
450     QColor fixedColor = ThemeManager::mix(this->palette().color(QPalette::Text), this->palette().color(QPalette::Base), 0.35);
451 
452     // Style the cells
453     for (int i = 0; i < _table->columnCount(); i++)
454     {
455         _table->item(numRow, i)->setFont(font);
456         _table->item(numRow, i)->setForeground(fixedColor);
457         _table->item(numRow, i)->setFlags(Qt::NoItemFlags);
458     }
459 
460     // Visibility of the row
461     _table->showRow(numRow);
462     _table->setRowHeight(numRow, 2 * font.pointSize());
463 }
464 
afficheRanges(bool justSelection)465 void PageTable::afficheRanges(bool justSelection)
466 {
467     if (!_currentIds.isEmpty())
468         _rangeEditor->display(_currentIds, justSelection);
469 }
470 
afficheEnvelops(bool justSelection)471 void PageTable::afficheEnvelops(bool justSelection)
472 {
473     if (_envelopEditor != nullptr)
474         _envelopEditor->display(_currentIds, justSelection);
475 }
476 
resetChamp(int colonne,AttributeType champ1,AttributeType champ2)477 void PageTable::resetChamp(int colonne, AttributeType champ1, AttributeType champ2)
478 {
479     EltID id = _table->getID(colonne);
480     bool ok = _sf2->isSet(id, champ1);
481     if (champ2 != champ_unknown)
482         ok = ok || _sf2->isSet(id, champ2);
483 
484     if (ok)
485     {
486         // On efface la donnée
487         id = _table->getID(colonne);
488         _sf2->reset(id, champ1);
489         if (champ2 != champ_unknown)
490             _sf2->reset(id, champ2);
491     }
492 }
493 
setOffset(int ligne,int colonne,AttributeType champ1,AttributeType champ2)494 void PageTable::setOffset(int ligne, int colonne, AttributeType champ1, AttributeType champ2)
495 {
496     EltID id = _table->getID(colonne);
497     bool ok;
498     QString texte = _table->item(ligne, colonne)->text().left(9);
499     AttributeValue genAmount = Attribute::fromString(champ1, _typePage == PAGE_PRST, texte, ok);
500     if (ok)
501     {
502         // Enregistrement de la nouvelle valeur
503         AttributeValue genAmount2 = Attribute::fromString(champ2, _typePage == PAGE_PRST, texte, ok);
504         int iVal = limit(32768 * genAmount2.shValue + genAmount.shValue, champ1, id);
505         genAmount2.shValue = static_cast<qint16>(iVal / 32768);
506         genAmount.shValue = static_cast<qint16>(iVal % 32768);
507         if (genAmount.shValue != _sf2->get(id, champ1).shValue ||
508                 genAmount2.shValue != _sf2->get(id, champ2).shValue)
509         {
510             // Modification du sf2
511             id = _table->getID(colonne);
512             _sf2->set(id, champ1, genAmount);
513             _sf2->set(id, champ2, genAmount2);
514         }
515 
516         // Update the cell value
517         _table->item(ligne, colonne)->setText(QString::number(genAmount.shValue + 32768 * genAmount2.shValue));
518     }
519     else
520     {
521         // Restauration valeur précédente
522         if (_sf2->isSet(id, champ1) || _sf2->isSet(id, champ2))
523         {
524             genAmount.shValue = _sf2->get(id, champ1).shValue + 32768 * _sf2->get(id, champ2).shValue;
525             _table->item(ligne, colonne)->setText(Attribute::toString(champ1, _typePage == PAGE_PRST, genAmount));
526         }
527         else
528             _table->item(ligne, colonne)->setText("");
529     }
530 }
531 
actionBegin()532 void PageTable::actionBegin()
533 {
534     if (_preparingPage)
535         return;
536 }
537 
actionFinished()538 void PageTable::actionFinished()
539 {
540     if (_preparingPage)
541         return;
542     _sf2->endEditing(getEditingSource());
543 }
544 
set(int ligne,int colonne,bool allowPropagation)545 void PageTable::set(int ligne, int colonne, bool allowPropagation)
546 {
547     if (_preparingPage)
548         return;
549 
550     // Modification d'un élément du tableau
551     AttributeType champ = _table->getChamp(ligne);
552     if (champ == champ_unknown)
553         return;
554 
555     EltID id = _table->getID(colonne);
556     if (allowPropagation && id.typeElement == elementInstSmpl && champ != champ_pan &&
557             ContextManager::configuration()->getValue(ConfManager::SECTION_NONE, "stereo_modification", false).toBool())
558     {
559         // Répercussion des modifications sur le sample stéréo s'il est présent
560         EltID idSmpl = id;
561         idSmpl.typeElement = elementSmpl;
562         idSmpl.indexElt = _sf2->get(id, champ_sampleID).wValue;
563         SFSampleLink typeLink = _sf2->get(idSmpl, champ_sfSampleType).sfLinkValue;
564         if (typeLink == rightSample || typeLink == leftSample || typeLink == linkedSample ||
565                 typeLink == RomRightSample || typeLink == RomLeftSample || typeLink == RomLinkedSample)
566         {
567             int numSmpl2 = _sf2->get(idSmpl, champ_wSampleLink).wValue;
568             RangesType keyRange = _sf2->get(id, champ_keyRange).rValue;
569             RangesType velRange = _sf2->get(id, champ_velRange).rValue;
570 
571             // Recherche d'une correspondance dans les samples liés
572             bool ok = true;
573             int numCol2 = -1;
574             EltID idTmp = id;
575             for (int i = 1; i < _table->columnCount(); i++)
576             {
577                 idTmp = _table->getID(i);
578                 if (i != colonne)
579                 {
580                     RangesType keyRange2 = _sf2->get(idTmp, champ_keyRange).rValue;
581                     RangesType velRange2 = _sf2->get(idTmp, champ_velRange).rValue;
582                     if (keyRange2.byLo == keyRange.byLo && keyRange2.byHi == keyRange.byHi &&
583                             velRange2.byLo == velRange.byLo && velRange2.byHi == velRange.byHi)
584                     {
585                         int iTmp = _sf2->get(idTmp, champ_sampleID).wValue;
586                         if (iTmp == idSmpl.indexElt)
587                             ok = false; // ambiguité
588                         else if (iTmp == numSmpl2)
589                         {
590                             if (numCol2 == -1)
591                                 numCol2 = i;
592                             else
593                                 ok = false; // ambiguité
594                         }
595                     }
596                 }
597             }
598 
599             // Application de la modification
600             if (numCol2 != -1 && ok)
601             {
602                 _table->blockSignals(true);
603 
604                 if (champ == champ_sampleModes)
605                 {
606                     _table->item(ligne, numCol2)->setData(Qt::DecorationRole, _table->item(ligne, colonne)->data(Qt::DecorationRole));
607                     _table->item(ligne, numCol2)->setData(Qt::UserRole, _table->item(ligne, colonne)->data(Qt::UserRole));
608                 }
609                 else
610                     _table->item(ligne, numCol2)->setText(_table->item(ligne, colonne)->text());
611                 _table->blockSignals(false);
612                 set(ligne, numCol2, false);
613             }
614         }
615     }
616 
617     if (champ == champ_sampleModes)
618     {
619         // Effacement du champ ?
620         if (_table->item(ligne, colonne)->data(Qt::UserRole).isNull())
621             resetChamp(colonne, champ, champ_unknown);
622         else
623         {
624             EltID id = _table->getID(colonne);
625             AttributeValue genAmount;
626             genAmount.wValue = static_cast<quint16>(_table->item(ligne, colonne)->data(Qt::UserRole).toInt());
627 
628             // Modification champ
629             if (genAmount.wValue != _sf2->get(id, champ).wValue || !_sf2->isSet(id, champ))
630             {
631                 // Modification du sf2
632                 _sf2->set(id, champ, genAmount);
633             }
634         }
635     }
636     else
637     {
638         if (_table->item(ligne, colonne)->text().isEmpty())
639         {
640             // Effacement d'un paramètre ?
641             switch (champ)
642             {
643             case champ_startAddrsOffset:
644                 resetChamp(colonne, champ_startAddrsOffset, champ_startAddrsCoarseOffset);
645                 break;
646             case champ_endAddrsOffset:
647                 resetChamp(colonne, champ_endAddrsOffset, champ_endAddrsCoarseOffset);
648                 break;
649             case champ_startloopAddrsOffset:
650                 resetChamp(colonne, champ_startloopAddrsOffset, champ_startloopAddrsCoarseOffset);
651                 break;
652             case champ_endloopAddrsOffset:
653                 resetChamp(colonne, champ_endloopAddrsOffset, champ_endloopAddrsCoarseOffset);
654                 break;
655             default:
656                 resetChamp(colonne, champ, champ_unknown);
657             }
658         }
659         else
660         {
661             _preparingPage = true;
662             switch (champ)
663             {
664             case champ_startAddrsOffset:
665                 setOffset(ligne, colonne, champ_startAddrsOffset, champ_startAddrsCoarseOffset);
666                 break;
667             case champ_endAddrsOffset:
668                 setOffset(ligne, colonne, champ_endAddrsOffset, champ_endAddrsCoarseOffset);
669                 break;
670             case champ_startloopAddrsOffset:
671                 setOffset(ligne, colonne, champ_startloopAddrsOffset, champ_startloopAddrsCoarseOffset);
672                 break;
673             case champ_endloopAddrsOffset:
674                 setOffset(ligne, colonne, champ_endloopAddrsOffset, champ_endloopAddrsCoarseOffset);
675                 break;
676             default:{
677                 QString texte = _table->item(ligne, colonne)->text().left(9);
678                 bool ok;
679                 EltID id = _table->getID(colonne);
680                 AttributeValue genAmount = Attribute::fromString(champ, _typePage == PAGE_PRST, texte, ok);
681                 if (ok)
682                 {
683                     // Modification champ
684                     if (genAmount.wValue != _sf2->get(id, champ).wValue || !_sf2->isSet(id, champ))
685                     {
686                         // Modification du sf2
687                         _sf2->set(id, champ, genAmount);
688                     }
689                     // Mise à jour de la valeur dans la cellule
690                     _table->item(ligne, colonne)->setText(Attribute::toString(champ, _typePage == PAGE_PRST, genAmount));
691                 }
692                 else
693                 {
694                     // Restauration valeur précédente
695                     if (_sf2->isSet(id, champ))
696                         _table->item(ligne, colonne)->setText(Attribute::toString(champ, _typePage == PAGE_PRST, _sf2->get(id, champ)));
697                     else _table->item(ligne, colonne)->setText("");
698                 }
699             }
700             }
701             _preparingPage = false;
702         }
703     }
704 
705     // Mise à jour partie mod (car entre 2 des mods peuvent être définitivement détruits, et les index peuvent être mis à jour)
706     _modulatorEditor->setIds(_currentIds);
707     if (champ == champ_overridingRootKey || champ == champ_keyRange)
708         customizeKeyboard();
709 }
710 
reselect()711 void PageTable::reselect()
712 {
713     _table->clearSelection();
714     _table->setSelectionMode(QAbstractItemView::MultiSelection);
715     QList<EltID> listID = _currentIds;
716     _table->setRowHidden(0, false); // Selection will be on a visible row
717     foreach (EltID id, listID)
718         this->select(id);
719     _table->setSelectionMode(QAbstractItemView::ExtendedSelection);
720     _table->setRowHidden(0, true); // It's now hidden again
721 }
722 
select(EltID id)723 void PageTable::select(EltID id)
724 {
725     _preparingPage = true;
726 
727     EltID id2;
728     int max;
729     for (int i = 0; i < _table->columnCount(); i++)
730     {
731         id2 = _table->getID(i);
732         if (id.typeElement == id2.typeElement && id.indexSf2 == id2.indexSf2 && id.indexElt == id2.indexElt &&
733                 (id.indexElt2 == id2.indexElt2 || id.typeElement == elementInst || id.typeElement == elementPrst))
734         {
735             _table->blockSignals(true);
736             _table->item(0, i)->setSelected(true);
737             _table->blockSignals(false);
738             max = _table->horizontalScrollBar()->maximum();
739             if (max / _table->columnCount() > 62)
740                 _table->horizontalScrollBar()->setValue((max*(i-1)) / _table->columnCount());
741             else
742                 _table->scrollToItem(_table->item(7, i), QAbstractItemView::PositionAtCenter);
743         }
744     }
745     _preparingPage = false;
746 }
747 
selected()748 void PageTable::selected()
749 {
750     if (_preparingPage)
751         return;
752     _preparingPage = true;
753 
754     // List of distinct selected ids
755     IdList ids;
756     QList<QTableWidgetItem*> listItems = _table->selectedItems();
757     if (listItems.empty()) {
758         _preparingPage = false;
759         return;
760     }
761     for (int i = 0; i < listItems.count(); i++)
762     {
763         EltID idTmp = _table->getID(listItems.at(i)->column());
764         if (!ids.contains(idTmp))
765             ids << idTmp;
766     }
767     _currentIds = ids;
768 
769     // Update mods
770     if (_currentIds.count() == 1)
771     {
772         // List of attributes
773         QList<AttributeType> attributes;
774         for (int i = 0; i < listItems.count(); i++)
775             attributes << _table->getChamp(listItems.at(i)->row());
776         _modulatorEditor->setIds(_currentIds, attributes);
777     }
778     else
779         _modulatorEditor->setIds(_currentIds);
780 
781     // Update the selection outside the table
782     emit(selectedIdsChanged(ids));
783     _preparingPage = false;
784 
785     customizeKeyboard();
786 }
787 
customizeKeyboard()788 void PageTable::customizeKeyboard()
789 {
790     ContextManager::midi()->keyboard()->clearCustomization();
791 
792     QList<EltID> ids = _currentIds;
793     if (ids.isEmpty())
794         return;
795 
796     // If the global division is in the list, exclude the rest
797     foreach (EltID id, ids)
798     {
799         if (id.typeElement == elementInst || id.typeElement == elementPrst)
800         {
801             ids.clear();
802             ids << id;
803             break;
804         }
805     }
806 
807     // Affichage des étendues et rootkeys des divisions sélectionnées
808     foreach (EltID id, ids)
809     {
810         if (id.typeElement == elementInstSmpl || id.typeElement == elementPrstInst)
811         {
812             int rootKey = -1;
813             if (id.typeElement == elementInstSmpl)
814             {
815                 if (_sf2->isSet(id, champ_overridingRootKey))
816                     rootKey = _sf2->get(id, champ_overridingRootKey).wValue;
817                 else
818                 {
819                     EltID idSmpl = id;
820                     idSmpl.typeElement = elementSmpl;
821                     idSmpl.indexElt = _sf2->get(id, champ_sampleID).wValue;
822                     rootKey = _sf2->get(idSmpl, champ_byOriginalPitch).bValue;
823                 }
824             }
825 
826             RangesType keyRange;
827             keyRange.byLo = 0;
828             keyRange.byHi = 127;
829             if (_sf2->isSet(id, champ_keyRange))
830                 keyRange = _sf2->get(id, champ_keyRange).rValue;
831             else
832             {
833                 if (id.typeElement == elementInstSmpl)
834                     id.typeElement = elementInst;
835                 else
836                     id.typeElement = elementPrst;
837                 if (_sf2->isSet(id, champ_keyRange))
838                     keyRange = _sf2->get(id, champ_keyRange).rValue;
839             }
840             ContextManager::midi()->keyboard()->addRangeAndRootKey(rootKey, keyRange.byLo, keyRange.byHi);
841         }
842         else if (id.typeElement == elementInst || id.typeElement == elementPrst)
843         {
844             RangesType defaultKeyRange;
845             if (_sf2->isSet(id, champ_keyRange))
846                 defaultKeyRange = _sf2->get(id, champ_keyRange).rValue;
847             else
848             {
849                 defaultKeyRange.byLo = 0;
850                 defaultKeyRange.byHi = 127;
851             }
852             if (id.typeElement == elementInst)
853                 id.typeElement = elementInstSmpl;
854             else
855                 id.typeElement = elementPrstInst;
856             foreach (int i, _sf2->getSiblings(id))
857             {
858                 id.indexElt2 = i;
859                 RangesType keyRange;
860                 if (_sf2->isSet(id, champ_keyRange))
861                     keyRange = _sf2->get(id, champ_keyRange).rValue;
862                 else
863                     keyRange = defaultKeyRange;
864                 ContextManager::midi()->keyboard()->addRangeAndRootKey(-1, keyRange.byLo, keyRange.byHi);
865             }
866         }
867     }
868 }
869 
limit(int iVal,AttributeType champ,EltID id)870 int PageTable::limit(int iVal, AttributeType champ, EltID id)
871 {
872     int ret = iVal;
873     EltID id2 = id;
874     EltID id3 = id;
875     if (id.typeElement != elementInst && id.typeElement != elementInstSmpl) return 0;
876     int limInf, limSup;
877     switch ((int)champ)
878     {
879     case champ_startAddrsOffset:
880         // Limite inférieure
881         if (ret < 0) ret = 0;
882         // Limite supérieure
883         limSup = 0;
884         if (id.typeElement == elementInstSmpl)
885         {
886             id2.typeElement = elementSmpl;
887             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
888             limSup = _sf2->get(id2, champ_dwLength).dwValue;
889         }
890         else
891         {
892             id3.typeElement = elementInstSmpl;
893             id2.typeElement = elementSmpl;
894             foreach (int i, _sf2->getSiblings(id3))
895             {
896                 id3.indexElt2 = i;
897                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
898                 if (i == 0 || _sf2->get(id2, champ_dwLength).dwValue < (unsigned)limSup)
899                     limSup = _sf2->get(id2, champ_dwLength).dwValue;
900             }
901         }
902         if (ret > limSup)
903             ret = limSup;
904         break;
905     case champ_endAddrsOffset:
906         // Limite inférieure
907         limInf = 0;
908         if (id.typeElement == elementInstSmpl)
909         {
910             id2.typeElement = elementSmpl;
911             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
912             limInf = - (int)_sf2->get(id2, champ_dwLength).dwValue;
913         }
914         else
915         {
916             id3.typeElement = elementInstSmpl;
917             id2.typeElement = elementSmpl;
918             foreach (int i, _sf2->getSiblings(id3))
919             {
920                 id3.indexElt2 = i;
921                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
922                 if (i == 0 || -(signed)_sf2->get(id2, champ_dwLength).dwValue > limInf)
923                     limInf = - (int)_sf2->get(id2, champ_dwLength).dwValue;
924             }
925         }
926         if (ret < limInf)
927             ret = limInf;
928 
929         // Limite supérieure
930         if (ret > 0)
931             ret = 0;
932         break;
933     case champ_startloopAddrsOffset:
934         // Limite inférieure
935         limInf = 0;
936         if (id.typeElement == elementInstSmpl)
937         {
938             id2.typeElement = elementSmpl;
939             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
940             limInf = - (int)_sf2->get(id2, champ_dwStartLoop).dwValue;
941         }
942         else
943         {
944             id3.typeElement = elementInstSmpl;
945             id2.typeElement = elementSmpl;
946             foreach (int i, _sf2->getSiblings(id3))
947             {
948                 id3.indexElt2 = i;
949                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
950                 if (i == 0 || -(signed)_sf2->get(id2, champ_dwStartLoop).dwValue > limInf)
951                     limInf = - (int)_sf2->get(id2, champ_dwStartLoop).dwValue;
952             }
953         }
954         if (ret < limInf) ret = limInf;
955         // Limite supérieure
956         limSup = 0;
957         if (id.typeElement == elementInstSmpl)
958         {
959             id2.typeElement = elementSmpl;
960             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
961             limSup = _sf2->get(id2, champ_dwLength).dwValue - _sf2->get(id2, champ_dwStartLoop).dwValue;
962         }
963         else
964         {
965             id3.typeElement = elementInstSmpl;
966             id2.typeElement = elementSmpl;
967             foreach (int i, _sf2->getSiblings(id3))
968             {
969                 id3.indexElt2 = i;
970                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
971                 if (i == 0 || \
972                         _sf2->get(id2, champ_dwLength).dwValue -
973                         _sf2->get(id2, champ_dwStartLoop).dwValue < (unsigned)limSup)
974                     limSup = _sf2->get(id2, champ_dwLength).dwValue -
975                             _sf2->get(id2, champ_dwStartLoop).dwValue;
976             }
977         }
978         if (ret > limSup) ret = limSup;
979         break;
980     case champ_endloopAddrsOffset:
981         // Limite inférieure
982         limInf = 0;
983         if (id.typeElement == elementInstSmpl)
984         {
985             id2.typeElement = elementSmpl;
986             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
987             limInf = - (int)_sf2->get(id2, champ_dwEndLoop).dwValue;
988         }
989         else
990         {
991             id3.typeElement = elementInstSmpl;
992             id2.typeElement = elementSmpl;
993             foreach (int i, _sf2->getSiblings(id3))
994             {
995                 id3.indexElt2 = i;
996                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
997                 if (i == 0 || -(signed)_sf2->get(id2, champ_dwEndLoop).dwValue > limInf)
998                     limInf = - (int)_sf2->get(id2, champ_dwEndLoop).dwValue;
999             }
1000         }
1001         if (ret < limInf)
1002             ret = limInf;
1003 
1004         // Limite supérieure
1005         limSup = 0;
1006         if (id.typeElement == elementInstSmpl)
1007         {
1008             id2.typeElement = elementSmpl;
1009             id2.indexElt = _sf2->get(id, champ_sampleID).wValue;
1010             limSup = _sf2->get(id2, champ_dwLength).dwValue - _sf2->get(id2, champ_dwEndLoop).dwValue;
1011         }
1012         else
1013         {
1014             id3.typeElement = elementInstSmpl;
1015             id2.typeElement = elementSmpl;
1016             foreach (int i, _sf2->getSiblings(id3))
1017             {
1018                 id3.indexElt2 = i;
1019                 id2.indexElt = _sf2->get(id3, champ_sampleID).wValue;
1020                 if (i == 0 ||
1021                         _sf2->get(id2, champ_dwLength).dwValue -
1022                         _sf2->get(id2, champ_dwEndLoop).dwValue < (unsigned)limSup)
1023                     limSup = _sf2->get(id2, champ_dwLength).dwValue -
1024                             _sf2->get(id2, champ_dwEndLoop).dwValue;
1025             }
1026         }
1027         if (ret > limSup) ret = limSup;
1028         break;
1029     }
1030     return ret;
1031 }
1032 
keyPlayedInternal(int key,int velocity)1033 void PageTable::keyPlayedInternal(int key, int velocity)
1034 {
1035     // Visualization on the table
1036     if (_table->isVisible())
1037     {
1038         // Update triggered elements
1039         if (velocity > 0 && _listKeyEnlighted.indexOf(key) == -1)
1040             _listKeyEnlighted.append(key);
1041         else
1042             _listKeyEnlighted.removeAll(key);
1043 
1044         // Color table
1045         for (int i = 1; i < _table->columnCount(); i++)
1046         {
1047             EltID id = _table->getID(i);
1048             if (_sf2->isValid(id))
1049             {
1050                 bool enlighted = false;
1051                 int key1 = _sf2->get(id, champ_keyRange).rValue.byLo;
1052                 int key2 = _sf2->get(id, champ_keyRange).rValue.byHi;
1053                 if (!_sf2->isSet(id, champ_keyRange))
1054                 {
1055                     key1 = 0;
1056                     key2 = 128;
1057                 }
1058                 for (int j = 0; j < _listKeyEnlighted.size(); j++)
1059                     enlighted = enlighted || (qMin(key1, key2) <= _listKeyEnlighted.at(j)
1060                                               && qMax(key1, key2) >= _listKeyEnlighted.at(j));
1061                 _table->setEnlighted(i, enlighted);
1062             }
1063         }
1064     }
1065 
1066     // Visualization on the range editor
1067     if (_rangeEditor->isVisible())
1068         _rangeEditor->playKey(key, velocity);
1069 
1070     // Specific commands for instruments or presets
1071     keyPlayedInternal2(key, velocity);
1072 }
1073 
onOpenElement(EltID id)1074 void PageTable::onOpenElement(EltID id)
1075 {
1076     // Find the element represented by the division
1077     if (id.typeElement == elementInstSmpl)
1078     {
1079         id.indexElt = _sf2->get(id, champ_sampleID).wValue;
1080         id.typeElement = elementSmpl;
1081     }
1082     else if (id.typeElement == elementPrstInst)
1083     {
1084         id.indexElt =_sf2->get(id, champ_instrument).wValue;
1085         id.typeElement = elementInst;
1086     }
1087 
1088     emit(selectedIdsChanged(IdList(id)));
1089 }
1090 
displayModInTable()1091 void PageTable::displayModInTable()
1092 {
1093     _table->resetModDisplay();
1094 
1095     // Mod for the global division
1096     for (int i = 0; i < _table->columnCount(); i++)
1097     {
1098         EltID id = _table->getID(i);
1099         if (_sf2->isValid(id))
1100         {
1101             if (id.typeElement == this->contenant)
1102                 id.typeElement = this->contenantMod;
1103             else
1104                 id.typeElement = this->lienMod;
1105 
1106             QList<int> modCount = _sf2->getSiblings(id);
1107             if (modCount.count() > 0)
1108             {
1109                 QList<int> rows;
1110                 foreach (int j, modCount)
1111                 {
1112                     id.indexMod = j;
1113                     AttributeType champ = static_cast<AttributeType>(_sf2->get(id, champ_sfModDestOper).wValue);
1114                     if (champ == champ_startAddrsCoarseOffset)
1115                         champ = champ_startAddrsOffset;
1116                     else if (champ == champ_endAddrsCoarseOffset)
1117                         champ = champ_endAddrsOffset;
1118                     else if (champ == champ_startloopAddrsCoarseOffset)
1119                         champ = champ_startloopAddrsOffset;
1120                     else if (champ == champ_endloopAddrsCoarseOffset)
1121                         champ = champ_endloopAddrsOffset;
1122                     rows << _table->getRow(champ);
1123                 }
1124                 _table->updateModDisplay(i, rows);
1125             }
1126         }
1127     }
1128 }
1129 
onShow()1130 void PageTable::onShow()
1131 {
1132     // Refresh the keyboard
1133     customizeKeyboard();
1134 }
1135 
divisionSortChanged()1136 void PageTable::divisionSortChanged()
1137 {
1138     this->afficheTable(false);
1139 }
1140 
onModSelectionChanged(QList<AttributeType> attributes)1141 void PageTable::onModSelectionChanged(QList<AttributeType> attributes)
1142 {
1143     if (!attributes.empty() && _currentIds.count() == 1)
1144         _table->selectCells(_currentIds[0], attributes);
1145 }
1146