comparison framework/MainWindowBase.cpp @ 673:d62fd61082a1

Merge from branch tuning-difference
author Chris Cannam
date Fri, 17 May 2019 09:46:22 +0100
parents 331be52cd473
children 5e9b1956b609
comparison
equal deleted inserted replaced
665:e19c609a7bec 673:d62fd61082a1
20 #include "view/PaneStack.h" 20 #include "view/PaneStack.h"
21 #include "data/model/ReadOnlyWaveFileModel.h" 21 #include "data/model/ReadOnlyWaveFileModel.h"
22 #include "data/model/WritableWaveFileModel.h" 22 #include "data/model/WritableWaveFileModel.h"
23 #include "data/model/SparseOneDimensionalModel.h" 23 #include "data/model/SparseOneDimensionalModel.h"
24 #include "data/model/NoteModel.h" 24 #include "data/model/NoteModel.h"
25 #include "data/model/FlexiNoteModel.h"
26 #include "data/model/Labeller.h" 25 #include "data/model/Labeller.h"
27 #include "data/model/TabularModel.h" 26 #include "data/model/TabularModel.h"
28 #include "view/ViewManager.h" 27 #include "view/ViewManager.h"
29 28
30 #include "layer/WaveformLayer.h" 29 #include "layer/WaveformLayer.h"
53 52
54 #include "data/fileio/DataFileReaderFactory.h" 53 #include "data/fileio/DataFileReaderFactory.h"
55 #include "data/fileio/PlaylistFileReader.h" 54 #include "data/fileio/PlaylistFileReader.h"
56 #include "data/fileio/WavFileWriter.h" 55 #include "data/fileio/WavFileWriter.h"
57 #include "data/fileio/MIDIFileWriter.h" 56 #include "data/fileio/MIDIFileWriter.h"
57 #include "data/fileio/CSVFileWriter.h"
58 #include "data/fileio/BZipFileDevice.h" 58 #include "data/fileio/BZipFileDevice.h"
59 #include "data/fileio/FileSource.h" 59 #include "data/fileio/FileSource.h"
60 #include "data/fileio/AudioFileReaderFactory.h" 60 #include "data/fileio/AudioFileReaderFactory.h"
61 #include "rdf/RDFImporter.h" 61 #include "rdf/RDFImporter.h"
62 #include "rdf/RDFExporter.h"
62 63
63 #include "base/RecentFiles.h" 64 #include "base/RecentFiles.h"
64 65
65 #include "base/PlayParameterRepository.h" 66 #include "base/PlayParameterRepository.h"
66 #include "base/XmlExportable.h" 67 #include "base/XmlExportable.h"
70 #include "base/Exceptions.h" 71 #include "base/Exceptions.h"
71 #include "base/ResourceFinder.h" 72 #include "base/ResourceFinder.h"
72 73
73 #include "data/osc/OSCQueue.h" 74 #include "data/osc/OSCQueue.h"
74 #include "data/midi/MIDIInput.h" 75 #include "data/midi/MIDIInput.h"
76 #include "OSCScript.h"
75 77
76 #include "system/System.h" 78 #include "system/System.h"
77 79
78 #include <bqaudioio/SystemPlaybackTarget.h> 80 #include <bqaudioio/SystemPlaybackTarget.h>
79 #include <bqaudioio/SystemAudioIO.h> 81 #include <bqaudioio/SystemAudioIO.h>
147 m_resamplerWrapper(nullptr), 149 m_resamplerWrapper(nullptr),
148 m_playTarget(nullptr), 150 m_playTarget(nullptr),
149 m_audioIO(nullptr), 151 m_audioIO(nullptr),
150 m_oscQueue(nullptr), 152 m_oscQueue(nullptr),
151 m_oscQueueStarter(nullptr), 153 m_oscQueueStarter(nullptr),
154 m_oscScript(nullptr),
152 m_midiInput(nullptr), 155 m_midiInput(nullptr),
153 m_recentFiles("RecentFiles", 20), 156 m_recentFiles("RecentFiles", 20),
154 m_recentTransforms("RecentTransforms", 20), 157 m_recentTransforms("RecentTransforms", 20),
155 m_documentModified(false), 158 m_documentModified(false),
156 m_openingAudioFile(false), 159 m_openingAudioFile(false),
325 delete m_playSource; 328 delete m_playSource;
326 delete m_recordTarget; 329 delete m_recordTarget;
327 330
328 delete m_viewManager; 331 delete m_viewManager;
329 delete m_midiInput; 332 delete m_midiInput;
333
334 if (m_oscScript) {
335 disconnect(m_oscScript, nullptr, nullptr, nullptr);
336 m_oscScript->abandon();
337 m_oscScript->wait(1000);
338 if (m_oscScript->isRunning()) {
339 m_oscScript->terminate();
340 m_oscScript->wait(1000);
341 }
342 delete m_oscScript;
343 }
330 344
331 if (m_oscQueueStarter) { 345 if (m_oscQueueStarter) {
332 disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr); 346 disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr);
333 m_oscQueueStarter->wait(1000); 347 m_oscQueueStarter->wait(1000);
334 if (m_oscQueueStarter->isRunning()) { 348 if (m_oscQueueStarter->isRunning()) {
499 std::min(size.height(), available.height())); 513 std::min(size.height(), available.height()));
500 resize(actual); 514 resize(actual);
501 } 515 }
502 516
503 void 517 void
504 MainWindowBase::startOSCQueue() 518 MainWindowBase::startOSCQueue(bool withNetworkPort)
505 { 519 {
506 m_oscQueueStarter = new OSCQueueStarter(this); 520 m_oscQueueStarter = new OSCQueueStarter(this, withNetworkPort);
507 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady())); 521 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
508 m_oscQueueStarter->start(); 522 m_oscQueueStarter->start();
509 } 523 }
510 524
511 void 525 void
514 if (m_oscQueue && m_oscQueue->isOK()) { 528 if (m_oscQueue && m_oscQueue->isOK()) {
515 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); 529 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
516 QTimer *oscTimer = new QTimer(this); 530 QTimer *oscTimer = new QTimer(this);
517 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); 531 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
518 oscTimer->start(1000); 532 oscTimer->start(1000);
519 SVCERR << "Finished setting up OSC interface" << endl; 533
520 } 534 if (m_oscQueue->hasPort()) {
535 SVDEBUG << "Finished setting up OSC interface" << endl;
536 } else {
537 SVDEBUG << "Finished setting up internal-only OSC queue" << endl;
538 }
539
540 if (m_oscScriptFile != QString()) {
541 startOSCScript();
542 }
543 }
544 }
545
546 void
547 MainWindowBase::startOSCScript()
548 {
549 m_oscScript = new OSCScript(m_oscScriptFile, m_oscQueue);
550 connect(m_oscScript, SIGNAL(finished()),
551 this, SLOT(oscScriptFinished()));
552 m_oscScriptFile = QString();
553 m_oscScript->start();
554 }
555
556 void
557 MainWindowBase::cueOSCScript(QString fileName)
558 {
559 m_oscScriptFile = fileName;
560 if (m_oscQueue && m_oscQueue->isOK()) {
561 startOSCScript();
562 }
563 }
564
565 void
566 MainWindowBase::oscScriptFinished()
567 {
568 delete m_oscScript;
569 m_oscScript = 0;
521 } 570 }
522 571
523 QString 572 QString
524 MainWindowBase::getOpenFileName(FileFinder::FileType type) 573 MainWindowBase::getOpenFileName(FileFinder::FileType type)
525 { 574 {
697 bool recordDeviceFailed = (m_playTarget != nullptr && m_audioIO == nullptr); 746 bool recordDeviceFailed = (m_playTarget != nullptr && m_audioIO == nullptr);
698 emit canRecord(!recordDisabled && !recordDeviceFailed); 747 emit canRecord(!recordDisabled && !recordDeviceFailed);
699 } 748 }
700 749
701 void 750 void
751 MainWindowBase::updateWindowTitle()
752 {
753 QString title;
754
755 if (m_sessionFile != "") {
756 if (m_originalLocation != "" &&
757 m_originalLocation != m_sessionFile) { // session + location
758 title = tr("%1: %2 [%3]")
759 .arg(QApplication::applicationName())
760 .arg(QFileInfo(m_sessionFile).fileName())
761 .arg(m_originalLocation);
762 } else { // session only
763 title = tr("%1: %2")
764 .arg(QApplication::applicationName())
765 .arg(QFileInfo(m_sessionFile).fileName());
766 }
767 } else {
768 if (m_originalLocation != "") { // location only
769 title = tr("%1: %2")
770 .arg(QApplication::applicationName())
771 .arg(m_originalLocation);
772 } else { // neither
773 title = QApplication::applicationName();
774 }
775 }
776
777 if (m_documentModified) {
778 title = tr("%1 (modified)").arg(title);
779 }
780
781 setWindowTitle(title);
782 }
783
784 void
702 MainWindowBase::documentModified() 785 MainWindowBase::documentModified()
703 { 786 {
704 // SVDEBUG << "MainWindowBase::documentModified" << endl; 787 // SVDEBUG << "MainWindowBase::documentModified" << endl;
705 788
706 if (!m_documentModified) {
707 //!!! this in subclass implementation?
708 setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
709 }
710
711 m_documentModified = true; 789 m_documentModified = true;
790 updateWindowTitle();
712 updateMenuStates(); 791 updateMenuStates();
713 } 792 }
714 793
715 void 794 void
716 MainWindowBase::documentRestored() 795 MainWindowBase::documentRestored()
717 { 796 {
718 // SVDEBUG << "MainWindowBase::documentRestored" << endl; 797 // SVDEBUG << "MainWindowBase::documentRestored" << endl;
719 798
720 if (m_documentModified) {
721 //!!! this in subclass implementation?
722 QString wt(windowTitle());
723 wt.replace(tr(" (modified)"), "");
724 setWindowTitle(wt);
725 }
726
727 m_documentModified = false; 799 m_documentModified = false;
800 updateWindowTitle();
728 updateMenuStates(); 801 updateMenuStates();
729 } 802 }
730 803
731 void 804 void
732 MainWindowBase::playLoopToggled() 805 MainWindowBase::playLoopToggled()
1134 } 1207 }
1135 1208
1136 if (layer) { 1209 if (layer) {
1137 1210
1138 Model *model = layer->getModel(); 1211 Model *model = layer->getModel();
1139 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> 1212 SparseOneDimensionalModel *sodm =
1140 (model); 1213 dynamic_cast<SparseOneDimensionalModel *>(model);
1141 1214
1142 if (sodm) { 1215 if (sodm) {
1143 SparseOneDimensionalModel::Point point(frame, ""); 1216 Event point(frame, "");
1144 1217 Event prevPoint(0);
1145 SparseOneDimensionalModel::Point prevPoint(0);
1146 bool havePrevPoint = false; 1218 bool havePrevPoint = false;
1147 1219
1148 SparseOneDimensionalModel::EditCommand *command = 1220 ChangeEventsCommand *command =
1149 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); 1221 new ChangeEventsCommand(sodm, tr("Add Point"));
1150 1222
1151 if (m_labeller) { 1223 if (m_labeller) {
1152 1224
1153 if (m_labeller->requiresPrevPoint()) { 1225 if (m_labeller->requiresPrevPoint()) {
1154 1226
1155 SparseOneDimensionalModel::PointList prevPoints = 1227 if (sodm->getNearestEventMatching
1156 sodm->getPreviousPoints(frame); 1228 (frame,
1157 1229 [](Event) { return true; },
1158 if (!prevPoints.empty()) { 1230 EventSeries::Backward,
1159 prevPoint = *prevPoints.begin(); 1231 prevPoint)) {
1160 havePrevPoint = true; 1232 havePrevPoint = true;
1161 } 1233 }
1162 } 1234 }
1163 1235
1164 m_labeller->setSampleRate(sodm->getSampleRate()); 1236 m_labeller->setSampleRate(sodm->getSampleRate());
1165 1237
1166 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { 1238 Labeller::Relabelling relabelling = m_labeller->label
1167 command->deletePoint(prevPoint);
1168 }
1169
1170 m_labeller->label<SparseOneDimensionalModel::Point>
1171 (point, havePrevPoint ? &prevPoint : nullptr); 1239 (point, havePrevPoint ? &prevPoint : nullptr);
1172 1240
1173 if (m_labeller->actingOnPrevPoint() && havePrevPoint) { 1241 if (relabelling.first == Labeller::AppliesToPreviousEvent) {
1174 command->addPoint(prevPoint); 1242 command->remove(prevPoint);
1243 command->add(relabelling.second);
1244 } else {
1245 point = relabelling.second;
1175 } 1246 }
1176 } 1247 }
1177 1248
1178 command->addPoint(point); 1249 command->add(point);
1179 1250
1180 command->setName(tr("Add Point at %1 s") 1251 command->setName(tr("Add Point at %1 s")
1181 .arg(RealTime::frame2RealTime 1252 .arg(RealTime::frame2RealTime
1182 (frame, 1253 (frame,
1183 sodm->getSampleRate()) 1254 sodm->getSampleRate())
1229 Layer *layer = pane->getSelectedLayer(); 1300 Layer *layer = pane->getSelectedLayer();
1230 if (!layer) return; 1301 if (!layer) return;
1231 1302
1232 RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel()); 1303 RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel());
1233 if (rm) { 1304 if (rm) {
1234 RegionModel::Point point(alignedStart, 1305 Event point(alignedStart,
1235 rm->getValueMaximum() + 1, 1306 rm->getValueMaximum() + 1,
1236 alignedDuration, 1307 alignedDuration,
1237 ""); 1308 "");
1238 RegionModel::EditCommand *command = 1309 ChangeEventsCommand *command = new ChangeEventsCommand(rm, name);
1239 new RegionModel::EditCommand(rm, tr("Add Point")); 1310 command->add(point);
1240 command->addPoint(point);
1241 command->setName(name);
1242 c = command->finish(); 1311 c = command->finish();
1243 } 1312 }
1244 1313
1245 if (c) { 1314 if (c) {
1246 CommandHistory::getInstance()->addCommand(c, false); 1315 CommandHistory::getInstance()->addCommand(c, false);
1247 return; 1316 return;
1248 } 1317 }
1249 1318
1250 NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel()); 1319 NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel());
1251 if (nm) { 1320 if (nm) {
1252 NoteModel::Point point(alignedStart, 1321 Event point(alignedStart,
1253 nm->getValueMinimum(), 1322 nm->getValueMinimum(),
1254 alignedDuration, 1323 alignedDuration,
1255 1.f, 1324 1.f,
1256 ""); 1325 "");
1257 NoteModel::EditCommand *command = 1326 ChangeEventsCommand *command = new ChangeEventsCommand(nm, name);
1258 new NoteModel::EditCommand(nm, tr("Add Point")); 1327 command->add(point);
1259 command->addPoint(point);
1260 command->setName(name);
1261 c = command->finish(); 1328 c = command->finish();
1262 } 1329 }
1263 1330
1264 if (c) {
1265 CommandHistory::getInstance()->addCommand(c, false);
1266 return;
1267 }
1268
1269 FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel());
1270 if (fnm) {
1271 FlexiNoteModel::Point point(alignedStart,
1272 fnm->getValueMinimum(),
1273 alignedDuration,
1274 1.f,
1275 "");
1276 FlexiNoteModel::EditCommand *command =
1277 new FlexiNoteModel::EditCommand(fnm, tr("Add Point"));
1278 command->addPoint(point);
1279 command->setName(name);
1280 c = command->finish();
1281 }
1282
1283 if (c) { 1331 if (c) {
1284 CommandHistory::getInstance()->addCommand(c, false); 1332 CommandHistory::getInstance()->addCommand(c, false);
1285 return; 1333 return;
1286 } 1334 }
1287 } 1335 }
1304 1352
1305 if (!m_labeller) return; 1353 if (!m_labeller) return;
1306 1354
1307 Labeller labeller(*m_labeller); 1355 Labeller labeller(*m_labeller);
1308 labeller.setSampleRate(sodm->getSampleRate()); 1356 labeller.setSampleRate(sodm->getSampleRate());
1309 1357 /*!!! to be updated after SODM API update
1310 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); 1358 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
1311 if (c) CommandHistory::getInstance()->addCommand(c, false); 1359 if (c) CommandHistory::getInstance()->addCommand(c, false);
1360 */
1312 } 1361 }
1313 1362
1314 void 1363 void
1315 MainWindowBase::subdivideInstantsBy(int n) 1364 MainWindowBase::subdivideInstantsBy(int n)
1316 { 1365 {
1330 if (!m_labeller) return; 1379 if (!m_labeller) return;
1331 1380
1332 Labeller labeller(*m_labeller); 1381 Labeller labeller(*m_labeller);
1333 labeller.setSampleRate(sodm->getSampleRate()); 1382 labeller.setSampleRate(sodm->getSampleRate());
1334 1383
1384 (void)n;
1385 /*!!! to be updated after SODM API update
1335 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point> 1386 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point>
1336 (*sodm, &ms, n); 1387 (*sodm, &ms, n);
1337 if (c) CommandHistory::getInstance()->addCommand(c, false); 1388 if (c) CommandHistory::getInstance()->addCommand(c, false);
1389 */
1338 } 1390 }
1339 1391
1340 void 1392 void
1341 MainWindowBase::winnowInstantsBy(int n) 1393 MainWindowBase::winnowInstantsBy(int n)
1342 { 1394 {
1356 if (!m_labeller) return; 1408 if (!m_labeller) return;
1357 1409
1358 Labeller labeller(*m_labeller); 1410 Labeller labeller(*m_labeller);
1359 labeller.setSampleRate(sodm->getSampleRate()); 1411 labeller.setSampleRate(sodm->getSampleRate());
1360 1412
1413 (void)n;
1414 /*!!! to be updated after SODM API update
1361 Command *c = labeller.winnow<SparseOneDimensionalModel::Point> 1415 Command *c = labeller.winnow<SparseOneDimensionalModel::Point>
1362 (*sodm, &ms, n); 1416 (*sodm, &ms, n);
1363 if (c) CommandHistory::getInstance()->addCommand(c, false); 1417 if (c) CommandHistory::getInstance()->addCommand(c, false);
1418 */
1364 } 1419 }
1365 1420
1366 MainWindowBase::FileOpenStatus 1421 MainWindowBase::FileOpenStatus
1367 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode) 1422 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
1368 { 1423 {
1630 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl; 1685 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
1631 mode = ReplaceMainModel; 1686 mode = ReplaceMainModel;
1632 } 1687 }
1633 1688
1634 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation())); 1689 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
1635 1690
1636 if (mode == ReplaceMainModel) { 1691 if (mode == ReplaceMainModel) {
1637 1692
1638 Model *prevMain = getMainModel(); 1693 Model *prevMain = getMainModel();
1639 if (prevMain) { 1694 if (prevMain) {
1640 m_playSource->removeModel(prevMain); 1695 m_playSource->removeModel(prevMain);
1646 1701
1647 m_document->setMainModel(newModel); 1702 m_document->setMainModel(newModel);
1648 1703
1649 setupMenus(); 1704 setupMenus();
1650 1705
1706 m_originalLocation = source.getLocation();
1707
1651 if (loadedTemplate || (m_sessionFile == "")) { 1708 if (loadedTemplate || (m_sessionFile == "")) {
1652 //!!! shouldn't be dealing directly with title from here -- call a method
1653 setWindowTitle(tr("%1: %2")
1654 .arg(QApplication::applicationName())
1655 .arg(source.getLocation()));
1656 CommandHistory::getInstance()->clear(); 1709 CommandHistory::getInstance()->clear();
1657 CommandHistory::getInstance()->documentSaved(); 1710 CommandHistory::getInstance()->documentSaved();
1658 m_documentModified = false; 1711 m_documentModified = false;
1659 } else { 1712 } else {
1660 setWindowTitle(tr("%1: %2 [%3]")
1661 .arg(QApplication::applicationName())
1662 .arg(QFileInfo(m_sessionFile).fileName())
1663 .arg(source.getLocation()));
1664 if (m_documentModified) { 1713 if (m_documentModified) {
1665 m_documentModified = false; 1714 m_documentModified = false;
1666 documentModified(); // so as to restore "(modified)" window title
1667 } 1715 }
1668 } 1716 }
1669 1717
1670 if (!source.isRemote() && registerSource) { 1718 if (!source.isRemote() && registerSource) {
1671 m_audioFile = source.getLocalFilename(); 1719 m_audioFile = source.getLocalFilename();
1672 } 1720 }
1673 1721
1722 updateWindowTitle();
1723
1674 } else if (mode == CreateAdditionalModel) { 1724 } else if (mode == CreateAdditionalModel) {
1675 1725
1676 SVCERR << "Mode is CreateAdditionalModel" << endl; 1726 SVCERR << "Mode is CreateAdditionalModel" << endl;
1677 1727
1678 CommandHistory::getInstance()->startCompoundOperation 1728 CommandHistory::getInstance()->startCompoundOperation
2131 2181
2132 if (ok) { 2182 if (ok) {
2133 2183
2134 emit activity(tr("Import session file \"%1\"").arg(source.getLocation())); 2184 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
2135 2185
2136 setWindowTitle(tr("%1: %2")
2137 .arg(QApplication::applicationName())
2138 .arg(source.getLocation()));
2139
2140 if (!source.isRemote() && !m_document->isIncomplete()) { 2186 if (!source.isRemote() && !m_document->isIncomplete()) {
2141 // Setting the session file path enables the Save (as 2187 // Setting the session file path enables the Save (as
2142 // opposed to Save As...) option. We can't do this if we 2188 // opposed to Save As...) option. We can't do this if we
2143 // don't have a local path to save to, but we also don't 2189 // don't have a local path to save to, but we also don't
2144 // want to do it if we failed to find an audio file or 2190 // want to do it if we failed to find an audio file or
2151 tr("Incomplete session loaded"), 2197 tr("Incomplete session loaded"),
2152 tr("Some of the audio content referred to by the original session file could not be loaded.\nIf you save this session, it will be saved without any reference to that audio, and information may be lost."), 2198 tr("Some of the audio content referred to by the original session file could not be loaded.\nIf you save this session, it will be saved without any reference to that audio, and information may be lost."),
2153 QMessageBox::Ok); 2199 QMessageBox::Ok);
2154 } 2200 }
2155 2201
2202 updateWindowTitle();
2156 setupMenus(); 2203 setupMenus();
2157 findTimeRulerLayer(); 2204 findTimeRulerLayer();
2158 2205
2159 CommandHistory::getInstance()->clear(); 2206 CommandHistory::getInstance()->clear();
2160 CommandHistory::getInstance()->documentSaved(); 2207 CommandHistory::getInstance()->documentSaved();
2167 // for file dialog 2214 // for file dialog
2168 registerLastOpenedFilePath(FileFinder::SessionFile, 2215 registerLastOpenedFilePath(FileFinder::SessionFile,
2169 source.getLocalFilename()); 2216 source.getLocalFilename());
2170 } 2217 }
2171 2218
2219 m_originalLocation = source.getLocation();
2220
2172 emit sessionLoaded(); 2221 emit sessionLoaded();
2173 2222
2174 } else { 2223 updateWindowTitle();
2175 setWindowTitle(QApplication::applicationName()); 2224 }
2176 } 2225
2177
2178 return ok ? FileOpenSucceeded : FileOpenFailed; 2226 return ok ? FileOpenSucceeded : FileOpenFailed;
2179 } 2227 }
2180 2228
2181 MainWindowBase::FileOpenStatus 2229 MainWindowBase::FileOpenStatus
2182 MainWindowBase::openSessionTemplate(QString templateName) 2230 MainWindowBase::openSessionTemplate(QString templateName)
2238 delete inputSource; 2286 delete inputSource;
2239 delete file; 2287 delete file;
2240 2288
2241 bool ok = (error == ""); 2289 bool ok = (error == "");
2242 2290
2243 setWindowTitle(QApplication::applicationName());
2244
2245 if (ok) { 2291 if (ok) {
2246 2292
2247 emit activity(tr("Open session template \"%1\"").arg(source.getLocation())); 2293 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
2248 2294
2249 setupMenus(); 2295 setupMenus();
2255 updateMenuStates(); 2301 updateMenuStates();
2256 2302
2257 emit sessionLoaded(); 2303 emit sessionLoaded();
2258 } 2304 }
2259 2305
2306 updateWindowTitle();
2307
2260 return ok ? FileOpenSucceeded : FileOpenFailed; 2308 return ok ? FileOpenSucceeded : FileOpenFailed;
2261 } 2309 }
2262 2310
2263 MainWindowBase::FileOpenStatus 2311 MainWindowBase::FileOpenStatus
2264 MainWindowBase::openSessionFromRDF(FileSource source) 2312 MainWindowBase::openSessionFromRDF(FileSource source)
2277 2325
2278 FileOpenStatus status = openLayersFromRDF(source); 2326 FileOpenStatus status = openLayersFromRDF(source);
2279 2327
2280 setupMenus(); 2328 setupMenus();
2281 findTimeRulerLayer(); 2329 findTimeRulerLayer();
2282 2330
2283 setWindowTitle(tr("%1: %2")
2284 .arg(QApplication::applicationName())
2285 .arg(source.getLocation()));
2286 CommandHistory::getInstance()->clear(); 2331 CommandHistory::getInstance()->clear();
2287 CommandHistory::getInstance()->documentSaved(); 2332 CommandHistory::getInstance()->documentSaved();
2288 m_documentModified = false; 2333 m_documentModified = false;
2334 updateWindowTitle();
2289 2335
2290 emit sessionLoaded(); 2336 emit sessionLoaded();
2291 2337
2292 return status; 2338 return status;
2293 } 2339 }
2647 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)), 2693 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)),
2648 this, SLOT(alignmentComplete(AlignmentModel *))); 2694 this, SLOT(alignmentComplete(AlignmentModel *)));
2649 connect(m_document, SIGNAL(alignmentFailed(QString)), 2695 connect(m_document, SIGNAL(alignmentFailed(QString)),
2650 this, SLOT(alignmentFailed(QString))); 2696 this, SLOT(alignmentFailed(QString)));
2651 2697
2698 m_document->setAutoAlignment(m_viewManager->getAlignMode());
2699
2652 emit replacedDocument(); 2700 emit replacedDocument();
2653 } 2701 }
2654 2702
2655 bool 2703 bool
2656 MainWindowBase::saveSessionFile(QString path) 2704 MainWindowBase::saveSessionFile(QString path)
2732 QMessageBox::critical(this, tr("Failed to write file"), 2780 QMessageBox::critical(this, tr("Failed to write file"),
2733 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2") 2781 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
2734 .arg(path).arg(f.what())); 2782 .arg(path).arg(f.what()));
2735 return false; 2783 return false;
2736 } 2784 }
2785 }
2786
2787 bool
2788 MainWindowBase::exportLayerTo(Layer *layer, QString path, QString &error)
2789 {
2790 if (QFileInfo(path).suffix() == "") path += ".svl";
2791
2792 QString suffix = QFileInfo(path).suffix().toLower();
2793
2794 Model *model = layer->getModel();
2795
2796 if (suffix == "xml" || suffix == "svl") {
2797
2798 QFile file(path);
2799 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
2800 error = tr("Failed to open file %1 for writing").arg(path);
2801 } else {
2802 QTextStream out(&file);
2803 out.setCodec(QTextCodec::codecForName("UTF-8"));
2804 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2805 << "<!DOCTYPE sonic-visualiser>\n"
2806 << "<sv>\n"
2807 << " <data>\n";
2808
2809 model->toXml(out, " ");
2810
2811 out << " </data>\n"
2812 << " <display>\n";
2813
2814 layer->toXml(out, " ");
2815
2816 out << " </display>\n"
2817 << "</sv>\n";
2818 }
2819
2820 } else if (suffix == "mid" || suffix == "midi") {
2821
2822 NoteModel *nm = dynamic_cast<NoteModel *>(model);
2823
2824 if (!nm) {
2825 error = tr("Can't export non-note layers to MIDI");
2826 } else {
2827 MIDIFileWriter writer(path, nm, nm->getSampleRate());
2828 writer.write();
2829 if (!writer.isOK()) {
2830 error = writer.getError();
2831 }
2832 }
2833
2834 } else if (suffix == "ttl" || suffix == "n3") {
2835
2836 if (!RDFExporter::canExportModel(model)) {
2837 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
2838 } else {
2839 RDFExporter exporter(path, model);
2840 exporter.write();
2841 if (!exporter.isOK()) {
2842 error = exporter.getError();
2843 }
2844 }
2845
2846 } else {
2847
2848 CSVFileWriter writer(path, model,
2849 ((suffix == "csv") ? "," : "\t"));
2850 writer.write();
2851
2852 if (!writer.isOK()) {
2853 error = writer.getError();
2854 }
2855 }
2856
2857 return (error == "");
2737 } 2858 }
2738 2859
2739 void 2860 void
2740 MainWindowBase::toXml(QTextStream &out, bool asTemplate) 2861 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
2741 { 2862 {
3179 3300
3180 m_document->setMainModel(model); 3301 m_document->setMainModel(model);
3181 setupMenus(); 3302 setupMenus();
3182 findTimeRulerLayer(); 3303 findTimeRulerLayer();
3183 3304
3305 m_originalLocation = model->getLocation();
3306
3184 if (loadedTemplate || (m_sessionFile == "")) { 3307 if (loadedTemplate || (m_sessionFile == "")) {
3185 //!!! shouldn't be dealing directly with title from here -- call a method
3186 setWindowTitle(tr("%1: %2")
3187 .arg(QApplication::applicationName())
3188 .arg(model->getLocation()));
3189 CommandHistory::getInstance()->clear(); 3308 CommandHistory::getInstance()->clear();
3190 CommandHistory::getInstance()->documentSaved(); 3309 CommandHistory::getInstance()->documentSaved();
3191 m_documentModified = false; 3310 }
3192 } else { 3311
3193 setWindowTitle(tr("%1: %2 [%3]") 3312 m_documentModified = false;
3194 .arg(QApplication::applicationName()) 3313 updateWindowTitle();
3195 .arg(QFileInfo(m_sessionFile).fileName())
3196 .arg(model->getLocation()));
3197 if (m_documentModified) {
3198 m_documentModified = false;
3199 documentModified(); // so as to restore "(modified)" window title
3200 }
3201 }
3202 3314
3203 } else { 3315 } else {
3204 3316
3205 CommandHistory::getInstance()->startCompoundOperation 3317 CommandHistory::getInstance()->startCompoundOperation
3206 (tr("Import Recorded Audio"), true); 3318 (tr("Import Recorded Audio"), true);