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"