lbajardsilogic@19: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@19: lbajardsilogic@19: /* Sound Access lbajardsilogic@19: EASAIER client application. lbajardsilogic@19: Silogic 2007. Laure Bajard. lbajardsilogic@19: lbajardsilogic@19: This program is free software; you can redistribute it and/or lbajardsilogic@19: modify it under the terms of the GNU General Public License as lbajardsilogic@19: published by the Free Software Foundation; either version 2 of the lbajardsilogic@19: License, or (at your option) any later version. See the file lbajardsilogic@19: COPYING included with this distribution for more information. lbajardsilogic@19: */ lbajardsilogic@19: lbajardsilogic@19: #include lbajardsilogic@19: #include lbajardsilogic@19: #include lbajardsilogic@19: #include lbajardsilogic@19: lbajardsilogic@19: #include lbajardsilogic@19: lbajardsilogic@19: #include "ESFileReader.h" lbajardsilogic@19: lbajardsilogic@19: #include "layer/Layer.h" lbajardsilogic@19: #include "view/View.h" lbajardsilogic@19: #include "base/PlayParameters.h" lbajardsilogic@19: #include "base/PlayParameterRepository.h" lbajardsilogic@19: #include "base/TempDirectory.h" lbajardsilogic@19: #include "data/fileio/AudioFileReaderFactory.h" lbajardsilogic@19: #include "data/model/WaveFileModel.h" lbajardsilogic@19: #include "data/model/DenseThreeDimensionalModel.h" lbajardsilogic@19: #include "data/model/SparseOneDimensionalModel.h" lbajardsilogic@19: #include "data/model/SparseTimeValueModel.h" lbajardsilogic@19: #include "data/model/NoteModel.h" lbajardsilogic@19: #include "data/model/TextModel.h" lbajardsilogic@19: #include "view/Pane.h" lbajardsilogic@19: #include "document/Document.h" lbajardsilogic@19: lbajardsilogic@19: lbajardsilogic@19: ESFileReader::ESFileReader(Document *document, lbajardsilogic@19: ESFileReaderPaneCallback &callback) : QXmlDefaultHandler(), lbajardsilogic@19: m_document(document), lbajardsilogic@19: m_paneCallback(callback), lbajardsilogic@19: m_currentPane(0), lbajardsilogic@19: m_inView(false), lbajardsilogic@19: m_inData(false), lbajardsilogic@19: m_ok(false) lbajardsilogic@19: {} lbajardsilogic@19: lbajardsilogic@19: void lbajardsilogic@19: ESFileReader::parse(const QString &xmlData) lbajardsilogic@19: { lbajardsilogic@19: QXmlInputSource inputSource; lbajardsilogic@19: inputSource.setData(xmlData); lbajardsilogic@19: parse(inputSource); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: void lbajardsilogic@19: ESFileReader::parse(QXmlInputSource &inputSource) lbajardsilogic@19: { lbajardsilogic@19: QXmlSimpleReader reader; lbajardsilogic@19: reader.setContentHandler(this); lbajardsilogic@19: reader.setErrorHandler(this); lbajardsilogic@19: m_ok = reader.parse(inputSource); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::isOK() lbajardsilogic@19: { lbajardsilogic@19: return m_ok; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: ESFileReader::~ESFileReader() lbajardsilogic@19: {} lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::startElement(const QString &, const QString &, lbajardsilogic@19: const QString &qName, lbajardsilogic@19: const QXmlAttributes &attributes) lbajardsilogic@19: { lbajardsilogic@19: QString name = qName.toLower(); lbajardsilogic@19: lbajardsilogic@19: bool ok = false; lbajardsilogic@19: lbajardsilogic@19: // Valid element names: lbajardsilogic@19: // lbajardsilogic@19: // easaiersession lbajardsilogic@19: // data lbajardsilogic@19: // easaierresources lbajardsilogic@19: // audio lbajardsilogic@19: // display lbajardsilogic@19: // window lbajardsilogic@19: // view lbajardsilogic@19: // layer lbajardsilogic@19: lbajardsilogic@19: if (name == "easaiersession") { lbajardsilogic@19: // nothing needed lbajardsilogic@19: ok = true; lbajardsilogic@19: lbajardsilogic@19: } else if (name == "data") { lbajardsilogic@19: // nothing needed lbajardsilogic@19: m_inData = true; lbajardsilogic@19: ok = true; lbajardsilogic@19: lbajardsilogic@19: } else if (name == "easaierresources") { lbajardsilogic@19: // nothing needed lbajardsilogic@19: ok = true; lbajardsilogic@19: lbajardsilogic@19: } else if (name == "audio") { lbajardsilogic@19: QString filename = attributes.value("value"); lbajardsilogic@19: m_document->setAudioSourceInfoFileName(filename); lbajardsilogic@19: lbajardsilogic@19: } else if (name == "display") { lbajardsilogic@19: // nothing needed lbajardsilogic@19: ok = true; lbajardsilogic@19: lbajardsilogic@19: } else if (name == "window") { lbajardsilogic@19: ok = readWindow(attributes); lbajardsilogic@19: lbajardsilogic@19: } else if (name == "view") { lbajardsilogic@19: m_inView = true; lbajardsilogic@19: ok = readView(attributes); lbajardsilogic@19: lbajardsilogic@19: } else if (name == "layer") { lbajardsilogic@19: ok = readLayer(attributes); lbajardsilogic@19: lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: if (!ok) { lbajardsilogic@19: std::cerr << "WARNING: Easaier Session-XML: Failed to completely process element \"" lbajardsilogic@19: << name.toLocal8Bit().data() << "\"" << std::endl; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::characters(const QString &text) lbajardsilogic@19: { lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::endElement(const QString &, const QString &, lbajardsilogic@19: const QString &qName) lbajardsilogic@19: { lbajardsilogic@19: QString name = qName.toLower(); lbajardsilogic@19: lbajardsilogic@19: if (name == "data") { lbajardsilogic@19: lbajardsilogic@19: m_inData = false; lbajardsilogic@19: lbajardsilogic@19: } else if (name == "view") { lbajardsilogic@19: m_inView = false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::error(const QXmlParseException &exception) lbajardsilogic@19: { lbajardsilogic@19: m_errorString = lbajardsilogic@19: QString("ERROR: Easaier Session-XML: %1 at line %2, column %3") lbajardsilogic@19: .arg(exception.message()) lbajardsilogic@19: .arg(exception.lineNumber()) lbajardsilogic@19: .arg(exception.columnNumber()); lbajardsilogic@19: std::cerr << m_errorString.toLocal8Bit().data() << std::endl; lbajardsilogic@19: return QXmlDefaultHandler::error(exception); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::fatalError(const QXmlParseException &exception) lbajardsilogic@19: { lbajardsilogic@19: m_errorString = lbajardsilogic@19: QString("FATAL ERROR: Easaier Session-XML: %1 at line %2, column %3") lbajardsilogic@19: .arg(exception.message()) lbajardsilogic@19: .arg(exception.lineNumber()) lbajardsilogic@19: .arg(exception.columnNumber()); lbajardsilogic@19: std::cerr << m_errorString.toLocal8Bit().data() << std::endl; lbajardsilogic@19: return QXmlDefaultHandler::fatalError(exception); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: lbajardsilogic@19: #define READ_MANDATORY(TYPE, NAME, CONVERSION) \ lbajardsilogic@19: TYPE NAME = attributes.value(#NAME).trimmed().CONVERSION(&ok); \ lbajardsilogic@19: if (!ok) { \ lbajardsilogic@19: std::cerr << "WARNING: Easaier Session-XML: Missing or invalid mandatory " #TYPE " attribute \"" #NAME "\"" << std::endl; \ lbajardsilogic@19: return false; \ lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::readWindow(const QXmlAttributes &attributes) lbajardsilogic@19: { lbajardsilogic@19: bool ok = false; lbajardsilogic@19: lbajardsilogic@19: READ_MANDATORY(int, width, toInt); lbajardsilogic@19: READ_MANDATORY(int, height, toInt); lbajardsilogic@19: lbajardsilogic@19: m_paneCallback.setWindowSize(width, height); lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::readView(const QXmlAttributes &attributes) lbajardsilogic@19: { lbajardsilogic@19: QString type = attributes.value("type"); lbajardsilogic@19: m_currentPane = 0; lbajardsilogic@19: lbajardsilogic@19: if (type != "pane") { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: Unexpected view type \"" lbajardsilogic@19: << type.toLocal8Bit().data() << "\"" << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: m_currentPane = m_paneCallback.addPane(); lbajardsilogic@19: lbajardsilogic@19: if (!m_currentPane) { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: Internal error: Failed to add pane!" lbajardsilogic@19: << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool ok = false; lbajardsilogic@19: lbajardsilogic@19: View *view = m_currentPane; lbajardsilogic@19: lbajardsilogic@19: // The view properties first lbajardsilogic@19: lbajardsilogic@19: READ_MANDATORY(size_t, centre, toUInt); lbajardsilogic@19: READ_MANDATORY(size_t, zoom, toUInt); lbajardsilogic@19: READ_MANDATORY(int, followPan, toInt); lbajardsilogic@19: READ_MANDATORY(int, followZoom, toInt); lbajardsilogic@19: QString tracking = attributes.value("tracking"); lbajardsilogic@19: lbajardsilogic@19: // Specify the follow modes before we set the actual values lbajardsilogic@19: view->setFollowGlobalPan(followPan); lbajardsilogic@19: view->setFollowGlobalZoom(followZoom); lbajardsilogic@19: view->setPlaybackFollow(tracking == "scroll" ? PlaybackScrollContinuous : lbajardsilogic@19: tracking == "page" ? PlaybackScrollPage lbajardsilogic@19: : PlaybackIgnore); lbajardsilogic@19: lbajardsilogic@19: // Then set these values lbajardsilogic@19: view->setCentreFrame(centre); lbajardsilogic@19: view->setZoomLevel(zoom); lbajardsilogic@19: lbajardsilogic@19: // And pane properties lbajardsilogic@19: READ_MANDATORY(int, centreLineVisible, toInt); lbajardsilogic@19: m_currentPane->setCentreLineVisible(centreLineVisible); lbajardsilogic@19: lbajardsilogic@19: int height = attributes.value("height").toInt(&ok); lbajardsilogic@19: if (ok) { lbajardsilogic@19: m_currentPane->resize(m_currentPane->width(), height); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: bool lbajardsilogic@19: ESFileReader::readLayer(const QXmlAttributes &attributes) lbajardsilogic@19: { lbajardsilogic@19: QString type = attributes.value("type"); lbajardsilogic@19: lbajardsilogic@19: int id; lbajardsilogic@19: bool ok = false; lbajardsilogic@19: id = attributes.value("id").trimmed().toInt(&ok); lbajardsilogic@19: lbajardsilogic@19: if (!ok) { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: No layer id for layer of type \"" lbajardsilogic@19: << type.toLocal8Bit().data() lbajardsilogic@19: << "\"" << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: Layer *layer = 0; lbajardsilogic@19: bool isNewLayer = false; lbajardsilogic@19: lbajardsilogic@19: // Layers are expected to be defined in layer elements in the data lbajardsilogic@19: // section, and referred to in layer elements in the view lbajardsilogic@19: // sections. So if we're in the data section, we expect this lbajardsilogic@19: // layer not to exist already; if we're in the view section, we lbajardsilogic@19: // expect it to exist. lbajardsilogic@19: lbajardsilogic@19: if (m_inData) { lbajardsilogic@19: lbajardsilogic@19: if (m_layers.find(id) != m_layers.end()) { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: Ignoring duplicate layer id " << id lbajardsilogic@19: << " in data section" << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: layer = m_layers[id] = m_document->createLayer lbajardsilogic@19: (LayerFactory::getInstance()->getLayerTypeForName(type)); lbajardsilogic@19: lbajardsilogic@19: if (layer) { lbajardsilogic@19: m_layers[id] = layer; lbajardsilogic@19: isNewLayer = true; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: } else { lbajardsilogic@19: lbajardsilogic@19: if (!m_currentPane) { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: No current pane for layer " << id lbajardsilogic@19: << " in view section" << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: if (m_layers.find(id) != m_layers.end()) { lbajardsilogic@19: lbajardsilogic@19: layer = m_layers[id]; lbajardsilogic@19: lbajardsilogic@19: } else { lbajardsilogic@19: layer = m_document->createLayer(LayerFactory::getInstance()->getLayerTypeForName(type)); lbajardsilogic@19: lbajardsilogic@19: if (layer) { lbajardsilogic@19: m_layers[id] = layer; lbajardsilogic@19: isNewLayer = true; lbajardsilogic@19: } lbajardsilogic@19: } lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: if (!layer) { lbajardsilogic@19: std::cerr << "WARNING: Easaier session-XML: Failed to add layer of type \"" lbajardsilogic@19: << type.toLocal8Bit().data() lbajardsilogic@19: << "\"" << std::endl; lbajardsilogic@19: return false; lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: if (isNewLayer) { lbajardsilogic@19: lbajardsilogic@19: QString name = attributes.value("name"); lbajardsilogic@19: layer->setObjectName(name); lbajardsilogic@19: lbajardsilogic@19: QString modelName = attributes.value("model"); lbajardsilogic@19: int modelId = attributes.value("modelId").toInt(); lbajardsilogic@19: lbajardsilogic@19: layer->setModelName(modelName); lbajardsilogic@19: layer->setModelId(modelId); lbajardsilogic@19: lbajardsilogic@19: layer->setProperties(attributes); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: if (!m_inData && m_currentPane) { lbajardsilogic@19: m_document->addLayerToView(m_currentPane, layer); lbajardsilogic@19: } lbajardsilogic@19: lbajardsilogic@19: return true; lbajardsilogic@19: } lbajardsilogic@19: