File indexing completed on 2024-04-21 05:45:51

0001 /*
0002     KT task editor window implementation
0003     --------------------------------------------------------------------
0004     SPDX-FileCopyrightText: 1999 Gary Meyer <gary@meyer.net>
0005     --------------------------------------------------------------------
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "taskEditorDialog.h"
0010 
0011 #include <QCheckBox>
0012 #include <QEvent>
0013 #include <QGridLayout>
0014 #include <QHBoxLayout>
0015 #include <QPalette>
0016 #include <QPushButton>
0017 #include <QStandardPaths>
0018 #include <QVBoxLayout>
0019 
0020 #include <QLocale>
0021 
0022 #include <QStyleOption>
0023 #include <QStylePainter>
0024 
0025 #include <KAcceleratorManager>
0026 #include <KLocalizedString>
0027 #include <KStandardShortcut>
0028 #include <QDialogButtonBox>
0029 #include <QPushButton>
0030 #include <kurlrequester.h>
0031 
0032 #include "cttask.h"
0033 #include "kcm_cron_debug.h"
0034 
0035 #include "crontabWidget.h"
0036 
0037 #include "kcronHelper.h"
0038 
0039 /**
0040  * TaskEditorDialog class implementation
0041  */
0042 
0043 TaskEditorDialog::TaskEditorDialog(CTTask *_ctTask, const QString &_caption, CrontabWidget *_crontabWidget)
0044     : QDialog(_crontabWidget)
0045 {
0046     setModal(true);
0047 
0048     // window
0049     setWindowIcon(QIcon::fromTheme(QStringLiteral("kcron")));
0050     setWindowTitle(_caption);
0051 
0052     mCtTask = _ctTask;
0053     mCrontabWidget = _crontabWidget;
0054 
0055     auto main = new QWidget(this);
0056     auto mainLayout = new QVBoxLayout(main);
0057     mainLayout->setContentsMargins(0, 0, 0, 0);
0058 
0059     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0060     mOkButton = buttonBox->button(QDialogButtonBox::Ok);
0061 
0062     auto dialogLayout = new QVBoxLayout();
0063     dialogLayout->addWidget(main);
0064     dialogLayout->addWidget(buttonBox);
0065     setLayout(dialogLayout);
0066 
0067     // top title widget
0068     mTitleWidget = new KTitleWidget(main);
0069     mTitleWidget->setText(i18n("Add or modify a scheduled task"));
0070     setupTitleWidget(i18n("<i>This task will be executed at the specified intervals.</i>"));
0071 
0072     mainLayout->addWidget(mTitleWidget);
0073 
0074     auto commandConfigurationLayout = new QGridLayout();
0075     mainLayout->addLayout(commandConfigurationLayout);
0076 
0077     // command
0078     auto labCommand = new QLabel(i18n("&Command:"), main);
0079     commandConfigurationLayout->addWidget(labCommand, 0, 0);
0080 
0081     auto commandLayout = new QHBoxLayout();
0082     mCommandIcon = new QLabel(main);
0083     mMissingCommandPixmap = QIcon::fromTheme(QStringLiteral("image-missing")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this));
0084     commandLayout->addWidget(mCommandIcon);
0085 
0086     mCommand = new KUrlRequester(main);
0087     labCommand->setBuddy(mCommand);
0088     commandLayout->addWidget(mCommand);
0089 
0090     mCommand->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly);
0091     mCommand->setUrl(QUrl::fromLocalFile(mCtTask->command));
0092 
0093     // Initialize special valid commands
0094     mSpecialValidCommands << QStringLiteral("cd");
0095 
0096     commandConfigurationLayout->addLayout(commandLayout, 0, 1);
0097 
0098     // User Combo
0099     auto userLabel = new QLabel(i18n("&Run as:"), main);
0100     commandConfigurationLayout->addWidget(userLabel, 1, 0);
0101 
0102     mUserCombo = new QComboBox(main);
0103 
0104     userLabel->setBuddy(mUserCombo);
0105     commandConfigurationLayout->addWidget(mUserCombo, 1, 1);
0106 
0107     // When in multiuser (system) mode, a user column is required.
0108     if (mCrontabWidget->tasksWidget()->needUserColumn()) {
0109         KCronHelper::initUserCombo(mUserCombo, mCrontabWidget, mCtTask->userLogin);
0110     } else {
0111         userLabel->hide();
0112         mUserCombo->hide();
0113     }
0114 
0115     // comment
0116     auto labComment = new QLabel(i18n("Co&mment:"), main);
0117     commandConfigurationLayout->addWidget(labComment, 2, 0, Qt::AlignTop);
0118 
0119     mLeComment = KCronHelper::createCommentEdit(main);
0120     labComment->setBuddy(mLeComment);
0121     commandConfigurationLayout->addWidget(mLeComment, 2, 1);
0122 
0123     mLeComment->setText(mCtTask->comment);
0124 
0125     auto checkboxesLayout = new QHBoxLayout();
0126     mainLayout->addLayout(checkboxesLayout);
0127 
0128     // enabled
0129     mChkEnabled = new QCheckBox(i18n("&Enable this task"), main);
0130     mChkEnabled->setChecked(mCtTask->enabled);
0131     checkboxesLayout->addWidget(mChkEnabled);
0132 
0133     // @reboot
0134     mChkReboot = new QCheckBox(i18n("Run at system &bootup"), main);
0135     mChkReboot->setChecked(mCtTask->reboot);
0136     checkboxesLayout->addWidget(mChkReboot);
0137 
0138     // Every day
0139     bool everyDay = isEveryDay();
0140     mCbEveryDay = new QCheckBox(i18n("Run &every day"), main);
0141     mCbEveryDay->setChecked(everyDay);
0142     checkboxesLayout->addWidget(mCbEveryDay);
0143 
0144     auto schedulingLayout = new QHBoxLayout();
0145     mainLayout->addLayout(schedulingLayout);
0146 
0147     auto monthLayout = new QVBoxLayout();
0148     schedulingLayout->addLayout(monthLayout);
0149 
0150     // months
0151     mBgMonth = createMonthsGroup(main);
0152     monthLayout->addWidget(mBgMonth);
0153     monthLayout->addStretch(1);
0154 
0155     auto v1 = new QVBoxLayout();
0156     schedulingLayout->addLayout(v1);
0157 
0158     // days of the month
0159     mBgDayOfMonth = createDaysOfMonthGroup(main);
0160     v1->addWidget(mBgDayOfMonth);
0161 
0162     // days of the week
0163     mBgDayOfWeek = createDaysOfWeekGroup(main);
0164     v1->addWidget(mBgDayOfWeek);
0165 
0166     v1->addStretch(1);
0167 
0168     auto v2 = new QVBoxLayout();
0169     schedulingLayout->addLayout(v2);
0170 
0171     mHoursGroup = createHoursGroup(main);
0172     v2->addWidget(mHoursGroup);
0173 
0174     createMinutesGroup(main);
0175     v2->addWidget(mMinutesGroup);
0176 
0177     v2->addStretch(1);
0178 
0179     // schedulingLayout->addStretch(1);
0180 
0181     mCommand->setFocus();
0182 
0183     connect(mCommand, &KUrlRequester::textChanged, this, &TaskEditorDialog::slotWizard);
0184 
0185     connect(mChkEnabled, &QCheckBox::clicked, this, &TaskEditorDialog::slotEnabledChanged);
0186     connect(mChkEnabled, &QCheckBox::clicked, this, &TaskEditorDialog::slotWizard);
0187 
0188     connect(mChkReboot, &QCheckBox::clicked, this, &TaskEditorDialog::slotRebootChanged);
0189     connect(mChkReboot, &QCheckBox::clicked, this, &TaskEditorDialog::slotWizard);
0190 
0191     connect(mCbEveryDay, &QCheckBox::clicked, this, &TaskEditorDialog::slotDailyChanged);
0192     connect(mCbEveryDay, &QCheckBox::clicked, this, &TaskEditorDialog::slotWizard);
0193 
0194     connect(buttonBox, &QDialogButtonBox::accepted, this, &TaskEditorDialog::slotOK);
0195     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0196 
0197     if (!mChkEnabled->isChecked()) {
0198         slotEnabledChanged();
0199     } else if (mChkReboot->isChecked()) {
0200         slotRebootChanged();
0201     } else if (mCbEveryDay->isChecked()) {
0202         slotDailyChanged();
0203     }
0204 
0205     slotMonthChanged();
0206     slotDayOfMonthChanged();
0207     slotDayOfWeekChanged();
0208     slotHourChanged();
0209     slotMinuteChanged();
0210 
0211     slotWizard();
0212 }
0213 
0214 TaskEditorDialog::~TaskEditorDialog()
0215 {
0216 }
0217 
0218 bool TaskEditorDialog::isEveryDay()
0219 {
0220     for (int dw = CTDayOfWeek::MINIMUM; dw <= CTDayOfWeek::MAXIMUM; dw++) {
0221         if (!mCtTask->dayOfWeek.isEnabled(dw)) {
0222             return false;
0223         }
0224     }
0225 
0226     for (int mo = mCtTask->month.minimum(); mo <= mCtTask->month.maximum(); mo++) {
0227         if (!mCtTask->month.isEnabled(mo)) {
0228             return false;
0229         }
0230     }
0231 
0232     for (int dm = CTDayOfMonth::MINIMUM; dm <= CTDayOfMonth::MAXIMUM; dm++) {
0233         if (!mCtTask->dayOfMonth.isEnabled(dm)) {
0234             return false;
0235         }
0236     }
0237 
0238     return true;
0239 }
0240 
0241 QGroupBox *TaskEditorDialog::createDaysOfMonthGroup(QWidget *main)
0242 {
0243     auto daysOfMonthGroup = new QGroupBox(i18n("Days of Month"), main);
0244     auto daysOfMonthLayout = new QGridLayout(daysOfMonthGroup);
0245 
0246     int dm = CTDayOfMonth::MINIMUM;
0247     for (int row = 0; row < 5; ++row) {
0248         for (int column = 0; column < 7; ++column) {
0249             auto day = new NumberPushButton(true, daysOfMonthGroup);
0250             day->setText(QString::number(dm));
0251             day->setCheckable(true);
0252             day->setChecked(mCtTask->dayOfMonth.isEnabled(dm));
0253             mDayOfMonthButtons[dm] = day;
0254 
0255             connect(mDayOfMonthButtons[dm], &QAbstractButton::clicked, this, &TaskEditorDialog::slotDayOfMonthChanged);
0256             connect(mDayOfMonthButtons[dm], &QAbstractButton::clicked, this, &TaskEditorDialog::slotWizard);
0257 
0258             daysOfMonthLayout->addWidget(day, row, column);
0259 
0260             if (dm == CTDayOfMonth::MAXIMUM) {
0261                 break;
0262                 break;
0263             }
0264 
0265             dm++;
0266         }
0267     }
0268 
0269     mAllDaysOfMonth = new SetOrClearAllButton(daysOfMonthGroup, SetOrClearAllButton::SET_ALL);
0270     daysOfMonthLayout->addWidget(mAllDaysOfMonth, 4, 3, 1, 4);
0271 
0272     connect(mAllDaysOfMonth, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotAllDaysOfMonth);
0273     connect(mAllDaysOfMonth, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotWizard);
0274 
0275     return daysOfMonthGroup;
0276 }
0277 
0278 QGroupBox *TaskEditorDialog::createMonthsGroup(QWidget *main)
0279 {
0280     auto monthsGroup = new QGroupBox(i18n("Months"), main);
0281     auto monthsLayout = new QGridLayout(monthsGroup);
0282 
0283     int column = 0;
0284     int row = 0;
0285 
0286     for (int mo = CTMonth::MINIMUM; mo <= CTMonth::MAXIMUM; mo++) {
0287         mMonthButtons[mo] = new NumberPushButton(monthsGroup);
0288         mMonthButtons[mo]->setText(mCtTask->month.getName(mo));
0289         mMonthButtons[mo]->setCheckable(true);
0290         mMonthButtons[mo]->setChecked(mCtTask->month.isEnabled(mo));
0291 
0292         monthsLayout->addWidget(mMonthButtons[mo], row, column);
0293 
0294         connect(mMonthButtons[mo], &QAbstractButton::clicked, this, &TaskEditorDialog::slotMonthChanged);
0295         connect(mMonthButtons[mo], &QAbstractButton::clicked, this, &TaskEditorDialog::slotWizard);
0296 
0297         if (column == 1) {
0298             column = 0;
0299             row++;
0300         } else {
0301             column = 1;
0302         }
0303     }
0304 
0305     mAllMonths = new SetOrClearAllButton(monthsGroup, SetOrClearAllButton::SET_ALL);
0306     monthsLayout->addWidget(mAllMonths, row, 0, 1, 2);
0307 
0308     connect(mAllMonths, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotAllMonths);
0309     connect(mAllMonths, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotWizard);
0310 
0311     return monthsGroup;
0312 }
0313 
0314 QGroupBox *TaskEditorDialog::createDaysOfWeekGroup(QWidget *main)
0315 {
0316     auto daysOfWeekGroup = new QGroupBox(i18n("Days of Week"), main);
0317     auto daysOfWeekLayout = new QGridLayout(daysOfWeekGroup);
0318 
0319     int column = 0;
0320     int row = 0;
0321     for (int dw = CTDayOfWeek::MINIMUM; dw <= CTDayOfWeek::MAXIMUM; dw++) {
0322         mDayOfWeekButtons[dw] = new NumberPushButton(daysOfWeekGroup);
0323         mDayOfWeekButtons[dw]->setText(mCtTask->dayOfWeek.getName(dw));
0324         mDayOfWeekButtons[dw]->setCheckable(true);
0325         mDayOfWeekButtons[dw]->setChecked(mCtTask->dayOfWeek.isEnabled(dw));
0326         daysOfWeekLayout->addWidget(mDayOfWeekButtons[dw], row, column);
0327 
0328         connect(mDayOfWeekButtons[dw], &QAbstractButton::clicked, this, &TaskEditorDialog::slotDayOfWeekChanged);
0329         connect(mDayOfWeekButtons[dw], &QAbstractButton::clicked, this, &TaskEditorDialog::slotWizard);
0330 
0331         if (column == 1) {
0332             column = 0;
0333             row++;
0334         } else {
0335             column = 1;
0336         }
0337     }
0338 
0339     mAllDaysOfWeek = new SetOrClearAllButton(daysOfWeekGroup, SetOrClearAllButton::SET_ALL);
0340     daysOfWeekLayout->addWidget(mAllDaysOfWeek);
0341 
0342     connect(mAllDaysOfWeek, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotAllDaysOfWeek);
0343     connect(mAllDaysOfWeek, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotWizard);
0344 
0345     return daysOfWeekGroup;
0346 }
0347 
0348 bool TaskEditorDialog::canReduceMinutesGroup()
0349 {
0350     for (int minuteIndex = 0; minuteIndex <= minuteTotal; ++minuteIndex) {
0351         if (minuteIndex % reducedMinuteStep != 0) {
0352             if (mMinuteButtons[minuteIndex]->isChecked()) {
0353                 return false;
0354             }
0355         }
0356     }
0357 
0358     return true;
0359 }
0360 
0361 void TaskEditorDialog::emptyMinutesGroup()
0362 {
0363     qCDebug(KCM_CRON_LOG) << "Empty minutes layout";
0364 
0365     for (int minuteIndex = 0; minuteIndex <= minuteTotal; ++minuteIndex) {
0366         mMinutesLayout->removeWidget(mMinuteButtons[minuteIndex]);
0367         mMinuteButtons[minuteIndex]->hide();
0368         qCDebug(KCM_CRON_LOG) << "Layout count" << mMinutesLayout->count();
0369     }
0370 
0371     mMinutesLayout->removeItem(mMinutesPreselectionLayout);
0372 }
0373 
0374 void TaskEditorDialog::increaseMinutesGroup()
0375 {
0376     /*
0377      * Test if the increase is already done. If this is the case, no need to redo it
0378      *
0379      * We test :
0380      * minutesButtons[1] will be hidden because 1%reducedMinuteStep != 0
0381      */
0382     /*
0383     if (minuteButtons[1]->isHidden() == false)
0384         return;
0385     */
0386     emptyMinutesGroup();
0387 
0388     qCDebug(KCM_CRON_LOG) << "Show all minutes";
0389 
0390     int minuteIndex = 0;
0391     for (int row = 0; row < (minuteTotal + 1) / minutePerColumn; ++row) {
0392         for (int column = 0; column < minutePerColumn; ++column) {
0393             mMinutesLayout->addWidget(mMinuteButtons[minuteIndex], row, column);
0394             mMinuteButtons[minuteIndex]->show();
0395             minuteIndex++;
0396         }
0397     }
0398 
0399     mMinutesLayout->addLayout(mMinutesPreselectionLayout, ((minuteTotal + 1) / minutePerColumn), 0, 1, minutePerColumn);
0400     mMinutesLayout->invalidate();
0401     this->resize(sizeHint());
0402 }
0403 
0404 void TaskEditorDialog::reduceMinutesGroup()
0405 {
0406     qCDebug(KCM_CRON_LOG) << "Reducing view";
0407 
0408     emptyMinutesGroup();
0409 
0410     int nextRow = 0;
0411     int nextColumn = 0;
0412 
0413     for (int minuteIndex = 0; minuteIndex <= minuteTotal; ++minuteIndex) {
0414         if (minuteIndex % reducedMinuteStep == 0) {
0415             mMinutesLayout->addWidget(mMinuteButtons[minuteIndex], nextRow, nextColumn);
0416             mMinuteButtons[minuteIndex]->show();
0417 
0418             nextColumn++;
0419             if (nextColumn == 6) {
0420                 nextColumn = 0;
0421                 nextRow = 1;
0422             }
0423         } else {
0424             qCDebug(KCM_CRON_LOG) << "Reducing id" << minuteIndex;
0425             mCtTask->minute.setEnabled(minuteIndex, false);
0426             mMinuteButtons[minuteIndex]->setChecked(false);
0427         }
0428     }
0429 
0430     mMinutesLayout->addLayout(mMinutesPreselectionLayout, 2, 0, 1, 6);
0431     mMinutesLayout->invalidate();
0432     this->resize(sizeHint());
0433 }
0434 
0435 NumberPushButton *TaskEditorDialog::createMinuteButton(int minuteIndex)
0436 {
0437     auto minuteButton = new NumberPushButton(true, mMinutesGroup);
0438     minuteButton->setText(QString::number(minuteIndex));
0439     minuteButton->setCheckable(true);
0440     minuteButton->setChecked(mCtTask->minute.isEnabled(minuteIndex));
0441 
0442     connect(minuteButton, &NumberPushButton::clicked, this, &TaskEditorDialog::slotMinuteChanged);
0443     connect(minuteButton, &NumberPushButton::clicked, this, &TaskEditorDialog::slotWizard);
0444 
0445     return minuteButton;
0446 }
0447 
0448 void TaskEditorDialog::createMinutesGroup(QWidget *main)
0449 {
0450     qCDebug(KCM_CRON_LOG) << "Creating minutes group";
0451 
0452     mMinutesGroup = new QGroupBox(i18n("Minutes"), main);
0453 
0454     mMinutesLayout = new QGridLayout(mMinutesGroup);
0455 
0456     for (int minuteIndex = 0; minuteIndex <= minuteTotal; ++minuteIndex) {
0457         mMinuteButtons[minuteIndex] = createMinuteButton(minuteIndex);
0458     }
0459 
0460     mMinutesPreselectionLayout = new QHBoxLayout();
0461 
0462     auto minutesPreselectionLabel = new QLabel(i18n("Preselection:"));
0463     mMinutesPreselectionLayout->addWidget(minutesPreselectionLabel);
0464 
0465     mMinutesPreselection = new QComboBox(this);
0466 
0467     minutesPreselectionLabel->setBuddy(mMinutesPreselection);
0468 
0469     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-ltr")), i18n("Clear selection"), -1);
0470     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Custom selection"), 0);
0471     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")), i18n("Each minute"), 1);
0472     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")), i18n("Every 2 minutes"), 2);
0473     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-workweek")), i18n("Every 5 minutes"), 5);
0474     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-upcoming-days")), i18n("Every 10 minutes"), 10);
0475     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-upcoming-days")), i18n("Every 15 minutes"), 15);
0476     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-day")), i18n("Every 20 minutes"), 20);
0477     mMinutesPreselection->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-day")), i18n("Every 30 minutes"), 30);
0478 
0479     mMinutesPreselectionLayout->addWidget(mMinutesPreselection);
0480 
0481     connect(mMinutesPreselection, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TaskEditorDialog::slotMinutesPreselection);
0482     connect(mMinutesPreselection, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TaskEditorDialog::slotWizard);
0483 
0484     // First mandatory increase
0485     increaseMinutesGroup();
0486 
0487     if (canReduceMinutesGroup()) {
0488         reduceMinutesGroup();
0489     }
0490 
0491     qCDebug(KCM_CRON_LOG) << "Minutes group created";
0492 }
0493 
0494 NumberPushButton *TaskEditorDialog::createHourButton(QGroupBox *hoursGroup, int hour)
0495 {
0496     auto hourButton = new NumberPushButton(true, hoursGroup);
0497     hourButton->setText(QString::number(hour));
0498     hourButton->setCheckable(true);
0499     hourButton->setChecked(mCtTask->hour.isEnabled(hour));
0500 
0501     connect(hourButton, &NumberPushButton::clicked, this, &TaskEditorDialog::slotHourChanged);
0502     connect(hourButton, &NumberPushButton::clicked, this, &TaskEditorDialog::slotWizard);
0503 
0504     return hourButton;
0505 }
0506 
0507 QGroupBox *TaskEditorDialog::createHoursGroup(QWidget *main)
0508 {
0509     // Hide the AM/PM labels if the locale is set to 24h format.
0510     // 'A' or 'a' means am/pm is shown (and then 'h' uses 12-hour format)
0511     // but 'H' forces a 24-hour format anyway, even with am/pm shown.
0512     const QString str = QLocale().timeFormat();
0513     static bool use12Clock = str.contains(QLatin1Char('a'), Qt::CaseInsensitive) && !str.contains(QLatin1Char('H'));
0514 
0515     qCDebug(KCM_CRON_LOG) << "Creating hours group";
0516     auto hoursGroup = new QGroupBox(i18n("Hours"), main);
0517 
0518     auto hoursLayout = new QGridLayout(hoursGroup); // 5 x 6 (24h) // 5 x 7 (12h)
0519 
0520     if (use12Clock) {
0521         mMorningLabel = new QLabel(i18n("AM:"), this);
0522         mMorningLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0523         mMorningLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
0524         hoursLayout->addWidget(mMorningLabel, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
0525     }
0526 
0527     int hourCount = 0;
0528     for (int column = 0; column <= 3; ++column) {
0529         for (int hour = 0; hour <= 5; ++hour) {
0530             NumberPushButton *hourButton = createHourButton(hoursGroup, hourCount);
0531             mHourButtons[hourCount] = hourButton;
0532             // When using the 12h format, the hour buttons need to be inserted 1 column over to leave room for the AM/PM labels.
0533             hoursLayout->addWidget(hourButton, column, hour + (use12Clock ? 1 : 0));
0534             hourCount++;
0535         }
0536     }
0537 
0538     if (use12Clock) {
0539         mAfternoonLabel = new QLabel(i18n("PM:"), this);
0540         mAfternoonLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
0541         mAfternoonLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
0542         hoursLayout->addWidget(mAfternoonLabel, 2, 0, Qt::AlignLeft | Qt::AlignVCenter);
0543     }
0544 
0545     mAllHours = new SetOrClearAllButton(this, SetOrClearAllButton::SET_ALL);
0546     hoursLayout->addWidget(mAllHours, 4, 0, 1, use12Clock ? 7 : 6);
0547 
0548     connect(mAllHours, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotAllHours);
0549     connect(mAllHours, &SetOrClearAllButton::clicked, this, &TaskEditorDialog::slotWizard);
0550 
0551     qCDebug(KCM_CRON_LOG) << "Create hours group";
0552     return hoursGroup;
0553 }
0554 
0555 void TaskEditorDialog::setupTitleWidget(const QString &comment, KTitleWidget::MessageType messageType)
0556 {
0557     mTitleWidget->setComment(comment, messageType);
0558     if (messageType == KTitleWidget::ErrorMessage) {
0559         mTitleWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-error")), KTitleWidget::ImageRight);
0560     } else {
0561         mTitleWidget->setIcon(QIcon::fromTheme(QStringLiteral("system-run")), KTitleWidget::ImageRight);
0562     }
0563 }
0564 
0565 void TaskEditorDialog::slotEnabledChanged()
0566 {
0567     bool enabled = mChkEnabled->isChecked();
0568     mUserCombo->setEnabled(enabled);
0569     mLeComment->setEnabled(enabled);
0570     mCommand->setEnabled(enabled);
0571     mChkReboot->setEnabled(enabled);
0572 
0573     // if chkReboot is already checked, allow setEnabled(false) but not setEnable(true) ...
0574     if (!mChkReboot->isChecked() || !enabled) {
0575         mCbEveryDay->setEnabled(enabled);
0576         mHoursGroup->setEnabled(enabled);
0577         mMinutesGroup->setEnabled(enabled);
0578     }
0579 
0580     // if cbEveryDay is already checked, allow setEnabled(false) but not setEnable(true) ...
0581     if ((!mChkReboot->isChecked() && !mCbEveryDay->isChecked()) || !enabled) {
0582         mBgMonth->setEnabled(enabled);
0583         mBgDayOfMonth->setEnabled(enabled);
0584         mBgDayOfWeek->setEnabled(enabled);
0585     }
0586 }
0587 
0588 void TaskEditorDialog::slotRebootChanged()
0589 {
0590     bool reboot = !mChkReboot->isChecked();
0591     mCbEveryDay->setEnabled(reboot);
0592     mHoursGroup->setEnabled(reboot);
0593     mMinutesGroup->setEnabled(reboot);
0594 
0595     // if cbEveryDay is already checked, bgMonth, bgDayOfMonth, bgDayOfWeek are already setEnable(flase)
0596     // so don't override them ! ...
0597     if (!mCbEveryDay->isChecked()) {
0598         mBgMonth->setEnabled(reboot);
0599         mBgDayOfMonth->setEnabled(reboot);
0600         mBgDayOfWeek->setEnabled(reboot);
0601     }
0602 }
0603 
0604 void TaskEditorDialog::slotDailyChanged()
0605 {
0606     if (mCbEveryDay->isChecked()) {
0607         for (int mo = 1; mo <= 12; mo++) {
0608             mMonthButtons[mo]->setChecked(true);
0609         }
0610         for (int dm = 1; dm <= 31; dm++) {
0611             mDayOfMonthButtons[dm]->setChecked(true);
0612         }
0613         for (int dw = 1; dw <= 7; dw++) {
0614             mDayOfWeekButtons[dw]->setChecked(true);
0615         }
0616         mBgMonth->setEnabled(false);
0617         mBgDayOfMonth->setEnabled(false);
0618         mBgDayOfWeek->setEnabled(false);
0619         mAllMonths->setEnabled(false);
0620         mAllDaysOfMonth->setEnabled(false);
0621         mAllDaysOfWeek->setEnabled(false);
0622     } else {
0623         mBgMonth->setEnabled(true);
0624         mBgDayOfMonth->setEnabled(true);
0625         mBgDayOfWeek->setEnabled(true);
0626         mAllMonths->setEnabled(true);
0627         mAllDaysOfMonth->setEnabled(true);
0628         mAllDaysOfWeek->setEnabled(true);
0629     }
0630 
0631     slotMonthChanged();
0632     slotDayOfMonthChanged();
0633     slotDayOfWeekChanged();
0634 }
0635 
0636 void TaskEditorDialog::slotOK()
0637 {
0638     // Make it friendly for just selecting days of the month or
0639     // days of the week.
0640 
0641     int monthDaysSelected(0);
0642     for (int dm = 1; dm <= 31; dm++) {
0643         if (mDayOfMonthButtons[dm]->isChecked()) {
0644             monthDaysSelected++;
0645         }
0646     }
0647 
0648     int weekDaysSelected(0);
0649     for (int dw = 1; dw <= 7; dw++) {
0650         if (mDayOfWeekButtons[dw]->isChecked()) {
0651             weekDaysSelected++;
0652         }
0653     }
0654 
0655     if ((monthDaysSelected == 0) && (weekDaysSelected > 0)) {
0656         for (int dm = 1; dm <= 31; dm++) {
0657             mDayOfMonthButtons[dm]->setChecked(true);
0658         }
0659     }
0660 
0661     if ((weekDaysSelected == 0) && (monthDaysSelected > 0)) {
0662         for (int dw = 1; dw <= 7; dw++) {
0663             mDayOfWeekButtons[dw]->setChecked(true);
0664         }
0665     }
0666 
0667     // save work in process
0668     if (mCrontabWidget->tasksWidget()->needUserColumn()) {
0669         mCtTask->userLogin = mUserCombo->currentText();
0670     }
0671 
0672     mCtTask->comment = mLeComment->toPlainText();
0673     mCtTask->command = mCommand->url().path();
0674     mCtTask->enabled = mChkEnabled->isChecked();
0675     mCtTask->reboot = mChkReboot->isChecked();
0676 
0677     for (int mo = CTMonth::MINIMUM; mo <= CTMonth::MAXIMUM; mo++) {
0678         mCtTask->month.setEnabled(mo, mMonthButtons[mo]->isChecked());
0679     }
0680 
0681     for (int dm = 1; dm <= 31; dm++) {
0682         mCtTask->dayOfMonth.setEnabled(dm, mDayOfMonthButtons[dm]->isChecked());
0683     }
0684     for (int dw = 1; dw <= 7; dw++) {
0685         mCtTask->dayOfWeek.setEnabled(dw, mDayOfWeekButtons[dw]->isChecked());
0686     }
0687     for (int ho = 0; ho <= 23; ho++) {
0688         mCtTask->hour.setEnabled(ho, mHourButtons[ho]->isChecked());
0689     }
0690 
0691     for (int mi = 0; mi <= minuteTotal; ++mi) {
0692         mCtTask->minute.setEnabled(mi, mMinuteButtons[mi]->isChecked());
0693     }
0694 
0695     accept();
0696 }
0697 
0698 void TaskEditorDialog::defineCommandIcon()
0699 {
0700     CTTask tempTask(*mCtTask);
0701     tempTask.command = mCommand->url().path();
0702 
0703     mCommandIcon->setPixmap(tempTask.commandIcon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this)));
0704 }
0705 
0706 bool TaskEditorDialog::checkCommand()
0707 {
0708     CTTask tempTask(*mCtTask);
0709     tempTask.command = mCommand->url().path();
0710 
0711     QPair<QString, bool> commandQuoted = tempTask.unQuoteCommand();
0712 
0713     if (commandQuoted.first.isEmpty()) {
0714         setupTitleWidget(i18n("<i>Please type a valid command line...</i>"), KTitleWidget::ErrorMessage);
0715         mOkButton->setEnabled(false);
0716         mCommand->setFocus();
0717         mCommandIcon->setPixmap(mMissingCommandPixmap);
0718 
0719         return false;
0720     }
0721 
0722     QStringList pathCommand = tempTask.separatePathCommand(commandQuoted.first, commandQuoted.second);
0723     if (pathCommand.isEmpty()) {
0724         setupTitleWidget(i18n("<i>Please type a valid command line...</i>"), KTitleWidget::ErrorMessage);
0725         mOkButton->setEnabled(false);
0726         mCommand->setFocus();
0727         mCommandIcon->setPixmap(mMissingCommandPixmap);
0728 
0729         return false;
0730     }
0731 
0732     QString path = pathCommand.at(0);
0733     QString binaryCommand = pathCommand.at(1);
0734 
0735     qCDebug(KCM_CRON_LOG) << "Looking for " << binaryCommand << "in" << path;
0736 
0737     bool found = false;
0738     bool exec = false;
0739     if (!QStandardPaths::findExecutable(binaryCommand, QStringList() << path).isEmpty() || mSpecialValidCommands.contains(binaryCommand)) {
0740         found = true;
0741     }
0742     // FIXME check if actually executable
0743     if (found) {
0744         exec = true;
0745     }
0746 
0747     if (found && !exec) {
0748         setupTitleWidget(i18n("<i>Please select an executable program...</i>"), KTitleWidget::ErrorMessage);
0749         mOkButton->setEnabled(false);
0750         mCommand->setFocus();
0751         mCommandIcon->setPixmap(mMissingCommandPixmap);
0752         return false;
0753     }
0754 
0755     if (!found) {
0756         setupTitleWidget(i18n("<i>Please browse for a program to execute...</i>"), KTitleWidget::ErrorMessage);
0757         mOkButton->setEnabled(false);
0758         mCommand->setFocus();
0759         mCommandIcon->setPixmap(mMissingCommandPixmap);
0760         return false;
0761     }
0762 
0763     return true;
0764 }
0765 
0766 void TaskEditorDialog::slotWizard()
0767 {
0768     if (!mChkEnabled->isChecked()) {
0769         setupTitleWidget(i18n("<i>This task is disabled.</i>"));
0770         mOkButton->setEnabled(true);
0771         mChkEnabled->setFocus();
0772         return;
0773     }
0774 
0775     if (mChkReboot->isChecked()) {
0776         setupTitleWidget(i18n("<i>This task will be run on system bootup.</i>"));
0777         mOkButton->setEnabled(true);
0778         return;
0779     }
0780 
0781     if (mCommand->url().path().isEmpty()) {
0782         setupTitleWidget(i18n("<i>Please browse for a program to execute...</i>"), KTitleWidget::ErrorMessage);
0783         mOkButton->setEnabled(false);
0784         mCommand->setFocus();
0785         mCommandIcon->setPixmap(mMissingCommandPixmap);
0786         return;
0787     }
0788 
0789     bool validCommand = checkCommand();
0790     if (!validCommand) {
0791         return;
0792     }
0793 
0794     // the months
0795     bool valid(false);
0796     for (int mo = CTMonth::MINIMUM; mo <= CTMonth::MAXIMUM; mo++) {
0797         if (mMonthButtons[mo]->isChecked()) {
0798             valid = true;
0799         }
0800     }
0801 
0802     if (!valid) {
0803         setupTitleWidget(i18n("<i>Please select from the 'Months' section...</i>"), KTitleWidget::ErrorMessage);
0804         mOkButton->setEnabled(false);
0805         if (!mCommand->hasFocus()) {
0806             mMonthButtons[1]->setFocus();
0807         }
0808         return;
0809     }
0810 
0811     // the days
0812     valid = false;
0813     for (int dm = CTDayOfMonth::MINIMUM; dm <= CTDayOfMonth::MAXIMUM; dm++) {
0814         if (mDayOfMonthButtons[dm]->isChecked()) {
0815             valid = true;
0816         }
0817     }
0818     for (int dw = CTDayOfWeek::MINIMUM; dw <= CTDayOfWeek::MAXIMUM; dw++) {
0819         if (mDayOfWeekButtons[dw]->isChecked()) {
0820             valid = true;
0821         }
0822     }
0823 
0824     if (!valid) {
0825         setupTitleWidget(i18n("<i>Please select from either the 'Days of Month' or the 'Days of Week' section...</i>"), KTitleWidget::ErrorMessage);
0826         mOkButton->setEnabled(false);
0827         if (!mCommand->hasFocus()) {
0828             mDayOfMonthButtons[1]->setFocus();
0829         }
0830         return;
0831     }
0832 
0833     // the hours
0834     valid = false;
0835     for (int ho = 0; ho <= 23; ho++) {
0836         if (mHourButtons[ho]->isChecked()) {
0837             valid = true;
0838         }
0839     }
0840 
0841     if (!valid) {
0842         setupTitleWidget(i18n("<i>Please select from the 'Hours' section...</i>"), KTitleWidget::ErrorMessage);
0843         mOkButton->setEnabled(false);
0844         if (!mCommand->hasFocus()) {
0845             mHourButtons[0]->setFocus();
0846         }
0847         return;
0848     }
0849 
0850     // the mins
0851     valid = false;
0852     for (int mi = 0; mi <= minuteTotal; ++mi) {
0853         if (mMinuteButtons[mi]->isChecked()) {
0854             valid = true;
0855         }
0856     }
0857 
0858     if (!valid) {
0859         setupTitleWidget(i18n("<i>Please select from the 'Minutes' section...</i>"), KTitleWidget::ErrorMessage);
0860         mOkButton->setEnabled(false);
0861         if (!mCommand->hasFocus()) {
0862             mMinuteButtons[0]->setFocus();
0863         }
0864         return;
0865     }
0866 
0867     defineCommandIcon();
0868     setupTitleWidget(i18n("<i>This task will be executed at the specified intervals.</i>"));
0869 
0870     mOkButton->setEnabled(true);
0871 }
0872 
0873 void TaskEditorDialog::slotAllMonths()
0874 {
0875     bool checked = false;
0876     if (mAllMonths->isSetAll()) {
0877         checked = true;
0878     }
0879 
0880     for (int mo = CTMonth::MINIMUM; mo <= CTMonth::MAXIMUM; mo++) {
0881         mMonthButtons[mo]->setChecked(checked);
0882     }
0883 
0884     slotMonthChanged();
0885 }
0886 
0887 void TaskEditorDialog::slotMonthChanged()
0888 {
0889     bool allCleared = true;
0890     for (int mo = CTMonth::MINIMUM; mo <= CTMonth::MAXIMUM; mo++) {
0891         if (mMonthButtons[mo]->isChecked()) {
0892             allCleared = false;
0893             break;
0894         }
0895     }
0896 
0897     if (allCleared) {
0898         mAllMonths->setStatus(SetOrClearAllButton::SET_ALL);
0899     } else {
0900         mAllMonths->setStatus(SetOrClearAllButton::CLEAR_ALL);
0901     }
0902 }
0903 
0904 void TaskEditorDialog::slotAllDaysOfMonth()
0905 {
0906     bool checked = false;
0907     if (mAllDaysOfMonth->isSetAll()) {
0908         checked = true;
0909     }
0910 
0911     for (int dm = CTDayOfMonth::MINIMUM; dm <= CTDayOfMonth::MAXIMUM; dm++) {
0912         mDayOfMonthButtons[dm]->setChecked(checked);
0913     }
0914 
0915     slotDayOfMonthChanged();
0916 }
0917 
0918 void TaskEditorDialog::slotDayOfMonthChanged()
0919 {
0920     bool allCleared = true;
0921     for (int dm = CTDayOfMonth::MINIMUM; dm <= CTDayOfMonth::MAXIMUM; dm++) {
0922         if (mDayOfMonthButtons[dm]->isChecked()) {
0923             allCleared = false;
0924             break;
0925         }
0926     }
0927 
0928     if (allCleared) {
0929         mAllDaysOfMonth->setStatus(SetOrClearAllButton::SET_ALL);
0930     } else {
0931         mAllDaysOfMonth->setStatus(SetOrClearAllButton::CLEAR_ALL);
0932     }
0933 }
0934 
0935 void TaskEditorDialog::slotAllDaysOfWeek()
0936 {
0937     if (mAllDaysOfWeek->isSetAll()) {
0938         for (int dw = 1; dw <= 7; dw++) {
0939             mDayOfWeekButtons[dw]->setChecked(true);
0940         }
0941     } else {
0942         for (int dw = 1; dw <= 7; dw++) {
0943             mDayOfWeekButtons[dw]->setChecked(false);
0944         }
0945     }
0946     slotDayOfWeekChanged();
0947 }
0948 
0949 void TaskEditorDialog::slotDayOfWeekChanged()
0950 {
0951     bool allCleared = true;
0952     for (int dw = 1; dw <= 7; dw++) {
0953         if (mDayOfWeekButtons[dw]->isChecked()) {
0954             allCleared = false;
0955         }
0956     }
0957     if (allCleared) {
0958         mAllDaysOfWeek->setStatus(SetOrClearAllButton::SET_ALL);
0959     } else {
0960         mAllDaysOfWeek->setStatus(SetOrClearAllButton::CLEAR_ALL);
0961     }
0962 }
0963 
0964 void TaskEditorDialog::slotAllHours()
0965 {
0966     if (mAllHours->isSetAll()) {
0967         for (int ho = 0; ho <= 23; ho++) {
0968             mHourButtons[ho]->setChecked(true);
0969         }
0970     } else {
0971         for (int ho = 0; ho <= 23; ho++) {
0972             mHourButtons[ho]->setChecked(false);
0973         }
0974     }
0975     slotHourChanged();
0976 }
0977 
0978 void TaskEditorDialog::slotHourChanged()
0979 {
0980     bool allCleared = true;
0981     for (int ho = 0; ho <= 23; ho++) {
0982         if (mHourButtons[ho]->isChecked()) {
0983             allCleared = false;
0984         }
0985     }
0986 
0987     if (allCleared) {
0988         mAllHours->setStatus(SetOrClearAllButton::SET_ALL);
0989     } else {
0990         mAllHours->setStatus(SetOrClearAllButton::CLEAR_ALL);
0991     }
0992 }
0993 
0994 void TaskEditorDialog::slotMinutesPreselection(int index)
0995 {
0996     QVariant itemData = mMinutesPreselection->itemData(index);
0997     int step = itemData.toInt();
0998     qCDebug(KCM_CRON_LOG) << "Selected step " << step;
0999 
1000     if (step == -1) {
1001         // Unselect everything
1002         for (int mi = 0; mi <= minuteTotal; ++mi) {
1003             mMinuteButtons[mi]->setChecked(false);
1004         }
1005 
1006         // Select Custom selection in the combo box
1007         for (int index = 0; index < mMinutesPreselection->count(); ++index) {
1008             if (mMinutesPreselection->itemData(index).toInt() == 0) {
1009                 mMinutesPreselection->setCurrentIndex(index);
1010                 break;
1011             }
1012         }
1013     } else if (step != 0) {
1014         for (int mi = 0; mi <= minuteTotal; ++mi) {
1015             if (mi % step == 0) {
1016                 mMinuteButtons[mi]->setChecked(true);
1017             } else {
1018                 mMinuteButtons[mi]->setChecked(false);
1019             }
1020         }
1021     }
1022 
1023     if (step < reducedMinuteStep && index != 0) {
1024         increaseMinutesGroup();
1025     } else {
1026         reduceMinutesGroup();
1027     }
1028 }
1029 
1030 void TaskEditorDialog::slotMinuteChanged()
1031 {
1032     CTMinute minutes;
1033 
1034     for (int index = 0; index <= minuteTotal; ++index) {
1035         minutes.setEnabled(index, mMinuteButtons[index]->isChecked());
1036     }
1037 
1038     int period = minutes.findPeriod();
1039 
1040     for (int index = 0; index < mMinutesPreselection->count(); ++index) {
1041         if (mMinutesPreselection->itemData(index).toInt() == period) {
1042             mMinutesPreselection->setCurrentIndex(index);
1043             break;
1044         }
1045     }
1046 }
1047 
1048 /**
1049  * SetOrClearAllButton class implementation
1050  */
1051 
1052 SetOrClearAllButton::SetOrClearAllButton(QWidget *parent, SetOrClearAllButton::Status status)
1053     : QPushButton(parent)
1054 {
1055     setStatus(status);
1056 }
1057 
1058 void SetOrClearAllButton::setStatus(SetOrClearAllButton::Status status)
1059 {
1060     currentStatus = status;
1061 
1062     if (currentStatus == SetOrClearAllButton::SET_ALL) {
1063         setText(i18n("Set All"));
1064     } else {
1065         setText(i18n("Clear All"));
1066     }
1067 }
1068 
1069 bool SetOrClearAllButton::isSetAll()
1070 {
1071     return currentStatus == SetOrClearAllButton::SET_ALL;
1072 }
1073 
1074 bool SetOrClearAllButton::isClearAll()
1075 {
1076     return currentStatus == SetOrClearAllButton::CLEAR_ALL;
1077 }
1078 
1079 /**
1080  * KTPushButton class implementation
1081  */
1082 
1083 NumberPushButton::NumberPushButton(QWidget *parent)
1084     : QPushButton(parent)
1085     , mIsDirty(false)
1086 {
1087     updatePalette();
1088 }
1089 
1090 NumberPushButton::NumberPushButton(bool digitMode, QWidget *parent)
1091     : QPushButton(parent)
1092     , mIsDirty(false)
1093 {
1094     if (digitMode) {
1095         setFixedWidth(12 + fontMetrics().boundingRect(QStringLiteral("44")).width());
1096         KAcceleratorManager::setNoAccel(this);
1097     }
1098     updatePalette();
1099 }
1100 
1101 void NumberPushButton::updatePalette()
1102 {
1103     mPalNormal = ((QWidget *)parent())->palette();
1104     mPalSelected = mPalNormal;
1105     for (int cg = (int)QPalette::Active; cg < (int)QPalette::NColorGroups; cg++) {
1106         mPalSelected.setColor((QPalette::ColorGroup)cg, QPalette::Button, mPalSelected.color((QPalette::ColorGroup)cg, QPalette::Highlight));
1107         mPalSelected.setColor((QPalette::ColorGroup)cg, QPalette::ButtonText, mPalSelected.color((QPalette::ColorGroup)cg, QPalette::HighlightedText));
1108     }
1109     mIsDirty = true;
1110 }
1111 
1112 bool NumberPushButton::event(QEvent *e)
1113 {
1114     if (e->type() == QEvent::PaletteChange) {
1115         updatePalette();
1116         update();
1117     }
1118     return QPushButton::event(e);
1119 }
1120 
1121 void NumberPushButton::paintEvent(QPaintEvent *)
1122 {
1123     QStylePainter p(this);
1124     QStyleOptionButton option;
1125     initStyleOption(&option);
1126 
1127     if (mIsDirty || isChecked()) {
1128         mIsDirty = false;
1129         if (isChecked()) {
1130             option.palette = mPalSelected;
1131             QFont f = p.font();
1132             f.setBold(true);
1133             p.setFont(f);
1134         }
1135     }
1136     p.drawControl(QStyle::CE_PushButton, option);
1137 }
1138 
1139 #include "moc_taskEditorDialog.cpp"