kwave  18.07.70
SignalManager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  SignalManager.cpp - manager class for multi channel signals
3  -------------------
4  begin : Sun Oct 15 2000
5  copyright : (C) 2000 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 <errno.h>
21 #include <limits.h>
22 #include <math.h>
23 #include <new>
24 
25 #include <QApplication>
26 #include <QCursor>
27 #include <QFile>
28 #include <QFileInfo>
29 #include <QMutableListIterator>
30 #include <QMutexLocker>
31 #include <QUrl>
32 
33 #include <KAboutData>
34 #include <KLocalizedString>
35 #include <kxmlgui_version.h>
36 
37 #include "libkwave/ClipBoard.h"
38 #include "libkwave/CodecManager.h"
39 #include "libkwave/Decoder.h"
40 #include "libkwave/Encoder.h"
41 #include "libkwave/FileProgress.h"
42 #include "libkwave/InsertMode.h"
43 #include "libkwave/LabelList.h"
44 #include "libkwave/MemoryManager.h"
45 #include "libkwave/MessageBox.h"
48 #include "libkwave/Parser.h"
49 #include "libkwave/Sample.h"
50 #include "libkwave/Signal.h"
51 #include "libkwave/SignalManager.h"
52 #include "libkwave/String.h"
53 #include "libkwave/Track.h"
54 #include "libkwave/Utils.h"
55 #include "libkwave/Writer.h"
68 
69 #define CASE_COMMAND(x) } else if (parser.command() == _(x)) {
70 
71 //***************************************************************************
73  :QObject(),
74  m_parent_widget(parent),
75  m_closed(true),
76  m_empty(true),
77  m_modified(false),
78  m_modified_enabled(true),
79  m_signal(),
80  m_selection(0,0),
81  m_last_selection(0,0),
82  m_last_track_selection(),
83  m_last_length(0),
84  m_playback_controller(*this),
85  m_undo_enabled(false),
86  m_undo_buffer(),
87  m_redo_buffer(),
88  m_undo_transaction(Q_NULLPTR),
89  m_undo_transaction_level(0),
90  m_undo_transaction_lock(QMutex::Recursive),
91  m_meta_data()
92 {
93  // connect to the track's signals
94  Kwave::Signal *sig = &m_signal;
95  connect(sig, SIGNAL(sigTrackInserted(uint,Kwave::Track*)),
96  this, SLOT(slotTrackInserted(uint,Kwave::Track*)));
97  connect(sig, SIGNAL(sigTrackDeleted(uint,Kwave::Track*)),
98  this, SLOT(slotTrackDeleted(uint,Kwave::Track*)));
99  connect(sig, SIGNAL(sigTrackSelectionChanged(bool)),
100  this,SIGNAL(sigTrackSelectionChanged(bool)));
101  connect(sig, SIGNAL(sigSamplesDeleted(unsigned int, sample_index_t,
102  sample_index_t)),
103  this, SLOT(slotSamplesDeleted(unsigned int, sample_index_t,
104  sample_index_t)));
105  connect(sig, SIGNAL(sigSamplesInserted(unsigned int, sample_index_t,
106  sample_index_t)),
107  this, SLOT(slotSamplesInserted(unsigned int, sample_index_t,
108  sample_index_t)));
109  connect(sig, SIGNAL(sigSamplesModified(unsigned int, sample_index_t,
110  sample_index_t)),
111  this, SLOT(slotSamplesModified(unsigned int, sample_index_t,
112  sample_index_t)));
113 }
114 
115 //***************************************************************************
117 {
118  close();
119 }
120 
121 //***************************************************************************
122 int Kwave::SignalManager::loadFile(const QUrl &url)
123 {
124  int res = 0;
125  Kwave::FileProgress *dialog = Q_NULLPTR;
126 
127  // take over the new file name, so that we have a valid signal
128  // name during loading
129  QString filename = url.path();
130  QFile src(filename);
131  QFileInfo fi(src);
132  {
134  info.set(Kwave::INF_FILENAME, fi.absoluteFilePath());
136  }
137 
138  // work with a copy of meta data, to avoid flicker effects
139  Kwave::MetaDataList meta_data(m_meta_data);
140 
141  // enter and stay in not modified state
142  enableModifiedChange(true);
143  setModified(false);
144  enableModifiedChange(false);
145 
146  // disable undo (discards all undo/redo data)
147  disableUndo();
148 
149  QString mimetype = Kwave::CodecManager::mimeTypeOf(url);
150  qDebug("SignalManager::loadFile(%s) - [%s]",
151  DBG(url.toDisplayString()), DBG(mimetype));
152  Kwave::Decoder *decoder = Kwave::CodecManager::decoder(mimetype);
153  while (decoder) {
154  // be sure that the current signal is really closed
155  m_signal.close();
156 
157  // open the source file
158  if (!(res = decoder->open(m_parent_widget, src))) {
159  qWarning("unable to open source: '%s'", DBG(url.toDisplayString()));
160  res = -EIO;
161  break;
162  }
163 
164  // get the initial meta data from the decoder
165  meta_data = decoder->metaData();
166  Kwave::FileInfo info(meta_data);
167 
168  // take the preliminary meta data, needed for estimated length
169  m_meta_data = meta_data;
170 
171  // detect stream mode. if so, use one sample as display
172  bool streaming = (!info.length());
173 
174  // we must change to open state to see the file while
175  // it is loaded
176  m_closed = false;
177  m_empty = false;
178 
179  // create all tracks (empty)
180  unsigned int track;
181  const unsigned int tracks = info.tracks();
182  const sample_index_t length = info.length();
183  Q_ASSERT(tracks);
184  if (!tracks) break;
185 
186  for (track = 0; track < tracks; ++track) {
187  Kwave::Track *t = m_signal.appendTrack(length, Q_NULLPTR);
188  Q_ASSERT(t);
189  if (!t || (t->length() != length)) {
190  qWarning("SignalManager::loadFile: out of memory");
191  res = -ENOMEM;
192  break;
193  }
194  }
195  if (track < tracks) break;
196 
197  // create the multitrack writer as destination
198  // if length was zero -> append mode / decode a stream ?
199  Kwave::InsertMode mode = (streaming) ? Kwave::Append : Kwave::Overwrite;
200  Kwave::MultiTrackWriter writers(*this, allTracks(), mode, 0,
201  (length) ? length-1 : 0);
202 
203  // try to calculate the resulting length, but if this is
204  // not possible, we try to use the source length instead
205  quint64 resulting_size = info.tracks() * info.length() *
206  (info.bits() >> 3);
207  bool use_src_size = (!resulting_size);
208  if (use_src_size) resulting_size = src.size();
209 
210  // prepare and show the progress dialog
212  QUrl(filename), resulting_size,
213  info.length(), info.rate(), info.bits(), info.tracks());
214  Q_ASSERT(dialog);
215 
216  if (dialog && use_src_size) {
217  // use source size for progress / stream mode
218  QObject::connect(decoder, SIGNAL(sourceProcessed(quint64)),
219  dialog, SLOT(setBytePosition(quint64)));
220  QObject::connect(&writers, SIGNAL(written(quint64)),
221  dialog, SLOT(setLength(quint64)));
222  } else {
223  // use resulting size percentage for progress
224  QObject::connect(&writers, SIGNAL(progress(qreal)),
225  dialog, SLOT(setValue(qreal)));
226  }
227  QObject::connect(dialog, SIGNAL(canceled()),
228  &writers, SLOT(cancel()));
229 
230  // now decode
231  res = 0;
232  if (!decoder->decode(m_parent_widget, writers)) {
233  qWarning("decoding failed.");
234  res = -EIO;
235  } else {
236  // read information back from the decoder, some settings
237  // might have become available during the decoding process
238  meta_data = decoder->metaData();
239  info = Kwave::FileInfo(meta_data);
240  }
241 
242  decoder->close();
243 
244  // check for length info in stream mode
245  if (!res && streaming) {
246  // source was opened in stream mode -> now we have the length
247  writers.flush();
248  sample_index_t new_length = writers.last();
249  if (new_length) new_length++;
250  info.setLength(new_length);
251  } else {
252  info.setLength(this->length());
253  info.setTracks(tracks);
254  }
255 
256  // enter the filename/mimetype and size into the file info
257  info.set(Kwave::INF_FILENAME, fi.absoluteFilePath());
258  info.set(Kwave::INF_FILESIZE, src.size());
259  if (!info.contains(Kwave::INF_MIMETYPE))
260  info.set(Kwave::INF_MIMETYPE, mimetype);
261 
262  // remove the estimated length again, it is no longer needed
263  info.set(Kwave::INF_ESTIMATED_LENGTH, QVariant());
264 
265  // take over the decoded and updated file info
266  meta_data.replace(Kwave::MetaDataList(info));
267  m_meta_data = meta_data;
268 
269  // update the length info in the progress dialog if needed
270  if (dialog && use_src_size) {
271  dialog->setLength(
272  quint64(info.length()) *
273  quint64(info.tracks()));
274  dialog->setBytePosition(src.size());
275  }
276 
277  break;
278  }
279 
280  if (!decoder) {
281  qWarning("unknown file type");
282  res = -EINVAL;
283  } else {
284  delete decoder;
285  }
286 
287  // process any queued events of the writers, like "sigSamplesInserted"
288  qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
289 
290  // remember the last length and selection
291  m_last_length = length();
293 
294  // from now on, undo is enabled
295  enableUndo();
296 
297  // modified can change from now on
298  enableModifiedChange(true);
299 
300  if (dialog) delete dialog;
301  if (res) close();
302 
303  m_meta_data.dump();
304 
305  // we now have new meta data
307 
308  return res;
309 }
310 
311 //***************************************************************************
312 int Kwave::SignalManager::save(const QUrl &url, bool selection)
313 {
314  int res = 0;
315  sample_index_t ofs = 0;
316  sample_index_t len = length();
317  unsigned int tracks = this->tracks();
318  unsigned int bits = this->bits();
319 
320  if (selection) {
321  // zero-length -> nothing to do
322  ofs = m_selection.offset();
323  len = m_selection.length();
324  tracks = selectedTracks().count();
325  }
326 
327  if (!tracks || !len) {
329  i18n("Signal is empty, nothing to save."));
330  return 0;
331  }
332 
333  QString mimetype_name;
334  mimetype_name = Kwave::CodecManager::mimeTypeOf(url);
335  qDebug("SignalManager::save(%s) - [%s] (%d bit, selection=%d)",
336  DBG(url.toDisplayString()), DBG(mimetype_name), bits, selection);
337 
338  Kwave::Encoder *encoder = Kwave::CodecManager::encoder(mimetype_name);
339  Kwave::FileInfo file_info(m_meta_data);
340  if (encoder) {
341 
342  // maybe we now have a new mime type
343  file_info.set(Kwave::INF_MIMETYPE, mimetype_name);
344 
345  // check if we lose information and ask the user if this would
346  // be acceptable
347  QList<Kwave::FileProperty> unsupported = encoder->unsupportedProperties(
348  file_info.properties().keys());
349  if (!unsupported.isEmpty()) {
350  QString list_of_lost_properties = _("\n");
351  foreach (const Kwave::FileProperty &p, unsupported) {
352  list_of_lost_properties +=
353  i18n(UTF8(file_info.name(p))) + _("\n");
354  }
355 
356  // show a warning to the user and ask him if he wants to continue
358  i18n("Saving in this format will lose the following "
359  "additional file attribute(s):\n"
360  "%1\n"
361  "Do you still want to continue?",
362  list_of_lost_properties),
363  QString(),
364  QString(),
365  QString(),
366  _("accept_lose_attributes_on_export")
367  ) != KMessageBox::Continue)
368  {
369  delete encoder;
370  return -1;
371  }
372  }
373 
374  // open the destination file
375  QString filename = url.path();
376  QFile dst(filename);
377 
379  (selection) ? selectedTracks() : allTracks(),
380  ofs, ofs + len - 1);
381 
382  // update the file information
383  file_info.setLength(len);
384  file_info.setRate(rate());
385  file_info.setBits(bits);
386  file_info.setTracks(tracks);
387 
388  if (!file_info.contains(Kwave::INF_SOFTWARE) &&
389  encoder->supportedProperties().contains(Kwave::INF_SOFTWARE))
390  {
391  // add our Kwave Software tag
392  const KAboutData about_data = KAboutData::applicationData();
393  QString software = about_data.displayName() + _("-") +
394  about_data.version() +
395  i18n("(built with KDE Frameworks %1)",
396  _(KXMLGUI_VERSION_STRING));
397  file_info.set(Kwave::INF_SOFTWARE, software);
398  }
399 
400  if (!file_info.contains(Kwave::INF_CREATION_DATE) &&
401  encoder->supportedProperties().contains(Kwave::INF_CREATION_DATE))
402  {
403  // add a date tag
404  QDate now(QDate::currentDate());
405  QString date;
406  date = date.sprintf("%04d-%02d-%02d",
407  now.year(), now.month(), now.day());
408  qDebug("adding date tag: '%s'", DBG(date));
409  file_info.set(Kwave::INF_CREATION_DATE, date);
410  }
411 
412  // prepare and show the progress dialog
414  QUrl(filename), file_info.length() * file_info.tracks() *
415  (file_info.bits() >> 3),
416  file_info.length(), file_info.rate(), file_info.bits(),
417  file_info.tracks());
418  Q_ASSERT(dialog);
419  QObject::connect(&src, SIGNAL(progress(qreal)),
420  dialog, SLOT(setValue(qreal)),
421  Qt::QueuedConnection);
422  QObject::connect(dialog, SIGNAL(canceled()),
423  &src, SLOT(cancel()));
424 
425  // invoke the encoder...
426  bool encoded = false;
428 
429  if (selection) {
430  // use a copy, don't touch the original !
432 
433  // we have to adjust all position aware meta data
434  meta.cropByRange(ofs, ofs + len - 1);
435 
436  // filter out all the track bound meta data that is not selected
438 
439  // set the filename in the copy of the fileinfo, the original
440  // file which is currently open keeps it's name
441  Kwave::FileInfo info(meta);
442  info.set(Kwave::INF_FILENAME, filename);
443  meta.replace(Kwave::MetaDataList(info));
444 
445  encoded = encoder->encode(m_parent_widget, src, dst, meta);
446  } else {
447  // in case of a "save as" -> modify the current filename
448  file_info.set(Kwave::INF_FILENAME, filename);
450  encoded = encoder->encode(m_parent_widget, src, dst, m_meta_data);
451  }
452  if (!encoded) {
454  i18n("An error occurred while saving the file."));
455  res = -1;
456  }
457 
458  delete encoder;
459  encoder = Q_NULLPTR;
460 
461  if (dialog) {
462  qApp->processEvents();
463  if (dialog->isCanceled()) {
464  // user really pressed cancel !
466  i18n("The file has been truncated and "
467  "might be corrupted."));
468  res = -EINTR;
469  }
470  delete dialog;
471  dialog = Q_NULLPTR;
472  }
473  } else {
475  i18n("Sorry, the file type is not supported."));
476  res = -EINVAL;
477  }
478 
479  if (!res && !selection) {
480  // saved without error -> no longer modified
482  enableModifiedChange(true);
483  setModified(false);
484  }
485 
487  qDebug("SignalManager::save(): res=%d",res);
488  return res;
489 }
490 
491 //***************************************************************************
493  unsigned int bits, unsigned int tracks)
494 {
495  // enter and stay in modified state
496  enableModifiedChange(true);
497  setModified(true);
498  enableModifiedChange(false);
499 
500  // disable undo (discards all undo/redo data)
501  disableUndo();
502 
503  m_meta_data.clear();
504  Kwave::FileInfo file_info(m_meta_data);
505  file_info.setRate(rate);
506  file_info.setBits(bits);
507  file_info.setTracks(tracks);
509 
510  // now the signal is considered not to be empty
511  m_closed = false;
512  m_empty = false;
513 
514  // add all empty tracks
515  while (tracks) {
516  m_signal.appendTrack(samples, Q_NULLPTR);
517  tracks--;
518  }
519 
520  // remember the last length
521  m_last_length = samples;
522  file_info.setLength(length());
525 
526  // from now on, undo is enabled
527  enableUndo();
528 
530 }
531 
532 //***************************************************************************
534 {
535  // stop the playback
538 
539  // fix the modified flag to false
540  enableModifiedChange(true);
541  setModified(false);
542  enableModifiedChange(false);
543 
544  // reset the last length of the signal
545  m_last_length = 0;
546 
547  // disable undo and discard all undo buffers
548  // undo will be re-enabled when a signal is loaded or created
549  disableUndo();
550 
551  // for safety: flush all undo/redo buffers
553  flushRedoBuffer();
554 
555  // reset the selection
556  m_selection.clear();
557 
558  m_empty = true;
559  while (tracks()) deleteTrack(tracks() - 1);
560  m_signal.close();
561 
562  // clear all meta data
563  m_meta_data.clear();
564 
565  m_closed = true;
567 
569 }
570 
571 //***************************************************************************
573 {
574  // if a file is loaded -> path of the URL if it has one
575  QUrl url;
576  url = QUrl(Kwave::FileInfo(m_meta_data).get(Kwave::INF_FILENAME).toString());
577  if (url.isValid()) return url.path();
578 
579  // we have something, but no name yet
580  if (!isClosed()) return QString(NEW_FILENAME);
581 
582  // otherwise: closed, nothing loaded
583  return _("");
584 }
585 
586 //***************************************************************************
587 const QList<unsigned int> Kwave::SignalManager::selectedTracks()
588 {
589  QList<unsigned int> list;
590  const unsigned int tracks = this->tracks();
591 
592  for (unsigned int track = 0; track < tracks; track++) {
593  if (!m_signal.trackSelected(track)) continue;
594  list.append(track);
595  }
596 
597  return list;
598 }
599 
600 //***************************************************************************
601 const QList<unsigned int> Kwave::SignalManager::allTracks()
602 {
603  return m_signal.allTracks();
604 }
605 
606 //***************************************************************************
607 int Kwave::SignalManager::executeCommand(const QString &command)
608 {
609  sample_index_t offset = m_selection.offset();
611 
612  if (!command.length()) return -EINVAL;
613  Kwave::Parser parser(command);
614 
615  if (false) {
616  // --- undo / redo ---
617  CASE_COMMAND("undo")
618  undo();
619  CASE_COMMAND("redo")
620  redo();
621  CASE_COMMAND("undo_all")
622  while (m_undo_enabled && !m_undo_buffer.isEmpty())
623  undo();
624  CASE_COMMAND("redo_all")
625  while (!m_redo_buffer.isEmpty())
626  redo();
627 
628  // --- copy & paste + clipboard ---
629  CASE_COMMAND("copy")
630  if (length) {
632  clip.copy(
634  *this,
635  selectedTracks(),
636  offset, length
637  );
638  // remember the last selection
640  }
641  CASE_COMMAND("insert_at")
643  if (clip.isEmpty()) return 0;
644  if (!selectedTracks().size()) return 0;
645  sample_index_t ofs = parser.toSampleIndex();
646 
648  i18n("Insert Clipboard at position"));
649 
650  selectRange(ofs, 0);
651  clip.paste(m_parent_widget, *this, ofs, 0);
652 
653  CASE_COMMAND("paste")
655  if (clip.isEmpty()) return 0;
656  if (!selectedTracks().size()) return 0;
657 
658  Kwave::UndoTransactionGuard undo(*this, i18n("Paste"));
659  clip.paste(m_parent_widget, *this, offset, length);
660  CASE_COMMAND("cut")
661  if (length) {
662  // remember the last selection
664 
666  clip.copy(
668  *this,
669  selectedTracks(),
670  offset, length
671  );
672  Kwave::UndoTransactionGuard undo(*this, i18n("Cut"));
673  deleteRange(offset, length);
675  }
676  CASE_COMMAND("clipboard_flush")
678  CASE_COMMAND("crop")
679  if (length) {
680  Kwave::UndoTransactionGuard undo(*this, i18n("Crop"));
681  sample_index_t rest = this->length() - offset;
682  rest = (rest > length) ? (rest-length) : 0;
683  QList<unsigned int> tracks = selectedTracks();
684  if (saveUndoDelete(tracks, offset+length, rest) &&
685  saveUndoDelete(tracks, 0, offset))
686  {
687  // remember the last selection
689 
690  unsigned int count = tracks.count();
691  while (count--) {
692  m_signal.deleteRange(count, offset+length, rest);
693  m_signal.deleteRange(count, 0, offset);
694  }
695  selectRange(0, length);
696  }
697  }
698  CASE_COMMAND("delete")
699  Kwave::UndoTransactionGuard undo(*this, i18n("Delete"));
700  deleteRange(offset, length);
702 
703 // CASE_COMMAND("mixpaste")
704 // if (globals.clipboard) {
705 // SignalManager *toinsert = globals.clipboard->getSignal();
706 // if (toinsert) {
707 // unsigned int clipchan = toinsert->channels();
708 // unsigned int sourcechan = 0;
709 //
710 // /* ### check if the signal has to be re-sampled ### */
711 //
712 // for (unsigned int i = 0; i < m_channels; i++) {
713 // Q_ASSERT(signal.at(i));
714 // if (signal.at(i)) {
715 // signal.at(i)->mixPaste(
716 // toinsert->getSignal(sourcechan)
717 // );
718 // }
719 // sourcechan++;
720 // sourcechan %= clipchan;
721 // }
722 // }
723 // }
724 
725  CASE_COMMAND("label:delete")
726  int index = parser.toInt();
727  deleteLabel(index, true);
728 
729  CASE_COMMAND("expandtolabel")
731  i18n("Expand Selection to Label"));
732  sample_index_t selection_left = m_selection.first();
733  sample_index_t selection_right = m_selection.last();
735  if (labels.isEmpty()) return false; // we need labels for this
736  Kwave::Label label_left = Kwave::Label();
737  Kwave::Label label_right = Kwave::Label();
738 
739  // the last label <= selection start -> label_left
740  // the first label >= selection end -> label_right
741  foreach (const Kwave::Label &label, labels) {
742  sample_index_t lp = label.pos();
743  if (lp <= selection_left)
744  label_left = label;
745  if ((lp >= selection_right) && (label_right.isNull())) {
746  label_right = label;
747  break; // done
748  }
749  }
750  // default left label = start of file
751  selection_left = (label_left.isNull()) ? 0 :
752  label_left.pos();
753  // default right label = end of file
754  selection_right = (label_right.isNull()) ?
755  (this->length() - 1) : label_right.pos();
756  sample_index_t len = selection_right - selection_left + 1;
757  selectRange(selection_left, len);
758 
759  CASE_COMMAND("selectnextlabels")
760  Kwave::UndoTransactionGuard undo(*this, i18n("Select Next Labels"));
761  sample_index_t selection_left;
762  sample_index_t selection_right = m_selection.last();
763  Kwave::Label label_left = Kwave::Label();
764  Kwave::Label label_right = Kwave::Label();
766  if (labels.isEmpty()) return false; // we need labels for this
767 
768  // special case: nothing selected -> select up to the first label
769  if (selection_right == 0) {
770  label_right = labels.first();
771  selection_left = 0;
772  } else {
773  // find the first label starting after the current selection
774  LabelListIterator it(labels);
775  while (it.hasNext()) {
776  Kwave::Label label = it.next();
777  if (label.pos() >= selection_right) {
778  // take it as selection start
779  label_left = label;
780  // and it's next one as selection end (might be null)
781  label_right = it.hasNext() ? it.next() : Kwave::Label();
782  break;
783  }
784  }
785  // default selection start = last label
786  if (label_left.isNull()) label_left = labels.last();
787  if (label_left.isNull()) return false; // no labels at all !?
788  selection_left = label_left.pos();
789  }
790  // default selection end = end of the file
791  selection_right = (label_right.isNull()) ?
792  (this->length() - 1) : label_right.pos();
793  sample_index_t len = (selection_right > selection_left) ?
794  (selection_right - selection_left + 1) : 1;
795  selectRange(selection_left, len);
796 
797  CASE_COMMAND("selectprevlabels")
798  Kwave::UndoTransactionGuard undo(*this, i18n("Select Previous Labels"));
799  sample_index_t selection_left = selection().first();
800  sample_index_t selection_right = selection().last();
801  Kwave::Label label_left = Kwave::Label();
802  Kwave::Label label_right = Kwave::Label();
804  if (labels.isEmpty()) return false; // we need labels for this
805 
806  // find the last label before the start of the selection
807  foreach (const Kwave::Label &label, labels) {
808  if (label.pos() > selection_left)
809  break; // done
810  label_left = label_right;
811  label_right = label;
812  }
813  // default selection start = start of file
814  selection_left = (label_left.isNull()) ? 0 :
815  label_left.pos();
816  // default selection end = first label
817  if (label_right.isNull()) label_right = labels.first();
818  if (label_right.isNull()) return false; // no labels at all !?
819  selection_right = label_right.pos();
820  sample_index_t len = selection_right - selection_left + 1;
821  selectRange(selection_left, len);
822 
823  // --- track related functions ---
824  CASE_COMMAND("add_track")
825  appendTrack();
826  CASE_COMMAND("delete_track")
827  Kwave::Parser p(command);
828  unsigned int track = p.toUInt();
829  if (track >= tracks()) return -EINVAL;
830  deleteTrack(track);
831  CASE_COMMAND("insert_track")
832  Kwave::Parser p(command);
833  unsigned int track = p.toUInt();
834  insertTrack(track);
835 
836  // track selection
837  CASE_COMMAND("select_track:all")
838  Kwave::UndoTransactionGuard undo(*this, i18n("Select All Tracks"));
839  foreach (unsigned int track, allTracks())
840  selectTrack(track, true);
841  CASE_COMMAND("select_track:none")
842  Kwave::UndoTransactionGuard undo(*this, i18n("Deselect all tracks"));
843  foreach (unsigned int track, allTracks())
844  selectTrack(track, false);
845  CASE_COMMAND("select_track:invert")
846  Kwave::UndoTransactionGuard undo(*this, i18n("Invert Track Selection"));
847  foreach (unsigned int track, allTracks())
848  selectTrack(track, !trackSelected(track));
849  CASE_COMMAND("select_track:on")
850  unsigned int track = parser.toUInt();
851  if (track >= tracks()) return -EINVAL;
852  Kwave::UndoTransactionGuard undo(*this, i18n("Select Track"));
853  selectTrack(track, true);
854  CASE_COMMAND("select_track:off")
855  unsigned int track = parser.toUInt();
856  if (track >= tracks()) return -EINVAL;
857  Kwave::UndoTransactionGuard undo(*this, i18n("Deselect Track"));
858  selectTrack(track, false);
859  CASE_COMMAND("select_track:toggle")
860  unsigned int track = parser.toUInt();
861  if (track >= tracks()) return -EINVAL;
862  Kwave::UndoTransactionGuard undo(*this, i18n("Toggle Track Selection"));
863  selectTrack(track, !(trackSelected(track)));
864 
865  // playback control
866  CASE_COMMAND("playback_start")
868 
869  CASE_COMMAND("fileinfo")
870  QString property = parser.firstParam();
871  QString value = parser.nextParam();
873  bool found = false;
874  foreach (Kwave::FileProperty p, info.allKnownProperties()) {
875  if (info.name(p) == property) {
876  if (value.length())
877  info.set(p, QVariant(value)); // add/modify
878  else
879  info.set(p, QVariant()); // delete
880  found = true;
881  break;
882  }
883  }
884 
885  if (found) {
887  // we now have new meta data
889  } else
890  return -EINVAL;
891  CASE_COMMAND("dump_metadata")
892  qDebug("DUMP OF META DATA => %s", DBG(parser.firstParam()));
893  m_meta_data.dump();
894  } else {
895  return -ENOSYS;
896  }
897 
898  return 0;
899 }
900 
901 //***************************************************************************
903 {
904  Kwave::UndoTransactionGuard undo(*this, i18n("Append Track"));
905  insertTrack(tracks());
906 }
907 
908 //***************************************************************************
909 void Kwave::SignalManager::insertTrack(unsigned int index)
910 {
911  Kwave::UndoTransactionGuard undo(*this, i18n("Insert Track"));
912 
913  const unsigned int count = tracks();
914  Q_ASSERT(index <= count);
915  if (index > count) index = count;
916 
917  // undo action for the track insert
919  new(std::nothrow) Kwave::UndoInsertTrack(m_signal, index))) return;
920 
921  // if the signal is currently empty, use the last
922  // known length instead of the current one
923  sample_index_t len = (count) ? length() : m_last_length;
924 
925  if (index >= count) {
926  // do an "append"
927  m_signal.appendTrack(len, Q_NULLPTR);
928  } else {
929  if (m_undo_enabled) {
930  // undo action for the corresponding meta data change
931  QList<unsigned int> tracks;
932  for (unsigned int t = index; t < count; t++) tracks.append(t);
934  if (!list.isEmpty() && !registerUndoAction(
935  new(std::nothrow) Kwave::UndoModifyMetaDataAction(list)))
936  return;
937  }
938 
939  // adjust the track bound meta data
940  m_meta_data.insertTrack(index);
941 
942  // insert into the list
943  m_signal.insertTrack(index, len, Q_NULLPTR);
944  }
945 
946  // remember the last length
947  m_last_length = length();
948 }
949 
950 //***************************************************************************
951 void Kwave::SignalManager::deleteTrack(unsigned int index)
952 {
953  Kwave::UndoTransactionGuard undo(*this, i18n("Delete Track"));
954 
955  const unsigned int count = tracks();
956  Q_ASSERT(index <= count);
957  if (index > count) return;
958 
959  if (m_undo_enabled) {
960  // undo action for the track deletion
961  if (!registerUndoAction(new(std::nothrow)
962  UndoDeleteTrack(m_signal, index)))
963  return;
964 
965  // undo action for the corresponding meta data change
966  QList<unsigned int> tracks;
967  for (unsigned int t = index; t < count; t++) tracks.append(t);
969  if (!list.isEmpty() && !registerUndoAction(
970  new(std::nothrow) Kwave::UndoModifyMetaDataAction(list)))
971  return;
972  }
973 
974  // adjust the track bound meta data
975  m_meta_data.deleteTrack(index);
976 
977  setModified(true);
978  m_signal.deleteTrack(index);
979 }
980 
981 //***************************************************************************
983  Kwave::Track *track)
984 {
985  setModified(true);
986 
987  Kwave::FileInfo file_info(m_meta_data);
988  file_info.setTracks(tracks());
990 
991  emit sigTrackInserted(index, track);
993 }
994 
995 //***************************************************************************
997  Kwave::Track *track)
998 {
999  setModified(true);
1000 
1001  Kwave::FileInfo file_info(m_meta_data);
1002  file_info.setTracks(tracks());
1004 
1005  emit sigTrackDeleted(index, track);
1007 }
1008 
1009 //***************************************************************************
1012 {
1013  // remember the last known length
1015 
1016  setModified(true);
1017 
1018  // only adjust the meta data once per operation
1019  QList<unsigned int> tracks = selectedTracks();
1020  if (track == tracks[0]) {
1021  m_meta_data.shiftRight(offset, length, tracks);
1022  }
1023 
1024  emit sigSamplesInserted(track, offset, length);
1025 
1027  info.setLength(m_last_length);
1030 }
1031 
1032 //***************************************************************************
1035 {
1036  // remember the last known length
1038 
1039  setModified(true);
1040 
1041  // only adjust the meta data once per operation
1042  QList<unsigned int> tracks = selectedTracks();
1043  if (track == tracks[0]) {
1044  m_meta_data.shiftLeft(offset, length, tracks);
1045  }
1046 
1047  emit sigSamplesDeleted(track, offset, length);
1048 
1050  info.setLength(m_last_length);
1053 }
1054 
1055 //***************************************************************************
1058 {
1059  setModified(true);
1060  emit sigSamplesModified(track, offset, length);
1061 }
1062 
1063 //***************************************************************************
1065  sample_index_t length, const QList<unsigned int> &track_list)
1066 {
1067  if (!length || track_list.isEmpty()) return true; // nothing to do
1068 
1069  // put the selected meta data into a undo action
1070  if (m_undo_enabled) {
1071  if (!registerUndoAction(new(std::nothrow) UndoDeleteMetaDataAction(
1072  m_meta_data.copy(offset, length, track_list))))
1073  {
1075  return false;
1076  }
1077  m_meta_data.deleteRange(offset, length, track_list);
1078 
1079  // store undo data for all audio data (without meta data)
1080  if (!registerUndoAction(new(std::nothrow) UndoDeleteAction(
1081  m_parent_widget, track_list, offset, length)))
1082  {
1084  return false;
1085  }
1086  } else {
1087  // delete without undo
1088  m_meta_data.deleteRange(offset, length, track_list);
1089  }
1090 
1091  // delete the ranges in all tracks
1092  // (this makes all metadata positions after the selected range invalid)
1093  foreach (unsigned int track, track_list) {
1094  m_signal.deleteRange(track, offset, length);
1095  }
1096 
1098 
1099  return true;
1100 }
1101 
1102 //***************************************************************************
1105 {
1106  return deleteRange(offset, length, selectedTracks());
1107 }
1108 
1109 //***************************************************************************
1112  const QList<unsigned int> &track_list)
1113 {
1114  if (!length) return true; // nothing to do
1115  Kwave::UndoTransactionGuard undo(*this, i18n("Insert Space"));
1116 
1117  unsigned int count = track_list.count();
1118  if (!count) return true; // nothing to do
1119 
1120  // first store undo data for all tracks
1121  unsigned int track;
1122  if (m_undo_enabled) {
1123  if (!registerUndoAction(new(std::nothrow) Kwave::UndoInsertAction(
1124  m_parent_widget, track_list, offset, length))) return false;
1125  }
1126 
1127  // then insert space into all tracks
1128  foreach (track, track_list) {
1129  m_signal.insertSpace(track, offset, length);
1130  }
1131 
1132  return true;
1133 }
1134 
1135 //***************************************************************************
1138 {
1139  // first do some range checking
1140  sample_index_t len = this->length();
1141 
1142  if (length > len) length = len;
1143  if (offset >= len) offset = (len) ? (len - 1) : 0;
1144  if ((offset + length) > len) length = len - offset;
1145 
1146  m_selection.select(offset, length);
1147 }
1148 
1149 //***************************************************************************
1150 void Kwave::SignalManager::selectTracks(QList<unsigned int> &track_list)
1151 {
1152  unsigned int track;
1153  unsigned int n_tracks = tracks();
1154  for (track = 0; track < n_tracks; track++) {
1155  bool old_select = m_signal.trackSelected(track);
1156  bool new_select = track_list.contains(track);
1157  if (new_select != old_select) {
1158  m_signal.selectTrack(track, new_select);
1159  }
1160  }
1161 }
1162 
1163 //***************************************************************************
1164 void Kwave::SignalManager::selectTrack(unsigned int track, bool select)
1165 {
1166  bool old_select = m_signal.trackSelected(track);
1167  if (select != old_select) {
1168  m_signal.selectTrack(track, select);
1169  }
1170 }
1171 
1172 //***************************************************************************
1173 QList<Kwave::Stripe::List> Kwave::SignalManager::stripes(
1174  const QList<unsigned int> &track_list,
1175  sample_index_t left, sample_index_t right)
1176 {
1177  QList<Kwave::Stripe::List> stripes;
1178 
1179  foreach (unsigned int track, track_list) {
1180  Kwave::Stripe::List s = m_signal.stripes(track, left, right);
1181  if (s.isEmpty()) {
1182  stripes.clear(); // something went wrong -> abort
1183  break;
1184  }
1185  stripes.append(s);
1186  }
1187 
1188  return stripes;
1189 }
1190 
1191 //***************************************************************************
1193  const QList<Kwave::Stripe::List> &stripes,
1194  const QList<unsigned int> &track_list)
1195 {
1196  Q_ASSERT(stripes.count() == track_list.count());
1197  if (stripes.count() != track_list.count())
1198  return false;
1199 
1200  QListIterator<Kwave::Stripe::List> it_s(stripes);
1201  QListIterator<unsigned int> it_t(track_list);
1202  while (it_s.hasNext() && it_t.hasNext()) {
1203  Kwave::Stripe::List s = it_s.next();
1204  unsigned int t = it_t.next();
1205  if (!m_signal.mergeStripes(s, t))
1206  return false; // operation failed
1207  }
1208 
1209  return true;
1210 }
1211 
1212 //***************************************************************************
1214 {
1215  return m_playback_controller;
1216 }
1217 
1218 //***************************************************************************
1220 {
1221  if (!m_undo_enabled) return; // undo is currently not enabled
1222 
1223  QMutexLocker lock(&m_undo_transaction_lock);
1224 
1225  // check for modified selection
1227 
1228  // increase recursion level
1230 
1231  // start/create a new transaction if none existing
1232  if (!m_undo_transaction) {
1233 
1234  // if a new action starts, discard all redo actions !
1235  flushRedoBuffer();
1236 
1237  m_undo_transaction = new(std::nothrow) Kwave::UndoTransaction(name);
1238  Q_ASSERT(m_undo_transaction);
1239  if (!m_undo_transaction) return;
1240 
1241  // give all registered undo handlers a chance to register their own
1242  // undo actions
1244  delete m_undo_transaction;
1245  m_undo_transaction = Q_NULLPTR;
1246  }
1247 
1248  // if it is the start of the transaction, also create one
1249  // for the selection
1250  UndoAction *selection = new(std::nothrow) Kwave::UndoSelection(*this);
1251  Q_ASSERT(selection);
1252  if (selection && selection->store(*this)) {
1253  m_undo_transaction->append(selection);
1254  } else {
1255  // out of memory
1256  delete selection;
1257  delete m_undo_transaction;
1258  m_undo_transaction = Q_NULLPTR;
1259  }
1260  }
1261 }
1262 
1263 //***************************************************************************
1265 {
1266  QMutexLocker lock(&m_undo_transaction_lock);
1267 
1268  // decrease recursion level
1269  if (!m_undo_transaction_level) return; // undo was not enabled ?
1271 
1272  if (!m_undo_transaction_level) {
1273  // append the current transaction to the undo buffer if
1274  // not empty
1275  if (m_undo_transaction) {
1276  if (!m_undo_transaction->isEmpty()) {
1277  // if the transaction has been aborted, undo all actions
1278  // that have currently been queued but do not
1279  // use the transaction any more, instead delete it.
1280  if (m_undo_transaction->isAborted()) {
1281  qDebug("SignalManager::closeUndoTransaction(): aborted");
1282  while (!m_undo_transaction->isEmpty()) {
1283  UndoAction *undo_action;
1284  UndoAction *redo_action;
1285 
1286  // unqueue the undo action
1287  undo_action = m_undo_transaction->takeLast();
1288  Q_ASSERT(undo_action);
1289  if (!undo_action) continue;
1290 
1291  // execute the undo operation
1292  redo_action = undo_action->undo(*this, false);
1293 
1294  // remove the old undo action if no longer used
1295  if (redo_action && (redo_action != undo_action))
1296  delete redo_action;
1297  delete undo_action;
1298  }
1299  delete m_undo_transaction;
1300  m_undo_transaction = Q_NULLPTR;
1301  } else {
1303  }
1304  } else {
1305  qDebug("SignalManager::closeUndoTransaction(): empty");
1306  delete m_undo_transaction;
1307  m_undo_transaction = Q_NULLPTR;
1308  }
1309  }
1310 
1311  // dump, for debugging
1312 // if (m_undo_transaction)
1313 // m_undo_transaction->dump("closed undo transaction: ");
1314 
1315  // declare the current transaction as "closed"
1317  m_undo_transaction = Q_NULLPTR;
1318  emitUndoRedoInfo();
1319  }
1320 }
1321 
1322 //***************************************************************************
1324 {
1325  m_undo_enabled = true;
1326  emitUndoRedoInfo();
1327 }
1328 
1329 //***************************************************************************
1331 {
1333  qWarning("SignalManager::disableUndo(): undo transaction level=%u",
1335 
1336  flushUndoBuffers();
1337  m_undo_enabled = false;
1338 }
1339 
1340 //***************************************************************************
1342 {
1343  QMutexLocker lock(&m_undo_transaction_lock);
1344 
1345  // if the signal was modified, it will stay in this state, it is
1346  // not possible to change to "non-modified" state through undo
1347  if ((!m_undo_buffer.isEmpty()) && (m_modified)) {
1348  enableModifiedChange(false);
1349  }
1350 
1351  // clear all buffers
1352  qDeleteAll(m_undo_buffer);
1353  qDeleteAll(m_redo_buffer);
1354  m_undo_buffer.clear();
1355  m_redo_buffer.clear();
1356 
1357  emitUndoRedoInfo();
1358 }
1359 
1360 //***************************************************************************
1362 {
1363  // abort the current transaction
1365 }
1366 
1367 //***************************************************************************
1369 {
1370  qDeleteAll(m_redo_buffer);
1371  m_redo_buffer.clear();
1372  emitUndoRedoInfo();
1373 }
1374 
1375 //***************************************************************************
1377 {
1378  // undo has not been enabled before?
1379  if (!m_undo_transaction) return true;
1380 
1381  // transaction has been aborted before
1382  if (m_undo_transaction->isAborted()) return false;
1383 
1384  // transaction is empty -> must have been flushed before, otherwise
1385  // it would contain at least a undo action for the selection
1386  // => user already has pressed "continue"
1387  if (m_undo_transaction->isEmpty()) return true;
1388 
1390  _("<html>") +
1391  i18n("Not enough memory for saving undo information.") +
1392  _("<br><br><b>") +
1393  i18n("Do you want to continue without the possibility to undo?") +
1394  _("</b><br><br><i>") +
1395  i18n("<b>Hint</b>: you can configure the amount of memory<br>"
1396  "available for undo under '%1'/'%2'.",
1397  i18n("Settings"),
1398  i18n("Memory") +
1399  _("</i></html>"))) == KMessageBox::Continue)
1400  {
1401  // the signal was modified, it will stay in this state, it is
1402  // not possible to change to "non-modified" state through undo
1403  // from now on...
1404  setModified(true);
1405  enableModifiedChange(false);
1406 
1407  // flush the current undo transaction
1408  while (!m_undo_transaction->isEmpty()) {
1409  Kwave::UndoAction *undo_action = m_undo_transaction->takeLast();
1410  if (undo_action) delete undo_action;
1411  }
1412 
1413  // flush all undo/redo buffers
1414  flushUndoBuffers();
1415  return true;
1416  }
1417 
1418  // Set the undo transaction into "aborted" state. The final
1419  // closeUndoTransaction() will take care of the rest when
1420  // detecting that state and clean up...
1422  return false;
1423 }
1424 
1425 //***************************************************************************
1427 {
1428  QMutexLocker lock(&m_undo_transaction_lock);
1429 
1430  if (!action) return continueWithoutUndo();
1431 
1432  // if undo is not enabled, this will fail -> discard the action
1433  if (!m_undo_enabled) {
1434  delete action;
1435  return true;
1436  }
1437 
1438  // check if the undo action is too large
1439  qint64 limit_mb = Kwave::MemoryManager::instance().undoLimit();
1440  qint64 needed_size = action->undoSize();
1441  qint64 needed_mb = needed_size >> 20;
1442  if (needed_mb > limit_mb) {
1443  delete action;
1444  return continueWithoutUndo();
1445  }
1446 
1447  // undo has been aborted before ?
1448  if (!m_undo_transaction) return true;
1449 
1450  // transaction has been aborted before
1451  if (m_undo_transaction->isAborted()) {
1452  delete action;
1453  return true;
1454  }
1455 
1456  // make room...
1457  freeUndoMemory(needed_size);
1458 
1459  // now we might have enough place to append the undo action
1460  // and store all undo info
1461  if (!action->store(*this)) {
1462  delete action;
1463  return continueWithoutUndo();
1464  }
1465 
1466  // everything went ok, register internally
1467  m_undo_transaction->append(action);
1468 
1469  return true;
1470 }
1471 
1472 //***************************************************************************
1473 bool Kwave::SignalManager::saveUndoDelete(QList<unsigned int> &track_list,
1474  sample_index_t offset,
1476 {
1477  if (!m_undo_enabled) return true;
1478  if (track_list.isEmpty()) return true;
1479 
1480  // create a undo action for deletion
1481  UndoDeleteAction *action = new(std::nothrow)
1482  UndoDeleteAction(m_parent_widget, track_list, offset, length);
1483  if (!registerUndoAction(action)) return false;
1484 
1485  return true;
1486 }
1487 
1488 //***************************************************************************
1490 {
1491  qint64 size = 0;
1492 
1494  if (undo) size += undo->undoSize();
1495 
1497  if (redo) size += redo->undoSize();
1498 
1499  return size;
1500 }
1501 
1502 //***************************************************************************
1504 {
1505  qint64 size = usedUndoRedoMemory() + needed;
1506  qint64 undo_limit = Kwave::MemoryManager::instance().undoLimit() << 20;
1507 
1508  // remove old undo actions if not enough free memory
1509  while (!m_undo_buffer.isEmpty() && (size > undo_limit)) {
1510  Kwave::UndoTransaction *undo = m_undo_buffer.takeFirst();
1511  if (!undo) continue;
1512  qint64 s = undo->undoSize();
1513  size = (size >= s) ? (size - s) : 0;
1514  delete undo;
1515 
1516  // if the signal was modified, it will stay in this state, it is
1517  // not possible to change to "non-modified" state through undo
1518  if (m_modified) enableModifiedChange(false);
1519  }
1520 
1521  // remove old redo actions if still not enough memory
1522  while (!m_redo_buffer.isEmpty() && (size > undo_limit)) {
1524  if (!redo) continue;
1525  qint64 s = redo->undoSize();
1526  size = (size >= s) ? (size - s) : 0;
1527  delete redo;
1528  }
1529 }
1530 
1531 //***************************************************************************
1533 {
1534  QString undo_name = QString();
1535  QString redo_name = QString();
1536 
1537  if (m_undo_enabled) {
1538  Kwave::UndoTransaction *transaction;
1539 
1540  // get the description of the last undo action
1541  if (!m_undo_buffer.isEmpty()) {
1542  transaction = m_undo_buffer.last();
1543  if (transaction) undo_name = transaction->description();
1544  if (!undo_name.length()) undo_name = i18n("Last Action");
1545  }
1546 
1547  // get the description of the last redo action
1548  if (!m_redo_buffer.isEmpty()) {
1549  transaction = m_redo_buffer.first();
1550  if (transaction) redo_name = transaction->description();
1551  if (!redo_name.length()) redo_name = i18n("Last Action");
1552  }
1553  }
1554 
1555  // now emit the undo/redo transaction names
1556  emit sigUndoRedoInfo(undo_name, redo_name);
1557 }
1558 
1559 //***************************************************************************
1561 {
1562  QMutexLocker lock(&m_undo_transaction_lock);
1563 
1564  // check for modified selection
1566 
1567  // remember the last selection
1569 
1570  // get the last undo transaction and abort if none present
1571  if (m_undo_buffer.isEmpty()) return;
1572  Kwave::UndoTransaction *undo_transaction = m_undo_buffer.takeLast();
1573  if (!undo_transaction) return;
1574 
1575  // dump, for debugging
1576 // undo_transaction->dump("before undo: ");
1577 
1578  // temporarily disable undo while undo itself is running
1579  bool old_undo_enabled = m_undo_enabled;
1580  m_undo_enabled = false;
1581 
1582  // get free memory for redo
1583  qint64 undo_limit = Kwave::MemoryManager::instance().undoLimit() << 20;
1584  qint64 redo_size = undo_transaction->redoSize();
1585  qint64 undo_size = undo_transaction->undoSize();
1586  Kwave::UndoTransaction *redo_transaction = Q_NULLPTR;
1587  if ((redo_size > undo_size) && (redo_size - undo_size > undo_limit)) {
1588  // not enough memory for redo
1589  qWarning("SignalManager::undo(): not enough memory for redo !");
1590  } else {
1591  // only free the memory if it will be used
1592  freeUndoMemory(redo_size);
1593 
1594  // create a new redo transaction
1595  QString name = undo_transaction->description();
1596  redo_transaction = new(std::nothrow) Kwave::UndoTransaction(name);
1597  Q_ASSERT(redo_transaction);
1598  }
1599 
1600  // if *one* redo fails, all following redoes will also fail or
1601  // produce inconsistent data -> remove all of them !
1602  if (!redo_transaction) {
1603  flushRedoBuffer();
1604  qDebug("SignalManager::undo(): redo buffer flushed!");
1605  }
1606 
1607  // set hourglass cursor
1608  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1609 
1610  // execute all undo actions and store the resulting redo
1611  // actions into the redo transaction
1612  while (!undo_transaction->isEmpty()) {
1613  UndoAction *undo_action;
1614  UndoAction *redo_action;
1615 
1616  // unqueue the undo action
1617  undo_action = undo_transaction->takeLast();
1618  Q_ASSERT(undo_action);
1619  if (!undo_action) continue;
1620 
1621  // execute the undo operation
1622  redo_action = undo_action->undo(*this, (redo_transaction != Q_NULLPTR));
1623 
1624  // remove the old undo action if no longer used
1625  if (redo_action != undo_action) {
1626  delete undo_action;
1627  }
1628 
1629  // queue the action into the redo transaction
1630  if (redo_action) {
1631  if (redo_transaction) {
1632  redo_transaction->prepend(redo_action);
1633  } else {
1634  // redo is not usable :-(
1635  delete redo_action;
1636  }
1637  }
1638  }
1639 
1640  // now the undo_transaction should be empty -> get rid of it
1641  Q_ASSERT(undo_transaction->isEmpty());
1642  delete undo_transaction;
1643 
1644  if (redo_transaction && (redo_transaction->count() < 1)) {
1645  // if there is no redo action -> no redo possible
1646  qWarning("SignalManager::undo(): no redo possible");
1647  delete redo_transaction;
1648  redo_transaction = Q_NULLPTR;
1649  }
1650 
1651  // check whether the selection has changed, if yes: put a undo action
1652  // for this selection change at the end of the redo transaction
1653  if (redo_transaction) {
1654  bool range_modified = !(m_selection == m_last_selection);
1655  QList<unsigned int> tracks = selectedTracks();
1656  bool tracks_modified = !(tracks == m_last_track_selection);
1657  if (range_modified || tracks_modified) {
1658  UndoAction *redo_action = new(std::nothrow) Kwave::UndoSelection(
1659  *this,
1663  Q_ASSERT(redo_action);
1664  if (redo_action) redo_transaction->append(redo_action);
1665  }
1666  }
1667 
1668  // find out if there is still an action in the undo buffer
1669  // that has to do with modification of the signal
1670  if (m_modified) {
1671  bool stay_modified = false;
1672  foreach (Kwave::UndoTransaction *transaction, m_undo_buffer) {
1673  if (!transaction) continue;
1674  if (transaction->containsModification()) {
1675  stay_modified = true;
1676  break;
1677  }
1678  }
1679  if (!stay_modified) {
1680  // try to return to non-modified mode (might be a nop if
1681  // not enabled)
1682  setModified(false);
1683  }
1684  }
1685 
1686  // save "redo" information if possible
1687  if (redo_transaction)
1688  m_redo_buffer.prepend(redo_transaction);
1689 
1690  // remember the last selection
1692 
1693  // re-enable undo
1694  m_undo_enabled = old_undo_enabled;
1695 
1696  // finished / buffers have changed, emit new undo/redo info
1697  emitUndoRedoInfo();
1698 
1699  // maybe the meta data has changed
1701 
1702  // remove hourglass
1703  QApplication::restoreOverrideCursor();
1704 }
1705 
1706 //***************************************************************************
1708 {
1709  QMutexLocker lock(&m_undo_transaction_lock);
1710 
1711  // get the last redo transaction and abort if none present
1712  if (m_redo_buffer.isEmpty()) return;
1713  Kwave::UndoTransaction *redo_transaction = m_redo_buffer.takeFirst();
1714  if (!redo_transaction) return;
1715 
1716  // check for modified selection
1718 
1719  // temporarily disable undo while redo is running
1720  bool old_undo_enabled = m_undo_enabled;
1721  m_undo_enabled = false;
1722 
1723  // get free memory for undo
1724  qint64 undo_limit = Kwave::MemoryManager::instance().undoLimit() << 20;
1725  qint64 undo_size = redo_transaction->undoSize();
1726  qint64 redo_size = redo_transaction->redoSize();
1727  Kwave::UndoTransaction *undo_transaction = Q_NULLPTR;
1728  if ((undo_size > redo_size) && (undo_size - redo_size > undo_limit)) {
1729  // not enough memory for undo
1730  qWarning("SignalManager::redo(): not enough memory for undo !");
1731  } else {
1732  // only free the memory if it will be used
1733  freeUndoMemory(undo_size);
1734 
1735  // create a new undo transaction
1736  QString name = redo_transaction->description();
1737  undo_transaction = new(std::nothrow) Kwave::UndoTransaction(name);
1738  Q_ASSERT(undo_transaction);
1739  }
1740 
1741  // if *one* undo fails, all following undoes will also fail or
1742  // produce inconsistent data -> remove all of them !
1743  if (!undo_transaction) {
1744  qDeleteAll(m_undo_buffer);
1745  m_undo_buffer.clear();
1746  } else {
1747  m_undo_buffer.append(undo_transaction);
1748  }
1749 
1750  // set hourglass cursor
1751  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1752 
1753  // execute all redo actions and store the resulting undo
1754  // actions into the undo transaction
1755  bool modified = false;
1756  while (!redo_transaction->isEmpty()) {
1757  UndoAction *undo_action;
1758  UndoAction *redo_action;
1759 
1760  // unqueue the undo action
1761  redo_action = redo_transaction->takeFirst();
1762 
1763  // execute the redo operation
1764  Q_ASSERT(redo_action);
1765  if (!redo_action) continue;
1766  modified |= redo_transaction->containsModification();
1767  undo_action = redo_action->undo(*this, (undo_transaction != Q_NULLPTR));
1768 
1769  // remove the old redo action if no longer used
1770  if (redo_action != undo_action) {
1771  delete redo_action;
1772  }
1773 
1774  // queue the action into the undo transaction
1775  if (undo_action) {
1776  if (undo_transaction) {
1777  undo_transaction->append(undo_action);
1778  } else {
1779  // undo is not usable :-(
1780  delete undo_action;
1781  }
1782  }
1783  }
1784 
1785  // now the redo_transaction should be empty -> get rid of it
1786  Q_ASSERT(redo_transaction->isEmpty());
1787  delete redo_transaction;
1788 
1789  if (undo_transaction && (undo_transaction->count() < 1)) {
1790  // if there is no undo action -> no undo possible
1791  qWarning("SignalManager::redo(): no undo possible");
1792  m_undo_buffer.removeAll(undo_transaction);
1793  delete undo_transaction;
1794  }
1795 
1796  // if the redo operation modified something,
1797  // we have to update the "modified" flag of the current signal
1798  if (modified) setModified(true);
1799 
1800  // remember the last selection
1802 
1803  // re-enable undo
1804  m_undo_enabled = old_undo_enabled;
1805 
1806  // finished / buffers have changed, emit new undo/redo info
1807  emitUndoRedoInfo();
1808 
1809  // maybe the meta data has changed
1811 
1812  // remove hourglass
1813  QApplication::restoreOverrideCursor();
1814 }
1815 
1816 //***************************************************************************
1818 {
1819  if (!m_modified_enabled) return;
1820 
1821  if (m_modified != mod) {
1822  m_modified = mod;
1823 // qDebug("SignalManager::setModified(%d)",mod);
1824  emit sigModified();
1825  }
1826 }
1827 
1828 //***************************************************************************
1830 {
1831  m_modified_enabled = en;
1832 }
1833 
1834 //***************************************************************************
1836  bool with_undo)
1837 {
1838  if (m_undo_enabled && with_undo) {
1839  /* save data for undo */
1840  Kwave::UndoTransactionGuard undo_transaction(*this,
1841  i18n("Modify File Info"));
1842  Kwave::FileInfo old_inf(m_meta_data);
1843  if (!registerUndoAction(
1844  new(std::nothrow) Kwave::UndoModifyMetaDataAction(
1845  Kwave::MetaDataList(old_inf))))
1846  return;
1847  }
1848 
1850  setModified(true);
1851  emitUndoRedoInfo();
1853 }
1854 
1855 //***************************************************************************
1857 {
1858  Kwave::LabelList labels(m_meta_data);
1859  foreach (const Kwave::Label &label, labels) {
1860  if (label.pos() == pos) return label; // found it
1861  }
1862  return Kwave::Label(); // nothing found
1863 }
1864 
1865 //***************************************************************************
1867 {
1868  int index = 0;
1869  Kwave::LabelList labels(m_meta_data);
1870  foreach (const Kwave::Label &l, labels) {
1871  if (l == label) return index; // found it
1872  index++;
1873  }
1874  return -1; // nothing found*/
1875 }
1876 
1877 //***************************************************************************
1879  const QString &name)
1880 {
1881  // if there already is a label at the given position, do nothing
1882  if (!findLabel(pos).isNull()) return Kwave::Label();
1883 
1884  // create a new label
1885  Kwave::Label label(pos, name);
1886 
1887  // register the undo action
1888  if (m_undo_enabled) {
1889  Kwave::UndoTransactionGuard undo(*this, i18n("Add Label"));
1890  if (!registerUndoAction(new(std::nothrow) UndoAddMetaDataAction(
1891  Kwave::MetaDataList(label))))
1892  return Kwave::Label();
1893  }
1894 
1895  // put the label into the list
1896  m_meta_data.add(label);
1897 
1898  // register this as a modification
1899  setModified(true);
1900 
1902 
1903  return label;
1904 }
1905 
1906 //***************************************************************************
1907 void Kwave::SignalManager::deleteLabel(int index, bool with_undo)
1908 {
1909  Kwave::LabelList labels(m_meta_data);
1910  int count = Kwave::toInt(labels.count());
1911  if (!count) return;
1912 
1913  if (index == -1) {
1914  // special handling for index == -1 -> delete all labels
1915 
1916  if (with_undo) startUndoTransaction(i18n("Delete All Labels"));
1917 
1918  for (index = count - 1; index >= 0; --index) {
1919  Kwave::MetaData label(labels.at(index));
1920  if (with_undo) {
1921  if (!registerUndoAction(new(std::nothrow)
1923  break;
1924  }
1925  m_meta_data.remove(label);
1926  }
1927  } else {
1928  // delete a single label
1929  if ((index < 0) || (index >= count)) return;
1930 
1931  Kwave::MetaData label(labels.at(index));
1932 
1933  // register the undo action
1934  if (with_undo) {
1935  startUndoTransaction(i18n("Delete Label"));
1936  if (!registerUndoAction(new(std::nothrow)
1939  return;
1940  }
1941  }
1942 
1943  m_meta_data.remove(label);
1944  }
1945 
1946  if (with_undo) closeUndoTransaction();
1947 
1948  // register this as a modification
1949  setModified(true);
1950 
1952 }
1953 
1954 //***************************************************************************
1956  const QString &name,
1957  bool with_undo)
1958 {
1959  Kwave::LabelList labels(m_meta_data);
1960  if ((index < 0) || (index >= Kwave::toInt(labels.count())))
1961  return false;
1962 
1963  Kwave::Label label = labels.at(index);
1964 
1965  // check: if the label should be moved and there already is a label
1966  // at the new position -> fail
1967  if ((pos != label.pos()) && !findLabel(pos).isNull())
1968  return false;
1969 
1970  // add a undo action
1971  if (with_undo && m_undo_enabled) {
1972  Kwave::UndoModifyMetaDataAction *undo_modify =
1973  new(std::nothrow) Kwave::UndoModifyMetaDataAction(
1974  Kwave::MetaDataList(label));
1975  if (!registerUndoAction(undo_modify))
1976  return false;
1977  }
1978 
1979  // now modify the label
1980  label.moveTo(pos);
1981  label.rename(name);
1982  m_meta_data.add(label);
1983 
1984  // register this as a modification
1985  setModified(true);
1986 
1988  return true;
1989 }
1990 
1991 //***************************************************************************
1993 {
1994  m_meta_data.merge(meta_data);
1996 }
1997 
1998 //***************************************************************************
2000 {
2003 }
2004 
2005 //***************************************************************************
2007 {
2008  if (m_undo_transaction_level) return;
2009 
2010  // detect sample selection change
2011  bool range_modified = !(m_selection == m_last_selection);
2012 
2013  // detect track selection change
2014  QList<unsigned int> tracks = selectedTracks();
2015  bool tracks_modified = !(tracks == m_last_track_selection);
2016 
2017  if (range_modified || tracks_modified) {
2018  // selection has changed since last undo/redo operation
2019 // qDebug("SignalManager::checkSelectionChange() => manually modified");
2020 // qDebug(" before: [%8u...%8u]", m_last_selection.first(), m_last_selection.last());
2021 // qDebug(" after : [%8u...%8u]", m_selection.first(), m_selection.last());
2022 
2023  // temporarily activate the previous selection (last stored)
2024  Kwave::Selection new_selection(m_selection);
2027 
2028  // save the last selection into a undo action
2029  if (tracks_modified && !range_modified)
2031  i18n("Manual Track Selection"));
2032  else
2034  i18n("Manual Selection"));
2035 
2036  // restore the current selection again
2037  m_selection = new_selection;
2038  selectTracks(tracks);
2039  }
2040 
2041 }
2042 
2043 //***************************************************************************
2044 //***************************************************************************
sample_index_t toSampleIndex()
Definition: Parser.cpp:246
virtual void cropByTracks(const QList< unsigned int > &tracks)
int loadFile(const QUrl &url)
void sigMetaDataChanged(Kwave::MetaDataList meta)
virtual MetaDataList copy(sample_index_t offset, sample_index_t length, const QList< unsigned int > &tracks) const
int save(const QUrl &url, bool selection)
void select(sample_index_t offset, sample_index_t length)
Definition: Selection.cpp:38
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
void deleteTrack(unsigned int index)
Definition: Signal.cpp:119
void selectRange(sample_index_t offset, sample_index_t length)
QList< Kwave::UndoTransaction * > m_redo_buffer
sample_index_t first() const
Definition: Selection.h:71
void mergeMetaData(const Kwave::MetaDataList &meta_data)
sample_index_t length()
Definition: Signal.cpp:258
Kwave::Stripe::List stripes(unsigned int track, sample_index_t left=0, sample_index_t right=SAMPLE_INDEX_MAX)
Definition: Signal.cpp:175
void sigSamplesModified(unsigned int track, sample_index_t offset, sample_index_t length)
Kwave::Track * appendTrack(sample_index_t length, QUuid *uuid)
Definition: Signal.cpp:113
#define NEW_FILENAME
Definition: SignalManager.h:46
void sigTrackInserted(unsigned int index, Kwave::Track *track)
void setBytePosition(quint64 pos)
double rate() const
Definition: FileInfo.cpp:415
virtual MetaDataList selectByTracks(const QList< unsigned int > &tracks) const
static MemoryManager & instance() Q_DECL_EXPORT
virtual void cropByRange(sample_index_t first, sample_index_t last)
sample_index_t last() const
Definition: Selection.h:76
static ClipBoard & instance()
Definition: ClipBoard.cpp:39
bool insertSpace(sample_index_t offset, sample_index_t length, const QList< unsigned int > &track_list)
virtual void shiftRight(sample_index_t offset, sample_index_t shift, const QList< unsigned int > &tracks)
QString name(FileProperty key) const
Definition: FileInfo.h:227
virtual void remove(const MetaData &metadata)
bool containsModification() const
void sigSamplesInserted(unsigned int track, sample_index_t offset, sample_index_t length)
virtual void moveTo(sample_index_t position)
Definition: Label.cpp:49
void slotTrackDeleted(unsigned int index, Kwave::Track *track)
void copy(QWidget *widget, Kwave::SignalManager &signal_manager, const QList< unsigned int > &track_list, sample_index_t offset, sample_index_t length)
Definition: ClipBoard.cpp:69
Kwave::Selection & selection()
void deleteLabel(int index, bool with_undo)
virtual sample_index_t pos() const
Definition: Label.cpp:56
void newSignal(sample_index_t samples, double rate, unsigned int bits, unsigned int tracks)
virtual void dump() const
void setLength(quint64 samples)
unsigned int tracks()
quint64 sample_index_t
Definition: Sample.h:28
Kwave::PlaybackController & playbackController()
bool deleteRange(sample_index_t offset, sample_index_t length, const QList< unsigned int > &track_list)
unsigned int m_undo_transaction_level
int toInt()
Definition: Parser.cpp:216
virtual void add(const MetaData &metadata)
virtual void insertTrack(unsigned int track)
static QString mimeTypeOf(const QUrl &url)
void merge(const MetaDataList &meta_data)
bool registerUndoAction(Kwave::UndoAction *action)
void insertTrack(unsigned int index)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
virtual sample_index_t last() const
Definition: MultiWriter.cpp:85
void setModified(bool mod)
sample_index_t length() const
Definition: Selection.h:66
void freeUndoMemory(qint64 needed)
void setRate(double rate)
Definition: FileInfo.cpp:424
void setFileInfo(const Kwave::FileInfo &new_info, bool with_undo)
sample_index_t m_last_length
const char name[16]
Definition: memcpy.c:510
QListIterator< Kwave::Label > LabelListIterator
Definition: LabelList.h:76
void sigTrackSelectionChanged(bool enabled)
virtual void flush()
bool trackSelected(unsigned int track)
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
void deleteRange(unsigned int track, sample_index_t offset, sample_index_t length)
Definition: Signal.cpp:220
QList< FileProperty > allKnownProperties() const
Definition: FileInfo.cpp:383
sample_index_t length() const
Definition: FileInfo.cpp:400
void sigSamplesDeleted(unsigned int track, sample_index_t offset, sample_index_t length)
Kwave::PlaybackController m_playback_controller
int labelIndex(const Kwave::Label &label) const
sample_index_t length()
void startUndoTransaction(const QString &name=QString())
const QMap< FileProperty, QVariant > properties() const
Definition: FileInfo.cpp:389
int toInt(T x)
Definition: Utils.h:127
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
double rate() const
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
Kwave::MetaDataList m_meta_data
virtual bool isNull() const
Definition: MetaData.cpp:69
virtual void rename(const QString &name)
Definition: Label.cpp:64
unsigned int toUInt()
Definition: Parser.cpp:231
bool saveUndoDelete(QList< unsigned int > &track_list, sample_index_t offset, sample_index_t length)
Kwave::Selection m_last_selection
virtual void replace(const MetaDataList &list)
Kwave::Signal m_signal
void slotSamplesInserted(unsigned int track, sample_index_t offset, sample_index_t length)
int executeCommand(const QString &command)
static int warningContinueCancel(QWidget *widget, QString message, QString caption=QString(), const QString buttonContinue=QString(), const QString buttonCancel=QString(), const QString &dontAskAgainName=QString())
Definition: MessageBox.cpp:115
const QList< unsigned int > allTracks()
unsigned int tracks() const
Definition: FileInfo.cpp:445
virtual Kwave::UndoAction * undo(Kwave::SignalManager &manager, bool with_redo)=0
void setBits(unsigned int bits)
Definition: FileInfo.cpp:439
Kwave::Label addLabel(sample_index_t pos, const QString &name)
bool paste(QWidget *widget, Kwave::SignalManager &signal_manager, sample_index_t offset, sample_index_t length)
Definition: ClipBoard.cpp:99
bool startUndoTransaction(Kwave::UndoTransaction *transaction)
Definition: UndoManager.cpp:54
Kwave::UndoTransaction * m_undo_transaction
virtual void deleteRange(sample_index_t offset, sample_index_t length, const QList< unsigned int > &tracks)
void enableModifiedChange(bool en)
Kwave::Track * insertTrack(unsigned int index, sample_index_t length, QUuid *uuid)
Definition: Signal.cpp:69
void slotSamplesDeleted(unsigned int track, sample_index_t offset, sample_index_t length)
static Kwave::Decoder * decoder(const QString &mimetype_name)
static Kwave::Encoder * encoder(const QString &mimetype_name)
quint64 undoLimit() const Q_DECL_EXPORT
const QString & firstParam()
Definition: Parser.cpp:168
#define _(m)
Definition: memcpy.c:66
unsigned int bits() const
#define DBG(qs)
Definition: String.h:55
void slotSamplesModified(unsigned int track, sample_index_t offset, sample_index_t length)
Kwave::Label findLabel(sample_index_t pos)
bool modifyLabel(int index, sample_index_t pos, const QString &name, bool with_undo)
SignalManager(QWidget *parent)
void deleteTrack(unsigned int index)
void sigTrackDeleted(unsigned int index, Kwave::Track *track)
const QList< unsigned int > selectedTracks()
InsertMode
Definition: InsertMode.h:26
unsigned int bits() const
Definition: FileInfo.cpp:430
virtual void shiftLeft(sample_index_t offset, sample_index_t shift, const QList< unsigned int > &tracks)
QList< Kwave::UndoTransaction * > m_undo_buffer
void selectTracks(QList< unsigned int > &track_list)
void close()
Definition: Signal.cpp:61
void insertSpace(unsigned int track, sample_index_t offset, sample_index_t length)
Definition: Signal.cpp:236
FileProperty
Definition: FileInfo.h:45
void slotTrackInserted(unsigned int index, Kwave::Track *track)
Kwave::Selection m_selection
QList< unsigned int > allTracks()
Definition: Signal.cpp:206
bool mergeStripes(const Kwave::Stripe::List &stripes, unsigned int track)
Definition: Signal.cpp:190
virtual qint64 undoSize()=0
bool trackSelected(unsigned int track)
Definition: Signal.cpp:272
void sigUndoRedoInfo(const QString &undo, const QString &redo)
sample_index_t length()
Definition: Track.cpp:173
void selectTrack(unsigned int track, bool select)
#define UTF8(qs)
Definition: String.h:48
sample_index_t offset() const
Definition: Selection.h:61
Kwave::UndoManager m_undo_manager
const QString & nextParam()
Definition: Parser.cpp:175
virtual bool store(Kwave::SignalManager &manager)=0
void selectTrack(unsigned int track, bool select)
Definition: Signal.cpp:283
QList< Kwave::Stripe::List > stripes(const QList< unsigned int > &track_list, sample_index_t left=0, sample_index_t right=SAMPLE_INDEX_MAX)
virtual void deleteTrack(unsigned int track)
bool mergeStripes(const QList< Kwave::Stripe::List > &stripes, const QList< unsigned int > &track_list)
QList< unsigned int > m_last_track_selection
#define CASE_COMMAND(x)