File indexing completed on 2024-11-24 04:48:39

0001 /*
0002  * Copyright (C) 2007 by Thorsten Staerk and Mathias Soeken <msoeken@tzi.de>
0003  * Copyright (C) 2019  Alexander Potashev <aspotashev@gmail.com>
0004  *
0005  *   This program is free software; you can redistribute it and/or modify
0006  *   it under the terms of the GNU General Public License as published by
0007  *   the Free Software Foundation; either version 2 of the License, or
0008  *   (at your option) any later version.
0009  *
0010  *   This program is distributed in the hope that it will be useful,
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *   GNU General Public License for more details.
0014  *
0015  *   You should have received a copy of the GNU General Public License along
0016  *   with this program; if not, write to the
0017  *      Free Software Foundation, Inc.
0018  *      51 Franklin Street, Fifth Floor
0019  *      Boston, MA  02110-1301  USA.
0020  *
0021  */
0022 
0023 #include "historydialog.h"
0024 
0025 #include <QDateTimeEdit>
0026 #include <QItemDelegate>
0027 #include <QPushButton>
0028 #include <QSet>
0029 
0030 #include <KMessageBox>
0031 #include <KStandardGuiItem>
0032 
0033 #include "file/filecalendar.h"
0034 #include "ktt_debug.h"
0035 #include "model/event.h"
0036 #include "model/eventsmodel.h"
0037 #include "model/projectmodel.h"
0038 #include "model/task.h"
0039 #include "model/tasksmodel.h"
0040 
0041 const QString HistoryDialog::dateTimeFormat = QStringLiteral("yyyy-MM-dd HH:mm:ss");
0042 
0043 class HistoryWidgetDelegate : public QItemDelegate
0044 {
0045 public:
0046     explicit HistoryWidgetDelegate(QObject *parent)
0047         : QItemDelegate(parent)
0048     {
0049     }
0050 
0051     QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const override
0052     {
0053         auto* editor = new QDateTimeEdit(parent);
0054         editor->setAutoFillBackground(true);
0055         editor->setPalette(option.palette);
0056         editor->setBackgroundRole(QPalette::Window);
0057         return editor;
0058     }
0059 
0060     void setEditorData(QWidget *editor, const QModelIndex &index) const override
0061     {
0062         QDateTime dateTime = QDateTime::fromString(index.model()->data(index, Qt::DisplayRole).toString(),
0063                                                    HistoryDialog::dateTimeFormat);
0064         auto *dateTimeWidget = dynamic_cast<QDateTimeEdit *>(editor);
0065         if (dateTimeWidget) {
0066             dateTimeWidget->setDateTime(dateTime);
0067         } else {
0068             qCWarning(KTT_LOG) << "Cast to QDateTimeEdit failed";
0069         }
0070     }
0071 
0072     void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
0073     {
0074         auto *dateTimeWidget = dynamic_cast<QDateTimeEdit *>(editor);
0075         if (dateTimeWidget) {
0076             QDateTime dateTime = dateTimeWidget->dateTime();
0077             model->setData(index, dateTime.toString(HistoryDialog::dateTimeFormat), Qt::EditRole);
0078         } else {
0079             qCWarning(KTT_LOG) << "Cast to QDateTimeEdit failed";
0080         }
0081     }
0082 
0083     void updateEditorGeometry(QWidget *editor,
0084                               const QStyleOptionViewItem &option,
0085                               const QModelIndex & /*index*/) const override
0086     {
0087         editor->setGeometry(option.rect);
0088     }
0089 };
0090 
0091 HistoryDialog::HistoryDialog(QWidget *parent, ProjectModel *projectModel)
0092     : QDialog(parent)
0093     , m_projectModel(projectModel)
0094     , m_ui()
0095 {
0096     m_ui.setupUi(this);
0097     /* Item Delegate for displaying KDateTimeWidget instead of QLineEdit */
0098     auto *historyWidgetDelegate = new HistoryWidgetDelegate(m_ui.historytablewidget);
0099     m_ui.historytablewidget->setItemDelegateForColumn(1, historyWidgetDelegate);
0100     m_ui.historytablewidget->setItemDelegateForColumn(2, historyWidgetDelegate);
0101 
0102     m_ui.historytablewidget->setEditTriggers(QAbstractItemView::AllEditTriggers);
0103     m_ui.historytablewidget->setColumnCount(5);
0104     m_ui.historytablewidget->setHorizontalHeaderLabels(
0105         QStringList{i18n("Task"), i18n("StartTime"), i18n("EndTime"), i18n("Comment"), QStringLiteral("event UID")});
0106     m_ui.historytablewidget->horizontalHeader()->setStretchLastSection(true);
0107     m_ui.historytablewidget->setColumnHidden(4, true); // hide the "UID" column
0108     listAllEvents();
0109     m_ui.historytablewidget->setSortingEnabled(true);
0110     m_ui.historytablewidget->sortItems(1, Qt::DescendingOrder);
0111     m_ui.historytablewidget->resizeColumnsToContents();
0112     auto *deleteButton = m_ui.buttonbox->addButton(QString(), QDialogButtonBox::ActionRole);
0113     KStandardGuiItem::assign(deleteButton, KStandardGuiItem::Delete);
0114     connect(deleteButton, &QPushButton::clicked, this, &HistoryDialog::onDeleteClicked);
0115 }
0116 
0117 QString HistoryDialog::listAllEvents()
0118 {
0119     QString err;
0120     // if sorting is enabled and we write to row x, we cannot be sure row x will
0121     // be in row x some lines later
0122     bool old_sortingenabled = m_ui.historytablewidget->isSortingEnabled();
0123     m_ui.historytablewidget->setSortingEnabled(false);
0124     connect(m_ui.historytablewidget, &QTableWidget::cellChanged, this, &HistoryDialog::onCellChanged);
0125 
0126     for (const auto *event : m_projectModel->eventsModel()->events()) {
0127         int row = m_ui.historytablewidget->rowCount();
0128         m_ui.historytablewidget->insertRow(row);
0129 
0130         // maybe the file is corrupt and (*i)->relatedTo is NULL
0131         if (event->relatedTo().isEmpty()) {
0132             qCDebug(KTT_LOG) << "There is no 'relatedTo' entry for " << event->summary();
0133             err = QStringLiteral("NoRelatedToForEvent");
0134             continue;
0135         }
0136 
0137         const Task *parent = dynamic_cast<Task *>(m_projectModel->tasksModel()->taskByUID(event->relatedTo()));
0138         if (!parent) {
0139             qCWarning(KTT_LOG) << "Unable to load 'relatedTo' entry for " << event->summary();
0140             err = QStringLiteral("NoRelatedToForEvent");
0141             continue;
0142         }
0143 
0144         auto *item = new QTableWidgetItem(parent->name());
0145         item->setFlags(Qt::ItemIsEnabled);
0146         item->setWhatsThis(i18nc("@info:whatsthis", "You can change this task's comment, start time and end time."));
0147         m_ui.historytablewidget->setItem(row, 0, item);
0148 
0149         QDateTime start = event->dtStart();
0150         QDateTime end = event->dtEnd();
0151         m_ui.historytablewidget->setItem(row, 1, new QTableWidgetItem(start.toString(dateTimeFormat)));
0152         m_ui.historytablewidget->setItem(row, 2, new QTableWidgetItem(end.toString(dateTimeFormat)));
0153         m_ui.historytablewidget->setItem(row, 4, new QTableWidgetItem(event->uid()));
0154         qDebug() << "event->comments.count() =" << event->comments().count();
0155         if (event->comments().count() > 0) {
0156             m_ui.historytablewidget->setItem(row, 3, new QTableWidgetItem(event->comments().last()));
0157         }
0158     }
0159 
0160     m_ui.historytablewidget->resizeColumnsToContents();
0161     m_ui.historytablewidget->setColumnWidth(1, 300);
0162     m_ui.historytablewidget->setColumnWidth(2, 300);
0163     setMinimumSize(m_ui.historytablewidget->columnWidth(0) + m_ui.historytablewidget->columnWidth(1)
0164                        + m_ui.historytablewidget->columnWidth(2) + m_ui.historytablewidget->columnWidth(3),
0165                    height());
0166     m_ui.historytablewidget->setSortingEnabled(old_sortingenabled);
0167     return err;
0168 }
0169 
0170 void HistoryDialog::changeEvent(QEvent *e)
0171 {
0172     QDialog::changeEvent(e);
0173     switch (e->type()) {
0174     case QEvent::LanguageChange:
0175         m_ui.retranslateUi(this);
0176         break;
0177     default:
0178         break;
0179     }
0180 }
0181 
0182 void HistoryDialog::onCellChanged(int row, int col)
0183 {
0184     qCDebug(KTT_LOG) << "row =" << row << " col =" << col;
0185 
0186     if (!m_ui.historytablewidget->item(row, 4)) {
0187         // the program did the change, not the user
0188         return;
0189     }
0190 
0191     switch (col) {
0192     case 1: {
0193         // StartDate changed
0194         qCDebug(KTT_LOG) << "user changed StartDate to" << m_ui.historytablewidget->item(row, col)->text();
0195         QDateTime datetime = QDateTime::fromString(m_ui.historytablewidget->item(row, col)->text(), dateTimeFormat);
0196         if (!datetime.isValid()) {
0197             KMessageBox::information(nullptr, i18n("This is not a valid Date/Time."));
0198             break;
0199         }
0200 
0201         QString uid = m_ui.historytablewidget->item(row, 4)->text();
0202         Event *event = m_projectModel->eventsModel()->eventByUID(uid);
0203 
0204         event->setDtStart(datetime);
0205         // setDtStart could modify date/time, sync it into our table
0206         m_ui.historytablewidget->item(row, col)->setText(event->dtStart().toString(dateTimeFormat));
0207 
0208         Q_EMIT m_projectModel->eventsModel()->timesChanged();
0209         qCDebug(KTT_LOG) << "Program SetDtStart to" << m_ui.historytablewidget->item(row, col)->text();
0210         break;
0211     }
0212     case 2: {
0213         // EndDate changed
0214         qCDebug(KTT_LOG) << "user changed EndDate to" << m_ui.historytablewidget->item(row, col)->text();
0215         QDateTime datetime = QDateTime::fromString(m_ui.historytablewidget->item(row, col)->text(), dateTimeFormat);
0216         if (!datetime.isValid()) {
0217             KMessageBox::information(nullptr, i18n("This is not a valid Date/Time."));
0218             break;
0219         }
0220 
0221         QString uid = m_ui.historytablewidget->item(row, 4)->text();
0222         Event *event = m_projectModel->eventsModel()->eventByUID(uid);
0223 
0224         event->setDtEnd(datetime);
0225         // setDtStart could modify date/time, sync it into our table
0226         m_ui.historytablewidget->item(row, col)->setText(event->dtEnd().toString(dateTimeFormat));
0227 
0228         Q_EMIT m_projectModel->eventsModel()->timesChanged();
0229         qCDebug(KTT_LOG) << "Program SetDtEnd to" << m_ui.historytablewidget->item(row, col)->text();
0230         break;
0231     }
0232     case 3: {
0233         // Comment changed
0234         qCDebug(KTT_LOG) << "user changed Comment to" << m_ui.historytablewidget->item(row, col)->text();
0235 
0236         QString uid = m_ui.historytablewidget->item(row, 4)->text();
0237         Event *event = m_projectModel->eventsModel()->eventByUID(uid);
0238         qCDebug(KTT_LOG) << "uid =" << uid;
0239         event->addComment(m_ui.historytablewidget->item(row, col)->text());
0240         qCDebug(KTT_LOG) << "added" << m_ui.historytablewidget->item(row, col)->text();
0241         break;
0242     }
0243     default:
0244         break;
0245     }
0246 }
0247 
0248 QString HistoryDialog::refresh()
0249 {
0250     while (m_ui.historytablewidget->rowCount() > 0) {
0251         m_ui.historytablewidget->removeRow(0);
0252     }
0253     listAllEvents();
0254     return QString();
0255 }
0256 
0257 void HistoryDialog::onDeleteClicked()
0258 {
0259     // Create a list of rows to delete.
0260     // This includes both the current row (i.e. the row the cursor is on) as
0261     // well as the rows that are part of the current selection. Because these
0262     // can overlap and because the selected items contain one item per cell, not
0263     // just per row, we use a QSet here to get a unique list of rows.
0264     QSet<int> rows;
0265     if (m_ui.historytablewidget->currentRow() >= 0) { // Could be -1 if table is empty or no row is selected
0266         rows.insert(m_ui.historytablewidget->currentRow());
0267     }
0268     for (const auto &item : m_ui.historytablewidget->selectedItems()) {
0269         rows.insert(item->row());
0270     }
0271 
0272     if (rows.empty()) {
0273         KMessageBox::information(this, i18n("Please select a task to delete."));
0274         return;
0275     }
0276 
0277     bool refresh = false;
0278     for (int row : rows) {
0279         QString uid = m_ui.historytablewidget->item(row, 4)->text();
0280         qDebug() << "uid =" << uid;
0281         const Event *event = m_projectModel->eventsModel()->eventByUID(uid);
0282         if (event) {
0283             qCDebug(KTT_LOG) << "removing uid " << event->uid();
0284             m_projectModel->eventsModel()->removeByUID(event->uid());
0285             refresh = true;
0286         }
0287     }
0288     if (refresh) {
0289         Q_EMIT m_projectModel->eventsModel()->timesChanged();
0290         this->refresh();
0291     }
0292 }
0293 
0294 void HistoryDialog::on_buttonbox_rejected()
0295 {
0296     m_ui.historytablewidget->setCurrentCell(0, 0); // you need to change the cell to store the value
0297     close();
0298 }
0299 
0300 #include "moc_historydialog.cpp"