File indexing completed on 2024-04-21 05:42:26

0001 // clang-format off
0002 /*
0003  * KDiff3 - Text Diff And Merge Tool
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 // clang-format on
0010 
0011 #include "kdiff3fileitemaction.h"
0012 
0013 #include "../src/Utils.h"
0014 #include "../src/TypeUtils.h"
0015 
0016 #include <memory>
0017 
0018 #include <QAction>
0019 #include <QMenu>
0020 #include <QProcess>
0021 #include <QUrl>
0022 
0023 #include <KConfig>
0024 #include <KConfigGroup>
0025 #include <KFileItem>
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 #include <KPluginFactory>
0029 
0030 std::unique_ptr<QStringList> s_pHistory;
0031 
0032 class KDiff3PluginHistory
0033 {
0034     std::unique_ptr<KConfig> m_pConfig;
0035     std::unique_ptr<KConfigGroup> m_pConfigGroup;
0036 
0037   public:
0038     KDiff3PluginHistory()
0039     {
0040         m_pConfig = nullptr;
0041         if(s_pHistory == nullptr)
0042         {
0043             //std::cout << "New History: " << instanceName << std::endl;
0044             s_pHistory = std::make_unique<QStringList>();
0045             m_pConfig = std::make_unique<KConfig>("kdiff3fileitemactionrc", KConfig::SimpleConfig);
0046             m_pConfigGroup = std::make_unique<KConfigGroup>(m_pConfig.get(), "KDiff3Plugin");
0047             *s_pHistory = m_pConfigGroup->readEntry("HistoryStack", QStringList());
0048         }
0049     }
0050 
0051     ~KDiff3PluginHistory()
0052     {
0053         //std::cout << "Delete History" << std::endl;
0054         if(s_pHistory && m_pConfigGroup)
0055             m_pConfigGroup->writeEntry("HistoryStack", *s_pHistory);
0056 
0057         s_pHistory = nullptr;
0058     }
0059 };
0060 
0061 KDiff3PluginHistory s_history;
0062 
0063 K_PLUGIN_FACTORY_WITH_JSON(KDiff3FileItemActionFactory, "kdiff3fileitemaction.json", registerPlugin<KDiff3FileItemAction>();)
0064 #include "kdiff3fileitemaction.moc"
0065 
0066 KDiff3FileItemAction::KDiff3FileItemAction(QObject* pParent, const QVariantList& /*args*/):
0067     KAbstractFileItemActionPlugin(pParent)
0068 {
0069 }
0070 
0071 QList<QAction*> KDiff3FileItemAction::actions(const KFileItemListProperties& fileItemInfos, QWidget* pParentWidget)
0072 {
0073     QList<QAction*> actions;
0074 
0075     if(QStandardPaths::findExecutable("kdiff3").isEmpty())
0076         return actions;
0077 
0078     //m_fileItemInfos = fileItemInfos;
0079     m_pParentWidget = pParentWidget;
0080 
0081     QAction* pMenuAction = new QAction(QIcon::fromTheme(QStringLiteral("kdiff3")), i18nc("Contexualmenu title", "KDiff3..."), this);
0082     QMenu* pActionMenu = new QMenu();
0083     pMenuAction->setMenu(pActionMenu);
0084 
0085     // remember currently selected files
0086     m_list = fileItemInfos.urlList();
0087 
0088     /* Menu structure:
0089       KDiff3 -> (1 File selected):  Save 'selection' for later comparison (push onto history stack)
0090                                     Compare 'selection' with first file on history stack.
0091                                     Compare 'selection' with -> choice from history stack
0092                                     Merge 'selection' with first file on history stack.
0093                                     Merge 'selection' with last two files on history stack.
0094                 (2 Files selected): Compare 's1' with 's2'
0095                                     Merge 's1' with 's2'
0096                 (3 Files selected): Compare 's1', 's2' and 's3'
0097    */
0098 
0099     QAction* pAction = nullptr;
0100     QString actionText;
0101 
0102     if(m_list.count() == 1)
0103     {
0104         QtSizeType historyCount = s_pHistory ? s_pHistory->count() : 0;
0105 
0106         actionText = i18nc("Contexualmenu option", "Compare with %1", (historyCount > 0 ? s_pHistory->first() : QString()));
0107         pAction = new QAction(actionText, this);
0108         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotCompareWith);
0109         pAction->setEnabled(m_list.count() > 0 && historyCount > 0);
0110         pActionMenu->addAction(pAction);
0111 
0112         actionText = i18nc("Contexualmenu option", "Merge with %1", historyCount > 0 ? s_pHistory->first() : QString());
0113         pAction = new QAction(actionText, this);
0114         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotMergeWith);
0115         pAction->setEnabled(m_list.count() > 0 && historyCount > 0);
0116         pActionMenu->addAction(pAction);
0117 
0118         actionText = i18nc("Contexualmenu option", "Save '%1' for later", m_list.first().fileName());
0119         pAction = new QAction(actionText, this);
0120         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotSaveForLater);
0121         pAction->setEnabled(m_list.count() > 0);
0122         pActionMenu->addAction(pAction);
0123 
0124         pAction = new QAction(i18nc("Contexualmenu option", "3-way merge with base"), this);
0125         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotMergeThreeWay);
0126         pAction->setEnabled(m_list.count() > 0 && historyCount >= 2);
0127         pActionMenu->addAction(pAction);
0128 
0129         if(s_pHistory && !s_pHistory->empty())
0130         {
0131             QAction* pHistoryMenuAction = new QAction(i18nc("Contexualmenu option", "Compare with..."), this);
0132             QMenu* pHistoryMenu = new QMenu();
0133             pHistoryMenuAction->setMenu(pHistoryMenu);
0134             pHistoryMenu->setEnabled(m_list.count() > 0 && historyCount > 0);
0135             pActionMenu->addAction(pHistoryMenuAction);
0136             for(const QString& file: qAsConst(*s_pHistory))
0137             {
0138                 pAction = new QAction(file, this);
0139                 pAction->setData(file);
0140                 connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotCompareWithHistoryItem);
0141                 pHistoryMenu->addAction(pAction);
0142             }
0143 
0144             pAction = new QAction(i18nc("Contexualmenu option to cleat comparison list", "Clear list"), this);
0145             connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotClearList);
0146             pActionMenu->addAction(pAction);
0147             pAction->setEnabled(historyCount > 0);
0148         }
0149     }
0150     else if(m_list.count() == 2)
0151     {
0152         pAction = new QAction(i18nc("Contexualmenu option", "Compare"), this);
0153         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotCompareTwoFiles);
0154         pActionMenu->addAction(pAction);
0155     }
0156     else if(m_list.count() == 3)
0157     {
0158         pAction = new QAction(i18nc("Contexualmenu option", "3 way comparison"), this);
0159         connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotCompareThreeFiles);
0160         pActionMenu->addAction(pAction);
0161     }
0162     pAction = new QAction(i18nc("Contexualmenu option", "About KDiff3 menu plugin..."), this);
0163     connect(pAction, &QAction::triggered, this, &KDiff3FileItemAction::slotAbout);
0164     pActionMenu->addAction(pAction);
0165 
0166     actions.append(pMenuAction);
0167     return actions;
0168 }
0169 
0170 KDiff3FileItemAction::~KDiff3FileItemAction() = default;
0171 
0172 void KDiff3FileItemAction::slotCompareWith()
0173 {
0174     if(m_list.count() > 0 && s_pHistory && !s_pHistory->empty())
0175     {
0176         QStringList args = {
0177             s_pHistory->first(),
0178             Utils::urlToString(m_list.first())
0179         };
0180         QProcess::startDetached("kdiff3", args);
0181     }
0182 }
0183 
0184 void KDiff3FileItemAction::slotCompareWithHistoryItem()
0185 {
0186     const QAction* pAction = dynamic_cast<const QAction*>(sender());
0187     if(!m_list.isEmpty() && pAction)
0188     {
0189         QStringList args = {
0190             pAction->data().toString(),
0191             Utils::urlToString(m_list.first())
0192         };
0193         QProcess::startDetached("kdiff3", args);
0194     }
0195 }
0196 
0197 void KDiff3FileItemAction::slotCompareTwoFiles()
0198 {
0199     if(m_list.count() == 2)
0200     {
0201         QStringList args = {
0202             Utils::urlToString(m_list.first()),
0203             Utils::urlToString(m_list.last())
0204         };
0205         QProcess::startDetached("kdiff3", args);
0206     }
0207 }
0208 
0209 void KDiff3FileItemAction::slotCompareThreeFiles()
0210 {
0211     if(m_list.count() == 3)
0212     {
0213         QStringList args = {
0214             Utils::urlToString(m_list.at(0)),
0215             Utils::urlToString(m_list.at(1)),
0216             Utils::urlToString(m_list.at(2))
0217         };
0218         QProcess::startDetached("kdiff3", args);
0219     }
0220 }
0221 
0222 void KDiff3FileItemAction::slotMergeWith()
0223 {
0224     if(m_list.count() > 0 && s_pHistory && !s_pHistory->empty())
0225     {
0226         QStringList args = {
0227             s_pHistory->first(),
0228             Utils::urlToString(m_list.first()),
0229             ("-o" + Utils::urlToString(m_list.first()))
0230         };
0231         QProcess::startDetached("kdiff3", args);
0232     }
0233 }
0234 
0235 void KDiff3FileItemAction::slotMergeThreeWay()
0236 {
0237     if(m_list.count() > 0 && s_pHistory && s_pHistory->count() >= 2)
0238     {
0239         QStringList args = {
0240             s_pHistory->at(1),
0241             s_pHistory->at(0),
0242             Utils::urlToString(m_list.first()),
0243             ("-o" + Utils::urlToString(m_list.first()))
0244         };
0245         QProcess::startDetached("kdiff3", args);
0246     }
0247 }
0248 
0249 void KDiff3FileItemAction::slotSaveForLater()
0250 {
0251     if(!m_list.isEmpty() && s_pHistory)
0252     {
0253         while(s_pHistory->count() >= 10)
0254         {
0255             s_pHistory->removeLast();
0256         }
0257         const QString file = Utils::urlToString(m_list.first());
0258         s_pHistory->removeAll(file);
0259         s_pHistory->prepend(file);
0260     }
0261 }
0262 
0263 void KDiff3FileItemAction::slotClearList()
0264 {
0265     if(s_pHistory)
0266     {
0267         s_pHistory->clear();
0268     }
0269 }
0270 
0271 void KDiff3FileItemAction::slotAbout()
0272 {
0273     QString s = i18n("KDiff3 File Item Action Plugin: Copyright (C) 2011 Joachim Eibl\n");
0274     s += i18n("Using the context menu extension:\n"
0275               "For simple comparison of two selected files choose \"Compare\".\n"
0276               "If the other file is somewhere else \"Save\" the first file for later. "
0277               "It will appear in the \"Compare with...\" submenu. "
0278               "Then use \"Compare With\" on the second file.\n"
0279               "For a 3-way merge first \"Save\" the base file, then the branch to merge and "
0280               "choose \"3-way merge with base\" on the other branch which will be used as destination.\n"
0281               "Same also applies to folder comparison and merge.");
0282     KMessageBox::information(m_pParentWidget, s, i18n("About KDiff3 File Item Action Plugin"));
0283 }