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