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@928
|
25 Selection::Selection(int startFrame, int 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@928
|
30 int 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@928
|
62 int
|
Chris@8
|
63 Selection::getStartFrame() const
|
Chris@8
|
64 {
|
Chris@8
|
65 return m_startFrame;
|
Chris@8
|
66 }
|
Chris@8
|
67
|
Chris@928
|
68 int
|
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@928
|
75 Selection::contains(int 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@928
|
177 MultiSelection::getExtents(int &startFrame, int &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@928
|
196 MultiSelection::getContainingSelection(int 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
|