1 /*
2 Copyright (C) 2009 Andres Cabrera
3 mantaraya36@gmail.com
4
5 This file is part of CsoundQt.
6
7 CsoundQt is free software; you can redistribute it
8 and/or modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 CsoundQt 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 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with Csound; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA
21 */
22
23 #include "eventsheet.h"
24 #include "liveeventframe.h"
25
26 #include <QMenu>
27 #include <QDir>
28 #include <QContextMenuEvent>
29 #include <QLabel>
30 #include <QVBoxLayout>
31 #include <QLineEdit>
32 #include <QDialog>
33 #include <QDoubleSpinBox>
34 #include <QApplication>
35 #include <QClipboard>
36
37 #include <QFile>
38 #include <QMessageBox>
39
40 // For rand() function
41 #include <cstdlib>
42 // Only for debug
43 #include <QtCore>
44
45 class OneValueDialog: public QDialog
46 {
47 public:
OneValueDialog(QWidget * parent,QString label,double defaultValue=0.0)48 OneValueDialog(QWidget *parent, QString label, double defaultValue = 0.0):
49 QDialog(parent)
50 {
51 l = new QVBoxLayout(this);
52 lab= new QLabel(label, this);
53 box = new QDoubleSpinBox(this);
54 box->setRange(-999999, 999999);
55 box->setDecimals(8);
56 box->setValue(defaultValue);
57 box->selectAll();
58 l->addWidget(lab);
59 l->addWidget(box);
60 connect(box, SIGNAL(editingFinished()), this, SLOT(accept ()) );
61 }
62
value()63 double value() { return box->value();}
64
65 QVBoxLayout *l;
66 QLabel *lab;
67 QDoubleSpinBox *box;
68 };
69
70 class ThreeValueDialog: public QDialog
71 {
72 public:
ThreeValueDialog(QWidget * parent,QStringList labels,QVector<double> defaultValues)73 ThreeValueDialog(QWidget *parent, QStringList labels, QVector<double> defaultValues):
74 QDialog(parent)
75 {
76 l = new QVBoxLayout(this);
77 lab1= new QLabel(labels[0], this);
78 box1 = new QDoubleSpinBox(this);
79 box1->setRange(-999999, 999999);
80 box1->setDecimals(8);
81 box1->setValue(defaultValues[0]);
82 box1->selectAll();
83 lab2= new QLabel(labels[1], this);
84 box2 = new QDoubleSpinBox(this);
85 box2->setRange(-999999, 999999);
86 box2->setDecimals(8);
87 box2->setValue(defaultValues[1]);
88 l->addWidget(lab1);
89 l->addWidget(box1);
90 l->addWidget(lab2);
91 l->addWidget(box2);
92 if (labels[2] != "") {
93 lab3= new QLabel(labels[2], this);
94 box3 = new QDoubleSpinBox(this);
95 box3->setRange(-999999, 999999);
96 box3->setDecimals(8);
97 box3->setValue(defaultValues[2]);
98 l->addWidget(lab3);
99 l->addWidget(box3);
100 }
101 // connect(box1, SIGNAL(editingFinished()), this, SLOT(accept ()) );
102 // connect(box2, SIGNAL(editingFinished()), this, SLOT(accept ()) );
103 connect(box3, SIGNAL(editingFinished()), this, SLOT(accept ()) );
104 }
105
value1()106 double value1() { return box1->value();}
value2()107 double value2() { return box2->value();}
value3()108 double value3() { return box3->value();} // Be careful, this is not checked!
109 QVBoxLayout *l;
110 QLabel *lab1;
111 QDoubleSpinBox *box1;
112 QLabel *lab2;
113 QDoubleSpinBox *box2;
114 QLabel *lab3;
115 QDoubleSpinBox *box3;
116
117 protected:
keyPressEvent(QKeyEvent * event)118 virtual void keyPressEvent (QKeyEvent * event) {
119 if (event->key() == Qt::Key_Escape) {
120 this->reject();
121 }
122 else if (event->key() == Qt::Key_Return) {
123 if (box1->hasFocus())
124 box2->setFocus(Qt::TabFocusReason);
125 else if (box2->hasFocus())
126 box3->setFocus(Qt::TabFocusReason);
127 }
128 else {
129 QDialog::keyPressEvent(event); // Otherwise propagate event
130 }
131 }
132 };
133
EventSheet(QWidget * parent)134 EventSheet::EventSheet(QWidget *parent) : QTableWidget(parent)
135 {
136 // qDebug() << "EventSheet::EventSheet";
137 this->setRowCount(10);
138 this->setColumnCount(6);
139 columnNames << tr("Event") << "p1 (instr)" << "p2 (start)" << "p3 (dur)" << "p4" << "p5";
140 this->setHorizontalHeaderLabels(columnNames);
141 this->setColumnWidth(0, 50);
142 this->setColumnWidth(1, 70);
143 this->setColumnWidth(2, 70);
144 this->setColumnWidth(3, 70);
145 this->setColumnWidth(4, 50);
146 this->setColumnWidth(5, 50);
147
148 m_name = "Events";
149 m_stopScript = false;
150 m_looping = false;
151 createActions();
152 connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(newSelection()));
153 // a bit of a hack to ensure that manual changes to the sheet are stored in the
154 // undo history. This seems better than calling markHistory() when a cell
155 // changes because large operations like add or subractract will produce
156 // many steps in the history
157 // connect(this, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClickedSlot(int, int)));
158 connect(this, SIGNAL(cellChanged (int, int)), this, SLOT(cellChangedSlot(int, int)));
159
160 loopTimer.setSingleShot(true);
161 connect(&loopTimer, SIGNAL(timeout()), this, SLOT(sendEvents()));
162
163 builtinScripts << ":/python/sort_by_start.py" << ":/python/produce_score.py"<< ":/python/fill_text.py";
164 converterScripts << ":/python/Conversion/cps2mid.py" << ":/python/Conversion/mid2cps.py" << ":/python/Conversion/cps2pch.py"
165 << ":/python/Conversion/pch2cps.py" ;
166 testScripts << ":/python/Tests/python_test.py" << ":/python/Tests/tk_test.py";
167
168 noHistoryChange = 0;
169 }
170
~EventSheet()171 EventSheet::~EventSheet()
172 {
173 }
174
getPlainText(bool scaleTempo)175 QString EventSheet::getPlainText(bool scaleTempo)
176 {
177 QString t = "";
178 for (int i = 0; i < this->rowCount(); i++) {
179 t += getLine(i, scaleTempo) + "\n"; // Don't scale by default
180 }
181 t.chop(1);
182 // qDebug() << " EventSheet::getPlainText " << t;
183 return t;
184 }
185
getSelection(bool cut)186 QString EventSheet::getSelection(bool cut)
187 {
188 QModelIndexList list = this->selectedIndexes();
189 QList<int> selectedRows;
190 QList<int> selectedColumns;
191 for (int i = 0; i < list.size(); i++) { // Get list of selected rows
192 if (!selectedRows.contains(list[i].row()) ) {
193 selectedRows.append(list[i].row());
194 }
195 if (!selectedColumns.contains(list[i].column()) ) {
196 selectedColumns.append(list[i].column());
197 }
198 }
199 QString text = "";
200 for (int i = 0; i < selectedRows.size(); i++) {
201 QString line = "";
202 for (int j = 0; j < selectedColumns.size(); j++) {
203 QTableWidgetItem * item = this->item(selectedRows[i], selectedColumns[j]);
204 if (item != 0) { // Item is not empty
205 line += item->data(Qt::DisplayRole).toString();
206 QString space = item->data(Qt::UserRole).toString();
207 if (!space.isEmpty())
208 line += space;
209 else
210 line += " ";
211 if (cut)
212 delete item;
213 }
214 }
215 text += line + "\n";
216 }
217 text.chop(1); // remove last line break
218 return text;
219 }
220
getLine(int number,bool scaleTempo,bool storeNumber,bool preprocess,double startOffset)221 QString EventSheet::getLine(int number, bool scaleTempo, bool storeNumber, bool preprocess, double startOffset)
222 {
223 QString line = "";
224 bool instrEvent = false;
225 bool comment = false;
226 for (int i = 0; i < this->columnCount(); i++) {
227 QTableWidgetItem * item = this->item(number, i);
228 if (item != 0) { // Item is not empty
229 if (i == 0 && (item->data(Qt::DisplayRole).toString() == "i") ) {
230 instrEvent = true; // Check if event is intrument note to allow scale by tempo
231 }
232 if (instrEvent && i == 1 && storeNumber) { // append current instrument number to active note list
233 bool ok = false;
234 double instrNum = item->data(Qt::DisplayRole).toDouble(&ok);
235 if (ok && !activeInstruments.contains(instrNum))
236 activeInstruments.append(instrNum);
237 }
238 if (preprocess && item->data(Qt::DisplayRole).toString() == ".") { // Carry value from above
239 QString cellText = ".";
240 int row = number;
241 while (row >= 0 && cellText == "." && cellText != "") {
242 item = this->item(row, i);
243 if (item != 0 ) {
244 cellText = item->data(Qt::DisplayRole).toString();
245 }
246 else {
247 cellText = "";
248 }
249 row--;
250 }
251 }
252 if (scaleTempo && instrEvent && (i == 2 || i == 3) ) { // Scale tempo only for pfields p2 and p3
253 bool ok = false;
254 double value = item->data(Qt::DisplayRole).toDouble(&ok);
255 if (ok) {
256 if (i == 2) { // Add start offset to p2 before scaling
257 value += startOffset;
258 }
259 value = value * (60.0/m_tempo);
260 line += QString::number(value, 'f', 8);;
261 }
262 else {
263 line += item->data(Qt::DisplayRole).toString();
264 }
265 }
266 else { // All other p-fields that don't require tempo scaling
267 QString cellText = item->data(Qt::DisplayRole).toString();
268 if (comment) {
269 bool dataRemaining = false; // Check if remaining cells have data
270 for (int j = i + 1; j < this->columnCount(); j++) {
271 QTableWidgetItem * checkItem = this->item(number, j);
272 if (checkItem != 0 && checkItem->data(Qt::DisplayRole).toString() != "") {
273 dataRemaining = true;
274 break;
275 }
276 }
277 if (dataRemaining || cellText != "")
278 line += ";" + cellText;
279 }
280 else if (cellText.startsWith(';')) {
281 comment = true; // is a comment from now on
282 line += cellText;
283 }
284 else {
285 line += cellText;
286 }
287 }
288 // Then add white space separation
289 QString space = item->data(Qt::UserRole).toString();
290 if (!space.isEmpty()) { // Separataion is stored in UserRole of items
291 line += space;
292 }
293 else {
294 line += " ";
295 }
296 }
297 else if (comment) { // empty cell part of a comment
298 // Do nothing
299 }
300 }
301 return line;
302 }
303
getData()304 QList< QList<QVariant> > EventSheet::getData()
305 {
306 // TODO this can be made a lot more efficient
307 QList< QList<QVariant> > data;
308 for (int i = 0; i < this->rowCount(); i++) {
309 QList<QVariant> row;
310 for (int j = 0; j < this->columnCount(); j++) {
311 QTableWidgetItem * item = this->item(i, j);
312 if (item != 0) { // Item is not empty
313 row.append(item->data(Qt::DisplayRole));
314 }
315 else {
316 row.append(QVariant());
317 }
318 }
319 data << row;
320 }
321 return data;
322 }
323
setFromText(QString text,int rowOffset,int columnOffset,int numRows,int numColumns,bool noHistoryMark)324 void EventSheet::setFromText(QString text, int rowOffset, int columnOffset, int numRows, int numColumns, bool noHistoryMark)
325 {
326 // Separataion is stored in UserRole of items
327 // remember to treat comments and formulas properly
328 QStringList lines = text.split("\n");
329 int nRows = 0; // Number of actual rows to process
330 // numRows = 0 : don't remove rows, only add if necessary. numRows = -1 limit rows to the ones in text
331 nRows = numRows <= 0 ? lines.size() : numRows;
332 if (this->rowCount() < nRows + rowOffset || numRows == -1) {
333 this->setRowCount(nRows + rowOffset);
334 }
335 for (int i = 0; i < nRows; i++) {
336 // if (nRows != 0 && i >= nRows) { // Only paste up to a certain number of rows if not 0
337 // break;
338 // }
339 QString line = "";
340 if (i < lines.size()) {
341 line = lines[i].trimmed(); //Remove whitespace from start and end
342 }
343 this->blockSignals(true); // To avoid triggering changed signal which goes to undo for each cell
344 QList<QPair<QString, QString> > fields = parseLine(line);
345 int nColumns = numColumns == 0 ? fields.size() : numColumns;
346 nColumns = (numColumns == -1 && nColumns < this->columnCount()) ? this->columnCount() : nColumns;
347 while (this->columnCount() < nColumns + columnOffset) {
348 appendColumn();
349 }
350 int j;
351 for (j = 0; j < nColumns; j++) {
352 QTableWidgetItem * item = this->item(i + rowOffset, j + columnOffset);
353 if (item == 0) {
354 item = new QTableWidgetItem();
355 noHistoryChange = (noHistoryMark ? 1: 0);
356 this->setItem(i + rowOffset, j + columnOffset, item);
357 }
358 if (j < fields.size()) {
359 noHistoryChange = (noHistoryMark ? 1: 0);
360 item->setData(Qt::DisplayRole, fields[j].first);
361 noHistoryChange = (noHistoryMark ? 1: 0);
362 item->setData(Qt::UserRole, fields[j].second);
363 }
364 else {
365 noHistoryChange = (noHistoryMark ? 1: 0);
366 item->setData(Qt::DisplayRole, "");
367 noHistoryChange = (noHistoryMark ? 1: 0);
368 item->setData(Qt::UserRole, "");
369 }
370 }
371 while (j < numColumns && j + columnOffset < this->columnCount()) {
372 this->removeCellWidget(i + rowOffset, j + columnOffset);
373 j++;
374 }
375 }
376 // column names mus be reset here as the names have been cleared
377 this->setHorizontalHeaderLabels(columnNames);
378 this->blockSignals(false);
379 if (!noHistoryMark) {
380 emit ( cellChanged(rowOffset, columnOffset) );
381 }
382 if (this->rowCount() == 0)
383 this->setRowCount(1);
384 }
385
setCell(int row,int column,QVariant value)386 void EventSheet::setCell(int row, int column, QVariant value)
387 {
388 QTableWidgetItem * item = this->item(row, column);
389 if (item == 0) {
390 item = new QTableWidgetItem();
391 this->setItem(row, column, item);
392 }
393 item->setData(Qt::DisplayRole, value);
394 }
395
setDebug(bool debug)396 void EventSheet::setDebug(bool debug)
397 {
398 m_debug = debug;
399 }
400
getSelectedRowsRange()401 QPair<int, int> EventSheet::getSelectedRowsRange()
402 {
403 QModelIndexList selection = this->selectedIndexes();
404 int min = 9999, max = -1;
405 for (int i = 0; i < selection.size(); i++) {
406 if (selection[i].row() > max) {
407 max = selection[i].row();
408 }
409 if (selection[i].row() < min) {
410 min = selection[i].row();
411 }
412 }
413 qDebug() << "EventSheet::getSelectedRowsRange " << min << " "<< max;
414 return QPair<int, int>(min,max);
415 }
416
setTempo(double value)417 void EventSheet::setTempo(double value)
418 {
419 // qDebug() << "EventSheet::setTempo " << value;
420 m_tempo = value;
421 }
422
setLoopLength(double value)423 void EventSheet::setLoopLength(double value)
424 {
425 // qDebug() << "EventSheet::setLoopLength " << value;
426 m_loopLength = value;
427 }
428
sendEvents()429 void EventSheet::sendEvents()
430 {
431 QModelIndexList list;
432 QPair<int, int> rowsRange;
433 if (m_looping && sender() != sendEventsAct) {
434 double time = 1000.0 * m_loopLength * 60.0 /m_tempo;
435 loopTimer.start(time);
436 qDebug() << " EventSheet::sendEvents() " << time;
437 rowsRange.first = m_loopStart;
438 rowsRange.second = m_loopEnd;
439 }
440 else {
441 rowsRange = getSelectedRowsRange();
442 }
443 for (int i = rowsRange.first; i <= rowsRange.second; i++) {
444 // double number = 0.0;
445 emit sendEvent(getLine(i, true, true, true)); // With tempo scaling
446 }
447 }
448
sendAllEvents()449 void EventSheet::sendAllEvents()
450 {
451 for (int i = 0; i < this->rowCount(); i++) {
452 // qDebug() << "EventSheet::sendAllEvents() " << i;
453 emit sendEvent(getLine(i, true, true, true)); // With tempo scaling
454 }
455 }
456
sendEventsOffset()457 void EventSheet::sendEventsOffset()
458 {
459 QModelIndexList list;
460 QList<int> selectedRows;
461 list = this->selectedIndexes();
462 for (int i = 0; i < list.size(); i++) {
463 if (!selectedRows.contains(list[i].row()) ) {
464 selectedRows.append(list[i].row());
465 }
466 }
467 double minTime = 999999999999999.0;
468 bool hasMin = false;
469 for (int i = 0; i < selectedRows.size(); i++) {
470 QTableWidgetItem * item = this->item(selectedRows[i], 2);
471 if (item != 0 && item->data(Qt::DisplayRole).canConvert(QVariant::Double)) {
472 bool ok = false;
473 double n = item->data(Qt::DisplayRole).toDouble(&ok);
474 if (ok && n < minTime) {
475 minTime = n;
476 hasMin = true;
477 }
478 }
479 }
480 if (!hasMin)
481 minTime = 0.0;
482 for (int i = 0; i < selectedRows.size(); i++) {
483 // double number = 0.0;
484 emit sendEvent(getLine(selectedRows[i], true, true, true, -minTime)); // With tempo scaling
485 }
486 }
487
loopEvents()488 void EventSheet::loopEvents()
489 {
490 QPair<int, int> rowsRange = getSelectedRowsRange();
491 markLoop(rowsRange.first,rowsRange.second);
492 setLoopActive(true);
493 emit setLoopEnabledFromSheet(true);
494 }
495
setLoopActive(bool loop)496 void EventSheet::setLoopActive(bool loop)
497 {
498 // qDebug() << "EventSheet::setLoopActive " << loop;
499 if (loop) {
500 if (!m_looping) {
501 m_looping = true;
502 markLoop(m_loopStart, m_loopEnd);
503 sendEvents();
504 }
505 }
506 else {
507 m_looping = false;
508 markLoop(m_loopStart, m_loopEnd);
509 }
510 }
511
markLoop(double start,double end)512 void EventSheet::markLoop(double start, double end)
513 {
514 // TODO move looping to eventframe class
515 m_loopStart = (int) start;
516 m_loopEnd = (int) end;
517 for (int i = 0; i < rowCount(); i++) {
518 for (int j = 0; j < 4; j++) {
519 QTableWidgetItem * item = this->item(i, j);
520 if (item == 0) {
521 item = new QTableWidgetItem();
522 this->setItem(i, j, item );
523 }
524 if (i < m_loopStart || i > m_loopEnd) {
525 item->setBackground(QBrush());
526 }
527 else {
528 if (m_looping) {
529 item->setBackground(QBrush(Qt::green));
530 }
531 else {
532 item->setBackground(QBrush(QColor(200,255,200)));
533 }
534 }
535 }
536 }
537 }
538
setLoopRange()539 void EventSheet::setLoopRange()
540 {
541 qDebug() << "EventSheet::setLoopRange()";
542 QPair<int, int> rowsRange = getSelectedRowsRange();
543 double start = rowsRange.first;
544 double end = rowsRange.second;
545 emit setLoopRangeFromSheet(start,end);
546 }
547
stopAllEvents()548 void EventSheet::stopAllEvents()
549 {
550 loopTimer.stop();
551 m_looping = false;
552 markLoop();
553 while (!activeInstruments.isEmpty()) {
554 QString event = "i -";
555 event += QString::number(activeInstruments.takeFirst(), 'f', 10);
556 event += " 0 1";
557 emit sendEvent(event);
558 }
559 }
560
del()561 void EventSheet::del()
562 {
563 QModelIndexList list = this->selectedIndexes();
564 for (int i = 0; i < list.size(); i++) {
565 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
566 if (item != 0) { // Item is not empty
567 delete item;
568 }
569 }
570 markHistory();
571 }
572
cut()573 void EventSheet::cut()
574 {
575 copy(true);
576 markHistory();
577 }
578
copy(bool cut)579 void EventSheet::copy(bool cut)
580 {
581 qApp->clipboard()->setText(getSelection(cut));
582 }
583
paste()584 void EventSheet::paste()
585 {
586 // qDebug() << "EventSheet::paste() text = " << qApp->clipboard()->text();
587 QModelIndexList list = this->selectedIndexes();
588 QList<int> selectedRows;
589 QList<int> selectedColumns;
590 int rowCount, columnCount;
591 int lowestRow = 0x0fffffff, lowestColumn = 0x0fffffff;
592 for (int i = 0; i < list.size(); i++) { // Get list of selected rows
593 if (!selectedRows.contains(list[i].row()) ) {
594 selectedRows.append(list[i].row());
595 if (list[i].row() < lowestRow) {
596 lowestRow = list[i].row();
597 }
598 }
599 if (!selectedColumns.contains(list[i].column()) ) {
600 selectedColumns.append(list[i].column());
601 if (list[i].column() < lowestColumn) {
602 lowestColumn = list[i].column();
603 }
604 }
605 }
606 if (lowestRow == 0x0fffffff) { // If there is no selection
607 lowestRow = 0;
608 }
609 if (lowestColumn == 0x0fffffff) { // If there is no selection
610 lowestColumn = 0;
611 }
612 rowCount = selectedRows.size();
613 columnCount = selectedColumns.size();
614 if (rowCount <= 1 && columnCount <= 1) {
615 rowCount = columnCount = 0;
616 }
617 // TODO comments that should be pasted on multiple cells are not.
618 setFromText(qApp->clipboard()->text(), lowestRow, lowestColumn, rowCount, columnCount, true);
619 markHistory();
620 }
621
undo()622 void EventSheet::undo()
623 {
624 qDebug() << "EventSheet::undo() " << historyIndex;
625 if (historyIndex > 0) {
626 historyIndex--;
627 setFromText(history[historyIndex], 0,0,rowCount(),columnCount(),true);
628 }
629 }
630
redo()631 void EventSheet::redo()
632 {
633 qDebug() << "EventSheet::redo() " << historyIndex << history.size();
634 if (historyIndex < history.size() - 1) {
635 historyIndex++;
636 setFromText(history[historyIndex], 0,0,rowCount(),columnCount(),true);
637 }
638 }
639
markHistory()640 void EventSheet::markHistory()
641 {
642 QString text = getPlainText();
643 if (history.isEmpty()) {
644 history << "";
645 historyIndex = 0;
646 }
647 if (history[historyIndex] != text) {
648 if (! history[historyIndex].isEmpty())
649 historyIndex++;
650 // if (historyIndex >= QCS_MAX_UNDO) {
651 // history.pop_front();
652 // historyIndex--;
653 // }
654 if (history.size() != historyIndex + 1)
655 history.resize(historyIndex + 1);
656 history[historyIndex] = text;
657 // qDebug() << "EventSheet::markHistory "<< historyIndex << " ....." << text;
658 }
659 }
660
clearHistory()661 void EventSheet::clearHistory()
662 {
663 QString text = getPlainText();
664 // qDebug() << "EventSheet::clearHistory() " << text;
665 history.clear();
666 historyIndex = 0;
667 }
668
setScriptDirectory(QString dir)669 void EventSheet::setScriptDirectory(QString dir)
670 {
671 scriptDir = dir;
672 if (!scriptDir.endsWith("/") && !scriptDir.isEmpty())
673 scriptDir += "/";
674 }
675
subtract()676 void EventSheet::subtract()
677 {
678 OneValueDialog d(this, tr("Subtract"));
679 d.exec();
680 if (d.result() == QDialog::Accepted) {
681 this->add(- d.value());
682 }
683 }
684
add()685 void EventSheet::add()
686 {
687 OneValueDialog d(this, tr("Add"));
688 d.exec();
689 if (d.result() == QDialog::Accepted) {
690 this->add(d.value());
691 }
692 }
693
multiply()694 void EventSheet::multiply()
695 {
696 OneValueDialog d(this, tr("Multiply by"));
697 d.exec();
698 if (d.result() == QDialog::Accepted) {
699 this->multiply(d.value());
700 }
701 }
702
divide()703 void EventSheet::divide()
704 {
705 OneValueDialog d(this, tr("Divide by"));
706 d.exec();
707 if (d.result() == QDialog::Accepted) {
708 this->divide(d.value());
709 }
710 }
711
randomize()712 void EventSheet::randomize()
713 {
714 QStringList labels;
715 labels << tr("Minimum") << tr("Maximum") << tr("Mode: 0=decimals 1=Integers only");
716 QVector<double> defaultValues;
717 defaultValues << 0.0 << 1.0 << 0.0;
718 ThreeValueDialog d(this, labels, defaultValues);
719 d.box3->setDecimals(0);
720 d.exec();
721 if (d.result() == QDialog::Accepted) {
722 this->randomize(d.value1(), d.value2(), d.value3());
723 }
724 }
725
reverse()726 void EventSheet::reverse()
727 {
728 QModelIndexList list = this->selectedIndexes();
729 if (list.size() < 2)
730 return;
731 QList<int> selectedColumns;
732 for (int i = 0; i < list.size(); i++) {
733 if (!selectedColumns.contains(list[i].column()) ) {
734 selectedColumns.append(list[i].column());
735 }
736 }
737 int numRows = list.size() / selectedColumns.size();
738 if (numRows < 2)
739 return;
740
741 QVector<QVariant> elements;
742 elements.resize(numRows);
743 for (int i = 0; i < selectedColumns.size(); i++) {
744 for (int j = 0; j < numRows; j++) {
745 QTableWidgetItem * item = this->item(list[(i*numRows) + j].row(), list[(i*numRows) + j].column());
746 if (item != 0) {
747 elements[numRows - j - 1] = item->data(Qt::DisplayRole);
748 }
749 else {
750 elements[numRows - j - 1] = QVariant();
751 }
752 }
753 for (int j = 0; j < numRows; j++) {
754 QTableWidgetItem * item = this->item(list[(i*numRows) + j].row(), list[(i*numRows) + j].column());
755 if (item == 0) {
756 item = new QTableWidgetItem();
757 this->setItem(list[(i*numRows) + j].row(), list[(i*numRows) + j].column(), item );
758 }
759 item->setData(Qt::DisplayRole, elements[j] );
760 }
761 }
762 }
763
shuffle()764 void EventSheet::shuffle()
765 {
766 OneValueDialog d(this, tr("Iterations"));
767 d.box->setValue(10.0);
768 d.box->setDecimals(0);
769 d.box->selectAll();
770 d.exec();
771 if (d.result() == QDialog::Accepted) {
772 this->shuffle(d.value());
773 }
774 }
775
776 //void EventSheet::mirror()
777 //{
778 //
779 //}
780
rotate()781 void EventSheet::rotate()
782 {
783 OneValueDialog d(this, tr("Rotate by"));
784 d.box->setValue(1.0);
785 d.box->setDecimals(0);
786 d.exec();
787 if (d.result() == QDialog::Accepted) {
788 this->rotate(d.value());
789 }
790 }
791
fill()792 void EventSheet::fill()
793 {
794 QStringList labels;
795 labels << tr("From") << tr("To") << tr("Slope (1=Linear)");
796 QVector<double> defaultValues;
797 defaultValues << 1.0 << 5.0 << 1.0;
798 ThreeValueDialog d(this, labels, defaultValues);
799 connect(d.box1, SIGNAL(valueChanged (double)),
800 d.box2, SLOT(setValue(double)));
801 d.exec();
802 if (d.result() == QDialog::Accepted) {
803 this->fill(d.value1(), d.value2(), d.value3());
804 }
805 }
806
runScript()807 void EventSheet::runScript()
808 {
809 runScript(static_cast<QAction *>(sender())->data().toString());
810 }
811
generateDataText(QString outFileName)812 QString EventSheet::generateDataText(QString outFileName)
813 {
814 QModelIndexList list = this->selectedIndexes();
815 int minRow = 999999, minCol = 999999, maxRow = -1, maxCol = -1;
816 for (int i = 0; i < list.size(); i++) { // First traverse to find size
817 if (list[i].row() > maxRow) {
818 maxRow = list[i].row();
819 }
820 if (list[i].row() < minRow) {
821 minRow = list[i].row();
822 }
823 if (list[i].column() > maxCol) {
824 maxCol = list[i].column();
825 }
826 if (list[i].column() < minCol) {
827 minCol = list[i].column();
828 }
829 }
830 // QString data = "[ ";
831 // for (int i = minRow; i <= maxRow; i++) {
832 // data += "[ ";
833 // for (int j = minCol; j <= maxCol; j++) {
834 // QTableWidgetItem * item = this->item(i, j);
835 // if ( item == 0) {
836 // data += "''";
837 // }
838 // else {
839 // bool ok;
840 // item->data(Qt::DisplayRole).toString().toDouble(&ok);
841 // if (ok) {
842 // data += item->data(Qt::DisplayRole).toString();
843 // }
844 // else {
845 // data += "'" + item->data(Qt::DisplayRole).toString() + "'";
846 // }
847 // }
848 // data += ", ";
849 // }
850 // data.chop(2);
851 // data += " ],\n";
852 // }
853 // data.chop(2);
854 // data += " ]";
855
856 QString data_all = "[ ";
857 for (int i = 0; i < this->rowCount(); i++) {
858 data_all += "[ ";
859 for (int j = 0; j < this->columnCount() ; j++) {
860 QTableWidgetItem * item = this->item(i, j);
861 if ( item == 0) {
862 data_all += "''";
863 }
864 else {
865 bool ok;
866 item->data(Qt::DisplayRole).toString().toDouble(&ok);
867 // qDebug() << item->data(Qt::DisplayRole).typeName() << item;
868 if (ok) {
869 data_all += item->data(Qt::DisplayRole).toString();
870 }
871 else {
872 data_all += "'" + item->data(Qt::DisplayRole).toString() + "'";
873 }
874 }
875 data_all += ", ";
876 }
877 data_all.chop(2);
878 data_all += " ],\n";
879 }
880 data_all.chop(2);
881 data_all += " ]";
882
883 QString text;
884 text += "row = " + QString::number(minRow) + "\n";
885 text += "col = " + QString::number(minCol) + "\n";
886 text += "num_rows = " + QString::number(maxRow - minRow + 1) + "\n";
887 text += "num_cols = " + QString::number(maxCol - minCol + 1) + "\n";
888 text += "total_rows = " + QString::number(this->rowCount()) + "\n";
889 text += "total_cols = " + QString::number(this->columnCount ()) + "\n";
890
891 // text += "data = " + data + "\n";
892 text += "data_all = " + data_all + "\n";
893 text += "out_filename = '" + outFileName + "'\n";
894 return text;
895 }
896
runScript(QString name)897 void EventSheet::runScript(QString name)
898 {
899 // qDebug() << "EventSheet::runScript " << name;
900
901 QString outFileName = "qutesheet_out_data.txt";
902 QDir oldDir = QDir::current();
903 QDir tempDir(QDir::tempPath());
904 QString subDir = "QCS-" + QString::number(qrand());
905 while (!tempDir.mkdir(subDir))
906 subDir = "QCS-" + QString(qrand());
907 tempDir.cd(subDir);
908 QDir::setCurrent(tempDir.absolutePath());
909 QFile module(tempDir.absolutePath() + QDir::separator() + "qutesheet.py");
910 module.open(QFile::WriteOnly | QIODevice::Text);
911 QFile file(":/python/qutesheet.py");
912 file.open(QIODevice::ReadOnly);
913 QTextStream moduleStream(&module);
914 moduleStream << file.readAll();
915 file.close();
916 module.close();
917 qDebug() << "EventSheet::runScript module " << module.fileName();
918
919 QFile script(tempDir.absolutePath() + QDir::separator() + name.mid(name.lastIndexOf("/") + 1));
920 script.open(QFile::WriteOnly | QIODevice::Text);
921 QFile file2(name);
922 file2.open(QIODevice::ReadOnly);
923 QTextStream scriptStream(&script);
924 scriptStream << file2.readAll();
925 file2.close();
926 script.close();
927
928 QFile dataFile(tempDir.absolutePath() + QDir::separator() + "qutesheet_data.py");
929 dataFile.open(QFile::WriteOnly | QIODevice::Text);
930 QTextStream dataStream(&dataFile);
931 dataStream << generateDataText(outFileName);
932 dataFile.close();
933
934 QFile outFile(tempDir.absolutePath() + QDir::separator() + outFileName);
935
936 QProcess p;
937 p.start("python " + name.mid(name.lastIndexOf("/") + 1));
938
939 while (!p.waitForFinished (10) && !m_stopScript) {
940 qApp->processEvents();
941 }
942 m_stopScript = false;
943 QByteArray sout = p.readAllStandardOutput();
944 QByteArray serr = p.readAllStandardError();
945 qDebug() << "EventSheet::runScript Error -----\n" << serr;
946 QDir::setCurrent(oldDir.absolutePath());
947 if (p.exitCode() != 0) {
948 if (m_debug) {
949 QMessageBox::critical(this, name.mid(name.lastIndexOf("/") + 1) ,
950 QString(serr),
951 QMessageBox::Ok);
952 }
953 else {
954 QMessageBox::critical(this, name.mid(name.lastIndexOf("/") + 1) ,
955 tr("Error running script"),
956 QMessageBox::Ok);
957 }
958 }
959 else {
960 if (m_debug && !sout.isEmpty()) {
961 QMessageBox::information(this, name.mid(name.lastIndexOf("/") + 1) + " Output" ,
962 QString(sout),
963 QMessageBox::Ok);
964 }
965 qDebug() << sout;
966 outFile.open(QIODevice::ReadWrite);
967 QString text = outFile.readAll();
968 QStringList lines = text.split("\n");
969 if (lines.size() > 0 && lines[0].startsWith("__@ ")) {
970 QStringList position = lines[0].split(" ");
971 position.pop_front();
972 lines.pop_front();
973 QString pasteText = lines.join("\n");
974 pasteText.chop(1);
975 setFromText(pasteText, position[0].toInt(), position[1].toInt(),
976 position[2].toInt(), position[3].toInt(), true);
977 markHistory();
978 }
979 else {
980 qDebug() << "EventSheet::runScript invalid out file format";
981 }
982 }
983 module.remove();
984 script.remove();
985 dataFile.remove();
986 outFile.remove();
987 tempDir.rmpath(tempDir.absolutePath());
988 }
989
insertColumnHere()990 void EventSheet::insertColumnHere()
991 {
992 // TODO implement
993 }
994
insertRowHere()995 void EventSheet::insertRowHere()
996 {
997 // TODO implement
998 }
999
appendColumn()1000 void EventSheet::appendColumn()
1001 {
1002 this->insertColumn(this->columnCount());
1003 columnNames << QString("p%1").arg(this->columnCount() - 1);
1004 this->setHorizontalHeaderLabels(columnNames);
1005 this->setColumnWidth(this->columnCount() - 1, 50);
1006 }
1007
appendRow()1008 void EventSheet::appendRow()
1009 {
1010 // qDebug() << "EventSheet::appendRow()";
1011 this->insertRow(this->rowCount());
1012 }
1013
appendColumns()1014 void EventSheet::appendColumns()
1015 {
1016 OneValueDialog d(this, tr("Add columns:"), 5);
1017 d.box->setDecimals(0);
1018 d.exec();
1019 if (d.result() == QDialog::Accepted) {
1020 for (int i = 0; i < d.value() && i < 256; i++) {
1021 appendColumn();
1022 }
1023 }
1024 }
1025
appendRows()1026 void EventSheet::appendRows()
1027 {
1028 OneValueDialog d(this, tr("Add Rows:"), 10);
1029 d.box->setDecimals(0);
1030 d.exec();
1031 if (d.result() == QDialog::Accepted) {
1032 for (int i = 0; i < d.value() && i < 256; i++) {
1033 appendRow();
1034 }
1035 }
1036 }
1037
deleteColumn()1038 void EventSheet::deleteColumn()
1039 {
1040 // TODO: remove multiple columns
1041 this->removeColumn(this->columnCount() - 1);
1042 columnNames.takeLast();
1043 }
1044
deleteRows()1045 void EventSheet::deleteRows()
1046 {
1047 // TODO: remove multiple rows
1048 QModelIndexList list = this->selectedIndexes();
1049 QList<int> selectedRows;
1050 for (int i = 0; i < list.size(); i++) {
1051 if (!selectedRows.contains(list[i].row()) ) {
1052 selectedRows.append(list[i].row());
1053 }
1054 }
1055 qSort(selectedRows);
1056 for (int i = selectedRows.size() - 1; i >=0; i--) {
1057 this->removeRow(selectedRows[i]);
1058 }
1059 }
1060
contextMenuEvent(QContextMenuEvent * event)1061 void EventSheet::contextMenuEvent (QContextMenuEvent * event)
1062 {
1063 // qDebug() << "EventSheet::contextMenuEvent";
1064
1065 QMenu menu;
1066 menu.addAction(sendEventsAct);
1067 menu.addAction(sendEventsOffsetAct);
1068 menu.addAction(loopSelectionAct);
1069 menu.addAction(markLoopAct);
1070 menu.addAction(stopAllEventsAct);
1071 menu.addSeparator();
1072 menu.addAction(subtractAct);
1073 menu.addAction(addAct);
1074 menu.addAction(multiplyAct);
1075 menu.addAction(divideAct);
1076 menu.addAction(randomizeAct);
1077 menu.addAction(reverseAct);
1078 menu.addAction(shuffleAct);
1079 // menu.addAction(mirrorAct);
1080 menu.addAction(rotateAct);
1081 menu.addAction(fillAct);
1082 menu.addSeparator();
1083 QMenu *scriptMenu = menu.addMenu(tr("Python Scripts"));
1084 QMenu *converterMenu = scriptMenu->addMenu(tr("Conversion"));
1085 for (int i = 0; i < converterScripts.size(); i++) {
1086 QAction *a = converterMenu->addAction(converterScripts[i].mid(converterScripts[i].lastIndexOf("/") + 1),
1087 this, SLOT(runScript() ));
1088 a->setData(converterScripts[i]);
1089 }
1090 QMenu *testMenu = scriptMenu->addMenu(tr("Tests"));
1091 for (int i = 0; i < testScripts.size(); i++) {
1092 QAction *a = testMenu->addAction(testScripts[i].mid(testScripts[i].lastIndexOf("/") + 1),
1093 this, SLOT(runScript() ));
1094 a->setData(testScripts[i]);
1095 }
1096 for (int i = 0; i < builtinScripts.size(); i++) {
1097 QAction *a = scriptMenu->addAction(builtinScripts[i].mid(builtinScripts[i].lastIndexOf("/") + 1),
1098 this, SLOT(runScript() ));
1099 a->setData(builtinScripts[i]);
1100 }
1101 if (!scriptDir.isEmpty()) {
1102 scriptMenu->addSeparator();
1103 addDirectoryToMenu(scriptMenu, scriptDir);
1104 }
1105 menu.addAction(stopScriptAct);
1106 menu.addSeparator();
1107 // menu.addAction(insertColumnHereAct);
1108 // menu.addAction(insertRowHereAct);
1109 menu.addAction(appendColumnAct);
1110 menu.addAction(appendRowAct);
1111 menu.addAction(appendColumnsAct);
1112 menu.addAction(appendRowsAct);
1113 menu.addAction(deleteColumnAct);
1114 menu.addAction(deleteRowAct);
1115 menu.exec(event->globalPos());
1116 }
1117
addDirectoryToMenu(QMenu * m,QString dir,int depth)1118 void EventSheet::addDirectoryToMenu(QMenu *m, QString dir, int depth)
1119 {
1120 if (depth > 4)
1121 return;
1122 QDir d(dir);
1123 QStringList filters;
1124 filters << "*.py";
1125 d.setNameFilters(filters);
1126 QStringList scripts = d.entryList(QDir::Files,QDir::Name);
1127 QStringList directories = d.entryList(QDir::AllDirs,QDir::Name);
1128 if (scripts.size() > 0 || directories.size() > 2) {
1129 QMenu *newMenu = m;
1130 if (dir != scriptDir)
1131 newMenu = m->addMenu(dir.mid(dir.lastIndexOf("/")+ 1));
1132 for (int i = 0; i < directories.size(); i++) {
1133 if (directories[i] != "." && directories[i] != "..")
1134 addDirectoryToMenu(newMenu, dir + "/" + directories[i], depth++);
1135 }
1136 for (int i = 0; i < scripts.size(); i++) {
1137 QAction *a = newMenu->addAction(scripts[i], this, SLOT(runScript() ));
1138 a->setData(dir + "/" + scripts[i]);
1139 }
1140 }
1141 }
1142
keyPressEvent(QKeyEvent * event)1143 void EventSheet::keyPressEvent (QKeyEvent * event) {
1144 if (event->matches(QKeySequence::Delete)) {
1145 this->del();
1146 }
1147 else if (event->matches(QKeySequence::Cut)) {
1148 this->cut();
1149 }
1150 else if (event->matches(QKeySequence::Copy)) {
1151 this->copy();
1152 }
1153 else if (event->matches(QKeySequence::Paste)) {
1154 this->paste();
1155 }
1156 else if (event->matches(QKeySequence::Undo)
1157 || (event->key() == Qt::Key_Z && event->modifiers() == Qt::ControlModifier) ){
1158 this->undo();
1159 }
1160 else if (event->matches(QKeySequence::Redo)
1161 || (event->key() == Qt::Key_Z
1162 && event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) ) {
1163 this->redo();
1164 }
1165 else if (event->matches(QKeySequence::InsertLineSeparator)) {
1166 this->sendEvents();
1167 }
1168 else {
1169 // qDebug() << "EventSheet::keyPressEvent " << event->key();
1170 // event->ignore();
1171 QTableWidget::keyPressEvent(event); // Propagate any other events
1172 }
1173 }
1174
add(double value)1175 void EventSheet::add(double value)
1176 {
1177 QModelIndexList list = this->selectedIndexes();
1178 for (int i = 0; i < list.size(); i++) {
1179 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1180 if (item != 0 && item->data(Qt::DisplayRole).canConvert(QVariant::Double)) {
1181 bool ok = false;
1182 double n = item->data(Qt::DisplayRole).toDouble(&ok);
1183 if (ok) {
1184 noHistoryChange = 1;
1185 item->setData(Qt::DisplayRole,
1186 QVariant(n + value));
1187 }
1188 }
1189 }
1190 noHistoryChange = 0;
1191 markHistory();
1192 }
1193
multiply(double value)1194 void EventSheet::multiply(double value)
1195 {
1196 QModelIndexList list = this->selectedIndexes();
1197 for (int i = 0; i < list.size(); i++) {
1198 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1199 if (item != 0 && item->data(Qt::DisplayRole).canConvert(QVariant::Double)) {
1200 bool ok = false;
1201 double n = item->data(Qt::DisplayRole).toDouble(&ok);
1202 if (ok) {
1203 noHistoryChange = 1;
1204 item->setData(Qt::DisplayRole,
1205 QVariant(n * value));
1206 }
1207 }
1208 }
1209 noHistoryChange = 0;
1210 markHistory();
1211 }
1212
divide(double value)1213 void EventSheet::divide(double value)
1214 {
1215 QModelIndexList list = this->selectedIndexes();
1216 for (int i = 0; i < list.size(); i++) {
1217 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1218 if (item != 0 && item->data(Qt::DisplayRole).canConvert(QVariant::Double)) {
1219 bool ok = false;
1220 double n = item->data(Qt::DisplayRole).toDouble(&ok);
1221 if (ok) {
1222 noHistoryChange = 1;
1223 item->setData(Qt::DisplayRole,
1224 QVariant(n / value));
1225 }
1226 }
1227 }
1228 noHistoryChange = 0;
1229 markHistory();
1230 }
1231
randomize(double min,double max,int mode)1232 void EventSheet::randomize(double min, double max, int mode)
1233 {
1234 // Mode 0 =
1235 // Mode 1 = integers only
1236 QModelIndexList list = this->selectedIndexes();
1237 QTime midnight(0, 0, 0);
1238 qsrand(midnight.secsTo(QTime::currentTime()));
1239 for (int i = 0; i < list.size(); i++) {
1240 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1241 if (item == 0) {
1242 item = new QTableWidgetItem();
1243 noHistoryChange = 1;
1244 this->setItem(list[i].row(), list[i].column(), item);
1245 }
1246 double value = 0.0;
1247 if (mode == 0) {
1248 value = min + ((double) qrand() / (double) RAND_MAX) * (max - min);
1249 }
1250 else /*if (mode == 1)*/ { // Integers only
1251 value = min + (qrand() % (int) (max - min + 1)); // Include max value as a possibility
1252 }
1253 noHistoryChange = 1;
1254 item->setData(Qt::DisplayRole,
1255 QVariant(value));
1256 }
1257 noHistoryChange = 0;
1258 markHistory();
1259 }
1260
shuffle(int iterations)1261 void EventSheet::shuffle(int iterations)
1262 {
1263 QTime midnight(0, 0, 0);
1264 qsrand(midnight.secsTo(QTime::currentTime()));
1265 QModelIndexList list = this->selectedIndexes();
1266 if (list.size() < 3)
1267 return;
1268 for (int i = 0; i < iterations; i++) { // First traverse to copy values
1269 int num1 = qrand() % list.size();
1270 QTableWidgetItem * item1 = this->item(list[num1].row(), list[num1].column());
1271 int num2 = qrand() % list.size();
1272 while (num2 == num1) {
1273 num2 = qrand() % list.size();
1274 }
1275 QTableWidgetItem * item2 = this->item(list[num2].row(), list[num2].column());
1276 QVariant value1 = QVariant();
1277 if (item1 != 0) {
1278 value1 = item1->data(Qt::DisplayRole);
1279 }
1280 else {
1281 item1 = new QTableWidgetItem();
1282 noHistoryChange = 1;
1283 this->setItem(list[i].row(), list[i].column(), item1);
1284 }
1285 if (item2 == 0) {
1286 item2 = new QTableWidgetItem();
1287 noHistoryChange = 1;
1288 this->setItem(list[i].row(), list[i].column(), item2);
1289 }
1290 noHistoryChange = 1;
1291 item1->setData(Qt::DisplayRole,item2->data(Qt::DisplayRole));
1292 noHistoryChange = 1;
1293 item2->setData(Qt::DisplayRole,value1);
1294 }
1295 noHistoryChange = 0;
1296 markHistory();
1297 }
1298
rotate(int amount)1299 void EventSheet::rotate(int amount)
1300 {
1301 QModelIndexList list = this->selectedIndexes();
1302 QList<QVariant> oldValues;
1303 for (int i = 0; i < list.size(); i++) { // First traverse to copy values
1304 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1305 if (item != 0) {
1306 oldValues.append(item->data(Qt::DisplayRole));
1307 }
1308 else {
1309 oldValues.append(QVariant());
1310 }
1311 }
1312 for (int i = 0; i < list.size(); i++) { // Then put in rotated values
1313 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1314 if (item == 0) {
1315 item = new QTableWidgetItem();
1316 noHistoryChange = 1;
1317 this->setItem(list[i].row(), list[i].column(), item);
1318 }
1319 int index = (i - amount + list.size())%list.size();
1320 noHistoryChange = 1;
1321 item->setData(Qt::DisplayRole,oldValues[index]);
1322 }
1323 noHistoryChange = 0;
1324 markHistory();
1325 }
1326
fill(double start,double end,double slope)1327 void EventSheet::fill(double start, double end, double slope)
1328 {
1329 QModelIndexList list = this->selectedIndexes();
1330 double inc = (end - start) / (list.size() - 1.0);
1331 double value = start;
1332 // double listSize = (double) list.size();
1333 for (int i = 0; i < list.size(); i++) {
1334 QTableWidgetItem * item = this->item(list[i].row(), list[i].column());
1335 if (item == 0) {
1336 item = new QTableWidgetItem();
1337 noHistoryChange = 1;
1338 this->setItem(list[i].row(), list[i].column(), item);
1339 }
1340 noHistoryChange = 1;
1341 item->setData(Qt::DisplayRole,
1342 QVariant(value));
1343 if (slope == 1.0) {
1344 value += inc;
1345 }
1346 else if (slope < 1.0 && slope >= 0.0) {
1347 value = start;
1348 value += (end-start) * (exp(((i + 1.0) / (list.size() - 1.0)) * log(slope))-1.0) / (slope-1.0);
1349 }
1350 else if (slope > 1.0) {
1351 value = start;
1352 value += (end-start) * (exp(((i + 1.0) / (list.size() - 1.0)) * log(slope))-1.0) / (slope-1.0);
1353 }
1354 }
1355 noHistoryChange = 0;
1356 markHistory();
1357 }
1358
createActions()1359 void EventSheet::createActions()
1360 {
1361 // For some reason, the shortcuts set here have no effect and need to be
1362 // decoded in keyPressEvent... (at least for linux)
1363 // cutAct = new QAction(/*QIcon(":/a.png"),*/ tr("Cut"), this);
1364 //// loopEventsAct->setIconText(tr("Loop Events"));
1365 // cutAct->setShortcut(QKeySequence(QKeySequence::Cut));
1366 // connect(cutAct, SIGNAL(triggered()), this, SLOT(cut()));
1367 //
1368 // copyAct = new QAction(/*QIcon(":/a.png"),*/ tr("Copy"), this);
1369 //// loopEventsAct->setIconText(tr("Loop Events"));
1370 // copyAct->setShortcut(QKeySequence(QKeySequence::Copy));
1371 // connect(copyAct, SIGNAL(triggered()), this, SLOT(copy()));
1372 //
1373 // pasteAct = new QAction(/*QIcon(":/a.png"),*/ tr("Paste"), this);
1374 //// loopEventsAct->setIconText(tr("Loop Events"));
1375 // pasteAct->setShortcut(QKeySequence(QKeySequence::Paste));
1376 // connect(pasteAct, SIGNAL(triggered()), this, SLOT(paste()));
1377
1378 sendEventsAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Send Events"), this);
1379 sendEventsAct->setStatusTip(tr("Send Events to Csound"));
1380 sendEventsAct->setIconText(tr("Send Events"));
1381 sendEventsAct->setShortcut(QKeySequence(QKeySequence::InsertLineSeparator));
1382 connect(sendEventsAct, SIGNAL(triggered()), this, SLOT(sendEvents()));
1383
1384 sendEventsOffsetAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Send Events without offset"), this);
1385 sendEventsOffsetAct->setStatusTip(tr("Send Events to Csound without offset"));
1386 sendEventsOffsetAct->setIconText(tr("Send Events no offset"));
1387 connect(sendEventsOffsetAct, SIGNAL(triggered()), this, SLOT(sendEventsOffset()));
1388
1389 loopSelectionAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Loop Selection"), this);
1390 loopSelectionAct->setStatusTip(tr("Mark loop to current selection and start looping"));
1391 loopSelectionAct->setIconText(tr("Loop Events"));
1392 connect(loopSelectionAct, SIGNAL(triggered()), this, SLOT(loopEvents()));
1393
1394 enableLoopAct = new QAction(/*QIcon(":/a.png"),*/ tr("Loop Active"), this);
1395 enableLoopAct->setStatusTip(tr("Activate Loop"));
1396 enableLoopAct->setCheckable(true);
1397 enableLoopAct->setChecked(false);
1398 connect(enableLoopAct, SIGNAL(toggled(bool)), this, SLOT(setLoopActive(bool)));
1399
1400 markLoopAct = new QAction(/*QIcon(":/a.png"),*/ tr("Mark Loop"), this);
1401 markLoopAct->setStatusTip(tr("Set Loop to selection, without starting loop"));
1402 connect(markLoopAct, SIGNAL(triggered()), this, SLOT(setLoopRange()));
1403
1404 stopAllEventsAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Stop Events"), this);
1405 stopAllEventsAct->setStatusTip(tr("Stop all running and pending events"));
1406 stopAllEventsAct->setIconText(tr("Stop Events"));
1407 connect(stopAllEventsAct, SIGNAL(triggered()), this, SLOT(stopAllEvents()));
1408
1409 subtractAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Subtract"), this);
1410 subtractAct->setStatusTip(tr("Subtract a value from the selected cells"));
1411 subtractAct->setIconText(tr("Subtract"));
1412 connect(subtractAct, SIGNAL(triggered()), this, SLOT(subtract()));
1413
1414 addAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Add"), this);
1415 addAct->setStatusTip(tr("Add a value to the selected cells"));
1416 addAct->setIconText(tr("Add"));
1417 connect(addAct, SIGNAL(triggered()), this, SLOT(add()));
1418
1419 multiplyAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Multiply"), this);
1420 multiplyAct->setStatusTip(tr("Multiply the selected cells by a value"));
1421 multiplyAct->setIconText(tr("Multiply"));
1422 connect(multiplyAct, SIGNAL(triggered()), this, SLOT(multiply()));
1423
1424 divideAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Divide"), this);
1425 divideAct->setStatusTip(tr("Divide the selected cells by a value"));
1426 divideAct->setIconText(tr("Divide"));
1427 connect(divideAct, SIGNAL(triggered()), this, SLOT(divide()));
1428
1429 randomizeAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Randomize"), this);
1430 randomizeAct->setStatusTip(tr("Randomize the selected cells"));
1431 randomizeAct->setIconText(tr("Randomize"));
1432 connect(randomizeAct, SIGNAL(triggered()), this, SLOT(randomize()));
1433
1434 reverseAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Reverse"), this);
1435 reverseAct->setStatusTip(tr("Reverse the selected cells by column"));
1436 reverseAct->setIconText(tr("Reverse"));
1437 connect(reverseAct, SIGNAL(triggered()), this, SLOT(reverse()));
1438
1439 shuffleAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Shuffle"), this);
1440 shuffleAct->setStatusTip(tr("Shuffle the selected cells"));
1441 shuffleAct->setIconText(tr("Shuffle"));
1442 connect(shuffleAct, SIGNAL(triggered()), this, SLOT(shuffle()));
1443
1444 // mirrorAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Mirror"), this);
1445 // mirrorAct->setStatusTip(tr("Mirror the selected cells"));
1446 // mirrorAct->setIconText(tr("Mirror"));
1447 // connect(mirrorAct, SIGNAL(triggered()), this, SLOT(mirror()));
1448
1449 rotateAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Rotate"), this);
1450 rotateAct->setStatusTip(tr("Rotate the selected cells"));
1451 rotateAct->setIconText(tr("Rotate"));
1452 connect(rotateAct, SIGNAL(triggered()), this, SLOT(rotate()));
1453
1454 fillAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Fill Cells"), this);
1455 fillAct->setStatusTip(tr("Fill selected cells"));
1456 fillAct->setIconText(tr("Fill"));
1457 connect(fillAct, SIGNAL(triggered()), this, SLOT(fill()));
1458
1459 insertColumnHereAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Insert Column"), this);
1460 insertColumnHereAct->setStatusTip(tr("Insert a column at the current position"));
1461 insertColumnHereAct->setIconText(tr("Insert Column"));
1462 connect(insertColumnHereAct, SIGNAL(triggered()), this, SLOT(insertColumnHere()));
1463
1464 insertRowHereAct = new QAction(/*QIcon(":/a.png"),*/ tr("Insert Row"), this);
1465 insertRowHereAct->setStatusTip(tr("Insert a row at the current position"));
1466 insertRowHereAct->setIconText(tr("Insert Row"));
1467 connect(insertRowHereAct, SIGNAL(triggered()), this, SLOT(insertRowHere()));
1468
1469 appendColumnAct = new QAction(/*QIcon(":/a.png"),*/ tr("Append Column"), this);
1470 appendColumnAct->setStatusTip(tr("Append a column to the sheet"));
1471 appendColumnAct->setIconText(tr("Append Column"));
1472 connect(appendColumnAct, SIGNAL(triggered()), this, SLOT(appendColumn()));
1473
1474 appendRowAct = new QAction(/*QIcon(":/a.png"),*/ tr("&Append Row"), this);
1475 appendRowAct->setStatusTip(tr("Append a row to the sheet"));
1476 appendRowAct->setIconText(tr("Append Row"));
1477 connect(appendRowAct, SIGNAL(triggered()), this, SLOT(appendRow()));
1478
1479 appendColumnsAct = new QAction(/*QIcon(":/a.png"),*/ tr("Append Columns..."), this);
1480 appendColumnsAct->setStatusTip(tr("Append columns to the sheet"));
1481 appendColumnsAct->setIconText(tr("Append Columns..."));
1482 connect(appendColumnsAct, SIGNAL(triggered()), this, SLOT(appendColumns()));
1483
1484 appendRowsAct = new QAction(/*QIcon(":/a.png"),*/ tr("Append Rows..."), this);
1485 appendRowsAct->setStatusTip(tr("Append rows to the sheet"));
1486 appendRowsAct->setIconText(tr("Append Rows..."));
1487 connect(appendRowsAct, SIGNAL(triggered()), this, SLOT(appendRows()));
1488
1489 deleteColumnAct = new QAction(/*QIcon(":/a.png"),*/ tr("Delete Last Column"), this);
1490 deleteColumnAct->setStatusTip(tr("Delete Last Column"));
1491 deleteColumnAct->setIconText(tr("Delete Last Column"));
1492 connect(deleteColumnAct, SIGNAL(triggered()), this, SLOT(deleteColumn()));
1493
1494 deleteRowAct = new QAction(/*QIcon(":/a.png"),*/ tr("Delete Selected Rows"), this);
1495 deleteRowAct->setStatusTip(tr("Delete Rows"));
1496 deleteRowAct->setIconText(tr("Delete Rows"));
1497 connect(deleteRowAct, SIGNAL(triggered()), this, SLOT(deleteRows()));
1498
1499 stopScriptAct = new QAction(/*QIcon(":/a.png"),*/ tr("Stop running script"), this);
1500 // stopScriptAct->setStatusTip(tr("Delete Rows"));
1501 // stopScriptAct->setIconText(tr("Delete Rows"));
1502 connect(stopScriptAct, SIGNAL(triggered()), this, SLOT(stopScript()));
1503 }
1504
parseLine(QString line)1505 QList<QPair<QString, QString> > EventSheet::parseLine(QString line)
1506 {
1507 QList<QPair<QString, QString> > list;
1508 QPair<QString, QString> field;
1509
1510 int count = 0;
1511 bool formula = false;
1512 bool string = false;
1513 bool isp = true; // Assume starting on a pfield, not white space
1514 QString pvalue = "";
1515 QString spacing = "";
1516 if (!line.isEmpty() && (line[0] == 'i' || line[0] == 'f') ) { // A space is not necessary between these and the first p-field
1517 field.first = QString(line[0]);
1518 field.second = QString();
1519 count++;
1520 isp = false; // consider p-field done with first character.
1521 }
1522 while (count < line.size()) { // More characters left
1523 if (isp == true || formula || string) { // Processing p-field
1524 if (line[count] == '"') { // string takes precedence over formulas and comments
1525 string = !string;
1526 }
1527 else if (line[count] == '[') { //Start of formula
1528 formula = true; // This should never happen as this character should always be after whitespace....
1529 }
1530 else if (line[count] == ']') { //End of formula
1531 formula = false;
1532 }
1533 else if (line[count] == ';') { // comment
1534 if (count > 0) {
1535 // First add current pfield, in case there is previous data
1536 field.first = pvalue;
1537 field.second = spacing;
1538 list.append(field);
1539 }
1540 // Now add comment
1541 count++;
1542 QString comment = line.mid(count);
1543 QStringList parts = comment.split(";");
1544 for (int i = 0; i < parts.size(); i++) {
1545 if (i==0) { // Only put the ; character visible on the first column
1546 field.first = ";" + parts[i];
1547 }
1548 else {
1549 field.first = parts[i];
1550 }
1551 field.second = "";
1552 list.append(field);
1553 }
1554 isp = false; // last p-field has been processed here
1555 break; // Nothing more todo for this line
1556 } // End of comment processing
1557 // ----
1558 if ( (line[count] == ' ' || line[count] == '\t') && !formula && !string) { // White space so p-field has finished
1559 spacing = line[count];
1560 isp = false;
1561 }
1562 else { // A character or formula so continue p-field processing
1563 pvalue.append(line[count]);
1564 field.first = pvalue;
1565 }
1566 }
1567 else { // Processing white space
1568 if (line[count] == '"') { // string
1569 string = !string;
1570 }
1571 else if (line[count] == '[') { //Start of formula
1572 formula = true;
1573 }
1574 else if (line[count] == ';') { // comment
1575 field.first = pvalue;
1576 field.second = spacing;
1577 list.append(field); //Should only append when pcount is incremented
1578 count++;
1579 QString comment = line.mid(count);
1580 QStringList parts = comment.split(";");
1581 for (int i = 0; i < parts.size(); i++) {
1582 if (i==0) { // Only put the ; character visible on the first column
1583 field.first = ";" + parts[i];
1584 }
1585 else {
1586 field.first = parts[i];
1587 }
1588 field.second = "";
1589 list.append(field);
1590 }
1591 isp = false; // last p-field has been processed here
1592 break; // Nothing more todo for this line
1593 }
1594 // ---
1595 if (line[count] != ' ' && line[count] != '\t') { // Not White space so new p-field has started
1596 field.second = spacing;
1597 list.append(field); //Should only append when pcount is incremented
1598 isp = true;
1599 pvalue = line[count];
1600 field.first = pvalue;
1601 field.second = "";
1602 spacing = "";
1603 }
1604 else { // Continue p-field processing
1605 spacing.append(line[count]);
1606 }
1607 }
1608 count++;
1609 }
1610 // Process final p-field
1611 if (isp == true) {
1612 field.first = pvalue;
1613 field.second = "";
1614 list.append(field);
1615 }
1616 return list;
1617 }
1618
newSelection()1619 void EventSheet::newSelection()
1620 {
1621 QModelIndexList list = this->selectedIndexes();
1622 if (list.size() > 1) {
1623 this->setDragDropMode(QAbstractItemView::DragDrop); // Allow dragging items
1624 }
1625 else {
1626 this->setDragDropMode(QAbstractItemView::NoDragDrop); // Allow extending selection
1627 }
1628 }
1629
cellDoubleClickedSlot(int,int)1630 void EventSheet::cellDoubleClickedSlot(int /*row*/, int /*column*/)
1631 {
1632 markHistory();
1633 }
1634
cellChangedSlot(int row,int column)1635 void EventSheet::cellChangedSlot(int row, int column)
1636 {
1637 if (this->item(row, column) != 0 && this->item(row, column)->data(Qt::DisplayRole).toString() != 0) {
1638 while (column > 0) {
1639 column--;
1640 QTableWidgetItem * item = this->item(row, column);
1641 // qDebug() << "EventSheet::cellChangedSlot " << column;
1642 if (item == 0 || item->data(Qt::DisplayRole).toString() == "") {
1643 this->setItem(row, column, this->takeItem(row, column + 1));
1644 }
1645 else {
1646 break;
1647 }
1648 }
1649 }
1650 if (noHistoryChange == 0) {
1651 markHistory();
1652 }
1653 else {
1654 noHistoryChange = 0;
1655 }
1656 emit modified();
1657 }
1658
stopScript()1659 void EventSheet::stopScript()
1660 {
1661 m_stopScript = true;
1662 }
1663