File indexing completed on 2024-04-21 05:41:00

0001 /*
0002     SPDX-FileCopyrightText: 2011 Vishesh Yadav <vishesh3y@gmail.com>
0003     SPDX-FileCopyrightText: 2015 Tomasz Bojczuk <seelook@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "commitdialog.h"
0009 #include "hgwrapper.h"
0010 #include "fileviewhgpluginsettings.h"
0011 
0012 #include <QGroupBox>
0013 #include <QGridLayout>
0014 #include <QLabel>
0015 #include <QActionGroup>
0016 #include <QPlainTextEdit>
0017 #include <QMenu>
0018 #include <QAction>
0019 #include <QLineEdit>
0020 #include <QSplitter>
0021 #include <KTextEditor/Document>
0022 #include <KTextEditor/View>
0023 #include <KTextEditor/Editor>
0024 #include <KMessageBox>
0025 #include <KLocalizedString>
0026 
0027 HgCommitDialog::HgCommitDialog(QWidget *parent):
0028     DialogBase(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent)
0029 {
0030     // dialog properties
0031     this->setWindowTitle(xi18nc("@title:window",
0032                 "<application>Hg</application> Commit"));
0033 
0034     okButton()->setText(xi18nc("@action:button", "Commit"));
0035     okButton()->setDisabled(true);
0036 
0037     // To show diff between commit
0038     KTextEditor::Editor *editor = KTextEditor::Editor::instance();
0039     if (!editor) {
0040         KMessageBox::error(this, 
0041                 i18n("The KTextEditor component could not be found;"
0042                      "\nplease check your KDE Frameworks installation."));
0043         return;
0044     }
0045     m_fileDiffDoc = editor->createDocument(nullptr);
0046     m_fileDiffView = qobject_cast<KTextEditor::View*>(m_fileDiffDoc->createView(this));
0047     m_fileDiffView->setStatusBarEnabled(false);
0048     m_fileDiffDoc->setReadWrite(false);
0049 
0050     // Setup actions
0051     m_useCurrentBranch = new QAction(this);
0052     m_useCurrentBranch->setCheckable(true);
0053     m_useCurrentBranch->setText(xi18nc("@action:inmenu",
0054                                "Commit to current branch"));
0055 
0056     m_newBranch = new QAction(this);
0057     m_newBranch->setCheckable(true);
0058     m_newBranch->setText(xi18nc("@action:inmenu",
0059                                "Create new branch"));
0060 
0061     m_closeBranch = new QAction(this);
0062     m_closeBranch->setCheckable(true);
0063     m_closeBranch->setText(xi18nc("@action:inmenu",
0064                                  "Close current branch"));
0065 
0066     m_branchMenu = new QMenu(this);
0067     m_branchMenu->addAction(m_useCurrentBranch);
0068     m_branchMenu->addAction(m_newBranch);
0069     m_branchMenu->addAction(m_closeBranch);
0070 
0071     QActionGroup *branchActionGroup = new QActionGroup(this);
0072     branchActionGroup->addAction(m_useCurrentBranch);
0073     branchActionGroup->addAction(m_newBranch);
0074     branchActionGroup->addAction(m_closeBranch);
0075     m_useCurrentBranch->setChecked(true);
0076     connect(branchActionGroup, &QActionGroup::triggered,
0077             this, &HgCommitDialog::slotBranchActions);
0078 
0079 
0080     //////////////
0081     // Setup UI //
0082     //////////////
0083 
0084     // Top bar of buttons
0085     QHBoxLayout *topBarLayout = new QHBoxLayout;
0086     m_copyMessageButton = new QPushButton(i18n("Copy Message"));
0087     m_branchButton = new QPushButton(i18n("Branch"));
0088 
0089     m_copyMessageMenu = new QMenu(this);
0090     createCopyMessageMenu();
0091 
0092     topBarLayout->addWidget(new QLabel(getParentForLabel()));
0093     topBarLayout->addStretch();
0094     topBarLayout->addWidget(m_branchButton);
0095     topBarLayout->addWidget(m_copyMessageButton);
0096     m_branchButton->setMenu(m_branchMenu);
0097     m_copyMessageButton->setMenu(m_copyMessageMenu);
0098 
0099     // the commit box itself
0100     QGroupBox *messageGroupBox = new QGroupBox;
0101     QVBoxLayout *commitLayout = new QVBoxLayout;
0102     m_commitMessage = editor->createDocument(nullptr);
0103     KTextEditor::View *messageView =
0104               qobject_cast<KTextEditor::View*>(m_commitMessage->createView(this));
0105     messageView->setStatusBarEnabled(false);
0106     messageView->setMinimumHeight(fontMetrics().height() * 4);
0107     commitLayout->addWidget(messageView);
0108     messageGroupBox->setTitle(xi18nc("@title:group", "Commit Message"));
0109     messageGroupBox->setLayout(commitLayout);
0110 
0111     // Show diff here
0112     QGroupBox *diffGroupBox = new QGroupBox;
0113     QVBoxLayout *diffLayout = new QVBoxLayout(diffGroupBox);
0114     diffLayout->addWidget(m_fileDiffView);
0115     diffGroupBox->setTitle(xi18nc("@title:group", "Diff/Content"));
0116     diffGroupBox->setLayout(diffLayout);
0117 
0118     // Set up layout for Status, Commit and Diff boxes
0119     m_verticalSplitter = new QSplitter(Qt::Horizontal);
0120     m_horizontalSplitter = new QSplitter(Qt::Vertical);
0121     m_horizontalSplitter->addWidget(messageGroupBox);
0122     m_horizontalSplitter->addWidget(diffGroupBox);
0123     m_statusList = new HgStatusList;
0124     m_verticalSplitter->addWidget(m_statusList);
0125     m_verticalSplitter->addWidget(m_horizontalSplitter);
0126 
0127     // Set up layout and container for main dialog
0128     QVBoxLayout *mainLayout = new QVBoxLayout;
0129     mainLayout->addLayout(topBarLayout);
0130     mainLayout->addWidget(m_verticalSplitter);
0131     layout()->insertLayout(0, mainLayout);
0132 
0133     slotBranchActions(m_useCurrentBranch);
0134     slotInitDiffOutput(); // initialize with whole repo diff
0135 
0136     // Load saved settings
0137     FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self();
0138     this->resize(QSize(settings->commitDialogWidth(),
0139                                settings->commitDialogHeight()));
0140 
0141     m_verticalSplitter->setSizes(settings->verticalSplitterSizes());
0142     m_horizontalSplitter->setSizes(settings->horizontalSplitterSizes());
0143 
0144     messageView->setFocus(); // message editor ready to get a text
0145 
0146     connect(m_statusList, &HgStatusList::itemSelectionChanged,
0147         this, &HgCommitDialog::slotItemSelectionChanged);
0148     connect(m_commitMessage, &KTextEditor::Document::textChanged,
0149          this, &HgCommitDialog::slotMessageChanged);
0150     connect(this, SIGNAL(finished(int)), this, SLOT(saveGeometry()));
0151 }
0152 
0153 QString HgCommitDialog::getParentForLabel()
0154 {
0155     HgWrapper *hgWrapper = HgWrapper::instance();
0156     const QString line = QLatin1String("<b>parents:</b> ") + hgWrapper->getParentsOfHead();
0157     return line;
0158 }
0159 
0160 void HgCommitDialog::slotInitDiffOutput()
0161 {
0162     m_fileDiffDoc->setReadWrite(true);
0163     m_fileDiffDoc->setModified(false);
0164     m_fileDiffDoc->closeUrl(true);
0165 
0166     QString diffOut;
0167     HgWrapper *hgWrapper = HgWrapper::instance();
0168     hgWrapper->executeCommand(QStringLiteral("diff"), QStringList(), diffOut);
0169     m_fileDiffDoc->setHighlightingMode(QStringLiteral("diff"));
0170     m_fileDiffDoc->setText(diffOut);
0171     m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) );
0172     m_fileDiffDoc->setReadWrite(false);
0173 }
0174 
0175 void HgCommitDialog::slotItemSelectionChanged(const char status, 
0176         const QString &fileName)
0177 {
0178     m_fileDiffDoc->setReadWrite(true);
0179     m_fileDiffDoc->setModified(false);
0180     m_fileDiffDoc->closeUrl(true);
0181 
0182     if (status != '?') {
0183         QStringList arguments;
0184         QString diffOut;
0185         HgWrapper *hgWrapper = HgWrapper::instance();
0186 
0187         arguments << fileName;
0188         hgWrapper->executeCommand(QStringLiteral("diff"), arguments, diffOut);
0189         m_fileDiffDoc->setText(diffOut);
0190         m_fileDiffDoc->setHighlightingMode(QStringLiteral("diff"));
0191     }
0192     else {
0193         QUrl url = QUrl::fromLocalFile(HgWrapper::instance()->getBaseDir());
0194         url = url.adjusted(QUrl::StripTrailingSlash);
0195         url.setPath(url.path() + QLatin1Char('/') + fileName);
0196         m_fileDiffDoc->openUrl(url);
0197     }
0198 
0199     m_fileDiffDoc->setReadWrite(false);
0200     m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) );
0201 }
0202 
0203 void HgCommitDialog::slotMessageChanged()
0204 {
0205     okButton()->setDisabled(m_commitMessage->isEmpty());
0206 }
0207 
0208 void HgCommitDialog::done(int r)
0209 {
0210     if (r == QDialog::Accepted) {
0211         QStringList files;
0212         if (m_statusList->getSelectionForCommit(files)) {
0213             HgWrapper *hgWrapper = HgWrapper::instance();
0214             if (m_branchAction == NewBranch) {
0215                 if (!hgWrapper->createBranch(m_newBranchName)) {
0216                     KMessageBox::error(this,
0217                             i18n("Could not create branch! Aborting commit!"));
0218                     return;
0219                 }
0220             }
0221             bool success = hgWrapper->commit(m_commitMessage->text(),
0222                     files, m_branchAction==CloseBranch);
0223             if (success) {
0224                 QDialog::done(r);
0225             }
0226             else {
0227                 KMessageBox::error(this, i18n("Commit unsuccessful!"));
0228             }
0229         }
0230         else {
0231             KMessageBox::error(this, i18n("No files for commit!"));
0232         }
0233     }
0234     else {
0235         QDialog::done(r);
0236     }
0237 }
0238 
0239 void HgCommitDialog::saveGeometry()
0240 {
0241     FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self();
0242     settings->setCommitDialogHeight(this->height());
0243     settings->setCommitDialogWidth(this->width());
0244     settings->setHorizontalSplitterSizes(m_horizontalSplitter->sizes());
0245     settings->setVerticalSplitterSizes(m_verticalSplitter->sizes());
0246     settings->save();
0247 }
0248 
0249 void HgCommitDialog::slotBranchActions(QAction *action)
0250 {
0251     HgWrapper *hgWrapper = HgWrapper::instance();
0252     QString currentBranch;
0253     hgWrapper->executeCommand(QStringLiteral("branch"), QStringList(), currentBranch);
0254     currentBranch.replace(QLatin1Char('\n'), QString());
0255     currentBranch = QLatin1String(" (") + currentBranch + QLatin1Char(')');
0256     if (action == m_useCurrentBranch) {
0257         m_branchAction = NoChanges;
0258         m_branchButton->setText(i18n("Branch: Current Branch") + currentBranch);
0259     }
0260     else if (action == m_newBranch) {
0261         NewBranchDialog diag;
0262         if (diag.exec() == QDialog::Accepted) {
0263            m_branchAction = NewBranch;
0264            m_newBranchName = diag.getBranchName(); 
0265            m_branchButton->setText(i18n("Branch: ") + m_newBranchName);
0266         }
0267         else { // restore previous check state
0268             if (m_branchAction == NoChanges) {
0269                 m_useCurrentBranch->setChecked(true);
0270             }
0271             else if (m_branchAction == CloseBranch) {
0272                 m_closeBranch->setChecked(true);
0273             }
0274         }
0275     }
0276     else if (action == m_closeBranch) {
0277         m_branchAction = CloseBranch;
0278         m_branchButton->setText(i18n("Branch: Close Current") + currentBranch);
0279     }
0280 }
0281 
0282 /*****************/
0283 /* Branch Dialog */
0284 /*****************/
0285 
0286 NewBranchDialog::NewBranchDialog(QWidget *parent):
0287     QDialog(parent)
0288 {
0289     // dialog properties
0290     this->setWindowTitle(xi18nc("@title:window",
0291                 "<application>Hg</application> Commit: New Branch"));
0292     QDialogButtonBox *buttonBox= new QDialogButtonBox(QDialogButtonBox::Cancel, this);
0293     m_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
0294     m_okButton->setDisabled(true);
0295     m_okButton->setDefault(true);
0296 
0297     m_branchList = HgWrapper::instance()->getBranches();
0298 
0299     QLabel *message = new QLabel(xi18nc("@label", "Enter new branch name"));
0300     m_branchNameInput = new QLineEdit;
0301     m_errorLabel = new QLabel;
0302 
0303     QVBoxLayout *layout = new QVBoxLayout;
0304     layout->addWidget(message);
0305     layout->addWidget(m_branchNameInput);
0306     layout->addWidget(m_errorLabel);
0307     layout->addWidget(buttonBox);
0308 
0309     setLayout(layout);
0310 
0311     connect(m_branchNameInput, &QLineEdit::textChanged,
0312             this, &NewBranchDialog::slotTextChanged);
0313     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0314     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0315 }
0316 
0317 void NewBranchDialog::slotTextChanged(const QString &text)
0318 {
0319     if (m_branchList.contains(text)) {
0320         m_errorLabel->setText(xi18nc("@label", "<b>Branch already exists!</b>"));
0321         m_okButton->setDisabled(true);
0322     }
0323     else if (text.length() > 0) {
0324         m_errorLabel->clear();
0325         m_okButton->setDisabled(false);
0326     }
0327     else {
0328         m_errorLabel->setText(xi18nc("@label", "<b>Enter some text!</b>"));
0329         m_okButton->setDisabled(true);
0330     }
0331 }
0332 
0333 QString NewBranchDialog::getBranchName() const
0334 {
0335     return m_branchNameInput->text();
0336 }
0337 
0338 void HgCommitDialog::createCopyMessageMenu()
0339 {
0340     QActionGroup *actionGroup = new QActionGroup(this);
0341     connect(actionGroup, &QActionGroup::triggered,
0342             this, &HgCommitDialog::slotInsertCopyMessage);
0343 
0344     const QStringList args{
0345         QStringLiteral("--limit"),
0346         QStringLiteral("7"),
0347         QStringLiteral("--template"),
0348         QStringLiteral("{desc}\n"),
0349     };
0350 //     args << QLatin1String("{desc|short}\n");
0351 
0352     HgWrapper *hgw = HgWrapper::instance();
0353     QString output;
0354     hgw->executeCommand(QStringLiteral("log"), args, output);
0355 
0356     const QStringList messages = output.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
0357     for (const QString &msg : messages) {
0358         QAction *action = m_copyMessageMenu->addAction(msg.left(40)); // max 40 characters
0359         action->setData(msg); // entire description into action data
0360         actionGroup->addAction(action);
0361     }
0362 }
0363 
0364 void HgCommitDialog::slotInsertCopyMessage(QAction *action)
0365 {
0366     m_commitMessage->setText(action->data().toString());
0367 }
0368 
0369 
0370 
0371 #include "moc_commitdialog.cpp"