MythTV  0.26-pre
channelimporter.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 /*
00003  *  Copyright (C) Daniel Kristjansson 2007
00004  *
00005  *  This file is licensed under GPL v2 or (at your option) any later version.
00006  *
00007  */
00008 
00009 #include <QTextStream>
00010 #include <iostream>
00011 
00012 using namespace std;
00013 
00014 // MythTV headers
00015 #include "channelimporter.h"
00016 #include "mythdialogs.h"
00017 #include "mythwidgets.h"
00018 #include "mythdb.h"
00019 #include "mpegstreamdata.h" // for kEncDecrypted
00020 #include "channelutil.h"
00021 
00022 #define LOC QString("ChanImport: ")
00023 
00024 static QString map_str(QString str)
00025 {
00026     if (str.isEmpty())
00027         return "";
00028     str.detach();
00029     return str;
00030 }
00031 
00032 void ChannelImporter::Process(const ScanDTVTransportList &_transports)
00033 {
00034     if (_transports.empty())
00035     {
00036         if (use_gui)
00037         {
00038             LOG(VB_GENERAL, LOG_INFO, LOC + (ChannelUtil::GetChannelCount() ?
00039                                              "No new channels to process" :
00040                                              "No channels to process.."));
00041             MythPopupBox::showOkPopup(
00042                 GetMythMainWindow(), QObject::tr("Channel Importer"),
00043                 ChannelUtil::GetChannelCount()
00044                 ? QObject::tr("Failed to find any new channels!")
00045                 : QObject::tr("Failed to find any channels."));
00046         }
00047         else
00048         {
00049             cout << (ChannelUtil::GetChannelCount() ?
00050                      "No new channels to process" :
00051                      "No channels to process..");
00052         }
00053 
00054         return;
00055     }
00056 
00057     ScanDTVTransportList transports = _transports;
00058 
00059     // Print out each channel
00060     if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
00061     {
00062         cout << "Before processing: " << endl;
00063         ChannelImporterBasicStats infoA = CollectStats(transports);
00064         cout << FormatChannels(transports, infoA).toAscii().constData() << endl;
00065         cout << endl << endl;
00066     }
00067 
00068     uint saved_scan = 0;
00069     if (do_save)
00070         saved_scan = SaveScan(transports);
00071 
00072     CleanupDuplicates(transports);
00073 
00074     FilterServices(transports);
00075 
00076     // Pull in DB info
00077     uint sourceid = transports[0].channels[0].source_id;
00078     ScanDTVTransportList db_trans = GetDBTransports(sourceid, transports);
00079 
00080     // Make sure "Open Cable" channels are marked that way.
00081     FixUpOpenCable(transports);
00082 
00083     // if scan was not aborted prematurely..
00084     uint deleted_count = 0;
00085     if (do_delete)
00086     {
00087         ScanDTVTransportList trans = transports;
00088         for (uint i = 0; i < db_trans.size(); i++)
00089             trans.push_back(db_trans[i]);
00090         deleted_count = DeleteChannels(trans);
00091         if (deleted_count)
00092             transports = trans;
00093     }
00094 
00095     // Determine System Info standards..
00096     ChannelImporterBasicStats info = CollectStats(transports);
00097 
00098     // Determine uniqueness of various naming schemes
00099     ChannelImporterUniquenessStats stats =
00100         CollectUniquenessStats(transports, info);
00101 
00102     // Print out each channel
00103     cout << FormatChannels(transports, info).toAscii().constData() << endl;
00104 
00105     // Create summary
00106     QString msg = GetSummary(transports.size(), info, stats);
00107     cout << msg.toAscii().constData() << endl << endl;
00108 
00109     if (do_insert)
00110         InsertChannels(transports, info);
00111 
00112     if (do_delete && sourceid)
00113         DeleteUnusedTransports(sourceid);
00114 
00115     if (do_delete || do_insert)
00116         ScanInfo::MarkProcessed(saved_scan);
00117 }
00118 
00119 QString ChannelImporter::toString(ChannelType type)
00120 {
00121     switch (type)
00122     {
00123         // non-conflicting
00124         case kATSCNonConflicting: return "ATSC";
00125         case kDVBNonConflicting:  return "DVB";
00126         case kSCTENonConflicting: return "SCTE";
00127         case kMPEGNonConflicting: return "MPEG";
00128         case kNTSCNonConflicting: return "NTSC";
00129         // conflicting
00130         case kATSCConflicting:    return "ATSC";
00131         case kDVBConflicting:     return "DVB";
00132         case kSCTEConflicting:    return "SCTE";
00133         case kMPEGConflicting:    return "MPEG";
00134         case kNTSCConflicting:    return "NTSC";
00135     }
00136     return "Unknown";
00137 }
00138 
00139 uint ChannelImporter::DeleteChannels(
00140     ScanDTVTransportList &transports)
00141 {
00142     vector<uint> off_air_list;
00143     QMap<uint,bool> deleted;
00144 
00145     for (uint i = 0; i < transports.size(); i++)
00146     {
00147         for (uint j = 0; j < transports[i].channels.size(); j++)
00148         {
00149             ChannelInsertInfo chan = transports[i].channels[j];
00150             bool was_in_db = chan.db_mplexid && chan.channel_id;
00151             if (!was_in_db)
00152                 continue;
00153 
00154             if (!chan.in_pmt)
00155                 off_air_list.push_back(i<<16|j);
00156         }
00157     }
00158 
00159     ScanDTVTransportList newlist;
00160     if (off_air_list.empty())
00161     {
00162         return 0;
00163     }
00164 
00165     // ask user whether to delete all or some of these stale channels
00166     //   if some is selected ask about each individually
00167     QString msg = QObject::tr(
00168         "Found %n off-air channel(s).", "", off_air_list.size());
00169     DeleteAction action = QueryUserDelete(msg);
00170     if (kDeleteIgnoreAll == action)
00171         return 0;
00172 
00173     if (kDeleteAll == action)
00174     {
00175         for (uint k = 0; k < off_air_list.size(); k++)
00176         {
00177             int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
00178             ChannelUtil::DeleteChannel(
00179                 transports[i].channels[j].channel_id);
00180             deleted[off_air_list[k]] = true;
00181         }
00182     }
00183     else if (kDeleteInvisibleAll == action)
00184     {
00185         for (uint k = 0; k < off_air_list.size(); k++)
00186         {
00187             int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
00188             int chanid = transports[i].channels[j].channel_id;
00189             QString channum = ChannelUtil::GetChanNum(chanid);
00190             ChannelUtil::SetVisible(chanid, false);
00191             ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum),
00192                                          chanid);
00193         }
00194     }
00195     else
00196     {
00197         // TODO manual delete
00198     }
00199 
00200     // TODO delete encrypted channels when m_fta_only set
00201 
00202     if (deleted.size() == 0)
00203         return 0;
00204 
00205     // Create a new transports list without the deleted channels
00206     for (uint i = 0; i < transports.size(); i++)
00207     {
00208         newlist.push_back(transports[i]);
00209         newlist.back().channels.clear();
00210         for (uint j = 0; j < transports[i].channels.size(); j++)
00211         {
00212             if (!deleted.contains(i<<16|j))
00213             {
00214                 newlist.back().channels.push_back(
00215                     transports[i].channels[j]);
00216             }
00217         }
00218     }
00219 
00220     // TODO print list of stale channels (as deleted if action approved).
00221 
00222     transports = newlist;
00223     return deleted.size();
00224 }
00225 
00226 uint ChannelImporter::DeleteUnusedTransports(uint sourceid)
00227 {
00228     MSqlQuery query(MSqlQuery::InitCon());
00229     query.prepare(
00230         "SELECT mplexid FROM dtv_multiplex "
00231         "WHERE sourceid = :SOURCEID1 AND "
00232         "      mplexid NOT IN "
00233         " (SELECT mplexid "
00234         "  FROM channel "
00235         "  WHERE sourceid = :SOURCEID2)");
00236     query.bindValue(":SOURCEID1", sourceid);
00237     query.bindValue(":SOURCEID2", sourceid);
00238     if (!query.exec())
00239     {
00240         MythDB::DBError("DeleteUnusedTransports() -- select", query);
00241         return 0;
00242     }
00243 
00244     QString msg = QObject::tr("Found %n unused transport(s).", "", query.size());
00245 
00246     LOG(VB_GENERAL, LOG_INFO, LOC + msg);
00247 
00248     if (query.size() == 0)
00249         return 0;
00250 
00251     DeleteAction action = QueryUserDelete(msg);
00252     if (kDeleteIgnoreAll == action)
00253         return 0;
00254 
00255     if (kDeleteAll == action)
00256     {
00257         query.prepare(
00258             "DELETE FROM dtv_multiplex "
00259             "WHERE sourceid = :SOURCEID1 AND "
00260             "      mplexid NOT IN "
00261             " (SELECT mplexid "
00262             "  FROM channel "
00263             "  WHERE sourceid = :SOURCEID2)");
00264         query.bindValue(":SOURCEID1", sourceid);
00265         query.bindValue(":SOURCEID2", sourceid);
00266         if (!query.exec())
00267         {
00268             MythDB::DBError("DeleteUnusedTransports() -- delete", query);
00269             return 0;
00270         }
00271     }
00272     else
00273     {
00274         // TODO manual delete
00275     }
00276     return 0;
00277 }
00278 
00279 void ChannelImporter::InsertChannels(
00280     const ScanDTVTransportList &transports,
00281     const ChannelImporterBasicStats &info)
00282 {
00283     ScanDTVTransportList list = transports;
00284     ScanDTVTransportList filtered;
00285 
00286     // insert/update all channels with non-conflicting channum
00287     // and complete tuning information.
00288 
00289     uint chantype = (uint) kChannelTypeNonConflictingFirst;
00290     for (; chantype <= (uint) kChannelTypeNonConflictingLast; chantype++)
00291     {
00292         ChannelType type = (ChannelType) chantype;
00293         uint new_chan, old_chan;
00294         CountChannels(list, info, type, new_chan, old_chan);
00295 
00296         if (kNTSCNonConflicting == type)
00297             continue;
00298 
00299         if (old_chan)
00300         {
00301             QString msg = QObject::tr("Found %n old %1 channel(s).", "", old_chan)
00302                                     .arg(toString(type));
00303 
00304             UpdateAction action = QueryUserUpdate(msg);
00305             list = UpdateChannels(list, info, action, type, filtered);
00306         }
00307         if (new_chan)
00308         {
00309             QString msg = QObject::tr(
00310                     "Found %n new non-conflicting %1 channel(s).", "", new_chan)
00311                         .arg(toString(type));
00312 
00313             InsertAction action = QueryUserInsert(msg);
00314             list = InsertChannels(list, info, action, type, filtered);
00315         }
00316     }
00317 
00318     if (!is_interactive)
00319         return;
00320 
00321     // sum uniques again
00322     ChannelImporterBasicStats      ninfo  = CollectStats(list);
00323     ChannelImporterUniquenessStats nstats = CollectUniquenessStats(list, ninfo);
00324     cout << endl << endl << "Printing remaining channels" << endl;
00325     cout << FormatChannels(list, ninfo).toAscii().constData() << endl;
00326     cout << GetSummary(list.size(), ninfo, nstats).toAscii().constData()
00327          << endl << endl;
00328 
00329     // if any of the potential uniques is high and inserting
00330     // with those as the channum would result in few conflicts
00331     // ask user if it is ok to to proceed using it as the channum
00332 
00333     // for remaining channels with complete tuning information
00334     // insert channels with contiguous list of numbers as the channums
00335     chantype = (uint) kChannelTypeConflictingFirst;
00336     for (; chantype <= (uint) kChannelTypeConflictingLast; chantype++)
00337     {
00338 
00339         ChannelType type = (ChannelType) chantype;
00340         uint new_chan, old_chan;
00341         CountChannels(list, info, type, new_chan, old_chan);
00342         if (new_chan)
00343         {
00344             QString msg = QObject::tr(
00345                         "Found %n new conflicting %1 channel(s).", "", new_chan)
00346                             .arg(toString(type));
00347 
00348             InsertAction action = QueryUserInsert(msg);
00349             list = InsertChannels(list, info, action, type, filtered);
00350         }
00351         if (old_chan)
00352         {
00353             QString msg = QObject::tr("Found %n conflicting old %1 channel(s).",
00354                                       "", old_chan).arg(toString(type));
00355 
00356             UpdateAction action = QueryUserUpdate(msg);
00357             list = UpdateChannels(list, info, action, type, filtered);
00358         }
00359     }
00360 
00361     // print list of inserted channels
00362     // print list of ignored channels (by ignored reason category)
00363     // print list of invalid channels
00364 }
00365 
00366 ScanDTVTransportList ChannelImporter::InsertChannels(
00367     const ScanDTVTransportList &transports,
00368     const ChannelImporterBasicStats &info,
00369     InsertAction action, ChannelType type,
00370     ScanDTVTransportList &filtered)
00371 {
00372     QString channelFormat = "%1_%2";
00373 
00374     ScanDTVTransportList next_list;
00375 
00376     bool ignore_rest = false;
00377 
00378     // insert all channels with non-conflicting channum
00379     // and complete tuning information.
00380     for (uint i = 0; i < transports.size(); i++)
00381     {
00382         bool created_new_transport = false;
00383         ScanDTVTransport new_transport;
00384         bool created_filter_transport = false;
00385         ScanDTVTransport filter_transport;
00386 
00387         for (uint j = 0; j < transports[i].channels.size(); j++)
00388         {
00389             ChannelInsertInfo chan = transports[i].channels[j];
00390 
00391             bool filter = false, handle = false;
00392             if (!chan.channel_id && (kInsertIgnoreAll == action) &&
00393                 IsType(info, chan, type))
00394             {
00395                 filter = true;
00396             }
00397             else if (!chan.channel_id && IsType(info, chan, type))
00398             {
00399                 handle = true;
00400             }
00401 
00402             if (ignore_rest)
00403             {
00404                 cout<<QString("Skipping Insert: %1")
00405                     .arg(FormatChannel(transports[i], chan))
00406                     .toAscii().constData()<<endl;
00407                 handle = false;
00408             }
00409 
00410             if (handle && kInsertManual == action)
00411             {
00412                 OkCancelType rc = QueryUserInsert(info, transports[i], chan);
00413                 if (kOCTCancelAll == rc)
00414                 {
00415                     ignore_rest = true;
00416                     handle = false;
00417                 }
00418                 else if (kOCTCancel == rc)
00419                 {
00420                     handle = false;
00421                 }
00422             }
00423 
00424             if (handle)
00425             {
00426                 bool conflicting = false;
00427 
00428                 if (chan.chan_num.isEmpty() ||
00429                     ChannelUtil::IsConflicting(chan.chan_num, chan.source_id))
00430                 {
00431                     if ((kATSCNonConflicting == type) ||
00432                         (kATSCConflicting == type))
00433                     {
00434                         chan.chan_num = channelFormat
00435                             .arg(chan.atsc_major_channel)
00436                             .arg(chan.atsc_minor_channel);
00437                     }
00438                     else if (chan.si_standard == "dvb")
00439                     {
00440                         chan.chan_num = QString("%1")
00441                                             .arg(chan.service_id);
00442                     }
00443                     else
00444                     {
00445                         chan.chan_num = QString("%1-%2")
00446                                             .arg(chan.freqid)
00447                                             .arg(chan.service_id);
00448                     }
00449 
00450                     conflicting = ChannelUtil::IsConflicting(
00451                         chan.chan_num, chan.source_id);
00452                 }
00453 
00454                 if (is_interactive &&
00455                     (conflicting || (kChannelTypeConflictingFirst <= type)))
00456                 {
00457                     OkCancelType rc =
00458                         QueryUserResolve(info, transports[i], chan);
00459 
00460                     conflicting = true;
00461                     if (kOCTCancelAll == rc)
00462                         ignore_rest = true;
00463                     else if (kOCTOk == rc)
00464                         conflicting = false;
00465                 }
00466 
00467                 if (conflicting)
00468                 {
00469                     cout<<QString("Skipping Insert: %1")
00470                         .arg(FormatChannel(transports[i], chan))
00471                         .toAscii().constData()<<endl;
00472                     handle = false;
00473                 }
00474             }
00475 
00476             bool inserted = false;
00477             if (handle)
00478             {
00479                 int chanid = ChannelUtil::CreateChanID(
00480                     chan.source_id, chan.chan_num);
00481 
00482                 chan.channel_id = (chanid > 0) ? chanid : chan.channel_id;
00483 
00484                 if (chan.channel_id && !chan.db_mplexid)
00485                 {
00486                     uint tsid = chan.vct_tsid;
00487                     tsid = (tsid) ? tsid : chan.sdt_tsid;
00488                     tsid = (tsid) ? tsid : chan.pat_tsid;
00489                     tsid = (tsid) ? tsid : chan.vct_chan_tsid;
00490 
00491                     chan.db_mplexid = ChannelUtil::CreateMultiplex(
00492                         chan.source_id, transports[i], tsid, chan.orig_netid);
00493                 }
00494 
00495                 if (chan.channel_id && chan.db_mplexid)
00496                 {
00497                     chan.channel_id = chanid;
00498 
00499                     cout<<"Insert("<<chan.si_standard.toAscii().constData()
00500                         <<"): "<<chan.chan_num.toAscii().constData()<<endl;
00501 
00502                     inserted = ChannelUtil::CreateChannel(
00503                         chan.db_mplexid,
00504                         chan.source_id,
00505                         chan.channel_id,
00506                         chan.callsign,
00507                         chan.service_name,
00508                         chan.chan_num,
00509                         chan.service_id,
00510                         chan.atsc_major_channel,
00511                         chan.atsc_minor_channel,
00512                         chan.use_on_air_guide,
00513                         chan.hidden, chan.hidden_in_guide,
00514                         chan.freqid,
00515                         QString::null,
00516                         QString::null,
00517                         QString::null,
00518                         chan.default_authority);
00519                 }
00520             }
00521 
00522             if (filter)
00523             {
00524                 if (!created_filter_transport)
00525                 {
00526                     filter_transport = transports[i];
00527                     filter_transport.channels.clear();
00528                     created_filter_transport = true;
00529                 }
00530                 filter_transport.channels.push_back(transports[i].channels[j]);
00531             }
00532             else if (!inserted)
00533             {
00534                 if (!created_new_transport)
00535                 {
00536                     new_transport = transports[i];
00537                     new_transport.channels.clear();
00538                     created_new_transport = true;
00539                 }
00540                 new_transport.channels.push_back(transports[i].channels[j]);
00541             }
00542         }
00543 
00544         if (created_filter_transport)
00545             filtered.push_back(filter_transport);
00546 
00547         if (created_new_transport)
00548             next_list.push_back(new_transport);
00549     }
00550 
00551     return next_list;
00552 }
00553 
00554 ScanDTVTransportList ChannelImporter::UpdateChannels(
00555     const ScanDTVTransportList &transports,
00556     const ChannelImporterBasicStats &info,
00557     UpdateAction action, ChannelType type,
00558     ScanDTVTransportList &filtered)
00559 {
00560     QString channelFormat = "%1_%2";
00561     bool renameChannels = false;
00562 
00563     ScanDTVTransportList next_list;
00564 
00565     // update all channels with non-conflicting channum
00566     // and complete tuning information.
00567     for (uint i = 0; i < transports.size(); i++)
00568     {
00569         bool created_transport = false;
00570         ScanDTVTransport new_transport;
00571         bool created_filter_transport = false;
00572         ScanDTVTransport filter_transport;
00573 
00574         for (uint j = 0; j < transports[i].channels.size(); j++)
00575         {
00576             ChannelInsertInfo chan = transports[i].channels[j];
00577 
00578             bool filter = false, handle = false;
00579             if (chan.channel_id && (kUpdateIgnoreAll == action) &&
00580                 IsType(info, chan, type))
00581             {
00582                 filter = true;
00583             }
00584             else if (chan.channel_id && IsType(info, chan, type))
00585             {
00586                 handle = true;
00587             }
00588 
00589             if (handle)
00590             {
00591                 bool conflicting = false;
00592 
00593                 if (chan.chan_num.isEmpty() || renameChannels ||
00594                     ChannelUtil::IsConflicting(
00595                         chan.chan_num, chan.source_id, chan.channel_id))
00596                 {
00597                     if (kATSCNonConflicting == type)
00598                     {
00599                         chan.chan_num = channelFormat
00600                             .arg(chan.atsc_major_channel)
00601                             .arg(chan.atsc_minor_channel);
00602                     }
00603                     else if (chan.si_standard == "dvb")
00604                     {
00605                         chan.chan_num = QString("%1")
00606                                             .arg(chan.service_id);
00607                     }
00608                     else
00609                     {
00610                         chan.chan_num = QString("%1-%2")
00611                                             .arg(chan.freqid)
00612                                             .arg(chan.service_id);
00613                     }
00614 
00615                     conflicting = ChannelUtil::IsConflicting(
00616                         chan.chan_num, chan.source_id, chan.channel_id);
00617                 }
00618 
00619                 if (conflicting)
00620                 {
00621                     cout<<"Skipping Update("
00622                         <<chan.si_standard.toAscii().constData()<<"): "
00623                         <<chan.chan_num.toAscii().constData()<<endl;
00624                     handle = false;
00625                 }
00626             }
00627 
00628             if (is_interactive && (kUpdateManual == action))
00629             {
00630                 // TODO Ask user how to update this channel..
00631             }
00632 
00633             bool updated = false;
00634             if (handle)
00635             {
00636                 cout<<"Update("<<chan.si_standard.toAscii().constData()<<"): "
00637                     <<chan.chan_num.toAscii().constData()<<endl;
00638 
00639                 ChannelUtil::UpdateInsertInfoFromDB(chan);
00640 
00641                 updated = ChannelUtil::UpdateChannel(
00642                     chan.db_mplexid,
00643                     chan.source_id,
00644                     chan.channel_id,
00645                     chan.callsign,
00646                     chan.service_name,
00647                     chan.chan_num,
00648                     chan.service_id,
00649                     chan.atsc_major_channel,
00650                     chan.atsc_minor_channel,
00651                     chan.use_on_air_guide,
00652                     chan.hidden, chan.hidden_in_guide,
00653                     chan.freqid,
00654                     QString::null,
00655                     QString::null,
00656                     QString::null,
00657                     chan.default_authority);
00658             }
00659 
00660             if (filter)
00661             {
00662                 if (!created_filter_transport)
00663                 {
00664                     filter_transport = transports[i];
00665                     filter_transport.channels.clear();
00666                     created_filter_transport = true;
00667                 }
00668                 filter_transport.channels.push_back(transports[i].channels[j]);
00669             }
00670             else if (!updated)
00671             {
00672                 if (!created_transport)
00673                 {
00674                     new_transport = transports[i];
00675                     new_transport.channels.clear();
00676                     created_transport = true;
00677                 }
00678                 new_transport.channels.push_back(transports[i].channels[j]);
00679             }
00680         }
00681 
00682         if (created_filter_transport)
00683             filtered.push_back(filter_transport);
00684 
00685         if (created_transport)
00686             next_list.push_back(new_transport);
00687     }
00688 
00689     return next_list;
00690 }
00691 
00692 void ChannelImporter::CleanupDuplicates(ScanDTVTransportList &transports) const
00693 {
00694     ScanDTVTransportList no_dups;
00695 
00696     DTVTunerType tuner_type = DTVTunerType::kTunerTypeATSC;
00697     if (!transports.empty())
00698         tuner_type = transports[0].tuner_type;
00699 
00700     bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
00701                     (DTVTunerType::kTunerTypeDVBS2 == tuner_type));
00702 
00703     uint freq_mult = (is_dvbs) ? 1 : 1000;
00704 
00705     vector<bool> ignore;
00706     ignore.resize(transports.size());
00707     for (uint i = 0; i < transports.size(); i++)
00708     {
00709         if (ignore[i])
00710             continue;
00711 
00712         for (uint j = i+1; j < transports.size(); j++)
00713         {
00714             if (!transports[i].IsEqual(
00715                     tuner_type, transports[j], 500 * freq_mult))
00716             {
00717                 continue;
00718             }
00719 
00720             for (uint k = 0; k < transports[j].channels.size(); k++)
00721             {
00722                 bool found_same = false;
00723                 for (uint l = 0; l < transports[i].channels.size(); l++)
00724                 {
00725                     if (transports[j].channels[k].IsSameChannel(
00726                             transports[i].channels[l]))
00727                     {
00728                         found_same = true;
00729                         transports[i].channels[l].ImportExtraInfo(
00730                             transports[j].channels[k]);
00731                     }
00732                 }
00733                 if (!found_same)
00734                     transports[i].channels.push_back(transports[j].channels[k]);
00735             }
00736             ignore[j] = true;
00737         }
00738         no_dups.push_back(transports[i]);
00739     }
00740 
00741     transports = no_dups;
00742 }
00743 
00744 void ChannelImporter::FilterServices(ScanDTVTransportList &transports) const
00745 {
00746     bool require_av = (m_service_requirements & kRequireAV) == kRequireAV;
00747     bool require_a  = m_service_requirements & kRequireAudio;
00748 
00749     for (uint i = 0; i < transports.size(); i++)
00750     {
00751         ChannelInsertInfoList filtered;
00752         for (uint k = 0; k < transports[i].channels.size(); k++)
00753         {
00754             if (m_fta_only && transports[i].channels[k].is_encrypted &&
00755                 transports[i].channels[k].decryption_status != kEncDecrypted)
00756                 continue;
00757 
00758             if (require_a && transports[i].channels[k].is_data_service)
00759                 continue;
00760 
00761             if (require_av && transports[i].channels[k].is_audio_service)
00762                 continue;
00763 
00764             // filter channels out only in channels.conf, i.e. not found
00765             if (transports[i].channels[k].in_channels_conf &&
00766                 !(transports[i].channels[k].in_pat ||
00767                   transports[i].channels[k].in_pmt ||
00768                   transports[i].channels[k].in_vct ||
00769                   transports[i].channels[k].in_nit ||
00770                   transports[i].channels[k].in_sdt))
00771                 continue;
00772 
00773             filtered.push_back(transports[i].channels[k]);
00774         }
00775         transports[i].channels = filtered;
00776     }
00777 }
00778 
00783 ScanDTVTransportList ChannelImporter::GetDBTransports(
00784     uint sourceid, ScanDTVTransportList &transports) const
00785 {
00786     ScanDTVTransportList not_in_scan;
00787 
00788     DTVTunerType tuner_type = DTVTunerType::kTunerTypeATSC;
00789     if (!transports.empty())
00790         tuner_type = transports[0].tuner_type;
00791 
00792     bool is_dvbs =
00793         (DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
00794         (DTVTunerType::kTunerTypeDVBS2 == tuner_type);
00795 
00796     uint freq_mult = (is_dvbs) ? 1 : 1000;
00797 
00798     MSqlQuery query(MSqlQuery::InitCon());
00799     query.prepare(
00800         "SELECT mplexid "
00801         "FROM dtv_multiplex "
00802         "WHERE sourceid = :SOURCEID "
00803         "GROUP BY mplexid "
00804         "ORDER BY mplexid");
00805     query.bindValue(":SOURCEID", sourceid);
00806 
00807     if (!query.exec())
00808     {
00809         MythDB::DBError("GetDBTransports()", query);
00810         return not_in_scan;
00811     }
00812 
00813     while (query.next())
00814     {
00815         uint mplexid = query.value(0).toUInt();
00816 
00817         ScanDTVTransport newt;
00818         if (!newt.FillFromDB(tuner_type, mplexid))
00819             continue;
00820 
00821         bool newt_found = false;
00822         QMap<uint,bool> found_chan;
00823 
00824         for (uint i = 0; i < transports.size(); i++)
00825         {
00826             if (!transports[i].IsEqual(tuner_type, newt, 500 * freq_mult, true))
00827                 continue;
00828 
00829             transports[i].mplex = mplexid;
00830             newt_found = true;
00831             for (uint j = 0; j < transports[i].channels.size(); j++)
00832             {
00833                 ChannelInsertInfo &chan = transports[i].channels[j];
00834                 for (uint k = 0; k < newt.channels.size(); k++)
00835                 {
00836                     if (newt.channels[k].IsSameChannel(chan))
00837                     {
00838                         found_chan[k] = true;
00839                         chan.db_mplexid = mplexid;
00840                         chan.channel_id = newt.channels[k].channel_id;
00841                     }
00842                 }
00843             }
00844             break;
00845         }
00846 
00847         if (!newt_found)
00848         {
00849             /* XXX HACK -- begin
00850              * disabling adding transponders not found in the scan list
00851              * to the db list to avoid deleting many channels as off air
00852              * for a single transponder scan
00853             not_in_scan.push_back(newt);
00854              * XXX HACK -- end */
00855         }
00856         else
00857         {
00858             ScanDTVTransport tmp = newt;
00859             tmp.channels.clear();
00860 
00861             for (uint k = 0; k < newt.channels.size(); k++)
00862             {
00863                 if (!found_chan[k])
00864                     tmp.channels.push_back(newt.channels[k]);
00865             }
00866 
00867             if (tmp.channels.size())
00868                 not_in_scan.push_back(tmp);
00869         }
00870     }
00871 
00872     return not_in_scan;
00873 }
00874 
00875 void ChannelImporter::FixUpOpenCable(ScanDTVTransportList &transports)
00876 {
00877     ChannelImporterBasicStats info;
00878     for (uint i = 0; i < transports.size(); i++)
00879     {
00880         for (uint j = 0; j < transports[i].channels.size(); j++)
00881         {
00882             ChannelInsertInfo &chan = transports[i].channels[j];
00883             if (((chan.could_be_opencable && (chan.si_standard == "mpeg")) ||
00884                  chan.is_opencable) && !chan.in_vct)
00885             {
00886                 chan.si_standard = "opencable";
00887             }
00888         }
00889     }
00890 }
00891 
00892 ChannelImporterBasicStats ChannelImporter::CollectStats(
00893     const ScanDTVTransportList &transports)
00894 {
00895     ChannelImporterBasicStats info;
00896     for (uint i = 0; i < transports.size(); i++)
00897     {
00898         for (uint j = 0; j < transports[i].channels.size(); j++)
00899         {
00900             const ChannelInsertInfo &chan = transports[i].channels[j];
00901             int enc = (chan.is_encrypted) ?
00902                 ((chan.decryption_status == kEncDecrypted) ? 2 : 1) : 0;
00903             info.atsc_channels[enc] += (chan.si_standard == "atsc");
00904             info.dvb_channels [enc] += (chan.si_standard == "dvb");
00905             info.mpeg_channels[enc] += (chan.si_standard == "mpeg");
00906             info.scte_channels[enc] += (chan.si_standard == "opencable");
00907             info.ntsc_channels[enc] += (chan.si_standard == "ntsc");
00908             if (chan.si_standard != "ntsc")
00909             {
00910                 info.prognum_cnt[chan.service_id]++;
00911                 info.channum_cnt[map_str(chan.chan_num)]++;
00912             }
00913             if (chan.si_standard == "atsc")
00914             {
00915                 info.atscnum_cnt[(chan.atsc_major_channel << 16) |
00916                                  (chan.atsc_minor_channel)]++;
00917                 info.atscmin_cnt[chan.atsc_minor_channel]++;
00918                 info.atscmaj_cnt[chan.atsc_major_channel]++;
00919             }
00920             if (chan.si_standard == "ntsc")
00921             {
00922                 info.atscnum_cnt[(chan.atsc_major_channel << 16) |
00923                                  (chan.atsc_minor_channel)]++;
00924             }
00925         }
00926     }
00927 
00928     return info;
00929 }
00930 
00931 ChannelImporterUniquenessStats ChannelImporter::CollectUniquenessStats(
00932     const ScanDTVTransportList &transports,
00933     const ChannelImporterBasicStats &info)
00934 {
00935     ChannelImporterUniquenessStats stats;
00936 
00937     for (uint i = 0; i < transports.size(); i++)
00938     {
00939         for (uint j = 0; j < transports[i].channels.size(); j++)
00940         {
00941             const ChannelInsertInfo &chan = transports[i].channels[j];
00942             stats.unique_prognum +=
00943                 (info.prognum_cnt[chan.service_id] == 1) ? 1 : 0;
00944             stats.unique_channum +=
00945                 (info.channum_cnt[map_str(chan.chan_num)] == 1) ? 1 : 0;
00946 
00947             if (chan.si_standard == "atsc")
00948             {
00949                 stats.unique_atscnum +=
00950                     (info.atscnum_cnt[(chan.atsc_major_channel << 16) |
00951                                  (chan.atsc_minor_channel)] == 1) ? 1 : 0;
00952                 stats.unique_atscmin +=
00953                     (info.atscmin_cnt[(chan.atsc_minor_channel)] == 1) ? 1 : 0;
00954                 stats.max_atscmajcnt = max(
00955                     stats.max_atscmajcnt,
00956                     info.atscmaj_cnt[chan.atsc_major_channel]);
00957             }
00958         }
00959     }
00960 
00961     stats.unique_total = (stats.unique_prognum + stats.unique_atscnum +
00962                           stats.unique_atscmin + stats.unique_channum);
00963 
00964     return stats;
00965 }
00966 
00967 
00968 QString ChannelImporter::FormatChannel(
00969     const ScanDTVTransport          &transport,
00970     const ChannelInsertInfo         &chan,
00971     const ChannelImporterBasicStats *info)
00972 {
00973     QString msg;
00974     QTextStream ssMsg(&msg);
00975 
00976     ssMsg << transport.modulation.toString().toAscii().constData()
00977           << ":";
00978     ssMsg << transport.frequency << ":";
00979 
00980     QString si_standard = (chan.si_standard=="opencable") ?
00981         QString("scte") : chan.si_standard;
00982 
00983     if (si_standard == "atsc" || si_standard == "scte")
00984         ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9")
00985                   .arg(chan.callsign).arg(chan.chan_num)
00986                   .arg(chan.atsc_major_channel)
00987                   .arg(chan.atsc_minor_channel)
00988                   .arg(chan.service_id)
00989                   .arg(chan.vct_tsid)
00990                   .arg(chan.vct_chan_tsid)
00991                   .arg(chan.pat_tsid)
00992                   .arg(si_standard)).toAscii().constData();
00993     else if (si_standard == "dvb")
00994         ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8")
00995                   .arg(chan.service_name).arg(chan.chan_num)
00996                   .arg(chan.netid).arg(chan.orig_netid)
00997                   .arg(chan.service_id)
00998                   .arg(chan.sdt_tsid)
00999                   .arg(chan.pat_tsid)
01000                   .arg(si_standard)).toAscii().constData();
01001     else
01002         ssMsg << (QString("%1:%2:%3:%4:%5")
01003                   .arg(chan.callsign).arg(chan.chan_num)
01004                   .arg(chan.service_id)
01005                   .arg(chan.pat_tsid)
01006                   .arg(si_standard)).toAscii().constData();
01007 
01008     if (info)
01009     {
01010         ssMsg <<"\t"
01011               << chan.channel_id;
01012     }
01013 
01014     if (info)
01015     {
01016         ssMsg << ":"
01017               << (QString("cnt(pnum:%1,channum:%2)")
01018                   .arg(info->prognum_cnt[chan.service_id])
01019                   .arg(info->channum_cnt[map_str(chan.chan_num)])
01020                   ).toAscii().constData();
01021         if (chan.si_standard == "atsc")
01022         {
01023             ssMsg <<
01024                 (QString(":atsc_cnt(tot:%1,minor:%2)")
01025                  .arg(info->atscnum_cnt[
01026                           (chan.atsc_major_channel << 16) |
01027                           (chan.atsc_minor_channel)])
01028                  .arg(info->atscmin_cnt[
01029                           chan.atsc_minor_channel])
01030                     ).toAscii().constData();
01031         }
01032     }
01033 
01034     return msg;
01035 }
01036 
01037 QString ChannelImporter::SimpleFormatChannel(
01038     const ScanDTVTransport          &transport,
01039     const ChannelInsertInfo         &chan)
01040 {
01041     QString msg;
01042     QTextStream ssMsg(&msg);
01043 
01044     QString si_standard = (chan.si_standard=="opencable") ?
01045         QString("scte") : chan.si_standard;
01046 
01047     if (si_standard == "atsc" || si_standard == "scte")
01048     {
01049 
01050         if (si_standard == "atsc")
01051             ssMsg << (QString("%1-%2")
01052                   .arg(chan.atsc_major_channel)
01053                   .arg(chan.atsc_minor_channel)).toAscii().constData();
01054         else
01055             ssMsg << (QString("%1-%2")
01056                   .arg(chan.freqid)
01057                   .arg(chan.service_id)).toAscii().constData();
01058 
01059         if (!chan.callsign.isEmpty())
01060             ssMsg << (QString(" (%1)")
01061                   .arg(chan.callsign)).toAscii().constData();
01062     }
01063     else if (si_standard == "dvb")
01064         ssMsg << (QString("%1 (%2 %3)")
01065                   .arg(chan.service_name).arg(chan.service_id)
01066                   .arg(chan.netid)).toAscii().constData();
01067     else
01068         ssMsg << (QString("%1-%2")
01069                   .arg(chan.freqid).arg(chan.service_id))
01070                   .toAscii().constData();
01071 
01072     return msg;
01073 }
01074 
01075 QString ChannelImporter::FormatChannels(
01076     const ScanDTVTransportList      &transports,
01077     const ChannelImporterBasicStats &info)
01078 {
01079     QString msg;
01080 
01081     for (uint i = 0; i < transports.size(); i++)
01082         for (uint j = 0; j < transports[i].channels.size(); j++)
01083             msg += FormatChannel(transports[i], transports[i].channels[j],
01084                                  &info) + "\n";
01085 
01086     return msg;
01087 }
01088 
01089 QString ChannelImporter::GetSummary(
01090     uint                                  transport_count,
01091     const ChannelImporterBasicStats      &info,
01092     const ChannelImporterUniquenessStats &stats)
01093 {
01094 
01095     QString msg = QObject::tr("Found %n transport(s):\n", "", transport_count);
01096     msg += QObject::tr("Channels: FTA Enc Dec\n") +
01097         QString("ATSC      %1 %2 %3\n")
01098         .arg(info.atsc_channels[0],3).arg(info.atsc_channels[1],3)
01099         .arg(info.atsc_channels[2],3) +
01100         QString("DVB       %1 %2 %3\n")
01101         .arg(info.dvb_channels [0],3).arg(info.dvb_channels [1],3)
01102         .arg(info.dvb_channels [2],3) +
01103         QString("SCTE      %1 %2 %3\n")
01104         .arg(info.scte_channels[0],3).arg(info.scte_channels[1],3)
01105         .arg(info.scte_channels[2],3) +
01106         QString("MPEG      %1 %2 %3\n")
01107         .arg(info.mpeg_channels[0],3).arg(info.mpeg_channels[1],3)
01108         .arg(info.mpeg_channels[2],3) +
01109         QString("NTSC      %1\n").arg(info.ntsc_channels[0],3) +
01110         QObject::tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
01111         .arg(stats.unique_prognum).arg(stats.unique_atscnum)
01112         .arg(stats.unique_atscmin).arg(stats.unique_channum) +
01113         QObject::tr("Max atsc major count: %1")
01114         .arg(stats.max_atscmajcnt);
01115 
01116     return msg;
01117 }
01118 
01119 bool ChannelImporter::IsType(
01120     const ChannelImporterBasicStats &info,
01121     const ChannelInsertInfo &chan, ChannelType type)
01122 {
01123     switch (type)
01124     {
01125         case kATSCNonConflicting:
01126             return ((chan.si_standard == "atsc") &&
01127                     (info.atscnum_cnt[(chan.atsc_major_channel << 16) |
01128                                       (chan.atsc_minor_channel)] == 1));
01129 
01130         case kDVBNonConflicting:
01131             return ((chan.si_standard == "dvb") &&
01132                     (info.prognum_cnt[chan.service_id] == 1));
01133 
01134         case kMPEGNonConflicting:
01135             return ((chan.si_standard == "mpeg") &&
01136                     (info.channum_cnt[map_str(chan.chan_num)] == 1));
01137 
01138         case kSCTENonConflicting:
01139             return (((chan.si_standard == "scte") ||
01140                     (chan.si_standard == "opencable")) &&
01141                     (info.channum_cnt[map_str(chan.chan_num)] == 1));
01142 
01143         case kNTSCNonConflicting:
01144             return ((chan.si_standard == "ntsc") &&
01145                     (info.atscnum_cnt[(chan.atsc_major_channel << 16) |
01146                                       (chan.atsc_minor_channel)] == 1));
01147 
01148         case kATSCConflicting:
01149             return ((chan.si_standard == "atsc") &&
01150                     (info.atscnum_cnt[(chan.atsc_major_channel << 16) |
01151                                       (chan.atsc_minor_channel)] != 1));
01152 
01153         case kDVBConflicting:
01154             return ((chan.si_standard == "dvb") &&
01155                     (info.prognum_cnt[chan.service_id] != 1));
01156 
01157         case kMPEGConflicting:
01158             return ((chan.si_standard == "mpeg") &&
01159                     (info.channum_cnt[map_str(chan.chan_num)] != 1));
01160 
01161         case kSCTEConflicting:
01162             return (((chan.si_standard == "scte") ||
01163                     (chan.si_standard == "opencable")) &&
01164                     (info.channum_cnt[map_str(chan.chan_num)] != 1));
01165 
01166         case kNTSCConflicting:
01167             return ((chan.si_standard == "ntsc") &&
01168                     (info.atscnum_cnt[(chan.atsc_major_channel << 16) |
01169                                       (chan.atsc_minor_channel)] != 1));
01170     }
01171     return false;
01172 }
01173 
01174 void ChannelImporter::CountChannels(
01175     const ScanDTVTransportList &transports,
01176     const ChannelImporterBasicStats &info,
01177     ChannelType type, uint &new_chan, uint &old_chan)
01178 {
01179     new_chan = old_chan = 0;
01180     for (uint i = 0; i < transports.size(); i++)
01181     {
01182         for (uint j = 0; j < transports[i].channels.size(); j++)
01183         {
01184             ChannelInsertInfo chan = transports[i].channels[j];
01185             if (IsType(info, chan, type))
01186             {
01187                 if (chan.channel_id)
01188                     old_chan++;
01189                 else
01190                     new_chan++;
01191             }
01192         }
01193     }
01194 }
01195 
01196 QString ChannelImporter::ComputeSuggestedChannelNum(
01197     const ChannelImporterBasicStats &info,
01198     const ScanDTVTransport          &transport,
01199     const ChannelInsertInfo         &chan)
01200 {
01201     static QMutex          last_free_lock;
01202     static QMap<uint,uint> last_free_chan_num_map;
01203 
01204     QString channelFormat = "%1_%2";
01205     QString chan_num = channelFormat
01206         .arg(chan.atsc_major_channel)
01207         .arg(chan.atsc_minor_channel);
01208 
01209     if (!chan.atsc_minor_channel)
01210     {
01211         if (chan.si_standard == "dvb")
01212         {
01213             chan_num = QString("%1")
01214                           .arg(chan.service_id);
01215         }
01216         else
01217             chan_num = QString("%1-%2")
01218                           .arg(chan.freqid)
01219                           .arg(chan.service_id);
01220     }
01221 
01222     if (!ChannelUtil::IsConflicting(chan_num, chan.source_id))
01223         return chan_num;
01224 
01225     QMutexLocker locker(&last_free_lock);
01226     uint last_free_chan_num = last_free_chan_num_map[chan.source_id];
01227     for (last_free_chan_num++; ; last_free_chan_num++)
01228     {
01229         chan_num = QString::number(last_free_chan_num);
01230         if (!ChannelUtil::IsConflicting(chan_num, chan.source_id))
01231             break;
01232     }
01233     last_free_chan_num_map[chan.source_id] = last_free_chan_num;
01234 
01235     return chan_num;
01236 }
01237 
01238 ChannelImporter::DeleteAction
01239 ChannelImporter::QueryUserDelete(const QString &msg)
01240 {
01241     DeleteAction action = kDeleteAll;
01242     if (use_gui)
01243     {
01244         QStringList buttons;
01245         buttons.push_back(QObject::tr("Delete all"));
01246         buttons.push_back(QObject::tr("Set all invisible"));
01247 //        buttons.push_back(QObject::tr("Handle manually"));
01248         buttons.push_back(QObject::tr("Ignore all"));
01249 
01250         DialogCode ret;
01251         do
01252         {
01253             ret = MythPopupBox::ShowButtonPopup(
01254                 GetMythMainWindow(), QObject::tr("Channel Importer"),
01255                 msg, buttons, kDialogCodeButton0);
01256 
01257             ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret;
01258 
01259         } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton3));
01260 
01261         action = (kDialogCodeButton0 == ret) ? kDeleteAll       : action;
01262         action = (kDialogCodeButton1 == ret) ? kDeleteInvisibleAll : action;
01263         action = (kDialogCodeButton2 == ret) ? kDeleteIgnoreAll   : action;
01264 //        action = (kDialogCodeButton2 == ret) ? kDeleteManual    : action;
01265 //        action = (kDialogCodeButton3 == ret) ? kDeleteIgnoreAll : action;
01266     }
01267     else if (is_interactive)
01268     {
01269         cout << msg.toAscii().constData()          << endl;
01270         cout << "Do you want to:"    << endl;
01271         cout << "1. Delete all"      << endl;
01272         cout << "2. Set all invisible" << endl;
01273 //        cout << "3. Handle manually" << endl;
01274         cout << "4. Ignore all"      << endl;
01275         while (true)
01276         {
01277             string ret;
01278             cin >> ret;
01279             bool ok;
01280             uint val = QString(ret.c_str()).toUInt(&ok);
01281             if (ok && (1 <= val) && (val <= 3))
01282             {
01283                 action = (1 == val) ? kDeleteAll       : action;
01284                 action = (2 == val) ? kDeleteInvisibleAll : action;
01285                 //action = (3 == val) ? kDeleteManual    : action;
01286                 action = (3 == val) ? kDeleteIgnoreAll : action;
01287                 action = (4 == val) ? kDeleteIgnoreAll : action;
01288                 break;
01289             }
01290             else
01291             {
01292                 //cout << "Please enter either 1, 2, 3 or 4:" << endl;
01293                 cout << "Please enter either 1, 2 or 4:" << endl;//
01294             }
01295         }
01296     }
01297 
01298     return action;
01299 }
01300 
01301 ChannelImporter::InsertAction
01302 ChannelImporter::QueryUserInsert(const QString &msg)
01303 {
01304     InsertAction action = kInsertAll;
01305     if (use_gui)
01306     {
01307         QStringList buttons;
01308         buttons.push_back(QObject::tr("Insert all"));
01309         buttons.push_back(QObject::tr("Insert manually"));
01310         buttons.push_back(QObject::tr("Ignore all"));
01311 
01312         DialogCode ret;
01313         do
01314         {
01315             ret = MythPopupBox::ShowButtonPopup(
01316                 GetMythMainWindow(), QObject::tr("Channel Importer"),
01317                 msg, buttons, kDialogCodeButton0);
01318 
01319             ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret;
01320 
01321         } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton2));
01322 
01323         action = (kDialogCodeButton0 == ret) ? kInsertAll       : action;
01324         action = (kDialogCodeButton1 == ret) ? kInsertManual    : action;
01325         action = (kDialogCodeButton2 == ret) ? kInsertIgnoreAll : action;
01326     }
01327     else if (is_interactive)
01328     {
01329         cout << msg.toAscii().constData()          << endl;
01330         cout << "Do you want to:"    << endl;
01331         cout << "1. Insert all"      << endl;
01332         cout << "2. Insert manually" << endl;
01333         cout << "3. Ignore all"      << endl;
01334         while (true)
01335         {
01336             string ret;
01337             cin >> ret;
01338             bool ok;
01339             uint val = QString(ret.c_str()).toUInt(&ok);
01340             if (ok && (1 <= val) && (val <= 3))
01341             {
01342                 action = (1 == val) ? kInsertAll       : action;
01343                 action = (2 == val) ? kInsertManual    : action;
01344                 action = (3 == val) ? kInsertIgnoreAll : action;
01345                 break;
01346             }
01347             else
01348             {
01349                 cout << "Please enter either 1, 2, or 3:" << endl;
01350             }
01351         }
01352     }
01353 
01354     return action;
01355 }
01356 
01357 ChannelImporter::UpdateAction
01358 ChannelImporter::QueryUserUpdate(const QString &msg)
01359 {
01360     UpdateAction action = kUpdateAll;
01361 
01362     if (use_gui)
01363     {
01364         QStringList buttons;
01365         buttons.push_back(QObject::tr("Update all"));
01366         buttons.push_back(QObject::tr("Update manually"));
01367         buttons.push_back(QObject::tr("Ignore all"));
01368 
01369         DialogCode ret;
01370         do
01371         {
01372             ret = MythPopupBox::ShowButtonPopup(
01373                 GetMythMainWindow(), QObject::tr("Channel Importer"),
01374                 msg, buttons, kDialogCodeButton0);
01375 
01376             ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret;
01377 
01378         } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton2));
01379 
01380         action = (kDialogCodeButton0 == ret) ? kUpdateAll       : action;
01381         action = (kDialogCodeButton1 == ret) ? kUpdateManual    : action;
01382         action = (kDialogCodeButton2 == ret) ? kUpdateIgnoreAll : action;
01383     }
01384     else if (is_interactive)
01385     {
01386         cout << msg.toAscii().constData()
01387              << endl
01388              << QObject::tr("Do you want to:").toAscii().constData()
01389              << endl
01390              << "1. " << QObject::tr("Update all").toAscii().constData()
01391              << endl
01392              << "2. " << QObject::tr("Update manually").toAscii().constData()
01393              << endl
01394              << "3. " << QObject::tr("Ignore all").toAscii().constData()
01395              << endl;
01396         while (true)
01397         {
01398             string ret;
01399             cin >> ret;
01400             bool ok;
01401             uint val = QString(ret.c_str()).toUInt(&ok);
01402             if (ok && (1 <= val) && (val <= 3))
01403             {
01404                 action = (1 == val) ? kUpdateAll       : action;
01405                 action = (2 == val) ? kUpdateManual    : action;
01406                 action = (3 == val) ? kUpdateIgnoreAll : action;
01407                 break;
01408             }
01409             else
01410             {
01411                 cout << QObject::tr(
01412                     "Please enter either 1, 2, or 3:")
01413                     .toAscii().constData() << endl;
01414             }
01415         }
01416     }
01417 
01418     return action;
01419 }
01420 
01421 OkCancelType ChannelImporter::ShowManualChannelPopup(
01422     MythMainWindow *parent, QString title,
01423     QString message, QString &text)
01424 {
01425     MythPopupBox *popup = new MythPopupBox(parent, title.toAscii().constData());
01426 
01427     popup->addLabel(message, MythPopupBox::Medium, true);
01428 
01429     MythLineEdit *textEdit = new MythLineEdit(popup);
01430 
01431     QString orig_text = text;
01432     text = "";
01433     textEdit->setText(text);
01434     popup->addWidget(textEdit);
01435 
01436     popup->addButton(QObject::tr("OK"),     popup, SLOT(accept()));
01437     popup->addButton(QObject::tr("Suggest"));
01438     popup->addButton(QObject::tr("Cancel"), popup, SLOT(reject()));
01439     popup->addButton(QObject::tr("Cancel All"));
01440 
01441     textEdit->setFocus();
01442 
01443     DialogCode dc = popup->ExecPopup();
01444     if (kDialogCodeButton1 == dc)
01445     {
01446         popup->hide();
01447         popup->deleteLater();
01448 
01449         popup = new MythPopupBox(parent, title.toAscii().constData());
01450         popup->addLabel(message, MythPopupBox::Medium, true);
01451 
01452         textEdit = new MythLineEdit(popup);
01453 
01454         text = orig_text;
01455         textEdit->setText(text);
01456         popup->addWidget(textEdit);
01457 
01458         popup->addButton(QObject::tr("OK"), popup, SLOT(accept()))->setFocus();
01459         popup->addButton(QObject::tr("Cancel"), popup, SLOT(reject()));
01460         popup->addButton(QObject::tr("Cancel All"));
01461 
01462         dc = popup->ExecPopup();
01463     }
01464 
01465     bool ok = (kDialogCodeAccepted == dc);
01466     if (ok)
01467         text = textEdit->text();
01468 
01469     popup->hide();
01470     popup->deleteLater();
01471 
01472     return (ok) ? kOCTOk :
01473         ((kDialogCodeRejected == dc) ? kOCTCancel : kOCTCancelAll);
01474 }
01475 
01476 OkCancelType ChannelImporter::QueryUserResolve(
01477     const ChannelImporterBasicStats &info,
01478     const ScanDTVTransport          &transport,
01479     ChannelInsertInfo               &chan)
01480 {
01481     QString msg = QObject::tr(
01482         "Channel %1 was found to be in conflict with other channels. ")
01483         .arg(SimpleFormatChannel(transport, chan));
01484 
01485     OkCancelType ret = kOCTCancel;
01486 
01487     if (use_gui)
01488     {
01489         while (true)
01490         {
01491             QString msg2 = msg;
01492             msg2 += QObject::tr("Please enter a unique channel number. ");
01493 
01494             QString val = ComputeSuggestedChannelNum(info, transport, chan);
01495             ret = ShowManualChannelPopup(
01496                 GetMythMainWindow(), QObject::tr("Channel Importer"),
01497                 msg2, val);
01498 
01499             if (kOCTOk != ret)
01500                 break; // user canceled..
01501 
01502             bool ok = (val.length() >= 1);
01503             ok = ok && ((val[0] >= '0') && (val[0] <= '9'));
01504             ok = ok && !ChannelUtil::IsConflicting(
01505                 val, chan.source_id, chan.channel_id);
01506 
01507             chan.chan_num = (ok) ? val : chan.chan_num;
01508             if (ok)
01509                 break;
01510         }
01511     }
01512     else if (is_interactive)
01513     {
01514         cout << msg.toAscii().constData() << endl;
01515 
01516         QString cancelStr = QObject::tr("Cancel").toLower();
01517         QString cancelAllStr = QObject::tr("Cancel All").toLower();
01518         QString msg2 = QObject::tr(
01519             "Please enter a non-conflicting channel number "
01520             "(or type %1 to skip, %2 to skip all): ")
01521             .arg(cancelStr).arg(cancelAllStr);
01522 
01523         while (true)
01524         {
01525             cout << msg2.toAscii().constData() << endl;
01526             string sret;
01527             cin >> sret;
01528             QString val = QString(sret.c_str());
01529             if (val.toLower() == cancelStr)
01530             {
01531                 ret = kOCTCancel;
01532                 break; // user canceled..
01533             }
01534             if (val.toLower() == cancelAllStr)
01535             {
01536                 ret = kOCTCancelAll;
01537                 break; // user canceled..
01538             }
01539 
01540             bool ok = (val.length() >= 1);
01541             ok = ok && ((val[0] >= '0') && (val[0] <= '9'));
01542             ok = ok && !ChannelUtil::IsConflicting(
01543                 val, chan.source_id, chan.channel_id);
01544 
01545             chan.chan_num = (ok) ? val : chan.chan_num;
01546             if (ok)
01547             {
01548                 ret = kOCTOk;
01549                 break;
01550             }
01551         }
01552     }
01553 
01554     return ret;
01555 }
01556 
01557 OkCancelType ChannelImporter::QueryUserInsert(
01558     const ChannelImporterBasicStats &info,
01559     const ScanDTVTransport          &transport,
01560     ChannelInsertInfo               &chan)
01561 {
01562     QString msg = QObject::tr(
01563         "You chose to manually insert channel %1.")
01564         .arg(SimpleFormatChannel(transport, chan));
01565 
01566     OkCancelType ret = kOCTCancel;
01567 
01568     if (use_gui)
01569     {
01570         while (true)
01571         {
01572             QString msg2 = msg;
01573             msg2 += QObject::tr("Please enter a unique channel number. ");
01574 
01575             QString val = ComputeSuggestedChannelNum(info, transport, chan);
01576             ret = ShowManualChannelPopup(
01577                 GetMythMainWindow(), QObject::tr("Channel Importer"),
01578                 msg2, val);
01579 
01580             if (kOCTOk != ret)
01581                 break; // user canceled..
01582 
01583             bool ok = (val.length() >= 1);
01584             ok = ok && ((val[0] >= '0') && (val[0] <= '9'));
01585             ok = ok && !ChannelUtil::IsConflicting(
01586                 val, chan.source_id, chan.channel_id);
01587 
01588             chan.chan_num = (ok) ? val : chan.chan_num;
01589             if (ok)
01590             {
01591                 ret = kOCTOk;
01592                 break;
01593             }
01594         }
01595     }
01596     else if (is_interactive)
01597     {
01598         cout << msg.toAscii().constData() << endl;
01599 
01600         QString cancelStr    = QObject::tr("Cancel").toLower();
01601         QString cancelAllStr = QObject::tr("Cancel All").toLower();
01602         QString msg2 = QObject::tr(
01603             "Please enter a non-conflicting channel number "
01604             "(or type %1 to skip, %2 to skip all): ")
01605             .arg(cancelStr).arg(cancelAllStr);
01606 
01607         while (true)
01608         {
01609             cout << msg2.toAscii().constData() << endl;
01610             string sret;
01611             cin >> sret;
01612             QString val = QString(sret.c_str());
01613             if (val.toLower() == cancelStr)
01614             {
01615                 ret = kOCTCancel;
01616                 break; // user canceled..
01617             }
01618             if (val.toLower() == cancelAllStr)
01619             {
01620                 ret = kOCTCancelAll;
01621                 break; // user canceled..
01622             }
01623 
01624             bool ok = (val.length() >= 1);
01625             ok = ok && ((val[0] >= '0') && (val[0] <= '9'));
01626             ok = ok && !ChannelUtil::IsConflicting(
01627                 val, chan.source_id, chan.channel_id);
01628 
01629             chan.chan_num = (ok) ? val : chan.chan_num;
01630             if (ok)
01631             {
01632                 ret = kOCTOk;
01633                 break;
01634             }
01635         }
01636     }
01637 
01638     return ret;
01639 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends