annotate base/Selection.cpp @ 490:c3fb8258e34d

* Make it possible to import an entire session from an RDF document. However, at the moment the timings of events appear to be constrained by how far the audio decoder has got through its audio file at the time the event is queried -- need to investigate.
author Chris Cannam
date Fri, 21 Nov 2008 18:03:14 +0000
parents 70a232b1f12a
children 6a94bb528e9d
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@8 2
Chris@8 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@52 7 This file copyright 2006 Chris Cannam.
Chris@8 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@8 14 */
Chris@8 15
Chris@8 16 #include "Selection.h"
Chris@314 17 #include <QTextStream>
Chris@8 18
Chris@8 19 Selection::Selection() :
Chris@8 20 m_startFrame(0),
Chris@8 21 m_endFrame(0)
Chris@8 22 {
Chris@8 23 }
Chris@8 24
Chris@8 25 Selection::Selection(size_t startFrame, size_t endFrame) :
Chris@8 26 m_startFrame(startFrame),
Chris@8 27 m_endFrame(endFrame)
Chris@8 28 {
Chris@8 29 if (m_startFrame > m_endFrame) {
Chris@8 30 size_t tmp = m_endFrame;
Chris@8 31 m_endFrame = m_startFrame;
Chris@8 32 m_startFrame = tmp;
Chris@8 33 }
Chris@8 34 }
Chris@8 35
Chris@8 36 Selection::Selection(const Selection &s) :
Chris@8 37 m_startFrame(s.m_startFrame),
Chris@8 38 m_endFrame(s.m_endFrame)
Chris@8 39 {
Chris@8 40 }
Chris@8 41
Chris@8 42 Selection &
Chris@8 43 Selection::operator=(const Selection &s)
Chris@8 44 {
Chris@8 45 if (this != &s) {
Chris@8 46 m_startFrame = s.m_startFrame;
Chris@8 47 m_endFrame = s.m_endFrame;
Chris@8 48 }
Chris@8 49 return *this;
Chris@8 50 }
Chris@8 51
Chris@8 52 Selection::~Selection()
Chris@8 53 {
Chris@8 54 }
Chris@8 55
Chris@8 56 bool
Chris@8 57 Selection::isEmpty() const
Chris@8 58 {
Chris@8 59 return m_startFrame == m_endFrame;
Chris@8 60 }
Chris@8 61
Chris@8 62 size_t
Chris@8 63 Selection::getStartFrame() const
Chris@8 64 {
Chris@8 65 return m_startFrame;
Chris@8 66 }
Chris@8 67
Chris@8 68 size_t
Chris@8 69 Selection::getEndFrame() const
Chris@8 70 {
Chris@8 71 return m_endFrame;
Chris@8 72 }
Chris@8 73
Chris@8 74 bool
Chris@9 75 Selection::contains(size_t frame) const
Chris@9 76 {
Chris@9 77 return (frame >= m_startFrame) && (frame < m_endFrame);
Chris@9 78 }
Chris@9 79
Chris@9 80 bool
Chris@8 81 Selection::operator<(const Selection &s) const
Chris@8 82 {
Chris@9 83 if (isEmpty()) {
Chris@9 84 if (s.isEmpty()) return false;
Chris@9 85 else return true;
Chris@9 86 } else {
Chris@9 87 if (s.isEmpty()) return false;
Chris@9 88 else return (m_startFrame < s.m_startFrame);
Chris@9 89 }
Chris@8 90 }
Chris@8 91
Chris@8 92 bool
Chris@8 93 Selection::operator==(const Selection &s) const
Chris@8 94 {
Chris@9 95 if (isEmpty()) return s.isEmpty();
Chris@9 96
Chris@8 97 return (m_startFrame == s.m_startFrame &&
Chris@8 98 m_endFrame == s.m_endFrame);
Chris@8 99 }
Chris@8 100
Chris@24 101
Chris@24 102 MultiSelection::MultiSelection()
Chris@24 103 {
Chris@24 104 }
Chris@24 105
Chris@24 106 MultiSelection::~MultiSelection()
Chris@24 107 {
Chris@24 108 }
Chris@24 109
Chris@24 110 const MultiSelection::SelectionList &
Chris@24 111 MultiSelection::getSelections() const
Chris@24 112 {
Chris@24 113 return m_selections;
Chris@24 114 }
Chris@24 115
Chris@24 116 void
Chris@24 117 MultiSelection::setSelection(const Selection &selection)
Chris@24 118 {
Chris@24 119 clearSelections();
Chris@24 120 addSelection(selection);
Chris@24 121 }
Chris@24 122
Chris@24 123 void
Chris@24 124 MultiSelection::addSelection(const Selection &selection)
Chris@24 125 {
Chris@24 126 m_selections.insert(selection);
Chris@24 127
Chris@24 128 // Cope with a sitation where the new selection overlaps one or
Chris@24 129 // more existing ones. This is a terribly inefficient way to do
Chris@24 130 // this, but that probably isn't significant in real life.
Chris@24 131
Chris@24 132 // It's essential for the correct operation of
Chris@24 133 // getContainingSelection that the selections do not overlap, so
Chris@24 134 // this is not just a frill.
Chris@24 135
Chris@24 136 for (SelectionList::iterator i = m_selections.begin();
Chris@24 137 i != m_selections.end(); ) {
Chris@24 138
Chris@24 139 SelectionList::iterator j = i;
Chris@24 140 if (++j == m_selections.end()) break;
Chris@24 141
Chris@24 142 if (i->getEndFrame() >= j->getStartFrame()) {
Chris@24 143 Selection merged(i->getStartFrame(),
Chris@24 144 std::max(i->getEndFrame(), j->getEndFrame()));
Chris@24 145 m_selections.erase(i);
Chris@24 146 m_selections.erase(j);
Chris@24 147 m_selections.insert(merged);
Chris@24 148 i = m_selections.begin();
Chris@24 149 } else {
Chris@24 150 ++i;
Chris@24 151 }
Chris@24 152 }
Chris@24 153 }
Chris@24 154
Chris@24 155 void
Chris@24 156 MultiSelection::removeSelection(const Selection &selection)
Chris@24 157 {
Chris@24 158 //!!! Likewise this needs to cope correctly with the situation
Chris@24 159 //where selection is not one of the original selection set but
Chris@24 160 //simply overlaps one of them (cutting down the original selection
Chris@24 161 //appropriately)
Chris@24 162
Chris@24 163 if (m_selections.find(selection) != m_selections.end()) {
Chris@24 164 m_selections.erase(selection);
Chris@24 165 }
Chris@24 166 }
Chris@24 167
Chris@24 168 void
Chris@24 169 MultiSelection::clearSelections()
Chris@24 170 {
Chris@24 171 if (!m_selections.empty()) {
Chris@24 172 m_selections.clear();
Chris@24 173 }
Chris@24 174 }
Chris@24 175
Chris@300 176 void
Chris@300 177 MultiSelection::getExtents(size_t &startFrame, size_t &endFrame) const
Chris@300 178 {
Chris@300 179 startFrame = 0;
Chris@300 180 endFrame = 0;
Chris@300 181
Chris@300 182 for (SelectionList::const_iterator i = m_selections.begin();
Chris@300 183 i != m_selections.end(); ++i) {
Chris@300 184
Chris@300 185 if (i == m_selections.begin() || i->getStartFrame() < startFrame) {
Chris@300 186 startFrame = i->getStartFrame();
Chris@300 187 }
Chris@300 188
Chris@300 189 if (i == m_selections.begin() || i->getEndFrame() > endFrame) {
Chris@300 190 endFrame = i->getEndFrame();
Chris@300 191 }
Chris@300 192 }
Chris@300 193 }
Chris@300 194
Chris@24 195 Selection
Chris@36 196 MultiSelection::getContainingSelection(size_t frame, bool defaultToFollowing) const
Chris@24 197 {
Chris@24 198 // This scales very badly with the number of selections, but it's
Chris@24 199 // more efficient for very small numbers of selections than a more
Chris@24 200 // scalable method, and I think that may be what we need
Chris@24 201
Chris@24 202 for (SelectionList::const_iterator i = m_selections.begin();
Chris@24 203 i != m_selections.end(); ++i) {
Chris@24 204
Chris@24 205 if (i->contains(frame)) return *i;
Chris@24 206
Chris@24 207 if (i->getStartFrame() > frame) {
Chris@24 208 if (defaultToFollowing) return *i;
Chris@24 209 else return Selection();
Chris@24 210 }
Chris@24 211 }
Chris@24 212
Chris@24 213 return Selection();
Chris@24 214 }
Chris@46 215
Chris@314 216 void
Chris@314 217 MultiSelection::toXml(QTextStream &stream, QString indent,
Chris@314 218 QString extraAttributes) const
Chris@46 219 {
Chris@314 220 stream << indent << QString("<selections %1>\n").arg(extraAttributes);
Chris@46 221 for (SelectionList::iterator i = m_selections.begin();
Chris@46 222 i != m_selections.end(); ++i) {
Chris@314 223 stream << indent
Chris@314 224 << QString(" <selection start=\"%1\" end=\"%2\"/>\n")
Chris@46 225 .arg(i->getStartFrame()).arg(i->getEndFrame());
Chris@46 226 }
Chris@314 227 stream << indent << "</selections>\n";
Chris@46 228 }
Chris@46 229