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