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"