MythTV  0.26-pre
videosource.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 // Standard UNIX C headers
00004 #include <unistd.h>
00005 #include <fcntl.h>
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008 
00009 // C++ headers
00010 #include <algorithm>
00011 using namespace std;
00012 
00013 // Qt headers
00014 #include <QCoreApplication>
00015 #include <QTextStream>
00016 #include <QStringList>
00017 #include <QCursor>
00018 #include <QLayout>
00019 #include <QFile>
00020 #include <QMap>
00021 #include <QDir>
00022 #include <QDateTime>
00023 
00024 // MythTV headers
00025 #include "mythconfig.h"
00026 #include "mythwidgets.h"
00027 #include "mythdialogs.h"
00028 #include "mythcorecontext.h"
00029 #include "videosource.h"
00030 #include "datadirect.h"
00031 #include "scanwizard.h"
00032 #include "cardutil.h"
00033 #include "sourceutil.h"
00034 #include "channelutil.h"
00035 #include "frequencies.h"
00036 #include "diseqcsettings.h"
00037 #include "firewiredevice.h"
00038 #include "compat.h"
00039 #include "mythdb.h"
00040 #include "mythdirs.h"
00041 #include "mythlogging.h"
00042 #include "libmythupnp/httprequest.h"    // for TestMimeType()
00043 #include "mythsystem.h"
00044 #include "exitcodes.h"
00045 
00046 #ifdef USING_DVB
00047 #include "dvbtypes.h"
00048 #endif
00049 
00050 #ifdef USING_V4L2
00051 #include <linux/videodev2.h>
00052 #endif
00053 
00054 #ifdef USING_HDHOMERUN
00055 #include "hdhomerun.h"
00056 #endif
00057 
00058 static const uint kDefaultMultirecCount = 2;
00059 
00060 VideoSourceSelector::VideoSourceSelector(uint           _initial_sourceid,
00061                                          const QString &_card_types,
00062                                          bool           _must_have_mplexid) :
00063     ComboBoxSetting(this),
00064     initial_sourceid(_initial_sourceid),
00065     card_types(_card_types),
00066     must_have_mplexid(_must_have_mplexid)
00067 {
00068     card_types.detach();
00069     setLabel(tr("Video Source"));
00070 }
00071 
00072 void VideoSourceSelector::Load(void)
00073 {
00074     MSqlQuery query(MSqlQuery::InitCon());
00075 
00076     QString querystr =
00077         "SELECT DISTINCT videosource.name, videosource.sourceid "
00078         "FROM cardinput, videosource, capturecard";
00079 
00080     querystr += (must_have_mplexid) ? ", channel " : " ";
00081 
00082     querystr +=
00083         "WHERE cardinput.sourceid   = videosource.sourceid AND "
00084         "      cardinput.cardid     = capturecard.cardid   AND "
00085         "      capturecard.hostname = :HOSTNAME ";
00086 
00087     if (!card_types.isEmpty())
00088     {
00089         querystr += QString(" AND capturecard.cardtype in %1 ")
00090             .arg(card_types);
00091     }
00092 
00093     if (must_have_mplexid)
00094     {
00095         querystr +=
00096             " AND channel.sourceid      = videosource.sourceid "
00097             " AND channel.mplexid      != 32767                "
00098             " AND channel.mplexid      != 0                    ";
00099     }
00100 
00101     query.prepare(querystr);
00102     query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
00103 
00104     if (!query.exec() || !query.isActive() || query.size() <= 0)
00105         return;
00106 
00107     uint sel = 0, cnt = 0;
00108     for (; query.next(); cnt++)
00109     {
00110         addSelection(query.value(0).toString(),
00111                      query.value(1).toString());
00112 
00113         sel = (query.value(1).toUInt() == initial_sourceid) ? cnt : sel;
00114     }
00115 
00116     if (initial_sourceid)
00117     {
00118         if (cnt)
00119             setValue(sel);
00120         setEnabled(false);
00121     }
00122 }
00123 
00124 class InstanceCount : public TransSpinBoxSetting
00125 {
00126   public:
00127     InstanceCount(const CaptureCard &parent) : TransSpinBoxSetting(1, 5, 1)
00128     {
00129         setLabel(QObject::tr("Max recordings"));
00130         setHelpText(
00131             QObject::tr(
00132                 "Maximum number of simultaneous recordings this device "
00133                 "should make. Some digital transmitters transmit multiple "
00134                 "programs on a multiplex, if this is set to a value greater "
00135                 "than one MythTV can sometimes take advantage of this."));
00136         uint cnt = parent.GetInstanceCount();
00137         cnt = (!cnt) ? kDefaultMultirecCount : ((cnt < 1) ? 1 : cnt);
00138         setValue(cnt);
00139     };
00140 };
00141 
00142 QString VideoSourceDBStorage::GetWhereClause(MSqlBindings &bindings) const
00143 {
00144     QString sourceidTag(":WHERESOURCEID");
00145 
00146     QString query("sourceid = " + sourceidTag);
00147 
00148     bindings.insert(sourceidTag, m_parent.getSourceID());
00149 
00150     return query;
00151 }
00152 
00153 QString VideoSourceDBStorage::GetSetClause(MSqlBindings& bindings) const
00154 {
00155     QString sourceidTag(":SETSOURCEID");
00156     QString colTag(":SET" + GetColumnName().toUpper());
00157 
00158     QString query("sourceid = " + sourceidTag + ", " +
00159             GetColumnName() + " = " + colTag);
00160 
00161     bindings.insert(sourceidTag, m_parent.getSourceID());
00162     bindings.insert(colTag, user->GetDBValue());
00163 
00164     return query;
00165 }
00166 
00167 QString CaptureCardDBStorage::GetWhereClause(MSqlBindings& bindings) const
00168 {
00169     QString cardidTag(":WHERECARDID");
00170 
00171     QString query("cardid = " + cardidTag);
00172 
00173     bindings.insert(cardidTag, m_parent.getCardID());
00174 
00175     return query;
00176 }
00177 
00178 QString CaptureCardDBStorage::GetSetClause(MSqlBindings& bindings) const
00179 {
00180     QString cardidTag(":SETCARDID");
00181     QString colTag(":SET" + GetColumnName().toUpper());
00182 
00183     QString query("cardid = " + cardidTag + ", " +
00184             GetColumnName() + " = " + colTag);
00185 
00186     bindings.insert(cardidTag, m_parent.getCardID());
00187     bindings.insert(colTag, user->GetDBValue());
00188 
00189     return query;
00190 }
00191 
00192 class XMLTVGrabber : public ComboBoxSetting, public VideoSourceDBStorage
00193 {
00194   public:
00195     XMLTVGrabber(const VideoSource &parent) :
00196         ComboBoxSetting(this),
00197         VideoSourceDBStorage(this, parent, "xmltvgrabber")
00198     {
00199         setLabel(QObject::tr("Listings grabber"));
00200     };
00201 };
00202 
00203 FreqTableSelector::FreqTableSelector(const VideoSource &parent) :
00204     ComboBoxSetting(this), VideoSourceDBStorage(this, parent, "freqtable")
00205 {
00206     setLabel(QObject::tr("Channel frequency table"));
00207     addSelection("default");
00208 
00209     for (uint i = 0; chanlists[i].name; i++)
00210         addSelection(chanlists[i].name);
00211 
00212     setHelpText(QObject::tr("Use default unless this source uses a "
00213                 "different frequency table than the system wide table "
00214                 "defined in the General settings."));
00215 }
00216 
00217 TransFreqTableSelector::TransFreqTableSelector(uint _sourceid) :
00218     ComboBoxSetting(this), sourceid(_sourceid),
00219     loaded_freq_table(QString::null)
00220 {
00221     setLabel(QObject::tr("Channel frequency table"));
00222 
00223     for (uint i = 0; chanlists[i].name; i++)
00224         addSelection(chanlists[i].name);
00225 }
00226 
00227 void TransFreqTableSelector::Load(void)
00228 {
00229     int idx = getValueIndex(gCoreContext->GetSetting("FreqTable"));
00230     if (idx >= 0)
00231         setValue(idx);
00232 
00233     if (!sourceid)
00234         return;
00235 
00236     MSqlQuery query(MSqlQuery::InitCon());
00237     query.prepare(
00238         "SELECT freqtable "
00239         "FROM videosource "
00240         "WHERE sourceid = :SOURCEID");
00241     query.bindValue(":SOURCEID", sourceid);
00242 
00243     if (!query.exec() || !query.isActive())
00244     {
00245         MythDB::DBError("TransFreqTableSelector::load", query);
00246         return;
00247     }
00248 
00249     loaded_freq_table = QString::null;
00250 
00251     if (query.next())
00252     {
00253         loaded_freq_table = query.value(0).toString();
00254         if (!loaded_freq_table.isEmpty() &&
00255             (loaded_freq_table.toLower() != "default"))
00256         {
00257             int idx = getValueIndex(loaded_freq_table);
00258             if (idx >= 0)
00259                 setValue(idx);
00260         }
00261     }
00262 }
00263 
00264 void TransFreqTableSelector::Save(void)
00265 {
00266     LOG(VB_GENERAL, LOG_INFO, "TransFreqTableSelector::Save(void)");
00267 
00268     if ((loaded_freq_table == getValue()) ||
00269         ((loaded_freq_table.toLower() == "default") &&
00270          (getValue() == gCoreContext->GetSetting("FreqTable"))))
00271     {
00272         return;
00273     }
00274 
00275     MSqlQuery query(MSqlQuery::InitCon());
00276     query.prepare(
00277         "UPDATE videosource "
00278         "SET freqtable = :FREQTABLE "
00279         "WHERE sourceid = :SOURCEID");
00280 
00281     query.bindValue(":FREQTABLE", getValue());
00282     query.bindValue(":SOURCEID",  sourceid);
00283 
00284     if (!query.exec() || !query.isActive())
00285     {
00286         MythDB::DBError("TransFreqTableSelector::load", query);
00287         return;
00288     }
00289 }
00290 
00291 void TransFreqTableSelector::SetSourceID(uint _sourceid)
00292 {
00293     sourceid = _sourceid;
00294     Load();
00295 }
00296 
00297 class UseEIT : public CheckBoxSetting, public VideoSourceDBStorage
00298 {
00299   public:
00300     UseEIT(const VideoSource &parent) :
00301         CheckBoxSetting(this), VideoSourceDBStorage(this, parent, "useeit")
00302     {
00303         setLabel(QObject::tr("Perform EIT scan"));
00304         setHelpText(QObject::tr(
00305                         "If enabled, program guide data for channels on this "
00306                         "source will be updated with data provided by the "
00307                         "channels themselves 'Over-the-Air'."));
00308     }
00309 };
00310 
00311 class DataDirectUserID : public LineEditSetting, public VideoSourceDBStorage
00312 {
00313   public:
00314     DataDirectUserID(const VideoSource &parent) :
00315         LineEditSetting(this), VideoSourceDBStorage(this, parent, "userid")
00316     {
00317         setLabel(QObject::tr("User ID"));
00318     }
00319 };
00320 
00321 class DataDirectPassword : public LineEditSetting, public VideoSourceDBStorage
00322 {
00323   public:
00324     DataDirectPassword(const VideoSource &parent) :
00325         LineEditSetting(this, true),
00326         VideoSourceDBStorage(this, parent, "password")
00327     {
00328         SetPasswordEcho(true);
00329         setLabel(QObject::tr("Password"));
00330     }
00331 };
00332 
00333 void DataDirectLineupSelector::fillSelections(const QString &uid,
00334                                               const QString &pwd,
00335                                               int _source)
00336 {
00337     (void) uid;
00338     (void) pwd;
00339 #ifdef USING_BACKEND
00340     if (uid.isEmpty() || pwd.isEmpty())
00341         return;
00342 
00343     qApp->processEvents();
00344 
00345     DataDirectProcessor ddp(_source, uid, pwd);
00346     QString waitMsg = tr("Fetching lineups from %1...")
00347         .arg(ddp.GetListingsProviderName());
00348 
00349     LOG(VB_GENERAL, LOG_INFO, waitMsg);
00350     MythProgressDialog *pdlg = new MythProgressDialog(waitMsg, 2);
00351 
00352     clearSelections();
00353 
00354     pdlg->setProgress(1);
00355 
00356     if (!ddp.GrabLineupsOnly())
00357     {
00358         LOG(VB_GENERAL, LOG_ERR,
00359             "DDLS: fillSelections did not successfully load selections");
00360         pdlg->deleteLater();
00361         return;
00362     }
00363     const DDLineupList lineups = ddp.GetLineups();
00364 
00365     DDLineupList::const_iterator it;
00366     for (it = lineups.begin(); it != lineups.end(); ++it)
00367         addSelection((*it).displayname, (*it).lineupid);
00368 
00369     pdlg->setProgress(2);
00370     pdlg->Close();
00371     pdlg->deleteLater();
00372 #else // USING_BACKEND
00373     LOG(VB_GENERAL, LOG_ERR,
00374         "You must compile the backend to set up a DataDirect line-up");
00375 #endif // USING_BACKEND
00376 }
00377 
00378 void DataDirect_config::Load()
00379 {
00380     VerticalConfigurationGroup::Load();
00381     bool is_sd_userid = userid->getValue().contains('@') > 0;
00382     bool match = ((is_sd_userid  && (source == DD_SCHEDULES_DIRECT)) ||
00383                   (!is_sd_userid && (source == DD_ZAP2IT)));
00384     if (((userid->getValue() != lastloadeduserid) ||
00385          (password->getValue() != lastloadedpassword)) && match)
00386     {
00387         lineupselector->fillSelections(userid->getValue(),
00388                                        password->getValue(),
00389                                        source);
00390         lastloadeduserid = userid->getValue();
00391         lastloadedpassword = password->getValue();
00392     }
00393 }
00394 
00395 DataDirect_config::DataDirect_config(const VideoSource& _parent, int _source) :
00396     VerticalConfigurationGroup(false, false, false, false),
00397     parent(_parent)
00398 {
00399     source = _source;
00400 
00401     HorizontalConfigurationGroup *up =
00402         new HorizontalConfigurationGroup(false, false, true, true);
00403 
00404     up->addChild(userid   = new DataDirectUserID(parent));
00405     addChild(up);
00406 
00407     HorizontalConfigurationGroup *lp =
00408         new HorizontalConfigurationGroup(false, false, true, true);
00409 
00410     lp->addChild(password = new DataDirectPassword(parent));
00411     lp->addChild(button   = new DataDirectButton());
00412     addChild(lp);
00413 
00414     addChild(lineupselector = new DataDirectLineupSelector(parent));
00415     addChild(new UseEIT(parent));
00416 
00417     connect(button, SIGNAL(pressed()),
00418             this,   SLOT(fillDataDirectLineupSelector()));
00419 }
00420 
00421 void DataDirect_config::fillDataDirectLineupSelector(void)
00422 {
00423     lineupselector->fillSelections(
00424         userid->getValue(), password->getValue(), source);
00425 }
00426 
00427 XMLTV_generic_config::XMLTV_generic_config(const VideoSource& _parent,
00428                                            QString _grabber) :
00429     VerticalConfigurationGroup(false, false, false, false),
00430     parent(_parent), grabber(_grabber)
00431 {
00432     QString filename = QString("%1/%2.xmltv")
00433         .arg(GetConfDir()).arg(parent.getSourceName());
00434 
00435     grabberArgs.push_back("--config-file");
00436     grabberArgs.push_back(filename);
00437     grabberArgs.push_back("--configure");
00438 
00439     addChild(new UseEIT(parent));
00440 
00441     TransButtonSetting *config = new TransButtonSetting();
00442     config->setLabel(tr("Configure"));
00443     config->setHelpText(tr("Run XMLTV configure command."));
00444 
00445     addChild(config);
00446 
00447     connect(config, SIGNAL(pressed()), SLOT(RunConfig()));
00448 }
00449 
00450 void XMLTV_generic_config::Save()
00451 {
00452     VerticalConfigurationGroup::Save();
00453 #if 0
00454     QString err_msg = QObject::tr(
00455         "You MUST run 'mythfilldatabase --manual' the first time,\n"
00456         "instead of just 'mythfilldatabase'.\nYour grabber does not provide "
00457         "channel numbers, so you have to set them manually.");
00458 
00459     if (is_grabber_external(grabber))
00460     {
00461         LOG(VB_GENERAL, LOG_ERR, err_msg);
00462         MythPopupBox::showOkPopup(
00463             GetMythMainWindow(), QObject::tr("Warning."), err_msg);
00464     }
00465 #endif
00466 }
00467 
00468 void XMLTV_generic_config::RunConfig(void)
00469 {
00470     TerminalWizard *tw = new TerminalWizard(grabber, grabberArgs);
00471     tw->exec(false, true);
00472     delete tw;
00473 }
00474 
00475 EITOnly_config::EITOnly_config(const VideoSource& _parent) :
00476     VerticalConfigurationGroup(false, false, true, true)
00477 {
00478     useeit = new UseEIT(_parent);
00479     useeit->setValue(true);
00480     useeit->setVisible(false);
00481     addChild(useeit);
00482 
00483     TransLabelSetting *label;
00484     label=new TransLabelSetting();
00485     label->setValue(QObject::tr("Use only the transmitted guide data."));
00486     addChild(label);
00487     label=new TransLabelSetting();
00488     label->setValue(
00489         QObject::tr("This will usually only work with ATSC or DVB channels,"));
00490     addChild(label);
00491     label=new TransLabelSetting();
00492     label->setValue(
00493         QObject::tr("and generally provides data only for the next few days."));
00494     addChild(label);
00495 }
00496 
00497 void EITOnly_config::Save(void)
00498 {
00499     // Force this value on
00500     useeit->setValue(true);
00501     useeit->Save();
00502 }
00503 
00504 NoGrabber_config::NoGrabber_config(const VideoSource& _parent) :
00505     VerticalConfigurationGroup(false, false, false, false)
00506 {
00507     useeit = new UseEIT(_parent);
00508     useeit->setValue(false);
00509     useeit->setVisible(false);
00510     addChild(useeit);
00511 
00512     TransLabelSetting *label = new TransLabelSetting();
00513     label->setValue(QObject::tr("Do not configure a grabber"));
00514     addChild(label);
00515 }
00516 
00517 void NoGrabber_config::Save(void)
00518 {
00519     useeit->setValue(false);
00520     useeit->Save();
00521 }
00522 
00523 
00524 XMLTVConfig::XMLTVConfig(const VideoSource &aparent) :
00525     TriggeredConfigurationGroup(false, true, false, false),
00526     parent(aparent), grabber(new XMLTVGrabber(parent))
00527 {
00528     addChild(grabber);
00529     setTrigger(grabber);
00530 
00531     // only save settings for the selected grabber
00532     setSaveAll(false);
00533 
00534 }
00535 
00536 void XMLTVConfig::Load(void)
00537 {
00538     addTarget("schedulesdirect1",
00539               new DataDirect_config(parent, DD_SCHEDULES_DIRECT));
00540     addTarget("eitonly",   new EITOnly_config(parent));
00541     addTarget("/bin/true", new NoGrabber_config(parent));
00542 
00543     grabber->addSelection(
00544         QObject::tr("North America (SchedulesDirect.org) (Internal)"),
00545         "schedulesdirect1");
00546 
00547     grabber->addSelection(
00548         QObject::tr("Transmitted guide only (EIT)"), "eitonly");
00549 
00550     grabber->addSelection(QObject::tr("No grabber"), "/bin/true");
00551 
00552     QString validValues;
00553     validValues += "schedulesdirect1";
00554     validValues += "eitonly";
00555     validValues += "/bin/true";
00556 
00557     QString gname, d1, d2, d3;
00558     SourceUtil::GetListingsLoginData(parent.getSourceID(), gname, d1, d2, d3);
00559 
00560     QString loc = "XMLTVConfig::Load: ";
00561     QString loc_err = "XMLTVConfig::Load, Error: ";
00562 
00563     QStringList name_list;
00564     QStringList prog_list;
00565 
00566     QStringList args;
00567     args += "baseline";
00568 
00569     MythSystem find_grabber_proc("tv_find_grabbers", args, 
00570                                  kMSStdOut | kMSBuffered | kMSRunShell);
00571     find_grabber_proc.Run(25);
00572     LOG(VB_GENERAL, LOG_INFO,
00573         loc + "Running 'tv_find_grabbers " + args.join(" ") + "'.");
00574     uint status = find_grabber_proc.Wait();
00575 
00576     if (status == GENERIC_EXIT_OK)
00577     {
00578         QTextStream ostream(find_grabber_proc.ReadAll());
00579         while (!ostream.atEnd())
00580         {
00581             QString grabber_list(ostream.readLine());
00582             QStringList grabber_split =
00583                 grabber_list.split("|", QString::SkipEmptyParts);
00584             QString grabber_name = grabber_split[1] + " (xmltv)";
00585             QFileInfo grabber_file(grabber_split[0]);
00586 
00587             name_list.push_back(grabber_name);
00588             prog_list.push_back(grabber_file.fileName());
00589             LOG(VB_GENERAL, LOG_DEBUG, "Found " + grabber_split[0]);
00590         }
00591         LOG(VB_GENERAL, LOG_INFO, loc + "Finished running tv_find_grabbers");
00592     }
00593     else
00594         LOG(VB_GENERAL, LOG_ERR, loc + "Failed to run tv_find_grabbers");
00595 
00596     LoadXMLTVGrabbers(name_list, prog_list);
00597 
00598     TriggeredConfigurationGroup::Load();
00599 
00600 }
00601 
00602 void XMLTVConfig::LoadXMLTVGrabbers(
00603     QStringList name_list, QStringList prog_list)
00604 {
00605     if (name_list.size() != prog_list.size())
00606         return;
00607 
00608     QString selValue = grabber->getValue();
00609     int     selIndex = grabber->getValueIndex(selValue);
00610     grabber->setValue(0);
00611 
00612     QString validValues;
00613     validValues += "schedulesdirect1";
00614     validValues += "eitonly";
00615     validValues += "/bin/true";
00616 
00617     for (uint i = 0; i < grabber->size(); i++)
00618     {
00619         if (!validValues.contains(grabber->GetValue(i)))
00620         {
00621             removeTarget(grabber->GetValue(i));
00622             i--;
00623         }
00624     }
00625 
00626     for (uint i = 0; i < (uint) name_list.size(); i++)
00627     {
00628         addTarget(prog_list[i],
00629                   new XMLTV_generic_config(parent, prog_list[i]));
00630         grabber->addSelection(name_list[i], prog_list[i]);
00631     }
00632 
00633     if (!selValue.isEmpty())
00634         selIndex = grabber->getValueIndex(selValue);
00635     if (selIndex >= 0)
00636         grabber->setValue(selIndex);
00637 
00638 }
00639 
00640 void XMLTVConfig::Save(void)
00641 {
00642     TriggeredConfigurationGroup::Save();
00643     MSqlQuery query(MSqlQuery::InitCon());
00644     query.prepare(
00645         "UPDATE videosource "
00646         "SET userid=NULL, password=NULL "
00647         "WHERE xmltvgrabber NOT IN ( 'datadirect', 'technovera', "
00648         "                            'schedulesdirect1' )");
00649     if (!query.exec())
00650         MythDB::DBError("XMLTVConfig::Save", query);
00651 }
00652 
00653 VideoSource::VideoSource()
00654 {
00655     // must be first
00656     addChild(id = new ID());
00657 
00658     ConfigurationGroup *group = new VerticalConfigurationGroup(false, false);
00659     group->setLabel(QObject::tr("Video Source Setup"));
00660     group->addChild(name = new Name(*this));
00661     group->addChild(xmltv = new XMLTVConfig(*this));
00662     group->addChild(new FreqTableSelector(*this));
00663     addChild(group);
00664 }
00665 
00666 bool VideoSourceEditor::cardTypesInclude(const int &sourceID,
00667                                          const QString &thecardtype)
00668 {
00669     MSqlQuery query(MSqlQuery::InitCon());
00670     query.prepare("SELECT count(cardtype)"
00671                   " FROM cardinput,capturecard "
00672                   " WHERE capturecard.cardid = cardinput.cardid "
00673                   " AND cardinput.sourceid= :SOURCEID "
00674                   " AND capturecard.cardtype= :CARDTYPE ;");
00675     query.bindValue(":SOURCEID", sourceID);
00676     query.bindValue(":CARDTYPE", thecardtype);
00677 
00678     if (query.exec() && query.next())
00679     {
00680         int count = query.value(0).toInt();
00681 
00682         if (count > 0)
00683             return true;
00684     }
00685 
00686     return false;
00687 }
00688 
00689 void VideoSource::fillSelections(SelectSetting* setting)
00690 {
00691     MSqlQuery result(MSqlQuery::InitCon());
00692     result.prepare("SELECT name, sourceid FROM videosource;");
00693 
00694     if (result.exec() && result.isActive() && result.size() > 0)
00695     {
00696         while (result.next())
00697         {
00698             setting->addSelection(result.value(0).toString(),
00699                                   result.value(1).toString());
00700         }
00701     }
00702 }
00703 
00704 void VideoSource::loadByID(int sourceid)
00705 {
00706     id->setValue(sourceid);
00707 }
00708 
00709 class VideoDevice : public PathSetting, public CaptureCardDBStorage
00710 {
00711   public:
00712     VideoDevice(const CaptureCard &parent,
00713                 uint    minor_min = 0,
00714                 uint    minor_max = UINT_MAX,
00715                 QString card      = QString::null,
00716                 QString driver    = QString::null) :
00717         PathSetting(this, true),
00718         CaptureCardDBStorage(this, parent, "videodevice")
00719     {
00720         setLabel(QObject::tr("Video device"));
00721 
00722         // /dev/v4l/video*
00723         QDir dev("/dev/v4l", "video*", QDir::Name, QDir::System);
00724         fillSelectionsFromDir(dev, minor_min, minor_max,
00725                               card, driver, false);
00726 
00727         // /dev/video*
00728         dev.setPath("/dev");
00729         fillSelectionsFromDir(dev, minor_min, minor_max,
00730                               card, driver, false);
00731 
00732         // /dev/dtv/video*
00733         dev.setPath("/dev/dtv");
00734         fillSelectionsFromDir(dev, minor_min, minor_max,
00735                               card, driver, false);
00736 
00737         // /dev/dtv*
00738         dev.setPath("/dev");
00739         dev.setNameFilters(QStringList("dtv*"));
00740         fillSelectionsFromDir(dev, minor_min, minor_max,
00741                               card, driver, false);
00742     };
00743 
00744     uint fillSelectionsFromDir(const QDir& dir,
00745                                uint minor_min, uint minor_max,
00746                                QString card, QString driver,
00747                                bool allow_duplicates)
00748     {
00749         uint cnt = 0;
00750 
00751         QFileInfoList il = dir.entryInfoList();
00752         QRegExp *driverExp = NULL;
00753         if (!driver.isEmpty())
00754             driverExp = new QRegExp(driver);
00755 
00756         for( QFileInfoList::iterator it  = il.begin();
00757                                      it != il.end();
00758                                    ++it )
00759         {
00760             QFileInfo &fi = *it;
00761 
00762             struct stat st;
00763             QString filepath = fi.absoluteFilePath();
00764             int err = lstat(filepath.toLocal8Bit().constData(), &st);
00765 
00766             if (err)
00767             {
00768                 LOG(VB_GENERAL, LOG_ERR,
00769                     QString("Could not stat file: %1").arg(filepath));
00770                 continue;
00771             }
00772 
00773             // is this is a character device?
00774             if (!S_ISCHR(st.st_mode))
00775                 continue;
00776 
00777             // is this device is in our minor range?
00778             uint minor_num = minor(st.st_rdev);
00779             if (minor_min > minor_num || minor_max < minor_num)
00780                 continue;
00781 
00782             // ignore duplicates if allow_duplicates not set
00783             if (!allow_duplicates && minor_list[minor_num])
00784                 continue;
00785 
00786             // if the driver returns any info add this device to our list
00787             QByteArray tmp = filepath.toAscii();
00788             int videofd = open(tmp.constData(), O_RDWR);
00789             if (videofd >= 0)
00790             {
00791                 QString cn, dn;
00792                 if (CardUtil::GetV4LInfo(videofd, cn, dn) &&
00793                     (!driverExp     || (driverExp->exactMatch(dn)))  &&
00794                     (card.isEmpty() || (cn == card)))
00795                 {
00796                     addSelection(filepath);
00797                     cnt++;
00798                 }
00799                 close(videofd);
00800             }
00801 
00802             // add to list of minors discovered to avoid duplicates
00803             minor_list[minor_num] = 1;
00804         }
00805         delete driverExp;
00806 
00807         return cnt;
00808     }
00809 
00810   private:
00811     QMap<uint, uint> minor_list;
00812 };
00813 
00814 class VBIDevice : public PathSetting, public CaptureCardDBStorage
00815 {
00816   public:
00817     VBIDevice(const CaptureCard &parent) :
00818         PathSetting(this, true),
00819         CaptureCardDBStorage(this, parent, "vbidevice")
00820     {
00821         setLabel(QObject::tr("VBI device"));
00822         setFilter(QString::null, QString::null);
00823     };
00824 
00825     void setFilter(const QString &card, const QString &driver)
00826     {
00827         clearSelections();
00828         QDir dev("/dev/v4l", "vbi*", QDir::Name, QDir::System);
00829         if (!fillSelectionsFromDir(dev, card, driver))
00830         {
00831             dev.setPath("/dev");
00832             if (!fillSelectionsFromDir(dev, card, driver) &&
00833                 !getValue().isEmpty())
00834             {
00835                 addSelection(getValue(),getValue(),true);
00836             }
00837         }
00838     }
00839 
00840     uint fillSelectionsFromDir(const QDir &dir, const QString &card,
00841                                const QString &driver)
00842     {
00843         QStringList devices;
00844         QFileInfoList il = dir.entryInfoList();
00845         for( QFileInfoList::iterator it  = il.begin();
00846                                      it != il.end();
00847                                    ++it )
00848         {
00849             QFileInfo &fi = *it;
00850 
00851             QString    device = fi.absoluteFilePath();
00852             QByteArray adevice = device.toAscii();
00853             int vbifd = open(adevice.constData(), O_RDWR);
00854             if (vbifd < 0)
00855                 continue;
00856 
00857             QString cn, dn;
00858             if (CardUtil::GetV4LInfo(vbifd, cn, dn)  &&
00859                 (driver.isEmpty() || (dn == driver)) &&
00860                 (card.isEmpty()   || (cn == card)))
00861             {
00862                 devices.push_back(device);
00863             }
00864 
00865             close(vbifd);
00866         }
00867 
00868         QString sel = getValue();
00869         for (uint i = 0; i < (uint) devices.size(); i++)
00870             addSelection(devices[i], devices[i], devices[i] == sel);
00871 
00872         return (uint) devices.size();
00873     }
00874 };
00875 
00876 class FileDevice : public PathSetting, public CaptureCardDBStorage
00877 {
00878   public:
00879     FileDevice(const CaptureCard &parent) :
00880         PathSetting(this, false),
00881         CaptureCardDBStorage(this, parent, "videodevice")
00882     {
00883         setLabel(QObject::tr("File path"));
00884     };
00885 };
00886 
00887 class AudioDevice : public PathSetting, public CaptureCardDBStorage
00888 {
00889   public:
00890     AudioDevice(const CaptureCard &parent) :
00891         PathSetting(this, false),
00892         CaptureCardDBStorage(this, parent, "audiodevice")
00893     {
00894         setLabel(QObject::tr("Audio device"));
00895 #if USING_OSS
00896         QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
00897         fillSelectionsFromDir(dev);
00898         dev.setPath("/dev/sound");
00899         fillSelectionsFromDir(dev);
00900 #endif
00901 #if USING_ALSA
00902         addSelection("ALSA:default", "ALSA:default");
00903 #endif
00904         addSelection(QObject::tr("(None)"), "NULL");
00905     };
00906 };
00907 
00908 class SignalTimeout : public SpinBoxSetting, public CaptureCardDBStorage
00909 {
00910   public:
00911     SignalTimeout(const CaptureCard &parent, uint value, uint min_val) :
00912         SpinBoxSetting(this, min_val, 60000, 250),
00913         CaptureCardDBStorage(this, parent, "signal_timeout")
00914     {
00915         setLabel(QObject::tr("Signal timeout (ms)"));
00916         setValue(value);
00917         setHelpText(QObject::tr(
00918                         "Maximum time (in milliseconds) MythTV waits for "
00919                         "a signal when scanning for channels."));
00920     };
00921 };
00922 
00923 class ChannelTimeout : public SpinBoxSetting, public CaptureCardDBStorage
00924 {
00925   public:
00926     ChannelTimeout(const CaptureCard &parent, uint value, uint min_val) :
00927         SpinBoxSetting(this, min_val, 65000, 250),
00928         CaptureCardDBStorage(this, parent, "channel_timeout")
00929     {
00930         setLabel(QObject::tr("Tuning timeout (ms)"));
00931         setValue(value);
00932         setHelpText(QObject::tr(
00933                         "Maximum time (in milliseconds) MythTV waits for "
00934                         "a channel lock when scanning for channels during setup, or for "
00935                         "issuing a warning in Live TV mode."));
00936     };
00937 };
00938 
00939 class AudioRateLimit : public ComboBoxSetting, public CaptureCardDBStorage
00940 {
00941   public:
00942     AudioRateLimit(const CaptureCard &parent) :
00943         ComboBoxSetting(this),
00944         CaptureCardDBStorage(this, parent, "audioratelimit")
00945     {
00946         setLabel(QObject::tr("Force audio sampling rate"));
00947         setHelpText(
00948             QObject::tr("If non-zero, override the audio sampling "
00949                         "rate in the recording profile when this card is "
00950                         "used.  Use this if your capture card does not "
00951                         "support all of the standard rates."));
00952         addSelection(QObject::tr("(None)"), "0");
00953         addSelection("32000");
00954         addSelection("44100");
00955         addSelection("48000");
00956     };
00957 };
00958 
00959 class SkipBtAudio : public CheckBoxSetting, public CaptureCardDBStorage
00960 {
00961   public:
00962     SkipBtAudio(const CaptureCard &parent) :
00963         CheckBoxSetting(this),
00964         CaptureCardDBStorage(this, parent, "skipbtaudio")
00965     {
00966         setLabel(QObject::tr("Do not adjust volume"));
00967         setHelpText(
00968             QObject::tr("Enable this option for budget BT878 based "
00969                         "DVB-T cards such as the AverTV DVB-T which "
00970                         "require the audio volume to be left alone."));
00971    };
00972 };
00973 
00974 class DVBCardNum : public ComboBoxSetting, public CaptureCardDBStorage
00975 {
00976   public:
00977     DVBCardNum(const CaptureCard &parent) :
00978         ComboBoxSetting(this),
00979         CaptureCardDBStorage(this, parent, "videodevice")
00980     {
00981         setLabel(QObject::tr("DVB device"));
00982         setHelpText(
00983             QObject::tr("When you change this setting, the text below "
00984                         "should change to the name and type of your card. "
00985                         "If the card cannot be opened, an error message "
00986                         "will be displayed."));
00987         fillSelections(QString::null);
00988     };
00989 
00993     void fillSelections(const QString &current)
00994     {
00995         clearSelections();
00996 
00997         // Get devices from filesystem
00998         QStringList sdevs = CardUtil::ProbeVideoDevices("DVB");
00999 
01000         // Add current if needed
01001         if (!current.isEmpty() &&
01002             (find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
01003         {
01004             stable_sort(sdevs.begin(), sdevs.end());
01005         }
01006 
01007         QStringList db = CardUtil::GetVideoDevices("DVB");
01008 
01009         QMap<QString,bool> in_use;
01010         QString sel = current;
01011         for (uint i = 0; i < (uint)sdevs.size(); i++)
01012         {
01013             const QString dev = sdevs[i];
01014             in_use[sdevs[i]] = find(db.begin(), db.end(), dev) != db.end();
01015             if (sel.isEmpty() && !in_use[sdevs[i]])
01016                 sel = dev;
01017         }
01018 
01019         if (sel.isEmpty() && sdevs.size())
01020             sel = sdevs[0];
01021 
01022         QString usestr = QString(" -- ");
01023         usestr += QObject::tr("Warning: already in use");
01024 
01025         for (uint i = 0; i < (uint)sdevs.size(); i++)
01026         {
01027             const QString dev = sdevs[i];
01028             QString desc = dev + (in_use[sdevs[i]] ? usestr : "");
01029             desc = (current == sdevs[i]) ? dev : desc;
01030             addSelection(desc, dev, dev == sel);
01031         }
01032     }
01033 
01034     virtual void Load(void)
01035     {
01036         clearSelections();
01037         addSelection(QString::null);
01038 
01039         CaptureCardDBStorage::Load();
01040 
01041         QString dev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, getValue());
01042         fillSelections(dev);
01043     }
01044 };
01045 
01046 class DVBCardType : public TransLabelSetting
01047 {
01048   public:
01049     DVBCardType()
01050     {
01051         setLabel(QObject::tr("Subtype"));
01052     };
01053 };
01054 
01055 class DVBCardName : public TransLabelSetting
01056 {
01057   public:
01058     DVBCardName()
01059     {
01060         setLabel(QObject::tr("Frontend ID"));
01061     };
01062 };
01063 
01064 class DVBNoSeqStart : public CheckBoxSetting, public CaptureCardDBStorage
01065 {
01066   public:
01067     DVBNoSeqStart(const CaptureCard &parent) :
01068         CheckBoxSetting(this),
01069         CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart")
01070     {
01071         setLabel(QObject::tr("Wait for SEQ start header."));
01072         setValue(true);
01073         setHelpText(
01074             QObject::tr("If enabled, drop packets from the start of a DVB "
01075                         "recording until a sequence start header is seen."));
01076     };
01077 };
01078 
01079 class DVBOnDemand : public CheckBoxSetting, public CaptureCardDBStorage
01080 {
01081   public:
01082     DVBOnDemand(const CaptureCard &parent) :
01083         CheckBoxSetting(this),
01084         CaptureCardDBStorage(this, parent, "dvb_on_demand")
01085     {
01086         setLabel(QObject::tr("Open DVB card on demand"));
01087         setValue(true);
01088         setHelpText(
01089             QObject::tr("If enabled, only open the DVB card when required, "
01090                         "leaving it free for other programs at other times."));
01091     };
01092 };
01093 
01094 class DVBEITScan : public CheckBoxSetting, public CaptureCardDBStorage
01095 {
01096   public:
01097     DVBEITScan(const CaptureCard &parent) :
01098         CheckBoxSetting(this),
01099         CaptureCardDBStorage(this, parent, "dvb_eitscan")
01100     {
01101         setLabel(QObject::tr("Use DVB card for active EIT scan"));
01102         setValue(true);
01103         setHelpText(
01104             QObject::tr("If enabled, activate active scanning for "
01105                         "program data (EIT). When this option is enabled "
01106                         "the DVB card is constantly in-use."));
01107     };
01108 };
01109 
01110 class DVBTuningDelay : public SpinBoxSetting, public CaptureCardDBStorage
01111 {
01112   public:
01113     DVBTuningDelay(const CaptureCard &parent) :
01114         SpinBoxSetting(this, 0, 2000, 25),
01115         CaptureCardDBStorage(this, parent, "dvb_tuning_delay")
01116     {
01117         setLabel(QObject::tr("DVB tuning delay (ms)"));
01118         setHelpText(
01119             QObject::tr("Some Linux DVB drivers, in particular for the "
01120                         "Hauppauge Nova-T, require that we slow down "
01121                         "the tuning process by specifying a delay "
01122                         "(in milliseconds)."));
01123     };
01124 };
01125 
01126 class FirewireGUID : public ComboBoxSetting, public CaptureCardDBStorage
01127 {
01128   public:
01129     FirewireGUID(const CaptureCard &parent) :
01130         ComboBoxSetting(this),
01131         CaptureCardDBStorage(this, parent, "videodevice")
01132     {
01133         setLabel(QObject::tr("GUID"));
01134 #ifdef USING_FIREWIRE
01135         vector<AVCInfo> list = FirewireDevice::GetSTBList();
01136         for (uint i = 0; i < list.size(); i++)
01137         {
01138             QString guid = list[i].GetGUIDString();
01139             guid_to_avcinfo[guid] = list[i];
01140             addSelection(guid);
01141         }
01142 #endif // USING_FIREWIRE
01143     }
01144 
01145     AVCInfo GetAVCInfo(const QString &guid) const
01146         { return guid_to_avcinfo[guid]; }
01147 
01148   private:
01149     QMap<QString,AVCInfo> guid_to_avcinfo;
01150 };
01151 
01152 FirewireModel::FirewireModel(const CaptureCard  &parent,
01153                              const FirewireGUID *_guid) :
01154     ComboBoxSetting(this),
01155     CaptureCardDBStorage(this, parent, "firewire_model"),
01156     guid(_guid)
01157 {
01158     setLabel(QObject::tr("Cable box model"));
01159     addSelection(QObject::tr("Motorola Generic"), "MOTO GENERIC");
01160     addSelection(QObject::tr("SA/Cisco Generic"), "SA GENERIC");
01161     addSelection("DCH-3200");
01162     addSelection("DCX-3200");
01163     addSelection("DCT-3412");
01164     addSelection("DCT-3416");
01165     addSelection("DCT-6200");
01166     addSelection("DCT-6212");
01167     addSelection("DCT-6216");
01168     addSelection("QIP-6200");
01169     addSelection("QIP-7100");
01170     addSelection("PACE-550");
01171     addSelection("PACE-779");
01172     addSelection("SA3250HD");
01173     addSelection("SA4200HD");
01174     addSelection("SA4250HDC");
01175     addSelection("SA8300HD");
01176     QString help = QObject::tr(
01177         "Choose the model that most closely resembles your set top box. "
01178         "Depending on firmware revision SA4200HD may work better for a "
01179         "SA3250HD box.");
01180     setHelpText(help);
01181 }
01182 
01183 void FirewireModel::SetGUID(const QString &_guid)
01184 {
01185     (void) _guid;
01186 
01187 #ifdef USING_FIREWIRE
01188     AVCInfo info = guid->GetAVCInfo(_guid);
01189     QString model = FirewireDevice::GetModelName(info.vendorid, info.modelid);
01190     setValue(max(getValueIndex(model), 0));
01191 #endif // USING_FIREWIRE
01192 }
01193 
01194 void FirewireDesc::SetGUID(const QString &_guid)
01195 {
01196     (void) _guid;
01197 
01198     setLabel(tr("Description"));
01199 
01200 #ifdef USING_FIREWIRE
01201     QString name = guid->GetAVCInfo(_guid).product_name;
01202     name.replace("Scientific-Atlanta", "SA");
01203     name.replace(", Inc.", "");
01204     name.replace("Explorer(R)", "");
01205     name = name.simplified();
01206     setValue((name.isEmpty()) ? "" : name);
01207 #endif // USING_FIREWIRE
01208 }
01209 
01210 class FirewireConnection : public ComboBoxSetting, public CaptureCardDBStorage
01211 {
01212   public:
01213     FirewireConnection(const CaptureCard &parent) :
01214         ComboBoxSetting(this),
01215         CaptureCardDBStorage(this, parent, "firewire_connection")
01216     {
01217         setLabel(QObject::tr("Connection Type"));
01218         addSelection(QObject::tr("Point to Point"),"0");
01219         addSelection(QObject::tr("Broadcast"),"1");
01220     }
01221 };
01222 
01223 class FirewireSpeed : public ComboBoxSetting, public CaptureCardDBStorage
01224 {
01225   public:
01226     FirewireSpeed(const CaptureCard &parent) :
01227         ComboBoxSetting(this),
01228         CaptureCardDBStorage(this, parent, "firewire_speed")
01229     {
01230         setLabel(QObject::tr("Speed"));
01231         addSelection(QObject::tr("100Mbps"),"0");
01232         addSelection(QObject::tr("200Mbps"),"1");
01233         addSelection(QObject::tr("400Mbps"),"2");
01234         addSelection(QObject::tr("800Mbps"),"3");
01235     }
01236 };
01237 
01238 class FirewireConfigurationGroup : public VerticalConfigurationGroup
01239 {
01240   public:
01241     FirewireConfigurationGroup(CaptureCard& a_parent) :
01242         VerticalConfigurationGroup(false, true, false, false),
01243         parent(a_parent),
01244         dev(new FirewireGUID(parent)),
01245         desc(new FirewireDesc(dev)),
01246         model(new FirewireModel(parent, dev))
01247     {
01248         addChild(dev);
01249         addChild(new EmptyAudioDevice(parent));
01250         addChild(new EmptyVBIDevice(parent));
01251         addChild(desc);
01252         addChild(model);
01253 
01254 #ifdef USING_LINUX_FIREWIRE
01255         addChild(new FirewireConnection(parent));
01256         addChild(new FirewireSpeed(parent));
01257 #endif // USING_LINUX_FIREWIRE
01258 
01259         addChild(new SignalTimeout(parent, 2000, 1000));
01260         addChild(new ChannelTimeout(parent, 9000, 1750));
01261 
01262         model->SetGUID(dev->getValue());
01263         desc->SetGUID(dev->getValue());
01264         connect(dev,   SIGNAL(valueChanged(const QString&)),
01265                 model, SLOT(  SetGUID(     const QString&)));
01266         connect(dev,   SIGNAL(valueChanged(const QString&)),
01267                 desc,  SLOT(  SetGUID(     const QString&)));
01268     };
01269 
01270   private:
01271     CaptureCard   &parent;
01272     FirewireGUID  *dev;
01273     FirewireDesc  *desc;
01274     FirewireModel *model;
01275 };
01276 
01277 // -----------------------
01278 // HDHomeRun Configuration
01279 // -----------------------
01280 
01281 HDHomeRunIP::HDHomeRunIP()
01282 {
01283     setLabel(QObject::tr("IP Address"));
01284     setEnabled(false);
01285     connect(this, SIGNAL(valueChanged( const QString&)),
01286             this, SLOT(  UpdateDevices(const QString&)));
01287     _oldValue="";
01288 };
01289 
01290 void HDHomeRunIP::setEnabled(bool e)
01291 {
01292     TransLineEditSetting::setEnabled(e);
01293     if (e)
01294     {
01295         if (!_oldValue.isEmpty())
01296             setValue(_oldValue);
01297         emit NewIP(getValue());
01298     }
01299     else
01300     {
01301         _oldValue = getValue();
01302         _oldValue.detach();
01303     }
01304 }
01305 
01306 void HDHomeRunIP::UpdateDevices(const QString &v)
01307 {
01308    if (isEnabled())
01309    {
01310 #if 0
01311        LOG(VB_GENERAL, LOG_DEBUG, QString("Emitting NewIP(%1)").arg(v));
01312 #endif
01313        emit NewIP(v);
01314    }
01315 }
01316 
01317 HDHomeRunTunerIndex::HDHomeRunTunerIndex()
01318 {
01319     setLabel(QObject::tr("Tuner"));
01320     setEnabled(false);
01321     connect(this, SIGNAL(valueChanged( const QString&)),
01322             this, SLOT(  UpdateDevices(const QString&)));
01323     _oldValue = "";
01324 };
01325 
01326 void HDHomeRunTunerIndex::setEnabled(bool e)
01327 {
01328     TransLineEditSetting::setEnabled(e);
01329     if (e) {
01330         if (!_oldValue.isEmpty())
01331             setValue(_oldValue);
01332         emit NewTuner(getValue());
01333     }
01334     else
01335     {
01336         _oldValue = getValue();
01337     }
01338 }
01339 
01340 void HDHomeRunTunerIndex::UpdateDevices(const QString &v)
01341 {
01342    if (isEnabled())
01343    {
01344 #if 0
01345        LOG(VB_GENERAL, LOG_DEBUG, QString("Emitting NewTuner(%1)").arg(v));
01346 #endif
01347        emit NewTuner(v);
01348    }
01349 }
01350 
01351 HDHomeRunDeviceID::HDHomeRunDeviceID(const CaptureCard &parent) :
01352     LabelSetting(this),
01353     CaptureCardDBStorage(this, parent, "videodevice"),
01354     _ip(QString::null),
01355     _tuner(QString::null),
01356     _overridedeviceid(QString::null)
01357 {
01358     setLabel(tr("Device ID"));
01359     setHelpText(tr("Device ID of HDHomeRun device"));
01360 }
01361 
01362 void HDHomeRunDeviceID::SetIP(const QString &ip)
01363 {
01364 #if 0
01365     LOG(VB_GENERAL, LOG_DEBUG, QString("Setting IP to %1").arg(ip));
01366 #endif
01367     _ip = ip;
01368     setValue(QString("%1-%2").arg(_ip).arg(_tuner));
01369 #if 0
01370     LOG(VB_GENERAL, LOG_DEBUG, QString("Done Setting IP to %1").arg(ip));
01371 #endif
01372 }
01373 
01374 void HDHomeRunDeviceID::SetTuner(const QString &tuner)
01375 {
01376 #if 0
01377     LOG(VB_GENERAL, LOG_DEBUG, QString("Setting Tuner to %1").arg(tuner));
01378 #endif
01379     _tuner = tuner;
01380     setValue(QString("%1-%2").arg(_ip).arg(_tuner));
01381 #if 0
01382     LOG(VB_GENERAL, LOG_DEBUG, QString("Done Setting Tuner to %1").arg(tuner));
01383 #endif
01384 }
01385 
01386 void HDHomeRunDeviceID::SetOverrideDeviceID(const QString &deviceid)
01387 {
01388     _overridedeviceid = deviceid;
01389     setValue(deviceid);
01390 }
01391 
01392 void HDHomeRunDeviceID::Load(void)
01393 {
01394     CaptureCardDBStorage::Load();
01395     if (!_overridedeviceid.isEmpty())
01396     {
01397         setValue(_overridedeviceid);
01398         _overridedeviceid = QString::null;
01399     }
01400 }
01401 
01402 HDHomeRunDeviceIDList::HDHomeRunDeviceIDList(
01403     HDHomeRunDeviceID   *deviceid,
01404     TransLabelSetting   *desc,
01405     HDHomeRunIP         *cardip,
01406     HDHomeRunTunerIndex *cardtuner,
01407     HDHomeRunDeviceList *devicelist) :
01408     _deviceid(deviceid),
01409     _desc(desc),
01410     _cardip(cardip),
01411     _cardtuner(cardtuner),
01412     _devicelist(devicelist)
01413 {
01414     setLabel(QObject::tr("Available devices"));
01415     setHelpText(
01416         QObject::tr(
01417             "Device ID and Tuner Number of available HDHomeRun devices."));
01418 
01419     connect(this, SIGNAL(valueChanged( const QString&)),
01420             this, SLOT(  UpdateDevices(const QString&)));
01421 
01422     _oldValue = "";
01423 };
01424 
01428 void HDHomeRunDeviceIDList::fillSelections(const QString &cur)
01429 {
01430     clearSelections();
01431 
01432     vector<QString> devs;
01433     QMap<QString, bool> in_use;
01434 
01435     QString current = cur;
01436 
01437 #if 0
01438     LOG(VB_GENERAL, LOG_DEBUG, QString("Filling List, current = '%1'")
01439             .arg(current));
01440 #endif
01441 
01442     HDHomeRunDeviceList::iterator it = _devicelist->begin();
01443     for (; it != _devicelist->end(); ++it)
01444     {
01445         if ((*it).discovered)
01446         {
01447             devs.push_back(it.key());
01448             in_use[it.key()] = (*it).inuse;
01449         }
01450     }
01451 
01452     QString man_addr = HDHomeRunDeviceIDList::tr("Manually Enter IP Address");
01453     QString sel = man_addr;
01454     devs.push_back(sel);
01455 
01456     if (3 == devs.size() && current.left(8).toUpper() == "FFFFFFFF")
01457     {
01458         current = sel = (current.right(1) == "0") ?
01459             *(devs.begin()) : *(++devs.begin());
01460     }
01461     else
01462     {
01463         vector<QString>::const_iterator it = devs.begin();
01464         for (; it != devs.end(); ++it)
01465             sel = (current == *it) ? *it : sel;
01466     }
01467 
01468     QString usestr = QString(" -- ");
01469     usestr += QObject::tr("Warning: already in use");
01470 
01471     for (uint i = 0; i < devs.size(); i++)
01472     {
01473         const QString dev = devs[i];
01474         QString desc = dev + (in_use[devs[i]] ? usestr : "");
01475         desc = (current == devs[i]) ? dev : desc;
01476         addSelection(desc, dev, dev == sel);
01477     }
01478 
01479     if (current != cur)
01480     {
01481         _deviceid->SetOverrideDeviceID(current);
01482     }
01483     else if (sel == man_addr && !current.isEmpty())
01484     {
01485         // Populate the proper values for IP address and tuner
01486         QStringList selection = current.split("-");
01487 
01488         _cardip->SetOldValue(selection.first());
01489         _cardtuner->SetOldValue(selection.last());
01490 
01491         _cardip->setValue(selection.first());
01492         _cardtuner->setValue(selection.last());
01493     }
01494 }
01495 
01496 void HDHomeRunDeviceIDList::Load(void)
01497 {
01498     clearSelections();
01499 
01500     fillSelections(_deviceid->getValue());
01501 }
01502 
01503 void HDHomeRunDeviceIDList::UpdateDevices(const QString &v)
01504 {
01505 #if 0
01506     LOG(VB_GENERAL, LOG_DEBUG, QString("Got signal with %1").arg(v));
01507 #endif
01508     if (v == HDHomeRunDeviceIDList::tr("Manually Enter IP Address"))
01509     {
01510         _cardip->setEnabled(true);
01511         _cardtuner->setEnabled(true);
01512 #if 0
01513         LOG(VB_GENERAL, LOG_DEBUG, "Done");
01514 #endif
01515     }
01516     else if (!v.isEmpty())
01517     {
01518         if (_oldValue == HDHomeRunDeviceIDList::tr("Manually Enter IP Address"))
01519         {
01520             _cardip->setEnabled(false);
01521             _cardtuner->setEnabled(false);
01522         }
01523         _deviceid->setValue(v);
01524 
01525         // Update _cardip and cardtuner
01526         _cardip->setValue((*_devicelist)[v].cardip);
01527         _cardtuner->setValue(QString("%1").arg((*_devicelist)[v].cardtuner));
01528         _desc->setValue((*_devicelist)[v].desc);
01529     }
01530     _oldValue = v;
01531 };
01532 
01533 class IPTVHost : public LineEditSetting, public CaptureCardDBStorage
01534 {
01535   public:
01536     IPTVHost(const CaptureCard &parent) :
01537         LineEditSetting(this),
01538         CaptureCardDBStorage(this, parent, "videodevice")
01539     {
01540         setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
01541         setLabel(QObject::tr("M3U URL"));
01542         setHelpText(QObject::tr("URL of M3U containing IPTV channel URLs."));
01543     }
01544 };
01545 
01546 class IPTVConfigurationGroup : public VerticalConfigurationGroup
01547 {
01548   public:
01549     IPTVConfigurationGroup(CaptureCard& a_parent):
01550        VerticalConfigurationGroup(false, true, false, false),
01551        parent(a_parent)
01552     {
01553         setUseLabel(false);
01554         addChild(new IPTVHost(parent));
01555         addChild(new ChannelTimeout(parent, 3000, 1750));
01556         addChild(new EmptyAudioDevice(parent));
01557         addChild(new EmptyVBIDevice(parent));
01558     };
01559 
01560   private:
01561     CaptureCard &parent;
01562 };
01563 
01564 class ASIDevice : public ComboBoxSetting, public CaptureCardDBStorage
01565 {
01566   public:
01567     ASIDevice(const CaptureCard &parent) :
01568         ComboBoxSetting(this, true),
01569         CaptureCardDBStorage(this, parent, "videodevice")
01570     {
01571         setLabel(QObject::tr("ASI device"));
01572         fillSelections(QString::null);
01573     };
01574 
01578     void fillSelections(const QString &current)
01579     {
01580         clearSelections();
01581 
01582         // Get devices from filesystem
01583         QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
01584 
01585         // Add current if needed
01586         if (!current.isEmpty() &&
01587             (find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
01588         {
01589             stable_sort(sdevs.begin(), sdevs.end());
01590         }
01591 
01592         // Get devices from DB
01593         QStringList db = CardUtil::GetVideoDevices("ASI");
01594 
01595         // Figure out which physical devices are already in use
01596         // by another card defined in the DB, and select a device
01597         // for new configs (preferring non-conflicing devices).
01598         QMap<QString,bool> in_use;
01599         QString sel = current;
01600         for (uint i = 0; i < (uint)sdevs.size(); i++)
01601         {
01602             const QString dev = sdevs[i];
01603             in_use[sdevs[i]] = find(db.begin(), db.end(), dev) != db.end();
01604             if (sel.isEmpty() && !in_use[sdevs[i]])
01605                 sel = dev;
01606         }
01607 
01608         // Unfortunately all devices are conflicted, select first device.
01609         if (sel.isEmpty() && sdevs.size())
01610             sel = sdevs[0];
01611 
01612         QString usestr = QString(" -- ");
01613         usestr += QObject::tr("Warning: already in use");
01614 
01615         // Add the devices to the UI
01616         bool found = false;
01617         for (uint i = 0; i < (uint)sdevs.size(); i++)
01618         {
01619             const QString dev = sdevs[i];
01620             QString desc = dev + (in_use[sdevs[i]] ? usestr : "");
01621             desc = (current == sdevs[i]) ? dev : desc;
01622             addSelection(desc, dev, dev == sel);
01623             found |= (dev == sel);
01624         }
01625 
01626         // If a configured device isn't on the list, add it with warning
01627         if (!found && !current.isEmpty())
01628         {
01629             QString desc = current + " -- " +
01630                 QObject::tr("Warning: unable to open");
01631             addSelection(desc, current, true);
01632         }
01633     }
01634 
01635     virtual void Load(void)
01636     {
01637         clearSelections();
01638         addSelection(QString::null);
01639         CaptureCardDBStorage::Load();
01640         fillSelections(getValue());
01641     }
01642 };
01643 
01644 ASIConfigurationGroup::ASIConfigurationGroup(CaptureCard& a_parent):
01645     VerticalConfigurationGroup(false, true, false, false),
01646     parent(a_parent),
01647     device(new ASIDevice(parent)),
01648     cardinfo(new TransLabelSetting()),
01649     instances(new InstanceCount(parent))
01650 {
01651     addChild(device);
01652     addChild(new EmptyAudioDevice(parent));
01653     addChild(new EmptyVBIDevice(parent));
01654     addChild(cardinfo);
01655     addChild(instances);
01656 
01657     connect(device, SIGNAL(valueChanged(const QString&)),
01658             this,   SLOT(  probeCard(   const QString&)));
01659     connect(instances, SIGNAL(valueChanged(int)),
01660             &parent,   SLOT(  SetInstanceCount(int)));
01661 
01662     probeCard(device->getValue());
01663 };
01664 
01665 void ASIConfigurationGroup::probeCard(const QString &device)
01666 {
01667 #ifdef USING_ASI
01668     if (device.isEmpty())
01669     {
01670         cardinfo->setValue("");
01671         return;
01672     }
01673 
01674     if (parent.getCardID() && parent.GetRawCardType() != "ASI")
01675     {
01676         cardinfo->setValue("");
01677         return;
01678     }
01679 
01680     QString error;
01681     int device_num = CardUtil::GetASIDeviceNumber(device, &error);
01682     if (device_num < 0)
01683     {
01684         cardinfo->setValue(tr("Not a valid DVEO ASI card"));
01685         LOG(VB_GENERAL, LOG_WARNING,
01686             "ASIConfigurationGroup::probeCard(), Warning: " + error);
01687         return;
01688     }
01689     cardinfo->setValue(tr("Valid DVEO ASI card"));
01690 #else
01691     cardinfo->setValue(QString("Not compiled with ASI support"));
01692 #endif
01693 }
01694 
01695 ImportConfigurationGroup::ImportConfigurationGroup(CaptureCard& a_parent):
01696     VerticalConfigurationGroup(false, true, false, false),
01697     parent(a_parent),
01698     info(new TransLabelSetting()), size(new TransLabelSetting())
01699 {
01700     FileDevice *device = new FileDevice(parent);
01701     device->setHelpText(tr("A local file used to simulate a recording."
01702                            " Leave empty to use MythEvents to trigger an"
01703                            " external program to import recording files."));
01704     addChild(device);
01705 
01706     addChild(new EmptyAudioDevice(parent));
01707     addChild(new EmptyVBIDevice(parent));
01708 
01709     info->setLabel(tr("File info"));
01710     addChild(info);
01711 
01712     size->setLabel(tr("File size"));
01713     addChild(size);
01714 
01715     connect(device, SIGNAL(valueChanged(const QString&)),
01716             this,   SLOT(  probeCard(   const QString&)));
01717 
01718     probeCard(device->getValue());
01719 };
01720 
01721 void ImportConfigurationGroup::probeCard(const QString &device)
01722 {
01723     QString   ci, cs;
01724     QFileInfo fileInfo(device);
01725 
01726     // For convenience, ImportRecorder allows both formats:
01727     if (device.toLower().startsWith("file:"))
01728         fileInfo.setFile(device.mid(5));
01729 
01730     if (fileInfo.exists())
01731     {
01732         if (fileInfo.isReadable() && (fileInfo.isFile()))
01733         {
01734             ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
01735             cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
01736         }
01737         else
01738             ci = tr("File not readable");
01739     }
01740     else
01741     {
01742         ci = tr("File %1 does not exist").arg(device);
01743     }
01744 
01745     info->setValue(ci);
01746     size->setValue(cs);
01747 }
01748 
01749 class HDHomeRunExtra : public ConfigurationWizard
01750 {
01751   public:
01752     HDHomeRunExtra(HDHomeRunConfigurationGroup &parent);
01753     uint GetInstanceCount(void) const
01754     {
01755         return (uint) count->intValue();
01756     }
01757 
01758   private:
01759     InstanceCount *count;
01760 };
01761 
01762 HDHomeRunExtra::HDHomeRunExtra(HDHomeRunConfigurationGroup &parent) :
01763     count(new InstanceCount(parent.parent))
01764 {
01765     VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false);
01766     rec->setLabel(QObject::tr("Recorder Options"));
01767     rec->setUseLabel(false);
01768 
01769     rec->addChild(new SignalTimeout(parent.parent, 1000, 250));
01770     rec->addChild(new ChannelTimeout(parent.parent, 3000, 1750));
01771     rec->addChild(count);
01772 
01773     addChild(rec);
01774 }
01775 
01776 HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
01777         (CaptureCard& a_parent) :
01778     VerticalConfigurationGroup(false, true, false, false),
01779     parent(a_parent)
01780 {
01781     setUseLabel(false);
01782 
01783     // Fill Device list
01784     FillDeviceList();
01785 
01786     deviceid     = new HDHomeRunDeviceID(parent);
01787     desc         = new TransLabelSetting();
01788     desc->setLabel(tr("Description"));
01789     cardip       = new HDHomeRunIP();
01790     cardtuner    = new HDHomeRunTunerIndex();
01791     deviceidlist = new HDHomeRunDeviceIDList(
01792         deviceid, desc, cardip, cardtuner, &devicelist);
01793 
01794     addChild(deviceidlist);
01795     addChild(new EmptyAudioDevice(parent));
01796     addChild(new EmptyVBIDevice(parent));
01797     addChild(deviceid);
01798     addChild(desc);
01799     addChild(cardip);
01800     addChild(cardtuner);
01801 
01802     TransButtonSetting *buttonRecOpt = new TransButtonSetting();
01803     buttonRecOpt->setLabel(tr("Recording Options"));
01804     addChild(buttonRecOpt);
01805 
01806     connect(buttonRecOpt, SIGNAL(pressed()),
01807             this,         SLOT(  HDHomeRunExtraPanel()));
01808 
01809     connect(cardip,    SIGNAL(NewIP(const QString&)),
01810             deviceid,  SLOT(  SetIP(const QString&)));
01811     connect(cardtuner, SIGNAL(NewTuner(const QString&)),
01812             deviceid,  SLOT(  SetTuner(const QString&)));
01813 };
01814 
01815 void HDHomeRunConfigurationGroup::FillDeviceList(void)
01816 {
01817     devicelist.clear();
01818 
01819     // Find physical devices first
01820     // ProbeVideoDevices returns "deviceid ip" pairs
01821     QStringList devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
01822 
01823     QStringList::const_iterator it;
01824 
01825     for (it = devs.begin(); it != devs.end(); ++it)
01826     {
01827         QString dev = *it;
01828         QStringList devinfo = dev.split(" ");
01829         QString devid = devinfo.at(0);
01830         QString devip = devinfo.at(1);
01831         QString devtuner = devinfo.at(2);
01832 
01833         HDHomeRunDevice tmpdevice;
01834         tmpdevice.deviceid   = devid;
01835         tmpdevice.desc       = CardUtil::GetHDHRdesc(devid);
01836         tmpdevice.cardip     = devip;
01837         tmpdevice.inuse      = false;
01838         tmpdevice.discovered = true;
01839         tmpdevice.cardtuner = devtuner;
01840         tmpdevice.mythdeviceid =
01841             tmpdevice.deviceid + "-" + tmpdevice.cardtuner;
01842         devicelist[tmpdevice.mythdeviceid] = tmpdevice;
01843     }
01844     uint found_device_count = devicelist.size();
01845 
01846     // Now find configured devices
01847 
01848     // returns "xxxxxxxx-n" or "ip.ip.ip.ip-n" values
01849     QStringList db = CardUtil::GetVideoDevices("HDHOMERUN");
01850 
01851     for (it = db.begin(); it != db.end(); ++it)
01852     {
01853         QMap<QString, HDHomeRunDevice>::iterator dit;
01854 
01855         dit = devicelist.find(*it);
01856 
01857         if (dit == devicelist.end())
01858         {
01859             if ((*it).toUpper() == "FFFFFFFF-0" && 2 == found_device_count)
01860                 dit = devicelist.begin();
01861 
01862             if ((*it).toUpper() == "FFFFFFFF-1" && 2 == found_device_count)
01863             {
01864                 dit = devicelist.begin();
01865                 ++dit;
01866             }
01867         }
01868 
01869         if (dit != devicelist.end())
01870         {
01871             (*dit).inuse = true;
01872             continue;
01873         }
01874 
01875         HDHomeRunDevice tmpdevice;
01876         tmpdevice.mythdeviceid = *it;
01877         tmpdevice.inuse        = true;
01878         tmpdevice.discovered   = false;
01879 
01880         if (ProbeCard(tmpdevice))
01881             devicelist[tmpdevice.mythdeviceid] = tmpdevice;
01882     }
01883 
01884 #if 0
01885     // Debug dump of cards
01886     QMap<QString, HDHomeRunDevice>::iterator debugit;
01887     for (debugit = devicelist.begin(); debugit != devicelist.end(); ++debugit)
01888     {
01889         LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2 %3 %4 %5 %6 %7")
01890                 .arg(debugit.key())
01891                 .arg((*debugit).mythdeviceid)
01892                 .arg((*debugit).deviceid)
01893                 .arg((*debugit).cardip)
01894                 .arg((*debugit).cardtuner)
01895                 .arg((*debugit).inuse)
01896                 .arg((*debugit).discovered));
01897     }
01898 #endif
01899 }
01900 
01901 bool HDHomeRunConfigurationGroup::ProbeCard(HDHomeRunDevice &tmpdevice)
01902 {
01903 #ifdef USING_HDHOMERUN
01904     hdhomerun_device_t *thisdevice =
01905         hdhomerun_device_create_from_str(
01906             tmpdevice.mythdeviceid.toLocal8Bit().constData(), NULL);
01907 
01908     if (thisdevice)
01909     {
01910         uint device_id = hdhomerun_device_get_device_id(thisdevice);
01911         uint device_ip = hdhomerun_device_get_device_ip(thisdevice);
01912         uint tuner     = hdhomerun_device_get_tuner(thisdevice);
01913         hdhomerun_device_destroy(thisdevice);
01914 
01915         if (device_id == 0)
01916             tmpdevice.deviceid = "NOTFOUND";
01917         else
01918         {
01919             tmpdevice.deviceid = QString("%1").arg(device_id, 8, 16);
01920             tmpdevice.desc     = CardUtil::GetHDHRdesc(tmpdevice.deviceid);
01921         }
01922 
01923         tmpdevice.deviceid = tmpdevice.deviceid.toUpper();
01924 
01925         tmpdevice.cardip = QString("%1.%2.%3.%4")
01926             .arg((device_ip>>24) & 0xFF).arg((device_ip>>16) & 0xFF)
01927             .arg((device_ip>> 8) & 0xFF).arg((device_ip>> 0) & 0xFF);
01928 
01929         tmpdevice.cardtuner = QString("%1").arg(tuner);
01930         return true;
01931     }
01932 #endif // USING_HDHOMERUN
01933     return false;
01934 }
01935 
01936 void HDHomeRunConfigurationGroup::HDHomeRunExtraPanel(void)
01937 {
01938     parent.reload(); // ensure card id is valid
01939 
01940     HDHomeRunExtra acw(*this);
01941     acw.exec();
01942     parent.SetInstanceCount(acw.GetInstanceCount());
01943 }
01944 
01945 // -----------------------
01946 // Ceton Configuration
01947 // -----------------------
01948 
01949 CetonSetting::CetonSetting(const char* label, const char* helptext)
01950 {
01951     setLabel(QObject::tr(label));
01952     setHelpText(tr(helptext));
01953     connect(this, SIGNAL(valueChanged( const QString&)),
01954             this, SLOT(  UpdateDevices(const QString&)));
01955 }
01956 
01957 void CetonSetting::UpdateDevices(const QString &v)
01958 {
01959     if (isEnabled())
01960         emit NewValue(v);
01961 }
01962 
01963 void CetonSetting::LoadValue(const QString &value)
01964 {
01965     setValue(value);
01966 }
01967 
01968 CetonDeviceID::CetonDeviceID(const CaptureCard &parent) :
01969     LabelSetting(this),
01970     CaptureCardDBStorage(this, parent, "videodevice"),
01971     _ip(), _card(), _tuner()
01972 {
01973     setLabel(tr("Device ID"));
01974     setHelpText(tr("Device ID of Ceton device"));
01975 }
01976 
01977 void CetonDeviceID::SetIP(const QString &ip)
01978 {
01979     QString regexp = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$";
01980     if (QRegExp(regexp).exactMatch(ip + "."))
01981     {
01982         _ip = ip;
01983         setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner));
01984     }
01985 }
01986 
01987 void CetonDeviceID::SetCard(const QString &card)
01988 {
01989     if (QRegExp("^(\\d|RTP)$").exactMatch(card))
01990     {
01991         _card = card;
01992         setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner));
01993     }
01994 }
01995 
01996 void CetonDeviceID::SetTuner(const QString &tuner)
01997 {
01998     if (QRegExp("^\\d$").exactMatch(tuner))
01999     {
02000         _tuner = tuner;
02001         setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner));
02002     }
02003 }
02004 
02005 void CetonDeviceID::Load(void)
02006 {
02007     CaptureCardDBStorage::Load();
02008     UpdateValues();
02009 }
02010 
02011 void CetonDeviceID::UpdateValues(void)
02012 {
02013     QRegExp newstyle("^([0-9.]+)-(\\d|RTP)\\.(\\d)$");
02014     if (newstyle.exactMatch(getValue()))
02015     {
02016         emit LoadedIP(newstyle.cap(1));
02017         emit LoadedCard(newstyle.cap(2));
02018         emit LoadedTuner(newstyle.cap(3));
02019     }
02020 }
02021 
02022 CetonConfigurationGroup::CetonConfigurationGroup
02023         (CaptureCard& a_parent) :
02024     VerticalConfigurationGroup(false, true, false, false),
02025     parent(a_parent)
02026 {
02027     setUseLabel(false);
02028 
02029     deviceid     = new CetonDeviceID(parent);
02030     desc         = new TransLabelSetting();
02031     desc->setLabel(tr("Description"));
02032     ip    = new CetonSetting(
02033         "IP Address",
02034         "IP Address of the Ceton device (192.168.200.1 by default)");
02035     card  = new CetonSetting(
02036         "Card Number",
02037         "Number of the installed Ceton card. Use 0 if only 1 Ceton card is "
02038         "installed in your system. Use the value RTP if this is a Ceton "
02039         "card installed in a remote system");
02040     tuner = new CetonSetting(
02041         "Tuner",
02042         "Number of the tuner on the Ceton device (first tuner is number 0)");
02043 
02044     addChild(ip);
02045     addChild(card);
02046     addChild(tuner);
02047     addChild(deviceid);
02048     addChild(desc);
02049 
02050     connect(ip,       SIGNAL(NewValue(const QString&)),
02051             deviceid, SLOT(  SetIP(const QString&)));
02052     connect(card,     SIGNAL(NewValue(const QString&)),
02053             deviceid, SLOT(  SetCard(const QString&)));
02054     connect(tuner,    SIGNAL(NewValue(const QString&)),
02055             deviceid, SLOT(  SetTuner(const QString&)));
02056 
02057     connect(deviceid, SIGNAL(LoadedIP(const QString&)),
02058             ip,       SLOT(  LoadValue(const QString&)));
02059     connect(deviceid, SIGNAL(LoadedCard(const QString&)),
02060             card,     SLOT(  LoadValue(const QString&)));
02061     connect(deviceid, SIGNAL(LoadedTuner(const QString&)),
02062             tuner,    SLOT(  LoadValue(const QString&)));
02063 
02064 };
02065 
02066 V4LConfigurationGroup::V4LConfigurationGroup(CaptureCard& a_parent) :
02067     VerticalConfigurationGroup(false, true, false, false),
02068     parent(a_parent),
02069     cardinfo(new TransLabelSetting()),  vbidev(new VBIDevice(parent))
02070 {
02071     QString drv = "(?!ivtv|hdpvr|(saa7164(.*)))";
02072     VideoDevice *device = new VideoDevice(parent, 0, 15, QString::null, drv);
02073     HorizontalConfigurationGroup *audgrp =
02074         new HorizontalConfigurationGroup(false, false, true, true);
02075 
02076     cardinfo->setLabel(tr("Probed info"));
02077     audgrp->addChild(new AudioRateLimit(parent));
02078     audgrp->addChild(new SkipBtAudio(parent));
02079 
02080     addChild(device);
02081     addChild(cardinfo);
02082     addChild(vbidev);
02083     addChild(new AudioDevice(parent));
02084     addChild(audgrp);
02085 
02086     connect(device, SIGNAL(valueChanged(const QString&)),
02087             this,   SLOT(  probeCard(   const QString&)));
02088 
02089     probeCard(device->getValue());
02090 };
02091 
02092 void V4LConfigurationGroup::probeCard(const QString &device)
02093 {
02094     QString cn = tr("Failed to open"), ci = cn, dn = QString::null;
02095 
02096     QByteArray adevice = device.toAscii();
02097     int videofd = open(adevice.constData(), O_RDWR);
02098     if (videofd >= 0)
02099     {
02100         if (!CardUtil::GetV4LInfo(videofd, cn, dn))
02101             ci = cn = tr("Failed to probe");
02102         else if (!dn.isEmpty())
02103             ci = cn + "  [" + dn + "]";
02104         close(videofd);
02105     }
02106 
02107     cardinfo->setValue(ci);
02108     vbidev->setFilter(cn, dn);
02109 }
02110 
02111 
02112 MPEGConfigurationGroup::MPEGConfigurationGroup(CaptureCard &a_parent) :
02113     VerticalConfigurationGroup(false, true, false, false),
02114     parent(a_parent),
02115     device(NULL), vbidevice(NULL),
02116     cardinfo(new TransLabelSetting())
02117 {
02118     QString drv = "ivtv|(saa7164(.*))";
02119     device    = new VideoDevice(parent, 0, 15, QString::null, drv);
02120     vbidevice = new VBIDevice(parent);
02121     vbidevice->setVisible(false);
02122 
02123     cardinfo->setLabel(tr("Probed info"));
02124 
02125     addChild(device);
02126     addChild(vbidevice);
02127     addChild(cardinfo);
02128     addChild(new ChannelTimeout(parent, 12000, 2000));
02129 
02130     connect(device, SIGNAL(valueChanged(const QString&)),
02131             this,   SLOT(  probeCard(   const QString&)));
02132 
02133     probeCard(device->getValue());
02134 }
02135 
02136 void MPEGConfigurationGroup::probeCard(const QString &device)
02137 {
02138     QString cn = tr("Failed to open"), ci = cn, dn = QString::null;
02139 
02140     QByteArray adevice = device.toAscii();
02141     int videofd = open(adevice.constData(), O_RDWR);
02142     if (videofd >= 0)
02143     {
02144         if (!CardUtil::GetV4LInfo(videofd, cn, dn))
02145             ci = cn = tr("Failed to probe");
02146         else if (!dn.isEmpty())
02147             ci = cn + "  [" + dn + "]";
02148         close(videofd);
02149     }
02150 
02151     cardinfo->setValue(ci);
02152     vbidevice->setVisible(dn!="ivtv");
02153     vbidevice->setFilter(cn, dn);
02154 }
02155 
02156 DemoConfigurationGroup::DemoConfigurationGroup(CaptureCard &a_parent) :
02157     VerticalConfigurationGroup(false, true, false, false),
02158     parent(a_parent),
02159     info(new TransLabelSetting()), size(new TransLabelSetting())
02160 {
02161     FileDevice *device = new FileDevice(parent);
02162     device->setHelpText(tr("A local MPEG file used to simulate a recording."
02163                            " Must be entered as file:/path/movie.mpg"));
02164     device->addSelection("file:/");
02165     addChild(device);
02166 
02167     addChild(new EmptyAudioDevice(parent));
02168     addChild(new EmptyVBIDevice(parent));
02169 
02170     info->setLabel(tr("File info"));
02171     addChild(info);
02172 
02173     size->setLabel(tr("File size"));
02174     addChild(size);
02175 
02176     connect(device, SIGNAL(valueChanged(const QString&)),
02177             this,   SLOT(  probeCard(   const QString&)));
02178 
02179     probeCard(device->getValue());
02180 }
02181 
02182 void DemoConfigurationGroup::probeCard(const QString &device)
02183 {
02184     if (!device.startsWith("file:", Qt::CaseInsensitive))
02185     {
02186         info->setValue("");
02187         size->setValue("");
02188         return;
02189     }
02190 
02191 
02192     QString   ci, cs;
02193     QFileInfo fileInfo(device.mid(5));
02194     if (fileInfo.exists())
02195     {
02196         if (fileInfo.isReadable() && (fileInfo.isFile()))
02197         {
02198             ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
02199             cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
02200         }
02201         else
02202             ci = tr("File not readable");
02203     }
02204     else
02205     {
02206         ci = tr("File does not exist");
02207     }
02208 
02209     info->setValue(ci);
02210     size->setValue(cs);
02211 }
02212 
02213 HDPVRConfigurationGroup::HDPVRConfigurationGroup(CaptureCard &a_parent) :
02214     VerticalConfigurationGroup(false, true, false, false),
02215     parent(a_parent), cardinfo(new TransLabelSetting()),
02216     audioinput(new TunerCardAudioInput(parent, QString::null, "HDPVR"))
02217 {
02218     VideoDevice *device =
02219         new VideoDevice(parent, 0, 15, QString::null, "hdpvr");
02220 
02221     cardinfo->setLabel(tr("Probed info"));
02222 
02223     addChild(device);
02224     addChild(new EmptyAudioDevice(parent));
02225     addChild(new EmptyVBIDevice(parent));
02226     addChild(cardinfo);
02227     addChild(audioinput);
02228     addChild(new ChannelTimeout(parent, 12000, 2000));
02229 
02230     connect(device, SIGNAL(valueChanged(const QString&)),
02231             this,   SLOT(  probeCard(   const QString&)));
02232 
02233     probeCard(device->getValue());
02234 }
02235 
02236 void HDPVRConfigurationGroup::probeCard(const QString &device)
02237 {
02238     QString cn = tr("Failed to open"), ci = cn, dn = QString::null;
02239 
02240     int videofd = open(device.toLocal8Bit().constData(), O_RDWR);
02241     if (videofd >= 0)
02242     {
02243         if (!CardUtil::GetV4LInfo(videofd, cn, dn))
02244             ci = cn = tr("Failed to probe");
02245         else if (!dn.isEmpty())
02246             ci = cn + "  [" + dn + "]";
02247         close(videofd);
02248     }
02249 
02250     cardinfo->setValue(ci);
02251     audioinput->fillSelections(device);
02252 }
02253 
02254 CaptureCardGroup::CaptureCardGroup(CaptureCard &parent) :
02255     TriggeredConfigurationGroup(true, true, false, false)
02256 {
02257     setLabel(QObject::tr("Capture Card Setup"));
02258 
02259     CardType* cardtype = new CardType(parent);
02260     addChild(cardtype);
02261 
02262     setTrigger(cardtype);
02263     setSaveAll(false);
02264 
02265 #ifdef USING_V4L2
02266     addTarget("V4L",       new V4LConfigurationGroup(parent));
02267 # ifdef USING_IVTV
02268     addTarget("MPEG",      new MPEGConfigurationGroup(parent));
02269 # endif // USING_IVTV
02270 # ifdef USING_HDPVR
02271     addTarget("HDPVR",     new HDPVRConfigurationGroup(parent));
02272 # endif // USING_HDPVR
02273 #endif // USING_V4L2
02274 
02275 #ifdef USING_DVB
02276     addTarget("DVB",       new DVBConfigurationGroup(parent));
02277 #endif // USING_DVB
02278 
02279 #ifdef USING_FIREWIRE
02280     addTarget("FIREWIRE",  new FirewireConfigurationGroup(parent));
02281 #endif // USING_FIREWIRE
02282 
02283 #ifdef USING_HDHOMERUN
02284     addTarget("HDHOMERUN", new HDHomeRunConfigurationGroup(parent));
02285 #endif // USING_HDHOMERUN
02286 
02287 #ifdef USING_IPTV
02288     addTarget("FREEBOX",   new IPTVConfigurationGroup(parent));
02289 #endif // USING_IPTV
02290 
02291 #ifdef USING_ASI
02292     addTarget("ASI",       new ASIConfigurationGroup(parent));
02293 #endif // USING_ASI
02294 
02295 #ifdef USING_CETON
02296     addTarget("CETON",     new CetonConfigurationGroup(parent));
02297 #endif // USING_CETON
02298 
02299     // for testing without any actual tuner hardware:
02300     addTarget("IMPORT",    new ImportConfigurationGroup(parent));
02301     addTarget("DEMO",      new DemoConfigurationGroup(parent));
02302 }
02303 
02304 void CaptureCardGroup::triggerChanged(const QString& value)
02305 {
02306     QString own = (value == "MJPEG" || value == "GO7007") ? "V4L" : value;
02307     TriggeredConfigurationGroup::triggerChanged(own);
02308 }
02309 
02310 CaptureCard::CaptureCard(bool use_card_group)
02311     : id(new ID), instance_count(0)
02312 {
02313     addChild(id);
02314     if (use_card_group)
02315         addChild(new CaptureCardGroup(*this));
02316     addChild(new Hostname(*this));
02317 }
02318 
02319 QString CaptureCard::GetRawCardType(void) const
02320 {
02321     int cardid = getCardID();
02322     if (cardid <= 0)
02323         return QString::null;
02324     return CardUtil::GetRawCardType(cardid);
02325 }
02326 
02327 void CaptureCard::fillSelections(SelectSetting *setting)
02328 {
02329     MSqlQuery query(MSqlQuery::InitCon());
02330     QString qstr =
02331         "SELECT cardid, videodevice, cardtype "
02332         "FROM capturecard "
02333         "WHERE hostname = :HOSTNAME "
02334         "ORDER BY cardid";
02335 
02336     query.prepare(qstr);
02337     query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
02338 
02339     if (!query.exec())
02340     {
02341         MythDB::DBError("CaptureCard::fillSelections", query);
02342         return;
02343     }
02344 
02345     QMap<QString, uint> device_refs;
02346     while (query.next())
02347     {
02348         uint    cardid      = query.value(0).toUInt();
02349         QString videodevice = query.value(1).toString();
02350         QString cardtype    = query.value(2).toString();
02351 
02352         bool sharable = CardUtil::IsTunerSharingCapable(cardtype.toUpper());
02353 
02354         if (sharable && (1 != ++device_refs[videodevice]))
02355             continue;
02356 
02357         QString label = CardUtil::GetDeviceLabel(cardtype, videodevice);
02358         setting->addSelection(label, QString::number(cardid));
02359     }
02360 }
02361 
02362 void CaptureCard::loadByID(int cardid)
02363 {
02364     id->setValue(cardid);
02365     Load();
02366 
02367     // Update instance count for cloned cards.
02368     uint new_cnt = 0;
02369     if (cardid > 0)
02370     {
02371         QString type = CardUtil::GetRawCardType(cardid);
02372         if (CardUtil::IsTunerSharingCapable(type))
02373         {
02374             QString dev = CardUtil::GetVideoDevice(cardid);
02375             vector<uint> cardids = CardUtil::GetCardIDs(dev, type);
02376             new_cnt = cardids.size();
02377         }
02378     }
02379     instance_count = new_cnt;
02380 }
02381 
02382 void CaptureCard::Save(void)
02383 {
02384     uint init_cardid = getCardID();
02385 
02387 
02388     ConfigurationWizard::Save();
02389 
02391 
02392     uint cardid = getCardID();
02393     QString type = CardUtil::GetRawCardType(cardid);
02394     if (!CardUtil::IsTunerSharingCapable(type))
02395         return;
02396 
02397     QString init_dev = CardUtil::GetVideoDevice(cardid);
02398     if (init_dev.isEmpty())
02399     {
02400         LOG(VB_GENERAL, LOG_ERR,
02401             QString("Cannot clone card #%1 with empty videodevice")
02402                 .arg(cardid));
02403         return;
02404     }
02405     vector<uint> cardids = CardUtil::GetCardIDs(init_dev, type);
02406 
02407     if (!instance_count)
02408     {
02409         instance_count = (init_cardid) ?
02410             max((size_t)1, cardids.size()) : kDefaultMultirecCount;
02411     }
02412     uint cloneCount = instance_count - 1;
02413 
02414     if (!init_cardid)
02415         init_cardid = cardid;
02416 
02417     // Delete old clone cards as required.
02418     for (uint i = cardids.size() - 1; (i > cloneCount) && cardids.size(); i--)
02419     {
02420         CardUtil::DeleteCard(cardids.back());
02421         cardids.pop_back();
02422     }
02423 
02424     // Make sure clones & original all share an input group
02425     if (cloneCount && !CardUtil::CreateInputGroupIfNeeded(cardid))
02426         return;
02427 
02428     // Clone this config to existing clone cards.
02429     for (uint i = 0; i < cardids.size(); i++)
02430     {
02431         if (cardids[i] != init_cardid)
02432             CardUtil::CloneCard(init_cardid, cardids[i]);
02433     }
02434 
02435     // Create new clone cards as required.
02436     for (uint i = cardids.size(); i < cloneCount + 1; i++)
02437     {
02438         CardUtil::CloneCard(init_cardid, 0);
02439     }
02440 }
02441 
02442 void CaptureCard::reload(void)
02443 {
02444     if (getCardID() == 0)
02445     {
02446         Save();
02447         Load();
02448     }
02449 }
02450 
02451 CardType::CardType(const CaptureCard &parent) :
02452     ComboBoxSetting(this),
02453     CaptureCardDBStorage(this, parent, "cardtype")
02454 {
02455     setLabel(QObject::tr("Card type"));
02456     setHelpText(QObject::tr("Change the cardtype to the appropriate type for "
02457                 "the capture card you are configuring."));
02458     fillSelections(this);
02459 }
02460 
02461 void CardType::fillSelections(SelectSetting* setting)
02462 {
02463 #ifdef USING_V4L2
02464     setting->addSelection(
02465         QObject::tr("Analog V4L capture card"), "V4L");
02466     setting->addSelection(
02467         QObject::tr("MJPEG capture card (Matrox G200, DC10)"), "MJPEG");
02468 # ifdef USING_IVTV
02469     setting->addSelection(
02470         QObject::tr("MPEG-2 encoder card"), "MPEG");
02471 # endif // USING_IVTV
02472 # ifdef USING_HDPVR
02473     setting->addSelection(
02474         QObject::tr("H.264 encoder card (HD-PVR)"), "HDPVR");
02475 # endif // USING_HDPVR
02476 #endif // USING_V4L2
02477 
02478 #ifdef USING_DVB
02479     setting->addSelection(
02480         QObject::tr("DVB DTV capture card (v3.x)"), "DVB");
02481 #endif // USING_DVB
02482 
02483 #ifdef USING_FIREWIRE
02484     setting->addSelection(
02485         QObject::tr("FireWire cable box"), "FIREWIRE");
02486 #endif // USING_FIREWIRE
02487 
02488 #ifdef USING_V4L2
02489     setting->addSelection(
02490         QObject::tr("USB MPEG-4 encoder box (Plextor ConvertX, etc)"),
02491         "GO7007");
02492 #endif // USING_V4L2
02493 
02494 #ifdef USING_HDHOMERUN
02495     setting->addSelection(
02496         QObject::tr("HDHomeRun DTV tuner box"), "HDHOMERUN");
02497 #endif // USING_HDHOMERUN
02498 
02499 #ifdef USING_IPTV
02500     setting->addSelection(QObject::tr("Network recorder"), "FREEBOX");
02501 #endif // USING_IPTV
02502 
02503 #ifdef USING_ASI
02504     setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI");
02505 #endif
02506 
02507 #ifdef USING_CETON
02508     setting->addSelection(
02509         QObject::tr("Ceton Cablecard tuner "), "CETON");
02510 #endif // USING_CETON
02511 
02512     setting->addSelection(QObject::tr("Import test recorder"), "IMPORT");
02513     setting->addSelection(QObject::tr("Demo test recorder"),   "DEMO");
02514 }
02515 
02516 class CardID : public SelectLabelSetting, public CardInputDBStorage
02517 {
02518   public:
02519     CardID(const CardInput &parent) :
02520         SelectLabelSetting(this), CardInputDBStorage(this, parent, "cardid")
02521     {
02522         setLabel(QObject::tr("Capture device"));
02523     };
02524 
02525     virtual void Load(void)
02526     {
02527         fillSelections();
02528         CardInputDBStorage::Load();
02529     };
02530 
02531     void fillSelections() {
02532         CaptureCard::fillSelections(this);
02533     };
02534 };
02535 
02536 class InputDisplayName : public LineEditSetting, public CardInputDBStorage
02537 {
02538   public:
02539     InputDisplayName(const CardInput &parent) :
02540         LineEditSetting(this),
02541         CardInputDBStorage(this, parent, "displayname")
02542     {
02543         setLabel(QObject::tr("Display name (optional)"));
02544         setHelpText(QObject::tr(
02545                         "This name is displayed on screen when Live TV begins "
02546                         "and when changing the selected input or card. If you "
02547                         "use this, make sure the information is unique for "
02548                         "each input."));
02549     };
02550 };
02551 
02552 class SourceID : public ComboBoxSetting, public CardInputDBStorage
02553 {
02554   public:
02555     SourceID(const CardInput &parent) :
02556         ComboBoxSetting(this), CardInputDBStorage(this, parent, "sourceid")
02557     {
02558         setLabel(QObject::tr("Video source"));
02559         addSelection(QObject::tr("(None)"), "0");
02560     };
02561 
02562     virtual void Load(void)
02563     {
02564         fillSelections();
02565         CardInputDBStorage::Load();
02566     };
02567 
02568     void fillSelections() {
02569         clearSelections();
02570         addSelection(QObject::tr("(None)"), "0");
02571         VideoSource::fillSelections(this);
02572     };
02573 };
02574 
02575 class InputName : public LabelSetting, public CardInputDBStorage
02576 {
02577   public:
02578     InputName(const CardInput &parent) :
02579         LabelSetting(this), CardInputDBStorage(this, parent, "inputname")
02580     {
02581         setLabel(QObject::tr("Input"));
02582     };
02583 };
02584 
02585 class InputGroup : public TransComboBoxSetting
02586 {
02587   public:
02588     InputGroup(const CardInput &parent, uint group_num) :
02589         TransComboBoxSetting(false), cardinput(parent),
02590         groupnum(group_num), groupid(0)
02591     {
02592         setLabel(QObject::tr("Input group") +
02593                  QString(" %1").arg(groupnum + 1));
02594         setHelpText(QObject::tr(
02595                         "Leave as 'Generic' unless this input is shared with "
02596                         "another device. Only one of the inputs in an input "
02597                         "group will be allowed to record at any given time."));
02598     }
02599 
02600     virtual void Load(void);
02601 
02602     virtual void Save(void)
02603     {
02604         uint inputid     = cardinput.getInputID();
02605         uint new_groupid = getValue().toUInt();
02606 
02607         if (groupid)
02608             CardUtil::UnlinkInputGroup(inputid, groupid);
02609 
02610         if (new_groupid)
02611         {
02612             if (CardUtil::UnlinkInputGroup(inputid, new_groupid))
02613                 CardUtil::LinkInputGroup(inputid, new_groupid);
02614         }
02615     }
02616 
02617   private:
02618     const CardInput &cardinput;
02619     uint             groupnum;
02620     uint             groupid;
02621 };
02622 
02623 void InputGroup::Load(void)
02624 {
02625 #if 0
02626     LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2")
02627             .arg(groupnum).arg(cardinput.getInputID()));
02628 #endif
02629 
02630     uint             inputid = cardinput.getInputID();
02631     QMap<uint, uint> grpcnt;
02632     vector<QString>  names;
02633     vector<uint>     grpid;
02634     vector<uint>     selected_groupids;
02635 
02636     names.push_back(QObject::tr("Generic"));
02637     grpid.push_back(0);
02638     grpcnt[0]++;
02639 
02640     MSqlQuery query(MSqlQuery::InitCon());
02641     query.prepare(
02642         "SELECT cardinputid, inputgroupid, inputgroupname "
02643         "FROM inputgroup "
02644         "ORDER BY inputgroupid, cardinputid, inputgroupname");
02645 
02646     if (!query.exec())
02647     {
02648         MythDB::DBError("InputGroup::Load()", query);
02649     }
02650     else
02651     {
02652         while (query.next())
02653         {
02654             uint groupid = query.value(1).toUInt();
02655             if (inputid && (query.value(0).toUInt() == inputid))
02656                 selected_groupids.push_back(groupid);
02657 
02658             grpcnt[groupid]++;
02659 
02660             if (grpcnt[groupid] == 1)
02661             {
02662                 names.push_back(query.value(2).toString());
02663                 grpid.push_back(groupid);
02664             }
02665         }
02666     }
02667 
02668     // makes sure we select something
02669     groupid = 0;
02670     if (groupnum < selected_groupids.size())
02671         groupid = selected_groupids[groupnum];
02672 
02673 #if 0
02674     LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2")
02675             .arg(groupnum).arg(groupid));
02676     {
02677         QString msg;
02678         for (uint i = 0; i < selected_groupids.size(); i++)
02679             msg += QString("%1 ").arg(selected_groupids[i]);
02680         LOG(VB_GENERAL, LOG_DEBUG, msg);
02681     }
02682 #endif
02683 
02684     // add selections to combobox
02685     clearSelections();
02686     uint index = 0;
02687     for (uint i = 0; i < names.size(); i++)
02688     {
02689         bool sel = (groupid == grpid[i]);
02690         index = (sel) ? i : index;
02691 
02692 #if 0
02693         LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4")
02694                 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F"));
02695 #endif
02696 
02697         addSelection(names[i], QString::number(grpid[i]), sel);
02698     }
02699 
02700 #if 0
02701     LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index));
02702 #endif
02703 
02704     if (!names.empty())
02705         setValue(index);
02706 }
02707 
02708 class QuickTune : public ComboBoxSetting, public CardInputDBStorage
02709 {
02710   public:
02711     QuickTune(const CardInput &parent) :
02712         ComboBoxSetting(this), CardInputDBStorage(this, parent, "quicktune")
02713     {
02714         setLabel(QObject::tr("Use quick tuning"));
02715         addSelection(QObject::tr("Never"),        "0", true);
02716         addSelection(QObject::tr("Live TV only"), "1", false);
02717         addSelection(QObject::tr("Always"),       "2", false);
02718         setHelpText(QObject::tr(
02719                         "If enabled, MythTV will tune using only the "
02720                         "MPEG program number. The program numbers "
02721                         "change more often than DVB or ATSC tuning "
02722                         "parameters, so this is slightly less reliable. "
02723                         "This will also inhibit EIT gathering during "
02724                         "Live TV and recording."));
02725     };
02726 };
02727 
02728 class ExternalChannelCommand :
02729     public LineEditSetting, public CardInputDBStorage
02730 {
02731   public:
02732     ExternalChannelCommand(const CardInput &parent) :
02733         LineEditSetting(this),
02734         CardInputDBStorage(this, parent, "externalcommand")
02735     {
02736         setLabel(QObject::tr("External channel change command"));
02737         setValue("");
02738         setHelpText(QObject::tr("If specified, this command will be run to "
02739                     "change the channel for inputs which have an external "
02740                     "tuner device such as a cable box. The first argument "
02741                     "will be the channel number."));
02742     };
02743 };
02744 
02745 class PresetTuner : public LineEditSetting, public CardInputDBStorage
02746 {
02747   public:
02748     PresetTuner(const CardInput &parent) :
02749         LineEditSetting(this),
02750         CardInputDBStorage(this, parent, "tunechan")
02751     {
02752         setLabel(QObject::tr("Preset tuner to channel"));
02753         setValue("");
02754         setHelpText(QObject::tr("Leave this blank unless you have an external "
02755                     "tuner that is connected to the tuner input of your card. "
02756                     "If so, you will need to specify the preset channel for "
02757                     "the signal (normally 3 or 4)."));
02758     };
02759 };
02760 
02761 void StartingChannel::SetSourceID(const QString &sourceid)
02762 {
02763     clearSelections();
02764     if (sourceid.isEmpty() || !sourceid.toUInt())
02765         return;
02766 
02767     // Get the existing starting channel
02768     QString startChan = CardUtil::GetStartingChannel(getInputID());
02769 
02770     DBChanList channels = ChannelUtil::GetAllChannels(sourceid.toUInt());
02771 
02772     if (channels.empty())
02773     {
02774         addSelection(tr("Please add channels to this source"),
02775                      startChan.isEmpty() ? "" : startChan);
02776         return;
02777     }
02778 
02779     // If there are channels sort them, then add theme
02780     // (selecting the old start channel if it is there).
02781     QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
02782     ChannelUtil::SortChannels(channels, order);
02783     bool has_visible = false, found_existing = false;
02784     for (uint i = 0; i < channels.size() && !has_visible; i++)
02785         has_visible |= channels[i].visible;
02786 
02787     for (uint i = 0; i < channels.size(); i++)
02788     {
02789         const QString channum = channels[i].channum;
02790         bool sel = channum == startChan;
02791         if (!has_visible || channels[i].visible || sel)
02792         {
02793             addSelection(channum, channum, sel);
02794             found_existing |= sel;
02795         }
02796     }
02797 }
02798 
02799 class InputPriority : public SpinBoxSetting, public CardInputDBStorage
02800 {
02801   public:
02802     InputPriority(const CardInput &parent) :
02803         SpinBoxSetting(this, -99, 99, 1),
02804         CardInputDBStorage(this, parent, "recpriority")
02805     {
02806         setLabel(QObject::tr("Input priority"));
02807         setValue(0);
02808         setHelpText(QObject::tr("If the input priority is not equal for "
02809                     "all inputs, the scheduler may choose to record a show "
02810                     "at a later time so that it can record on an input with "
02811                     "a higher value."));
02812     };
02813 };
02814 
02815 class ScheduleOrder : public SpinBoxSetting, public CardInputDBStorage
02816 {
02817   public:
02818     ScheduleOrder(const CardInput &parent, int _value) :
02819         SpinBoxSetting(this, 0, 99, 1),
02820         CardInputDBStorage(this, parent, "schedorder")
02821     {
02822         setLabel(QObject::tr("Schedule order"));
02823         setValue(_value);
02824         setHelpText(QObject::tr("If priorities and other factors are equal "
02825                                 "the scheduler will choose the available "
02826                                 "input with the lowest, non-zero value.  "
02827                                 "Setting this value to zero will make the "
02828                                 "input unavailable to the scheduler."));
02829     };
02830 };
02831 
02832 class LiveTVOrder : public SpinBoxSetting, public CardInputDBStorage
02833 {
02834   public:
02835     LiveTVOrder(const CardInput &parent, int _value) :
02836         SpinBoxSetting(this, 0, 99, 1),
02837         CardInputDBStorage(this, parent, "livetvorder")
02838     {
02839         setLabel(QObject::tr("Live TV order"));
02840         setValue(_value);
02841         setHelpText(QObject::tr("When entering Live TV, the available, local "
02842                                 "input with the lowest, non-zero value will "
02843                                 "be used.  If no local inputs are available, "
02844                                 "the available, remote input with the lowest, "
02845                                 "non-zero value will be used.  "
02846                                 "Setting this value to zero will make the "
02847                                 "input unavailable to live TV."));
02848     };
02849 };
02850 
02851 class DishNetEIT : public CheckBoxSetting, public CardInputDBStorage
02852 {
02853   public:
02854     DishNetEIT(const CardInput &parent) :
02855         CheckBoxSetting(this),
02856         CardInputDBStorage(this, parent, "dishnet_eit")
02857     {
02858         setLabel(QObject::tr("Use DishNet long-term EIT data"));
02859         setValue(false);
02860         setHelpText(
02861             QObject::tr(
02862                 "If you point your satellite dish toward DishNet's birds, "
02863                 "you may wish to enable this feature. For best results, "
02864                 "enable general EIT collection as well."));
02865     };
02866 };
02867 
02868 CardInput::CardInput(bool isDTVcard,  bool isDVBcard,
02869                      bool isNewInput, int _cardid) :
02870     id(new ID()),
02871     cardid(new CardID(*this)),
02872     inputname(new InputName(*this)),
02873     sourceid(new SourceID(*this)),
02874     startchan(new StartingChannel(*this)),
02875     scan(new TransButtonSetting()),
02876     srcfetch(new TransButtonSetting()),
02877     externalInputSettings(new DiSEqCDevSettings()),
02878     inputgrp0(new InputGroup(*this, 0)),
02879     inputgrp1(new InputGroup(*this, 1))
02880 {
02881     addChild(id);
02882 
02883     if (CardUtil::IsInNeedOfExternalInputConf(_cardid))
02884     {
02885         addChild(new DTVDeviceConfigGroup(*externalInputSettings,
02886                                           _cardid, isNewInput));
02887     }
02888 
02889     ConfigurationGroup *basic =
02890         new VerticalConfigurationGroup(false, false, true, true);
02891 
02892     basic->setLabel(QObject::tr("Connect source to input"));
02893 
02894     basic->addChild(cardid);
02895     basic->addChild(inputname);
02896     basic->addChild(new InputDisplayName(*this));
02897     basic->addChild(sourceid);
02898 
02899     if (!isDTVcard)
02900     {
02901         basic->addChild(new ExternalChannelCommand(*this));
02902         basic->addChild(new PresetTuner(*this));
02903     }
02904     else
02905     {
02906         ConfigurationGroup *chgroup =
02907             new HorizontalConfigurationGroup(false, false, true, true);
02908         chgroup->addChild(new QuickTune(*this));
02909         if (isDVBcard)
02910             chgroup->addChild(new DishNetEIT(*this));
02911         basic->addChild(chgroup);
02912     }
02913 
02914     scan->setLabel(tr("Scan for channels"));
02915     scan->setHelpText(
02916         tr("Use channel scanner to find channels for this input."));
02917 
02918     srcfetch->setLabel(tr("Fetch channels from listings source"));
02919     srcfetch->setHelpText(
02920         tr("This uses the listings data source to "
02921            "provide the channels for this input.") + " " +
02922         tr("This can take a long time to run."));
02923 
02924     ConfigurationGroup *sgrp =
02925         new HorizontalConfigurationGroup(false, false, true, true);
02926     sgrp->addChild(scan);
02927     sgrp->addChild(srcfetch);
02928     basic->addChild(sgrp);
02929 
02930     basic->addChild(startchan);
02931 
02932     addChild(basic);
02933 
02934     ConfigurationGroup *interact =
02935         new VerticalConfigurationGroup(false, false, true, true);
02936 
02937     interact->setLabel(QObject::tr("Interactions between inputs"));
02938     interact->addChild(new InputPriority(*this));
02939     interact->addChild(new ScheduleOrder(*this, _cardid));
02940     interact->addChild(new LiveTVOrder(*this, _cardid));
02941 
02942     TransButtonSetting *ingrpbtn = new TransButtonSetting("newgroup");
02943     ingrpbtn->setLabel(QObject::tr("Create a New Input Group"));
02944     ingrpbtn->setHelpText(
02945         QObject::tr("Input groups are only needed when two or more cards "
02946                     "share the same resource such as a FireWire card and "
02947                     "an analog card input controlling the same set top box."));
02948     interact->addChild(ingrpbtn);
02949     interact->addChild(inputgrp0);
02950     interact->addChild(inputgrp1);
02951 
02952     addChild(interact);
02953 
02954     setObjectName("CardInput");
02955     SetSourceID("-1");
02956 
02957     connect(scan,     SIGNAL(pressed()), SLOT(channelScanner()));
02958     connect(srcfetch, SIGNAL(pressed()), SLOT(sourceFetch()));
02959     connect(sourceid, SIGNAL(valueChanged(const QString&)),
02960             startchan,SLOT(  SetSourceID (const QString&)));
02961     connect(sourceid, SIGNAL(valueChanged(const QString&)),
02962             this,     SLOT(  SetSourceID (const QString&)));
02963     connect(ingrpbtn, SIGNAL(pressed(QString)),
02964             this,     SLOT(  CreateNewInputGroup()));
02965 }
02966 
02967 CardInput::~CardInput()
02968 {
02969     if (externalInputSettings)
02970     {
02971         delete externalInputSettings;
02972         externalInputSettings = NULL;
02973     }
02974 }
02975 
02976 void CardInput::SetSourceID(const QString &sourceid)
02977 {
02978     uint cid = cardid->getValue().toUInt();
02979     QString raw_card_type = CardUtil::GetRawCardType(cid);
02980     bool enable = (sourceid.toInt() > 0);
02981     scan->setEnabled(enable && !raw_card_type.isEmpty() &&
02982                      !CardUtil::IsUnscanable(raw_card_type));
02983     srcfetch->setEnabled(enable);
02984 }
02985 
02986 QString CardInput::getSourceName(void) const
02987 {
02988     return sourceid->getSelectionLabel();
02989 }
02990 
02991 void CardInput::CreateNewInputGroup(void)
02992 {
02993     QString new_name = QString::null;
02994     QString tmp_name = QString::null;
02995 
02996     inputgrp0->Save();
02997     inputgrp1->Save();
02998 
02999     while (true)
03000     {
03001         tmp_name = "";
03002         bool ok = MythPopupBox::showGetTextPopup(
03003             GetMythMainWindow(), tr("Create Input Group"),
03004             tr("Enter new group name"), tmp_name);
03005 
03006         new_name = tmp_name;
03007 
03008         if (!ok)
03009             return;
03010 
03011         if (new_name.isEmpty())
03012         {
03013             MythPopupBox::showOkPopup(
03014                 GetMythMainWindow(), tr("Error"),
03015                 tr("Sorry, this Input Group name cannot be blank."));
03016             continue;
03017         }
03018 
03019         MSqlQuery query(MSqlQuery::InitCon());
03020         query.prepare(
03021             "SELECT inputgroupname "
03022             "FROM inputgroup "
03023             "WHERE inputgroupname = :GROUPNAME");
03024         query.bindValue(":GROUPNAME", new_name);
03025 
03026         if (!query.exec())
03027         {
03028             MythDB::DBError("CreateNewInputGroup 1", query);
03029             return;
03030         }
03031 
03032         if (query.next())
03033         {
03034             MythPopupBox::showOkPopup(
03035                 GetMythMainWindow(), tr("Error"),
03036                 tr("Sorry, this Input Group name is already in use."));
03037             continue;
03038         }
03039 
03040         break;
03041     }
03042 
03043     uint inputgroupid = CardUtil::CreateInputGroup(new_name);
03044 
03045     inputgrp0->Load();
03046     inputgrp1->Load();
03047 
03048     if (!inputgrp0->getValue().toUInt())
03049     {
03050         inputgrp0->setValue(
03051             inputgrp0->getValueIndex(QString::number(inputgroupid)));
03052     }
03053     else
03054     {
03055         inputgrp1->setValue(
03056             inputgrp1->getValueIndex(QString::number(inputgroupid)));
03057     }
03058 }
03059 
03060 void CardInput::channelScanner(void)
03061 {
03062     uint srcid = sourceid->getValue().toUInt();
03063     uint crdid = cardid->getValue().toUInt();
03064     QString in = inputname->getValue();
03065 
03066 #ifdef USING_BACKEND
03067     uint num_channels_before = SourceUtil::GetChannelCount(srcid);
03068 
03069     Save(); // save info for scanner.
03070 
03071     QString cardtype = CardUtil::GetRawCardType(crdid);
03072     if (CardUtil::IsUnscanable(cardtype))
03073     {
03074         LOG(VB_GENERAL, LOG_ERR, 
03075             QString("Sorry, %1 cards do not yet support scanning.")
03076                 .arg(cardtype));
03077         return;
03078     }
03079 
03080     ScanWizard *scanwizard = new ScanWizard(srcid, crdid, in);
03081     scanwizard->exec(false, true);
03082     scanwizard->deleteLater();
03083 
03084     if (SourceUtil::GetChannelCount(srcid))
03085         startchan->SetSourceID(QString::number(srcid));
03086     if (num_channels_before)
03087     {
03088         startchan->Load();
03089         startchan->Save();
03090     }
03091 #else
03092     LOG(VB_GENERAL, LOG_ERR, "You must compile the backend "
03093                              "to be able to scan for channels");
03094 #endif
03095 }
03096 
03097 void CardInput::sourceFetch(void)
03098 {
03099     uint srcid = sourceid->getValue().toUInt();
03100     uint crdid = cardid->getValue().toUInt();
03101 
03102     uint num_channels_before = SourceUtil::GetChannelCount(srcid);
03103 
03104     if (crdid && srcid)
03105     {
03106         Save(); // save info for fetch..
03107 
03108         QString cardtype = CardUtil::GetRawCardType(crdid);
03109 
03110         if (!CardUtil::IsCableCardPresent(crdid, cardtype) &&
03111             !CardUtil::IsUnscanable(cardtype) &&
03112             !CardUtil::IsEncoder(cardtype)    &&
03113             !num_channels_before)
03114         {
03115             LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to "
03116                                      "scan for channels first.");
03117             return;
03118         }
03119 
03120         SourceUtil::UpdateChannelsFromListings(srcid, cardtype);
03121     }
03122 
03123     if (SourceUtil::GetChannelCount(srcid))
03124         startchan->SetSourceID(QString::number(srcid));
03125     if (num_channels_before)
03126     {
03127         startchan->Load();
03128         startchan->Save();
03129     }
03130 }
03131 
03132 QString CardInputDBStorage::GetWhereClause(MSqlBindings &bindings) const
03133 {
03134     QString cardinputidTag(":WHERECARDINPUTID");
03135 
03136     QString query("cardinputid = " + cardinputidTag);
03137 
03138     bindings.insert(cardinputidTag, m_parent.getInputID());
03139 
03140     return query;
03141 }
03142 
03143 QString CardInputDBStorage::GetSetClause(MSqlBindings &bindings) const
03144 {
03145     QString cardinputidTag(":SETCARDINPUTID");
03146     QString colTag(":SET" + GetColumnName().toUpper());
03147 
03148     QString query("cardinputid = " + cardinputidTag + ", " +
03149             GetColumnName() + " = " + colTag);
03150 
03151     bindings.insert(cardinputidTag, m_parent.getInputID());
03152     bindings.insert(colTag, user->GetDBValue());
03153 
03154     return query;
03155 }
03156 
03157 void CardInput::loadByID(int inputid)
03158 {
03159     id->setValue(inputid);
03160     externalInputSettings->Load(inputid);
03161     ConfigurationWizard::Load();
03162 }
03163 
03164 void CardInput::loadByInput(int _cardid, QString _inputname)
03165 {
03166     MSqlQuery query(MSqlQuery::InitCon());
03167     query.prepare("SELECT cardinputid FROM cardinput "
03168                   "WHERE cardid = :CARDID AND inputname = :INPUTNAME");
03169     query.bindValue(":CARDID", _cardid);
03170     query.bindValue(":INPUTNAME", _inputname);
03171 
03172     if (query.exec() && query.isActive() && query.next())
03173     {
03174         loadByID(query.value(0).toInt());
03175     }
03176     else
03177     { // create new input connection
03178         Load();
03179         cardid->setValue(QString::number(_cardid));
03180         inputname->setValue(_inputname);
03181     }
03182 }
03183 
03184 void CardInput::Save(void)
03185 {
03186 
03187     if (sourceid->getValue() == "0")
03188     {
03189         // "None" is represented by the lack of a row
03190         MSqlQuery query(MSqlQuery::InitCon());
03191         query.prepare("DELETE FROM cardinput WHERE cardinputid = :INPUTID");
03192         query.bindValue(":INPUTID", getInputID());
03193         if (!query.exec())
03194             MythDB::DBError("CardInput::Save", query);
03195     }
03196     else
03197     {
03198         ConfigurationWizard::Save();
03199         externalInputSettings->Store(getInputID());
03200     }
03201 
03202     // Handle any cloning we may need to do
03203     uint src_cardid = cardid->getValue().toUInt();
03204     QString type = CardUtil::GetRawCardType(src_cardid);
03205     if (CardUtil::IsTunerSharingCapable(type))
03206     {
03207         vector<uint> clones = CardUtil::GetCloneCardIDs(src_cardid);
03208         if (clones.size() && CardUtil::CreateInputGroupIfNeeded(src_cardid))
03209         {
03210             for (uint i = 0; i < clones.size(); i++)
03211                 CardUtil::CloneCard(src_cardid, clones[i]);
03212         }
03213     }
03214 
03215     // Delete any orphaned inputs
03216     CardUtil::DeleteOrphanInputs();
03217     // Delete any unused input groups
03218     CardUtil::UnlinkInputGroup(0,0);
03219 }
03220 
03221 int CardInputDBStorage::getInputID(void) const
03222 {
03223     return m_parent.getInputID();
03224 }
03225 
03226 int CaptureCardDBStorage::getCardID(void) const
03227 {
03228     return m_parent.getCardID();
03229 }
03230 
03231 CaptureCardEditor::CaptureCardEditor() : listbox(new ListBoxSetting(this))
03232 {
03233     listbox->setLabel(tr("Capture cards"));
03234     addChild(listbox);
03235 }
03236 
03237 DialogCode CaptureCardEditor::exec(void)
03238 {
03239     while (ConfigurationDialog::exec() == kDialogCodeAccepted)
03240         edit();
03241 
03242     return kDialogCodeRejected;
03243 }
03244 
03245 void CaptureCardEditor::Load(void)
03246 {
03247     listbox->clearSelections();
03248     listbox->addSelection(QObject::tr("(New capture card)"), "0");
03249     listbox->addSelection(QObject::tr("(Delete all capture cards on %1)")
03250                           .arg(gCoreContext->GetHostName()), "-1");
03251     listbox->addSelection(QObject::tr("(Delete all capture cards)"), "-2");
03252     CaptureCard::fillSelections(listbox);
03253 }
03254 
03255 MythDialog* CaptureCardEditor::dialogWidget(MythMainWindow* parent,
03256                                             const char* widgetName)
03257 {
03258     dialog = ConfigurationDialog::dialogWidget(parent, widgetName);
03259     connect(dialog, SIGNAL(menuButtonPressed()), this, SLOT(menu()));
03260     connect(dialog, SIGNAL(editButtonPressed()), this, SLOT(edit()));
03261     connect(dialog, SIGNAL(deleteButtonPressed()), this, SLOT(del()));
03262     return dialog;
03263 }
03264 
03265 void CaptureCardEditor::menu(void)
03266 {
03267     if (!listbox->getValue().toInt())
03268     {
03269         CaptureCard cc;
03270         cc.exec();
03271     }
03272     else
03273     {
03274         DialogCode val = MythPopupBox::Show2ButtonPopup(
03275             GetMythMainWindow(),
03276             "",
03277             tr("Capture Card Menu"),
03278             tr("Edit..."),
03279             tr("Delete..."),
03280             kDialogCodeButton0);
03281 
03282         if (kDialogCodeButton0 == val)
03283             edit();
03284         else if (kDialogCodeButton1 == val)
03285             del();
03286     }
03287 }
03288 
03289 void CaptureCardEditor::edit(void)
03290 {
03291     const int cardid = listbox->getValue().toInt();
03292     if (-1 == cardid)
03293     {
03294         DialogCode val = MythPopupBox::Show2ButtonPopup(
03295             GetMythMainWindow(), "",
03296             tr("Are you sure you want to delete "
03297                "ALL capture cards on %1?").arg(gCoreContext->GetHostName()),
03298             tr("Yes, delete capture cards"),
03299             tr("No, don't"), kDialogCodeButton1);
03300 
03301         if (kDialogCodeButton0 == val)
03302         {
03303             MSqlQuery cards(MSqlQuery::InitCon());
03304 
03305             cards.prepare(
03306                 "SELECT cardid "
03307                 "FROM capturecard "
03308                 "WHERE hostname = :HOSTNAME");
03309             cards.bindValue(":HOSTNAME", gCoreContext->GetHostName());
03310 
03311             if (!cards.exec() || !cards.isActive())
03312             {
03313                 MythPopupBox::showOkPopup(
03314                     GetMythMainWindow(),
03315                     tr("Error getting list of cards for this host"),
03316                     tr("Unable to delete capturecards for %1")
03317                     .arg(gCoreContext->GetHostName()));
03318 
03319                 MythDB::DBError("Selecting cardids for deletion", cards);
03320                 return;
03321             }
03322 
03323             while (cards.next())
03324                 CardUtil::DeleteCard(cards.value(0).toUInt());
03325         }
03326     }
03327     else if (-2 == cardid)
03328     {
03329         DialogCode val = MythPopupBox::Show2ButtonPopup(
03330             GetMythMainWindow(), "",
03331             tr("Are you sure you want to delete "
03332                "ALL capture cards?"),
03333             tr("Yes, delete capture cards"),
03334             tr("No, don't"), kDialogCodeButton1);
03335 
03336         if (kDialogCodeButton0 == val)
03337         {
03338             CardUtil::DeleteAllCards();
03339             Load();
03340         }
03341     }
03342     else
03343     {
03344         CaptureCard cc;
03345         if (cardid)
03346             cc.loadByID(cardid);
03347         cc.exec();
03348     }
03349 }
03350 
03351 void CaptureCardEditor::del(void)
03352 {
03353     DialogCode val = MythPopupBox::Show2ButtonPopup(
03354         GetMythMainWindow(), "",
03355         tr("Are you sure you want to delete this capture card?"),
03356         tr("Yes, delete capture card"),
03357         tr("No, don't"), kDialogCodeButton1);
03358 
03359     if (kDialogCodeButton0 == val)
03360     {
03361         CardUtil::DeleteCard(listbox->getValue().toUInt());
03362         Load();
03363     }
03364 }
03365 
03366 VideoSourceEditor::VideoSourceEditor() : listbox(new ListBoxSetting(this))
03367 {
03368     listbox->setLabel(tr("Video sources"));
03369     addChild(listbox);
03370 }
03371 
03372 MythDialog* VideoSourceEditor::dialogWidget(MythMainWindow* parent,
03373                                             const char* widgetName)
03374 {
03375     dialog = ConfigurationDialog::dialogWidget(parent, widgetName);
03376     connect(dialog, SIGNAL(menuButtonPressed()), this, SLOT(menu()));
03377     connect(dialog, SIGNAL(editButtonPressed()), this, SLOT(edit()));
03378     connect(dialog, SIGNAL(deleteButtonPressed()), this, SLOT(del()));
03379     return dialog;
03380 }
03381 
03382 DialogCode VideoSourceEditor::exec(void)
03383 {
03384     while (ConfigurationDialog::exec() == kDialogCodeAccepted)
03385         edit();
03386 
03387     return kDialogCodeRejected;
03388 }
03389 
03390 void VideoSourceEditor::Load(void)
03391 {
03392     listbox->clearSelections();
03393     listbox->addSelection(QObject::tr("(New video source)"), "0");
03394     listbox->addSelection(QObject::tr("(Delete all video sources)"), "-1");
03395     VideoSource::fillSelections(listbox);
03396 }
03397 
03398 void VideoSourceEditor::menu(void)
03399 {
03400     if (!listbox->getValue().toInt())
03401     {
03402         VideoSource vs;
03403         vs.exec();
03404     }
03405     else
03406     {
03407         DialogCode val = MythPopupBox::Show2ButtonPopup(
03408             GetMythMainWindow(),
03409             "",
03410             tr("Video Source Menu"),
03411             tr("Edit..."),
03412             tr("Delete..."),
03413             kDialogCodeButton0);
03414 
03415         if (kDialogCodeButton0 == val)
03416             edit();
03417         else if (kDialogCodeButton1 == val)
03418             del();
03419     }
03420 }
03421 
03422 void VideoSourceEditor::edit(void)
03423 {
03424     const int sourceid = listbox->getValue().toInt();
03425     if (-1 == sourceid)
03426     {
03427         DialogCode val = MythPopupBox::Show2ButtonPopup(
03428             GetMythMainWindow(), "",
03429             tr("Are you sure you want to delete "
03430                "ALL video sources?"),
03431             tr("Yes, delete video sources"),
03432             tr("No, don't"), kDialogCodeButton1);
03433 
03434         if (kDialogCodeButton0 == val)
03435         {
03436             SourceUtil::DeleteAllSources();
03437             Load();
03438         }
03439     }
03440     else
03441     {
03442         VideoSource vs;
03443         if (sourceid)
03444             vs.loadByID(sourceid);
03445         vs.exec();
03446     }
03447 }
03448 
03449 void VideoSourceEditor::del()
03450 {
03451     DialogCode val = MythPopupBox::Show2ButtonPopup(
03452         GetMythMainWindow(), "",
03453         tr("Are you sure you want to delete "
03454            "this video source?"),
03455         tr("Yes, delete video source"),
03456         tr("No, don't"),
03457         kDialogCodeButton1);
03458 
03459     if (kDialogCodeButton0 == val)
03460     {
03461         SourceUtil::DeleteSource(listbox->getValue().toUInt());
03462         Load();
03463     }
03464 }
03465 
03466 CardInputEditor::CardInputEditor() : listbox(new ListBoxSetting(this))
03467 {
03468     listbox->setLabel(tr("Input connections"));
03469     addChild(listbox);
03470 }
03471 
03472 DialogCode CardInputEditor::exec(void)
03473 {
03474     while (ConfigurationDialog::exec() == kDialogCodeAccepted)
03475     {
03476         if (!listbox)
03477             return kDialogCodeRejected;
03478 
03479         if (cardinputs.empty())
03480             return kDialogCodeRejected;
03481 
03482         int val = listbox->getValue().toInt();
03483 
03484         if (cardinputs[val])
03485             cardinputs[val]->exec();
03486     }
03487 
03488     return kDialogCodeRejected;
03489 }
03490 
03491 void CardInputEditor::Load(void)
03492 {
03493     cardinputs.clear();
03494     listbox->clearSelections();
03495 
03496     // We do this manually because we want custom labels.  If
03497     // SelectSetting provided a facility to edit the labels, we
03498     // could use CaptureCard::fillSelections
03499 
03500     MSqlQuery query(MSqlQuery::InitCon());
03501     query.prepare(
03502         "SELECT cardid, videodevice, cardtype "
03503         "FROM capturecard "
03504         "WHERE hostname = :HOSTNAME "
03505         "ORDER BY cardid");
03506     query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
03507 
03508     if (!query.exec())
03509     {
03510         MythDB::DBError("CardInputEditor::load", query);
03511         return;
03512     }
03513 
03514     uint j = 0;
03515     QMap<QString, uint> device_refs;
03516     while (query.next())
03517     {
03518         uint    cardid      = query.value(0).toUInt();
03519         QString videodevice = query.value(1).toString();
03520         QString cardtype    = query.value(2).toString();
03521 
03522         bool sharable = CardUtil::IsTunerSharingCapable(cardtype.toUpper());
03523 
03524         if (sharable && (1 != ++device_refs[videodevice]))
03525             continue;
03526 
03527         QStringList        inputLabels;
03528         vector<CardInput*> cardInputs;
03529 
03530         CardUtil::GetCardInputs(cardid, videodevice, cardtype,
03531                                 inputLabels, cardInputs);
03532 
03533         for (int i = 0; i < inputLabels.size(); i++, j++)
03534         {
03535             cardinputs.push_back(cardInputs[i]);
03536             listbox->addSelection(inputLabels[i], QString::number(j));
03537         }
03538     }
03539 }
03540 
03541 #ifdef USING_DVB
03542 static QString remove_chaff(const QString &name)
03543 {
03544     // Trim off some of the chaff.
03545     QString short_name = name;
03546     if (short_name.left(14) == "LG Electronics")
03547         short_name = short_name.right(short_name.length() - 15);
03548     if (short_name.left(4) == "Oren")
03549         short_name = short_name.right(short_name.length() - 5);
03550     if (short_name.left(8) == "Nextwave")
03551         short_name = short_name.right(short_name.length() - 9);
03552     if (short_name.right(8).toLower() == "frontend")
03553         short_name = short_name.left(short_name.length() - 9);
03554     if (short_name.right(7) == "VSB/QAM")
03555         short_name = short_name.left(short_name.length() - 8);
03556     if (short_name.right(3) == "VSB")
03557         short_name = short_name.left(short_name.length() - 4);
03558     if (short_name.right(5) == "DVB-T")
03559         short_name = short_name.left(short_name.length() - 6);
03560 
03561     // It would be infinitely better if DVB allowed us to query
03562     // the vendor ID. But instead we have to guess based on the
03563     // demodulator name. This means cards like the Air2PC HD5000
03564     // and DViCO Fusion HDTV cards are not identified correctly.
03565     short_name = short_name.simplified();
03566     if (short_name.left(7).toLower() == "or51211")
03567         short_name = "pcHDTV HD-2000";
03568     else if (short_name.left(7).toLower() == "or51132")
03569         short_name = "pcHDTV HD-3000";
03570     else if (short_name.left(7).toLower() == "bcm3510")
03571         short_name = "Air2PC v1";
03572     else if (short_name.left(7).toLower() == "nxt2002")
03573         short_name = "Air2PC v2";
03574     else if (short_name.left(7).toLower() == "nxt200x")
03575         short_name = "Air2PC v2";
03576     else if (short_name.left(8).toLower() == "lgdt3302")
03577         short_name = "DViCO HDTV3";
03578     else if (short_name.left(8).toLower() == "lgdt3303")
03579         short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500";
03580 
03581     return short_name;
03582 }
03583 #endif // USING_DVB
03584 
03585 void DVBConfigurationGroup::probeCard(const QString &videodevice)
03586 {
03587     if (videodevice.isEmpty())
03588     {
03589         cardname->setValue("");
03590         cardtype->setValue("");
03591         return;
03592     }
03593 
03594     if (parent.getCardID() && parent.GetRawCardType() != "DVB")
03595     {
03596         cardname->setValue("");
03597         cardtype->setValue("");
03598         return;
03599     }
03600 
03601 #ifdef USING_DVB
03602     QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
03603     QString subtype = CardUtil::ProbeDVBType(videodevice);
03604 
03605     QString err_open  = tr("Could not open card %1").arg(videodevice);
03606     QString err_other = tr("Could not get card info for card %1").arg(videodevice);
03607 
03608     switch (CardUtil::toCardType(subtype))
03609     {
03610         case CardUtil::ERROR_OPEN:
03611             cardname->setValue(err_open);
03612             cardtype->setValue(strerror(errno));
03613             break;
03614         case CardUtil::ERROR_UNKNOWN:
03615             cardname->setValue(err_other);
03616             cardtype->setValue("Unknown error");
03617             break;
03618         case CardUtil::ERROR_PROBE:
03619             cardname->setValue(err_other);
03620             cardtype->setValue(strerror(errno));
03621             break;
03622         case CardUtil::QPSK:
03623             cardtype->setValue("DVB-S");
03624             cardname->setValue(frontend_name);
03625             signal_timeout->setValue(7000);
03626             channel_timeout->setValue(10000);
03627             break;
03628         case CardUtil::DVBS2:
03629             cardtype->setValue("DVB-S2");
03630             cardname->setValue(frontend_name);
03631             signal_timeout->setValue(7000);
03632             channel_timeout->setValue(10000);
03633             break;
03634         case CardUtil::QAM:
03635             cardtype->setValue("DVB-C");
03636             cardname->setValue(frontend_name);
03637             signal_timeout->setValue(1000);
03638             channel_timeout->setValue(3000);
03639             break;
03640         case CardUtil::OFDM:
03641         {
03642             cardtype->setValue("DVB-T");
03643             cardname->setValue(frontend_name);
03644             signal_timeout->setValue(1000);
03645             channel_timeout->setValue(3000);
03646             if (frontend_name.toLower().indexOf("usb") >= 0)
03647             {
03648                 signal_timeout->setValue(40000);
03649                 channel_timeout->setValue(42500);
03650             }
03651 
03652             // slow down tuning for buggy drivers
03653             if ((frontend_name == "DiBcom 3000P/M-C DVB-T") ||
03654                 (frontend_name ==
03655                  "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"))
03656             {
03657                 tuning_delay->setValue(200);
03658             }
03659 
03660 #if 0 // frontends on hybrid DVB-T/Analog cards
03661             QString short_name = remove_chaff(frontend_name);
03662             buttonAnalog->setVisible(
03663                 short_name.left(15).toLower() == "zarlink zl10353" ||
03664                 short_name.toLower() == "wintv hvr 900 m/r: 65008/a1c0" ||
03665                 short_name.left(17).toLower() == "philips tda10046h");
03666 #endif
03667         }
03668         break;
03669         case CardUtil::ATSC:
03670         {
03671             QString short_name = remove_chaff(frontend_name);
03672             cardtype->setValue("ATSC");
03673             cardname->setValue(short_name);
03674             signal_timeout->setValue(500);
03675             channel_timeout->setValue(3000);
03676 
03677             // According to #1779 and #1935 the AverMedia 180 needs
03678             // a 3000 ms signal timeout, at least for QAM tuning.
03679             if (frontend_name == "Nextwave NXT200X VSB/QAM frontend")
03680             {
03681                 signal_timeout->setValue(3000);
03682                 channel_timeout->setValue(5500);
03683             }
03684 
03685 #if 0 // frontends on hybrid DVB-T/Analog cards
03686             if (frontend_name.toLower().indexOf("usb") < 0)
03687             {
03688                 buttonAnalog->setVisible(
03689                     short_name.left(6).toLower() == "pchdtv" ||
03690                     short_name.left(5).toLower() == "dvico" ||
03691                     short_name.left(8).toLower() == "nextwave");
03692             }
03693 #endif
03694         }
03695         break;
03696         default:
03697             break;
03698     }
03699 #else
03700     cardtype->setValue(QString("Recompile with DVB-Support!"));
03701 #endif
03702 }
03703 
03704 TunerCardAudioInput::TunerCardAudioInput(const CaptureCard &parent,
03705                                          QString dev, QString type) :
03706     ComboBoxSetting(this), CaptureCardDBStorage(this, parent, "audiodevice"),
03707     last_device(dev), last_cardtype(type)
03708 {
03709     setLabel(QObject::tr("Audio input"));
03710     int cardid = parent.getCardID();
03711     if (cardid <= 0)
03712         return;
03713 
03714     last_cardtype = CardUtil::GetRawCardType(cardid);
03715     last_device   = CardUtil::GetAudioDevice(cardid);
03716 }
03717 
03718 void TunerCardAudioInput::fillSelections(const QString &device)
03719 {
03720     clearSelections();
03721 
03722     if (device.isEmpty())
03723         return;
03724 
03725     last_device = device;
03726     QStringList inputs =
03727         CardUtil::ProbeAudioInputs(device, last_cardtype);
03728 
03729     for (uint i = 0; i < (uint)inputs.size(); i++)
03730     {
03731         addSelection(inputs[i], QString::number(i),
03732                      last_device == QString::number(i));
03733     }
03734 }
03735 
03736 class DVBExtra : public ConfigurationWizard
03737 {
03738   public:
03739     DVBExtra(DVBConfigurationGroup &parent);
03740     uint GetInstanceCount(void) const
03741     {
03742         return (uint) count->intValue();
03743     }
03744 
03745   private:
03746     InstanceCount *count;
03747 };
03748 
03749 DVBExtra::DVBExtra(DVBConfigurationGroup &parent)
03750     : count(new InstanceCount(parent.parent))
03751 {
03752     VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false);
03753     rec->setLabel(QObject::tr("Recorder Options"));
03754     rec->setUseLabel(false);
03755 
03756     rec->addChild(count);
03757     rec->addChild(new DVBNoSeqStart(parent.parent));
03758     rec->addChild(new DVBOnDemand(parent.parent));
03759     rec->addChild(new DVBEITScan(parent.parent));
03760     rec->addChild(new DVBTuningDelay(parent.parent));
03761 
03762     addChild(rec);
03763 }
03764 
03765 DVBConfigurationGroup::DVBConfigurationGroup(CaptureCard& a_parent) :
03766     VerticalConfigurationGroup(false, true, false, false),
03767     parent(a_parent),
03768     diseqc_tree(new DiSEqCDevTree())
03769 {
03770     cardnum  = new DVBCardNum(parent);
03771     cardname = new DVBCardName();
03772     cardtype = new DVBCardType();
03773 
03774     signal_timeout = new SignalTimeout(parent, 500, 250);
03775     channel_timeout = new ChannelTimeout(parent, 3000, 1750);
03776 
03777     addChild(cardnum);
03778 
03779     HorizontalConfigurationGroup *hg0 =
03780         new HorizontalConfigurationGroup(false, false, true, true);
03781     hg0->addChild(cardname);
03782     hg0->addChild(cardtype);
03783     addChild(hg0);
03784 
03785     addChild(signal_timeout);
03786     addChild(channel_timeout);
03787 
03788     addChild(new EmptyAudioDevice(parent));
03789     addChild(new EmptyVBIDevice(parent));
03790 
03791     TransButtonSetting *buttonRecOpt = new TransButtonSetting();
03792     buttonRecOpt->setLabel(tr("Recording Options"));
03793 
03794     HorizontalConfigurationGroup *advcfg =
03795         new HorizontalConfigurationGroup(false, false, true, true);
03796     advcfg->addChild(buttonRecOpt);
03797     addChild(advcfg);
03798 
03799     diseqc_btn = new TransButtonSetting();
03800     diseqc_btn->setLabel(tr("DiSEqC (Switch, LNB, and Rotor Configuration)"));
03801     diseqc_btn->setHelpText(tr("Input and satellite settings."));
03802 
03803     HorizontalConfigurationGroup *diseqc_cfg =
03804         new HorizontalConfigurationGroup(false, false, true, true);
03805     diseqc_cfg->addChild(diseqc_btn);
03806     diseqc_btn->setVisible(false);
03807     addChild(diseqc_cfg);
03808 
03809     tuning_delay = new DVBTuningDelay(parent);
03810     addChild(tuning_delay);
03811     tuning_delay->setVisible(false);
03812 
03813     connect(cardnum,      SIGNAL(valueChanged(const QString&)),
03814             this,         SLOT(  probeCard   (const QString&)));
03815     connect(diseqc_btn,   SIGNAL(pressed()),
03816             this,         SLOT(  DiSEqCPanel()));
03817     connect(buttonRecOpt, SIGNAL(pressed()),
03818             this,         SLOT(  DVBExtraPanel()));
03819 }
03820 
03821 DVBConfigurationGroup::~DVBConfigurationGroup()
03822 {
03823     if (diseqc_tree)
03824     {
03825         delete diseqc_tree;
03826         diseqc_tree = NULL;
03827     }
03828 }
03829 
03830 void DVBConfigurationGroup::DiSEqCPanel()
03831 {
03832     parent.reload(); // ensure card id is valid
03833 
03834     DTVDeviceTreeWizard diseqcWiz(*diseqc_tree);
03835     diseqcWiz.exec();
03836 }
03837 
03838 void DVBConfigurationGroup::Load(void)
03839 {
03840     VerticalConfigurationGroup::Load();
03841     diseqc_tree->Load(parent.getCardID());
03842     if (cardtype->getValue() == "DVB-S" ||
03843         cardtype->getValue() == "DVB-S2" ||
03844         DiSEqCDevTree::Exists(parent.getCardID()))
03845     {
03846         diseqc_btn->setVisible(true);
03847     }
03848 }
03849 
03850 void DVBConfigurationGroup::Save(void)
03851 {
03852     VerticalConfigurationGroup::Save();
03853     diseqc_tree->Store(parent.getCardID());
03854     DiSEqCDev trees;
03855     trees.InvalidateTrees();
03856 }
03857 
03858 void DVBConfigurationGroup::DVBExtraPanel(void)
03859 {
03860     parent.reload(); // ensure card id is valid
03861 
03862     DVBExtra acw(*this);
03863     acw.exec();
03864     parent.SetInstanceCount(acw.GetInstanceCount());
03865 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends