1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "TocEdit.h"
21 
22 #include <stddef.h>
23 #include <gnome.h>
24 #include <iostream>
25 #include <sstream>
26 #include <set>
27 
28 #include "util.h"
29 #include "Toc.h"
30 #include "TocEditView.h"
31 #include "TrackData.h"
32 #include "TrackDataList.h"
33 #include "TrackDataScrap.h"
34 
35 #include "guiUpdate.h"
36 #include "SampleManager.h"
37 
TocEdit(Toc * t,const char * filename)38 TocEdit::TocEdit(Toc *t, const char *filename)
39 {
40   toc_ = NULL;
41   sampleManager_ = NULL;
42   filename_ = NULL;
43   trackDataScrap_ = NULL;
44   threadActive_ = false;
45   curState_ = TE_IDLE;
46   curConv_ = NULL;
47   cur_ = NULL;
48 
49   updateLevel_ = 0;
50   editBlocked_ = false;
51 
52   if (filename == NULL)
53     toc(t, "unnamed.toc");
54   else
55     toc(t, filename);
56 }
57 
~TocEdit()58 TocEdit::~TocEdit()
59 {
60   if (toc_)
61     delete toc_;
62 
63   if (sampleManager_)
64     delete sampleManager_;
65 
66   if (filename_)
67     delete[] filename_;
68 
69   if (trackDataScrap_)
70     delete trackDataScrap_;
71 }
72 
toc(Toc * t,const char * filename)73 void TocEdit::toc(Toc *t, const char *filename)
74 {
75   if (toc_)
76     delete toc_;
77 
78   if (t == NULL)
79     toc_ = new Toc;
80   else
81     toc_ = t;
82 
83   if (filename != NULL) {
84     delete[] filename_;
85     filename_ = strdupCC(filename);
86   }
87 
88   tocDirty_ = false;
89   editBlocked_ = false;
90 
91   if (sampleManager_)
92     delete sampleManager_;
93   sampleManager_ = new SampleManager(588);
94 
95   sampleManager_->setTocEdit(this);
96 
97   if (toc_->length().samples() > 0) {
98 
99     // First collect all filenames and queue their conversions to WAV
100     std::set<std::string> set;
101     toc_->collectFiles(set);
102     std::set<std::string>::iterator i = set.begin();
103     for (; i != set.end(); i++)
104       queueConversion((*i).c_str());
105 
106     // Second, queue for toc scan.
107     unsigned long maxSample = toc_->length().samples() - 1;
108     queueScan(0, -1);
109   }
110 
111   updateLevel_ = UPD_ALL;
112 }
113 
toc() const114 Toc *TocEdit::toc() const
115 {
116   return toc_;
117 }
118 
sampleManager()119 SampleManager *TocEdit::sampleManager()
120 {
121   return sampleManager_;
122 }
123 
tocDirty(bool f)124 void TocEdit::tocDirty(bool f)
125 {
126   bool old = tocDirty_;
127 
128   tocDirty_ = f;
129 
130   if (old != tocDirty_)
131     updateLevel_ |= UPD_TOC_DIRTY;
132 }
133 
blockEdit()134 void TocEdit::blockEdit()
135 {
136   if (editBlocked_ == 0)
137     updateLevel_ |= UPD_EDITABLE_STATE;
138 
139   editBlocked_ += 1;
140 }
141 
unblockEdit()142 void TocEdit::unblockEdit()
143 {
144   if (editBlocked_ > 0) {
145     editBlocked_ -= 1;
146 
147     if (editBlocked_ == 0)
148       updateLevel_ |= UPD_EDITABLE_STATE;
149   }
150 }
151 
updateLevel()152 unsigned long TocEdit::updateLevel()
153 {
154   unsigned long level = updateLevel_;
155 
156   updateLevel_ = 0;
157   return level;
158 }
159 
lengthSample() const160 unsigned long TocEdit::lengthSample() const
161 {
162   return toc_->length().samples();
163 }
164 
filename(const char * fname)165 void TocEdit::filename(const char *fname)
166 {
167   if (fname != NULL && *fname != 0) {
168     char *s = strdupCC(fname);
169     delete[] filename_;
170     filename_ = s;
171 
172     updateLevel_ |= UPD_TOC_DATA;
173   }
174 }
175 
filename() const176 const char *TocEdit::filename() const
177 {
178   return filename_;
179 }
180 
181 
readToc(const char * fname)182 int TocEdit::readToc(const char *fname)
183 {
184   if (!editable())
185     return 0;
186 
187   if (fname == NULL || *fname == 0)
188     return 1;
189 
190   Toc *t = Toc::read(fname);
191 
192   if (t != NULL) {
193 
194     // Check and resolve input files paths
195     t->resolveFilenames(fname);
196 
197     // Sometimes length fields are ommited. Make sure we got everything.
198     t->recomputeLength();
199 
200     toc(t, fname);
201     return 0;
202   }
203 
204   return 1;
205 }
206 
saveToc()207 int TocEdit::saveToc()
208 {
209   int ret = toc_->write(filename_);
210 
211   if (ret == 0)
212     tocDirty(0);
213 
214   return ret;
215 }
216 
saveAsToc(const char * fname)217 int TocEdit::saveAsToc(const char *fname)
218 {
219   int ret;
220 
221   if (fname != NULL && *fname != 0) {
222     ret = toc_->write(fname);
223 
224     if (ret == 0) {
225       filename(fname);
226       tocDirty(0);
227       updateLevel_ |= UPD_TOC_DATA;
228     }
229 
230     return ret;
231   }
232 
233   return 1;
234 }
235 
236 
moveTrackMarker(int trackNr,int indexNr,long lba)237 int TocEdit::moveTrackMarker(int trackNr, int indexNr, long lba)
238 {
239   if (!editable())
240     return 0;
241 
242   int ret = toc_->moveTrackMarker(trackNr, indexNr, lba);
243 
244   if (ret == 0) {
245     tocDirty(1);
246     updateLevel_ |= UPD_TRACK_DATA;
247   }
248 
249   return ret;
250 }
251 
addTrackMarker(long lba)252 int TocEdit::addTrackMarker(long lba)
253 {
254   if (!editable())
255     return 0;
256 
257   int ret = toc_->addTrackMarker(lba);
258 
259   if (ret == 0) {
260     tocDirty(1);
261 //llanero: different views
262 //    updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
263   }
264 
265   return ret;
266 }
267 
addIndexMarker(long lba)268 int TocEdit::addIndexMarker(long lba)
269 {
270   if (!editable())
271     return 0;
272 
273   int ret = toc_->addIndexMarker(lba);
274 
275   if (ret == 0) {
276     tocDirty(1);
277 //llanero: different views
278 //    updateLevel_ |= UPD_TRACK_DATA;
279   }
280 
281   return ret;
282 }
283 
addPregap(long lba)284 int TocEdit::addPregap(long lba)
285 {
286   if (!editable())
287     return 0;
288 
289   int ret = toc_->addPregap(lba);
290 
291   if (ret == 0) {
292     tocDirty(1);
293 //llanero: different views
294 //    updateLevel_ |= UPD_TRACK_DATA;
295   }
296 
297   return ret;
298 }
299 
removeTrackMarker(int trackNr,int indexNr)300 int TocEdit::removeTrackMarker(int trackNr, int indexNr)
301 {
302   if (!editable())
303     return 0;
304 
305   int ret = toc_->removeTrackMarker(trackNr, indexNr);
306 
307   if (ret == 0) {
308     tocDirty(1);
309 //llanero: different views
310 //    updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
311   }
312 
313   return ret;
314 }
315 
curScan()316 bool TocEdit::curScan()
317 {
318   // An end position of -1 means recompute the toc length and scan to
319   // the last sample position.
320 
321   if (cur_->end == -1) {
322     // (Denis Leroy) The reason for this code is somewhat
323     // complex. When importing a CUE file with MP3s, the length of the
324     // last track is not known until the MP3 is actually converted to
325     // a WAV (because, unlike TOC files, CUE files don't specify
326     // explicitely the length of each track). Unlike WAV, you can't
327     // guess the length of the track based on the mp3 file size
328     // without scanning the whole thing, which we don't want to do
329     // twice obviously. So the semantic of the "scan" job is changed
330     // to integrate a length recalculation when the end is not
331     // specified. It would be cleaner to create a specific job task to
332     // do this.
333     toc_->recomputeLength();
334     cur_->end = toc_->length().samples() - 1;
335     updateLevel_ |= UPD_SAMPLES;
336   }
337   int ret = sampleManager_->scanToc(cur_->pos, cur_->end);
338 
339   if (ret == 0)
340     return true;
341 
342   if (ret == 2) {
343     signalError("Unable to open or read from input audio files");
344     return false;
345   }
346 
347   return false;
348 }
349 
curAppendTrack()350 bool TocEdit::curAppendTrack()
351 {
352   TrackData* data;
353   int ret = curCreateAudioData(&data);
354   if (ret != 0)
355     return false;
356 
357   TrackDataList list;
358   long start, end;
359   list.append(data);
360   toc_->appendTrack(&list, &start, &end);
361   tocDirty(1);
362   sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1);
363   return true;
364 }
365 
curAppendFile()366 bool TocEdit::curAppendFile()
367 {
368   TrackData* data;
369   int ret = curCreateAudioData(&data);
370   if (ret != 0)
371     return false;
372 
373   TrackDataList list;
374   long start, end;
375   list.append(data);
376   if (toc_->appendTrackData(&list, &start, &end) != 0) {
377     delete data;
378     return false;
379   }
380   tocDirty(1);
381   sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1);
382   return true;
383 }
384 
curInsertFile()385 bool TocEdit::curInsertFile()
386 {
387   TrackData* data;
388   int ret = curCreateAudioData(&data);
389   if (ret != 0)
390     return false;
391 
392   TrackDataList list;
393   list.append(data);
394   if (toc_->insertTrackData(cur_->pos, &list) != 0) {
395     signalError(_("Cannot insert file into a data track"));
396     delete data;
397     return false;
398   }
399   cur_->len = list.length();
400   sampleManager_->insertSamples(cur_->pos, cur_->len, NULL);
401   sampleManager_->scanToc(cur_->pos, cur_->pos + cur_->len);
402   tocDirty(1);
403   return true;
404 }
405 
406 // Creates an audio data object for given filename. Errors are send to
407 // status line.
408 // data: filled with newly allocated TrackData object on success
409 // return: 0: OK
410 //         1: cannot open file
411 //         2: file has wrong format
curCreateAudioData(TrackData ** data)412 int TocEdit::curCreateAudioData(TrackData **data)
413 {
414   unsigned long len;
415   std::string msg;
416 
417   switch (TrackData::checkAudioFile(cur_->cfile.c_str(), &len)) {
418   case 1:
419     msg = _("Could not open file \"");
420     msg += cur_->cfile;
421     msg += "\"";
422     signalError(msg.c_str());
423     return 1; // Cannot open file
424 
425   case 2:
426     msg = _("Could not open file \"");
427     msg += cur_->cfile;
428     msg += "\" : wrong file format";
429     signalError(msg.c_str());
430     return 2; // File format error
431   }
432 
433   *data = new TrackData(cur_->file.c_str(), 0, len);
434   (*data)->effectiveFilename(cur_->cfile.c_str());
435 
436   return 0;
437 }
438 
curSignalConversionError(FormatSupport::Status err)439 void TocEdit::curSignalConversionError(FormatSupport::Status err)
440 {
441   std::string msg = _("Unable to decode audio file \"");
442   msg += cur_->file;
443   msg += "\" : ";
444   switch (err) {
445   case FormatSupport::FS_DISK_FULL:
446     msg += _("disk is full");
447     break;
448   case FormatSupport::FS_OUTPUT_PROBLEM:
449     msg += _("error creating output file");
450     break;
451   default:
452     msg += _("read error or wrong file format");
453   }
454   signalError(msg.c_str());
455 }
456 
queueConversion(const char * filename)457 void TocEdit::queueConversion(const char* filename)
458 {
459   QueueJob* job = new QueueJob("convert");
460   job->file = filename;
461   queue_.push_back(job);
462 
463   if (!threadActive_)
464     activateQueue();
465 }
466 
queueAppendTrack(const char * filename)467 void TocEdit::queueAppendTrack(const char* filename)
468 {
469   QueueJob* job = new QueueJob("aptrack");
470   job->op = "aptrack";
471   job->file = filename;
472   queue_.push_back(job);
473 
474   if (!threadActive_)
475     activateQueue();
476 }
477 
queueAppendFile(const char * filename)478 void TocEdit::queueAppendFile(const char* filename)
479 {
480   QueueJob* job = new QueueJob("apfile");
481   job->file = filename;
482   queue_.push_back(job);
483 
484   if (!threadActive_)
485     activateQueue();
486 }
487 
queueInsertFile(const char * filename,unsigned long pos)488 void TocEdit::queueInsertFile(const char* filename, unsigned long pos)
489 {
490   QueueJob* job = new QueueJob("infile");
491   job->file = filename;
492   job->pos = pos;
493   queue_.push_back(job);
494 
495   if (!threadActive_)
496     activateQueue();
497 }
498 
queueScan(long start,long end)499 void TocEdit::queueScan(long start, long end)
500 {
501   QueueJob* job = new QueueJob("scan");
502   job->pos = start;
503   job->end = end;
504   queue_.push_back(job);
505 
506   if (!threadActive_)
507     activateQueue();
508 }
509 
activateQueue()510 void TocEdit::activateQueue()
511 {
512   if (!threadActive_) {
513     threadActive_ = true;
514     blockEdit();
515     signalCancelEnable(true);
516     Glib::signal_idle().connect(sigc::mem_fun(*this,
517                                               &TocEdit::queueThread));
518     guiUpdate();
519   }
520 }
521 
queueAbort()522 void TocEdit::queueAbort()
523 {
524   if (threadActive_) {
525     queue_.clear();
526     curState_ = TE_IDLE;
527     if (curConv_) {
528       curConv_->convertAbort();
529       delete curConv_;
530       curConv_ = NULL;
531       signalStatusMessage("");
532     }
533   }
534 }
535 
isQueueActive()536 bool TocEdit::isQueueActive()
537 {
538   return threadActive_;
539 }
540 
541 // The queueThread is run by the Gtk idle thread when asynchronous
542 // work has to be done, such as decoding an MP3 file or reading
543 // samples from a WAV file.
544 //
545 // Asynchronous work (i.e. CPU-intensive work that has do be done in
546 // the background without blocking the GUI) can be scheduled by adding
547 // a new QueueJob object in the queue_ (see queueXXX methods above).
548 
queueThread()549 bool TocEdit::queueThread()
550 {
551   static int pulse = 0;
552 
553   // If we're idle, pop next queue entry.
554   if (curState_ == TE_IDLE) {
555 
556     // Queue empty ? Stop queue thread.
557     if (queue_.empty()) {
558       threadActive_ = false;
559       unblockEdit();
560       signalProgressFraction(0.0);
561       signalCancelEnable(false);
562       guiUpdate();
563       return false; // false means disconnect idle thread
564     }
565 
566     if (cur_)
567       delete cur_;
568     cur_ = queue_.front();
569     queue_.pop_front();
570 
571     if (cur_->op == "scan") {
572       if (curScan()) {
573         curState_ = TE_READING;
574         signalStatusMessage("Scanning audio data");
575       } else {
576         curState_ = TE_IDLE;
577         return true;
578       }
579     } else {
580 
581       if (curConv_)
582         delete curConv_;
583 
584       FormatSupport::Status err;
585       curConv_ = formatConverter.newConverterStart(cur_->file.c_str(),
586                                                    cur_->cfile, &err);
587       if (curConv_) {
588         std::string msg = "Decoding audio file ";
589         msg += cur_->file;
590         curState_ = TE_CONVERTING;
591         signalStatusMessage(msg.c_str());
592 
593       } else {
594 
595         if (err != FormatSupport::FS_SUCCESS) {
596           curSignalConversionError(err);
597           curState_ = TE_IDLE;
598           return true;
599         }
600         // File is already converted, or can't be converted (it's a WAV
601         // or RAW file already).
602         if (cur_->cfile.empty())
603           cur_->cfile = cur_->file;
604         curState_ = TE_CONVERTED;
605       }
606     }
607   }
608 
609   // ------------------ TE_CONVERTING state: do file format conversion
610 
611   if (curState_ == TE_CONVERTING) {
612     // Perform incremental file conversion.
613     FormatSupport::Status status = curConv_->convertContinue();
614     if (pulse++ > 5) {
615       signalProgressPulse();
616       pulse = 0;
617     }
618 
619     // Still in progress, likely exit here.
620     if (status == FormatSupport::FS_IN_PROGRESS)
621       return true;
622 
623     // Conversion done.
624     delete curConv_;
625     curConv_ = NULL;
626 
627     if (status == FormatSupport::FS_SUCCESS)
628       curState_ = TE_CONVERTED;
629     else {
630       curSignalConversionError(status);
631       // Conversion failed, move on with next queue entry.
632       curState_ = TE_IDLE;
633     }
634 
635     return true;
636   }
637 
638   // ------------------- TE_CONVERTED state: conversion done, prepare reading
639 
640   if (curState_ == TE_CONVERTED) {
641 
642     // Sanity check: the cfile (converted filename) will be read as
643     // either a WAV file or a file containing raw samples. If the
644     // extension is still that of an encoded audio file, return an
645     // error. Otherwise it'll be read as a RAW samples file which is
646     // not was users expect.
647 
648     TrackData::FileType ctype = TrackData::audioFileType(cur_->cfile.c_str());
649     if (ctype != TrackData::RAW && ctype != TrackData::WAVE) {
650       std::string msg = _("Cannot decode file");
651       msg += " \"";
652       msg += cur_->cfile;
653       msg += "\" : ";
654       msg += _("unsupported audio format");
655       signalError(msg.c_str());
656       curState_ = TE_IDLE;
657       return true;
658     }
659 
660     // If all we wanted to do was format conversion, we're done.
661     if (cur_->op == "convert") {
662       toc_->markFileConversion(cur_->file.c_str(), cur_->cfile.c_str());
663       curState_ = TE_IDLE;
664       return true;
665     }
666 
667     curState_ = TE_READING;
668     signalProgressFraction(0.0);
669 
670     if (cur_->op == "aptrack") {
671       if (!curAppendTrack()) {
672         curState_ = TE_IDLE;
673         return true;
674       }
675     } else if (cur_->op == "apfile") {
676       if (!curAppendFile()) {
677         curState_ = TE_IDLE;
678         return true;
679       }
680 
681     } else if (cur_->op == "infile") {
682       if (!curInsertFile()) {
683         curState_ = TE_IDLE;
684         return true;
685       }
686     }
687 
688     std::string msg = "Scanning audio file ";
689     msg += cur_->file;
690     signalStatusMessage(msg.c_str());
691   }
692 
693   // ------------------- TE_READING state : read/scan WAV samples
694 
695   if (curState_ == TE_READING) {
696 
697     int result = sampleManager_->readSamples();
698 
699     if (result != 0) {
700 
701       if (result < 0)
702         signalError(_("An error occured while reading audio data"));
703 
704       else {
705         // Post operating code here.
706         if (cur_->op == "aptrack") {
707           updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
708           signalFullView();
709           std::string msg = "Appended track ";
710           msg += cur_->file;
711           signalStatusMessage(msg.c_str());
712           guiUpdate();
713         } else if (cur_->op == "apfile") {
714           updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
715           signalFullView();
716           std::string msg = "Appended file ";
717           msg += cur_->file;
718           signalStatusMessage(msg.c_str());
719           guiUpdate();
720         } else if (cur_->op == "infile") {
721           updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
722           signalFullView();
723           std::string msg = "Inserted file ";
724           msg += cur_->file;
725           signalStatusMessage(msg.c_str());
726           signalSampleSelection(cur_->pos, cur_->pos + cur_->len - 1);
727           guiUpdate();
728         } else if (cur_->op == "scan") {
729           std::stringstream ss;
730           ss << "Scanned ";
731           ss << (cur_->end - cur_->pos + 1);
732           ss << " samples of data";
733           std::string msg = ss.str();
734           signalStatusMessage(msg.c_str());
735           updateLevel_ |= UPD_SAMPLES;
736         }
737       }
738       curState_ = TE_IDLE;
739     }
740     return true;
741   }
742   return true;
743 }
744 
appendSilence(unsigned long length)745 int TocEdit::appendSilence(unsigned long length)
746 {
747   if (!editable())
748     return 0;
749 
750   if (length > 0) {
751     long start, end;
752 
753     TrackData *data = new TrackData(length);
754     TrackDataList list;
755     list.append(data);
756 
757     if (toc_->appendTrackData(&list, &start, &end) == 0) {
758 
759       sampleManager_->scanToc(Msf(start).samples(), Msf(end).samples() - 1,
760                               true);
761 
762       tocDirty(1);
763       updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA;
764     }
765   }
766 
767   return 0;
768 }
769 
770 // Return: 0: OK
771 //         1: No modify allowed
772 //         2: error?
insertSilence(unsigned long length,unsigned long pos)773 int TocEdit::insertSilence(unsigned long length, unsigned long pos)
774 {
775   if (!editable())
776     return 1;
777 
778   if (length > 0) {
779     TrackData *data = new TrackData(length);
780     TrackDataList list;
781 
782     list.append(data);
783 
784     if (toc_->insertTrackData(pos, &list) == 0) {
785       sampleManager_->insertSamples(pos, length, NULL);
786       sampleManager_->scanToc(pos, pos + length, true);
787 
788       tocDirty(1);
789       updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
790       return 0;
791     }
792   }
793 
794   return 2;
795 }
796 
797 
setTrackCopyFlag(int trackNr,int flag)798 void TocEdit::setTrackCopyFlag(int trackNr, int flag)
799 {
800   if (!editable())
801     return;
802 
803   Track *t = toc_->getTrack(trackNr);
804 
805   if (t != NULL) {
806     t->copyPermitted(flag);
807     tocDirty(1);
808     updateLevel_ |= UPD_TRACK_DATA;
809   }
810 }
811 
setTrackPreEmphasisFlag(int trackNr,int flag)812 void TocEdit::setTrackPreEmphasisFlag(int trackNr, int flag)
813 {
814   if (!editable())
815     return;
816 
817   Track *t = toc_->getTrack(trackNr);
818 
819   if (t != NULL) {
820     t->preEmphasis(flag);
821     tocDirty(1);
822     updateLevel_ |= UPD_TRACK_DATA;
823   }
824 }
825 
setTrackAudioType(int trackNr,int flag)826 void TocEdit::setTrackAudioType(int trackNr, int flag)
827 {
828   if (!editable())
829     return;
830 
831   Track *t = toc_->getTrack(trackNr);
832 
833   if (t != NULL) {
834     t->audioType(flag);
835     tocDirty(1);
836     updateLevel_ |= UPD_TRACK_DATA;
837   }
838 
839 }
840 
setTrackIsrcCode(int trackNr,const char * s)841 void TocEdit::setTrackIsrcCode(int trackNr, const char *s)
842 {
843   if (!editable())
844     return;
845 
846   Track *t = toc_->getTrack(trackNr);
847 
848   if (t != NULL) {
849     if (t->isrc(s) == 0) {
850       tocDirty(1);
851       updateLevel_ |= UPD_TRACK_DATA;
852     }
853   }
854 }
855 
setCdTextItem(int trackNr,CdTextItem::PackType type,int blockNr,const char * s)856 void TocEdit::setCdTextItem(int trackNr, CdTextItem::PackType type,
857 			    int blockNr, const char *s)
858 {
859   if (!editable())
860     return;
861 
862   if (s != NULL) {
863     CdTextItem *item = new CdTextItem(type, blockNr, s);
864 
865     toc_->addCdTextItem(trackNr, item);
866   }
867   else {
868     toc_->removeCdTextItem(trackNr, type, blockNr);
869   }
870 
871   tocDirty(1);
872 
873   updateLevel_ |= (trackNr == 0) ? UPD_TOC_DATA : UPD_TRACK_DATA;
874 
875 }
876 
setCdTextGenreItem(int blockNr,int code1,int code2,const char * description)877 void TocEdit::setCdTextGenreItem(int blockNr, int code1, int code2,
878 				 const char *description)
879 {
880   if (code1 > 255 || code2 > 255)
881     return;
882 
883   if (!editable())
884     return;
885 
886   if (code1 < 0 || code2 < 0) {
887     toc_->removeCdTextItem(0, CdTextItem::CDTEXT_GENRE, blockNr);
888   }
889   else {
890     CdTextItem *item = new CdTextItem(blockNr, (unsigned char)code1,
891 				      (unsigned char)code2, description);
892 
893     toc_->addCdTextItem(0, item);
894   }
895 
896   tocDirty(1);
897 
898   updateLevel_ |= UPD_TOC_DATA;
899 }
900 
901 
setCdTextLanguage(int blockNr,int langCode)902 void TocEdit::setCdTextLanguage(int blockNr, int langCode)
903 {
904   if (!editable())
905     return;
906 
907   toc_->cdTextLanguage(blockNr, langCode);
908   tocDirty(1);
909 
910   updateLevel_ |= UPD_TOC_DATA;
911 
912 }
913 
setCatalogNumber(const char * s)914 void TocEdit::setCatalogNumber(const char *s)
915 {
916   if (!editable())
917     return;
918 
919   if (toc_->catalog(s) == 0) {
920     tocDirty(1);
921     updateLevel_ |= UPD_TOC_DATA;
922   }
923 
924 }
925 
setTocType(Toc::TocType type)926 void TocEdit::setTocType(Toc::TocType type)
927 {
928   if (!editable())
929     return;
930 
931   toc_->tocType(type);
932   tocDirty(1);
933   updateLevel_ |= UPD_TOC_DATA;
934 }
935 
936 
937 // Removes selected track data
938 // Return: 0: OK
939 //         1: no selection
940 //         2: selection crosses track boundaries
941 //         3: cannot modify data track
removeTrackData(TocEditView * view)942 int TocEdit::removeTrackData(TocEditView *view)
943 {
944   TrackDataList *list;
945   unsigned long selMin, selMax;
946 
947   if (!editable())
948     return 0;
949 
950   if (!view->sampleSelection(&selMin, &selMax))
951     return 1;
952 
953   switch (toc_->removeTrackData(selMin, selMax, &list)) {
954   case 0:
955     if (list != NULL) {
956       if (list->length() > 0) {
957 	delete trackDataScrap_;
958 	trackDataScrap_ = new TrackDataScrap(list);
959       }
960       else {
961 	delete list;
962       }
963 
964       sampleManager_->removeSamples(selMin, selMax, trackDataScrap_);
965 
966       view->sampleSelectionClear();
967       view->sampleMarker(selMin);
968 
969       tocDirty(1);
970     }
971     break;
972 
973   case 1:
974     return 2;
975     break;
976 
977   case 2:
978     return 3;
979     break;
980   }
981   return 0;
982 }
983 
984 // Inserts track data from scrap
985 // Return: 0: OK
986 //         1: no scrap data to paste
insertTrackData(TocEditView * view)987 int TocEdit::insertTrackData(TocEditView *view)
988 
989 {
990   if (!editable())
991     return 0;
992 
993   if (trackDataScrap_ == NULL)
994     return 1;
995 
996   unsigned long len = trackDataScrap_->trackDataList()->length();
997   unsigned long marker;
998 
999   if (view->sampleMarker(&marker) && marker < toc_->length().samples()) {
1000     if (toc_->insertTrackData(marker, trackDataScrap_->trackDataList())
1001 	== 0) {
1002 
1003       sampleManager_->insertSamples(marker, len, trackDataScrap_);
1004       sampleManager_->scanToc(marker, marker, true);
1005       sampleManager_->scanToc(marker + len - 1, marker + len - 1, true);
1006 
1007       view->sampleSelect(marker, marker + len - 1);
1008 
1009       tocDirty(1);
1010 //llanero: different views
1011 //      updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
1012     }
1013   }
1014   else {
1015     long start, end;
1016 
1017     if (toc_->appendTrackData(trackDataScrap_->trackDataList(), &start, &end)
1018 	== 0) {
1019 
1020       sampleManager_->insertSamples(Msf(start).samples(),
1021 				    Msf(end - start).samples(),
1022                                     trackDataScrap_);
1023 
1024       sampleManager_->scanToc(Msf(start).samples(), Msf(start).samples(),
1025                               true);
1026       if (end > 0)
1027 	sampleManager_->scanToc(Msf(start).samples() + len,
1028                                 Msf(end).samples() - 1, true);
1029 
1030       view->sampleSelect(Msf(start).samples(), Msf(end).samples() - 1);
1031 
1032       tocDirty(1);
1033       updateLevel_ |= UPD_TOC_DATA | UPD_TRACK_DATA | UPD_SAMPLE_SEL;
1034     }
1035   }
1036   return 0;
1037 }
1038