File indexing completed on 2024-04-28 09:41:04

0001 /*
0002     --------------------------------------------------------------------
0003     KDE\QT printing implementation.
0004     --------------------------------------------------------------------
0005     SPDX-FileCopyrightText: 1999 Robert Berry <rjmber@ntlworld.com>
0006     --------------------------------------------------------------------
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "crontabPrinter.h"
0011 
0012 #include <QPainter>
0013 #include <QPrintDialog>
0014 #include <QPrinter>
0015 #include <numeric>
0016 
0017 #include <KLocalizedString>
0018 
0019 #include "crontabWidget.h"
0020 
0021 #include "ctcron.h"
0022 #include "cttask.h"
0023 #include "ctvariable.h"
0024 
0025 #include "kcm_cron_debug.h"
0026 
0027 CrontabPrinter::CrontabPrinter(CrontabWidget *crontabWidget)
0028     : mCrontabWidget(crontabWidget)
0029 {
0030 }
0031 
0032 CrontabPrinter::~CrontabPrinter()
0033 {
0034     delete mCrontabPrinterWidget;
0035 
0036     delete mPainter;
0037     delete mPrinter;
0038     delete mPrintView;
0039 }
0040 
0041 bool CrontabPrinter::start()
0042 {
0043     qCDebug(KCM_CRON_LOG) << "Printing selection...";
0044 
0045     if (!mPrinter) {
0046         mPrinter = new QPrinter();
0047     }
0048 
0049     // do some printer initialization
0050     mPrinter->setFullPage(true);
0051 
0052     /*
0053     CrontabPrinterWidget* dialogPage = new CrontabPrinterWidget(crontabWidge);
0054     printer->addDialogPage(dialogPage);
0055     */
0056 
0057     // initialize the printer using the print dialog
0058     auto printDialog = new QPrintDialog(mPrinter, nullptr);
0059     printDialog->setOptions(QAbstractPrintDialog::PrintToFile);
0060     if (printDialog->exec() == QDialog::Rejected) {
0061         qCDebug(KCM_CRON_LOG) << "Printing canceled";
0062         delete printDialog;
0063         return false;
0064     }
0065 
0066     delete printDialog;
0067 
0068     // create a painter to paint on the printer object
0069     mPainter = new QPainter();
0070 
0071     // start painting
0072     mPainter->begin(mPrinter);
0073 
0074     const int margin = computeMargin();
0075     mPrintView = new QRect(margin, margin, mPainter->device()->width() - 2 * margin, mPainter->device()->height() - 2 * margin);
0076 
0077     mPage = 1;
0078     mCurrentRowPosition = 0;
0079 
0080     drawMainTitle();
0081 
0082     return true;
0083 }
0084 
0085 void CrontabPrinter::printTasks()
0086 {
0087     CTCron *cron = mCrontabWidget->currentCron();
0088 
0089     drawTitle(i18n("Scheduled Tasks"));
0090 
0091     QList<QStringList> tasksContent;
0092     tasksContent.reserve(cron->tasks().count());
0093     const auto tasks = cron->tasks();
0094     for (CTTask *task : tasks) {
0095         QStringList values;
0096         values << task->schedulingCronFormat();
0097         values << task->command;
0098         values << task->comment;
0099 
0100         tasksContent.append(values);
0101     }
0102 
0103     const QList<int> tasksColumnWidths = findColumnWidths(tasksContent, 3);
0104 
0105     QStringList taskHeaders;
0106     taskHeaders << i18n("Scheduling") << i18n("Command") << i18n("Description");
0107     drawHeader(tasksColumnWidths, taskHeaders);
0108 
0109     for (const QStringList &contents : std::as_const(tasksContent)) {
0110         drawContentRow(tasksColumnWidths, contents);
0111 
0112         needNewPage();
0113     }
0114 
0115     drawTable(tasksColumnWidths);
0116 }
0117 
0118 void CrontabPrinter::printVariables()
0119 {
0120     CTCron *cron = mCrontabWidget->currentCron();
0121 
0122     mPainter->translate(0, 20);
0123     mCurrentRowPosition = 0;
0124 
0125     // Environment Variables
0126 
0127     drawTitle(i18n("Environment Variables"));
0128 
0129     // QList<QStringList> variablesContent;
0130     const auto variables = cron->variables();
0131     for (CTVariable *variable : variables) {
0132         mPainter->drawText(*(mPrintView), Qt::AlignLeft | Qt::TextWordWrap, variable->variable + QLatin1String(" = ") + variable->value);
0133 
0134         const int moveBy = computeStringHeight(variable->variable);
0135         mPainter->translate(0, moveBy);
0136     }
0137 }
0138 
0139 void CrontabPrinter::drawMainTitle()
0140 {
0141     CTCron *cron = mCrontabWidget->currentCron();
0142 
0143     QFont originalFont = mPainter->font();
0144     QFont titleFont(originalFont);
0145     titleFont.setPixelSize(20);
0146     titleFont.setBold(true);
0147 
0148     mPainter->setFont(titleFont);
0149 
0150     QString mainTitle;
0151     if (cron->isSystemCron()) {
0152         mainTitle = i18n("System Crontab");
0153     } else if (cron->isMultiUserCron()) {
0154         mainTitle = i18n("All Users Crontabs");
0155     } else {
0156         mainTitle = i18nc("Crontab of user login", "Crontab of user %1", cron->userLogin());
0157     }
0158 
0159     mPainter->drawText(*(mPrintView), Qt::AlignHCenter | Qt::TextWordWrap, mainTitle);
0160 
0161     mPainter->translate(0, computeStringHeight(mainTitle));
0162 
0163     mPainter->setFont(originalFont);
0164 }
0165 
0166 void CrontabPrinter::drawTitle(const QString &title)
0167 {
0168     QFont originalFont = mPainter->font();
0169     QFont titleFont(originalFont);
0170     titleFont.setPixelSize(16);
0171     titleFont.setBold(true);
0172 
0173     mPainter->setFont(titleFont);
0174 
0175     mPainter->drawText(*(mPrintView), Qt::AlignLeft | Qt::TextWordWrap, title);
0176 
0177     mPainter->translate(0, computeStringHeight(title));
0178 
0179     mPainter->setFont(originalFont);
0180 }
0181 
0182 void CrontabPrinter::drawHeader(const QList<int> &columnWidths, const QStringList &headers)
0183 {
0184     QFont originalFont = mPainter->font();
0185     QFont titleFont(originalFont);
0186     titleFont.setBold(true);
0187 
0188     mPainter->setFont(titleFont);
0189 
0190     drawContentRow(columnWidths, headers);
0191 
0192     mPainter->setFont(originalFont);
0193 }
0194 
0195 void CrontabPrinter::drawContentRow(const QList<int> &columnWidths, const QStringList &contents)
0196 {
0197     QString firstColumn;
0198 
0199     int totalWidths = 0;
0200     int index = 0;
0201     for (const QString &content : contents) {
0202         if (index == 0) {
0203             firstColumn = content;
0204         }
0205 
0206         mPainter->drawText(*(mPrintView), Qt::AlignLeft | Qt::TextWordWrap, QLatin1String(" ") + content);
0207 
0208         mPainter->translate(columnWidths[index], 0);
0209 
0210         totalWidths += columnWidths[index];
0211 
0212         index++;
0213     }
0214 
0215     int moveBy = computeStringHeight(firstColumn);
0216 
0217     changeRow(-totalWidths, moveBy);
0218 }
0219 
0220 void CrontabPrinter::finish()
0221 {
0222     // stop painting, this will automatically send the print data to the printer
0223     mPainter->end();
0224 }
0225 
0226 void CrontabPrinter::printPageNumber()
0227 {
0228     qCDebug(KCM_CRON_LOG) << "Printing page number...";
0229 
0230     mPainter->translate(0, -mCurrentRowPosition);
0231     mPrintView->moveTo(QPoint(0, mPrintView->height()));
0232     mPainter->translate(0, -mPrintView->height());
0233     mPainter->drawText(mPrintView->right() - mPainter->fontMetrics().boundingRect(QString::number(mPage)).width(),
0234                        mPrintView->bottom() + mPainter->fontMetrics().ascent() + 5,
0235                        QString::number(mPage));
0236 }
0237 
0238 void CrontabPrinter::changeRow(int x, int y)
0239 {
0240     mPainter->translate(x, y);
0241     mCurrentRowPosition = mCurrentRowPosition + y;
0242 }
0243 
0244 int CrontabPrinter::computeMargin() const
0245 {
0246     const int dpiy = mPainter->device()->logicalDpiY();
0247     const int margin = (int)((2 / 2.54) * dpiy); // 2 cm margins
0248 
0249     return margin;
0250 }
0251 
0252 int CrontabPrinter::computeStringHeight(const QString &text) const
0253 {
0254     const int fontHeight = mPainter->fontMetrics().height();
0255     const int lines = mPainter->fontMetrics().boundingRect(text).width() / mPrintView->width() + 1;
0256     const int moveBy = (fontHeight + 2) * lines;
0257 
0258     return moveBy;
0259 }
0260 
0261 /**
0262  * Whether crontab should be printed
0263  */
0264 bool CrontabPrinter::isPrintCrontab() const
0265 {
0266     return mCrontabPrinterWidget->printCrontab();
0267 }
0268 
0269 /**
0270  * Whether all users should be printed (root only)
0271  */
0272 bool CrontabPrinter::isAllUsers() const
0273 {
0274     return mCrontabPrinterWidget->printAllUsers();
0275 }
0276 
0277 void CrontabPrinter::drawTable(const QList<int> &columnWidths)
0278 {
0279     mPainter->translate(0, -mCurrentRowPosition + computeMargin());
0280 
0281     int columnWidthsTotal = std::accumulate(columnWidths.begin(), columnWidths.end(), 0);
0282 
0283     const int margin = computeMargin();
0284     int linePositionX = margin;
0285 
0286     QPen originalPen = mPainter->pen();
0287     QPen pen(originalPen);
0288 
0289     pen.setWidth(1);
0290 
0291     mPainter->setPen(pen);
0292 
0293     // First horizontal line
0294     mPainter->drawLine(QPoint(margin, 0), QPoint(margin + columnWidthsTotal, 0));
0295 
0296     // Second horizontal line
0297     mPainter->drawLine(QPoint(margin, 0 + computeStringHeight(QStringLiteral(" "))),
0298                        QPoint(margin + columnWidthsTotal, 0 + computeStringHeight(QStringLiteral(" "))));
0299 
0300     // First vertical line
0301     mPainter->drawLine(QPoint(linePositionX, 0), QPoint(linePositionX, mCurrentRowPosition));
0302 
0303     for (int columnWidth : columnWidths) {
0304         linePositionX += columnWidth;
0305         mPainter->drawLine(QPoint(linePositionX, 0), QPoint(linePositionX, mCurrentRowPosition));
0306     }
0307 
0308     // Last horizontal line
0309     mPainter->drawLine(QPoint(margin, mCurrentRowPosition), QPoint(margin + columnWidthsTotal, mCurrentRowPosition));
0310 
0311     mPainter->setPen(originalPen);
0312 
0313     mPainter->translate(0, mCurrentRowPosition - computeMargin());
0314 }
0315 
0316 QList<int> CrontabPrinter::findMaxWidths(const QList<QStringList> &contents, int columnCount)
0317 {
0318     QList<int> columnWidths;
0319     columnWidths.reserve(columnCount);
0320     for (int i = 0; i < columnCount; ++i) {
0321         columnWidths.append(0);
0322     }
0323 
0324     for (const QStringList &content : contents) {
0325         int columnIndex = 0;
0326         while (columnIndex < columnWidths.count()) {
0327             const int valueWidth = mPainter->fontMetrics().boundingRect(content.at(columnIndex)).width();
0328             if (columnWidths[columnIndex] < valueWidth) {
0329                 columnWidths[columnIndex] = valueWidth;
0330             }
0331 
0332             columnIndex++;
0333         }
0334     }
0335 
0336     return columnWidths;
0337 }
0338 
0339 QList<int> CrontabPrinter::findColumnWidths(const QList<QStringList> &contents, int columnCount)
0340 {
0341     QList<int> columnWidths = findMaxWidths(contents, columnCount);
0342 
0343     int margin = computeMargin();
0344     int pageWidth = mPainter->device()->width() - 2 * margin;
0345 
0346     int columnWidthSum = 0;
0347     for (int width : std::as_const(columnWidths)) {
0348         qCDebug(KCM_CRON_LOG) << "Column : " << width;
0349         columnWidthSum += width;
0350     }
0351 
0352     if (columnWidthSum >= pageWidth) {
0353         qCDebug(KCM_CRON_LOG) << "The printing could be out of the page";
0354         return columnWidths;
0355     }
0356 
0357     int additionalSpace = (pageWidth - columnWidthSum) / columnWidths.count();
0358 
0359     int columnIndex = 0;
0360     while (columnIndex < columnWidths.count()) {
0361         columnWidths[columnIndex] = columnWidths[columnIndex] + additionalSpace;
0362 
0363         columnIndex++;
0364     }
0365 
0366     return columnWidths;
0367 }
0368 
0369 void CrontabPrinter::needNewPage()
0370 {
0371     const int margin = computeMargin();
0372     if (mCurrentRowPosition + margin >= mPrintView->height()) {
0373         printPageNumber();
0374         mPrinter->newPage();
0375         mPage++;
0376         mCurrentRowPosition = 0;
0377     }
0378 }