/*
    Rosegarden
    A MIDI and audio sequencer and musical notation editor.
 
    This program is Copyright 2000-2008
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <richard.bown@ferventsoftware.com>
 
    The moral rights of Guillaume Laurent, Chris Cannam, and Richard
    Bown to claim authorship of this work have been asserted.
 
    Other copyrights also apply to some parts of this work.  Please
    see the AUTHORS file and individual file headers for details.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/


#include "RoseXmlHandler.h"

#include "sound/Midi.h"
#include <tdelocale.h>
#include "misc/Debug.h"
#include "misc/Strings.h"
#include "base/AudioLevel.h"
#include "base/AudioPluginInstance.h"
#include "base/BaseProperties.h"
#include "base/Colour.h"
#include "base/ColourMap.h"
#include "base/Composition.h"
#include "base/ControlParameter.h"
#include "base/Device.h"
#include "base/Instrument.h"
#include "base/Marker.h"
#include "base/MidiDevice.h"
#include "base/MidiProgram.h"
#include "base/MidiTypes.h"
#include "base/NotationTypes.h"
#include "base/RealTime.h"
#include "base/Segment.h"
#include "base/Studio.h"
#include "base/Track.h"
#include "base/TriggerSegment.h"
#include "gui/application/RosegardenGUIApp.h"
#include "gui/application/RosegardenApplication.h"
#include "gui/dialogs/FileLocateDialog.h"
#include "gui/general/ProgressReporter.h"
#include "gui/kdeext/TDEStartupLogo.h"
#include "gui/studio/AudioPlugin.h"
#include "gui/studio/AudioPluginManager.h"
#include "gui/widgets/CurrentProgressDialog.h"
#include "gui/widgets/ProgressDialog.h"
#include "RosegardenGUIDoc.h"
#include "sound/AudioFileManager.h"
#include <tdefiledialog.h>
#include <tdemessagebox.h>
#include <tqcstring.h>
#include <tqdatastream.h>
#include <tqdialog.h>
#include <tqfileinfo.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include "XmlStorableEvent.h"
#include "XmlSubHandler.h"

namespace Rosegarden
{

using namespace BaseProperties;

class ConfigurationXmlSubHandler : public XmlSubHandler
{
public:
    ConfigurationXmlSubHandler(const TQString &elementName,
			       Rosegarden::Configuration *configuration);
    
    virtual bool startElement(const TQString& namespaceURI,
                              const TQString& localName,
                              const TQString& qName,
                              const TQXmlAttributes& atts);

    virtual bool endElement(const TQString& namespaceURI,
                            const TQString& localName,
                            const TQString& qName,
                            bool& finished);

    virtual bool characters(const TQString& ch);

    //--------------- Data members ---------------------------------

    Rosegarden::Configuration *m_configuration;

    TQString m_elementName;
    TQString m_propertyName;
    TQString m_propertyType;
};

ConfigurationXmlSubHandler::ConfigurationXmlSubHandler(const TQString &elementName,
						       Rosegarden::Configuration *configuration)
    : m_configuration(configuration),
      m_elementName(elementName)
{
}

bool ConfigurationXmlSubHandler::startElement(const TQString&, const TQString&,
                                              const TQString& lcName,
                                              const TQXmlAttributes& atts)
{
    m_propertyName = lcName;
    m_propertyType = atts.value("type");

    if (m_propertyName == "property") {
	// handle alternative encoding for properties with arbitrary names
	m_propertyName = atts.value("name");
	TQString value = atts.value("value");
	if (!value.isNull()) {
	    m_propertyType = "String";
	    m_configuration->set<String>(qstrtostr(m_propertyName),
					 qstrtostr(value));
	}
    }

    return true;
}

bool ConfigurationXmlSubHandler::characters(const TQString& chars)
{
    TQString ch = chars.stripWhiteSpace();
    // this method is also called on newlines - skip these cases
    if (ch.isEmpty()) return true;


    if (m_propertyType == "Int") {
        long i = ch.toInt();
        RG_DEBUG << "\"" << m_propertyName << "\" "
                 << "value = " << i << endl;
        m_configuration->set<Int>(qstrtostr(m_propertyName), i);

        return true;
    }
    
    if (m_propertyType == "RealTime") {
        Rosegarden::RealTime rt;
        int sepIdx = ch.find(',');
        
        rt.sec = ch.left(sepIdx).toInt();
        rt.nsec = ch.mid(sepIdx + 1).toInt();

        RG_DEBUG << "\"" << m_propertyName << "\" "
                 << "sec = " << rt.sec << ", nsec = " << rt.nsec << endl;

        m_configuration->set<Rosegarden::RealTimeT>(qstrtostr(m_propertyName), rt);

        return true;
    }

    if (m_propertyType == "Bool") {
        TQString chLc = ch.lower();
        
        bool b = (chLc == "true" ||
                  chLc == "1"    ||
                  chLc == "on");
        
        m_configuration->set<Rosegarden::Bool>(qstrtostr(m_propertyName), b);

        return true;
    }

    if (!m_propertyType ||
	m_propertyType == "String") {
        
        m_configuration->set<Rosegarden::String>(qstrtostr(m_propertyName),
						 qstrtostr(ch));

        return true;
    }
    

    return true;
}

bool
ConfigurationXmlSubHandler::endElement(const TQString&,
                                       const TQString&,
                                       const TQString& lcName,
                                       bool& finished)
{
    m_propertyName = "";
    m_propertyType = "";
    finished = (lcName == m_elementName);
    return true;
}


//----------------------------------------



RoseXmlHandler::RoseXmlHandler(RosegardenGUIDoc *doc,
                               unsigned int elementCount,
                               bool createNewDevicesWhenNeeded)
        : ProgressReporter(0),
        m_doc(doc),
        m_currentSegment(0),
        m_currentEvent(0),
        m_currentTime(0),
        m_chordDuration(0),
        m_segmentEndMarkerTime(0),
        m_inChord(false),
        m_inGroup(false),
        m_inComposition(false),
        m_groupId(0),
        m_foundTempo(false),
        m_section(NoSection),
        m_device(0),
        m_deviceRunningId(Device::NO_DEVICE),
        m_msb(0),
        m_lsb(0),
        m_instrument(0),
        m_plugin(0),
        m_pluginInBuss(false),
        m_colourMap(0),
        m_keyMapping(0),
        m_pluginId(0),
        m_totalElements(elementCount),
        m_elementsSoFar(0),
        m_subHandler(0),
        m_deprecation(false),
        m_createDevices(createNewDevicesWhenNeeded),
        m_haveControls(false),
        m_cancelled(false),
        m_skipAllAudio(false),
        m_hasActiveAudio(false)
{}

RoseXmlHandler::~RoseXmlHandler()
{
    delete m_subHandler;
}

Composition &
RoseXmlHandler::getComposition()
{
    return m_doc->getComposition();
}

Studio &
RoseXmlHandler::getStudio()
{
    return m_doc->getStudio();
}

AudioFileManager &
RoseXmlHandler::getAudioFileManager()
{
    return m_doc->getAudioFileManager();
}

AudioPluginManager *
RoseXmlHandler::getAudioPluginManager()
{
    return m_doc->getPluginManager();
}

bool
RoseXmlHandler::startDocument()
{
    // Clear tracks
    //
    getComposition().clearTracks();

    // And the loop
    //
    getComposition().setLoopStart(0);
    getComposition().setLoopEnd(0);

    // All plugins
    //
    m_doc->clearAllPlugins();

    // reset state
    return true;
}

bool
RoseXmlHandler::startElement(const TQString& namespaceURI,
                             const TQString& localName,
                             const TQString& qName, const TQXmlAttributes& atts)
{
    // First check if user pressed cancel button on the progress
    // dialog
    //
    if (isOperationCancelled()) {
        // Ideally, we'd throw here, but at this point TQt is in the stack
        // and TQt is very often compiled without exception support.
        //
        m_cancelled = true;
        return false;
    }

    TQString lcName = qName.lower();

    if (getSubHandler()) {
        return getSubHandler()->startElement(namespaceURI, localName, lcName, atts);
    }

    if (lcName == "event") {

        //        RG_DEBUG << "RoseXmlHandler::startElement: found event, current time is " << m_currentTime << endl;

        if (m_currentEvent) {
            RG_DEBUG << "RoseXmlHandler::startElement: Warning: new event found at time " << m_currentTime << " before previous event has ended; previous event will be lost" << endl;
            delete m_currentEvent;
        }

        m_currentEvent = new XmlStorableEvent(atts, m_currentTime);

        if (m_currentEvent->has(BEAMED_GROUP_ID)) {

            // remap -- we want to ensure that the segment's nextId
            // is always used (and incremented) in preference to the
            // stored id

            if (!m_currentSegment) {
                m_errorString = "Got grouped event outside of a segment";
                return false;
            }

            long storedId = m_currentEvent->get
                            <Int>(BEAMED_GROUP_ID);

            if (m_groupIdMap.find(storedId) == m_groupIdMap.end()) {
                m_groupIdMap[storedId] = m_currentSegment->getNextId();
            }

            m_currentEvent->set
            <Int>(BEAMED_GROUP_ID, m_groupIdMap[storedId]);

        } else if (m_inGroup) {
            m_currentEvent->set
            <Int>(BEAMED_GROUP_ID, m_groupId);
            m_currentEvent->set
            <String>(BEAMED_GROUP_TYPE, m_groupType);
            if (m_groupType == GROUP_TYPE_TUPLED) {
                m_currentEvent->set
                <Int>
                (BEAMED_GROUP_TUPLET_BASE, m_groupTupletBase);
                m_currentEvent->set
                <Int>
                (BEAMED_GROUP_TUPLED_COUNT, m_groupTupledCount);
                m_currentEvent->set
                <Int>
                (BEAMED_GROUP_UNTUPLED_COUNT, m_groupUntupledCount);
            }
        }

        timeT duration = m_currentEvent->getDuration();

        if (!m_inChord) {

            m_currentTime = m_currentEvent->getAbsoluteTime() + duration;

            //            RG_DEBUG << "RoseXmlHandler::startElement: (we're not in a chord) " << endl;

        } else if (duration != 0) {

            // set chord duration to the duration of the shortest
            // element with a non-null duration (if no such elements,
            // leave it as 0).

            if (m_chordDuration == 0 || duration < m_chordDuration) {
                m_chordDuration = duration;
            }
        }

    } else if (lcName == "property") {

        if (!m_currentEvent) {
            RG_DEBUG << "RoseXmlHandler::startElement: Warning: Found property outside of event at time " << m_currentTime << ", ignoring" << endl;
        } else {
            m_currentEvent->setPropertyFromAttributes(atts, true);
        }

    } else if (lcName == "nproperty") {

        if (!m_currentEvent) {
            RG_DEBUG << "RoseXmlHandler::startElement: Warning: Found nproperty outside of event at time " << m_currentTime << ", ignoring" << endl;
        } else {
            m_currentEvent->setPropertyFromAttributes(atts, false);
        }

    } else if (lcName == "chord") {

        m_inChord = true;

    } else if (lcName == "group") {

        if (!m_currentSegment) {
            m_errorString = "Got group outside of a segment";
            return false;
        }

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"group\".  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        m_inGroup = true;
        m_groupId = m_currentSegment->getNextId();
        m_groupType = qstrtostr(atts.value("type"));

        if (m_groupType == GROUP_TYPE_TUPLED) {
            m_groupTupletBase = atts.value("base").toInt();
            m_groupTupledCount = atts.value("tupled").toInt();
            m_groupUntupledCount = atts.value("untupled").toInt();
        }

    } else if (lcName == "rosegarden-data") {

        // FILE FORMAT VERSIONING -- see comments in
        // rosegardenguidoc.cpp.  We only care about major and minor
        // here, not point.

        TQString version = atts.value("version");
        TQString smajor = atts.value("format-version-major");
        TQString sminor = atts.value("format-version-minor");

//        std::cerr << "\n\n\nRosegarden file version = \"" << version << "\"\n\n\n" << std::endl;

        if (!smajor.isNull()) {

            int major = smajor.toInt();
            int minor = sminor.toInt();

            if (major > RosegardenGUIDoc::FILE_FORMAT_VERSION_MAJOR) {
                m_errorString = i18n("This file was written by Rosegarden %1, and it uses\na different file format that cannot be read by this version.").arg(version);
                return false;
            }

            if (major == RosegardenGUIDoc::FILE_FORMAT_VERSION_MAJOR &&
                    minor > RosegardenGUIDoc::FILE_FORMAT_VERSION_MINOR) {

                CurrentProgressDialog::freeze();
                TDEStartupLogo::hideIfStillThere();

                KMessageBox::information(0, i18n("This file was written by Rosegarden %1, which is more recent than this version.\nThere may be some incompatibilities with the file format.").arg(version));

                CurrentProgressDialog::thaw();
            }
        }

    } else if (lcName == "studio") {

        if (m_section != NoSection) {
            m_errorString = "Found Studio in another section";
            return false;
        }

        // In the Studio we clear down everything apart from Devices and
        // Instruments before we reload.  Instruments are derived from
        // the Sequencer, the bank/program information is loaded from
        // the file we're currently examining.
        //
        getStudio().clearMidiBanksAndPrograms();
        getStudio().clearBusses();
        getStudio().clearRecordIns();

        m_section = InStudio; // set top level section

        // Get and set MIDI filters
        //
        TQString thruStr = atts.value("thrufilter");

        if (!thruStr.isNull())
            getStudio().setMIDIThruFilter(thruStr.toInt());

        TQString recordStr = atts.value("recordfilter");

        if (!recordStr.isNull())
            getStudio().setMIDIRecordFilter(recordStr.toInt());

        TQString inputStr = atts.value("audioinputpairs");

        if (!inputStr.isNull()) {
            int inputs = inputStr.toInt();
            if (inputs < 1)
                inputs = 1; // we simply don't permit no inputs
            while (int(getStudio().getRecordIns().size()) < inputs) {
                getStudio().addRecordIn(new RecordIn());
            }
        }

        TQString mixerStr = atts.value("mixerdisplayoptions");

        if (!mixerStr.isNull()) {
            unsigned int mixer = mixerStr.toUInt();
            getStudio().setMixerDisplayOptions(mixer);
        }

        TQString metronomeStr = atts.value("metronomedevice");

        if (!metronomeStr.isNull()) {
            DeviceId metronome = metronomeStr.toUInt();
            getStudio().setMetronomeDevice(metronome);
        }

    } else if (lcName == "timesignature") {

        if (m_inComposition == false) {
            m_errorString = "TimeSignature object found outside Composition";
            return false;
        }

        timeT t = 0;
        TQString timeStr = atts.value("time");
        if (!timeStr.isNull())
            t = timeStr.toInt();

        int num = 4;
        TQString numStr = atts.value("numerator");
        if (!numStr.isNull())
            num = numStr.toInt();

        int denom = 4;
        TQString denomStr = atts.value("denominator");
        if (!denomStr.isNull())
            denom = denomStr.toInt();

        bool common = false;
        TQString commonStr = atts.value("common");
        if (!commonStr.isNull())
            common = (commonStr == "true");

        bool hidden = false;
        TQString hiddenStr = atts.value("hidden");
        if (!hiddenStr.isNull())
            hidden = (hiddenStr == "true");

        bool hiddenBars = false;
        TQString hiddenBarsStr = atts.value("hiddenbars");
        if (!hiddenBarsStr.isNull())
            hiddenBars = (hiddenBarsStr == "true");

        getComposition().addTimeSignature
        (t, TimeSignature(num, denom, common, hidden, hiddenBars));

    } else if (lcName == "tempo") {

        timeT t = 0;
        TQString timeStr = atts.value("time");
        if (!timeStr.isNull())
            t = timeStr.toInt();

        tempoT tempo = Composition::getTempoForQpm(120.0);
        TQString tempoStr = atts.value("tempo");
        TQString targetStr = atts.value("target");
        TQString bphStr = atts.value("bph");
        if (!tempoStr.isNull()) {
            tempo = tempoStr.toInt();
        } else if (!bphStr.isNull()) {
            tempo = Composition::getTempoForQpm
                    (double(bphStr.toInt()) / 60.0);
        }

        if (!targetStr.isNull()) {
            getComposition().addTempoAtTime(t, tempo, targetStr.toInt());
        } else {
            getComposition().addTempoAtTime(t, tempo);
        }

    } else if (lcName == "composition") {

        if (m_section != NoSection) {
            m_errorString = "Found Composition in another section";
            return false;
        }

        // set Segment
        m_section = InComposition;

        // Get and set the record track
        //
        TQString recordStr = atts.value("recordtrack");
        if (!recordStr.isNull()) {
            getComposition().setTrackRecording(recordStr.toInt(), true);
        }

        TQString recordPlStr = atts.value("recordtracks");
        if (!recordPlStr.isNull()) {
            RG_DEBUG << "Record tracks: " << recordPlStr << endl;
            TQStringList recordList = TQStringList::split(',', recordPlStr);
            for (TQStringList::iterator i = recordList.begin();
                    i != recordList.end(); ++i) {
                RG_DEBUG << "Record track: " << (*i).toInt() << endl;
                getComposition().setTrackRecording((*i).toInt(), true);
            }
        }

        // Get and set the position pointer
        //
        int position = 0;
        TQString positionStr = atts.value("pointer");
        if (!positionStr.isNull()) {
            position = positionStr.toInt();
        }

        getComposition().setPosition(position);


        // Get and (eventually) set the default tempo.
        // We prefer the new compositionDefaultTempo over the
        // older defaultTempo.
        //
        TQString tempoStr = atts.value("compositionDefaultTempo");
        if (!tempoStr.isNull()) {
            tempoT tempo = tempoT(tempoStr.toInt());
            getComposition().setCompositionDefaultTempo(tempo);
        } else {
            tempoStr = atts.value("defaultTempo");
            if (!tempoStr.isNull()) {
                double tempo = qstrtodouble(tempoStr);
                getComposition().setCompositionDefaultTempo
                (Composition::getTempoForQpm(tempo));
            }
        }

        // set the composition flag
        m_inComposition = true;


        // Set the loop
        //
        TQString loopStartStr = atts.value("loopstart");
        TQString loopEndStr = atts.value("loopend");

        if (!loopStartStr.isNull() && !loopEndStr.isNull()) {
            int loopStart = loopStartStr.toInt();
            int loopEnd = loopEndStr.toInt();

            getComposition().setLoopStart(loopStart);
            getComposition().setLoopEnd(loopEnd);
        }

        TQString selectedTrackStr = atts.value("selected");

        if (!selectedTrackStr.isNull()) {
            TrackId selectedTrack =
                (TrackId)selectedTrackStr.toInt();

            getComposition().setSelectedTrack(selectedTrack);
        }

        TQString soloTrackStr = atts.value("solo");
        if (!soloTrackStr.isNull()) {
            if (soloTrackStr.toInt() == 1)
                getComposition().setSolo(true);
            else
                getComposition().setSolo(false);
        }


        TQString playMetStr = atts.value("playmetronome");
        if (!playMetStr.isNull()) {
            if (playMetStr.toInt())
                getComposition().setPlayMetronome(true);
            else
                getComposition().setPlayMetronome(false);
        }

        TQString recMetStr = atts.value("recordmetronome");
        if (!recMetStr.isNull()) {
            if (recMetStr.toInt())
                getComposition().setRecordMetronome(true);
            else
                getComposition().setRecordMetronome(false);
        }

        TQString nextTriggerIdStr = atts.value("nexttriggerid");
        if (!nextTriggerIdStr.isNull()) {
            getComposition().setNextTriggerSegmentId(nextTriggerIdStr.toInt());
        }

        TQString copyrightStr = atts.value("copyright");
        if (!copyrightStr.isNull()) {
            getComposition().setCopyrightNote(qstrtostr(copyrightStr));
        }

        TQString startMarkerStr = atts.value("startMarker");
        TQString endMarkerStr = atts.value("endMarker");

        if (!startMarkerStr.isNull()) {
            getComposition().setStartMarker(startMarkerStr.toInt());
        }

        if (!endMarkerStr.isNull()) {
            getComposition().setEndMarker(endMarkerStr.toInt());
        }

    } else if (lcName == "track") {

        if (m_section != InComposition) {
            m_errorString = "Track object found outside Composition";
            return false;
        }

        int id = -1;
        int position = -1;
        int instrument = -1;
        std::string label;
        bool muted = false;

        TQString trackNbStr = atts.value("id");
        if (!trackNbStr.isNull()) {
            id = trackNbStr.toInt();
        }

        TQString labelStr = atts.value("label");
        if (!labelStr.isNull()) {
            label = qstrtostr(labelStr);
        }

        TQString mutedStr = atts.value("muted");
        if (!mutedStr.isNull()) {
            if (mutedStr == "true")
                muted = true;
            else
                muted = false;
        }

        TQString positionStr = atts.value("position");
        if (!positionStr.isNull()) {
            position = positionStr.toInt();
        }

        TQString instrumentStr = atts.value("instrument");
        if (!instrumentStr.isNull()) {
            instrument = instrumentStr.toInt();
        }

        Track *track = new Track(id,
                                 instrument,
                                 position,
                                 label,
                                 muted);

        // track properties affecting newly created segments are initialized
        // to default values in the ctor, so they don't need to be initialized
        // here
        
	TQString presetLabelStr = atts.value("defaultLabel");
	if (!labelStr.isNull()) {
	    track->setPresetLabel(presetLabelStr.ascii());
	}	
	
        TQString clefStr = atts.value("defaultClef");
        if (!clefStr.isNull()) {
            track->setClef(clefStr.toInt());
        }

        TQString transposeStr = atts.value("defaultTranspose");
        if (!transposeStr.isNull()) {
            track->setTranspose(transposeStr.toInt());
        }

        TQString colorStr = atts.value("defaultColour");
        if (!colorStr.isNull()) {
            track->setColor(colorStr.toInt());
        }

        TQString highplayStr = atts.value("defaultHighestPlayable");
        if (!highplayStr.isNull()) {
            track->setHighestPlayable(highplayStr.toInt());
        }

        TQString lowplayStr = atts.value("defaultLowestPlayable");
        if (!lowplayStr.isNull()) {
            track->setLowestPlayable(lowplayStr.toInt());
        }

	TQString staffSizeStr = atts.value("staffSize");
	if (!staffSizeStr.isNull()) {
	    track->setStaffSize(staffSizeStr.toInt());
	}

	TQString staffBracketStr = atts.value("staffBracket");
	if (!staffBracketStr.isNull()) {
	    track->setStaffBracket(staffBracketStr.toInt());
	}

        getComposition().addTrack(track);


    } else if (lcName == "segment") {

        if (m_section != NoSection) {
            m_errorString = "Found Segment in another section";
            return false;
        }

        // set Segment
        m_section = InSegment;

        int track = -1, startTime = 0;
        unsigned int colourindex = 0;
        TQString trackNbStr = atts.value("track");
        if (!trackNbStr.isNull()) {
            track = trackNbStr.toInt();
        }

        TQString startIdxStr = atts.value("start");
        if (!startIdxStr.isNull()) {
            startTime = startIdxStr.toInt();
        }

        TQString segmentType = (atts.value("type")).lower();
        if (!segmentType.isNull()) {
            if (segmentType == "audio") {
                int audioFileId = atts.value("file").toInt();

                // check this file id exists on the AudioFileManager

                if (getAudioFileManager().fileExists(audioFileId) == false) {
                    // We don't report an error as this audio file might've
                    // been excluded deliberately as we could't actually
                    // find the audio file itself.
                    //
                    return true;
                }

                // Create an Audio segment and add its reference
                //
                m_currentSegment = new Segment(Segment::Audio);
                m_currentSegment->setAudioFileId(audioFileId);
                m_currentSegment->setStartTime(startTime);
            } else {
                // Create a (normal) internal Segment
                m_currentSegment = new Segment(Segment::Internal);
            }

        } else {
            // for the moment we default
            m_currentSegment = new Segment(Segment::Internal);
        }

        TQString repeatStr = atts.value("repeat");
        if (repeatStr.lower() == "true") {
            m_currentSegment->setRepeating(true);
        }

        TQString delayStr = atts.value("delay");
        if (!delayStr.isNull()) {
            RG_DEBUG << "Delay string is \"" << delayStr << "\"" << endl;
            long delay = delayStr.toLong();
            RG_DEBUG << "Delay is " << delay << endl;
            m_currentSegment->setDelay(delay);
        }

        TQString rtDelaynSec = atts.value("rtdelaynsec");
        TQString rtDelayuSec = atts.value("rtdelayusec");
        TQString rtDelaySec = atts.value("rtdelaysec");
        if (!rtDelaySec.isNull() && (!rtDelaynSec.isNull() || !rtDelayuSec.isNull())) {
            if (!rtDelaynSec.isNull()) {
                m_currentSegment->setRealTimeDelay
                (RealTime(rtDelaySec.toInt(),
                          rtDelaynSec.toInt()));
            } else {
                m_currentSegment->setRealTimeDelay
                (RealTime(rtDelaySec.toInt(),
                          rtDelayuSec.toInt() * 1000));
            }
        }

        TQString transposeStr = atts.value("transpose");
        if (!transposeStr.isNull())
            m_currentSegment->setTranspose(transposeStr.toInt());

        // fill in the label
        TQString labelStr = atts.value("label");
        if (!labelStr.isNull())
            m_currentSegment->setLabel(qstrtostr(labelStr));

        m_currentSegment->setTrack(track);
        //m_currentSegment->setStartTime(startTime);

        TQString colourIndStr = atts.value("colourindex");
        if (!colourIndStr.isNull()) {
            colourindex = colourIndStr.toInt();
        }

        m_currentSegment->setColourIndex(colourindex);

        TQString snapGridSizeStr = atts.value("snapgridsize");
        if (!snapGridSizeStr.isNull()) {
            m_currentSegment->setSnapGridSize(snapGridSizeStr.toInt());
        }

        TQString viewFeaturesStr = atts.value("viewfeatures");
        if (!viewFeaturesStr.isNull()) {
            m_currentSegment->setViewFeatures(viewFeaturesStr.toInt());
        }

        m_currentTime = startTime;

        TQString triggerIdStr = atts.value("triggerid");
        TQString triggerPitchStr = atts.value("triggerbasepitch");
        TQString triggerVelocityStr = atts.value("triggerbasevelocity");
        TQString triggerRetuneStr = atts.value("triggerretune");
        TQString triggerAdjustTimeStr = atts.value("triggeradjusttimes");

        if (!triggerIdStr.isNull()) {
            int pitch = -1;
            if (!triggerPitchStr.isNull())
                pitch = triggerPitchStr.toInt();
            int velocity = -1;
            if (!triggerVelocityStr.isNull())
                velocity = triggerVelocityStr.toInt();
            TriggerSegmentRec *rec =
                getComposition().addTriggerSegment(m_currentSegment,
                                                   triggerIdStr.toInt(),
                                                   pitch, velocity);
            if (rec) {
                if (!triggerRetuneStr.isNull())
                    rec->setDefaultRetune(triggerRetuneStr.lower() == "true");
                if (!triggerAdjustTimeStr.isNull())
                    rec->setDefaultTimeAdjust(qstrtostr(triggerAdjustTimeStr));
            }
            m_currentSegment->setStartTimeDataMember(startTime);
        } else {
            getComposition().addSegment(m_currentSegment);
            getComposition().setSegmentStartTime(m_currentSegment, startTime);
        }

        TQString endMarkerStr = atts.value("endmarker");
        if (!endMarkerStr.isNull()) {
            delete m_segmentEndMarkerTime;
            m_segmentEndMarkerTime = new timeT(endMarkerStr.toInt());
        }

        m_groupIdMap.clear();

    } else if (lcName == "gui") {

        if (m_section != InSegment) {
            m_errorString = "Found GUI element outside Segment";
            return false;
        }

    } else if (lcName == "controller") {

        if (m_section != InSegment) {
            m_errorString = "Found Controller element outside Segment";
            return false;
        }

        TQString type = atts.value("type");
        //RG_DEBUG << "RoseXmlHandler::startElement - controller type = " << type << endl;

        if (type == strtoqstr(PitchBend::EventType))
            m_currentSegment->addEventRuler(PitchBend::EventType);
        else if (type == strtoqstr(Controller::EventType)) {
            TQString value = atts.value("value");

            if (value != "")
                m_currentSegment->addEventRuler(Controller::EventType, value.toInt());
        }

    } else if (lcName == "resync") {

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"resync\".  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        TQString time(atts.value("time"));
        bool isNumeric;
        int numTime = time.toInt(&isNumeric);
        if (isNumeric)
            m_currentTime = numTime;

    } else if (lcName == "audio") {

        if (m_section != InAudioFiles) {
            m_errorString = "Audio object found outside Audio section";
            return false;
        }

        if (m_skipAllAudio) {
            std::cout << "SKIPPING audio file" << std::endl;
            return true;
        }

        TQString id(atts.value("id"));
        TQString file(atts.value("file"));
        TQString label(atts.value("label"));

        if (id.isEmpty() || file.isEmpty() || label.isEmpty()) {
            m_errorString = "Audio object has empty parameters";
            return false;
        }

        m_hasActiveAudio = true;

        // attempt to insert file into AudioFileManager
        // (this checks the integrity of the file at the
        // same time)
        //
        if (getAudioFileManager().insertFile(qstrtostr(label),
                                             qstrtostr(file),
                                             id.toInt()) == false) {
            // Ok, now attempt to use the KFileDialog saved default
            // value for the AudioPath.
            //
            TQString thing;
            KURL url = KFileDialog::getStartURL(TQString(":WAVS"), thing);
            getAudioFileManager().setAudioPath(url.path().latin1());

            /*
            RG_DEBUG << "ATTEMPTING TO FIND IN PATH = " 
                << url.path() << endl;
                */

            if (getAudioFileManager().
                    insertFile(qstrtostr(label),
                               qstrtostr(file), id.toInt()) == false) {

                // Freeze the progress dialog
                CurrentProgressDialog::freeze();

                // Hide splash screen if present on startup
                TDEStartupLogo::hideIfStillThere();

                // Create a locate file dialog - give it the file name
                // and the AudioFileManager path that we've already
                // tried.  If we manually locate the file then we reset
                // the audiofilepath to the new value and see if this
                // helps us locate the rest of the files.
                //

                TQString newFilename = "";
                TQString newPath = "";

                do {

                    FileLocateDialog fL((RosegardenGUIApp *)m_doc->parent(),
                                        file,
                                        TQString(getAudioFileManager().getAudioPath().c_str()));
                    int result = fL.exec();

                    if (result == TQDialog::Accepted) {
                        newFilename = fL.getFilename();
                        newPath = fL.getDirectory();
                    } else if (result == TQDialog::Rejected) {
                        // just skip the file
                        break;
                    } else {
                        // don't process any more audio files
                        m_skipAllAudio = true;
                        CurrentProgressDialog::thaw();
                        return true;
                    }


                } while (getAudioFileManager().insertFile(qstrtostr(label),
                         qstrtostr(newFilename),
                         id.toInt()) == false);

                if (newPath != "") {
                    getAudioFileManager().setAudioPath(qstrtostr(newPath));
                    // Set a document post-modify flag
                    //m_doc->setModified(true);
                }

                getAudioFileManager().print();

                // Restore progress dialog's normal state
                CurrentProgressDialog::thaw();
            } else {
                // AudioPath is modified so set a document post modify flag
                //
                //m_doc->setModified(true);
            }

        }

    } else if (lcName == "audiopath") {

        if (m_section != InAudioFiles) {
            m_errorString = "Audiopath object found outside AudioFiles section";
            return false;
        }

        TQString search(atts.value("value"));

        if (search.isEmpty()) {
            m_errorString = "Audiopath has no value";
            return false;
        }

        if (!search.startsWith("/") && !search.startsWith("~")) {
            TQString docPath = m_doc->getAbsFilePath();
            TQString dirPath = TQFileInfo(docPath).dirPath();
            if (TQFileInfo(dirPath).exists()) {
                search = dirPath + "/" + search;
            }
        }

        getAudioFileManager().setAudioPath(qstrtostr(search));

    } else if (lcName == "begin") {

        double marker = qstrtodouble(atts.value("index"));

        if (!m_currentSegment) {
            // Don't fail - as this segment could be defunct if we
            // skipped loading the audio file
            //
            return true;
        }

        if (m_currentSegment->getType() != Segment::Audio) {
            m_errorString = "Found audio begin index in non audio segment";
            return false;
        }

        // convert to RealTime from float
        int sec = (int)marker;
        int usec = (int)((marker - ((double)sec)) * 1000000.0);
        m_currentSegment->setAudioStartTime(RealTime(sec, usec * 1000));


    } else if (lcName == "end") {

        double marker = qstrtodouble(atts.value("index"));

        if (!m_currentSegment) {
            // Don't fail - as this segment could be defunct if we
            // skipped loading the audio file
            //
            return true;
        }

        if (m_currentSegment->getType() != Segment::Audio) {
            m_errorString = "found audio end index in non audio segment";
            return false;
        }

        int sec = (int)marker;
        int usec = (int)((marker - ((double)sec)) * 1000000.0);
        RealTime markerTime(sec, usec * 1000);

        if (markerTime < m_currentSegment->getAudioStartTime()) {
            m_errorString = "Audio end index before audio start marker";
            return false;
        }

        m_currentSegment->setAudioEndTime(markerTime);

        // Ensure we set end time according to correct RealTime end of Segment
        //
        RealTime realEndTime = getComposition().
                               getElapsedRealTime(m_currentSegment->getStartTime()) +
                               m_currentSegment->getAudioEndTime() -
                               m_currentSegment->getAudioStartTime();

        timeT absEnd = getComposition().getElapsedTimeForRealTime(realEndTime);
        m_currentSegment->setEndTime(absEnd);

    } else if (lcName == "fadein") {

        if (!m_currentSegment) {
            // Don't fail - as this segment could be defunct if we
            // skipped loading the audio file
            //
            return true;
        }

        if (m_currentSegment->getType() != Segment::Audio) {
            m_errorString = "found fade in time in non audio segment";
            return false;
        }

        double marker = qstrtodouble(atts.value("time"));
        int sec = (int)marker;
        int usec = (int)((marker - ((double)sec)) * 1000000.0);
        RealTime markerTime(sec, usec * 1000);

        m_currentSegment->setFadeInTime(markerTime);
        m_currentSegment->setAutoFade(true);


    } else if (lcName == "fadeout") {

        if (!m_currentSegment) {
            // Don't fail - as this segment could be defunct if we
            // skipped loading the audio file
            //
            return true;
        }

        if (m_currentSegment->getType() != Segment::Audio) {
            m_errorString = "found fade out time in non audio segment";
            return false;
        }

        double marker = qstrtodouble(atts.value("time"));
        int sec = (int)marker;
        int usec = (int)((marker - ((double)sec)) * 1000000.0);
        RealTime markerTime(sec, usec * 1000);

        m_currentSegment->setFadeOutTime(markerTime);
        m_currentSegment->setAutoFade(true);

    } else if (lcName == "device") {

        if (m_section != InStudio) {
            m_errorString = "Found Device outside Studio";
            return false;
        }

        m_haveControls = false;

        TQString type = (atts.value("type")).lower();
        TQString idString = atts.value("id");
        TQString nameStr = atts.value("name");

        if (idString.isNull()) {
            m_errorString = "No ID on Device tag";
            return false;
        }
        int id = idString.toInt();

        if (type == "midi") {
            TQString direction = atts.value("direction").lower();

            if (direction.isNull() ||
                    direction == "" ||
                    direction == "play") { // ignore inputs

                // This will leave m_device set only if there is a
                // valid play midi device to modify:
                skipToNextPlayDevice();

                if (m_device) {
                    if (!nameStr.isNull() && nameStr != "") {
                        m_device->setName(qstrtostr(nameStr));
                    }
                } else if (!nameStr.isNull() && nameStr != "") {
                    addMIDIDevice(nameStr, m_createDevices); // also sets m_device
                }
            }

            TQString connection = atts.value("connection");
            if (m_createDevices && m_device &&
                    !connection.isNull() && connection != "") {
                setMIDIDeviceConnection(connection);
            }

            setMIDIDeviceName(nameStr);

            TQString vstr = atts.value("variation").lower();
            MidiDevice::VariationType variation =
                MidiDevice::NoVariations;
            if (!vstr.isNull()) {
                if (vstr == "lsb") {
                    variation = MidiDevice::VariationFromLSB;
                } else if (vstr == "msb") {
                    variation = MidiDevice::VariationFromMSB;
                } else if (vstr == "") {
                    variation = MidiDevice::NoVariations;
                }
            }
            MidiDevice *md = dynamic_cast<MidiDevice *>
                             (m_device);
            if (md) {
                md->setVariationType(variation);
            }
        } else if (type == "softsynth") {
            m_device = getStudio().getDevice(id);

            if (m_device && m_device->getType() == Device::SoftSynth)
                m_device->setName(qstrtostr(nameStr));
        } else if (type == "audio") {
            m_device = getStudio().getDevice(id);

            if (m_device && m_device->getType() == Device::Audio)
                m_device->setName(qstrtostr(nameStr));
        } else {
            m_errorString = "Found unknown Device type";
            return false;
        }

    } else if (lcName == "librarian") {

        // The contact details for the maintainer of the banks/programs
        // information.
        //
        if (m_device && m_device->getType() == Device::Midi) {
            TQString name = atts.value("name");
            TQString email = atts.value("email");

            dynamic_cast<MidiDevice*>(m_device)->
            setLibrarian(qstrtostr(name), qstrtostr(email));
        }

    } else if (lcName == "bank") {

        if (m_device) // only if we have a device
        {
            if (m_section != InStudio && m_section != InInstrument)
            {
                m_errorString = "Found Bank outside Studio or Instrument";
                return false;
            }

            TQString nameStr = atts.value("name");
            m_percussion = (atts.value("percussion").lower() == "true");
            m_msb = (atts.value("msb")).toInt();
            m_lsb = (atts.value("lsb")).toInt();

            // To actually create a bank
            //
            if (m_section == InStudio)
            {
                // Create a new bank
                MidiBank bank(m_percussion,
                              m_msb,
                              m_lsb,
                              qstrtostr(nameStr));

                if (m_device->getType() == Device::Midi) {
                    // Insert the bank
                    //
                    dynamic_cast<MidiDevice*>(m_device)->addBank(bank);
                }
            } else // otherwise we're referencing it in an instrument
                if (m_section == InInstrument)
                {
                    if (m_instrument) {
                        m_instrument->setPercussion(m_percussion);
                        m_instrument->setMSB(m_msb);
                        m_instrument->setLSB(m_lsb);
                        m_instrument->setSendBankSelect(true);
                    }
                }
        }

    } else if (lcName == "program") {

        if (m_device) // only if we have a device
        {
            if (m_section == InStudio)
            {
                TQString nameStr = (atts.value("name"));
                MidiByte pc = atts.value("id").toInt();
                TQString keyMappingStr = (atts.value("keymapping"));

                // Create a new program
                MidiProgram program
                (MidiBank(m_percussion,
                          m_msb,
                          m_lsb),
                 pc,
                 qstrtostr(nameStr),
                 (!keyMappingStr.isNull()) ? qstrtostr(keyMappingStr) : "");

                if (m_device->getType() == Device::Midi) {
                    // Insert the program
                    //
                    dynamic_cast<MidiDevice*>(m_device)->
                    addProgram(program);
                }

            } else if (m_section == InInstrument)
            {
                if (m_instrument) {
                    MidiByte id = atts.value("id").toInt();
                    m_instrument->setProgramChange(id);
                    m_instrument->setSendProgramChange(true);
                }
            } else
            {
                m_errorString = "Found Program outside Studio and Instrument";
                return false;
            }
        }

    } else if (lcName == "keymapping") {

        if (m_section == InInstrument) {
            RG_DEBUG << "Old-style keymapping in instrument found, ignoring" << endl;
        } else {

            if (m_section != InStudio) {
                m_errorString = "Found Keymapping outside Studio";
                return false;
            }

            if (m_device && (m_device->getType() == Device::Midi)) {
                TQString name = atts.value("name");
                m_keyMapping = new MidiKeyMapping(qstrtostr(name));
                m_keyNameMap.clear();
            }
        }

    } else if (lcName == "key") {

        if (m_keyMapping) {
            TQString numStr = atts.value("number");
            TQString namStr = atts.value("name");
            if (!numStr.isNull() && !namStr.isNull()) {
                m_keyNameMap[numStr.toInt()] = qstrtostr(namStr);
            }
        }

    } else if (lcName == "controls") {

        // Only clear down the controllers list if we have found some controllers in the RG file
        //
        if (m_device) {
            dynamic_cast<MidiDevice*>(m_device)->clearControlList();
        }

        m_haveControls = true;

    } else if (lcName == "control") {

        if (m_section != InStudio) {
            m_errorString = "Found ControlParameter outside Studio";
            return false;
        }

        if (!m_device) {
            //!!! ach no, we can't give this warning -- we might be in a <device> elt
            // but have no sequencer support, for example.  we need a separate m_inDevice
            // flag
            //	    m_deprecation = true;
            //	    std::cerr << "WARNING: This Rosegarden file uses a deprecated control parameter structure.  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;

        } else if (m_device->getType() == Device::Midi) {

            if (!m_haveControls) {
                m_errorString = "Found ControlParameter outside Controls block";
                return false;
            }

            TQString name = atts.value("name");
            TQString type = atts.value("type");
            TQString descr = atts.value("description");
            TQString min = atts.value("min");
            TQString max = atts.value("max");
            TQString def = atts.value("default");
            TQString conVal = atts.value("controllervalue");
            TQString colour = atts.value("colourindex");
            TQString ipbPosition = atts.value("ipbposition");

            ControlParameter con(qstrtostr(name),
                                 qstrtostr(type),
                                 qstrtostr(descr),
                                 min.toInt(),
                                 max.toInt(),
                                 def.toInt(),
                                 MidiByte(conVal.toInt()),
                                 colour.toInt(),
                                 ipbPosition.toInt());

            dynamic_cast<MidiDevice*>(m_device)->
            addControlParameter(con);
        }

    } else if (lcName == "reverb") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"reverb\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Reverb outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_REVERB, value);


    } else if (lcName == "chorus") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"chorus\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Chorus outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_CHORUS, value);

    } else if (lcName == "filter") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"filter\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Filter outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_FILTER, value);


    } else if (lcName == "resonance") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"resonance\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Resonance outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_RESONANCE, value);


    } else if (lcName == "attack") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"attack\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Attack outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_ATTACK, value);

    } else if (lcName == "release") { // deprecated but we still read 'em

        if (!m_deprecation)
            std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"release\" (now replaced by a control parameter).  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
        m_deprecation = true;

        if (m_section != InInstrument) {
            m_errorString = "Found Release outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument)
            m_instrument->setControllerValue(MIDI_CONTROLLER_RELEASE, value);

    } else if (lcName == "pan") {

        if (m_section != InInstrument && m_section != InBuss) {
            m_errorString = "Found Pan outside Instrument or Buss";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_section == InInstrument) {
            if (m_instrument) {
                m_instrument->setPan(value);
                m_instrument->setSendPan(true);
            }
        } else if (m_section == InBuss) {
            if (m_buss) {
                m_buss->setPan(value);
            }
        }

        // keep "velocity" so we're backwards compatible
    } else if (lcName == "velocity" || lcName == "volume") {

        if (lcName == "velocity") {
            if (!m_deprecation)
                std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"velocity\" for an overall MIDI instrument level (now replaced by \"volume\").  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
            m_deprecation = true;
        }

        if (m_section != InInstrument) {
            m_errorString = "Found Volume outside Instrument";
            return false;
        }

        MidiByte value = atts.value("value").toInt();

        if (m_instrument) {
            if (m_instrument->getType() == Instrument::Audio ||
                    m_instrument->getType() == Instrument::SoftSynth) {
                // Backward compatibility: "volume" was in a 0-127
                // range and we now store "level" (float dB) instead.
                // Note that we have no such compatibility for
                // "recordLevel", whose range has changed silently.
                if (!m_deprecation)
                    std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"volume\" for an audio instrument (now replaced by \"level\").  We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
                m_deprecation = true;
                m_instrument->setLevel
                (AudioLevel::multiplier_to_dB(float(value) / 100.0));
            } else {
                m_instrument->setVolume(value);
                m_instrument->setSendVolume(true);
            }
        }

    } else if (lcName == "level") {

        if (m_section != InBuss &&
                (m_section != InInstrument ||
                 (m_instrument &&
                  m_instrument->getType() != Instrument::Audio &&
                  m_instrument->getType() != Instrument::SoftSynth))) {
            m_errorString = "Found Level outside (audio) Instrument or Buss";
            return false;
        }

        double value = qstrtodouble(atts.value("value"));

        if (m_section == InBuss) {
            if (m_buss)
                m_buss->setLevel(value);
        } else {
            if (m_instrument)
                m_instrument->setLevel(value);
        }

    } else if (lcName == "controlchange") {

        if (m_section != InInstrument) {
            m_errorString = "Found ControlChange outside Instrument";
            return false;
        }

        MidiByte type = atts.value("type").toInt();
        MidiByte value = atts.value("value").toInt();

        if (m_instrument) {
            m_instrument->setControllerValue(type, value);
        }

    } else if (lcName == "plugin" || lcName == "synth") {

        PluginContainer *container = 0;

        if (m_section == InInstrument) {
//            std::cerr << "Found plugin in instrument" << std::endl;
            container = m_instrument;
            m_pluginInBuss = false;
        } else if (m_section == InBuss) {
//            std::cerr << "Found plugin in buss" << std::endl;
            container = m_buss;
            m_pluginInBuss = true;
        } else {
            m_errorString = "Found Plugin outside Instrument or Buss";
            return false;
        }

        // Despite being InInstrument or InBuss we might not actually
        // have a valid one.
        //
        if (container) {

//            std::cerr << "Have container" << std::endl;

            emit setOperationName(i18n("Loading plugins..."));
            ProgressDialog::processEvents();

            // Get the details
            int position;
            if (lcName == "synth") {
                position = Instrument::SYNTH_PLUGIN_POSITION;
            } else {
                position = atts.value("position").toInt();
            }

            bool bypassed = false;
            TQString bpStr = atts.value("bypassed");
            if (bpStr.lower() == "true")
                bypassed = true;

            std::string program = "";
            TQString progStr = atts.value("program");
            if (!progStr.isNull()) {
                program = qstrtostr(progStr);
            }

            // Plugins are identified by a structured identifier
            // string, but we will accept a LADSPA UniqueId if there's
            // no identifier, for backward compatibility

            TQString identifier = atts.value("identifier");

            AudioPlugin *plugin = 0;
            AudioPluginManager *apm = getAudioPluginManager();

            if (identifier.isNull()) {
                if (!(atts.value("id")).isNull()) {
                    unsigned long id = atts.value("id").toULong();
                    if (apm)
                        plugin = apm->getPluginByUniqueId(id);
                }
            } else {
                if (apm)
                    plugin = apm->getPluginByIdentifier(identifier);
            }

//            std::cerr << "Plugin identifier " << identifier << " -> plugin " << plugin << std::endl;

            // If we find the plugin all is well and good but if
            // we don't we just skip it.
            //
            if (plugin) {
                m_plugin = container->getPlugin(position);
                if (!m_plugin) {
                    RG_DEBUG << "WARNING: RoseXmlHandler: instrument/buss "
                    << container->getId() << " has no plugin position "
                    << position << endl;
                } else {
                    m_plugin->setAssigned(true);
                    m_plugin->setBypass(bypassed);
                    m_plugin->setIdentifier(plugin->getIdentifier().ascii());
//                    std::cerr << "set identifier to plugin at position " << position << std::endl;
                    if (program != "") {
                        m_plugin->setProgram(program);
                    }
                }
            } else {
                // we shouldn't be halting import of the RG file just because
                // we can't match a plugin
                //
                if (!identifier.isNull()) {
                    RG_DEBUG << "WARNING: RoseXmlHandler: plugin " << identifier << " not found" << endl;
                    m_pluginsNotFound.insert(identifier);
                } else if (!(atts.value("id")).isNull()) {
                    RG_DEBUG << "WARNING: RoseXmlHandler: plugin uid " << atts.value("id") << " not found" << endl;
                } else {
                    m_errorString = "No plugin identifier or uid specified";
                    return false;
                }
            }
        } else { // no instrument

            if (lcName == "synth") {
                TQString identifier = atts.value("identifier");
                if (!identifier.isNull()) {
                    RG_DEBUG << "WARNING: RoseXmlHandler: no instrument for plugin " << identifier << endl;
                    m_pluginsNotFound.insert(identifier);
                }
            }
        }

        m_section = InPlugin;

    } else if (lcName == "port") {

        if (m_section != InPlugin) {
            m_errorString = "Found Port outside Plugin";
            return false;
        }
        unsigned long portId = atts.value("id").toULong();
        double value = qstrtodouble(atts.value("value"));

        TQString changed = atts.value("changed");
        bool changedSinceProgram = (changed == "true");

        if (m_plugin) {
            m_plugin->addPort(portId, value);
            if (changedSinceProgram) {
                PluginPortInstance *ppi = m_plugin->getPort(portId);
                if (ppi)
                    ppi->changedSinceProgramChange = true;
            }
        }

    } else if (lcName == "configure") {

        if (m_section != InPlugin) {
            m_errorString = "Found Configure outside Plugin";
            return false;
        }

        TQString key = atts.value("key");
        TQString value = atts.value("value");

        if (m_plugin) {
            m_plugin->setConfigurationValue(qstrtostr(key), qstrtostr(value));
        }

    } else if (lcName == "metronome") {

        if (m_section != InStudio) {
            m_errorString = "Found Metronome outside Studio";
            return false;
        }

        // Only create if we have a device
        //
        if (m_device && m_device->getType() == Device::Midi) {
            InstrumentId instrument =
                atts.value("instrument").toInt();

            MidiMetronome metronome(instrument);

            if (!(atts.value("barpitch")).isNull())
                metronome.setBarPitch(atts.value("barpitch").toInt());
            if (!(atts.value("beatpitch")).isNull())
                metronome.setBeatPitch(atts.value("beatpitch").toInt());
            if (!(atts.value("subbeatpitch")).isNull())
                metronome.setSubBeatPitch(atts.value("subbeatpitch").toInt());
            if (!(atts.value("depth")).isNull())
                metronome.setDepth(atts.value("depth").toInt());
            if (!(atts.value("barvelocity")).isNull())
                metronome.setBarVelocity(atts.value("barvelocity").toInt());
            if (!(atts.value("beatvelocity")).isNull())
                metronome.setBeatVelocity(atts.value("beatvelocity").toInt());
            if (!(atts.value("subbeatvelocity")).isNull())
                metronome.setSubBeatVelocity(atts.value("subbeatvelocity").toInt());

            dynamic_cast<MidiDevice*>(m_device)->
            setMetronome(metronome);
        }

    } else if (lcName == "instrument") {

        if (m_section != InStudio) {
            m_errorString = "Found Instrument outside Studio";
            return false;
        }

        m_section = InInstrument;

        InstrumentId id = atts.value("id").toInt();
        std::string stringType = qstrtostr(atts.value("type"));
        Instrument::InstrumentType type;

        if (stringType == "midi")
            type = Instrument::Midi;
        else if (stringType == "audio")
            type = Instrument::Audio;
        else if (stringType == "softsynth")
            type = Instrument::SoftSynth;
        else {
            m_errorString = "Found unknown Instrument type";
            return false;
        }

        // Try and match an Instrument in the file with one in
        // our studio
        //
        Instrument *instrument = getStudio().getInstrumentById(id);

        // If we've got an instrument and the types match then
        // we use it from now on.
        //
        if (instrument && instrument->getType() == type) {
            m_instrument = instrument;

            // We can also get the channel from this tag
            //
            MidiByte channel =
                (MidiByte)atts.value("channel").toInt();
            m_instrument->setMidiChannel(channel);
        }

    } else if (lcName == "buss") {

        if (m_section != InStudio) {
            m_errorString = "Found Buss outside Studio";
            return false;
        }

        m_section = InBuss;

        BussId id = atts.value("id").toInt();
        Buss *buss = getStudio().getBussById(id);

        // If we've got a buss then we use it from now on.
        //
        if (buss) {
            m_buss = buss;
        } else {
            m_buss = new Buss(id);
            getStudio().addBuss(m_buss);
        }

    } else if (lcName == "audiofiles") {

        if (m_section != NoSection) {
            m_errorString = "Found AudioFiles inside another section";
            return false;
        }

        m_section = InAudioFiles;

        int rate = atts.value("expectedRate").toInt();
        if (rate) {
            getAudioFileManager().setExpectedSampleRate(rate);
        }

    } else if (lcName == "configuration") {

        setSubHandler(new ConfigurationXmlSubHandler
                      (lcName, &m_doc->getConfiguration()));

    } else if (lcName == "metadata") {

        if (m_section != InComposition) {
            m_errorString = "Found Metadata outside Composition";
            return false;
        }

        setSubHandler(new ConfigurationXmlSubHandler
                      (lcName, &getComposition().getMetadata()));

    } else if (lcName == "recordlevel") {

        if (m_section != InInstrument) {
            m_errorString = "Found recordLevel outside Instrument";
            return false;
        }

        double value = qstrtodouble(atts.value("value"));

        // if the value retrieved is greater than (say) 15 then we
        // must have an old-style 0-127 value instead of a shiny new
        // dB value, so convert it
        if (value > 15.0) {
            value = AudioLevel::multiplier_to_dB(value / 100);
        }

        if (m_instrument)
            m_instrument->setRecordLevel(value);

    } else if (lcName == "audioinput") {

        if (m_section != InInstrument) {
            m_errorString = "Found audioInput outside Instrument";
            return false;
        }

        int value = atts.value("value").toInt();
        int channel = atts.value("channel").toInt();

        TQString type = atts.value("type");
        if (!type.isNull()) {
            if (type.lower() == "buss") {
                if (m_instrument)
                    m_instrument->setAudioInputToBuss(value, channel);
            } else if (type.lower() == "record") {
                if (m_instrument)
                    m_instrument->setAudioInputToRecord(value, channel);
            }
        }

    } else if (lcName == "audiooutput") {

        if (m_section != InInstrument) {
            m_errorString = "Found audioOutput outside Instrument";
            return false;
        }

        int value = atts.value("value").toInt();
        if (m_instrument)
            m_instrument->setAudioOutput(value);

    } else if (lcName == "appearance") {

        m_section = InAppearance;

    } else if (lcName == "colourmap") {

        if (m_section == InAppearance) {
            TQString mapName = atts.value("name");
            m_inColourMap = true;
            if (mapName == "segmentmap") {
                m_colourMap = &m_doc->getComposition().getSegmentColourMap();
            } else
                if (mapName == "generalmap") {
                    m_colourMap = &m_doc->getComposition().getGeneralColourMap();
                } else { // This will change later once we get more of the Appearance code sorted out
                    RG_DEBUG << "RoseXmlHandler::startElement : Found colourmap with unknown name\n";
                }
        } else {
            m_errorString = "Found colourmap outside Appearance";
            return false;
        }

    } else if (lcName == "colourpair") {

        if (m_inColourMap && m_colourMap) {
            unsigned int id = atts.value("id").toInt();
            TQString name = atts.value("name");
            unsigned int red = atts.value("red").toInt();
            unsigned int blue = atts.value("blue").toInt();
            unsigned int green = atts.value("green").toInt();
            Colour colour(red, green, blue);
            m_colourMap->addItem(colour, qstrtostr(name), id);
        } else {
            m_errorString = "Found colourpair outside ColourMap";
            return false;
        }

    } else if (lcName == "markers") {

        if (!m_inComposition) {
            m_errorString = "Found Markers outside Composition";
            return false;
        }

        // clear down any markers
        getComposition().clearMarkers();

    } else if (lcName == "marker") {
        if (!m_inComposition) {
            m_errorString = "Found Marker outside Composition";
            return false;
        }
        int time = atts.value("time").toInt();
        TQString name = atts.value("name");
        TQString descr = atts.value("description");

        Marker *marker =
            new Marker(time,
                       qstrtostr(name),
                       qstrtostr(descr));

        getComposition().addMarker(marker);
    } else {
        RG_DEBUG << "RoseXmlHandler::startElement : Don't know how to parse this : " << qName << endl;
    }

    return true;
}

bool
RoseXmlHandler::endElement(const TQString& namespaceURI,
                           const TQString& localName,
                           const TQString& qName)
{
    if (getSubHandler()) {
        bool finished;
        bool res = getSubHandler()->endElement(namespaceURI, localName, qName.lower(), finished);
        if (finished)
            setSubHandler(0);
        return res;
    }

    // Set percentage done
    //
    if ((m_totalElements > m_elementsSoFar) &&
            (++m_elementsSoFar % 300 == 0)) {

        emit setProgress(int(double(m_elementsSoFar) / double(m_totalElements) * 100.0));
        ProgressDialog::processEvents();
    }

    TQString lcName = qName.lower();

    if (lcName == "rosegarden-data") {

        getComposition().updateTriggerSegmentReferences();

    } else if (lcName == "event") {

        if (m_currentSegment && m_currentEvent) {
            m_currentSegment->insert(m_currentEvent);
            m_currentEvent = 0;
        } else if (!m_currentSegment && m_currentEvent) {
            m_errorString = "Got event outside of a Segment";
            return false;
        }

    } else if (lcName == "chord") {

        m_currentTime += m_chordDuration;
        m_inChord = false;
        m_chordDuration = 0;

    } else if (lcName == "group") {

        m_inGroup = false;

    } else if (lcName == "segment") {

        if (m_currentSegment && m_segmentEndMarkerTime) {
            m_currentSegment->setEndMarkerTime(*m_segmentEndMarkerTime);
            delete m_segmentEndMarkerTime;
            m_segmentEndMarkerTime = 0;
        }

        m_currentSegment = 0;
        m_section = NoSection;

    } else if (lcName == "bar-segment" || lcName == "tempo-segment") {

        m_currentSegment = 0;

    } else if (lcName == "composition") {
        m_inComposition = false;
        m_section = NoSection;

    } else if (lcName == "studio") {

        m_section = NoSection;

    } else if (lcName == "buss") {

        m_section = InStudio;
        m_buss = 0;

    } else if (lcName == "instrument") {

        m_section = InStudio;
        m_instrument = 0;

    } else if (lcName == "plugin") {

        if (m_pluginInBuss) {
            m_section = InBuss;
        } else {
            m_section = InInstrument;
        }
        m_plugin = 0;
        m_pluginId = 0;

    } else if (lcName == "device") {

        m_device = 0;

    } else if (lcName == "keymapping") {

        if (m_section == InStudio) {
            if (m_keyMapping) {
                if (!m_keyNameMap.empty()) {
                    MidiDevice *md = dynamic_cast<MidiDevice *>
                                     (m_device);
                    if (md) {
                        m_keyMapping->setMap(m_keyNameMap);
                        md->addKeyMapping(*m_keyMapping);
                    }
                }
                m_keyMapping = 0;
            }
        }

    } else if (lcName == "audiofiles") {

        m_section = NoSection;

    } else if (lcName == "appearance") {

        m_section = NoSection;

    } else if (lcName == "colourmap") {
        m_inColourMap = false;
        m_colourMap = 0;
    }

    return true;
}

