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@8
|
17
|
Chris@8
|
18 Selection::Selection() :
|
Chris@8
|
19 m_startFrame(0),
|
Chris@8
|
20 m_endFrame(0)
|
Chris@8
|
21 {
|
Chris@8
|
22 }
|
Chris@8
|
23
|
Chris@8
|
24 Selection::Selection(size_t startFrame, size_t endFrame) :
|
Chris@8
|
25 m_startFrame(startFrame),
|
Chris@8
|
26 m_endFrame(endFrame)
|
Chris@8
|
27 {
|
Chris@8
|
28 if (m_startFrame > m_endFrame) {
|
Chris@8
|
29 size_t tmp = m_endFrame;
|
Chris@8
|
30 m_endFrame = m_startFrame;
|
Chris@8
|
31 m_startFrame = tmp;
|
Chris@8
|
32 }
|
Chris@8
|
33 }
|
Chris@8
|
34
|
Chris@8
|
35 Selection::Selection(const Selection &s) :
|
Chris@8
|
36 m_startFrame(s.m_startFrame),
|
Chris@8
|
37 m_endFrame(s.m_endFrame)
|
Chris@8
|
38 {
|
Chris@8
|
39 }
|
Chris@8
|
40
|
Chris@8
|
41 Selection &
|
Chris@8
|
42 Selection::operator=(const Selection &s)
|
Chris@8
|
43 {
|
Chris@8
|
44 if (this != &s) {
|
Chris@8
|
45 m_startFrame = s.m_startFrame;
|
Chris@8
|
46 m_endFrame = s.m_endFrame;
|
Chris@8
|
47 }
|
Chris@8
|
48 return *this;
|
Chris@8
|
49 }
|
Chris@8
|
50
|
Chris@8
|
51 Selection::~Selection()
|
Chris@8
|
52 {
|
Chris@8
|
53 }
|
Chris@8
|
54
|
Chris@8
|
55 bool
|
Chris@8
|
56 Selection::isEmpty() const
|
Chris@8
|
57 {
|
Chris@8
|
58 return m_startFrame == m_endFrame;
|
Chris@8
|
59 }
|
Chris@8
|
60
|
Chris@8
|
61 size_t
|
Chris@8
|
62 Selection::getStartFrame() const
|
Chris@8
|
63 {
|
Chris@8
|
64 return m_startFrame;
|
Chris@8
|
65 }
|
Chris@8
|
66
|
Chris@8
|
67 size_t
|
Chris@8
|
68 Selection::getEndFrame() const
|
Chris@8
|
69 {
|
Chris@8
|
70 return m_endFrame;
|
Chris@8
|
71 }
|
Chris@8
|
72
|
Chris@8
|
73 bool
|
Chris@9
|
74 Selection::contains(size_t frame) const
|
Chris@9
|
75 {
|
Chris@9
|
76 return (frame >= m_startFrame) && (frame < m_endFrame);
|
Chris@9
|
77 }
|
Chris@9
|
78
|
Chris@9
|
79 bool
|
Chris@8
|
80 Selection::operator<(const Selection &s) const
|
Chris@8
|
81 {
|
Chris@9
|
82 if (isEmpty()) {
|
Chris@9
|
83 if (s.isEmpty()) return false;
|
Chris@9
|
84 else return true;
|
Chris@9
|
85 } else {
|
Chris@9
|
86 if (s.isEmpty()) return false;
|
Chris@9
|
87 else return (m_startFrame < s.m_startFrame);
|
Chris@9
|
88 }
|
Chris@8
|
89 }
|
Chris@8
|
90
|
Chris@8
|
91 bool
|
Chris@8
|
92 Selection::operator==(const Selection &s) const
|
Chris@8
|
93 {
|
Chris@9
|
94 if (isEmpty()) return s.isEmpty();
|
Chris@9
|
95
|
Chris@8
|
96 return (m_startFrame == s.m_startFrame &&
|
Chris@8
|
97 m_endFrame == s.m_endFrame);
|
Chris@8
|
98 }
|
Chris@8
|
99
|
Chris@24
|
100
|
Chris@24
|
101 MultiSelection::MultiSelection()
|
Chris@24
|
102 {
|
Chris@24
|
103 }
|
Chris@24
|
104
|
Chris@24
|
105 MultiSelection::~MultiSelection()
|
Chris@24
|
106 {
|
Chris@24
|
107 }
|
Chris@24
|
108
|
Chris@24
|
109 const MultiSelection::SelectionList &
|
Chris@24
|
110 MultiSelection::getSelections() const
|
Chris@24
|
111 {
|
Chris@24
|
112 return m_selections;
|
Chris@24
|
113 }
|
Chris@24
|
114
|
Chris@24
|
115 void
|
Chris@24
|
116 MultiSelection::setSelection(const Selection &selection)
|
Chris@24
|
117 {
|
Chris@24
|
118 clearSelections();
|
Chris@24
|
119 addSelection(selection);
|
Chris@24
|
120 }
|
Chris@24
|
121
|
Chris@24
|
122 void
|
Chris@24
|
123 MultiSelection::addSelection(const Selection &selection)
|
Chris@24
|
124 {
|
Chris@24
|
125 m_selections.insert(selection);
|
Chris@24
|
126
|
Chris@24
|
127 // Cope with a sitation where the new selection overlaps one or
|
Chris@24
|
128 // more existing ones. This is a terribly inefficient way to do
|
Chris@24
|
129 // this, but that probably isn't significant in real life.
|
Chris@24
|
130
|
Chris@24
|
131 // It's essential for the correct operation of
|
Chris@24
|
132 // getContainingSelection that the selections do not overlap, so
|
Chris@24
|
133 // this is not just a frill.
|
Chris@24
|
134
|
Chris@24
|
135 for (SelectionList::iterator i = m_selections.begin();
|
Chris@24
|
136 i != m_selections.end(); ) {
|
Chris@24
|
137
|
Chris@24
|
138 SelectionList::iterator j = i;
|
Chris@24
|
139 if (++j == m_selections.end()) break;
|
Chris@24
|
140
|
Chris@24
|
141 if (i->getEndFrame() >= j->getStartFrame()) {
|
Chris@24
|
142 Selection merged(i->getStartFrame(),
|
Chris@24
|
143 std::max(i->getEndFrame(), j->getEndFrame()));
|
Chris@24
|
144 m_selections.erase(i);
|
Chris@24
|
145 m_selections.erase(j);
|
Chris@24
|
146 m_selections.insert(merged);
|
Chris@24
|
147 i = m_selections.begin();
|
Chris@24
|
148 } else {
|
Chris@24
|
149 ++i;
|
Chris@24
|
150 }
|
Chris@24
|
151 }
|
Chris@24
|
152 }
|
Chris@24
|
153
|
Chris@24
|
154 void
|
Chris@24
|
155 MultiSelection::removeSelection(const Selection &selection)
|
Chris@24
|
156 {
|
Chris@24
|
157 //!!! Likewise this needs to cope correctly with the situation
|
Chris@24
|
158 //where selection is not one of the original selection set but
|
Chris@24
|
159 //simply overlaps one of them (cutting down the original selection
|
Chris@24
|
160 //appropriately)
|
Chris@24
|
161
|
Chris@24
|
162 if (m_selections.find(selection) != m_selections.end()) {
|
Chris@24
|
163 m_selections.erase(selection);
|
Chris@24
|
164 }
|
Chris@24
|
165 }
|
Chris@24
|
166
|
Chris@24
|
167 void
|
Chris@24
|
168 MultiSelection::clearSelections()
|
Chris@24
|
169 {
|
Chris@24
|
170 if (!m_selections.empty()) {
|
Chris@24
|
171 m_selections.clear();
|
Chris@24
|
172 }
|
Chris@24
|
173 }
|
Chris@24
|
174
|
Chris@300
|
175 void
|
Chris@300
|
176 MultiSelection::getExtents(size_t &startFrame, size_t &endFrame) const
|
Chris@300
|
177 {
|
Chris@300
|
178 startFrame = 0;
|
Chris@300
|
179 endFrame = 0;
|
Chris@300
|
180
|
Chris@300
|
181 for (SelectionList::const_iterator i = m_selections.begin();
|
Chris@300
|
182 i != m_selections.end(); ++i) {
|
Chris@300
|
183
|
Chris@300
|
184 if (i == m_selections.begin() || i->getStartFrame() < startFrame) {
|
Chris@300
|
185 startFrame = i->getStartFrame();
|
Chris@300
|
186 }
|
Chris@300
|
187
|
Chris@300
|
188 if (i == m_selections.begin() || i->getEndFrame() > endFrame) {
|
Chris@300
|
189 endFrame = i->getEndFrame();
|
Chris@300
|
190 }
|
Chris@300
|
191 }
|
Chris@300
|
192 }
|
Chris@300
|
193
|
Chris@24
|
194 Selection
|
Chris@36
|
195 MultiSelection::getContainingSelection(size_t frame, bool defaultToFollowing) const
|
Chris@24
|
196 {
|
Chris@24
|
197 // This scales very badly with the number of selections, but it's
|
Chris@24
|
198 // more efficient for very small numbers of selections than a more
|
Chris@24
|
199 // scalable method, and I think that may be what we need
|
Chris@24
|
200
|
Chris@24
|
201 for (SelectionList::const_iterator i = m_selections.begin();
|
Chris@24
|
202 i != m_selections.end(); ++i) {
|
Chris@24
|
203
|
Chris@24
|
204 if (i->contains(frame)) return *i;
|
Chris@24
|
205
|
Chris@24
|
206 if (i->getStartFrame() > frame) {
|
Chris@24
|
207 if (defaultToFollowing) return *i;
|
Chris@24
|
208 else return Selection();
|
Chris@24
|
209 }
|
Chris@24
|
210 }
|
Chris@24
|
211
|
Chris@24
|
212 return Selection();
|
Chris@24
|
213 }
|
Chris@46
|
214
|
Chris@46
|
215 QString
|
Chris@46
|
216 MultiSelection::toXmlString(QString indent,
|
Chris@46
|
217 QString extraAttributes) const
|
Chris@46
|
218 {
|
Chris@46
|
219 QString s;
|
Chris@46
|
220 s += 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@46
|
223 s += indent + QString(" <selection start=\"%1\" end=\"%2\"/>\n")
|
Chris@46
|
224 .arg(i->getStartFrame()).arg(i->getEndFrame());
|
Chris@46
|
225 }
|
Chris@46
|
226 s += indent + "</selections>\n";
|
Chris@46
|
227 return s;
|
Chris@46
|
228 }
|
Chris@46
|
229
|