File indexing completed on 2024-05-12 04:52:20

0001 /*
0002  * dvbtab.cpp
0003  *
0004  * Copyright (C) 2007-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include "../log.h"
0022 
0023 #include <kwidgetsaddons_version.h>
0024 #include <KActionCollection>
0025 #include <KConfigGroup>
0026 #include <KMessageBox>
0027 #include <KSharedConfig>
0028 #include <QAction>
0029 #include <QBoxLayout>
0030 #include <QDir>
0031 #include <QHeaderView>
0032 #include <QLineEdit>
0033 #include <QMenu>
0034 #include <QSplitter>
0035 #include <QThread>
0036 #include <QToolButton>
0037 
0038 #include "../osdwidget.h"
0039 #include "dvbchanneldialog.h"
0040 #include "dvbconfigdialog.h"
0041 #include "dvbepg.h"
0042 #include "dvbepgdialog.h"
0043 #include "dvbliveview.h"
0044 #include "dvbmanager.h"
0045 #include "dvbrecordingdialog.h"
0046 #include "dvbscandialog.h"
0047 #include "dvbtab.h"
0048 
0049 class DvbTimeShiftCleaner : public QThread
0050 {
0051 public:
0052     explicit DvbTimeShiftCleaner(QObject *parent) : QThread(parent) { }
0053 
0054     ~DvbTimeShiftCleaner()
0055     {
0056         wait();
0057     }
0058 
0059     void remove(const QString &path_, const QStringList &files_);
0060 
0061 private:
0062     void run() override;
0063 
0064     QString path;
0065     QStringList files;
0066 };
0067 
0068 void DvbTimeShiftCleaner::remove(const QString &path_, const QStringList &files_)
0069 {
0070     path = path_;
0071     files = files_;
0072 
0073     start();
0074 }
0075 
0076 void DvbTimeShiftCleaner::run()
0077 {
0078     // delete files asynchronously because it may block for several seconds
0079     foreach (const QString &file, files) {
0080         QFile::remove(path + QLatin1Char('/') + file);
0081     }
0082 }
0083 
0084 DvbTab::DvbTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_) :
0085     mediaWidget(mediaWidget_)
0086 {
0087     manager = new DvbManager(mediaWidget, this);
0088 
0089     mediaRecordIcon = QIcon::fromTheme(QLatin1String("media-record"), QIcon(":media-record"));
0090     documentSaveIcon = QIcon::fromTheme(QLatin1String("document-save"), QIcon(":document-save"));
0091 
0092     QAction *channelsAction = new QAction(QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), i18n("Channels"), this);
0093     channelsAction->setShortcut(Qt::Key_C);
0094     connect(channelsAction, SIGNAL(triggered(bool)), this, SLOT(showChannelDialog()));
0095     menu->addAction(collection->addAction(QLatin1String("dvb_channels"), channelsAction));
0096 
0097     QAction *epgAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-details"), QIcon(":view-list-details")), i18n("Program Guide"), this);
0098     epgAction->setShortcut(Qt::Key_G);
0099     connect(epgAction, SIGNAL(triggered(bool)), this, SLOT(toggleEpgDialog()));
0100     menu->addAction(collection->addAction(QLatin1String("dvb_epg"), epgAction));
0101 
0102     QAction *osdAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-information"), QIcon(":dialog-information")), i18n("OSD"), this);
0103     osdAction->setShortcut(Qt::Key_O);
0104     connect(osdAction, SIGNAL(triggered(bool)), manager->getLiveView(), SLOT(toggleOsd()));
0105     menu->addAction(collection->addAction(QLatin1String("dvb_osd"), osdAction));
0106 
0107     QAction *recordingsAction = new QAction(QIcon::fromTheme(QLatin1String("view-pim-calendar"), QIcon(":view-pim-calendar")),
0108         i18nc("dialog", "Recording Schedule"), this);
0109     recordingsAction->setShortcut(Qt::Key_R);
0110     connect(recordingsAction, SIGNAL(triggered(bool)), this, SLOT(showRecordingDialog()));
0111     menu->addAction(collection->addAction(QLatin1String("dvb_recordings"), recordingsAction));
0112 
0113     menu->addSeparator();
0114 
0115     instantRecordAction = new QAction(documentSaveIcon, i18n("Instant Record"), this);
0116     instantRecordAction->setCheckable(true);
0117     connect(instantRecordAction, SIGNAL(triggered(bool)), this, SLOT(instantRecord(bool)));
0118     menu->addAction(collection->addAction(QLatin1String("dvb_instant_record"), instantRecordAction));
0119 
0120     menu->addSeparator();
0121 
0122     QAction *configureAction = new QAction(QIcon::fromTheme(QLatin1String("configure"), QIcon(":configure")),
0123         i18nc("@action:inmenu", "Configure Television..."), this);
0124     connect(configureAction, SIGNAL(triggered()), this, SLOT(configureDvb()));
0125     menu->addAction(collection->addAction(QLatin1String("settings_dvb"), configureAction));
0126 
0127     connect(manager->getLiveView(), SIGNAL(previous()), this, SLOT(previousChannel()));
0128     connect(manager->getLiveView(), SIGNAL(next()), this, SLOT(nextChannel()));
0129 
0130     connect(manager->getRecordingModel(), SIGNAL(recordingRemoved(DvbSharedRecording)),
0131         this, SLOT(recordingRemoved(DvbSharedRecording)));
0132 
0133     QBoxLayout *boxLayout = new QHBoxLayout(this);
0134     boxLayout->setMargin(0);
0135 
0136     splitter = new QSplitter(this);
0137     boxLayout->addWidget(splitter);
0138 
0139     leftWidget = new QWidget(splitter);
0140     QBoxLayout *leftLayout = new QVBoxLayout(leftWidget);
0141 
0142     boxLayout = new QHBoxLayout();
0143     boxLayout->addWidget(new QLabel(i18n("Search:")));
0144 
0145     QLineEdit *lineEdit = new QLineEdit(leftWidget);
0146     lineEdit->setClearButtonEnabled(true);
0147     boxLayout->addWidget(lineEdit);
0148     leftLayout->addLayout(boxLayout);
0149 
0150     channelView = new DvbChannelView(leftWidget);
0151     channelView->setContextMenuPolicy(Qt::ActionsContextMenu);
0152     channelProxyModel = new DvbChannelTableModel(this);
0153     channelView->setModel(channelProxyModel);
0154     channelView->setRootIsDecorated(false);
0155 
0156     if (!channelView->header()->restoreState(QByteArray::fromBase64(
0157         KSharedConfig::openConfig()->group("DVB").readEntry("ChannelViewState", QByteArray())))) {
0158         channelView->sortByColumn(0, Qt::AscendingOrder);
0159     }
0160 
0161     channelView->setSortingEnabled(true);
0162     channelView->addEditAction();
0163     connect(channelView, SIGNAL(activated(QModelIndex)), this, SLOT(playChannel(QModelIndex)));
0164     connect(channelView, SIGNAL(channelPidsUpdated(DvbSharedChannel)), this, SLOT(channelPidsUpdated(DvbSharedChannel)));
0165 
0166     channelProxyModel->setChannelModel(manager->getChannelModel());
0167     connect(lineEdit, SIGNAL(textChanged(QString)),
0168         channelProxyModel, SLOT(setFilter(QString)));
0169     manager->setChannelView(channelView);
0170     leftLayout->addWidget(channelView);
0171 
0172     boxLayout = new QHBoxLayout();
0173 
0174     const QSize BUTTON_SIZE = QSize(22, 22);
0175 
0176     QToolButton *toolButton = new QToolButton(leftWidget);
0177     toolButton->setDefaultAction(configureAction);
0178     toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0179     toolButton->setFixedSize(BUTTON_SIZE);
0180     boxLayout->addWidget(toolButton);
0181 
0182     toolButton = new QToolButton(leftWidget);
0183     toolButton->setDefaultAction(channelsAction);
0184     toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0185     toolButton->setFixedSize(BUTTON_SIZE);
0186     boxLayout->addWidget(toolButton);
0187 
0188     toolButton = new QToolButton(leftWidget);
0189     toolButton->setDefaultAction(epgAction);
0190     toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0191     toolButton->setFixedSize(BUTTON_SIZE);
0192     boxLayout->addWidget(toolButton);
0193 
0194     toolButton = new QToolButton(leftWidget);
0195     toolButton->setDefaultAction(recordingsAction);
0196     toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0197     toolButton->setFixedSize(BUTTON_SIZE);
0198     boxLayout->addWidget(toolButton);
0199 
0200     toolButton = new QToolButton(leftWidget);
0201     toolButton->setDefaultAction(instantRecordAction);
0202     toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0203     toolButton->setFixedSize(BUTTON_SIZE);
0204     boxLayout->addWidget(toolButton);
0205     leftLayout->addLayout(boxLayout);
0206 
0207     QWidget *mediaContainer = new QWidget(splitter);
0208     mediaLayout = new QHBoxLayout(mediaContainer);
0209     mediaLayout->setMargin(0);
0210     splitter->setStretchFactor(1, 1);
0211 
0212     connect(mediaWidget, SIGNAL(osdKeyPressed(int)), this, SLOT(osdKeyPressed(int)));
0213     connect(&osdChannelTimer, SIGNAL(timeout()), this, SLOT(tuneOsdChannel()));
0214 
0215     lastChannel = KSharedConfig::openConfig()->group("DVB").readEntry("LastChannel");
0216 
0217     splitter->restoreState(QByteArray::fromBase64(
0218         KSharedConfig::openConfig()->group("DVB").readEntry("TabSplitterState", QByteArray())));
0219 
0220     timeShiftCleaner = new DvbTimeShiftCleaner(this);
0221 
0222     QTimer *timer = new QTimer(this);
0223     timer->start(30000);
0224     connect(timer, SIGNAL(timeout()), this, SLOT(cleanTimeShiftFiles()));
0225 
0226     autoHideMenu = false;
0227 }
0228 
0229 DvbTab::~DvbTab()
0230 {
0231     KSharedConfig::openConfig()->group("DVB").writeEntry("TabSplitterState",
0232         splitter->saveState().toBase64());
0233     KSharedConfig::openConfig()->group("DVB").writeEntry("ChannelViewState",
0234         channelView->header()->saveState().toBase64());
0235 
0236     if (!currentChannel.isEmpty()) {
0237         lastChannel = currentChannel;
0238     }
0239 
0240     KSharedConfig::openConfig()->group("DVB").writeEntry("LastChannel", lastChannel);
0241 }
0242 
0243 void DvbTab::playChannel(const QString &nameOrNumber)
0244 {
0245     DvbChannelModel *channelModel = manager->getChannelModel();
0246     DvbSharedChannel channel;
0247     int number = nameOrNumber.toInt();
0248 
0249     if (number > 0) {
0250         channel = channelModel->findChannelByNumber(number);
0251     }
0252 
0253     if (!channel.isValid()) {
0254         channel = channelModel->findChannelByName(nameOrNumber);
0255     }
0256 
0257     if (channel.isValid()) {
0258         playChannel(channel, channelProxyModel->find(channel));
0259     }
0260 }
0261 
0262 void DvbTab::playLastChannel()
0263 {
0264     if (!manager->getLiveView()->getChannel().isValid() && !currentChannel.isEmpty()) {
0265         lastChannel = currentChannel;
0266     }
0267 
0268     DvbSharedChannel channel = manager->getChannelModel()->findChannelByName(lastChannel);
0269 
0270     if (channel.isValid()) {
0271         playChannel(channel, channelProxyModel->find(channel));
0272     }
0273 }
0274 
0275 void DvbTab::channelPidsUpdated(const DvbSharedChannel &updatedChannel)
0276 {
0277     DvbSharedChannel channel = manager->getChannelModel()->findChannelByName(lastChannel);
0278     MediaWidget::PlaybackStatus status;
0279 
0280     /*
0281      * This slot is called when a channel is played and
0282      * a channel configuration that would require to reload
0283      * the Digital TV filtering was changed at the editor,
0284      * e. g. if a PID has changed.
0285      * The common reason is if someone wants to change the
0286      * audio PID, in order to select a different language or
0287      * enable/disable narration.
0288      */
0289 
0290     // Do nothing if the edited channel is not the active one
0291     if (updatedChannel != channel)
0292         return;
0293 
0294     status = mediaWidget->getPlaybackStatus();
0295 
0296     // Ignore channels that are not being played
0297     if (status == MediaWidget::Idle)
0298         return;
0299 
0300     // Re-run the channel play, in order to update the
0301     // PID filters
0302     playLastChannel();
0303 
0304 #if 0
0305     /*
0306      * TODO: ideally, if the channel is paused, editing it should
0307      * preserve it. However, togglePause() will only work after
0308      * the channel starts to play, with takes some time. So,
0309      * the code below won't work.
0310      * Also, now the old pause status data is not valid anymore,
0311      * as it contains a different PID, so we need to flush the
0312      * data anyway - or implement a way more complex logic that
0313      * would be handling a paused off-line file, and a new
0314      * on-line paused file.
0315      *
0316      * So, for now, let's just assume that changing the PIDs
0317      * while pausing a video would flush the old pause stuff
0318      * and un-pause the channel.
0319      */
0320     if (status == MediaWidget::Paused) {
0321         qInfo() << "Toggling pause";
0322         mediaWidget->togglePause();
0323     }
0324 #endif
0325 }
0326 
0327 
0328 void DvbTab::toggleOsd()
0329 {
0330     manager->getLiveView()->toggleOsd();
0331 }
0332 
0333 void DvbTab::toggleInstantRecord()
0334 {
0335     instantRecordAction->trigger();
0336 }
0337 
0338 void DvbTab::enableDvbDump()
0339 {
0340     manager->enableDvbDump();
0341 }
0342 
0343 void DvbTab::mouse_move(int x, int)
0344 {
0345     if (!autoHideMenu)
0346         return;
0347 
0348     unsetCursor();
0349 
0350     leftWidget->setVisible(x >= 0 && x < 120);
0351 }
0352 
0353 void DvbTab::toggleDisplayMode(MediaWidget::DisplayMode displayMode)
0354 {
0355     switch (displayMode) {
0356     case MediaWidget::FullScreenMode:
0357     case MediaWidget::FullScreenReturnToMinimalMode:
0358     case MediaWidget::MinimalMode:
0359         leftWidget->hide();
0360         autoHideMenu = true;
0361         break;
0362     case MediaWidget::NormalMode:
0363         leftWidget->show();
0364         autoHideMenu = false;
0365         break;
0366     }
0367 }
0368 
0369 void DvbTab::osdKeyPressed(int key)
0370 {
0371     if ((key >= Qt::Key_0) && (key <= Qt::Key_9)) {
0372         osdChannel += QString::number(key - Qt::Key_0);
0373         osdChannelTimer.start(1500);
0374         mediaWidget->getOsdWidget()->showText(i18nc("osd", "Channel: %1_", osdChannel),
0375             1500);
0376     }
0377 }
0378 
0379 void DvbTab::mayCloseApplication(bool *ok, QWidget *parent)
0380 {
0381     if (*ok) {
0382         DvbRecordingModel *recordingModel = manager->getRecordingModel();
0383 
0384         if (recordingModel->hasActiveRecordings()) {
0385 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0386             if (KMessageBox::warningTwoActions(parent,
0387 #else
0388             if (KMessageBox::warningYesNo(parent,
0389 #endif
0390                 i18nc("message box",
0391                 "Kaffeine is currently recording programs.\n"
0392                 "Do you really want to close the application?"),
0393                 QString(),
0394                 KStandardGuiItem::quit(),
0395                 KStandardGuiItem::cancel())
0396 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0397                 != KMessageBox::PrimaryAction) {
0398 #else
0399                 != KMessageBox::Yes) {
0400 #endif
0401                 *ok = false;
0402             }
0403 
0404             return;
0405         }
0406 
0407         if (recordingModel->hasRecordings()) {
0408 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0409             if (KMessageBox::questionTwoActions(parent,
0410 #else
0411             if (KMessageBox::questionYesNo(parent,
0412 #endif
0413                 i18nc("message box",
0414                 "Kaffeine has scheduled recordings.\n"
0415                 "Do you really want to close the application?"), QString(),
0416                 KStandardGuiItem::quit(), KStandardGuiItem::cancel(),
0417                 QLatin1String("ScheduledRecordings"))
0418 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0419                 != KMessageBox::PrimaryAction) {
0420 #else
0421                 != KMessageBox::Yes) {
0422 #endif
0423                 *ok = false;
0424             }
0425 
0426             return;
0427         }
0428     }
0429 }
0430 
0431 void DvbTab::showChannelDialog()
0432 {
0433     QDialog *dialog = new DvbScanDialog(manager, this);
0434     dialog->setAttribute(Qt::WA_DeleteOnClose, true);
0435     dialog->setModal(true);
0436     dialog->show();
0437 }
0438 
0439 void DvbTab::showRecordingDialog()
0440 {
0441     DvbRecordingDialog::showDialog(manager, this);
0442 }
0443 
0444 void DvbTab::toggleEpgDialog()
0445 {
0446     if (epgDialog.isNull()) {
0447         epgDialog = new DvbEpgDialog(manager, this);
0448         epgDialog->setAttribute(Qt::WA_DeleteOnClose, true);
0449         epgDialog->setCurrentChannel(manager->getLiveView()->getChannel());
0450         epgDialog->setModal(false);
0451         epgDialog->show();
0452     } else {
0453         epgDialog->deleteLater();
0454         epgDialog = NULL;
0455     }
0456 }
0457 
0458 void DvbTab::instantRecord(bool checked)
0459 {
0460     if (checked) {
0461         const DvbSharedChannel &channel = manager->getLiveView()->getChannel();
0462 
0463         if (!channel.isValid()) {
0464             instantRecordAction->setChecked(false);
0465             return;
0466         }
0467 
0468         DvbRecording recording;
0469         QList<DvbSharedEpgEntry> epgEntries =
0470             manager->getEpgModel()->getCurrentNext(channel);
0471 
0472         if (!epgEntries.isEmpty()) {
0473             recording.name = epgEntries.at(0)->title();
0474         }
0475 
0476         if (recording.name.isEmpty()) {
0477             recording.name =
0478                 (channel->name + QTime::currentTime().toString(QLatin1String("-hhmmss")));
0479         }
0480 
0481         recording.channel = channel;
0482         recording.begin = QDateTime::currentDateTime().toUTC();
0483         recording.duration = QTime(12, 0);
0484         instantRecording = manager->getRecordingModel()->addRecording(recording);
0485         instantRecordings.push_back(instantRecording);
0486         instantRecordAction->setIcon(mediaRecordIcon);
0487         mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Started"),
0488             1500);
0489     } else {
0490         manager->getRecordingModel()->removeRecording(instantRecording);
0491         instantRecordings.removeOne(instantRecording);
0492         instantRecordAction->setIcon(documentSaveIcon);
0493         mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"),
0494             1500);
0495     }
0496 }
0497 
0498 void DvbTab::recordingRemoved(const DvbSharedRecording &recording)
0499 {
0500     if (instantRecording == recording) {
0501         instantRecording = DvbSharedRecording();
0502         instantRecordAction->setChecked(false);
0503         instantRecordAction->setIcon(documentSaveIcon);
0504         mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"),
0505             1500);
0506     }
0507 
0508     instantRecordings.removeOne(recording);
0509 }
0510 
0511 void DvbTab::configureDvb()
0512 {
0513     QDialog *dialog = new DvbConfigDialog(manager, this);
0514     dialog->setAttribute(Qt::WA_DeleteOnClose, true);
0515     dialog->setModal(true);
0516     dialog->show();
0517 }
0518 
0519 void DvbTab::tuneOsdChannel()
0520 {
0521     int number = osdChannel.toInt();
0522     osdChannel.clear();
0523     osdChannelTimer.stop();
0524 
0525     DvbSharedChannel channel = manager->getChannelModel()->findChannelByNumber(number);
0526 
0527     if (channel.isValid()) {
0528         playChannel(channel, channelProxyModel->find(channel));
0529     }
0530 }
0531 
0532 void DvbTab::playChannel(const QModelIndex &index)
0533 {
0534     if (index.isValid()) {
0535         playChannel(channelProxyModel->value(index), index);
0536     }
0537 }
0538 
0539 void DvbTab::previousChannel()
0540 {
0541     QModelIndex index = channelView->currentIndex();
0542 
0543     if (index.isValid()) {
0544         playChannel(index.sibling(index.row() - 1, index.column()));
0545     }
0546 }
0547 
0548 void DvbTab::nextChannel()
0549 {
0550     QModelIndex index = channelView->currentIndex();
0551 
0552     if (index.isValid()) {
0553         playChannel(index.sibling(index.row() + 1, index.column()));
0554     }
0555 }
0556 
0557 void DvbTab::cleanTimeShiftFiles()
0558 {
0559     if (timeShiftCleaner->isRunning()) {
0560         return;
0561     }
0562 
0563     QDir dir(manager->getTimeShiftFolder());
0564     QStringList entries =
0565         dir.entryList(QStringList(QLatin1String("TimeShift-*.m2t")), QDir::Files, QDir::Name);
0566 
0567     if (entries.count() < 2) {
0568         return;
0569     }
0570 
0571     entries.removeLast();
0572 
0573     timeShiftCleaner->remove(dir.path(), entries);
0574 }
0575 
0576 void DvbTab::activate()
0577 {
0578     mediaLayout->addWidget(mediaWidget);
0579     mediaWidget->setFocus();
0580 }
0581 
0582 void DvbTab::playChannel(const DvbSharedChannel &channel, const QModelIndex &index)
0583 {
0584     QIcon *icon;
0585     DvbSharedRecording *recording;
0586     bool isRecording;
0587 
0588     if (!channel.isValid()) {
0589         qCWarning(logDvb, "Channel is invalid");
0590         return;
0591     }
0592 
0593     if (!currentChannel.isEmpty()) {
0594         lastChannel = currentChannel;
0595     }
0596 
0597     recording = getInstantRecording(channel);
0598 
0599     if (recording) {
0600         instantRecording = *recording;
0601         isRecording = true;
0602         icon = &mediaRecordIcon;
0603     } else {
0604         isRecording = false;
0605         icon = &documentSaveIcon;
0606     }
0607 
0608     instantRecordAction->setChecked(isRecording);
0609     instantRecordAction->setIcon(*icon);
0610 
0611     channelView->setCurrentIndex(index);
0612     currentChannel = channel->name;
0613     manager->getLiveView()->playChannel(channel);
0614 
0615     if (!epgDialog.isNull()) {
0616         epgDialog->setCurrentChannel(manager->getLiveView()->getChannel());
0617     }
0618 }
0619 
0620 DvbSharedRecording *DvbTab::getInstantRecording(DvbSharedChannel ch) {
0621     DvbSharedRecording *ret = NULL;
0622 
0623     QListIterator<DvbSharedRecording> i(instantRecordings);
0624     while (i.hasNext()) {
0625         DvbSharedRecording r = i.next();
0626         if(r.constData()->channel == ch) {
0627             ret = &r;
0628             break;
0629         }
0630     }
0631 
0632     return ret;
0633 }
0634 
0635 #include "moc_dvbtab.cpp"