File indexing completed on 2024-04-14 05:34:14
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 "fileviewhgplugin.h" 0009 #include "hgconfig.h" 0010 #include "configdialog.h" 0011 #include "renamedialog.h" 0012 #include "commitdialog.h" 0013 #include "branchdialog.h" 0014 #include "tagdialog.h" 0015 #include "updatedialog.h" 0016 #include "clonedialog.h" 0017 #include "createdialog.h" 0018 #include "pushdialog.h" 0019 #include "pulldialog.h" 0020 #include "mergedialog.h" 0021 #include "bundledialog.h" 0022 #include "exportdialog.h" 0023 #include "importdialog.h" 0024 #include "servedialog.h" 0025 #include "backoutdialog.h" 0026 0027 #include <QTextCodec> 0028 #include <QDir> 0029 #include <QAction> 0030 #include <QIcon> 0031 #include <QMenu> 0032 #include <QDebug> 0033 0034 #include <KApplicationTrader> 0035 #include <KMessageBox> 0036 #include <QFileDialog> 0037 #include <KConfig> 0038 #include <KConfigGroup> 0039 #include <KService> 0040 0041 #include <KLocalizedString> 0042 #include <KPluginFactory> 0043 #include <QRegularExpression> 0044 0045 K_PLUGIN_CLASS_WITH_JSON(FileViewHgPlugin, "fileviewhgplugin.json") 0046 0047 //TODO: Build a proper status signal system to sync HgWrapper/Dialogs with this 0048 //TODO: Show error messages and set their message appropriately(hg output) 0049 //TODO: Use xi18nc rather than i18c throughout plugin 0050 0051 FileViewHgPlugin::FileViewHgPlugin(QObject *parent, const QList<QVariant> &args): 0052 KVersionControlPlugin(parent), 0053 m_mainContextMenu(nullptr), 0054 m_addAction(nullptr), 0055 m_removeAction(nullptr), 0056 m_renameAction(nullptr), 0057 m_commitAction(nullptr), 0058 m_branchAction(nullptr), 0059 m_tagAction(nullptr), 0060 m_updateAction(nullptr), 0061 m_cloneAction(nullptr), 0062 m_createAction(nullptr), 0063 m_configAction(nullptr), 0064 m_globalConfigAction(nullptr), 0065 m_repoConfigAction(nullptr), 0066 m_pushAction(nullptr), 0067 m_pullAction(nullptr), 0068 m_revertAction(nullptr), 0069 m_revertAllAction(nullptr), 0070 m_rollbackAction(nullptr), 0071 m_mergeAction(nullptr), 0072 m_bundleAction(nullptr), 0073 m_exportAction(nullptr), 0074 m_unbundleAction(nullptr), 0075 m_importAction(nullptr), 0076 m_diffAction(nullptr), 0077 m_serveAction(nullptr), 0078 m_backoutAction(nullptr), 0079 m_isCommitable(false), 0080 m_hgWrapper(nullptr), 0081 m_retrievalHgw(nullptr) 0082 { 0083 Q_UNUSED(args); 0084 0085 qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError"); 0086 qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus"); 0087 qRegisterMetaType<QProcess::ProcessState>("QProcess::ProcessState"); 0088 0089 m_parentWidget = qobject_cast<QWidget*>(parent); 0090 0091 m_addAction = new QAction(this); 0092 m_addAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0093 m_addAction->setText(xi18nc("@action:inmenu", 0094 "<application>Hg</application> Add")); 0095 connect(m_addAction, &QAction::triggered, 0096 this, &FileViewHgPlugin::addFiles); 0097 0098 m_removeAction = new QAction(this); 0099 m_removeAction->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0100 m_removeAction->setText(xi18nc("@action:inmenu", 0101 "<application>Hg</application> Remove")); 0102 connect(m_removeAction, &QAction::triggered, 0103 this, &FileViewHgPlugin::removeFiles); 0104 0105 m_renameAction = new QAction(this); 0106 m_renameAction->setIcon(QIcon::fromTheme(QStringLiteral("list-rename"))); 0107 m_renameAction->setText(xi18nc("@action:inmenu", 0108 "<application>Hg</application> Rename")); 0109 connect(m_renameAction, &QAction::triggered, 0110 this, &FileViewHgPlugin::renameFile); 0111 0112 m_commitAction = new QAction(this); 0113 m_commitAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-commit"))); 0114 m_commitAction->setText(xi18nc("@action:inmenu", 0115 "<application>Hg</application> Commit")); 0116 connect(m_commitAction, &QAction::triggered, 0117 this, &FileViewHgPlugin::commit); 0118 0119 m_tagAction = new QAction(this); 0120 m_tagAction->setIcon(QIcon::fromTheme(QStringLiteral("svn-tag"))); 0121 m_tagAction->setText(xi18nc("@action:inmenu", 0122 "<application>Hg</application> Tag")); 0123 connect(m_tagAction, &QAction::triggered, 0124 this, &FileViewHgPlugin::tag); 0125 0126 m_branchAction = new QAction(this); 0127 m_branchAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-branch"))); 0128 m_branchAction->setText(xi18nc("@action:inmenu", 0129 "<application>Hg</application> Branch")); 0130 connect(m_branchAction, &QAction::triggered, 0131 this, &FileViewHgPlugin::branch); 0132 0133 m_cloneAction = new QAction(this); 0134 m_cloneAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-clone"))); 0135 m_cloneAction->setText(xi18nc("@action:inmenu", 0136 "<application>Hg</application> Clone")); 0137 connect(m_cloneAction, &QAction::triggered, 0138 this, &FileViewHgPlugin::clone); 0139 0140 m_createAction = new QAction(this); 0141 m_createAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-create"))); 0142 m_createAction->setText(xi18nc("@action:inmenu", 0143 "<application>Hg</application> Init")); 0144 connect(m_createAction, &QAction::triggered, 0145 this, &FileViewHgPlugin::create); 0146 0147 m_updateAction = new QAction(this); 0148 m_updateAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-pull"))); 0149 m_updateAction->setText(xi18nc("@action:inmenu", 0150 "<application>Hg</application> Update")); 0151 connect(m_updateAction, &QAction::triggered, 0152 this, &FileViewHgPlugin::update); 0153 0154 m_globalConfigAction = new QAction(this); 0155 m_globalConfigAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-config"))); 0156 m_globalConfigAction->setText(xi18nc("@action:inmenu", 0157 "<application>Hg</application> Global Config")); 0158 connect(m_globalConfigAction, &QAction::triggered, 0159 this, &FileViewHgPlugin::global_config); 0160 0161 m_repoConfigAction = new QAction(this); 0162 m_repoConfigAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-config"))); 0163 m_repoConfigAction->setText(xi18nc("@action:inmenu", 0164 "<application>Hg</application> Repository Config")); 0165 connect(m_repoConfigAction, &QAction::triggered, 0166 this, &FileViewHgPlugin::repo_config); 0167 0168 m_pushAction = new QAction(this); 0169 m_pushAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-push"))); 0170 m_pushAction->setText(xi18nc("@action:inmenu", 0171 "<application>Hg</application> Push")); 0172 connect(m_pushAction, &QAction::triggered, 0173 this, &FileViewHgPlugin::push); 0174 0175 m_pullAction = new QAction(this); 0176 m_pullAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-pull"))); 0177 m_pullAction->setText(xi18nc("@action:inmenu", 0178 "<application>Hg</application> Pull")); 0179 connect(m_pullAction, &QAction::triggered, 0180 this, &FileViewHgPlugin::pull); 0181 0182 m_revertAction = new QAction(this); 0183 m_revertAction->setIcon(QIcon::fromTheme(QStringLiteral("document-revert"))); 0184 m_revertAction->setText(xi18nc("@action:inmenu", 0185 "<application>Hg</application> Revert")); 0186 connect(m_revertAction, &QAction::triggered, 0187 this, &FileViewHgPlugin::revert); 0188 0189 m_revertAllAction = new QAction(this); 0190 m_revertAllAction->setIcon(QIcon::fromTheme(QStringLiteral("document-revert"))); 0191 m_revertAllAction->setText(xi18nc("@action:inmenu", 0192 "<application>Hg</application> Revert All")); 0193 connect(m_revertAllAction, &QAction::triggered, 0194 this, &FileViewHgPlugin::revertAll); 0195 0196 m_rollbackAction = new QAction(this); 0197 m_rollbackAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-rollback"))); 0198 m_rollbackAction->setText(xi18nc("@action:inmenu", 0199 "<application>Hg</application> Rollback")); 0200 connect(m_rollbackAction, &QAction::triggered, 0201 this, &FileViewHgPlugin::rollback); 0202 0203 m_mergeAction = new QAction(this); 0204 m_mergeAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-merge"))); 0205 m_mergeAction->setText(xi18nc("@action:inmenu", 0206 "<application>Hg</application> Merge")); 0207 connect(m_mergeAction, &QAction::triggered, 0208 this, &FileViewHgPlugin::merge); 0209 0210 m_bundleAction = new QAction(this); 0211 m_bundleAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-bundle"))); 0212 m_bundleAction->setText(xi18nc("@action:inmenu", 0213 "<application>Hg</application> Bundle")); 0214 connect(m_bundleAction, &QAction::triggered, 0215 this, &FileViewHgPlugin::bundle); 0216 0217 m_exportAction = new QAction(this); 0218 m_exportAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-export"))); 0219 m_exportAction->setText(xi18nc("@action:inmenu", 0220 "<application>Hg</application> Export")); 0221 connect(m_exportAction, &QAction::triggered, 0222 this, &FileViewHgPlugin::exportChangesets); 0223 0224 m_importAction = new QAction(this); 0225 m_importAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-import"))); 0226 m_importAction->setText(xi18nc("@action:inmenu", 0227 "<application>Hg</application> Import")); 0228 connect(m_importAction, &QAction::triggered, 0229 this, &FileViewHgPlugin::importChangesets); 0230 0231 m_unbundleAction = new QAction(this); 0232 m_unbundleAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-unbundle"))); 0233 m_unbundleAction->setText(xi18nc("@action:inmenu", 0234 "<application>Hg</application> Unbundle")); 0235 connect(m_unbundleAction, &QAction::triggered, 0236 this, &FileViewHgPlugin::unbundle); 0237 0238 m_serveAction = new QAction(this); 0239 m_serveAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-serve"))); 0240 m_serveAction->setText(xi18nc("@action:inmenu", 0241 "<application>Hg</application> Serve")); 0242 connect(m_serveAction, &QAction::triggered, 0243 this, &FileViewHgPlugin::serve); 0244 0245 m_backoutAction = new QAction(this); 0246 m_backoutAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-backout"))); 0247 m_backoutAction->setText(xi18nc("@action:inmenu", 0248 "<application>Hg</application> Backout")); 0249 connect(m_backoutAction, &QAction::triggered, 0250 this, &FileViewHgPlugin::backout); 0251 0252 m_diffAction = new QAction(this); 0253 m_diffAction->setIcon(QIcon::fromTheme(QStringLiteral("vcs-diff"))); 0254 m_diffAction->setText(xi18nc("@action:inmenu", 0255 "<application>Hg</application> Diff")); 0256 connect(m_diffAction, &QAction::triggered, 0257 this, &FileViewHgPlugin::diff); 0258 0259 /* Submenu to make the main menu less cluttered */ 0260 m_mainContextMenu = new QMenu; 0261 m_mainContextMenu->addAction(m_updateAction); 0262 m_mainContextMenu->addAction(m_branchAction); 0263 m_mainContextMenu->addAction(m_tagAction); 0264 m_mainContextMenu->addAction(m_mergeAction); 0265 m_mainContextMenu->addAction(m_revertAllAction); 0266 m_mainContextMenu->addAction(m_rollbackAction); 0267 m_mainContextMenu->addAction(m_backoutAction); 0268 m_mainContextMenu->addAction(m_bundleAction); 0269 m_mainContextMenu->addAction(m_unbundleAction); 0270 m_mainContextMenu->addAction(m_exportAction); 0271 m_mainContextMenu->addAction(m_importAction); 0272 m_mainContextMenu->addAction(m_serveAction); 0273 m_mainContextMenu->addAction(m_globalConfigAction); 0274 m_mainContextMenu->addAction(m_repoConfigAction); 0275 0276 m_menuAction = new QAction(this); 0277 m_menuAction->setIcon(QIcon::fromTheme(QStringLiteral("hg-main"))); 0278 m_menuAction->setText(xi18nc("@action:inmenu", 0279 "<application>Mercurial</application>")); 0280 m_menuAction->setMenu(m_mainContextMenu); 0281 } 0282 0283 FileViewHgPlugin::~FileViewHgPlugin() 0284 { 0285 } 0286 0287 void FileViewHgPlugin::createHgWrapper() const 0288 { 0289 static bool created = false; 0290 0291 if (created && m_hgWrapper != nullptr) { 0292 return; 0293 } 0294 0295 created = true; 0296 0297 m_hgWrapper = HgWrapper::instance(); 0298 0299 connect(m_hgWrapper, &HgWrapper::primaryOperationFinished, 0300 this, &FileViewHgPlugin::slotOperationCompleted); 0301 connect(m_hgWrapper, &HgWrapper::primaryOperationError, 0302 this, &FileViewHgPlugin::slotOperationError); 0303 } 0304 0305 QString FileViewHgPlugin::fileName() const 0306 { 0307 return QStringLiteral(".hg"); 0308 } 0309 0310 QString FileViewHgPlugin::localRepositoryRoot(const QString& directory) const 0311 { 0312 QProcess process; 0313 process.setWorkingDirectory(directory); 0314 process.start(QStringLiteral("hg"), {QStringLiteral("root")}); 0315 if (process.waitForReadyRead(100) && process.exitCode() == 0) { 0316 return QString::fromUtf8(process.readAll().chopped(1)); 0317 } 0318 return QString(); 0319 } 0320 0321 bool FileViewHgPlugin::beginRetrieval(const QString &directory) 0322 { 0323 clearMessages(); 0324 m_currentDir = directory; 0325 m_versionInfoHash.clear(); 0326 //createHgWrapper(); 0327 //m_hgWrapper->setCurrentDir(directory); 0328 //m_hgWrapper->getItemVersions(m_versionInfoHash); 0329 if (m_retrievalHgw == nullptr) { 0330 m_retrievalHgw = new HgWrapper; 0331 } 0332 m_retrievalHgw->setCurrentDir(directory); 0333 m_retrievalHgw->getItemVersions(m_versionInfoHash); 0334 return true; 0335 } 0336 0337 void FileViewHgPlugin::endRetrieval() 0338 { 0339 } 0340 0341 KVersionControlPlugin::ItemVersion FileViewHgPlugin::itemVersion(const KFileItem &item) const 0342 { 0343 //FIXME: When folder is empty or all files within untracked. 0344 const QString itemUrl = item.localPath(); 0345 if (item.isDir()) { 0346 QHash<QString, ItemVersion>::const_iterator it 0347 = m_versionInfoHash.constBegin(); 0348 while (it != m_versionInfoHash.constEnd()) { 0349 if (it.key().startsWith(itemUrl)) { 0350 const ItemVersion state = m_versionInfoHash.value(it.key()); 0351 if (state == LocallyModifiedVersion || 0352 state == AddedVersion || 0353 state == RemovedVersion) { 0354 return LocallyModifiedVersion; 0355 } 0356 } 0357 ++it; 0358 } 0359 0360 // Making folders with all files within untracked 'Unversioned' 0361 // will disable the context menu there. Will enable recursive 0362 // add however. 0363 QDir dir(item.localPath()); 0364 const QStringList filesInside = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); 0365 for (const QString &fileName : filesInside) { 0366 const QUrl tempUrl(dir.absoluteFilePath(fileName)); 0367 KFileItem tempFileItem(tempUrl); 0368 if (itemVersion(tempFileItem) == NormalVersion) { 0369 return NormalVersion; 0370 } 0371 } 0372 return UnversionedVersion; 0373 } 0374 if (m_versionInfoHash.contains(itemUrl)) { 0375 return m_versionInfoHash.value(itemUrl); 0376 } 0377 return NormalVersion; 0378 } 0379 0380 QList<QAction*> FileViewHgPlugin::versionControlActions(const KFileItemList &items) const 0381 { 0382 //TODO: Make it work with universal context menu when implemented 0383 // in dolphin 0384 qDebug() << items.count(); 0385 if (items.count() == 1 && items.first().isDir()) { 0386 return directoryContextMenu(m_currentDir); 0387 } 0388 else { 0389 return itemContextMenu(items); 0390 } 0391 return QList<QAction*>(); 0392 } 0393 0394 QList<QAction*> FileViewHgPlugin::outOfVersionControlActions(const KFileItemList &items) const 0395 { 0396 Q_UNUSED(items) 0397 0398 return {}; 0399 } 0400 0401 QList<QAction*> FileViewHgPlugin::universalContextMenuActions(const QString &directory) const 0402 { 0403 QList<QAction*> result; 0404 m_universalCurrentDirectory = directory; 0405 result.append(m_createAction); 0406 result.append(m_cloneAction); 0407 return result; 0408 } 0409 0410 QList<QAction*> FileViewHgPlugin::itemContextMenu(const KFileItemList &items) const 0411 { 0412 Q_ASSERT(!items.isEmpty()); 0413 0414 clearMessages(); 0415 createHgWrapper(); 0416 m_hgWrapper->setCurrentDir(m_currentDir); 0417 if (!m_hgWrapper->isBusy()) { 0418 m_contextItems.clear(); 0419 for (const KFileItem &item : items) { 0420 m_contextItems.append(item); 0421 } 0422 0423 //see which actions should be enabled 0424 int versionedCount = 0; 0425 int addableCount = 0; 0426 int revertableCount = 0; 0427 for (const KFileItem &item : items) { 0428 const ItemVersion state = itemVersion(item); 0429 if (state != UnversionedVersion && state != RemovedVersion) { 0430 ++versionedCount; 0431 } 0432 if (state == UnversionedVersion || 0433 state == LocallyModifiedUnstagedVersion) { 0434 ++addableCount; 0435 } 0436 if (state == LocallyModifiedVersion || 0437 state == AddedVersion || 0438 state == RemovedVersion) { 0439 ++revertableCount; 0440 } 0441 } 0442 0443 m_addAction->setEnabled(addableCount == items.count()); 0444 m_removeAction->setEnabled(versionedCount == items.count()); 0445 m_revertAction->setEnabled(revertableCount == items.count()); 0446 m_diffAction->setEnabled(revertableCount == items.count() && 0447 items.size() == 1); 0448 m_renameAction->setEnabled(items.size() == 1 && 0449 itemVersion(items.first()) != UnversionedVersion); 0450 } 0451 else { 0452 m_addAction->setEnabled(false); 0453 m_removeAction->setEnabled(false); 0454 m_renameAction->setEnabled(false); 0455 m_revertAction->setEnabled(false); 0456 m_diffAction->setEnabled(false); 0457 } 0458 0459 QList<QAction*> actions; 0460 actions.append(m_addAction); 0461 actions.append(m_removeAction); 0462 actions.append(m_renameAction); 0463 actions.append(m_revertAction); 0464 actions.append(m_diffAction); 0465 0466 return actions; 0467 } 0468 0469 QList<QAction*> FileViewHgPlugin::directoryContextMenu(const QString &directory) const 0470 { 0471 QList<QAction*> actions; 0472 clearMessages(); 0473 createHgWrapper(); 0474 m_hgWrapper->setCurrentDir(directory); 0475 if (!m_hgWrapper->isBusy()) { 0476 actions.append(m_commitAction); 0477 } 0478 actions.append(m_pushAction); 0479 actions.append(m_pullAction); 0480 actions.append(m_diffAction); 0481 actions.append(m_menuAction); 0482 return actions; 0483 } 0484 0485 void FileViewHgPlugin::addFiles() 0486 { 0487 Q_ASSERT(!m_contextItems.isEmpty()); 0488 QString infoMsg = xi18nc("@info:status", 0489 "Adding files to <application>Hg</application> repository..."); 0490 m_errorMsg = xi18nc("@info:status", 0491 "Adding files to <application>Hg</application> repository failed."); 0492 m_operationCompletedMsg = xi18nc("@info:status", 0493 "Added files to <application>Hg</application> repository."); 0494 0495 Q_EMIT infoMessage(infoMsg); 0496 m_hgWrapper->addFiles(m_contextItems); 0497 Q_EMIT itemVersionsChanged(); 0498 } 0499 0500 void FileViewHgPlugin::removeFiles() 0501 { 0502 Q_ASSERT(!m_contextItems.isEmpty()); 0503 0504 int answer = KMessageBox::questionTwoActions(nullptr, 0505 xi18nc("@message:yesorno", 0506 "Would you like to remove selected files " 0507 "from the repository?"), i18n("Remove Files"), KStandardGuiItem::remove(), KStandardGuiItem::cancel()); 0508 if (answer == KMessageBox::ButtonCode::SecondaryAction) { 0509 return; 0510 } 0511 0512 QString infoMsg = xi18nc("@info:status", 0513 "Removing files from <application>Hg</application> repository..."); 0514 m_errorMsg = xi18nc("@info:status", 0515 "Removing files from <application>Hg</application> repository failed."); 0516 m_operationCompletedMsg = xi18nc("@info:status", 0517 "Removed files from <application>Hg</application> repository."); 0518 0519 Q_EMIT infoMessage(infoMsg); 0520 m_hgWrapper->removeFiles(m_contextItems); 0521 } 0522 0523 0524 void FileViewHgPlugin::renameFile() 0525 { 0526 Q_ASSERT(m_contextItems.size() == 1); 0527 0528 m_errorMsg = xi18nc("@info:status", 0529 "Renaming of file in <application>Hg</application> repository failed."); 0530 m_operationCompletedMsg = xi18nc("@info:status", 0531 "Renamed file in <application>Hg</application> repository successfully."); 0532 Q_EMIT infoMessage(xi18nc("@info:status", 0533 "Renaming file in <application>Hg</application> repository.")); 0534 0535 HgRenameDialog dialog(m_contextItems.first(), m_parentWidget); 0536 dialog.exec(); 0537 m_contextItems.clear(); 0538 } 0539 0540 0541 void FileViewHgPlugin::commit() 0542 { 0543 if (m_hgWrapper->isWorkingDirectoryClean()) { 0544 KMessageBox::information(nullptr, xi18nc("@message", "No changes for commit!")); 0545 return; 0546 } 0547 //FIXME: Disable emitting of status messages when executing sub tasks. 0548 m_errorMsg = xi18nc("@info:status", 0549 "Commit to <application>Hg</application> repository failed."); 0550 m_operationCompletedMsg = xi18nc("@info:status", 0551 "Committed to <application>Hg</application> repository."); 0552 Q_EMIT infoMessage(xi18nc("@info:status", 0553 "Commit <application>Hg</application> repository.")); 0554 0555 HgCommitDialog dialog(m_parentWidget); 0556 if (dialog.exec() == QDialog::Accepted) { 0557 Q_EMIT itemVersionsChanged(); 0558 }; 0559 } 0560 0561 void FileViewHgPlugin::tag() 0562 { 0563 m_errorMsg = xi18nc("@info:status", 0564 "Tag operation in <application>Hg</application> repository failed."); 0565 m_operationCompletedMsg = xi18nc("@info:status", 0566 "Tagging operation in <application>Hg</application> repository is successful."); 0567 Q_EMIT infoMessage(xi18nc("@info:status", 0568 "Tagging operation in <application>Hg</application> repository.")); 0569 0570 HgTagDialog dialog(m_parentWidget); 0571 dialog.exec(); 0572 } 0573 0574 void FileViewHgPlugin::update() 0575 { 0576 m_errorMsg = xi18nc("@info:status", 0577 "Update of <application>Hg</application> working directory failed."); 0578 m_operationCompletedMsg = xi18nc("@info:status", 0579 "Update of <application>Hg</application> working directory is successful."); 0580 Q_EMIT infoMessage(xi18nc("@info:status", 0581 "Updating <application>Hg</application> working directory.")); 0582 0583 HgUpdateDialog dialog(m_parentWidget); 0584 dialog.exec(); 0585 } 0586 0587 void FileViewHgPlugin::branch() 0588 { 0589 m_errorMsg = xi18nc("@info:status", 0590 "Branch operation on <application>Hg</application> repository failed."); 0591 m_operationCompletedMsg = xi18nc("@info:status", 0592 "Branch operation on <application>Hg</application> repository completed successfully."); 0593 Q_EMIT infoMessage(xi18nc("@info:status", 0594 "Branch operation on <application>Hg</application> repository.")); 0595 0596 HgBranchDialog dialog(m_parentWidget); 0597 dialog.exec(); 0598 } 0599 0600 void FileViewHgPlugin::clone() 0601 { 0602 clearMessages(); 0603 HgCloneDialog dialog(m_universalCurrentDirectory, m_parentWidget); 0604 dialog.exec(); 0605 } 0606 0607 void FileViewHgPlugin::create() 0608 { 0609 clearMessages(); 0610 HgCreateDialog dialog(m_universalCurrentDirectory, m_parentWidget); 0611 dialog.exec(); 0612 } 0613 0614 void FileViewHgPlugin::global_config() 0615 { 0616 clearMessages(); 0617 HgConfigDialog diag(HgConfig::GlobalConfig, m_parentWidget); 0618 diag.exec(); 0619 } 0620 0621 void FileViewHgPlugin::repo_config() 0622 { 0623 clearMessages(); 0624 HgConfigDialog diag(HgConfig::RepoConfig, m_parentWidget); 0625 diag.exec(); 0626 } 0627 0628 void FileViewHgPlugin::push() 0629 { 0630 clearMessages(); 0631 HgPushDialog diag(m_parentWidget); 0632 diag.exec(); 0633 } 0634 0635 void FileViewHgPlugin::pull() 0636 { 0637 clearMessages(); 0638 HgPullDialog diag(m_parentWidget); 0639 diag.exec(); 0640 } 0641 0642 void FileViewHgPlugin::merge() 0643 { 0644 clearMessages(); 0645 HgMergeDialog diag(m_parentWidget); 0646 diag.exec(); 0647 } 0648 0649 void FileViewHgPlugin::bundle() 0650 { 0651 clearMessages(); 0652 HgBundleDialog diag(m_parentWidget); 0653 diag.exec(); 0654 } 0655 0656 void FileViewHgPlugin::unbundle() 0657 { 0658 clearMessages(); 0659 QString bundle = QFileDialog::getOpenFileName(); 0660 if (bundle.isEmpty()) { 0661 return; 0662 } 0663 0664 QStringList args; 0665 args << bundle; 0666 if (m_hgWrapper->executeCommandTillFinished(QLatin1String("unbundle"), args)) { 0667 } 0668 else { 0669 KMessageBox::error(nullptr, m_hgWrapper->readAllStandardError()); 0670 } 0671 } 0672 0673 void FileViewHgPlugin::importChangesets() 0674 { 0675 clearMessages(); 0676 HgImportDialog diag(m_parentWidget); 0677 diag.exec(); 0678 } 0679 0680 void FileViewHgPlugin::exportChangesets() 0681 { 0682 clearMessages(); 0683 HgExportDialog diag(m_parentWidget); 0684 diag.exec(); 0685 } 0686 0687 void FileViewHgPlugin::revert() 0688 { 0689 clearMessages(); 0690 int answer = KMessageBox::questionTwoActions(nullptr, 0691 xi18nc("@message:yesorno", 0692 "Would you like to revert changes " 0693 "made to selected files?"), i18n("Revert"), KGuiItem(i18n("Revert")), KStandardGuiItem::cancel()); 0694 if (answer == KMessageBox::ButtonCode::SecondaryAction) { 0695 return; 0696 } 0697 0698 QString infoMsg = xi18nc("@info:status", 0699 "Reverting files in <application>Hg</application> repository..."); 0700 m_errorMsg = xi18nc("@info:status", 0701 "Reverting files in <application>Hg</application> repository failed."); 0702 m_operationCompletedMsg = xi18nc("@info:status", 0703 "Reverting files in <application>Hg</application> repository completed successfully."); 0704 0705 Q_EMIT infoMessage(infoMsg); 0706 m_hgWrapper->revert(m_contextItems); 0707 } 0708 0709 void FileViewHgPlugin::revertAll() 0710 { 0711 int answer = KMessageBox::questionTwoActions(nullptr, 0712 xi18nc("@message:yesorno", 0713 "Would you like to revert all changes " 0714 "made to current working directory?"), i18n("Revert All"), KGuiItem(i18n("Revert")), KStandardGuiItem::cancel()); 0715 if (answer == KMessageBox::ButtonCode::SecondaryAction) { 0716 return; 0717 } 0718 0719 QString infoMsg = xi18nc("@info:status", 0720 "Reverting files in <application>Hg</application> repository..."); 0721 m_errorMsg = xi18nc("@info:status", 0722 "Reverting files in <application>Hg</application> repository failed."); 0723 m_operationCompletedMsg = xi18nc("@info:status", 0724 "Reverting files in <application>Hg</application> repository completed successfully."); 0725 0726 Q_EMIT infoMessage(infoMsg); 0727 m_hgWrapper->revertAll(); 0728 } 0729 0730 void FileViewHgPlugin::diff() 0731 { 0732 QString infoMsg = xi18nc("@info:status", 0733 "Generating diff for <application>Hg</application> repository..."); 0734 m_errorMsg = xi18nc("@info:status", 0735 "Could not get <application>Hg</application> repository diff."); 0736 m_operationCompletedMsg = xi18nc("@info:status", 0737 "Generated <application>Hg</application> diff successfully."); 0738 0739 Q_EMIT infoMessage(infoMsg); 0740 0741 QStringList args; 0742 args << QLatin1String("--config"); 0743 args << QLatin1String("extensions.hgext.extdiff="); 0744 args << QLatin1String("-p"); 0745 args << this->visualDiffExecPath(); 0746 0747 if (m_contextItems.length() == 1) { 0748 args << m_contextItems.takeFirst().localPath(); 0749 } 0750 0751 m_hgWrapper->executeCommand(QLatin1String("extdiff"), args); 0752 } 0753 0754 void FileViewHgPlugin::serve() 0755 { 0756 clearMessages(); 0757 HgServeDialog diag(m_parentWidget); 0758 diag.exec(); 0759 } 0760 0761 void FileViewHgPlugin::backout() 0762 { 0763 clearMessages(); 0764 m_hgWrapper = HgWrapper::instance(); 0765 if (!m_hgWrapper->isWorkingDirectoryClean()) { 0766 KMessageBox::error(nullptr, xi18nc("@message:error", 0767 "abort: Uncommitted changes in working directory!")); 0768 return; 0769 } 0770 0771 HgBackoutDialog diag(m_parentWidget); 0772 diag.exec(); 0773 } 0774 0775 void FileViewHgPlugin::rollback() 0776 { 0777 // execute a dry run rollback first to see if there is anything to 0778 // be rolled back, or check what will be rolled back 0779 if (!m_hgWrapper->rollback(true)) { 0780 KMessageBox::error(nullptr, xi18nc("@info:message", "No rollback " 0781 "information available!")); 0782 return; 0783 } 0784 // get what will be rolled back 0785 QString lastTransaction = m_hgWrapper->readAllStandardOutput(); 0786 int cutOfFrom = lastTransaction.indexOf(QRegularExpression(QStringLiteral("\\d"))); 0787 lastTransaction = lastTransaction.mid(cutOfFrom); 0788 0789 // ask 0790 int answer = KMessageBox::questionTwoActions(nullptr, 0791 xi18nc("@message:yesorno", 0792 "Would you like to rollback last transaction?") 0793 + QLatin1String("\nrevision: ") + lastTransaction, i18n("Rollback"), KGuiItem(i18n("Rollback")), KStandardGuiItem::cancel()); 0794 if (answer == KMessageBox::ButtonCode::SecondaryAction) { 0795 return; 0796 } 0797 0798 QString infoMsg = xi18nc("@info:status", 0799 "Executing Rollback <application>Hg</application> repository..."); 0800 m_errorMsg = xi18nc("@info:status", 0801 "Rollback of <application>Hg</application> repository failed."); 0802 m_operationCompletedMsg = xi18nc("@info:status", 0803 "Rollback of <application>Hg</application> repository completed successfully."); 0804 0805 Q_EMIT infoMessage(infoMsg); 0806 m_hgWrapper->rollback(); 0807 KMessageBox::information(nullptr, m_hgWrapper->readAllStandardOutput()); 0808 Q_EMIT itemVersionsChanged(); 0809 } 0810 0811 void FileViewHgPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus) 0812 { 0813 if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { 0814 Q_EMIT errorMessage(m_errorMsg); 0815 } 0816 else { 0817 m_contextItems.clear(); 0818 Q_EMIT operationCompletedMessage(m_operationCompletedMsg); 0819 Q_EMIT itemVersionsChanged(); 0820 } 0821 } 0822 0823 void FileViewHgPlugin::slotOperationError() 0824 { 0825 m_contextItems.clear(); 0826 Q_EMIT errorMessage(m_errorMsg); 0827 } 0828 0829 void FileViewHgPlugin::clearMessages() const 0830 { 0831 m_operationCompletedMsg.clear(); 0832 m_errorMsg.clear(); 0833 } 0834 0835 QString FileViewHgPlugin::visualDiffExecPath() 0836 { 0837 KConfig config(QStringLiteral("dolphin-hg"), KConfig::SimpleConfig, 0838 QStandardPaths::GenericConfigLocation); 0839 0840 KConfigGroup group(&config, QStringLiteral("diff")); 0841 QString result = group.readEntry(QLatin1String("exec"), QString()).trimmed(); 0842 0843 if (result.length() > 0) { 0844 return result; 0845 } 0846 0847 KService::Ptr service = KApplicationTrader::preferredService(QStringLiteral("text/x-diff")); 0848 return service ? service->exec().split(QLatin1Char(' ')).takeFirst() : QString(); 0849 } 0850 0851 #include "fileviewhgplugin.moc" 0852 0853 #include "moc_fileviewhgplugin.cpp"