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

0001 /*
0002  * dvbrecordingdialog.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 <QDialogButtonBox>
0029 #include <QHeaderView>
0030 #include <QLabel>
0031 #include <QLineEdit>
0032 #include <QLocale>
0033 #include <QPushButton>
0034 #include <QVBoxLayout>
0035 
0036 #include "../datetimeedit.h"
0037 #include "dvbchanneldialog.h"
0038 #include "dvbmanager.h"
0039 #include "dvbrecordingdialog.h"
0040 #include "dvbrecordingdialog_p.h"
0041 
0042 DvbRecordingDialog::DvbRecordingDialog(DvbManager *manager_, QWidget *parent) : QDialog(parent),
0043     manager(manager_)
0044 {
0045     setWindowTitle(i18nc("@title:window", "Recording Schedule"));
0046 
0047     QWidget *mainWidget = new QWidget(this);
0048     QBoxLayout *mainLayout = new QVBoxLayout;
0049     setLayout(mainLayout);
0050     mainLayout->addWidget(mainWidget);
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 
0056     QWidget *widget = new QWidget(this);
0057 
0058     model = new DvbRecordingTableModel(this);
0059     treeView = new QTreeView(widget);
0060     treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0061     treeView->setContextMenuPolicy(Qt::ActionsContextMenu);
0062     treeView->setModel(model);
0063     treeView->setRootIsDecorated(false);
0064     treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
0065     treeView->sortByColumn(2, Qt::AscendingOrder);
0066     treeView->setSortingEnabled(true);
0067     model->setRecordingModel(manager->getRecordingModel());
0068 
0069     QBoxLayout *boxLayout = new QHBoxLayout();
0070     QAction *action = new QAction(QIcon::fromTheme(QLatin1String("list-add"), QIcon(":list-add")), i18nc("@action", "New"), widget);
0071     connect(action, SIGNAL(triggered()), this, SLOT(newRecording()));
0072     treeView->addAction(action);
0073     QPushButton *pushButton = new QPushButton(action->icon(), action->text(), widget);
0074     connect(pushButton, SIGNAL(clicked()), this, SLOT(newRecording()));
0075     boxLayout->addWidget(pushButton);
0076 
0077     action = new QAction(QIcon::fromTheme(QLatin1String("configure"), QIcon(":configure")), i18nc("@action", "Edit"), widget);
0078     connect(action, SIGNAL(triggered()), this, SLOT(editRecording()));
0079     treeView->addAction(action);
0080     pushButton = new QPushButton(action->icon(), action->text(), widget);
0081     connect(pushButton, SIGNAL(clicked()), this, SLOT(editRecording()));
0082     boxLayout->addWidget(pushButton);
0083 
0084     action = new QAction(QIcon::fromTheme(QLatin1String("edit-delete"), QIcon(":edit-delete")), i18nc("@action", "Remove"), widget);
0085     connect(action, SIGNAL(triggered()), this, SLOT(removeRecording()));
0086     treeView->addAction(action);
0087     pushButton = new QPushButton(action->icon(), action->text(), widget);
0088     connect(pushButton, SIGNAL(clicked()), this, SLOT(removeRecording()));
0089     boxLayout->addWidget(pushButton);
0090     boxLayout->addStretch();
0091 
0092     mainLayout->addLayout(boxLayout);
0093     mainLayout->addWidget(treeView);
0094     mainLayout->addWidget(buttonBox);
0095 
0096     resize(100 * fontMetrics().averageCharWidth(), 20 * fontMetrics().height());
0097 }
0098 
0099 DvbRecordingDialog::~DvbRecordingDialog()
0100 {
0101 }
0102 
0103 void DvbRecordingDialog::showDialog(DvbManager *manager_, QWidget *parent)
0104 {
0105     QDialog *dialog = new DvbRecordingDialog(manager_, parent);
0106     dialog->setAttribute(Qt::WA_DeleteOnClose, true);
0107     dialog->setModal(true);
0108     dialog->show();
0109 }
0110 
0111 void DvbRecordingDialog::newRecording()
0112 {
0113     QDialog *dialog = new DvbRecordingEditor(manager, DvbSharedRecording(), this);
0114     dialog->setAttribute(Qt::WA_DeleteOnClose, true);
0115     dialog->setModal(true);
0116     dialog->show();
0117 }
0118 
0119 void DvbRecordingDialog::editRecording()
0120 {
0121     QModelIndex index = treeView->currentIndex();
0122 
0123     if (index.isValid()) {
0124         QDialog *dialog = new DvbRecordingEditor(manager, model->value(index), this);
0125         dialog->setAttribute(Qt::WA_DeleteOnClose, true);
0126         dialog->setModal(true);
0127         dialog->show();
0128     }
0129 }
0130 
0131 void DvbRecordingDialog::removeRecording()
0132 {
0133     QSet<DvbSharedRecording> recordings;
0134 
0135     foreach (const QModelIndex &modelIndex, treeView->selectionModel()->selectedIndexes()) {
0136         recordings.insert(model->value(modelIndex));
0137     }
0138 
0139     DvbRecordingModel *recordingModel = manager->getRecordingModel();
0140 
0141     foreach (const DvbSharedRecording &recording, recordings) {
0142         recordingModel->addToUnwantedRecordings(recording);
0143         recordingModel->removeRecording(recording);
0144     }
0145 }
0146 
0147 void DvbRecordingLessThan::setSortOrder(SortOrder sortOrder_)
0148 {
0149     for (int index = 0; index < 4; ++index) {
0150         if ((sortOrder[index] >> 1) == (sortOrder_ >> 1)) {
0151             for (int j = index; j > 0; --j) {
0152                 sortOrder[j] = sortOrder[j - 1];
0153             }
0154 
0155             sortOrder[0] = sortOrder_;
0156             return;
0157         }
0158     }
0159 }
0160 
0161 bool DvbRecordingLessThan::operator()(const DvbSharedRecording &x,
0162     const DvbSharedRecording &y) const
0163 {
0164     for (int index = 0; index < 4; ++index) {
0165         switch (sortOrder[index]) {
0166         case NameAscending:
0167             if (x->name != y->name) {
0168                 return (x->name.localeAwareCompare(y->name) < 0);
0169             }
0170 
0171             break;
0172         case NameDescending:
0173             if (x->name != y->name) {
0174                 return (x->name.localeAwareCompare(y->name) > 0);
0175             }
0176 
0177             break;
0178         case ChannelAscending:
0179             if (x->channel->name != y->channel->name) {
0180                 return (x->channel->name.localeAwareCompare(y->channel->name) < 0);
0181             }
0182 
0183             break;
0184         case ChannelDescending:
0185             if (x->channel->name != y->channel->name) {
0186                 return (x->channel->name.localeAwareCompare(y->channel->name) > 0);
0187             }
0188 
0189             break;
0190         case BeginAscending:
0191             if (x->begin != y->begin) {
0192                 return (x->begin < y->begin);
0193             }
0194 
0195             break;
0196         case BeginDescending:
0197             if (x->begin != y->begin) {
0198                 return (x->begin > y->begin);
0199             }
0200 
0201             break;
0202         case DurationAscending:
0203             if (x->duration != y->duration) {
0204                 return (x->duration < y->duration);
0205             }
0206 
0207             break;
0208         case DurationDescending:
0209             if (x->duration != y->duration) {
0210                 return (x->duration > y->duration);
0211             }
0212 
0213             break;
0214         }
0215     }
0216 
0217     return (x < y);
0218 }
0219 
0220 DvbRecordingTableModel::DvbRecordingTableModel(QObject *parent) :
0221     TableModel<DvbRecordingTableModelHelper>(parent), recordingModel(NULL)
0222 {
0223 }
0224 
0225 DvbRecordingTableModel::~DvbRecordingTableModel()
0226 {
0227 }
0228 
0229 void DvbRecordingTableModel::setRecordingModel(DvbRecordingModel *recordingModel_)
0230 {
0231     if (recordingModel != NULL) {
0232         qCWarning(logDvb, "Recording model already set");
0233         return;
0234     }
0235 
0236     recordingModel = recordingModel_;
0237     connect(recordingModel, SIGNAL(recordingAdded(DvbSharedRecording)),
0238         this, SLOT(recordingAdded(DvbSharedRecording)));
0239     connect(recordingModel, SIGNAL(recordingAboutToBeUpdated(DvbSharedRecording)),
0240         this, SLOT(recordingAboutToBeUpdated(DvbSharedRecording)));
0241     connect(recordingModel, SIGNAL(recordingUpdated(DvbSharedRecording)),
0242         this, SLOT(recordingUpdated(DvbSharedRecording)));
0243     connect(recordingModel, SIGNAL(recordingRemoved(DvbSharedRecording)),
0244         this, SLOT(recordingRemoved(DvbSharedRecording)));
0245     reset(recordingModel->getRecordings());
0246 }
0247 
0248 QVariant DvbRecordingTableModel::headerData(int section, Qt::Orientation orientation,
0249     int role) const
0250 {
0251     if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) {
0252         switch (section) {
0253         case 0:
0254             return i18nc("@title:column recording", "Name");
0255         case 1:
0256             return i18nc("@title:column tv show", "Channel");
0257         case 2:
0258             return i18nc("@title:column tv show", "Start");
0259         case 3:
0260             return i18nc("@title:column tv show", "Duration");
0261         case 4:
0262             return i18nc("@title:column tv show", "Disabled");
0263         }
0264     }
0265 
0266     return QVariant();
0267 }
0268 
0269 QVariant DvbRecordingTableModel::data(const QModelIndex &index, int role) const
0270 {
0271     const DvbSharedRecording &recording = value(index);
0272 
0273     if (recording.isValid()) {
0274         switch (role) {
0275         case Qt::DecorationRole:
0276             if (index.column() == 0) {
0277                 if (recording->disabled) {
0278                     return QIcon::fromTheme(QLatin1String("dialog-error"), QIcon(":dialog-error"));
0279                 }
0280                 switch (recording->status) {
0281                 case DvbRecording::Inactive:
0282                     break;
0283                 case DvbRecording::Recording:
0284                     return QIcon::fromTheme(QLatin1String("media-record"), QIcon(":media-record"));
0285                 case DvbRecording::Error:
0286                     return QIcon::fromTheme(QLatin1String("dialog-error"), QIcon(":dialog-error"));
0287                 }
0288 
0289                 if (recording->repeat != 0) {
0290                     return QIcon::fromTheme(QLatin1String("view-refresh"), QIcon(":view-refresh"));
0291                 }
0292             }
0293 
0294             break;
0295         case Qt::DisplayRole:
0296             switch (index.column()) {
0297             case 0:
0298                 return recording->name;
0299             case 1:
0300                 return recording->channel->name;
0301             case 2:
0302                 return QLocale().toString((recording->begin.toLocalTime()), QLocale::ShortFormat);
0303             case 3:
0304                 return recording->duration.toString("HH:mm");
0305             case 4:
0306                 if (recording->disabled) {
0307                     return i18n("Disabled");
0308                 }
0309                 return i18n("Enabled");
0310             }
0311             break;
0312         }
0313     }
0314 
0315     return QVariant();
0316 }
0317 
0318 void DvbRecordingTableModel::sort(int column, Qt::SortOrder order)
0319 {
0320     DvbRecordingLessThan::SortOrder sortOrder;
0321 
0322     if (order == Qt::AscendingOrder) {
0323         switch (column) {
0324         case 0:
0325             sortOrder = DvbRecordingLessThan::NameAscending;
0326             break;
0327         case 1:
0328             sortOrder = DvbRecordingLessThan::ChannelAscending;
0329             break;
0330         case 2:
0331             sortOrder = DvbRecordingLessThan::BeginAscending;
0332             break;
0333         case 3:
0334             sortOrder = DvbRecordingLessThan::DurationAscending;
0335             break;
0336         default:
0337             return;
0338         }
0339     } else {
0340         switch (column) {
0341         case 0:
0342             sortOrder = DvbRecordingLessThan::NameDescending;
0343             break;
0344         case 1:
0345             sortOrder = DvbRecordingLessThan::ChannelDescending;
0346             break;
0347         case 2:
0348             sortOrder = DvbRecordingLessThan::BeginDescending;
0349             break;
0350         case 3:
0351             sortOrder = DvbRecordingLessThan::DurationDescending;
0352             break;
0353         default:
0354             return;
0355         }
0356     }
0357 
0358     internalSort(sortOrder);
0359 }
0360 
0361 void DvbRecordingTableModel::recordingAdded(const DvbSharedRecording &recording)
0362 {
0363     insert(recording);
0364 }
0365 
0366 void DvbRecordingTableModel::recordingAboutToBeUpdated(const DvbSharedRecording &recording)
0367 {
0368     aboutToUpdate(recording);
0369 }
0370 
0371 void DvbRecordingTableModel::recordingUpdated(const DvbSharedRecording &recording)
0372 {
0373     update(recording);
0374 }
0375 
0376 void DvbRecordingTableModel::recordingRemoved(const DvbSharedRecording &recording)
0377 {
0378     remove(recording);
0379 }
0380 
0381 DvbRecordingEditor::DvbRecordingEditor(DvbManager *manager_, const DvbSharedRecording &recording_,
0382     QWidget *parent) : QDialog(parent), manager(manager_), recording(recording_)
0383 {
0384     setWindowTitle(i18nc("@title:window recording", "Edit Schedule Entry"));
0385     QWidget *widget = new QWidget(this);
0386     QGridLayout *gridLayout = new QGridLayout(widget);
0387     QBoxLayout *mainLayout = new QVBoxLayout;
0388     setLayout(mainLayout);
0389 
0390     nameEdit = new QLineEdit(widget);
0391     mainLayout->addWidget(nameEdit);
0392     gridLayout->addWidget(nameEdit, 0, 1);
0393 
0394     QLabel *label = new QLabel(i18nc("@label recording", "Name:"), widget);
0395     mainLayout->addWidget(label);
0396     label->setBuddy(nameEdit);
0397     gridLayout->addWidget(label, 0, 0);
0398 
0399     channelBox = new QComboBox(widget);
0400     mainLayout->addWidget(channelBox);
0401     DvbChannelTableModel *channelModel = new DvbChannelTableModel(widget);
0402     QHeaderView *header = manager->getChannelView()->header();
0403     channelModel->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
0404     channelModel->setChannelModel(manager->getChannelModel());
0405     channelBox->setModel(channelModel);
0406     gridLayout->addWidget(channelBox, 1, 1);
0407 
0408     label = new QLabel(i18nc("@label tv show", "Channel:"), widget);
0409     mainLayout->addWidget(label);
0410     label->setBuddy(channelBox);
0411     gridLayout->addWidget(label, 1, 0);
0412 
0413     beginEdit = new DateTimeEdit(widget);
0414     mainLayout->addWidget(beginEdit);
0415     beginEdit->setCurrentSection(DateTimeEdit::HourSection);
0416     connect(beginEdit, SIGNAL(dateTimeChanged(QDateTime)),
0417         this, SLOT(beginChanged(QDateTime)));
0418     gridLayout->addWidget(beginEdit, 2, 1);
0419 
0420     label = new QLabel(i18nc("@label tv show", "Start:"), widget);
0421     mainLayout->addWidget(label);
0422     label->setBuddy(beginEdit);
0423     gridLayout->addWidget(label, 2, 0);
0424 
0425     durationEdit = new DurationEdit(widget);
0426     mainLayout->addWidget(durationEdit);
0427     connect(durationEdit, SIGNAL(timeChanged(QTime)), this, SLOT(durationChanged(QTime)));
0428     gridLayout->addWidget(durationEdit, 3, 1);
0429 
0430     label = new QLabel(i18nc("@label tv show", "Duration:"), widget);
0431     mainLayout->addWidget(label);
0432     label->setBuddy(durationEdit);
0433     gridLayout->addWidget(label, 3, 0);
0434 
0435     endEdit = new DateTimeEdit(widget);
0436     mainLayout->addWidget(endEdit);
0437     endEdit->setCurrentSection(DateTimeEdit::HourSection);
0438     connect(endEdit, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(endChanged(QDateTime)));
0439     gridLayout->addWidget(endEdit, 4, 1);
0440 
0441     label = new QLabel(i18nc("@label tv show", "End:"), widget);
0442     mainLayout->addWidget(label);
0443     label->setBuddy(endEdit);
0444     gridLayout->addWidget(label, 4, 0);
0445 
0446     gridLayout->addWidget(new QLabel(i18nc("@label recording", "Repeat:"), widget), 5, 0);
0447 
0448     QBoxLayout *boxLayout = new QHBoxLayout();
0449     QPushButton *pushButton =
0450         new QPushButton(i18nc("@action next to 'Repeat:'", "Never"), widget);
0451     connect(pushButton, SIGNAL(clicked()), this, SLOT(repeatNever()));
0452     boxLayout->addWidget(pushButton);
0453 
0454     pushButton = new QPushButton(i18nc("@action next to 'Repeat:'", "Daily"), widget);
0455     mainLayout->addWidget(pushButton);
0456     connect(pushButton, SIGNAL(clicked()), this, SLOT(repeatDaily()));
0457     boxLayout->addWidget(pushButton);
0458     gridLayout->addLayout(boxLayout, 5, 1);
0459 
0460     QGridLayout *dayLayout = new QGridLayout();
0461 
0462     for (int i = 0; i < 7; ++i) {
0463         dayCheckBoxes[i] = new QCheckBox(
0464             QLocale::system().dayName(i + 1, QLocale::ShortFormat), widget);
0465         dayLayout->addWidget(dayCheckBoxes[i], (i >> 2), (i & 0x03));
0466     }
0467 
0468     gridLayout->addLayout(dayLayout, 6, 1);
0469     mainLayout->addWidget(widget);
0470 
0471     if (recording.isValid()) {
0472         nameEdit->setText(recording->name);
0473         channelBox->setCurrentIndex(channelModel->find(recording->channel).row());
0474         beginEdit->setDateTime(recording->begin.toLocalTime());
0475         durationEdit->setTime(recording->duration);
0476 
0477         for (int i = 0; i < 7; ++i) {
0478             if ((recording->repeat & (1 << i)) != 0) {
0479                 dayCheckBoxes[i]->setChecked(true);
0480             }
0481         }
0482 
0483         switch (recording->status) {
0484         case DvbRecording::Inactive:
0485             break;
0486         case DvbRecording::Recording:
0487         case DvbRecording::Error:
0488             nameEdit->setEnabled(false);
0489             channelBox->setEnabled(false);
0490             beginEdit->setEnabled(false);
0491             break;
0492         }
0493     } else {
0494         channelBox->setCurrentIndex(-1);
0495         beginEdit->setDateTime(QDateTime::currentDateTime());
0496         durationEdit->setTime(QTime(2, 0));
0497     }
0498 
0499     buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0500     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
0501     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0502     mainLayout->addWidget(buttonBox);
0503 
0504     checkValidity();
0505 
0506     connect(nameEdit, SIGNAL(textChanged(QString)), this, SLOT(checkValidity()));
0507     connect(channelBox, SIGNAL(currentIndexChanged(int)), this, SLOT(checkValidity()));
0508 
0509     if (nameEdit->text().isEmpty()) {
0510         nameEdit->setFocus();
0511     }
0512 }
0513 
0514 DvbRecordingEditor::~DvbRecordingEditor()
0515 {
0516 }
0517 
0518 void DvbRecordingEditor::beginChanged(const QDateTime &begin)
0519 {
0520     // attention: setDateTimeRange and setDateTime influence each other!
0521     QTime duration = durationEdit->time();
0522     endEdit->setDateTimeRange(begin, begin.addSecs((23 * 60 + 59) * 60));
0523     endEdit->setDateTime(begin.addSecs(QTime(0, 0, 0).secsTo(duration)));
0524 }
0525 
0526 void DvbRecordingEditor::durationChanged(const QTime &duration)
0527 {
0528     endEdit->setDateTime(beginEdit->dateTime().addSecs(QTime(0, 0, 0).secsTo(duration)));
0529 }
0530 
0531 void DvbRecordingEditor::endChanged(const QDateTime &end)
0532 {
0533     durationEdit->setTime(QTime(0, 0, 0).addSecs(beginEdit->dateTime().secsTo(end)));
0534 }
0535 
0536 void DvbRecordingEditor::repeatNever()
0537 {
0538     for (int i = 0; i < 7; ++i) {
0539         dayCheckBoxes[i]->setChecked(false);
0540     }
0541 }
0542 
0543 void DvbRecordingEditor::repeatDaily()
0544 {
0545     for (int i = 0; i < 7; ++i) {
0546         dayCheckBoxes[i]->setChecked(true);
0547     }
0548 }
0549 
0550 void DvbRecordingEditor::checkValidity()
0551 {
0552     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
0553                 !nameEdit->text().isEmpty() &&
0554                 (channelBox->currentIndex() != -1));
0555 }
0556 
0557 void DvbRecordingEditor::accept()
0558 {
0559     DvbRecording newRecording;
0560     newRecording.disabled = false;
0561     newRecording.priority = 100;
0562     newRecording.name = nameEdit->text();
0563     newRecording.channel =
0564         manager->getChannelModel()->findChannelByName(channelBox->currentText());
0565     newRecording.begin = beginEdit->dateTime().toUTC();
0566     newRecording.duration = durationEdit->time();
0567 
0568     for (int i = 0; i < 7; ++i) {
0569         if (dayCheckBoxes[i]->isChecked()) {
0570             newRecording.repeat |= (1 << i);
0571         }
0572     }
0573 
0574     if (!recording.isValid()) {
0575         manager->getRecordingModel()->addRecording(newRecording);
0576     } else {
0577         manager->getRecordingModel()->updateRecording(recording, newRecording);
0578     }
0579 
0580     QDialog::accept();
0581 }
0582 
0583 #include "moc_dvbrecordingdialog.cpp"
0584 #include "moc_dvbrecordingdialog_p.cpp"