Chris@1631
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@1631
|
2
|
Chris@1631
|
3 /*
|
Chris@1631
|
4 Sonic Visualiser
|
Chris@1631
|
5 An audio file viewer and annotation editor.
|
Chris@1631
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@1631
|
7
|
Chris@1631
|
8 This program is free software; you can redistribute it and/or
|
Chris@1631
|
9 modify it under the terms of the GNU General Public License as
|
Chris@1631
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@1631
|
11 License, or (at your option) any later version. See the file
|
Chris@1631
|
12 COPYING included with this distribution for more information.
|
Chris@1631
|
13 */
|
Chris@1631
|
14
|
Chris@1631
|
15 #include "EventSeries.h"
|
Chris@1631
|
16
|
Chris@1631
|
17 bool
|
Chris@1631
|
18 EventSeries::isEmpty() const
|
Chris@1631
|
19 {
|
Chris@1631
|
20 return m_events.empty();
|
Chris@1631
|
21 }
|
Chris@1631
|
22
|
Chris@1631
|
23 int
|
Chris@1631
|
24 EventSeries::count() const
|
Chris@1631
|
25 {
|
Chris@1631
|
26 if (m_events.size() > INT_MAX) {
|
Chris@1632
|
27 throw std::logic_error("too many events");
|
Chris@1631
|
28 }
|
Chris@1631
|
29 return int(m_events.size());
|
Chris@1631
|
30 }
|
Chris@1631
|
31
|
Chris@1631
|
32 void
|
Chris@1631
|
33 EventSeries::add(const Event &p)
|
Chris@1631
|
34 {
|
Chris@1631
|
35 bool isUnique = true;
|
Chris@1631
|
36
|
Chris@1631
|
37 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
38 if (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
39 isUnique = false;
|
Chris@1631
|
40 }
|
Chris@1631
|
41 m_events.insert(pitr, p);
|
Chris@1631
|
42
|
Chris@1640
|
43 if (!p.hasDuration() && p.getFrame() > m_finalDurationlessEventFrame) {
|
Chris@1640
|
44 m_finalDurationlessEventFrame = p.getFrame();
|
Chris@1640
|
45 }
|
Chris@1640
|
46
|
Chris@1631
|
47 if (p.hasDuration() && isUnique) {
|
Chris@1631
|
48
|
Chris@1631
|
49 const sv_frame_t frame = p.getFrame();
|
Chris@1631
|
50 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1631
|
51
|
Chris@1631
|
52 createSeam(frame);
|
Chris@1631
|
53 createSeam(endFrame);
|
Chris@1631
|
54
|
Chris@1631
|
55 // These calls must both succeed after calling createSeam above
|
Chris@1631
|
56 const auto i0 = m_seams.find(frame);
|
Chris@1631
|
57 const auto i1 = m_seams.find(endFrame);
|
Chris@1631
|
58
|
Chris@1631
|
59 for (auto i = i0; i != i1; ++i) {
|
Chris@1631
|
60 if (i == m_seams.end()) {
|
Chris@1631
|
61 SVCERR << "ERROR: EventSeries::add: "
|
Chris@1631
|
62 << "reached end of seam map"
|
Chris@1631
|
63 << endl;
|
Chris@1631
|
64 break;
|
Chris@1631
|
65 }
|
Chris@1631
|
66 i->second.push_back(p);
|
Chris@1631
|
67 }
|
Chris@1631
|
68 }
|
Chris@1631
|
69
|
Chris@1631
|
70 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
71 std::cerr << "after add:" << std::endl;
|
Chris@1631
|
72 dumpEvents();
|
Chris@1631
|
73 dumpSeams();
|
Chris@1631
|
74 #endif
|
Chris@1631
|
75 }
|
Chris@1631
|
76
|
Chris@1631
|
77 void
|
Chris@1631
|
78 EventSeries::remove(const Event &p)
|
Chris@1631
|
79 {
|
Chris@1631
|
80 // If we are removing the last (unique) example of an event,
|
Chris@1631
|
81 // then we also need to remove it from the seam map. If this
|
Chris@1631
|
82 // is only one of multiple identical events, then we don't.
|
Chris@1631
|
83 bool isUnique = true;
|
Chris@1631
|
84
|
Chris@1631
|
85 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
86 if (pitr == m_events.end() || *pitr != p) {
|
Chris@1631
|
87 // we don't know this event
|
Chris@1631
|
88 return;
|
Chris@1631
|
89 } else {
|
Chris@1631
|
90 auto nitr = pitr;
|
Chris@1631
|
91 ++nitr;
|
Chris@1631
|
92 if (nitr != m_events.end() && *nitr == p) {
|
Chris@1631
|
93 isUnique = false;
|
Chris@1631
|
94 }
|
Chris@1631
|
95 }
|
Chris@1631
|
96
|
Chris@1631
|
97 m_events.erase(pitr);
|
Chris@1631
|
98
|
Chris@1640
|
99 if (!p.hasDuration() && isUnique &&
|
Chris@1640
|
100 p.getFrame() == m_finalDurationlessEventFrame) {
|
Chris@1640
|
101 m_finalDurationlessEventFrame = 0;
|
Chris@1640
|
102 for (auto ritr = m_events.rbegin(); ritr != m_events.rend(); ++ritr) {
|
Chris@1640
|
103 if (!ritr->hasDuration()) {
|
Chris@1640
|
104 m_finalDurationlessEventFrame = ritr->getFrame();
|
Chris@1640
|
105 break;
|
Chris@1640
|
106 }
|
Chris@1640
|
107 }
|
Chris@1640
|
108 }
|
Chris@1640
|
109
|
Chris@1631
|
110 if (p.hasDuration() && isUnique) {
|
Chris@1631
|
111
|
Chris@1631
|
112 const sv_frame_t frame = p.getFrame();
|
Chris@1631
|
113 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
|
Chris@1631
|
114
|
Chris@1631
|
115 const auto i0 = m_seams.find(frame);
|
Chris@1631
|
116 const auto i1 = m_seams.find(endFrame);
|
Chris@1631
|
117
|
Chris@1631
|
118 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
119 // This should be impossible if we found p in m_events above
|
Chris@1631
|
120 if (i0 == m_seams.end() || i1 == m_seams.end()) {
|
Chris@1631
|
121 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
|
Chris@1631
|
122 << " or endFrame " << endFrame
|
Chris@1631
|
123 << " for event not found in seam map: event is "
|
Chris@1631
|
124 << p.toXmlString() << endl;
|
Chris@1631
|
125 }
|
Chris@1631
|
126 #endif
|
Chris@1631
|
127
|
Chris@1631
|
128 // Remove any and all instances of p from the seam map; we
|
Chris@1631
|
129 // are only supposed to get here if we are removing the
|
Chris@1631
|
130 // last instance of p from the series anyway
|
Chris@1631
|
131
|
Chris@1631
|
132 for (auto i = i0; i != i1; ++i) {
|
Chris@1631
|
133 if (i == m_seams.end()) {
|
Chris@1631
|
134 // This can happen only if we have a negative
|
Chris@1631
|
135 // duration, which Event forbids
|
Chris@1631
|
136 throw std::logic_error("unexpectedly reached end of map");
|
Chris@1631
|
137 }
|
Chris@1631
|
138 for (size_t j = 0; j < i->second.size(); ) {
|
Chris@1631
|
139 if (i->second[j] == p) {
|
Chris@1631
|
140 i->second.erase(i->second.begin() + j);
|
Chris@1631
|
141 } else {
|
Chris@1631
|
142 ++j;
|
Chris@1631
|
143 }
|
Chris@1631
|
144 }
|
Chris@1631
|
145 }
|
Chris@1631
|
146
|
Chris@1631
|
147 // Tidy up by removing any entries that are now identical
|
Chris@1631
|
148 // to their predecessors
|
Chris@1631
|
149
|
Chris@1631
|
150 std::vector<sv_frame_t> redundant;
|
Chris@1631
|
151
|
Chris@1631
|
152 auto pitr = m_seams.end();
|
Chris@1631
|
153 if (i0 != m_seams.begin()) {
|
Chris@1631
|
154 pitr = i0;
|
Chris@1631
|
155 --pitr;
|
Chris@1631
|
156 }
|
Chris@1631
|
157
|
Chris@1631
|
158 for (auto i = i0; i != m_seams.end(); ++i) {
|
Chris@1631
|
159 if (pitr != m_seams.end() &&
|
Chris@1631
|
160 seamsEqual(i->second, pitr->second)) {
|
Chris@1631
|
161 redundant.push_back(i->first);
|
Chris@1631
|
162 }
|
Chris@1631
|
163 pitr = i;
|
Chris@1631
|
164 if (i == i1) {
|
Chris@1631
|
165 break;
|
Chris@1631
|
166 }
|
Chris@1631
|
167 }
|
Chris@1631
|
168
|
Chris@1631
|
169 for (sv_frame_t f: redundant) {
|
Chris@1631
|
170 m_seams.erase(f);
|
Chris@1631
|
171 }
|
Chris@1631
|
172
|
Chris@1631
|
173 // And remove any empty seams from the start of the map
|
Chris@1631
|
174
|
Chris@1631
|
175 while (m_seams.begin() != m_seams.end() &&
|
Chris@1631
|
176 m_seams.begin()->second.empty()) {
|
Chris@1631
|
177 m_seams.erase(m_seams.begin());
|
Chris@1631
|
178 }
|
Chris@1631
|
179 }
|
Chris@1631
|
180
|
Chris@1631
|
181 #ifdef DEBUG_EVENT_SERIES
|
Chris@1631
|
182 std::cerr << "after remove:" << std::endl;
|
Chris@1631
|
183 dumpEvents();
|
Chris@1631
|
184 dumpSeams();
|
Chris@1631
|
185 #endif
|
Chris@1631
|
186 }
|
Chris@1631
|
187
|
Chris@1631
|
188 bool
|
Chris@1631
|
189 EventSeries::contains(const Event &p) const
|
Chris@1631
|
190 {
|
Chris@1631
|
191 return binary_search(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
192 }
|
Chris@1631
|
193
|
Chris@1631
|
194 void
|
Chris@1631
|
195 EventSeries::clear()
|
Chris@1631
|
196 {
|
Chris@1631
|
197 m_events.clear();
|
Chris@1631
|
198 m_seams.clear();
|
Chris@1640
|
199 m_finalDurationlessEventFrame = 0;
|
Chris@1640
|
200 }
|
Chris@1640
|
201
|
Chris@1640
|
202 sv_frame_t
|
Chris@1640
|
203 EventSeries::getStartFrame() const
|
Chris@1640
|
204 {
|
Chris@1640
|
205 if (m_events.empty()) return 0;
|
Chris@1640
|
206 return m_events.begin()->getFrame();
|
Chris@1640
|
207 }
|
Chris@1640
|
208
|
Chris@1640
|
209 sv_frame_t
|
Chris@1640
|
210 EventSeries::getEndFrame() const
|
Chris@1640
|
211 {
|
Chris@1640
|
212 sv_frame_t latest = 0;
|
Chris@1640
|
213
|
Chris@1640
|
214 if (m_events.empty()) return latest;
|
Chris@1640
|
215
|
Chris@1640
|
216 latest = m_finalDurationlessEventFrame;
|
Chris@1640
|
217
|
Chris@1640
|
218 if (m_seams.empty()) return latest;
|
Chris@1640
|
219
|
Chris@1640
|
220 sv_frame_t lastSeam = m_seams.rbegin()->first;
|
Chris@1640
|
221 if (lastSeam > latest) {
|
Chris@1640
|
222 latest = lastSeam;
|
Chris@1640
|
223 }
|
Chris@1640
|
224
|
Chris@1640
|
225 return latest;
|
Chris@1631
|
226 }
|
Chris@1631
|
227
|
Chris@1631
|
228 EventVector
|
Chris@1631
|
229 EventSeries::getEventsSpanning(sv_frame_t frame,
|
Chris@1631
|
230 sv_frame_t duration) const
|
Chris@1631
|
231 {
|
Chris@1631
|
232 EventVector span;
|
Chris@1631
|
233
|
Chris@1631
|
234 const sv_frame_t start = frame;
|
Chris@1631
|
235 const sv_frame_t end = frame + duration;
|
Chris@1631
|
236
|
Chris@1631
|
237 // first find any zero-duration events
|
Chris@1631
|
238
|
Chris@1631
|
239 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1631
|
240 Event(start));
|
Chris@1631
|
241 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1631
|
242 if (!pitr->hasDuration()) {
|
Chris@1631
|
243 span.push_back(*pitr);
|
Chris@1631
|
244 }
|
Chris@1631
|
245 ++pitr;
|
Chris@1631
|
246 }
|
Chris@1631
|
247
|
Chris@1631
|
248 // now any non-zero-duration ones from the seam map
|
Chris@1631
|
249
|
Chris@1631
|
250 std::set<Event> found;
|
Chris@1631
|
251 auto sitr = m_seams.lower_bound(start);
|
Chris@1631
|
252 if (sitr == m_seams.end() || sitr->first > start) {
|
Chris@1631
|
253 if (sitr != m_seams.begin()) {
|
Chris@1631
|
254 --sitr;
|
Chris@1631
|
255 }
|
Chris@1631
|
256 }
|
Chris@1631
|
257 while (sitr != m_seams.end() && sitr->first < end) {
|
Chris@1631
|
258 for (const auto &p: sitr->second) {
|
Chris@1631
|
259 found.insert(p);
|
Chris@1631
|
260 }
|
Chris@1631
|
261 ++sitr;
|
Chris@1631
|
262 }
|
Chris@1631
|
263 for (const auto &p: found) {
|
Chris@1631
|
264 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
265 while (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
266 span.push_back(p);
|
Chris@1631
|
267 ++pitr;
|
Chris@1631
|
268 }
|
Chris@1631
|
269 }
|
Chris@1631
|
270
|
Chris@1631
|
271 return span;
|
Chris@1631
|
272 }
|
Chris@1631
|
273
|
Chris@1631
|
274 EventVector
|
Chris@1636
|
275 EventSeries::getEventsWithin(sv_frame_t frame,
|
Chris@1654
|
276 sv_frame_t duration,
|
Chris@1654
|
277 int overspill) const
|
Chris@1636
|
278 {
|
Chris@1636
|
279 EventVector span;
|
Chris@1636
|
280
|
Chris@1636
|
281 const sv_frame_t start = frame;
|
Chris@1636
|
282 const sv_frame_t end = frame + duration;
|
Chris@1636
|
283
|
Chris@1654
|
284 // because we don't need to "look back" at events that end within
|
Chris@1654
|
285 // but started without, we can do this entirely from m_events.
|
Chris@1654
|
286 // The core operation is very simple, it's just overspill that
|
Chris@1654
|
287 // complicates it.
|
Chris@1636
|
288
|
Chris@1654
|
289 Events::const_iterator reference =
|
Chris@1654
|
290 lower_bound(m_events.begin(), m_events.end(), Event(start));
|
Chris@1654
|
291
|
Chris@1654
|
292 Events::const_iterator first = reference;
|
Chris@1654
|
293 for (int i = 0; i < overspill; ++i) {
|
Chris@1654
|
294 if (first == m_events.begin()) break;
|
Chris@1654
|
295 --first;
|
Chris@1654
|
296 }
|
Chris@1654
|
297 for (int i = 0; i < overspill; ++i) {
|
Chris@1654
|
298 if (first == reference) break;
|
Chris@1654
|
299 span.push_back(*first);
|
Chris@1654
|
300 ++first;
|
Chris@1654
|
301 }
|
Chris@1654
|
302
|
Chris@1654
|
303 Events::const_iterator pitr = reference;
|
Chris@1654
|
304 Events::const_iterator last = reference;
|
Chris@1654
|
305
|
Chris@1636
|
306 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1654
|
307 if (!pitr->hasDuration() ||
|
Chris@1654
|
308 (pitr->getFrame() + pitr->getDuration() <= end)) {
|
Chris@1636
|
309 span.push_back(*pitr);
|
Chris@1654
|
310 last = pitr;
|
Chris@1654
|
311 ++last;
|
Chris@1636
|
312 }
|
Chris@1636
|
313 ++pitr;
|
Chris@1636
|
314 }
|
Chris@1654
|
315
|
Chris@1654
|
316 for (int i = 0; i < overspill; ++i) {
|
Chris@1654
|
317 if (last == m_events.end()) break;
|
Chris@1654
|
318 span.push_back(*last);
|
Chris@1654
|
319 ++last;
|
Chris@1654
|
320 }
|
Chris@1654
|
321
|
Chris@1636
|
322 return span;
|
Chris@1636
|
323 }
|
Chris@1636
|
324
|
Chris@1636
|
325 EventVector
|
Chris@1638
|
326 EventSeries::getEventsStartingWithin(sv_frame_t frame,
|
Chris@1638
|
327 sv_frame_t duration) const
|
Chris@1638
|
328 {
|
Chris@1638
|
329 EventVector span;
|
Chris@1638
|
330
|
Chris@1638
|
331 const sv_frame_t start = frame;
|
Chris@1638
|
332 const sv_frame_t end = frame + duration;
|
Chris@1638
|
333
|
Chris@1638
|
334 // because we don't need to "look back" at events that started
|
Chris@1638
|
335 // earlier than the start of the given range, we can do this
|
Chris@1638
|
336 // entirely from m_events
|
Chris@1638
|
337
|
Chris@1638
|
338 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1638
|
339 Event(start));
|
Chris@1638
|
340 while (pitr != m_events.end() && pitr->getFrame() < end) {
|
Chris@1638
|
341 span.push_back(*pitr);
|
Chris@1638
|
342 ++pitr;
|
Chris@1638
|
343 }
|
Chris@1638
|
344
|
Chris@1638
|
345 return span;
|
Chris@1638
|
346 }
|
Chris@1638
|
347
|
Chris@1638
|
348 EventVector
|
Chris@1631
|
349 EventSeries::getEventsCovering(sv_frame_t frame) const
|
Chris@1631
|
350 {
|
Chris@1631
|
351 EventVector cover;
|
Chris@1631
|
352
|
Chris@1631
|
353 // first find any zero-duration events
|
Chris@1631
|
354
|
Chris@1631
|
355 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1631
|
356 Event(frame));
|
Chris@1631
|
357 while (pitr != m_events.end() && pitr->getFrame() == frame) {
|
Chris@1631
|
358 if (!pitr->hasDuration()) {
|
Chris@1631
|
359 cover.push_back(*pitr);
|
Chris@1631
|
360 }
|
Chris@1631
|
361 ++pitr;
|
Chris@1631
|
362 }
|
Chris@1631
|
363
|
Chris@1631
|
364 // now any non-zero-duration ones from the seam map
|
Chris@1631
|
365
|
Chris@1631
|
366 std::set<Event> found;
|
Chris@1631
|
367 auto sitr = m_seams.lower_bound(frame);
|
Chris@1631
|
368 if (sitr == m_seams.end() || sitr->first > frame) {
|
Chris@1631
|
369 if (sitr != m_seams.begin()) {
|
Chris@1631
|
370 --sitr;
|
Chris@1631
|
371 }
|
Chris@1631
|
372 }
|
Chris@1631
|
373 if (sitr != m_seams.end() && sitr->first <= frame) {
|
Chris@1631
|
374 for (const auto &p: sitr->second) {
|
Chris@1631
|
375 found.insert(p);
|
Chris@1631
|
376 }
|
Chris@1631
|
377 ++sitr;
|
Chris@1631
|
378 }
|
Chris@1631
|
379 for (const auto &p: found) {
|
Chris@1631
|
380 auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
|
Chris@1631
|
381 while (pitr != m_events.end() && *pitr == p) {
|
Chris@1631
|
382 cover.push_back(p);
|
Chris@1631
|
383 ++pitr;
|
Chris@1631
|
384 }
|
Chris@1631
|
385 }
|
Chris@1631
|
386
|
Chris@1631
|
387 return cover;
|
Chris@1631
|
388 }
|
Chris@1631
|
389
|
Chris@1644
|
390 EventVector
|
Chris@1644
|
391 EventSeries::getAllEvents() const
|
Chris@1644
|
392 {
|
Chris@1644
|
393 return m_events;
|
Chris@1644
|
394 }
|
Chris@1644
|
395
|
Chris@1632
|
396 bool
|
Chris@1632
|
397 EventSeries::getEventPreceding(const Event &e, Event &preceding) const
|
Chris@1632
|
398 {
|
Chris@1632
|
399 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
|
Chris@1632
|
400 if (pitr == m_events.end() || *pitr != e) {
|
Chris@1632
|
401 return false;
|
Chris@1632
|
402 }
|
Chris@1632
|
403 if (pitr == m_events.begin()) {
|
Chris@1632
|
404 return false;
|
Chris@1632
|
405 }
|
Chris@1632
|
406 --pitr;
|
Chris@1632
|
407 preceding = *pitr;
|
Chris@1632
|
408 return true;
|
Chris@1632
|
409 }
|
Chris@1632
|
410
|
Chris@1632
|
411 bool
|
Chris@1632
|
412 EventSeries::getEventFollowing(const Event &e, Event &following) const
|
Chris@1632
|
413 {
|
Chris@1632
|
414 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
|
Chris@1632
|
415 if (pitr == m_events.end() || *pitr != e) {
|
Chris@1632
|
416 return false;
|
Chris@1632
|
417 }
|
Chris@1633
|
418 while (*pitr == e) {
|
Chris@1633
|
419 ++pitr;
|
Chris@1633
|
420 if (pitr == m_events.end()) {
|
Chris@1633
|
421 return false;
|
Chris@1633
|
422 }
|
Chris@1632
|
423 }
|
Chris@1632
|
424 following = *pitr;
|
Chris@1632
|
425 return true;
|
Chris@1632
|
426 }
|
Chris@1632
|
427
|
Chris@1653
|
428 bool
|
Chris@1653
|
429 EventSeries::getNearestEventMatching(sv_frame_t startSearchAt,
|
Chris@1653
|
430 std::function<bool(const Event &)> predicate,
|
Chris@1653
|
431 Direction direction,
|
Chris@1653
|
432 Event &found) const
|
Chris@1653
|
433 {
|
Chris@1653
|
434 auto pitr = lower_bound(m_events.begin(), m_events.end(),
|
Chris@1653
|
435 Event(startSearchAt));
|
Chris@1653
|
436
|
Chris@1653
|
437 while (true) {
|
Chris@1653
|
438
|
Chris@1653
|
439 if (direction == Backward) {
|
Chris@1653
|
440 if (pitr == m_events.begin()) {
|
Chris@1653
|
441 break;
|
Chris@1653
|
442 } else {
|
Chris@1653
|
443 --pitr;
|
Chris@1653
|
444 }
|
Chris@1653
|
445 } else {
|
Chris@1653
|
446 if (pitr == m_events.end()) {
|
Chris@1653
|
447 break;
|
Chris@1653
|
448 }
|
Chris@1653
|
449 }
|
Chris@1653
|
450
|
Chris@1653
|
451 const Event &e = *pitr;
|
Chris@1653
|
452 if (predicate(e)) {
|
Chris@1653
|
453 found = e;
|
Chris@1653
|
454 return true;
|
Chris@1653
|
455 }
|
Chris@1653
|
456
|
Chris@1653
|
457 if (direction == Forward) {
|
Chris@1653
|
458 ++pitr;
|
Chris@1653
|
459 }
|
Chris@1653
|
460 }
|
Chris@1653
|
461
|
Chris@1653
|
462 return false;
|
Chris@1653
|
463 }
|
Chris@1653
|
464
|
Chris@1632
|
465 Event
|
Chris@1632
|
466 EventSeries::getEventByIndex(int index) const
|
Chris@1632
|
467 {
|
Chris@1632
|
468 if (index < 0 || index >= count()) {
|
Chris@1632
|
469 throw std::logic_error("index out of range");
|
Chris@1632
|
470 }
|
Chris@1632
|
471 return m_events[index];
|
Chris@1632
|
472 }
|
Chris@1632
|
473
|
Chris@1640
|
474 int
|
Chris@1640
|
475 EventSeries::getIndexForEvent(const Event &e) const
|
Chris@1640
|
476 {
|
Chris@1640
|
477 auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
|
Chris@1642
|
478 auto d = distance(m_events.begin(), pitr);
|
Chris@1642
|
479 if (d < 0 || d > INT_MAX) return 0;
|
Chris@1642
|
480 return int(d);
|
Chris@1640
|
481 }
|
Chris@1640
|
482
|
Chris@1631
|
483 void
|
Chris@1631
|
484 EventSeries::toXml(QTextStream &out,
|
Chris@1631
|
485 QString indent,
|
Chris@1631
|
486 QString extraAttributes) const
|
Chris@1631
|
487 {
|
Chris@1631
|
488 out << indent << QString("<dataset id=\"%1\" %2>\n")
|
Chris@1631
|
489 .arg(getObjectExportId(this))
|
Chris@1631
|
490 .arg(extraAttributes);
|
Chris@1631
|
491
|
Chris@1631
|
492 for (const auto &p: m_events) {
|
Chris@1631
|
493 p.toXml(out, indent + " ");
|
Chris@1631
|
494 }
|
Chris@1631
|
495
|
Chris@1631
|
496 out << indent << "</dataset>\n";
|
Chris@1631
|
497 }
|
Chris@1631
|
498
|
Chris@1631
|
499
|