File indexing completed on 2024-04-28 13:37:56
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 }