kwave  18.07.70
SelectionTracker.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  SelectionTracker.cpp - tracker for selection changes
3  -------------------
4  begin : Tue Feb 25 2014
5  copyright : (C) 2014 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include "libkwave/Track.h"
24 
26 
27 //***************************************************************************
29  sample_index_t offset,
30  sample_index_t length,
31  const QList<unsigned int> *tracks)
32  :m_signal(signal),
33  m_offset(offset),
34  m_length((length || !signal) ? length : signal->length()),
35  m_tracks(),
36  m_selection_only((length != 0)),
37  m_lock(QMutex::Recursive)
38 {
39  Q_ASSERT(signal);
40  if (!signal) return;
41 
43  signal, SIGNAL(sigTrackInserted(uint,Kwave::Track*)),
44  this, SLOT(slotTrackInserted(uint,Kwave::Track*)));
45  connect(signal, SIGNAL(sigTrackDeleted(uint,Kwave::Track*)),
46  this, SLOT(slotTrackDeleted(uint,Kwave::Track*)));
47  connect(
48  signal,
49  SIGNAL(sigSamplesDeleted(uint,sample_index_t,sample_index_t)),
50  this,
52  );
53  connect(
54  signal,
55  SIGNAL(sigSamplesInserted(uint,sample_index_t,sample_index_t)),
56  this,
58  );
59  connect(
60  signal,
61  SIGNAL(sigSamplesModified(uint,sample_index_t,sample_index_t)),
62  this,
64  );
65 
66  // register us at the undo manager
67  Kwave::UndoManager &undo = signal->undoManager();
68  undo.registerHandler(this);
69 
70  if (tracks && !tracks->isEmpty()) {
71  // having a list of selected tracks
72  foreach (unsigned int track, *tracks) {
73  slotTrackInserted(track, Q_NULLPTR);
74  if (m_selection_only)
75  m_tracks.append(m_signal->uuidOfTrack(track));
76  }
77  } else {
78  // take over all tracks from the signal manager
79  foreach (unsigned int track, m_signal->allTracks()) {
80  slotTrackInserted(track, Q_NULLPTR);
81  if (m_selection_only)
82  m_tracks.append(m_signal->uuidOfTrack(track));
83  }
84  }
85 }
86 
87 //***************************************************************************
89 {
90  // unregister us from the undo manager (if it still exists)
91  if (m_signal) {
92  Kwave::UndoManager &undo = m_signal->undoManager();
93  undo.unregisterHandler(this);
94  }
95 }
96 
97 //***************************************************************************
99 {
100  return m_tracks;
101 }
102 
103 //***************************************************************************
105 {
106  // shortcut: we only have to do something when we are in
107  // "selection only" mode
108  if (!m_selection_only)
109  return true;
110 
111  Q_ASSERT(!m_signal.isNull());
112  if (m_signal.isNull())
113  return false; // should never happen
114 
115  Kwave::UndoAction *action =
116  new(std::nothrow) Kwave::SelectionTracker::Undo(this);
117  Q_ASSERT(action);
118  if (!action) return false;
119 
120  if (action->store(*m_signal)) {
121  undo.append(action);
122  } else {
123  // out of memory
124  delete action;
125  return false;
126  }
127 
128  return true;
129 }
130 
131 //***************************************************************************
133  Kwave::Track *track)
134 {
135  QMutexLocker lock(&m_lock);
136 
137  if (m_selection_only)
138  return; // in "selection only" mode we are not interested in this
139 
140  Q_ASSERT(track || !m_signal.isNull());
141  if (!track && m_signal.isNull()) return;
142 
143  const QUuid &uuid = (track) ? track->uuid() : m_signal->uuidOfTrack(index);
144  Q_ASSERT(!uuid.isNull());
145 
146  // track signal length changes when tracks were inserted
147  sample_index_t new_len = m_signal->length();
148  if (m_length != new_len) {
149  m_length = new_len;
151  }
152 
153  // a new track has been inserted
154  m_tracks.append(uuid);
155  emit sigTrackInserted(uuid);
156 }
157 
158 //***************************************************************************
160  Kwave::Track *track)
161 {
162  QMutexLocker lock(&m_lock);
163 
164  Q_UNUSED(index);
165 
166  Q_ASSERT(track);
167  if (!track) return;
168 
169  const QUuid &uuid = track->uuid();
170  Q_ASSERT(!uuid.isNull());
171  if (!m_tracks.contains(uuid))
172  return; // track not selected
173 
174  // track signal length changes when tracks were inserted
175  if (!m_selection_only) {
176  sample_index_t new_len = m_signal->length();
177  if (m_length != new_len) {
178  m_length = new_len;
180  }
181  }
182 
183  // one of our selected tracks was deleted
184  m_tracks.removeAll(uuid);
185  emit sigTrackDeleted(uuid);
186 }
187 
188 //***************************************************************************
192 {
193  QMutexLocker lock(&m_lock);
194 
195  Q_ASSERT(!m_signal.isNull());
196  if (m_signal.isNull()) return;
197 
198  const QUuid uuid = m_signal->uuidOfTrack(track);
199  if (!m_tracks.contains(uuid))
200  return; // track not selected
201 
202  if (!length)
203  return; // nothing to do
204 
205  // NOTE: adjust offsets/lengths only for the first selected track
206  const bool is_first = (uuid == m_tracks.first());
207 
208  if (m_selection_only) {
209  if (offset >= (m_offset + m_length))
210  return; // right of us
211 
212  if (offset < m_offset) {
213  // left of us, no overlap
214  if (is_first) {
215  m_offset += length;
217  }
218  return;
219  }
220  }
221 
222  // in our range -> increase length and invalidate all
223  // samples from offset to end of file
224  if (is_first) {
225  m_length += length;
227  }
228 
229  emit sigInvalidated(&uuid, offset, SAMPLE_INDEX_MAX);
230 }
231 
232 //***************************************************************************
236 {
237  QMutexLocker lock(&m_lock);
238 
239  Q_ASSERT(!m_signal.isNull());
240  if (m_signal.isNull()) return;
241 
242  const QUuid uuid = m_signal->uuidOfTrack(track);
243  if (!m_tracks.contains(uuid))
244  return; // track not selected
245 
246  if (!length)
247  return; // nothing to do
248 
249  if (offset >= (m_offset + m_length))
250  return; // right of us
251 
252  // NOTE: adjust offsets/lengths only for the first selected track
253  const bool is_first = (uuid == m_tracks.first());
254 
255  if ((offset + length - 1) < m_offset) {
256  // left of us, no overlap
257  if (is_first) {
258  m_offset -= length;
260  }
261  return;
262  }
263 
264  // determine shift and number of deleted samples
265  sample_index_t first_del = offset;
266  sample_index_t last_del = offset + length - 1;
267  sample_index_t shift = (first_del < m_offset) ? (m_offset - first_del) : 0;
268  sample_index_t left = qMax(first_del, m_offset);
269  sample_index_t right = qMin(last_del, m_offset + m_length - 1);
270  sample_index_t deleted_samples = right - left + 1;
271 
272  // adjust the start of the selection
273  if (is_first && shift) {
274  m_offset -= shift;
275  left = m_offset;
277  }
278 
279  // adjust the length of the selection
280  if (is_first && deleted_samples) {
281  m_length -= deleted_samples;
283  }
284 
285  // in our range -> invalidate all samples from offset to end of file
286  emit sigInvalidated(&uuid, left, SAMPLE_INDEX_MAX);
287 }
288 
289 //***************************************************************************
293 {
294  QMutexLocker lock(&m_lock);
295 
296  if (!length) return; // nothing to do
297 
298  Q_ASSERT(!m_signal.isNull());
299  if (m_signal.isNull()) return;
300 
301  const QUuid uuid = m_signal->uuidOfTrack(track);
302  if (!m_tracks.contains(uuid))
303  return; // track not selected
304 
305  if (offset >= (m_offset + m_length))
306  return; // right out of our range -> out of interest
307 
308  if (offset + length < m_offset)
309  return; // completely left from us -> out of interest
310 
311  // overlapping
312  sample_index_t first_mod = offset;
313  sample_index_t last_mod = offset + length - 1;
314  sample_index_t left = qMax(first_mod, m_offset);
315  sample_index_t right = qMin(last_mod, m_offset + m_length - 1);
316 
317  emit sigInvalidated(&uuid, left, right);
318 }
319 
320 //***************************************************************************
321 void Kwave::SelectionTracker::selectRange(QList<QUuid> tracks,
324 {
325  QMutexLocker lock(&m_lock);
326 
327  // remove deleted tracks
328  foreach (const QUuid &uuid, m_tracks) {
329  if (!tracks.contains(uuid)) {
330  m_tracks.removeAll(uuid);
331  emit sigTrackDeleted(uuid);
332  }
333  }
334 
335  // add new tracks
336  foreach (const QUuid &uuid, tracks) {
337  if (!m_tracks.contains(uuid)) {
338  m_tracks.append(uuid);
339  emit sigTrackInserted(uuid);
340  }
341  }
342 
343  // track signal length changes when tracks were inserted
344  if (!m_selection_only) {
345  sample_index_t new_len = m_signal->length();
346  if (m_length != new_len) {
347  m_length = new_len;
349  }
350 
351  return; // nothing more to do in this mode
352  }
353 
354  if ((offset == m_offset) && (length == m_length))
355  return; // no change, nothing to do
356 
357  if (!length)
358  return; // makes no sense
359 
360  // remember the old settings
361  const sample_index_t old_ofs = m_offset;
362  const sample_index_t old_len = m_length;
363 
364  // take over the new selection
365  if (m_length != length) {
366  m_length = length;
368  }
369  if (offset != old_ofs) {
370  // offset has changed -> invalidate all
371  m_offset = offset;
373  emit sigInvalidated(Q_NULLPTR, m_offset, SAMPLE_INDEX_MAX);
374  } else if (length > old_len) {
375  // length has changed and increased -> invalidate new area
376  emit sigInvalidated(Q_NULLPTR, m_offset + old_len - 1, SAMPLE_INDEX_MAX);
377  } else if (length < old_len) {
378  // length was reduced -> invalidate shrinked area at end
379  emit sigInvalidated(Q_NULLPTR, m_offset + length - 1, SAMPLE_INDEX_MAX);
380  }
381 }
382 
383 //***************************************************************************
384 //***************************************************************************
386  :Kwave::UndoAction(),
387  m_tracker(selection),
388  m_tracks(selection ? selection->allTracks() : QList<QUuid>()),
389  m_offset(selection ? selection->offset() : 0),
390  m_length(selection ? selection->length() : 0)
391 {
392 }
393 
394 //***************************************************************************
396 {
397 }
398 
399 //***************************************************************************
401 {
402  return QString();
403 }
404 
405 //***************************************************************************
407 {
408  return sizeof(*this);
409 }
410 
411 //***************************************************************************
413 {
414  return 0;
415 }
416 
417 //***************************************************************************
419 {
420  Q_UNUSED(manager); // data has already been stored in the constructor
421  return true;
422 }
423 
424 //***************************************************************************
426  Kwave::SignalManager &manager, bool with_redo)
427 {
428  Q_UNUSED(manager);
429 
430  if (!m_tracker.isNull()) {
431  QList<QUuid> tracks = m_tracks;
432  sample_index_t ofs = m_offset;
433  sample_index_t len = m_length;
434 
435  m_tracks = m_tracker->allTracks();
436  m_offset = m_tracker->offset();
437  m_length = m_tracker->length();
438 
439  m_tracker->selectRange(tracks, ofs, len);
440  }
441 
442  return (with_redo) ? this : Q_NULLPTR;
443 }
444 
445 //***************************************************************************
446 //***************************************************************************
void sigTrackInserted(const QUuid &uuid)
void selectRange(QList< QUuid > tracks, sample_index_t offset, sample_index_t length)
virtual QString description() Q_DECL_OVERRIDE
sample_index_t offset() const
Definition: App.h:33
virtual ~Undo() Q_DECL_OVERRIDE
Kwave::UndoManager & undoManager()
const QUuid & uuid() const
Definition: Track.h:143
bool registerHandler(Kwave::UndoHandler *handler)
Definition: UndoManager.cpp:36
void slotSamplesDeleted(unsigned int track, sample_index_t offset, sample_index_t length)
void slotSamplesModified(unsigned int track, sample_index_t offset, sample_index_t length)
QList< QUuid > allTracks()
quint64 sample_index_t
Definition: Sample.h:28
void sigInvalidated(const QUuid *track, sample_index_t first, sample_index_t last)
virtual qint64 undoSize() Q_DECL_OVERRIDE
void sigOffsetChanged(sample_index_t offset)
QPointer< Kwave::SignalManager > m_signal
void sigTrackDeleted(const QUuid &uuid)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
QPointer< Kwave::SelectionTracker > m_tracker
virtual ~SelectionTracker() Q_DECL_OVERRIDE
SelectionTracker(Kwave::SignalManager *signal, sample_index_t offset, sample_index_t length, const QList< unsigned int > *tracks)
virtual bool saveUndoData(Kwave::UndoTransaction &undo) Q_DECL_OVERRIDE
void slotTrackDeleted(unsigned int index, Kwave::Track *track)
virtual qint64 redoSize() Q_DECL_OVERRIDE
void slotTrackInserted(unsigned int index, Kwave::Track *track)
void sigLengthChanged(sample_index_t length)
Undo(Kwave::SelectionTracker *selection)
virtual Kwave::UndoAction * undo(Kwave::SignalManager &manager, bool with_redo) Q_DECL_OVERRIDE
bool unregisterHandler(Kwave::UndoHandler *handler)
Definition: UndoManager.cpp:45
#define SAMPLE_INDEX_MAX
Definition: Sample.h:31
void slotSamplesInserted(unsigned int track, sample_index_t offset, sample_index_t length)
virtual bool store(Kwave::SignalManager &manager)=0
sample_index_t length() const
virtual bool store(Kwave::SignalManager &manager) Q_DECL_OVERRIDE