1 /**
2  * @file parstore.cpp
3  * @brief Implements the ParStore class.
4  *
5  *
6  *      Copyright 2009 - 2017 <qmidiarp-devel@lists.sourceforge.net>
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      This program is distributed in the hope that it will be useful,
14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *      GNU General Public License for more details.
17  *
18  *      You should have received a copy of the GNU General Public License
19  *      along with this program; if not, write to the Free Software
20  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  *      MA 02110-1301, USA.
22  */
23 
24 #include "main.h"
25 #include "parstore.h"
26 #include "storagebutton.h"
27 
28 #include "pixmaps/filesave.xpm"
29 
30 ParStore::ParStore(GlobStore *p_globStore, const QString &name,
31             QAction *p_muteOutAction, QAction *p_deferChangesAction,
32             QWidget *p_parent): globStore(p_globStore)
33 {
34     setParent(p_parent);
35     // when temp.empty is true, restoring from that set is ignored
36     temp.empty = false;
37     temp.muteOut = false;
38     temp.res = 1;
39     temp.size = 0;
40     temp.loopMode = 0;
41     temp.waveForm = 0;
42     temp.portOut = 0;
43     temp.channelOut = 0;
44     temp.chIn = 0;
45     temp.wave.clear();
46     temp.muteMask.clear();
47     /* LFO Modules */
48     temp.ccnumber = -1;
49     temp.ccnumberIn = 0;
50     temp.freq = 0;
51     temp.ampl = 0;
52     temp.offs = 0;
53     /* Seq Modules */
54     temp.loopMarker = 0;
55     temp.notelen = 0;
56     temp.vel = 0;
57     temp.transp = 0;
58     temp.dispVertIndex = 0;
59     /* Arp Modules */
60     temp.indexIn0 = 0;
61     temp.indexIn1 = 127;
62     temp.rangeIn0 = 0;
63     temp.rangeIn1 = 127;
64     temp.attack = 0;
65     temp.release = 0;
66     temp.repeatMode = 0;
67     temp.rndTick = 0;
68     temp.rndLen = 0;
69     temp.rndVel = 0;
70     temp.pattern = "";
71     list.clear();
72 
73     ndc = new Indicator(14, name.at(0));
74 
75     topButton = new QToolButton;
76     topButton->setText(name);
77     topButton->setMinimumSize(QSize(75, 10));
78 
79     muteOutAction = p_muteOutAction;
80     muteOut = new QToolButton;
81     muteOut->setDefaultAction(muteOutAction);
82     muteOut->setMinimumSize(QSize(10, 10));
83 
84     deferChangesAction = p_deferChangesAction;
85     deferChanges = new QToolButton;
86     deferChanges->setDefaultAction(deferChangesAction);
87     deferChanges->setMinimumSize(QSize(10, 10));
88 
89     QHBoxLayout *muteRowLayout = new QHBoxLayout;
90     muteRowLayout->addStretch();
91     muteRowLayout->addWidget(muteOut);
92     muteRowLayout->addWidget(deferChanges);
93     muteRowLayout->setMargin(0);
94     muteRowLayout->setSpacing(0);
95 
96     QVBoxLayout *controlLayout = new QVBoxLayout;
97     controlLayout->addWidget(topButton);
98     controlLayout->addLayout(muteRowLayout);
99     controlLayout->setMargin(0);
100     controlLayout->setSpacing(0);
101 
102     QWidget *indicatorBox = new QWidget;
103     QHBoxLayout *indicatorLayout = new QHBoxLayout;
104     indicatorBox->setMinimumHeight(20);
105     indicatorBox->setMinimumWidth(25);
106     indicatorLayout->addWidget(ndc);
107     indicatorLayout->setMargin(2);
108     indicatorLayout->setSpacing(1);
109     indicatorBox->setLayout(indicatorLayout);
110 
111     QFrame *topRow = new QFrame;
112     QHBoxLayout *topRowLayout = new QHBoxLayout;
113     topRowLayout->addWidget(indicatorBox);
114     topRowLayout->addLayout(controlLayout);
115     topRowLayout->setSpacing(0);
116     topRowLayout->setMargin(0);
117     topRow->setMinimumSize(QSize(104,46));;
118     topRow->setFrameStyle(QFrame::StyledPanel);
119     topRow->setLayout(topRowLayout);
120 
121     QVBoxLayout *buttonLayout = new QVBoxLayout;
122     buttonLayout->addWidget(topRow);
123 
124     locContextMenu = new QMenu(this);
125 
126     QAction *storeHereAction = new QAction(tr("&Store here"), this);
127     storeHereAction->setProperty("index", list.count());
128     storeHereAction->setIcon(QPixmap(filesave_xpm));
129     locContextMenu->addAction(storeHereAction);
130     connect(storeHereAction, SIGNAL(triggered()), this, SLOT(mapStoreSignal()));
131 
132     QAction *onlyPatternAction = new QAction(tr("&Act on pattern only"), this);
133     onlyPatternAction->setProperty("index", list.count());
134     onlyPatternAction->setCheckable(true);
135     locContextMenu->addAction(onlyPatternAction);
136     connect(onlyPatternAction, SIGNAL(toggled(bool)), this, SLOT(updateOnlyPattern(bool)));
137 
138     jumpToIndexMenu = new QMenu(tr("When finished"), this);
139 
140     jumpToGroup = new QActionGroup(this);
141     connect(jumpToGroup, SIGNAL(triggered(QAction *))
142              , this, SLOT(mapJumpToGroup(QAction *)));
143 
144     jumpToIndexMenu->addAction(new QAction(tr("Stay here"), this));
145     jumpToIndexMenu->actions().last()->setProperty("index", -2);
146     jumpToIndexMenu->actions().last()->setActionGroup(jumpToGroup);
147     jumpToIndexMenu->actions().last()->setCheckable(true);
148     jumpToIndexMenu->actions().last()->setChecked(true);
149 
150     jumpToIndexMenu->addAction(new QAction(tr("Jump back"), this));
151     jumpToIndexMenu->actions().last()->setProperty("index", -1);
152     jumpToIndexMenu->actions().last()->setActionGroup(jumpToGroup);
153     jumpToIndexMenu->actions().last()->setCheckable(true);
154 
155     jumpToIndexMenu->addSeparator()->setText(tr("Jump to:"));
156 
157     locContextMenu->addMenu(jumpToIndexMenu);
158 
159     for (int l1 = 0; l1 < list.size() - 1; l1++) addLocation();
160 
161     QVBoxLayout *columnLayout = new QVBoxLayout;
162     columnLayout->addLayout(buttonLayout);
163     columnLayout->addStretch();
164     columnLayout->setMargin(0);
165     columnLayout->setSpacing(0);
166     setLayout(columnLayout);
167 
168     globStore->indivButtonLayout->addWidget(this);
169 
170     isRestoreMaster = false;
171     restoreRequest = -1;
172     oldRestoreRequest = 0;
173     restoreRunOnce = false;
174     activeStore = 0;
175     currentRequest = 0;
176     dispReqIx = 0;
177     dispReqSelected = 0;
178     needsGUIUpdate = false;
179 }
180 
181 void ParStore::writeData(QXmlStreamWriter& xml)
182 {
183     QByteArray tempArray;
184 
185     xml.writeStartElement("globalStores");
186 
187     for (int ix = 0; ix < list.size(); ix++) {
188         xml.writeStartElement("parStore");
189         xml.writeAttribute("ID", QString::number(ix));
190             xml.writeTextElement("empty", QString::number(list.at(ix).empty));
191             xml.writeTextElement("muteOut", QString::number(list.at(ix).muteOut));
192             xml.writeTextElement("res", QString::number(list.at(ix).res));
193             xml.writeTextElement("size", QString::number(list.at(ix).size));
194             xml.writeTextElement("loopMode", QString::number(list.at(ix).loopMode));
195             xml.writeTextElement("waveForm", QString::number(list.at(ix).waveForm));
196             xml.writeTextElement("portOut", QString::number(list.at(ix).portOut));
197             xml.writeTextElement("channelOut", QString::number(list.at(ix).channelOut));
198             xml.writeTextElement("chIn", QString::number(list.at(ix).chIn));
199             xml.writeTextElement("ccnumber", QString::number(list.at(ix).ccnumber));
200             xml.writeTextElement("ccnumberIn", QString::number(list.at(ix).ccnumberIn));
201             xml.writeTextElement("freq", QString::number(list.at(ix).freq));
202             xml.writeTextElement("ampl", QString::number(list.at(ix).ampl));
203             xml.writeTextElement("offs", QString::number(list.at(ix).offs));
204             xml.writeTextElement("loopMarker", QString::number(list.at(ix).loopMarker));
205             xml.writeTextElement("notelen", QString::number(list.at(ix).notelen));
206             xml.writeTextElement("vel", QString::number(list.at(ix).vel));
207             xml.writeTextElement("dispVertical", QString::number(list.at(ix).dispVertIndex));
208             xml.writeTextElement("transp", QString::number(list.at(ix).transp));
209             xml.writeTextElement("indexIn0", QString::number(list.at(ix).indexIn0));
210             xml.writeTextElement("indexIn1", QString::number(list.at(ix).indexIn1));
211             xml.writeTextElement("rangeIn0", QString::number(list.at(ix).rangeIn0));
212             xml.writeTextElement("rangeIn1", QString::number(list.at(ix).rangeIn1));
213             xml.writeTextElement("attack", QString::number(list.at(ix).attack));
214             xml.writeTextElement("release", QString::number(list.at(ix).release));
215             xml.writeTextElement("repeatMode", QString::number(list.at(ix).repeatMode));
216             xml.writeTextElement("rndTick", QString::number(list.at(ix).rndTick));
217             xml.writeTextElement("rndLen", QString::number(list.at(ix).rndLen));
218             xml.writeTextElement("rndVel", QString::number(list.at(ix).rndVel));
219             xml.writeTextElement("pattern", list.at(ix).pattern);
220 
221             xml.writeTextElement("jumpTo", QString::number(jumpToList.at(ix)));
222             xml.writeTextElement("onlyPattern", QString::number((int)onlyPatternList.at(ix)));
223 
224             tempArray.clear();
225             int l1 = 0;
226             while (l1 < list.at(ix).muteMask.count()) {
227                 tempArray.append(list.at(ix).muteMask.at(l1));
228                 l1++;
229             }
230             xml.writeStartElement("muteMask");
231                 xml.writeTextElement("data", tempArray.toHex());
232             xml.writeEndElement();
233 
234             tempArray.clear();
235             l1 = 0;
236             while (l1 < list.at(ix).wave.count()) {
237                 tempArray.append(list.at(ix).wave.at(l1).value);
238                 l1++;
239             }
240             xml.writeStartElement("wave");
241                 xml.writeTextElement("data", tempArray.toHex());
242             xml.writeEndElement();
243         xml.writeEndElement();
244     }
245     xml.writeEndElement();
246 }
247 
248 void ParStore::readData(QXmlStreamReader& xml)
249 {
250     int ix = 0;
251     int step = 0;
252     int tmpjumpto = -2;
253     int tmponlypattern = 0;
254 
255     while (!xml.atEnd()) {
256         xml.readNext();
257         if (xml.isEndElement())
258             break;
259 
260         if (xml.isStartElement() && (xml.name() == "parStore")) {
261             while (!xml.atEnd()) {
262                 xml.readNext();
263                 if (xml.isEndElement())
264                     break;
265                 if (xml.name() == "empty")
266                     temp.empty = xml.readElementText().toInt();
267                 else if (xml.name() == "muteOut")
268                     temp.muteOut = xml.readElementText().toInt();
269                 else if (xml.name() == "res")
270                     temp.res = xml.readElementText().toInt();
271                 else if (xml.name() == "size")
272                     temp.size = xml.readElementText().toInt();
273                 else if (xml.name() == "loopMode")
274                     temp.loopMode = xml.readElementText().toInt();
275                 else if (xml.name() == "waveForm")
276                     temp.waveForm = xml.readElementText().toInt();
277                 else if (xml.name() == "portOut")
278                     temp.portOut = xml.readElementText().toInt();
279                 else if (xml.name() == "channelOut")
280                     temp.channelOut = xml.readElementText().toInt();
281                 else if (xml.name() == "chIn")
282                     temp.chIn = xml.readElementText().toInt();
283                 else if (xml.name() == "ccnumber")
284                     temp.ccnumber = xml.readElementText().toInt();
285                 else if (xml.name() == "ccnumberIn")
286                     temp.ccnumberIn = xml.readElementText().toInt();
287                 else if (xml.name() == "freq")
288                     temp.freq = xml.readElementText().toInt();
289                 else if (xml.name() == "ampl")
290                     temp.ampl = xml.readElementText().toInt();
291                 else if (xml.name() == "offs")
292                     temp.offs = xml.readElementText().toInt();
293                 else if (xml.name() == "vel")
294                     temp.vel = xml.readElementText().toInt();
295                 else if (xml.name() == "dispVertical")
296                     temp.dispVertIndex = xml.readElementText().toInt();
297                 else if (xml.name() == "transp")
298                     temp.transp = xml.readElementText().toInt();
299                 else if (xml.name() == "notelen")
300                     temp.notelen = xml.readElementText().toInt();
301                 else if (xml.name() == "loopMarker")
302                     temp.loopMarker = xml.readElementText().toInt();
303                 else if (xml.name() == "indexIn0")
304                     temp.indexIn0 = xml.readElementText().toInt();
305                 else if (xml.name() == "indexIn1")
306                     temp.indexIn1 = xml.readElementText().toInt();
307                 else if (xml.name() == "rangeIn0")
308                     temp.rangeIn0 = xml.readElementText().toInt();
309                 else if (xml.name() == "rangeIn1")
310                     temp.rangeIn1 = xml.readElementText().toInt();
311                 else if (xml.name() == "attack")
312                     temp.attack = xml.readElementText().toInt();
313                 else if (xml.name() == "release")
314                     temp.release = xml.readElementText().toInt();
315                 else if (xml.name() == "repeatMode")
316                     temp.repeatMode = xml.readElementText().toInt();
317                 else if (xml.name() == "rndTick")
318                     temp.rndTick = xml.readElementText().toInt();
319                 else if (xml.name() == "rndLen")
320                     temp.rndLen = xml.readElementText().toInt();
321                 else if (xml.name() == "rndVel")
322                     temp.rndVel = xml.readElementText().toInt();
323                 else if (xml.name() == "pattern")
324                     temp.pattern = xml.readElementText();
325                 else if (xml.name() == "jumpTo")
326                     tmpjumpto = xml.readElementText().toInt();
327                 else if (xml.name() == "onlyPattern")
328                     tmponlypattern = xml.readElementText().toInt();
329                 else if (xml.isStartElement() && (xml.name() == "muteMask")) {
330                     while (!xml.atEnd()) {
331                         xml.readNext();
332                         if (xml.isEndElement())
333                             break;
334                         if (xml.isStartElement() && (xml.name() == "data")) {
335                             temp.muteMask.clear();
336                             QByteArray tmpArray =
337                                     QByteArray::fromHex(xml.readElementText().toLatin1());
338                             for (int l1 = 0; l1 < tmpArray.count(); l1++) {
339                                 temp.muteMask.append(tmpArray.at(l1));
340                             }
341                         }
342                         else skipXmlElement(xml);
343                     }
344                 }
345                 else if (xml.isStartElement() && (xml.name() == "wave")) {
346                     while (!xml.atEnd()) {
347                         xml.readNext();
348                         if (xml.isEndElement())
349                             break;
350                         if (xml.isStartElement() && (xml.name() == "data")) {
351                             temp.wave.clear();
352                             QByteArray tmpArray =
353                                     QByteArray::fromHex(xml.readElementText().toLatin1());
354 
355                             if (temp.ccnumberIn >= 0)
356                                 step = TPQN / lfoResValues[temp.res];
357                             else
358                                 step = TPQN / seqResValues[temp.res];
359 
360                             int lt = 0;
361                             Sample sample;
362                             for (int l1 = 0; l1 < tmpArray.count(); l1++) {
363                                 sample.value = tmpArray.at(l1);
364                                 sample.tick = lt;
365                                 sample.muted = temp.muteMask.at(l1);
366                                 temp.wave.append(sample);
367                                 lt+=step;
368                             }
369                         }
370                         else skipXmlElement(xml);
371                     }
372                 }
373                 else skipXmlElement(xml);
374             }
375             //For compatibility with files stored before all modules got
376             //Note filters:
377             if (!(temp.indexIn0 + temp.indexIn1)) temp.indexIn1 = 127;
378             if (!(temp.rangeIn0 + temp.rangeIn1)) temp.rangeIn1 = 127;
379             tempToList(ix);
380             updateRunOnce(ix, tmpjumpto);
381             onlyPatternList.replace(ix, tmponlypattern);
382             ix++;
383         }
384     }
385 }
386 
387 void ParStore::skipXmlElement(QXmlStreamReader& xml)
388 {
389     if (xml.isStartElement()) {
390         qWarning("Unknown Element in XML File: %s",qPrintable(xml.name().toString()));
391         while (!xml.atEnd()) {
392             xml.readNext();
393 
394             if (xml.isEndElement())
395                 break;
396 
397             if (xml.isStartElement()) {
398                 skipXmlElement(xml);
399             }
400         }
401     }
402 }
403 
404 void ParStore::addLocation()
405 {
406     StorageButton *toolButton = new StorageButton(this);
407     toolButton->setText(QString::number(list.count()));
408     toolButton->setStyleSheet("font: 10pt");
409     toolButton->setProperty("index", list.count());
410     connect(toolButton, SIGNAL(pressed()), this, SLOT(mapRestoreSignal()));
411 
412     toolButton->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::CustomContextMenu));
413     connect(toolButton, SIGNAL(customContextMenuRequested(const QPoint &))
414                     , this, SLOT(showLocContextMenu(const QPoint &)));
415 
416     jumpToIndexMenu->addAction(new QAction(QString::number(list.count()), this));
417     jumpToIndexMenu->actions().last()->setActionGroup(jumpToGroup);
418     jumpToIndexMenu->actions().last()->setProperty("index", list.count() - 1);
419     jumpToIndexMenu->actions().last()->setCheckable(true);
420 
421     layout()->itemAt(0)->layout()->addWidget(toolButton);
422     jumpToList.append(-2);
423     onlyPatternList.append(false);
424 }
425 
426 void ParStore::removeLocation(int ix)
427 {
428     if (ix == -1) ix = list.count() - 1;
429 
430     list.removeAt(ix);
431     QWidget *button = layout()->itemAt(0)->layout()->takeAt(ix + 1)->widget();
432     QAction *action = jumpToIndexMenu->actions().at(ix + 3);
433     delete button;
434     delete action;
435     jumpToList.removeAt(ix);
436     onlyPatternList.removeAt(ix);
437 
438     for (int l1 = 0; l1 < jumpToList.count(); l1++) {
439         if (jumpToList.at(l1) >= jumpToList.count()) updateRunOnce(l1, -2);
440     }
441 }
442 
443 
444 void ParStore::setRestoreRequest(int ix)
445 {
446     restoreRequest = ix;
447     restoreRunOnce = (jumpToList.at(ix) > -2 );
448 
449     setDispState(ix, 2);
450 }
451 
452 void ParStore::mapJumpToGroup(QAction *action)
453 {
454     int choice = action->property("index").toInt();
455     int location = sender()->property("index").toInt();
456 
457     updateRunOnce(location, choice);
458 }
459 
460 void ParStore::updateRunOnce(int location, int choice)
461 {
462     if (choice == -2) { //stay here
463         jumpToList.replace(location, -2);
464         setBGColorAt(location + 1, 0);
465         ((StorageButton *)(layout()->itemAt(0)->layout()->itemAt(location + 1)
466             ->widget()))->setSecondText("", 0);
467     }
468     else if (choice == -1) { //jump back to last
469         jumpToList.replace(location, -1);
470         setBGColorAt(location + 1, 3);
471         ((StorageButton *)(layout()->itemAt(0)->layout()->itemAt(location + 1)
472             ->widget()))->setSecondText("<- ", 1);
473     }
474     else if (choice >= 0) { //jump to location
475         jumpToList.replace(location, choice);
476         ((StorageButton *)(layout()->itemAt(0)->layout()->itemAt(location + 1)
477             ->widget()))->setSecondText("-> "+QString::number(choice + 1), 2);
478         setBGColorAt(location + 1, 3);
479     }
480 }
481 
482 void ParStore::setBGColorAt(int row, int color)
483 {
484     QString styleSheet;
485 
486     if (color == 1)         //green
487         styleSheet = "QToolButton { background-color: rgba(50, 255, 50, 40%); }";
488     else if (color == 2)    //yellow
489         styleSheet = "QToolButton { background-color: rgba(150, 255, 150, 10%); }";
490     else if (color == 3)    //blueish
491         styleSheet = "QToolButton { }";
492     else                    //no color
493         styleSheet = "QToolButton { }";
494 
495     layout()->itemAt(0)->layout()->itemAt(row)->widget()->setStyleSheet(styleSheet);
496 }
497 
498 void ParStore::tempToList(int ix)
499 {
500     if (ix >= list.size()) {
501         list.append(temp);
502         addLocation();
503     }
504     else {
505         list.replace(ix, temp);
506     }
507     currentRequest = ix;
508     setDispState(ix, 1);
509 }
510 
511 void ParStore::mapRestoreSignal()
512 {
513     int ix = sender()->property("index").toInt();
514 
515     setRestoreRequest(ix - 1);
516 }
517 
518 void ParStore::mapStoreSignal()
519 {
520     int ix = sender()->property("index").toInt();
521 
522     emit store(ix - 1, false);
523 }
524 
525 void ParStore::setDispState(int ix, int selected)
526 {
527     if (selected == 1) {
528         for (int l2 = 1; l2 <= list.count(); l2++) {
529             setBGColorAt(l2, 3 * (jumpToList.at(l2 - 1) > -2));
530         }
531         setBGColorAt(ix + 1, 1);
532         activeStore = ix;
533     }
534     else if (selected == 2) {
535         setBGColorAt(ix + 1, 2);
536         if (currentRequest != activeStore) {
537             setBGColorAt(currentRequest + 1, 0);
538         }
539         currentRequest = ix;
540     }
541 }
542 
543 void ParStore::requestDispState(int ix, int selected)
544 {
545     dispReqIx = ix;
546     dispReqSelected = selected;
547     needsGUIUpdate = true;
548 }
549 
550 void ParStore::updateDisplay(int frame, bool reverse)
551 {
552     ndc->updateDraw();
553 
554     if (needsGUIUpdate) {
555         needsGUIUpdate = false;
556         setDispState(dispReqIx, dispReqSelected);
557     }
558 
559     if ((restoreRequest >= 0) && !frame) {
560         int req = restoreRequest;
561         setDispState(req, 1);
562         emit restore(req);
563         restoreRequest = -1;
564         if (!restoreRunOnce) {
565             oldRestoreRequest = req;
566         }
567     }
568 
569     if ((frame == 1)
570             && (restoreRequest != oldRestoreRequest)
571             && restoreRunOnce
572             && !reverse) {
573         if (jumpToList.at(activeStore) >= 0) {
574             restoreRequest = jumpToList.at(activeStore);
575             oldRestoreRequest = restoreRequest;
576         }
577         else {
578             restoreRequest = oldRestoreRequest;
579         }
580         restoreRunOnce = (jumpToList.at(restoreRequest) > -2);
581         setDispState(restoreRequest, 2);
582     }
583 
584 }
585 
586 void ParStore::showLocContextMenu(const QPoint &pos)
587 {
588     int senderlocation = sender()->property("index").toInt() - 1;
589     int l1;
590 
591     for (l1 = 0; l1 < locContextMenu->actions().count(); l1++) {
592         locContextMenu->actions().at(l1)->setProperty("index",
593             senderlocation + 1);
594     }
595     for (l1 = 0; l1 < list.count(); l1++) {
596         jumpToGroup->actions().at(l1 + 2)
597                 ->setDisabled(l1 == senderlocation);
598     }
599 
600     locContextMenu->setProperty("index", senderlocation);
601     jumpToGroup->setProperty("index", senderlocation);
602 
603     jumpToGroup->actions().at(jumpToList.at(senderlocation) + 2)
604             ->setChecked(true);
605     locContextMenu->actions().at(1)->setChecked(onlyPatternList.at(senderlocation));
606     locContextMenu->popup(QWidget::mapToGlobal(pos));
607 }
608 
609 void ParStore::updateOnlyPattern(bool on)
610 {
611     onlyPatternList.replace(sender()->property("index").toInt() - 1, on);
612 }
613