MythTV  0.26-pre
cdrip.cpp
Go to the documentation of this file.
00001 // ANSI C includes
00002 #include <cstdio>
00003 #include <cstring>
00004 
00005 // Unix C includes
00006 #include <sys/types.h>
00007 #include <fcntl.h>
00008 
00009 #include "config.h"
00010 #ifdef HAVE_CDIO
00011 # include <cdio/cdda.h>
00012 # include <cdio/paranoia.h>
00013 #endif //def HAVE_CDIO
00014 
00015 // C++ includes
00016 #include <iostream>
00017 #include <memory>
00018 using namespace std;
00019 
00020 // Qt includes
00021 #include <QApplication>
00022 #include <QDir>
00023 #include <QRegExp>
00024 #include <QKeyEvent>
00025 #include <QEvent>
00026 #include <QFile>
00027 
00028 // MythTV plugin includes
00029 #include <mythcontext.h>
00030 #include <mythdb.h>
00031 #include <lcddevice.h>
00032 #include <mythmediamonitor.h>
00033 
00034 // MythUI
00035 #include <mythdialogbox.h>
00036 #include <mythuitext.h>
00037 #include <mythuicheckbox.h>
00038 #include <mythuitextedit.h>
00039 #include <mythuibutton.h>
00040 #include <mythuiprogressbar.h>
00041 #include <mythuibuttonlist.h>
00042 #include <mythsystem.h>
00043 
00044 // MythUI headers
00045 #include <mythtv/libmythui/mythscreenstack.h>
00046 #include <mythtv/libmythui/mythprogressdialog.h>
00047 
00048 // MythMusic includes
00049 #include "cdrip.h"
00050 #ifdef HAVE_CDIO
00051 #include "cddecoder.h"
00052 #endif
00053 #include "encoder.h"
00054 #include "vorbisencoder.h"
00055 #include "lameencoder.h"
00056 #include "flacencoder.h"
00057 #include "genres.h"
00058 #include "editmetadata.h"
00059 #include "mythlogging.h"
00060 #include "musicutils.h"
00061 
00062 #ifdef HAVE_CDIO
00063 // libparanoia compatibility
00064 #ifndef cdrom_paranoia
00065 #define cdrom_paranoia cdrom_paranoia_t
00066 #endif
00067 
00068 #ifndef CD_FRAMESIZE_RAW
00069 # define CD_FRAMESIZE_RAW CDIO_CD_FRAMESIZE_RAW
00070 #endif
00071 #endif
00072 
00073 QEvent::Type RipStatusEvent::kTrackTextEvent =
00074     (QEvent::Type) QEvent::registerEventType();
00075 QEvent::Type RipStatusEvent::kOverallTextEvent =
00076     (QEvent::Type) QEvent::registerEventType();
00077 QEvent::Type RipStatusEvent::kStatusTextEvent =
00078     (QEvent::Type) QEvent::registerEventType();
00079 QEvent::Type RipStatusEvent::kTrackProgressEvent =
00080     (QEvent::Type) QEvent::registerEventType();
00081 QEvent::Type RipStatusEvent::kTrackPercentEvent =
00082     (QEvent::Type) QEvent::registerEventType();
00083 QEvent::Type RipStatusEvent::kTrackStartEvent =
00084     (QEvent::Type) QEvent::registerEventType();
00085 QEvent::Type RipStatusEvent::kOverallProgressEvent =
00086     (QEvent::Type) QEvent::registerEventType();
00087 QEvent::Type RipStatusEvent::kOverallPercentEvent =
00088     (QEvent::Type) QEvent::registerEventType();
00089 QEvent::Type RipStatusEvent::kOverallStartEvent =
00090     (QEvent::Type) QEvent::registerEventType();
00091 QEvent::Type RipStatusEvent::kFinishedEvent =
00092     (QEvent::Type) QEvent::registerEventType();
00093 QEvent::Type RipStatusEvent::kEncoderErrorEvent =
00094     (QEvent::Type) QEvent::registerEventType();
00095 
00096 CDScannerThread::CDScannerThread(Ripper *ripper) :
00097     MThread("CDScanner"), m_parent(ripper)
00098 {
00099 }
00100 
00101 void CDScannerThread::run()
00102 {
00103     RunProlog();
00104     m_parent->scanCD();
00105     RunEpilog();
00106 }
00107 
00109 
00110 CDEjectorThread::CDEjectorThread(Ripper *ripper) :
00111     MThread("CDEjector"), m_parent(ripper)
00112 {
00113 }
00114 
00115 void CDEjectorThread::run()
00116 {
00117     RunProlog();
00118     m_parent->ejectCD();
00119     RunEpilog();
00120 }
00121 
00123 
00124 static long int getSectorCount (QString &cddevice, int tracknum)
00125 {
00126 #if defined HAVE_CDIO
00127     QByteArray devname = cddevice.toAscii();
00128     cdrom_drive *device = cdda_identify(devname.constData(), 0, NULL);
00129 
00130     if (!device)
00131     {
00132         LOG(VB_GENERAL, LOG_ERR,
00133             QString("Error: %1('%2',track=%3) failed at cdda_identify()").
00134             arg(__func__).arg(cddevice).arg(tracknum));
00135         return -1;
00136     }
00137 
00138     if (cdda_open(device))
00139     {
00140         LOG(VB_GENERAL, LOG_ERR,
00141             QString("Error: %1('%2',track=%3) failed at cdda_open() - cdda not supported").
00142             arg(__func__).arg(cddevice).arg(tracknum));
00143         cdda_close(device);
00144         return -1;
00145     }
00146 
00147     // we only care about audio tracks
00148     if (cdda_track_audiop (device, tracknum))
00149     {
00150         cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
00151         long int start = cdda_track_firstsector(device, tracknum);
00152         long int end   = cdda_track_lastsector( device, tracknum);
00153         cdda_close(device);
00154         return end - start + 1;
00155     }
00156     LOG(VB_GENERAL, LOG_ERR,
00157         QString("Error: cdrip - cdda_track_audiop(%1) returned 0").arg(cddevice));
00158 
00159     cdda_close(device);
00160 #else
00161     (void)cddevice; (void)tracknum;
00162 #endif
00163     return 0;
00164 }
00165 
00166 #ifdef HAVE_CDIO
00167 static void paranoia_cb(long, paranoia_cb_mode_t)
00168 {
00169 }
00170 #endif
00171 
00172 CDRipperThread::CDRipperThread(RipStatus *parent,  QString device,
00173                                QVector<RipTrack*> *tracks, int quality) :
00174     MThread("CDRipper"),
00175     m_parent(parent),   m_quit(false),
00176     m_CDdevice(device), m_quality(quality),
00177     m_tracks(tracks), m_totalSectors(0),
00178     m_totalSectorsDone(0), m_lastTrackPct(0),
00179     m_lastOverallPct(0)
00180 {
00181 #ifdef WIN32 // libcdio needs the drive letter with no path
00182     if (m_CDdevice.endsWith('\\'))
00183         m_CDdevice.chop(1);
00184 #endif
00185 }
00186 
00187 CDRipperThread::~CDRipperThread(void)
00188 {
00189     cancel();
00190     wait();
00191 }
00192 
00193 void CDRipperThread::cancel(void)
00194 {
00195     m_quit = true;
00196 }
00197 
00198 bool CDRipperThread::isCancelled(void)
00199 {
00200     return m_quit;
00201 }
00202 
00203 void CDRipperThread::run(void)
00204 {
00205     RunProlog();
00206     if (!m_tracks->size() > 0)
00207     {
00208         RunEpilog();
00209         return;
00210     }
00211 
00212     Metadata *track = m_tracks->at(0)->metadata;
00213     QString tots;
00214 
00215     if (track->Compilation())
00216     {
00217         tots = track->CompilationArtist() + " ~ " + track->Album();
00218     }
00219     else
00220     {
00221         tots = track->Artist() + " ~ " + track->Album();
00222     }
00223 
00224     QApplication::postEvent(
00225         m_parent,
00226         new RipStatusEvent(RipStatusEvent::kOverallTextEvent, tots));
00227     QApplication::postEvent(
00228         m_parent,
00229         new RipStatusEvent(RipStatusEvent::kOverallProgressEvent, 0));
00230     QApplication::postEvent(
00231         m_parent,
00232         new RipStatusEvent(RipStatusEvent::kTrackProgressEvent, 0));
00233 
00234     QString textstatus;
00235     QString encodertype = gCoreContext->GetSetting("EncoderType");
00236     bool mp3usevbr = gCoreContext->GetNumSetting("Mp3UseVBR", 0);
00237 
00238     m_totalSectors = 0;
00239     m_totalSectorsDone = 0;
00240     for (int trackno = 0; trackno < m_tracks->size(); trackno++)
00241     {
00242         m_totalSectors += getSectorCount(m_CDdevice, trackno + 1);
00243     }
00244 
00245     QApplication::postEvent(m_parent,
00246         new RipStatusEvent(RipStatusEvent::kOverallStartEvent, m_totalSectors));
00247 
00248     if (LCD *lcd = LCD::Get())
00249     {
00250         QString lcd_tots = QObject::tr("Importing ") + tots;
00251         QList<LCDTextItem> textItems;
00252         textItems.append(LCDTextItem(1, ALIGN_CENTERED,
00253                                          lcd_tots, "Generic", false));
00254         lcd->switchToGeneric(textItems);
00255     }
00256 
00257     Metadata *titleTrack = NULL;
00258     QString outfile;
00259 
00260     std::auto_ptr<Encoder> encoder;
00261 
00262     for (int trackno = 0; trackno < m_tracks->size(); trackno++)
00263     {
00264         if (isCancelled())
00265             break;
00266 
00267         QApplication::postEvent(
00268             m_parent,
00269             new RipStatusEvent(RipStatusEvent::kStatusTextEvent,
00270                                QString("Track %1 of %2")
00271                                .arg(trackno + 1).arg(m_tracks->size())));
00272 
00273         QApplication::postEvent(
00274             m_parent,
00275             new RipStatusEvent(RipStatusEvent::kTrackProgressEvent, 0));
00276 
00277         track = m_tracks->at(trackno)->metadata;
00278 
00279         if (track)
00280         {
00281             textstatus = track->Title();
00282             QApplication::postEvent(
00283                 m_parent,
00284                 new RipStatusEvent(
00285                     RipStatusEvent::kTrackTextEvent, textstatus));
00286             QApplication::postEvent(
00287                 m_parent,
00288                 new RipStatusEvent(RipStatusEvent::kTrackProgressEvent, 0));
00289             QApplication::postEvent(
00290                 m_parent,
00291                 new RipStatusEvent(RipStatusEvent::kTrackPercentEvent, 0));
00292 
00293             // do we need to start a new file?
00294             if (m_tracks->at(trackno)->active)
00295             {
00296                 titleTrack = track;
00297                 titleTrack->setLength(m_tracks->at(trackno)->length);
00298 
00299                 outfile = filenameFromMetadata(track);
00300 
00301                 if (m_quality < 3)
00302                 {
00303                     if (encodertype == "mp3")
00304                     {
00305                         outfile += ".mp3";
00306                         encoder.reset(new LameEncoder(gMusicData->musicDir + outfile, m_quality,
00307                                                       titleTrack, mp3usevbr));
00308                     }
00309                     else // ogg
00310                     {
00311                         outfile += ".ogg";
00312                         encoder.reset(new VorbisEncoder(gMusicData->musicDir + outfile, m_quality,
00313                                                         titleTrack));
00314                     }
00315                 }
00316                 else
00317                 {
00318                     outfile += ".flac";
00319                     encoder.reset(new FlacEncoder(gMusicData->musicDir + outfile, m_quality,
00320                                                   titleTrack));
00321                 }
00322 
00323                 if (!encoder->isValid())
00324                 {
00325                     QApplication::postEvent(
00326                         m_parent,
00327                         new RipStatusEvent(
00328                             RipStatusEvent::kEncoderErrorEvent,
00329                             "Encoder failed to open file for writing"));
00330                     LOG(VB_GENERAL, LOG_ERR, "MythMusic: Encoder failed"
00331                                              " to open file for writing");
00332 
00333                     RunEpilog();
00334                     return;
00335                 }
00336             }
00337 
00338             if (!encoder.get())
00339             {
00340                 // This should never happen.
00341                 QApplication::postEvent(
00342                     m_parent,
00343                     new RipStatusEvent(RipStatusEvent::kEncoderErrorEvent,
00344                                        "Failed to create encoder"));
00345                 LOG(VB_GENERAL, LOG_ERR, "MythMusic: No encoder, failing");
00346                 RunEpilog();
00347                 return;
00348             }
00349             ripTrack(m_CDdevice, encoder.get(), trackno + 1);
00350 
00351             if (isCancelled())
00352             {
00353                 RunEpilog();
00354                 return;
00355             }
00356 
00357             // save the metadata to the DB
00358             if (m_tracks->at(trackno)->active)
00359             {
00360                 titleTrack->setFilename(outfile);
00361                 titleTrack->dumpToDatabase();
00362             }
00363         }
00364     }
00365 
00366     QString PostRipCDScript = gCoreContext->GetSetting("PostCDRipScript");
00367 
00368     if (!PostRipCDScript.isEmpty())
00369         myth_system(PostRipCDScript);
00370 
00371     QApplication::postEvent(
00372         m_parent, new RipStatusEvent(RipStatusEvent::kFinishedEvent, ""));
00373 
00374     RunEpilog();
00375 }
00376 
00377 int CDRipperThread::ripTrack(QString &cddevice, Encoder *encoder, int tracknum)
00378 {
00379 #if defined HAVE_CDIO
00380     QByteArray devname = cddevice.toAscii();
00381     cdrom_drive *device = cdda_identify(devname.constData(), 0, NULL);
00382 
00383     if (!device)
00384     {
00385         LOG(VB_GENERAL, LOG_ERR,
00386             QString("cdda_identify failed for device '%1', "
00387                     "CDRipperThread::ripTrack(tracknum = %2) exiting.")
00388                 .arg(cddevice).arg(tracknum));
00389         return -1;
00390     }
00391 
00392     if (cdda_open(device))
00393     {
00394         LOG(VB_MEDIA, LOG_INFO,
00395             QString("Error: %1('%2',track=%3) failed at cdda_open() - cdda not supported")
00396             .arg(__func__).arg(cddevice).arg(tracknum));
00397         cdda_close(device);
00398         return -1;
00399     }
00400 
00401     cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
00402     long int start = cdda_track_firstsector(device, tracknum);
00403     long int end = cdda_track_lastsector(device, tracknum);
00404     LOG(VB_MEDIA, LOG_INFO, QString("%1(%2,track=%3) start=%4 end=%5")
00405         .arg(__func__).arg(cddevice).arg(tracknum).arg(start).arg(end));
00406 
00407     cdrom_paranoia *paranoia = paranoia_init(device);
00408     if (gCoreContext->GetSetting("ParanoiaLevel") == "full")
00409         paranoia_modeset(paranoia, PARANOIA_MODE_FULL |
00410                 PARANOIA_MODE_NEVERSKIP);
00411     else
00412         paranoia_modeset(paranoia, PARANOIA_MODE_OVERLAP);
00413 
00414     paranoia_seek(paranoia, start, SEEK_SET);
00415 
00416     long int curpos = start;
00417     int16_t *buffer;
00418 
00419     QApplication::postEvent(
00420         m_parent,
00421         new RipStatusEvent(RipStatusEvent::kTrackStartEvent, end - start + 1));
00422     m_lastTrackPct = -1;
00423     m_lastOverallPct = -1;
00424 
00425     int every15 = 15;
00426     while (curpos < end)
00427     {
00428         buffer = paranoia_read(paranoia, paranoia_cb);
00429 
00430         if (encoder->addSamples(buffer, CD_FRAMESIZE_RAW))
00431             break;
00432 
00433         curpos++;
00434 
00435         every15--;
00436 
00437         if (every15 <= 0)
00438         {
00439             every15 = 15;
00440 
00441             // updating the UITypes can be slow - only update if we need to:
00442             int newOverallPct = (int) (100.0 /  (double) ((double) m_totalSectors /
00443                     (double) (m_totalSectorsDone + curpos - start)));
00444             if (newOverallPct != m_lastOverallPct)
00445             {
00446                 m_lastOverallPct = newOverallPct;
00447                 QApplication::postEvent(
00448                     m_parent,
00449                     new RipStatusEvent(RipStatusEvent::kOverallPercentEvent,
00450                                        newOverallPct));
00451                 QApplication::postEvent(
00452                     m_parent,
00453                     new RipStatusEvent(RipStatusEvent::kOverallProgressEvent,
00454                                        m_totalSectorsDone + curpos - start));
00455             }
00456 
00457             int newTrackPct = (int) (100.0 / (double) ((double) (end - start + 1) /
00458                     (double) (curpos - start)));
00459             if (newTrackPct != m_lastTrackPct)
00460             {
00461                 m_lastTrackPct = newTrackPct;
00462                 QApplication::postEvent(
00463                     m_parent,
00464                     new RipStatusEvent(RipStatusEvent::kTrackPercentEvent,
00465                                        newTrackPct));
00466                 QApplication::postEvent(
00467                     m_parent,
00468                     new RipStatusEvent(RipStatusEvent::kTrackProgressEvent,
00469                                        curpos - start));
00470             }
00471 
00472             if (LCD *lcd = LCD::Get())
00473             {
00474                 float fProgress = (float)(m_totalSectorsDone + (curpos - start))
00475                                              / m_totalSectors;
00476                 lcd->setGenericProgress(fProgress);
00477             }
00478         }
00479 
00480         if (isCancelled())
00481         {
00482             break;
00483         }
00484     }
00485 
00486     m_totalSectorsDone += end - start + 1;
00487 
00488     paranoia_free(paranoia);
00489     cdda_close(device);
00490 
00491     return (curpos - start + 1) * CD_FRAMESIZE_RAW;
00492 #else
00493     (void)cddevice; (void)encoder; (void)tracknum;
00494     return 0;
00495 #endif
00496 }
00497 
00499 
00500 Ripper::Ripper(MythScreenStack *parent, QString device) :
00501     MythScreenType(parent, "ripcd"),
00502     m_decoder(NULL),
00503 
00504     m_artistEdit(NULL),
00505     m_albumEdit(NULL),
00506     m_genreEdit(NULL),
00507     m_yearEdit(NULL),
00508 
00509     m_compilationCheck(NULL),
00510 
00511     m_trackList(NULL),
00512     m_qualityList(NULL),
00513 
00514     m_switchTitleArtist(NULL),
00515     m_scanButton(NULL),
00516     m_ripButton(NULL),
00517     m_searchArtistButton(NULL),
00518     m_searchAlbumButton(NULL),
00519     m_searchGenreButton(NULL),
00520 
00521     m_tracks(new QVector<RipTrack*>),
00522 
00523     m_somethingwasripped(false),
00524     m_mediaMonitorActive(false),
00525 
00526     m_CDdevice(device),
00527 
00528     m_ejectThread(NULL), m_scanThread(NULL)
00529 {
00530 #ifndef _WIN32
00531     // if the MediaMonitor is running stop it
00532     m_mediaMonitorActive = false;
00533     MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
00534     if (mon && mon->IsActive())
00535     {
00536         m_mediaMonitorActive = true;
00537         mon->StopMonitoring();
00538     }
00539 #endif
00540 }
00541 
00542 Ripper::~Ripper(void)
00543 {
00544     if (m_decoder)
00545         delete m_decoder;
00546 
00547 #ifndef _WIN32
00548     // if the MediaMonitor was active when we started then restart it
00549     if (m_mediaMonitorActive)
00550     {
00551         MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
00552         if (mon)
00553             mon->StartMonitoring();
00554     }
00555 #endif
00556 
00557     if (m_somethingwasripped)
00558         emit ripFinished();
00559 }
00560 
00561 bool Ripper::Create(void)
00562 {
00563     if (!LoadWindowFromXML("music-ui.xml", "cdripper", this))
00564         return false;
00565 
00566     m_qualityList = dynamic_cast<MythUIButtonList *>(GetChild("quality"));
00567     m_artistEdit = dynamic_cast<MythUITextEdit *>(GetChild("artist"));
00568     m_searchArtistButton = dynamic_cast<MythUIButton *>(GetChild("searchartist"));
00569     m_albumEdit = dynamic_cast<MythUITextEdit *>(GetChild("album"));
00570     m_searchAlbumButton = dynamic_cast<MythUIButton *>(GetChild("searchalbum"));
00571     m_genreEdit = dynamic_cast<MythUITextEdit *>(GetChild("genre"));
00572     m_yearEdit = dynamic_cast<MythUITextEdit *>(GetChild("year"));
00573     m_searchGenreButton = dynamic_cast<MythUIButton *>(GetChild("searchgenre"));
00574     m_compilationCheck = dynamic_cast<MythUICheckBox *>(GetChild("compilation"));
00575     m_switchTitleArtist = dynamic_cast<MythUIButton *>(GetChild("switch"));
00576     m_scanButton = dynamic_cast<MythUIButton *>(GetChild("scan"));
00577     m_ripButton = dynamic_cast<MythUIButton *>(GetChild("rip"));
00578     m_trackList = dynamic_cast<MythUIButtonList *>(GetChild("tracks"));
00579 
00580     BuildFocusList();
00581 
00582     if (!m_artistEdit || !m_scanButton || !m_ripButton || !m_switchTitleArtist
00583         || !m_trackList || !m_compilationCheck || !m_searchGenreButton
00584         || !m_yearEdit || !m_genreEdit || !m_searchArtistButton
00585         || !m_albumEdit || !m_searchAlbumButton || !m_qualityList)
00586     {
00587         LOG(VB_GENERAL, LOG_ERR,
00588             "Missing theme elements for screen 'cdripper'");
00589         return false;
00590     }
00591 
00592     connect(m_trackList, SIGNAL(itemClicked(MythUIButtonListItem *)),
00593             SLOT(toggleTrackActive(MythUIButtonListItem *)));
00594     connect(m_ripButton, SIGNAL(Clicked()), SLOT(startRipper()));
00595     connect(m_scanButton, SIGNAL(Clicked()), SLOT(startScanCD()));
00596     connect(m_switchTitleArtist, SIGNAL(Clicked()),
00597             SLOT(switchTitlesAndArtists()));
00598     connect(m_compilationCheck, SIGNAL(toggled(bool)),
00599             SLOT(compilationChanged(bool)));
00600     connect(m_searchGenreButton, SIGNAL(Clicked()), SLOT(searchGenre()));
00601     connect(m_genreEdit, SIGNAL(valueChanged()), SLOT(genreChanged()));
00602     m_yearEdit->SetFilter((InputFilter)(FilterAlpha | FilterSymbols | FilterPunct));
00603     m_yearEdit->SetMaxLength(4);
00604     connect(m_yearEdit, SIGNAL(valueChanged()), SLOT(yearChanged()));
00605     connect(m_artistEdit, SIGNAL(valueChanged()), SLOT(artistChanged()));
00606     connect(m_searchArtistButton, SIGNAL(Clicked()), SLOT(searchArtist()));
00607     connect(m_albumEdit, SIGNAL(valueChanged()), SLOT(albumChanged()));
00608     connect(m_searchAlbumButton, SIGNAL(Clicked()), SLOT(searchAlbum()));
00609 
00610     // Populate Quality List
00611     new MythUIButtonListItem(m_qualityList, tr("Low"), qVariantFromValue(0));
00612     new MythUIButtonListItem(m_qualityList, tr("Medium"), qVariantFromValue(1));
00613     new MythUIButtonListItem(m_qualityList, tr("High"), qVariantFromValue(2));
00614     new MythUIButtonListItem(m_qualityList, tr("Perfect"), qVariantFromValue(3));
00615     m_qualityList->SetValueByData(qVariantFromValue(
00616                         gCoreContext->GetNumSetting("DefaultRipQuality", 1)));
00617 
00618     QTimer::singleShot(500, this, SLOT(startScanCD()));
00619 
00620     return true;
00621 }
00622 
00623 bool Ripper::keyPressEvent(QKeyEvent *event)
00624 {
00625     if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
00626         return true;
00627 
00628     bool handled = false;
00629     QStringList actions;
00630     handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
00631 
00632     for (int i = 0; i < actions.size() && !handled; i++)
00633     {
00634         QString action = actions[i];
00635         handled = true;
00636 
00637         if (action == "INFO")
00638         {
00639             showEditMetadataDialog(m_trackList->GetItemCurrent());
00640         }
00641         else
00642             handled = false;
00643     }
00644 
00645     if (!handled && MythScreenType::keyPressEvent(event))
00646         handled = true;
00647 
00648     return handled;
00649 }
00650 
00651 void Ripper::startScanCD(void)
00652 {
00653     if (m_scanThread)
00654         return;
00655 
00656     QString message = QObject::tr("Scanning CD. Please Wait ...");
00657     OpenBusyPopup(message);
00658 
00659     m_scanThread = new CDScannerThread(this);
00660     connect(m_scanThread->qthread(), SIGNAL(finished()), SLOT(ScanFinished()));
00661     m_scanThread->start();
00662 }
00663 
00664 void Ripper::ScanFinished()
00665 {
00666     delete m_scanThread;
00667     m_scanThread = NULL;
00668 
00669     m_tracks->clear();
00670 
00671     bool isCompilation = false;
00672     if (m_decoder)
00673     {
00674         QString label;
00675         Metadata *metadata;
00676 
00677         m_artistName.clear();
00678         m_albumName.clear();
00679         m_genreName.clear();
00680         m_year.clear();
00681 
00682         for (int trackno = 0; trackno < m_decoder->getNumTracks(); trackno++)
00683         {
00684             RipTrack *ripTrack = new RipTrack;
00685 
00686             metadata = m_decoder->getMetadata(trackno + 1);
00687             if (metadata)
00688             {
00689                 ripTrack->metadata = metadata;
00690                 ripTrack->length = metadata->Length();
00691 
00692                 if (metadata->Compilation())
00693                 {
00694                     isCompilation = true;
00695                     m_artistName = metadata->CompilationArtist();
00696                 }
00697                 else if (m_artistName.isEmpty())
00698                 {
00699                     m_artistName = metadata->Artist();
00700                 }
00701 
00702                 if (m_albumName.isEmpty())
00703                     m_albumName = metadata->Album();
00704 
00705                 if (m_genreName.isEmpty() && !metadata->Genre().isEmpty())
00706                     m_genreName = metadata->Genre();
00707 
00708                 if (m_year.isEmpty() && metadata->Year() > 0)
00709                     m_year = QString::number(metadata->Year());
00710 
00711                 QString title = metadata->Title();
00712                 ripTrack->isNew = isNewTune(m_artistName, m_albumName, title);
00713 
00714                 ripTrack->active = ripTrack->isNew;
00715 
00716                 m_tracks->push_back(ripTrack);
00717 
00718             }
00719             else
00720                 delete ripTrack;
00721         }
00722 
00723         m_artistEdit->SetText(m_artistName);
00724         m_albumEdit->SetText(m_albumName);
00725         m_genreEdit->SetText(m_genreName);
00726         m_yearEdit->SetText(m_year);
00727         m_compilationCheck->SetCheckState(isCompilation);
00728 
00729         if (!isCompilation)
00730             m_switchTitleArtist->SetVisible(false);
00731         else
00732             m_switchTitleArtist->SetVisible(true);
00733     }
00734 
00735     BuildFocusList();
00736     updateTrackList();
00737 
00738     CloseBusyPopup();
00739 }
00740 
00741 void Ripper::scanCD(void)
00742 {
00743 #ifdef HAVE_CDIO
00744     {
00745     LOG(VB_MEDIA, LOG_INFO, QString("Ripper::%1 CD='%2'").
00746         arg(__func__).arg(m_CDdevice));
00747     (void)cdio_close_tray(m_CDdevice.toAscii().constData(), NULL);
00748     }
00749 #endif
00750 
00751     if (m_decoder)
00752         delete m_decoder;
00753 
00754     m_decoder = new CdDecoder("cda", NULL, NULL, NULL);
00755     if (m_decoder)
00756         m_decoder->setDevice(m_CDdevice);
00757 }
00758 
00759 void Ripper::deleteAllExistingTracks(void)
00760 {
00761     QVector<RipTrack*>::iterator it;
00762     for (it = m_tracks->begin(); it < m_tracks->end(); ++it)
00763     {
00764         RipTrack *track = (*it);
00765         if (track && !track->isNew)
00766         {
00767             if (deleteExistingTrack(track))
00768             {
00769                 track->isNew = true;
00770                 toggleTrackActive(track);
00771             }
00772         }
00773     }
00774 }
00775 
00776 bool Ripper::deleteExistingTrack(RipTrack *track)
00777 {
00778     if (!track)
00779         return false;
00780 
00781     Metadata *metadata = track->metadata;
00782 
00783     if (!metadata)
00784         return false;
00785 
00786     QString artist = metadata->Artist();
00787     QString album = metadata->Album();
00788     QString title = metadata->Title();
00789 
00790     MSqlQuery query(MSqlQuery::InitCon());
00791     QString queryString("SELECT song_id, "
00792             "CONCAT_WS('/', music_directories.path, music_songs.filename) AS filename "
00793             "FROM music_songs "
00794             "LEFT JOIN music_artists"
00795             " ON music_songs.artist_id=music_artists.artist_id "
00796             "LEFT JOIN music_albums"
00797             " ON music_songs.album_id=music_albums.album_id "
00798             "LEFT JOIN music_directories "
00799             " ON music_songs.directory_id=music_directories.directory_id "
00800             "WHERE artist_name REGEXP \'");
00801     QString token = artist;
00802     token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"),
00803                   QString("."));
00804 
00805     queryString += token + "\' AND " + "album_name REGEXP \'";
00806     token = album;
00807     token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"),
00808                   QString("."));
00809     queryString += token + "\' AND " + "name    REGEXP \'";
00810     token = title;
00811     token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"),
00812                   QString("."));
00813     queryString += token + "\' ORDER BY artist_name, album_name,"
00814                            " name, song_id, filename LIMIT 1";
00815     query.prepare(queryString);
00816 
00817     if (!query.exec() || !query.isActive())
00818     {
00819         MythDB::DBError("Search music database", query);
00820         return false;
00821     }
00822 
00823     if (query.next())
00824     {
00825         int trackID = query.value(0).toInt();
00826         QString filename = gMusicData->musicDir + query.value(1).toString();
00827 
00828         // delete file
00829         if (!QFile::remove(filename))
00830         {
00831             LOG(VB_GENERAL, LOG_NOTICE, QString("Ripper::deleteExistingTrack() "
00832                                                 "Could not delete %1")
00833                                                 .arg(filename));
00834             return false;
00835         }
00836 
00837         // remove database entry
00838         MSqlQuery deleteQuery(MSqlQuery::InitCon());
00839         deleteQuery.prepare("DELETE FROM music_songs"
00840                             " WHERE song_id = :SONG_ID");
00841         deleteQuery.bindValue(":SONG_ID", trackID);
00842         if (!deleteQuery.exec())
00843         {
00844             MythDB::DBError("Delete Track", deleteQuery);
00845             return false;
00846         }
00847         return true;
00848     }
00849 
00850     return false;
00851 }
00852 
00853 bool Ripper::somethingWasRipped()
00854 {
00855     return m_somethingwasripped;
00856 }
00857 
00858 void Ripper::artistChanged()
00859 {
00860     QString newartist = m_artistEdit->GetText();
00861     Metadata *data;
00862 
00863     if (m_tracks->size() > 0)
00864     {
00865         for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00866         {
00867             data = m_tracks->at(trackno)->metadata;
00868 
00869             if (data)
00870             {
00871                 if (m_compilationCheck->GetBooleanCheckState())
00872                 {
00873                     data->setCompilationArtist(newartist);
00874                 }
00875                 else
00876                 {
00877                     data->setArtist(newartist);
00878                     data->setCompilationArtist("");
00879                 }
00880             }
00881         }
00882 
00883         updateTrackList();
00884     }
00885 
00886     m_artistName = newartist;
00887 }
00888 
00889 void Ripper::albumChanged()
00890 {
00891     QString newalbum = m_albumEdit->GetText();
00892     Metadata *data;
00893 
00894     if (m_tracks->size() > 0)
00895     {
00896         for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00897         {
00898             data = m_tracks->at(trackno)->metadata;
00899 
00900             if (data)
00901                 data->setAlbum(newalbum);
00902         }
00903     }
00904 
00905     m_albumName = newalbum;
00906 }
00907 
00908 void Ripper::genreChanged()
00909 {
00910     QString newgenre = m_genreEdit->GetText();
00911     Metadata *data;
00912 
00913     if (m_tracks->size() > 0)
00914     {
00915         for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00916         {
00917             data = m_tracks->at(trackno)->metadata;
00918 
00919             if (data)
00920                 data->setGenre(newgenre);
00921         }
00922     }
00923 
00924     m_genreName = newgenre;
00925 }
00926 
00927 void Ripper::yearChanged()
00928 {
00929     QString newyear = m_yearEdit->GetText();
00930 
00931     Metadata *data;
00932 
00933     if (m_tracks->size() > 0)
00934     {
00935         for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00936         {
00937             data = m_tracks->at(trackno)->metadata;
00938 
00939             if (data)
00940                 data->setYear(newyear.toInt());
00941         }
00942     }
00943 
00944     m_year = newyear;
00945 }
00946 
00947 void Ripper::compilationChanged(bool state)
00948 {
00949     if (!state)
00950     {
00951         Metadata *data;
00952         if (m_tracks->size() > 0)
00953         {
00954             // Update artist MetaData of each track on the ablum...
00955             for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00956             {
00957                 data = m_tracks->at(trackno)->metadata;
00958 
00959                 if (data)
00960                 {
00961                     data->setCompilationArtist("");
00962                     data->setArtist(m_artistName);
00963                     data->setCompilation(false);
00964                 }
00965             }
00966         }
00967 
00968         m_switchTitleArtist->SetVisible(false);
00969     }
00970     else
00971     {
00972         if (m_tracks->size() > 0)
00973         {
00974             // Update artist MetaData of each track on the album...
00975             for (int trackno = 0; trackno < m_tracks->size(); ++trackno)
00976             {
00977                 Metadata *data;
00978                 data = m_tracks->at(trackno)->metadata;
00979 
00980                 if (data)
00981                 {
00982                     data->setCompilationArtist(m_artistName);
00983                     data->setCompilation(true);
00984                 }
00985             }
00986         }
00987 
00988         m_switchTitleArtist->SetVisible(true);
00989     }
00990 
00991     BuildFocusList();
00992     updateTrackList();
00993 }
00994 
00995 void Ripper::switchTitlesAndArtists()
00996 {
00997     if (!m_compilationCheck->GetBooleanCheckState())
00998         return;
00999 
01000     Metadata *data;
01001 
01002     // Switch title and artist for each track
01003     QString tmp;
01004     if (m_tracks->size() > 0)
01005     {
01006         for (int track = 0; track < m_tracks->size(); ++track)
01007         {
01008             data = m_tracks->at(track)->metadata;
01009 
01010             if (data)
01011             {
01012                 tmp = data->Artist();
01013                 data->setArtist(data->Title());
01014                 data->setTitle(tmp);
01015             }
01016         }
01017 
01018         updateTrackList();
01019     }
01020 }
01021 
01022 void Ripper::startRipper(void)
01023 {
01024     if (m_tracks->isEmpty())
01025     {
01026         ShowOkPopup(tr("There are no tracks to rip?"));
01027         return;
01028     }
01029 
01030     MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
01031 
01032     int quality = m_qualityList->GetItemCurrent()->GetData().toInt();
01033 
01034     RipStatus *statusDialog = new RipStatus(mainStack, m_CDdevice, m_tracks,
01035                                             quality);
01036 
01037     if (statusDialog->Create())
01038     {
01039         connect(statusDialog, SIGNAL(Result(bool)), SLOT(RipComplete(bool)));
01040         mainStack->AddScreen(statusDialog);
01041     }
01042     else
01043         delete statusDialog;
01044 }
01045 
01046 void Ripper::RipComplete(bool result)
01047 {
01048     if (result == true)
01049     {
01050         bool EjectCD = gCoreContext->GetNumSetting("EjectCDAfterRipping", 1);
01051         if (EjectCD)
01052             startEjectCD();
01053 
01054         ShowOkPopup(tr("Rip completed successfully."));
01055 
01056         m_somethingwasripped = true;
01057     }
01058 
01059     if (LCD *lcd = LCD::Get())
01060         lcd->switchToTime();
01061 }
01062 
01063 
01064 void Ripper::startEjectCD()
01065 {
01066     if (m_ejectThread)
01067         return;
01068 
01069     QString message = tr("Ejecting CD. Please Wait ...");
01070 
01071     OpenBusyPopup(message);
01072 
01073     m_ejectThread = new CDEjectorThread(this);
01074     connect(m_ejectThread->qthread(),
01075             SIGNAL(finished()), SLOT(EjectFinished()));
01076     m_ejectThread->start();
01077 }
01078 
01079 void Ripper::EjectFinished()
01080 {
01081     delete m_ejectThread;
01082     m_ejectThread = NULL;
01083 
01084     CloseBusyPopup();
01085 }
01086 
01087 void Ripper::ejectCD()
01088 {
01089     LOG(VB_MEDIA, LOG_INFO, __PRETTY_FUNCTION__);
01090     bool bEjectCD = gCoreContext->GetNumSetting("EjectCDAfterRipping",1);
01091     if (bEjectCD)
01092     {
01093 #ifdef HAVE_CDIO
01094         LOG(VB_MEDIA, LOG_INFO, QString("Ripper::%1 '%2'").
01095             arg(__func__).arg(m_CDdevice));
01096         (void)cdio_eject_media_drive(m_CDdevice.toAscii().constData());
01097 #else
01098         MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
01099         if (mon)
01100         {
01101             QByteArray devname = m_CDdevice.toAscii();
01102             MythMediaDevice *pMedia = mon->GetMedia(devname.constData());
01103             if (pMedia && mon->ValidateAndLock(pMedia))
01104             {
01105                 pMedia->eject();
01106                 mon->Unlock(pMedia);
01107             }
01108         }
01109 #endif
01110     }
01111 }
01112 
01113 void Ripper::updateTrackList(void)
01114 {
01115     if (m_tracks->isEmpty())
01116         return;
01117 
01118     QString tmptitle;
01119     if (m_trackList)
01120     {
01121         m_trackList->Reset();
01122 
01123         int i;
01124         for (i = 0; i < (int)m_tracks->size(); i++)
01125         {
01126             if (i >= m_tracks->size())
01127                 break;
01128 
01129             RipTrack *track = m_tracks->at(i);
01130             Metadata *metadata = track->metadata;
01131 
01132             MythUIButtonListItem *item = new MythUIButtonListItem(m_trackList,"");
01133 
01134             item->setCheckable(true);
01135 
01136             item->SetData(qVariantFromValue(track));
01137 
01138             if (track->isNew)
01139                 item->DisplayState("new", "yes");
01140             else
01141                 item->DisplayState("new", "no");
01142 
01143             if (track->active)
01144                 item->setChecked(MythUIButtonListItem::FullChecked);
01145             else
01146                 item->setChecked(MythUIButtonListItem::NotChecked);
01147 
01148             item->SetText(QString::number(metadata->Track()), "track");
01149             item->SetText(metadata->Title(), "title");
01150             item->SetText(metadata->Artist(), "artist");
01151 
01152             int length = track->length / 1000;
01153             if (length > 0)
01154             {
01155                 int min, sec;
01156                 min = length / 60;
01157                 sec = length % 60;
01158                 QString s;
01159                 s.sprintf("%02d:%02d", min, sec);
01160                 item->SetText(s, "length");
01161             }
01162             else
01163                 item->SetText("", "length");
01164 
01165 //             if (i == m_currentTrack)
01166 //                 m_trackList->SetItemCurrent(i);
01167         }
01168     }
01169 }
01170 
01171 void Ripper::searchArtist()
01172 {
01173     QString msg = tr("Select an Artist");
01174     QStringList searchList = Metadata::fillFieldList("artist");
01175 
01176     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
01177     MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
01178 
01179     if (!searchDlg->Create())
01180     {
01181         delete searchDlg;
01182         return;
01183     }
01184 
01185     connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setArtist(QString)));
01186 
01187     popupStack->AddScreen(searchDlg);
01188 }
01189 
01190 void Ripper::setArtist(QString artist)
01191 {
01192     m_artistEdit->SetText(artist);
01193 }
01194 
01195 void Ripper::searchAlbum()
01196 {
01197     QString msg = tr("Select an Album");
01198     QStringList searchList = Metadata::fillFieldList("album");
01199 
01200     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
01201     MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
01202 
01203     if (!searchDlg->Create())
01204     {
01205         delete searchDlg;
01206         return;
01207     }
01208 
01209     connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setAlbum(QString)));
01210 
01211     popupStack->AddScreen(searchDlg);
01212 }
01213 
01214 void Ripper::setAlbum(QString album)
01215 {
01216     m_albumEdit->SetText(album);
01217 }
01218 
01219 void Ripper::searchGenre()
01220 {
01221     QString msg = tr("Select a Genre");
01222     QStringList searchList = Metadata::fillFieldList("genre");
01223     // load genre list
01224     m_searchList.clear();
01225     for (int x = 0; x < genre_table_size; x++)
01226         m_searchList.push_back(QString(genre_table[x]));
01227     m_searchList.sort();
01228 
01229     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
01230     MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
01231 
01232     if (!searchDlg->Create())
01233     {
01234         delete searchDlg;
01235         return;
01236     }
01237 
01238     connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setGenre(QString)));
01239 
01240     popupStack->AddScreen(searchDlg);
01241 }
01242 
01243 void Ripper::setGenre(QString genre)
01244 {
01245     m_genreEdit->SetText(genre);
01246 }
01247 
01248 void Ripper::showEditMetadataDialog(MythUIButtonListItem *item)
01249 {
01250     if (!item || m_tracks->isEmpty())
01251         return;
01252 
01253     RipTrack *track = qVariantValue<RipTrack *>(item->GetData());
01254 
01255     if (!track)
01256         return;
01257 
01258     Metadata *editMeta = track->metadata;
01259 
01260     MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
01261 
01262     EditMetadataDialog *editDialog = new EditMetadataDialog(mainStack, editMeta);
01263     editDialog->setSaveMetadataOnly();
01264 
01265     if (!editDialog->Create())
01266     {
01267         delete editDialog;
01268         return;
01269     }
01270 
01271     connect(editDialog, SIGNAL(metadataChanged()), this, SLOT(metadataChanged()));
01272 
01273     mainStack->AddScreen(editDialog);
01274 }
01275 
01276 void Ripper::metadataChanged(void)
01277 {
01278     updateTrackList();
01279 }
01280 
01281 void Ripper::toggleTrackActive(RipTrack* track)
01282 {
01283     QVariant data = QVariant::fromValue(track);
01284     MythUIButtonListItem *item = m_trackList->GetItemByData(data);
01285     if (item)
01286     {
01287         toggleTrackActive(item);
01288     }
01289 }
01290 
01291 void Ripper::toggleTrackActive(MythUIButtonListItem *item)
01292 {
01293     if (m_tracks->isEmpty() || !item)
01294         return;
01295 
01296     RipTrack *track = m_tracks->at(m_trackList->GetItemPos(item));
01297 
01298     if (!track->active && !track->isNew)
01299     {
01300         ShowConflictMenu(track);
01301         return;
01302     }
01303 
01304     track->active = !track->active;
01305 
01306     if (track->active)
01307         item->setChecked(MythUIButtonListItem::FullChecked);
01308     else
01309         item->setChecked(MythUIButtonListItem::NotChecked);
01310 
01311     updateTrackLengths();
01312 }
01313 
01314 void Ripper::ShowConflictMenu(RipTrack* track)
01315 {
01316     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
01317 
01318     QString msg = tr("This track has been disabled because it is already "
01319                      "present in the database.\n"
01320                      "Do you want to permanently delete the existing "
01321                      "file(s)?");
01322     MythDialogBox *menu = new MythDialogBox(msg, popupStack, "conflictmenu",
01323                                             true);
01324 
01325     if (menu->Create())
01326         popupStack->AddScreen(menu);
01327     else
01328     {
01329         delete menu;
01330         return;
01331     }
01332 
01333     menu->SetReturnEvent(this, "conflictmenu");
01334     menu->AddButton(tr("No, Cancel"));
01335     menu->AddButton(tr("Yes, Delete"), QVariant::fromValue(track));
01336     menu->AddButton(tr("Yes, Delete All"));
01337 }
01338 
01339 void Ripper::updateTrackLengths()
01340 {
01341     QVector<RipTrack*>::iterator it;
01342     RipTrack *track;
01343     int length = 0;
01344 
01345     for (it = m_tracks->end() - 1; it == m_tracks->begin(); --it)
01346     {
01347         track = *it;
01348         if (track->active)
01349         {
01350             track->length = length + track->metadata->Length();
01351             length = 0;
01352         }
01353         else
01354         {
01355             track->length = 0;
01356             length += track->metadata->Length();
01357         }
01358     }
01359 }
01360 
01361 void Ripper::customEvent(QEvent* event)
01362 {
01363     if (event->type() == DialogCompletionEvent::kEventType)
01364     {
01365         DialogCompletionEvent *dce = static_cast<DialogCompletionEvent *>(event);
01366 
01367         if (dce->GetId() == "conflictmenu")
01368         {
01369             int buttonNum = dce->GetResult();
01370             RipTrack *track = qVariantValue<RipTrack *>(dce->GetData());
01371 
01372             switch (buttonNum)
01373             {
01374                 case 0:
01375                     // Do nothing
01376                     break;
01377                 case 1:
01378                     if (deleteExistingTrack(track))
01379                     {
01380                         track->isNew = true;
01381                         toggleTrackActive(track);
01382                     }
01383                     break;
01384                 case 2:
01385                     deleteAllExistingTracks();
01386                     break;
01387                 default:
01388                     break;
01389             }
01390         }
01391 
01392         return;
01393     }
01394 
01395     MythUIType::customEvent(event);
01396 }
01397 
01398 
01400 
01401 RipStatus::RipStatus(MythScreenStack *parent, const QString &device,
01402                      QVector<RipTrack*> *tracks, int quality)
01403     : MythScreenType(parent, "ripstatus")
01404 {
01405     m_CDdevice = device;
01406     m_tracks = tracks;
01407     m_quality = quality;
01408     m_ripperThread = NULL;
01409 
01410     m_overallText = m_trackText = m_statusText = m_trackPctText =
01411     m_overallPctText = NULL;
01412 
01413     m_overallProgress = m_trackProgress = NULL;
01414 }
01415 
01416 RipStatus::~RipStatus(void)
01417 {
01418     if (m_ripperThread)
01419         delete m_ripperThread;
01420 
01421     if (LCD *lcd = LCD::Get())
01422         lcd->switchToTime();
01423 }
01424 
01425 bool RipStatus::Create(void)
01426 {
01427     if (!LoadWindowFromXML("music-ui.xml", "ripstatus", this))
01428         return false;
01429 
01430     m_overallText = dynamic_cast<MythUIText *>(GetChild("overall"));
01431     m_trackText = dynamic_cast<MythUIText *>(GetChild("track"));
01432     m_statusText = dynamic_cast<MythUIText *>(GetChild("status"));
01433     m_trackPctText = dynamic_cast<MythUIText *>(GetChild("trackpct"));
01434     m_overallPctText = dynamic_cast<MythUIText *>(GetChild("overallpct"));
01435 
01436     m_overallProgress = dynamic_cast<MythUIProgressBar *>(GetChild("overall_progress"));
01437     m_trackProgress = dynamic_cast<MythUIProgressBar *>(GetChild("track_progress"));
01438 
01439     BuildFocusList();
01440 
01441     startRip();
01442 
01443     return true;
01444 }
01445 
01446 bool RipStatus::keyPressEvent(QKeyEvent *event)
01447 {
01448     if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
01449         return true;
01450 
01451     bool handled = false;
01452     QStringList actions;
01453     handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
01454 
01455     for (int i = 0; i < actions.size() && !handled; i++)
01456     {
01457         QString action = actions[i];
01458         handled = true;
01459 
01460 
01461         if (action == "ESCAPE" &&
01462             m_ripperThread && m_ripperThread->isRunning())
01463         {
01464             MythConfirmationDialog *dialog =
01465                 ShowOkPopup(tr("Cancel ripping the CD?"), this, NULL, true);
01466             if (dialog)
01467                 dialog->SetReturnEvent(this, "stop_ripping");
01468         }
01469         else
01470             handled = false;
01471     }
01472 
01473     if (!handled && MythScreenType::keyPressEvent(event))
01474         handled = true;
01475 
01476     return handled;
01477 }
01478 
01479 void RipStatus::customEvent(QEvent *event)
01480 {
01481     if (event->type() == DialogCompletionEvent::kEventType)
01482     {
01483         DialogCompletionEvent *dce = static_cast<DialogCompletionEvent *>(event);
01484 
01485         if (dce->GetId() == "stop_ripping" && dce->GetResult())
01486         {
01487             m_ripperThread->cancel();
01488             m_ripperThread->wait();
01489             Close();
01490         }
01491 
01492         return;
01493     }
01494 
01495     RipStatusEvent *rse = dynamic_cast<RipStatusEvent *> (event);
01496 
01497     if (!rse)
01498         return;
01499 
01500     if (event->type() == RipStatusEvent::kTrackTextEvent)
01501     {
01502         if (m_trackText)
01503             m_trackText->SetText(rse->text);
01504     }
01505     else if (event->type() == RipStatusEvent::kOverallTextEvent)
01506     {
01507         if (m_overallText)
01508             m_overallText->SetText(rse->text);
01509     }
01510     else if (event->type() == RipStatusEvent::kStatusTextEvent)
01511     {
01512         if (m_statusText)
01513             m_statusText->SetText(rse->text);
01514     }
01515     else if (event->type() == RipStatusEvent::kTrackProgressEvent)
01516     {
01517         if (m_trackProgress)
01518             m_trackProgress->SetUsed(rse->value);
01519     }
01520     else if (event->type() == RipStatusEvent::kTrackPercentEvent)
01521     {
01522         if (m_trackPctText)
01523             m_trackPctText->SetText(QString("%1%").arg(rse->value));
01524     }
01525     else if (event->type() == RipStatusEvent::kTrackStartEvent)
01526     {
01527         if (m_trackProgress)
01528             m_trackProgress->SetTotal(rse->value);
01529     }
01530     else if (event->type() == RipStatusEvent::kOverallProgressEvent)
01531     {
01532         if (m_overallProgress)
01533             m_overallProgress->SetUsed(rse->value);
01534     }
01535     else if (event->type() == RipStatusEvent::kOverallStartEvent)
01536     {
01537         if (m_overallProgress)
01538             m_overallProgress->SetTotal(rse->value);
01539     }
01540     else if (event->type() == RipStatusEvent::kOverallPercentEvent)
01541     {
01542         if (m_overallPctText)
01543             m_overallPctText->SetText(QString("%1%").arg(rse->value));
01544     }
01545     else if (event->type() == RipStatusEvent::kFinishedEvent)
01546     {
01547         emit Result(true);
01548         Close();
01549     }
01550     else if (event->type() == RipStatusEvent::kEncoderErrorEvent)
01551     {
01552         ShowOkPopup(tr("The encoder failed to create the file.\n"
01553                        "Do you have write permissions"
01554                        " for the music directory?"));
01555         Close();
01556     }
01557     else
01558     {
01559         LOG(VB_GENERAL, LOG_ERR, "Received an unknown event type!");
01560     }
01561 }
01562 
01563 void RipStatus::startRip(void)
01564 {
01565     if (m_ripperThread)
01566         delete m_ripperThread;
01567 
01568     m_ripperThread = new CDRipperThread(this, m_CDdevice, m_tracks, m_quality);
01569     m_ripperThread->start();
01570 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends