Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@8: Chris@8: /* Chris@52: Sonic Visualiser Chris@52: An audio file viewer and annotation editor. Chris@52: Centre for Digital Music, Queen Mary, University of London. Chris@52: This file copyright 2006 Chris Cannam. Chris@8: Chris@52: This program is free software; you can redistribute it and/or Chris@52: modify it under the terms of the GNU General Public License as Chris@52: published by the Free Software Foundation; either version 2 of the Chris@52: License, or (at your option) any later version. See the file Chris@52: COPYING included with this distribution for more information. Chris@8: */ Chris@8: Chris@8: #include "Selection.h" Chris@314: #include Chris@8: Chris@8: Selection::Selection() : Chris@8: m_startFrame(0), Chris@8: m_endFrame(0) Chris@8: { Chris@8: } Chris@8: Chris@1038: Selection::Selection(sv_frame_t startFrame, sv_frame_t endFrame) : Chris@8: m_startFrame(startFrame), Chris@8: m_endFrame(endFrame) Chris@8: { Chris@8: if (m_startFrame > m_endFrame) { Chris@1038: sv_frame_t tmp = m_endFrame; Chris@8: m_endFrame = m_startFrame; Chris@8: m_startFrame = tmp; Chris@8: } Chris@8: } Chris@8: Chris@8: Selection::Selection(const Selection &s) : Chris@8: m_startFrame(s.m_startFrame), Chris@8: m_endFrame(s.m_endFrame) Chris@8: { Chris@8: } Chris@8: Chris@8: Selection & Chris@8: Selection::operator=(const Selection &s) Chris@8: { Chris@8: if (this != &s) { Chris@8: m_startFrame = s.m_startFrame; Chris@8: m_endFrame = s.m_endFrame; Chris@8: } Chris@8: return *this; Chris@8: } Chris@8: Chris@8: Selection::~Selection() Chris@8: { Chris@8: } Chris@8: Chris@8: bool Chris@8: Selection::isEmpty() const Chris@8: { Chris@8: return m_startFrame == m_endFrame; Chris@8: } Chris@8: Chris@1038: sv_frame_t Chris@8: Selection::getStartFrame() const Chris@8: { Chris@8: return m_startFrame; Chris@8: } Chris@8: Chris@1038: sv_frame_t Chris@8: Selection::getEndFrame() const Chris@8: { Chris@8: return m_endFrame; Chris@8: } Chris@8: Chris@8: bool Chris@1038: Selection::contains(sv_frame_t frame) const Chris@9: { Chris@9: return (frame >= m_startFrame) && (frame < m_endFrame); Chris@9: } Chris@9: Chris@9: bool Chris@8: Selection::operator<(const Selection &s) const Chris@8: { Chris@9: if (isEmpty()) { Chris@9: if (s.isEmpty()) return false; Chris@9: else return true; Chris@9: } else { Chris@9: if (s.isEmpty()) return false; Chris@9: else return (m_startFrame < s.m_startFrame); Chris@9: } Chris@8: } Chris@8: Chris@8: bool Chris@8: Selection::operator==(const Selection &s) const Chris@8: { Chris@9: if (isEmpty()) return s.isEmpty(); Chris@9: Chris@8: return (m_startFrame == s.m_startFrame && Chris@8: m_endFrame == s.m_endFrame); Chris@8: } Chris@8: Chris@24: Chris@24: MultiSelection::MultiSelection() Chris@24: { Chris@24: } Chris@24: Chris@24: MultiSelection::~MultiSelection() Chris@24: { Chris@24: } Chris@24: Chris@24: const MultiSelection::SelectionList & Chris@24: MultiSelection::getSelections() const Chris@24: { Chris@24: return m_selections; Chris@24: } Chris@24: Chris@24: void Chris@24: MultiSelection::setSelection(const Selection &selection) Chris@24: { Chris@24: clearSelections(); Chris@24: addSelection(selection); Chris@24: } Chris@24: Chris@24: void Chris@24: MultiSelection::addSelection(const Selection &selection) Chris@24: { Chris@24: m_selections.insert(selection); Chris@24: Chris@24: // Cope with a sitation where the new selection overlaps one or Chris@24: // more existing ones. This is a terribly inefficient way to do Chris@24: // this, but that probably isn't significant in real life. Chris@24: Chris@24: // It's essential for the correct operation of Chris@24: // getContainingSelection that the selections do not overlap, so Chris@24: // this is not just a frill. Chris@24: Chris@24: for (SelectionList::iterator i = m_selections.begin(); Chris@24: i != m_selections.end(); ) { Chris@24: Chris@24: SelectionList::iterator j = i; Chris@24: if (++j == m_selections.end()) break; Chris@24: Chris@24: if (i->getEndFrame() >= j->getStartFrame()) { Chris@24: Selection merged(i->getStartFrame(), Chris@24: std::max(i->getEndFrame(), j->getEndFrame())); Chris@24: m_selections.erase(i); Chris@24: m_selections.erase(j); Chris@24: m_selections.insert(merged); Chris@24: i = m_selections.begin(); Chris@24: } else { Chris@24: ++i; Chris@24: } Chris@24: } Chris@24: } Chris@24: Chris@24: void Chris@24: MultiSelection::removeSelection(const Selection &selection) Chris@24: { Chris@24: //!!! Likewise this needs to cope correctly with the situation Chris@24: //where selection is not one of the original selection set but Chris@24: //simply overlaps one of them (cutting down the original selection Chris@24: //appropriately) Chris@24: Chris@24: if (m_selections.find(selection) != m_selections.end()) { Chris@24: m_selections.erase(selection); Chris@24: } Chris@24: } Chris@24: Chris@24: void Chris@24: MultiSelection::clearSelections() Chris@24: { Chris@24: if (!m_selections.empty()) { Chris@24: m_selections.clear(); Chris@24: } Chris@24: } Chris@24: Chris@300: void Chris@1038: MultiSelection::getExtents(sv_frame_t &startFrame, sv_frame_t &endFrame) const Chris@300: { Chris@300: startFrame = 0; Chris@300: endFrame = 0; Chris@300: Chris@300: for (SelectionList::const_iterator i = m_selections.begin(); Chris@300: i != m_selections.end(); ++i) { Chris@300: Chris@300: if (i == m_selections.begin() || i->getStartFrame() < startFrame) { Chris@300: startFrame = i->getStartFrame(); Chris@300: } Chris@300: Chris@300: if (i == m_selections.begin() || i->getEndFrame() > endFrame) { Chris@300: endFrame = i->getEndFrame(); Chris@300: } Chris@300: } Chris@300: } Chris@300: Chris@24: Selection Chris@1038: MultiSelection::getContainingSelection(sv_frame_t frame, bool defaultToFollowing) const Chris@24: { Chris@24: // This scales very badly with the number of selections, but it's Chris@24: // more efficient for very small numbers of selections than a more Chris@24: // scalable method, and I think that may be what we need Chris@24: Chris@24: for (SelectionList::const_iterator i = m_selections.begin(); Chris@24: i != m_selections.end(); ++i) { Chris@24: Chris@24: if (i->contains(frame)) return *i; Chris@24: Chris@24: if (i->getStartFrame() > frame) { Chris@24: if (defaultToFollowing) return *i; Chris@24: else return Selection(); Chris@24: } Chris@24: } Chris@24: Chris@24: return Selection(); Chris@24: } Chris@46: Chris@314: void Chris@314: MultiSelection::toXml(QTextStream &stream, QString indent, Chris@314: QString extraAttributes) const Chris@46: { Chris@314: stream << indent << QString("\n").arg(extraAttributes); Chris@46: for (SelectionList::iterator i = m_selections.begin(); Chris@46: i != m_selections.end(); ++i) { Chris@314: stream << indent Chris@314: << QString(" \n") Chris@46: .arg(i->getStartFrame()).arg(i->getEndFrame()); Chris@46: } Chris@314: stream << indent << "\n"; Chris@46: } Chris@46: