File indexing completed on 2024-05-12 04:52:14
0001 /* 0002 * dvbepgdialog.cpp 0003 * 0004 * Copyright (C) 2009-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 <KConfigGroup> 0024 #include <QAction> 0025 #include <QBoxLayout> 0026 #include <QCheckBox> 0027 #include <QComboBox> 0028 #include <QCoreApplication> 0029 #include <QDialogButtonBox> 0030 #include <QHeaderView> 0031 #include <QLabel> 0032 #include <QLineEdit> 0033 #include <QLocale> 0034 #include <QPushButton> 0035 #include <QScrollArea> 0036 #include <QVBoxLayout> 0037 0038 #include "dvbepgdialog.h" 0039 #include "dvbepgdialog_p.h" 0040 #include "dvbmanager.h" 0041 #include "../iso-codes.h" 0042 0043 DvbEpgDialog::DvbEpgDialog(DvbManager *manager_, QWidget *parent) : QDialog(parent), 0044 manager(manager_) 0045 { 0046 setWindowTitle(i18nc("@title:window", "Program Guide")); 0047 0048 QWidget *mainWidget = new QWidget(this); 0049 QBoxLayout *mainLayout = new QHBoxLayout; 0050 setLayout(mainLayout); 0051 0052 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0053 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); 0054 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 0055 mainLayout->addWidget(mainWidget); 0056 0057 QWidget *widget = new QWidget(this); 0058 0059 epgChannelTableModel = new DvbEpgChannelTableModel(this); 0060 epgChannelTableModel->setManager(manager); 0061 channelView = new QTreeView(widget); 0062 channelView->setMaximumWidth(30 * fontMetrics().averageCharWidth()); 0063 channelView->setModel(epgChannelTableModel); 0064 channelView->setRootIsDecorated(false); 0065 connect(channelView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0066 this, SLOT(channelActivated(QModelIndex))); 0067 mainLayout->addWidget(channelView); 0068 0069 QBoxLayout *rightLayout = new QVBoxLayout(); 0070 QBoxLayout *boxLayout = new QHBoxLayout(); 0071 QBoxLayout *langLayout = new QHBoxLayout(); 0072 0073 QLabel *label = new QLabel(i18n("EPG language:"), mainWidget); 0074 langLayout->addWidget(label); 0075 0076 languageBox = new QComboBox(mainWidget); 0077 languageBox->addItem(""); 0078 QHashIterator<QString, bool> i(manager_->languageCodes); 0079 int j = 1; 0080 while (i.hasNext()) { 0081 i.next(); 0082 QString lang = i.key(); 0083 if (lang != FIRST_LANG) { 0084 languageBox->addItem(lang); 0085 if (manager_->currentEpgLanguage == lang) { 0086 languageBox->setCurrentIndex(j); 0087 currentLanguage = lang; 0088 } 0089 j++; 0090 } 0091 } 0092 langLayout->addWidget(languageBox); 0093 connect(languageBox, &QComboBox::currentTextChanged, 0094 this, &DvbEpgDialog::languageChanged); 0095 connect(manager_->getEpgModel(), &DvbEpgModel::languageAdded, 0096 this, &DvbEpgDialog::languageAdded); 0097 0098 languageLabel = new QLabel(mainWidget); 0099 langLayout->addWidget(languageLabel); 0100 languageLabel->setBuddy(languageBox); 0101 QString languageString; 0102 if (IsoCodes::getLanguage(currentLanguage, &languageString)) 0103 languageLabel->setText(languageString); 0104 else if (currentLanguage.isEmpty()) 0105 languageLabel->setText(i18n("Any language")); 0106 else 0107 languageLabel->setText(""); 0108 0109 rightLayout->addLayout(langLayout); 0110 0111 QHBoxLayout *showLayout = new QHBoxLayout(); 0112 showLayout->addWidget(new QLabel(i18n("Show channels with empty EPG data"))); 0113 showBox = new QCheckBox(widget); 0114 showLayout->addWidget(showBox, Qt::AlignLeft); 0115 rightLayout->addLayout(showLayout); 0116 connect(showBox, SIGNAL(clicked()), this, SLOT(changeShowMode())); 0117 0118 QAction *scheduleAction = new QAction(QIcon::fromTheme(QLatin1String("media-record"), QIcon(":media-record")), 0119 i18nc("@action:inmenu tv show", "Record Show"), this); 0120 connect(scheduleAction, SIGNAL(triggered()), this, SLOT(scheduleProgram())); 0121 0122 QPushButton *pushButton = 0123 new QPushButton(scheduleAction->icon(), scheduleAction->text(), widget); 0124 connect(pushButton, SIGNAL(clicked()), this, SLOT(scheduleProgram())); 0125 boxLayout->addWidget(pushButton); 0126 0127 boxLayout->addWidget(new QLabel(i18nc("@label:textbox", "Search:"), widget)); 0128 0129 epgTableModel = new DvbEpgTableModel(this); 0130 epgTableModel->setEpgModel(manager->getEpgModel()); 0131 epgTableModel->setLanguage(currentLanguage); 0132 connect(epgTableModel, SIGNAL(layoutChanged()), this, SLOT(checkEntry())); 0133 QLineEdit *lineEdit = new QLineEdit(widget); 0134 lineEdit->setClearButtonEnabled(true); 0135 connect(lineEdit, SIGNAL(textChanged(QString)), 0136 epgTableModel, SLOT(setContentFilter(QString))); 0137 boxLayout->addWidget(lineEdit); 0138 rightLayout->addLayout(boxLayout); 0139 0140 epgView = new QTreeView(widget); 0141 epgView->addAction(scheduleAction); 0142 epgView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0143 epgView->setContextMenuPolicy(Qt::ActionsContextMenu); 0144 epgView->setMinimumWidth(75 * fontMetrics().averageCharWidth()); 0145 epgView->setModel(epgTableModel); 0146 epgView->setRootIsDecorated(false); 0147 epgView->setUniformRowHeights(true); 0148 connect(epgView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), 0149 this, SLOT(entryActivated(QModelIndex))); 0150 rightLayout->addWidget(epgView); 0151 0152 contentLabel = new QLabel(widget); 0153 contentLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); 0154 contentLabel->setMargin(5); 0155 contentLabel->setWordWrap(true); 0156 0157 QScrollArea *scrollArea = new QScrollArea(widget); 0158 scrollArea->setBackgroundRole(QPalette::Light); 0159 scrollArea->setMinimumHeight(12 * fontMetrics().height()); 0160 scrollArea->setWidget(contentLabel); 0161 scrollArea->setWidgetResizable(true); 0162 rightLayout->addWidget(scrollArea); 0163 mainLayout->addLayout(rightLayout); 0164 mainLayout->addWidget(widget); 0165 0166 rightLayout->addWidget(buttonBox); 0167 } 0168 0169 DvbEpgDialog::~DvbEpgDialog() 0170 { 0171 } 0172 0173 void DvbEpgDialog::setCurrentChannel(const DvbSharedChannel &channel) 0174 { 0175 channelView->setCurrentIndex(epgChannelTableModel->find(channel)); 0176 } 0177 0178 void DvbEpgDialog::channelActivated(const QModelIndex &index) 0179 { 0180 if (!index.isValid()) { 0181 epgTableModel->setChannelFilter(DvbSharedChannel()); 0182 return; 0183 } 0184 0185 epgTableModel->setChannelFilter(epgChannelTableModel->value(index)); 0186 epgView->setCurrentIndex(epgTableModel->index(0, 0)); 0187 } 0188 0189 void DvbEpgDialog::languageChanged(const QString lang) 0190 { 0191 // Handle the any language case 0192 if (languageBox->currentIndex() <= 0) 0193 currentLanguage = ""; 0194 else 0195 currentLanguage = lang; 0196 QString languageString; 0197 if (IsoCodes::getLanguage(currentLanguage, &languageString)) 0198 languageLabel->setText(languageString); 0199 else if (currentLanguage.isEmpty()) 0200 languageLabel->setText(i18n("Any language")); 0201 else 0202 languageLabel->setText(QString()); 0203 0204 epgTableModel->setLanguage(currentLanguage); 0205 epgView->setCurrentIndex(epgTableModel->index(0, 0)); 0206 entryActivated(epgTableModel->index(0, 0)); 0207 manager->currentEpgLanguage = currentLanguage; 0208 } 0209 0210 void DvbEpgDialog::languageAdded(const QString lang) 0211 { 0212 if (lang != FIRST_LANG) 0213 languageBox->addItem(lang); 0214 } 0215 0216 void DvbEpgDialog::entryActivated(const QModelIndex &index) 0217 { 0218 const DvbSharedEpgEntry &entry = epgTableModel->value(index.row()); 0219 0220 if (!entry.isValid()) { 0221 contentLabel->setText(QString()); 0222 return; 0223 } 0224 0225 QString text = "<font color=#008000 size=\"+1\">" + entry->title(currentLanguage) + "</font>"; 0226 0227 if (!entry->subheading().isEmpty()) { 0228 text += "<br/><font color=#808000>" + entry->subheading(currentLanguage) + "</font>"; 0229 } 0230 0231 QDateTime begin = entry->begin.toLocalTime(); 0232 QTime end = entry->begin.addSecs(QTime(0, 0, 0).secsTo(entry->duration)).toLocalTime().time(); 0233 text += "<br/><br/><font color=#800080>" + QLocale().toString(begin, QLocale::LongFormat) + " - " + QLocale().toString(end) + "</font>"; 0234 0235 if (!entry->details(currentLanguage).isEmpty() && entry->details(currentLanguage) != entry->title(currentLanguage)) { 0236 text += "<br/><br/>" + entry->details(currentLanguage); 0237 } 0238 0239 if (!entry->content.isEmpty()) { 0240 text += "<br/><br/><font color=#000080>" + entry->content + "</font>"; 0241 } 0242 if (!entry->parental.isEmpty()) { 0243 text += "<br/><br/><font color=#800000>" + entry->parental + "</font>"; 0244 } 0245 0246 contentLabel->setText(text); 0247 } 0248 0249 void DvbEpgDialog::checkEntry() 0250 { 0251 if (!epgView->currentIndex().isValid()) { 0252 // FIXME workaround --> file bug 0253 contentLabel->setText(QString()); 0254 } 0255 } 0256 0257 void DvbEpgDialog::scheduleProgram() 0258 { 0259 const DvbSharedEpgEntry &entry = epgTableModel->value(epgView->currentIndex()); 0260 0261 if (entry.isValid()) { 0262 manager->getEpgModel()->scheduleProgram(entry, manager->getBeginMargin(), 0263 manager->getEndMargin()); 0264 } 0265 } 0266 0267 void DvbEpgDialog::changeShowMode() 0268 { 0269 epgChannelTableModel->setViewMode(showBox->isChecked()); 0270 } 0271 0272 bool DvbEpgEntryLessThan::operator()(const DvbSharedEpgEntry &x, const DvbSharedEpgEntry &y) const 0273 { 0274 if (x->channel != y->channel) { 0275 return (x->channel->name.localeAwareCompare(y->channel->name) < 0); 0276 } 0277 0278 if (x->begin != y->begin) { 0279 return (x->begin < y->begin); 0280 } 0281 0282 if (x->duration != y->duration) { 0283 return (x->duration < y->duration); 0284 } 0285 0286 if (x->title(FIRST_LANG) != y->title(FIRST_LANG)) { 0287 return (x->title(FIRST_LANG) < y->title(FIRST_LANG)); 0288 } 0289 0290 if (x->subheading(FIRST_LANG) != y->subheading(FIRST_LANG)) { 0291 return (x->subheading(FIRST_LANG) < y->subheading(FIRST_LANG)); 0292 } 0293 0294 if (x->details(FIRST_LANG) < y->details(FIRST_LANG)) { 0295 return (x->details(FIRST_LANG) < y->details(FIRST_LANG)); 0296 } 0297 0298 return (x < y); 0299 } 0300 0301 DvbEpgChannelTableModel::DvbEpgChannelTableModel(QObject *parent) : 0302 TableModel<DvbEpgChannelTableModelHelper>(parent) 0303 { 0304 } 0305 0306 DvbEpgChannelTableModel::~DvbEpgChannelTableModel() 0307 { 0308 } 0309 0310 void DvbEpgChannelTableModel::setViewMode(bool enableEmptyEpgChannels) 0311 { 0312 if (enableEmptyEpgChannels) { 0313 DvbChannelModel *channelModel = manager->getChannelModel(); 0314 reset(channelModel->getChannels()); 0315 } else { 0316 DvbEpgModel *epgModel = manager->getEpgModel(); 0317 resetFromKeys(epgModel->getEpgChannels()); 0318 } 0319 } 0320 0321 void DvbEpgChannelTableModel::setManager(DvbManager *_manager) 0322 { 0323 manager = _manager; 0324 DvbEpgModel *epgModel = manager->getEpgModel(); 0325 connect(epgModel, SIGNAL(epgChannelAdded(DvbSharedChannel)), 0326 this, SLOT(epgChannelAdded(DvbSharedChannel))); 0327 connect(epgModel, SIGNAL(epgChannelRemoved(DvbSharedChannel)), 0328 this, SLOT(epgChannelRemoved(DvbSharedChannel))); 0329 // theoretically we should monitor the channel model for updated channels, 0330 // but it's very unlikely that this has practical relevance 0331 0332 QHeaderView *headerView = manager->getChannelView()->header(); 0333 DvbChannelLessThan::SortOrder sortOrder; 0334 0335 if (headerView->sortIndicatorOrder() == Qt::AscendingOrder) { 0336 if (headerView->sortIndicatorSection() == 0) { 0337 sortOrder = DvbChannelLessThan::ChannelNameAscending; 0338 } else { 0339 sortOrder = DvbChannelLessThan::ChannelNumberAscending; 0340 } 0341 } else { 0342 if (headerView->sortIndicatorSection() == 0) { 0343 sortOrder = DvbChannelLessThan::ChannelNameDescending; 0344 } else { 0345 sortOrder = DvbChannelLessThan::ChannelNumberDescending; 0346 } 0347 } 0348 0349 internalSort(sortOrder); 0350 0351 setViewMode(false); 0352 } 0353 0354 QVariant DvbEpgChannelTableModel::data(const QModelIndex &index, int role) const 0355 { 0356 const DvbSharedChannel &channel = value(index); 0357 0358 if (channel.isValid() && (role == Qt::DisplayRole) && (index.column() == 0)) { 0359 return channel->name; 0360 } 0361 0362 return QVariant(); 0363 } 0364 0365 QVariant DvbEpgChannelTableModel::headerData(int section, Qt::Orientation orientation, 0366 int role) const 0367 { 0368 if ((section == 0) && (orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) { 0369 return i18nc("@title:column tv show", "Channel"); 0370 } 0371 0372 return QVariant(); 0373 } 0374 0375 void DvbEpgChannelTableModel::epgChannelAdded(const DvbSharedChannel &channel) 0376 { 0377 insert(channel); 0378 } 0379 0380 void DvbEpgChannelTableModel::epgChannelRemoved(const DvbSharedChannel &channel) 0381 { 0382 remove(channel); 0383 } 0384 0385 bool DvbEpgTableModelHelper::filterAcceptsItem(const DvbSharedEpgEntry &entry) const 0386 { 0387 switch (filterType) { 0388 case ChannelFilter: 0389 return (entry->channel == channelFilter); 0390 case ContentFilter: 0391 return ((contentFilter.indexIn(entry->title(FIRST_LANG)) >= 0) || 0392 (contentFilter.indexIn(entry->subheading(FIRST_LANG)) >= 0) || 0393 (contentFilter.indexIn(entry->details(FIRST_LANG)) >= 0)); 0394 } 0395 0396 return false; 0397 } 0398 0399 DvbEpgTableModel::DvbEpgTableModel(QObject *parent) : TableModel<DvbEpgTableModelHelper>(parent), 0400 epgModel(NULL), contentFilterEventPending(false) 0401 { 0402 helper.contentFilter.setCaseSensitivity(Qt::CaseInsensitive); 0403 } 0404 0405 DvbEpgTableModel::~DvbEpgTableModel() 0406 { 0407 } 0408 0409 void DvbEpgTableModel::setEpgModel(DvbEpgModel *epgModel_) 0410 { 0411 if (epgModel != NULL) { 0412 qCWarning(logEpg, "EPG model already set"); 0413 return; 0414 } 0415 0416 epgModel = epgModel_; 0417 connect(epgModel, SIGNAL(entryAdded(DvbSharedEpgEntry)), 0418 this, SLOT(entryAdded(DvbSharedEpgEntry))); 0419 connect(epgModel, SIGNAL(entryAboutToBeUpdated(DvbSharedEpgEntry)), 0420 this, SLOT(entryAboutToBeUpdated(DvbSharedEpgEntry))); 0421 connect(epgModel, SIGNAL(entryUpdated(DvbSharedEpgEntry)), 0422 this, SLOT(entryUpdated(DvbSharedEpgEntry))); 0423 connect(epgModel, SIGNAL(entryRemoved(DvbSharedEpgEntry)), 0424 this, SLOT(entryRemoved(DvbSharedEpgEntry))); 0425 } 0426 0427 void DvbEpgTableModel::setChannelFilter(const DvbSharedChannel &channel) 0428 { 0429 helper.channelFilter = channel; 0430 helper.contentFilter.setPattern(QString()); 0431 helper.filterType = DvbEpgTableModelHelper::ChannelFilter; 0432 reset(epgModel->getEntries()); 0433 } 0434 0435 void DvbEpgTableModel::setLanguage(QString lang) 0436 { 0437 currentLanguage = lang; 0438 reset(epgModel->getEntries()); 0439 } 0440 0441 QVariant DvbEpgTableModel::data(const QModelIndex &index, int role) const 0442 { 0443 const DvbSharedEpgEntry &entry = value(index); 0444 0445 if (entry.isValid()) { 0446 switch (role) { 0447 case Qt::DecorationRole: 0448 if ((index.column() == 2) && entry->recording.isValid()) { 0449 return QIcon::fromTheme(QLatin1String("media-record"), QIcon(":media-record")); 0450 } 0451 0452 break; 0453 case Qt::DisplayRole: 0454 switch (index.column()) { 0455 case 0: 0456 return QLocale().toString((entry->begin.toLocalTime()), QLocale::NarrowFormat); 0457 case 1: 0458 return entry->duration.toString("HH:mm"); 0459 case 2: 0460 return entry->title(currentLanguage); 0461 case 3: 0462 return entry->channel->name; 0463 } 0464 0465 break; 0466 } 0467 } 0468 0469 return QVariant(); 0470 } 0471 0472 QVariant DvbEpgTableModel::headerData(int section, Qt::Orientation orientation, int role) const 0473 { 0474 if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) { 0475 switch (section) { 0476 case 0: 0477 return i18nc("@title:column tv show", "Start"); 0478 case 1: 0479 return i18nc("@title:column tv show", "Duration"); 0480 case 2: 0481 return i18nc("@title:column tv show", "Title"); 0482 case 3: 0483 return i18nc("@title:column tv show", "Channel"); 0484 } 0485 } 0486 0487 return QVariant(); 0488 } 0489 0490 void DvbEpgTableModel::setContentFilter(const QString &pattern) 0491 { 0492 helper.channelFilter = DvbSharedChannel(); 0493 helper.contentFilter.setPattern(pattern); 0494 0495 if (!pattern.isEmpty()) { 0496 helper.filterType = DvbEpgTableModelHelper::ContentFilter; 0497 0498 if (!contentFilterEventPending) { 0499 contentFilterEventPending = true; 0500 QCoreApplication::postEvent(this, new QEvent(QEvent::User), 0501 Qt::LowEventPriority); 0502 } 0503 } else { 0504 // use channel filter so that content won't be unnecessarily filtered 0505 helper.filterType = DvbEpgTableModelHelper::ChannelFilter; 0506 reset(QMap<DvbEpgEntryId, DvbSharedEpgEntry>()); 0507 } 0508 } 0509 0510 void DvbEpgTableModel::entryAdded(const DvbSharedEpgEntry &entry) 0511 { 0512 insert(entry); 0513 } 0514 0515 void DvbEpgTableModel::entryAboutToBeUpdated(const DvbSharedEpgEntry &entry) 0516 { 0517 aboutToUpdate(entry); 0518 } 0519 0520 void DvbEpgTableModel::entryUpdated(const DvbSharedEpgEntry &entry) 0521 { 0522 update(entry); 0523 } 0524 0525 void DvbEpgTableModel::entryRemoved(const DvbSharedEpgEntry &entry) 0526 { 0527 remove(entry); 0528 } 0529 0530 void DvbEpgTableModel::customEvent(QEvent *event) 0531 { 0532 Q_UNUSED(event) 0533 contentFilterEventPending = false; 0534 0535 if (helper.filterType == DvbEpgTableModelHelper::ContentFilter) { 0536 reset(epgModel->getEntries()); 0537 } 0538 } 0539 0540 #include "moc_dvbepgdialog.cpp" 0541 #include "moc_dvbepgdialog_p.cpp"