bool
RoseXmlHandler::characters(const TQString& s)
{
    if (m_subHandler)
        return m_subHandler->characters(s);

    return true;
}

TQString
RoseXmlHandler::errorString()
{
    return m_errorString;
}

bool
RoseXmlHandler::error(const TQXmlParseException& exception)
{
    m_errorString = TQString("%1 at line %2, column %3")
                    .arg(exception.message())
                    .arg(exception.lineNumber())
                    .arg(exception.columnNumber());
    return TQXmlDefaultHandler::error( exception );
}

bool
RoseXmlHandler::fatalError(const TQXmlParseException& exception)
{
    m_errorString = TQString("%1 at line %2, column %3")
                    .arg(exception.message())
                    .arg(exception.lineNumber())
                    .arg(exception.columnNumber());
    return TQXmlDefaultHandler::fatalError( exception );
}

bool
RoseXmlHandler::endDocument()
{
    if (m_foundTempo == false) {
        getComposition().setCompositionDefaultTempo
        (Composition::getTempoForQpm(120.0));
    }

    return true;
}

void
RoseXmlHandler::setSubHandler(XmlSubHandler* sh)
{
    delete m_subHandler;
    m_subHandler = sh;
}

void
RoseXmlHandler::addMIDIDevice(TQString name, bool createAtSequencer)
{
    unsigned int deviceId = 0;

    if (createAtSequencer) {

        TQByteArray data;
        TQByteArray replyData;
        TQCString replyType;
        TQDataStream arg(data, IO_WriteOnly);

        arg << (int)Device::Midi;
        arg << (unsigned int)MidiDevice::Play;

        if (!rgapp->sequencerCall("addDevice(int, unsigned int)", replyType, replyData, data)) {
            SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
            << "can't call sequencer addDevice" << endl;
            return ;
        }

        if (replyType == "unsigned int") {
            TQDataStream reply(replyData, IO_ReadOnly);
            reply >> deviceId;
        } else {
            SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
            << "got unknown returntype from addDevice()" << endl;
            return ;
        }

        if (deviceId == Device::NO_DEVICE) {
            SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
            << "sequencer addDevice failed" << endl;
            return ;
        }

        SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
        << " added device " << deviceId << endl;

    } else {
        // Generate a new device id at the base Studio side only.
        // This may not correspond to any given device id at the
        // sequencer side.  We should _never_ do this in a document
        // that's actually intended to be retained for use, only
        // in temporary documents for device import etc.
        int tempId = -1;
        for (DeviceListIterator i = getStudio().getDevices()->begin();
                i != getStudio().getDevices()->end(); ++i) {
            if (int((*i)->getId()) > tempId)
                tempId = int((*i)->getId());
        }
        deviceId = tempId + 1;
    }

    // add the device, so we can name it and set our pointer to it --
    // instruments will be sync'd later in the natural course of things
    getStudio().addDevice(qstrtostr(name), deviceId, Device::Midi);
    m_device = getStudio().getDevice(deviceId);
    m_deviceRunningId = deviceId;
}

void
RoseXmlHandler::skipToNextPlayDevice()
{
    SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice; m_deviceRunningId is " << m_deviceRunningId << endl;

    for (DeviceList::iterator i = getStudio().getDevices()->begin();
            i != getStudio().getDevices()->end(); ++i) {

        MidiDevice *md =
            dynamic_cast<MidiDevice *>(*i);

        if (md && md->getDirection() == MidiDevice::Play) {
            if (m_deviceRunningId == Device::NO_DEVICE ||
                    md->getId() > m_deviceRunningId) {

                SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice: found next device: id " << md->getId() << endl;

                m_device = md;
                m_deviceRunningId = md->getId();
                return ;
            }
        }
    }

    SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice: fresh out of devices" << endl;

    m_device = 0;
}

void
RoseXmlHandler::setMIDIDeviceConnection(TQString connection)
{
    SEQMAN_DEBUG << "RoseXmlHandler::setMIDIDeviceConnection(" << connection << ")" << endl;

    MidiDevice *md = dynamic_cast<MidiDevice *>(m_device);
    if (!md)
        return ;

    TQByteArray data;
    TQDataStream arg(data, IO_WriteOnly);

    arg << (unsigned int)md->getId();
    arg << connection;

    rgapp->sequencerSend("setPlausibleConnection(unsigned int, TQString)",
                         data);
    // connection should be sync'd later in the natural course of things
}

void
RoseXmlHandler::setMIDIDeviceName(TQString name)
{
    SEQMAN_DEBUG << "RoseXmlHandler::setMIDIDeviceName(" << name << ")" << endl;

    MidiDevice *md = dynamic_cast<MidiDevice *>(m_device);
    if (!md)
        return ;

    TQByteArray data;
    TQDataStream arg(data, IO_WriteOnly);

    arg << (unsigned int)md->getId();
    arg << name;

    std::cerr << "Renaming device " << md->getId() << " to " << name.ascii() << std::endl;

    rgapp->sequencerSend("renameDevice(unsigned int, TQString)",
                         data);
}

}
