File indexing completed on 2024-04-28 05:49:05

0001 /*
0002     SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "gitcommitdialog.h"
0007 #include "git/gitutils.h"
0008 #include "gitwidget.h"
0009 
0010 #include <QCoreApplication>
0011 #include <QInputMethodEvent>
0012 #include <QSyntaxHighlighter>
0013 #include <QVBoxLayout>
0014 
0015 #include <KColorScheme>
0016 #include <KLocalizedString>
0017 
0018 #include <ktexteditor_utils.h>
0019 
0020 class BadLengthHighlighter : public QSyntaxHighlighter
0021 {
0022 public:
0023     explicit BadLengthHighlighter(QTextDocument *doc, int badLen)
0024         : QSyntaxHighlighter(doc)
0025         , badLength(badLen)
0026     {
0027     }
0028 
0029     void highlightBlock(const QString &text) override
0030     {
0031         if (text.length() < badLength) {
0032             return;
0033         }
0034         setFormat(badLength, text.length() - badLength, red);
0035     }
0036 
0037 private:
0038     int badLength = 0;
0039     const QColor red = KColorScheme().foreground(KColorScheme::NegativeText).color();
0040 };
0041 
0042 static void changeTextColorToRed(QLineEdit *lineEdit, const QColor &red)
0043 {
0044     if (!lineEdit)
0045         return;
0046 
0047     // Everything > 52 = red color
0048     QList<QInputMethodEvent::Attribute> attributes;
0049     if (lineEdit->text().length() > 52) {
0050         int start = 52 - lineEdit->cursorPosition();
0051         int len = lineEdit->text().length() - start;
0052         QInputMethodEvent::AttributeType type = QInputMethodEvent::TextFormat;
0053 
0054         QTextCharFormat fmt;
0055         fmt.setForeground(red);
0056         QVariant format = fmt;
0057 
0058         attributes.append(QInputMethodEvent::Attribute(type, start, len, format));
0059     }
0060     QInputMethodEvent event(QString(), attributes);
0061     QCoreApplication::sendEvent(lineEdit, &event);
0062 }
0063 
0064 GitCommitDialog::GitCommitDialog(const QString &lastCommit, QWidget *parent, Qt::WindowFlags f)
0065     : QDialog(parent, f)
0066 {
0067     Q_ASSERT(parent);
0068 
0069     setWindowTitle(i18n("Commit Changes"));
0070 
0071     QFont font = Utils::editorFont();
0072 
0073     ok.setText(i18n("Commit"));
0074     cancel.setText(i18n("Cancel"));
0075 
0076     m_le.setPlaceholderText(i18n("Write commit message..."));
0077     m_le.setFont(font);
0078 
0079     QFontMetrics fm(font);
0080 
0081     m_leLen.setText(QStringLiteral("0 / 52"));
0082 
0083     m_pe.setPlaceholderText(i18n("Extended commit description..."));
0084     m_pe.setFont(font);
0085 
0086     /** Dialog's main layout **/
0087     QVBoxLayout *vlayout = new QVBoxLayout(this);
0088     vlayout->setContentsMargins(4, 4, 4, 4);
0089     setLayout(vlayout);
0090 
0091     /** Setup the label at the top **/
0092     QHBoxLayout *hLayoutLine = new QHBoxLayout;
0093     hLayoutLine->addStretch();
0094     hLayoutLine->addWidget(&m_leLen);
0095 
0096     /** Setup plaintextedit and line edit **/
0097     vlayout->addLayout(hLayoutLine);
0098     vlayout->addWidget(&m_le);
0099     vlayout->addWidget(&m_pe);
0100 
0101     loadCommitMessage(lastCommit);
0102 
0103     auto bottomLayout = new QHBoxLayout;
0104 
0105     /** Setup checkboxes at the bottom **/
0106     m_cbSignOff.setChecked(false);
0107     m_cbSignOff.setText(i18n("Sign off"));
0108     bottomLayout->addWidget(&m_cbSignOff);
0109 
0110     m_cbAmend.setChecked(false);
0111     m_cbAmend.setText(i18n("Amend"));
0112     m_cbAmend.setToolTip(i18n("Amend Last Commit"));
0113     connect(&m_cbAmend, &QCheckBox::stateChanged, this, [this](int state) {
0114         if (state != Qt::Checked) {
0115             ok.setText(i18n("Commit"));
0116             setWindowTitle(i18n("Commit Changes"));
0117             return;
0118         }
0119         setWindowTitle(i18n("Amending Commit"));
0120         ok.setText(i18n("Amend"));
0121         const auto [msg, desc] = GitUtils::getLastCommitMessage(static_cast<GitWidget *>(this->parent())->dotGitPath());
0122         m_le.setText(msg);
0123         m_pe.setPlainText(desc);
0124     });
0125 
0126     bottomLayout->addWidget(&m_cbAmend);
0127     bottomLayout->addStretch();
0128 
0129     vlayout->addLayout(bottomLayout);
0130 
0131     /** Setup Ok / Cancel Button **/
0132     QHBoxLayout *hLayout = new QHBoxLayout;
0133     hLayout->addStretch();
0134     hLayout->addWidget(&ok);
0135     hLayout->addWidget(&cancel);
0136 
0137     connect(&ok, &QPushButton::clicked, this, &QDialog::accept);
0138     connect(&cancel, &QPushButton::clicked, this, &QDialog::reject);
0139     connect(&m_le, &QLineEdit::textChanged, this, &GitCommitDialog::updateLineSizeLabel);
0140 
0141     updateLineSizeLabel();
0142 
0143     vlayout->addLayout(hLayout);
0144 
0145     /**
0146      * Setup highlighting which changes text color to red if
0147      * it crosses the 72 char threshold
0148      */
0149     auto hl = new BadLengthHighlighter(m_pe.document(), 72);
0150     Q_UNUSED(hl)
0151 
0152     // set 72 chars wide plain text edit
0153     const int avgCharWidth = fm.averageCharWidth();
0154     const int width = (avgCharWidth * 72);
0155     const int fw = width + vlayout->contentsMargins().left() * 2 + m_pe.frameWidth() * 2 + m_pe.contentsMargins().left() + vlayout->spacing();
0156     resize(fw, avgCharWidth * 52);
0157 }
0158 
0159 void GitCommitDialog::loadCommitMessage(const QString &lastCommit)
0160 {
0161     if (lastCommit.isEmpty()) {
0162         return;
0163     }
0164     // restore last message ?
0165     auto msgs = lastCommit.split(QStringLiteral("[[\n\n]]"));
0166     if (!msgs.isEmpty()) {
0167         m_le.setText(msgs.at(0));
0168         if (msgs.length() > 1) {
0169             m_pe.setPlainText(msgs.at(1));
0170         }
0171     }
0172 }
0173 
0174 QString GitCommitDialog::subject() const
0175 {
0176     return m_le.text();
0177 }
0178 
0179 QString GitCommitDialog::description() const
0180 {
0181     return m_pe.toPlainText();
0182 }
0183 
0184 bool GitCommitDialog::signoff() const
0185 {
0186     return m_cbSignOff.isChecked();
0187 }
0188 
0189 bool GitCommitDialog::amendingLastCommit() const
0190 {
0191     return m_cbAmend.isChecked();
0192 }
0193 
0194 void GitCommitDialog::setAmendingCommit()
0195 {
0196     m_cbAmend.setChecked(true);
0197 }
0198 
0199 void GitCommitDialog::updateLineSizeLabel()
0200 {
0201     int len = m_le.text().length();
0202     if (len <= 52) {
0203         m_leLen.setText(i18nc("Number of characters", "%1 / 52", QString::number(len)));
0204     } else {
0205         const QColor red = KColorScheme().foreground(KColorScheme::NegativeText).color();
0206         changeTextColorToRed(&m_le, red);
0207         m_leLen.setText(i18nc("Number of characters", "<span style=\"color:%1;\">%2</span> / 52", red.name(), QString::number(len)));
0208     }
0209 }
0210 
0211 #include "moc_gitcommitdialog.cpp"