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

0001 /*
0002     SPDX-FileCopyrightText: 2011 Vishesh Yadav <vishesh3y@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "syncdialogbase.h"
0008 #include "hgconfig.h"
0009 #include "pathselector.h"
0010 #include "fileviewhgpluginsettings.h"
0011 
0012 #include <QApplication>
0013 #include <QGridLayout>
0014 #include <QHBoxLayout>
0015 #include <QVBoxLayout>
0016 #include <QStringList>
0017 #include <QTextCodec>
0018 #include <QHeaderView>
0019 #include <QLabel>
0020 #include <QCheckBox>
0021 #include <QGroupBox>
0022 #include <QProgressBar>
0023 #include <KTextEdit>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 
0027 HgSyncBaseDialog::HgSyncBaseDialog(DialogType dialogType, QWidget *parent):
0028     DialogBase(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent),
0029     m_haveChanges(false),
0030     m_terminated(false),
0031     m_dialogType(dialogType)
0032 {
0033     m_hgw = HgWrapper::instance();
0034 }
0035 
0036 void HgSyncBaseDialog::setup()
0037 {
0038     createChangesGroup();
0039     readBigSize();
0040     setupUI();
0041     
0042     connect(m_changesButton, &QAbstractButton::clicked,
0043             this, &HgSyncBaseDialog::slotGetChanges);
0044     connect(&m_process, &QProcess::stateChanged,
0045             this, &HgSyncBaseDialog::slotUpdateBusy);
0046     connect(&m_main_process, &QProcess::stateChanged,
0047             this, &HgSyncBaseDialog::slotUpdateBusy);
0048     connect(&m_main_process, &QProcess::finished,
0049             this, &HgSyncBaseDialog::slotOperationComplete);
0050     connect(&m_main_process, &QProcess::errorOccurred,
0051             this, &HgSyncBaseDialog::slotOperationError);
0052     connect(&m_process, &QProcess::errorOccurred,
0053             this, &HgSyncBaseDialog::slotOperationError);
0054     connect(&m_process, &QProcess::finished,
0055             this, &HgSyncBaseDialog::slotChangesProcessComplete);
0056     connect(this, &QDialog::finished, this, &HgSyncBaseDialog::slotWriteBigSize);
0057 }
0058 
0059 void HgSyncBaseDialog::createOptionGroup()
0060 {
0061     setOptions();
0062     QVBoxLayout *layout = new QVBoxLayout;
0063 
0064     for (QCheckBox *cb : std::as_const(m_options)) {
0065         layout->addWidget(cb);
0066     }
0067 
0068     m_optionGroup = new QGroupBox(this);
0069     m_optionGroup->setLayout(layout);
0070     m_optionGroup->setVisible(false);
0071 }
0072 
0073 void HgSyncBaseDialog::setupUI()
0074 {
0075     // top url bar
0076     m_pathSelector = new HgPathSelector;
0077 
0078     // changes button
0079     //FIXME not very good idea. Bad HACK
0080     if (m_dialogType == PullDialog) {
0081         m_changesButton = new QPushButton(i18nc("@label:button",
0082                 "Show Incoming Changes"));
0083     }
0084     else {
0085         m_changesButton = new QPushButton(i18nc("@label:button",
0086                 "Show Outgoing Changes"));
0087     }
0088     m_changesButton->setSizePolicy(QSizePolicy::Fixed,
0089             QSizePolicy::Fixed);
0090     m_changesButton->setCheckable(true);
0091 
0092     // dialog's main widget
0093     QWidget *widget = new QWidget(this);
0094     QVBoxLayout *lay = new QVBoxLayout;
0095     lay->addWidget(m_pathSelector);
0096 
0097     // changes
0098     m_changesGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0099     lay->addWidget(m_changesGroup);
0100 
0101     // bottom bar
0102     QHBoxLayout *bottomLayout = new QHBoxLayout;
0103     m_statusProg = new QProgressBar;
0104     m_statusProg->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0105     bottomLayout->addWidget(m_changesButton, Qt::AlignLeft);
0106     bottomLayout->addStretch();
0107     bottomLayout->addWidget(m_statusProg);
0108     
0109     //
0110     lay->addLayout(bottomLayout);
0111     widget->setLayout(lay);
0112 
0113     createOptionGroup();
0114 
0115     QVBoxLayout *mainLayout = new QVBoxLayout;
0116     mainLayout->addWidget(widget);
0117     mainLayout->addWidget(m_optionGroup);
0118 
0119     // bottom button box with OK/Cancel and Options buttons
0120     okButton()->setText(m_dialogType == PullDialog ? i18nc("@action:button", "Pull") : i18nc("@action:button", "Push"));
0121     okButton()->setIcon(QIcon::fromTheme(
0122                                m_dialogType == PullDialog ? QStringLiteral("vcs-pull") : QStringLiteral("vcs-push")));
0123     m_optionsButton = new QPushButton(buttonBox());
0124     m_optionsButton->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
0125     switchOptionsButton(true);
0126     buttonBox()->addButton(m_optionsButton, QDialogButtonBox::ResetRole);
0127     layout()->insertLayout(0, mainLayout);
0128 
0129     connect(m_optionsButton, &QAbstractButton::clicked, this, &HgSyncBaseDialog::slotOptionsButtonClick);
0130 }
0131 
0132 void HgSyncBaseDialog::slotGetChanges()
0133 {
0134     if (m_haveChanges) {
0135         m_changesGroup->setVisible(!m_changesGroup->isVisible());
0136         m_changesButton->setChecked(m_changesGroup->isVisible());
0137         if (m_changesGroup->isVisible()) {
0138             loadBigSize();
0139         }
0140         else {
0141             loadSmallSize();
0142         }
0143         return;
0144     }
0145     if (m_process.state() == QProcess::Running) {
0146         return;
0147     }
0148 
0149     QStringList args;
0150     getHgChangesArguments(args);
0151     m_process.setWorkingDirectory(m_hgw->getBaseDir());
0152     m_process.start(QLatin1String("hg"), args);
0153 }
0154 
0155 
0156 void HgSyncBaseDialog::slotChangesProcessComplete(int exitCode, QProcess::ExitStatus status)
0157 {
0158 
0159     if (exitCode != 0 || status != QProcess::NormalExit) {
0160         QString message = QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardError());
0161         if (message.isEmpty()) {
0162             message = i18nc("@message", "No changes found!");
0163         }
0164         KMessageBox::error(this, message);
0165         return;
0166     }
0167 
0168     char buffer[512];
0169 
0170     /**
0171      * unwantedRead boolean to ensure that unwanted information messages
0172      * by mercurial are filtered out.
0173      *  eg. Comparing with /path/to/repository
0174      *      Searching for changes
0175      */
0176     bool unwantedRead = false;
0177 
0178     /**
0179      * hasChanges boolean checks whether there are any changes to be sent 
0180      * or received and invoke noChangesMessage() if its false
0181      */
0182     bool hasChanges = false;
0183 
0184     while (m_process.readLine(buffer, sizeof(buffer)) > 0) {
0185         QString line(QTextCodec::codecForLocale()->toUnicode(buffer));
0186         if (unwantedRead ) {
0187             line.remove(QLatin1String("Commit: "));
0188             parseUpdateChanges(line.trimmed());
0189             hasChanges = true;;
0190         }
0191         else if (line.startsWith(QLatin1String("Commit: "))) {
0192             unwantedRead = true;
0193             line.remove(QLatin1String("Commit: "));
0194             parseUpdateChanges(line.trimmed());
0195             hasChanges = true;
0196         }
0197     }
0198 
0199     if (!hasChanges) {
0200         noChangesMessage();
0201     }
0202 
0203     m_changesGroup->setVisible(true);
0204     m_changesButton->setChecked(true);
0205     loadBigSize();
0206     m_haveChanges = true; 
0207     Q_EMIT changeListAvailable();
0208 }
0209 
0210 void HgSyncBaseDialog::slotChangesProcessError()
0211 {
0212     qDebug() << "Cant get changes";
0213     KMessageBox::error(this, i18n("Error!"));
0214 }
0215 
0216 void HgSyncBaseDialog::loadSmallSize()
0217 {
0218     m_bigSize = size();
0219     resize(m_smallSize);
0220     adjustSize();
0221     updateGeometry();
0222 }
0223 
0224 void HgSyncBaseDialog::loadBigSize()
0225 {
0226     m_smallSize = size();
0227     resize(m_bigSize);
0228 }
0229 
0230 void HgSyncBaseDialog::slotWriteBigSize()
0231 {
0232     if (m_changesGroup->isVisible()) {
0233         m_bigSize = size();
0234     }
0235     writeBigSize();
0236 }
0237 
0238 void HgSyncBaseDialog::done(int r)
0239 {
0240     if (r == QDialog::Accepted) {
0241         if (m_main_process.state() == QProcess::Running ||
0242                 m_main_process.state() == QProcess::Starting) {
0243             qDebug() << "HgWrapper already busy";
0244             return;
0245         }
0246 
0247         QStringList args;
0248         QString command = (m_dialogType==PullDialog) ? QStringLiteral("pull") : QStringLiteral("push");
0249         args << command;
0250         args << m_pathSelector->remote();
0251         appendOptionArguments(args);
0252 
0253         m_terminated = false;
0254         
0255         m_main_process.setWorkingDirectory(m_hgw->getBaseDir());
0256         m_main_process.start(QStringLiteral("hg"), args);
0257     }
0258     else {
0259         if (m_process.state() == QProcess::Running || 
0260             m_process.state() == QProcess::Starting ||
0261             m_main_process.state() == QProcess::Running ||
0262             m_main_process.state() == QProcess::Starting) 
0263         {
0264             if (m_process.state() == QProcess::Running ||
0265                     m_process.state() == QProcess::Starting) {
0266                 m_process.terminate();
0267             }
0268             if (m_main_process.state() == QProcess::Running ||
0269                      m_main_process.state() == QProcess::Starting) {
0270                 qDebug() << "terminating pull/push process";
0271                 m_terminated = true;
0272                 m_main_process.terminate();
0273             }
0274         }
0275         else {
0276             QDialog::done(r);
0277         }
0278     }
0279 }
0280 
0281 void HgSyncBaseDialog::slotOperationComplete(int exitCode, QProcess::ExitStatus status)
0282 {
0283     if (exitCode == 0 && status == QProcess::NormalExit) {
0284         QDialog::done(QDialog::Accepted);
0285     }
0286     else {
0287         if (!m_terminated) {
0288             KMessageBox::error(this, i18n("Error!"));
0289         }
0290     }
0291 }
0292 
0293 void HgSyncBaseDialog::slotOperationError()
0294 {
0295     KMessageBox::error(this, i18n("Error!"));
0296 }
0297 
0298 void HgSyncBaseDialog::slotUpdateBusy(QProcess::ProcessState state)
0299 {
0300     if (state == QProcess::Running || state == QProcess::Starting) {
0301         m_statusProg->setRange(0, 0);
0302         m_changesButton->setEnabled(false);
0303         m_changesButton->setChecked(true);
0304         okButton()->setDisabled(true);
0305     }
0306     else {
0307         m_statusProg->setRange(0, 100);
0308         m_changesButton->setEnabled(true);
0309         okButton()->setDisabled(false);
0310     }
0311     m_statusProg->repaint();
0312     QApplication::processEvents();
0313 }
0314 
0315 void HgSyncBaseDialog::slotOptionsButtonClick()
0316 {
0317   if (m_optionsButton->text().contains(QLatin1String(">>"))) {
0318     switchOptionsButton(false);
0319     m_optionGroup->setVisible(true);
0320   }
0321   else {
0322     switchOptionsButton(true);
0323     m_optionGroup->setVisible(false);
0324   }
0325 }
0326 
0327 void HgSyncBaseDialog::switchOptionsButton(bool switchOn)
0328 {
0329   m_optionsButton->setText(xi18nc("@action:button", "Options") +
0330                       (switchOn ? QLatin1String(" >>") : QLatin1String(" <<")));
0331 }
0332 
0333 
0334 
0335 
0336 #include "moc_syncdialogbase.cpp"