File indexing completed on 2024-04-28 17:00:48
0001 /* 0002 SPDX-FileCopyrightText: 2003-2006, Sergey Zorin. All rights reserved. 0003 SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com 0004 SPDX-License-Identifier: BSD-2-Clause 0005 */ 0006 0007 #include "diff_ext.h" 0008 0009 #include <KLocalizedString> 0010 0011 #include <QString> 0012 0013 #include <assert.h> 0014 #include <stdio.h> 0015 #include <tchar.h> 0016 0017 #include <map> 0018 #include <vector> 0019 0020 DIFF_EXT::DIFF_EXT(): 0021 m_nrOfSelectedFiles(0), _ref_count(0L), 0022 m_recentFiles(SERVER::instance()->recent_files()) 0023 { 0024 LOG(); 0025 _resource = SERVER::instance()->handle(); 0026 0027 SERVER::instance()->lock(); 0028 } 0029 0030 DIFF_EXT::~DIFF_EXT() 0031 { 0032 LOG(); 0033 if(_resource != SERVER::instance()->handle()) 0034 { 0035 FreeLibrary(_resource); 0036 } 0037 0038 SERVER::instance()->release(); 0039 } 0040 0041 STDMETHODIMP 0042 DIFF_EXT::QueryInterface(REFIID refiid, void** ppv) 0043 { 0044 HRESULT ret = E_NOINTERFACE; 0045 *ppv = 0; 0046 0047 if(IsEqualIID(refiid, IID_IShellExtInit) || IsEqualIID(refiid, IID_IUnknown)) 0048 { 0049 *ppv = static_cast<IShellExtInit*>(this); 0050 } 0051 else if(IsEqualIID(refiid, IID_IContextMenu)) 0052 { 0053 *ppv = static_cast<IContextMenu*>(this); 0054 } 0055 0056 if(*ppv != 0) 0057 { 0058 AddRef(); 0059 0060 ret = NOERROR; 0061 } 0062 0063 return ret; 0064 } 0065 0066 STDMETHODIMP_(ULONG) 0067 DIFF_EXT::AddRef() 0068 { 0069 return InterlockedIncrement((LPLONG)&_ref_count); 0070 } 0071 0072 STDMETHODIMP_(ULONG) 0073 DIFF_EXT::Release() 0074 { 0075 ULONG ret = 0L; 0076 0077 if(InterlockedDecrement((LPLONG)&_ref_count) != 0) 0078 { 0079 ret = _ref_count; 0080 } 0081 else 0082 { 0083 delete this; 0084 } 0085 0086 return ret; 0087 } 0088 0089 STDMETHODIMP 0090 DIFF_EXT::Initialize(LPCITEMIDLIST /*folder not used*/, IDataObject* data, HKEY /*key not used*/) 0091 { 0092 LOG(); 0093 0094 FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 0095 STGMEDIUM medium; 0096 medium.tymed = TYMED_HGLOBAL; 0097 HRESULT ret = E_INVALIDARG; 0098 0099 if(data->GetData(&format, &medium) == S_OK) 0100 { 0101 HDROP drop = (HDROP)medium.hGlobal; 0102 m_nrOfSelectedFiles = DragQueryFile(drop, 0xFFFFFFFF, 0, 0); 0103 0104 TCHAR tmp[MAX_PATH]; 0105 0106 if(m_nrOfSelectedFiles >= 1 && m_nrOfSelectedFiles <= 3) 0107 { 0108 DragQueryFile(drop, 0, tmp, MAX_PATH); 0109 _file_name1 = tmp; 0110 0111 if(m_nrOfSelectedFiles >= 2) 0112 { 0113 DragQueryFile(drop, 1, tmp, MAX_PATH); 0114 _file_name2 = tmp; 0115 } 0116 0117 if(m_nrOfSelectedFiles == 3) 0118 { 0119 DragQueryFile(drop, 2, tmp, MAX_PATH); 0120 _file_name3 = tmp; 0121 } 0122 0123 ret = S_OK; 0124 } 0125 } 0126 else 0127 { 0128 SYSERRORLOG(TEXT("GetData")); 0129 } 0130 0131 return ret; 0132 } 0133 0134 static int insertMenuItemHelper(HMENU menu, UINT id, UINT position, const tstring& text, 0135 UINT fState = MFS_ENABLED, HMENU hSubMenu = 0) 0136 { 0137 MENUITEMINFO item_info; 0138 ZeroMemory(&item_info, sizeof(item_info)); 0139 item_info.cbSize = sizeof(MENUITEMINFO); 0140 item_info.wID = id; 0141 if(text.empty()) 0142 { // Separator 0143 item_info.fMask = MIIM_TYPE; 0144 item_info.fType = MFT_SEPARATOR; 0145 item_info.dwTypeData = 0; 0146 } 0147 else 0148 { 0149 item_info.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | (hSubMenu != 0 ? MIIM_SUBMENU : 0); 0150 item_info.fType = MFT_STRING; 0151 item_info.fState = fState; 0152 item_info.dwTypeData = (LPTSTR)text.c_str(); 0153 item_info.hSubMenu = hSubMenu; 0154 } 0155 if(0 == InsertMenuItem(menu, position, TRUE, &item_info)) 0156 SYSERRORLOG(TEXT("InsertMenuItem")); 0157 return id; 0158 } 0159 0160 STDMETHODIMP 0161 DIFF_EXT::QueryContextMenu(HMENU menu, UINT position, UINT first_cmd, UINT /*last_cmd not used*/, UINT flags) 0162 { 0163 LOG(); 0164 0165 SERVER::instance()->recent_files(); // updates recent files list (reads from registry) 0166 0167 m_id_Diff = UINT(-1); 0168 m_id_DiffWith = UINT(-1); 0169 m_id_DiffLater = UINT(-1); 0170 m_id_MergeWith = UINT(-1); 0171 m_id_Merge3 = UINT(-1); 0172 m_id_Diff3 = UINT(-1); 0173 m_id_DiffWith_Base = UINT(-1); 0174 m_id_ClearList = UINT(-1); 0175 m_id_About = UINT(-1); 0176 0177 HRESULT ret = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); 0178 0179 if(!(flags & CMF_DEFAULTONLY)) 0180 { 0181 /* Menu structure: 0182 KDiff3 -> (1 File selected): Save 'selection' for later comparison (push onto history stack) 0183 Compare 'selection' with first file on history stack. 0184 Compare 'selection' with -> choice from history stack 0185 Merge 'selection' with first file on history stack. 0186 Merge 'selection' with last two files on history stack. 0187 (2 Files selected): Compare 's1' with 's2' 0188 Merge 's1' with 's2' 0189 (3 Files selected): Compare 's1', 's2' and 's3' 0190 */ 0191 HMENU subMenu = CreateMenu(); 0192 0193 UINT id = first_cmd; 0194 m_id_FirstCmd = first_cmd; 0195 0196 insertMenuItemHelper(menu, id++, position++, TEXT("")); // begin separator 0197 0198 tstring menuString; 0199 UINT pos2 = 0; 0200 if(m_nrOfSelectedFiles == 1) 0201 { 0202 size_t nrOfRecentFiles = m_recentFiles.size(); 0203 tstring menuStringCompare; 0204 tstring menuStringMerge; 0205 tstring firstFileName; 0206 if(nrOfRecentFiles >= 1) 0207 { 0208 firstFileName = TEXT("'") + cut_to_length(m_recentFiles.front()) + TEXT("'"); 0209 } 0210 0211 menuStringCompare = fromQString(i18nc("Contexualmenu option", "Compare with %1", toQString(firstFileName))); 0212 menuStringMerge = fromQString(i18nc("Contexualmenu option", "Merge with %1", toQString(firstFileName))); 0213 0214 m_id_DiffWith = insertMenuItemHelper(subMenu, id++, pos2++, menuStringCompare, nrOfRecentFiles >= 1 ? MFS_ENABLED : MFS_DISABLED); 0215 m_id_MergeWith = insertMenuItemHelper(subMenu, id++, pos2++, menuStringMerge, nrOfRecentFiles >= 1 ? MFS_ENABLED : MFS_DISABLED); 0216 0217 m_id_Merge3 = insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "3-way merge with base")), 0218 nrOfRecentFiles >= 2 ? MFS_ENABLED : MFS_DISABLED); 0219 0220 menuString = fromQString(i18nc("Contexualmenu option", "Save '%1' for later", toQString(_file_name1))); 0221 m_id_DiffLater = insertMenuItemHelper(subMenu, id++, pos2++, menuString); 0222 0223 HMENU file_list = CreateMenu(); 0224 std::list<tstring>::iterator i; 0225 m_id_DiffWith_Base = id; 0226 int n = 0; 0227 for(i = m_recentFiles.begin(); i != m_recentFiles.end(); ++i) 0228 { 0229 tstring s = cut_to_length(*i); 0230 insertMenuItemHelper(file_list, id++, n, s); 0231 ++n; 0232 } 0233 0234 insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "Compare with ...")), 0235 nrOfRecentFiles > 0 ? MFS_ENABLED : MFS_DISABLED, file_list); 0236 0237 m_id_ClearList = insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "Clear list")), nrOfRecentFiles >= 1 ? MFS_ENABLED : MFS_DISABLED); 0238 } 0239 else if(m_nrOfSelectedFiles == 2) 0240 { 0241 //= "Diff " + cut_to_length(_file_name1, 20)+" and "+cut_to_length(_file_name2, 20); 0242 m_id_Diff = insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "Compare"))); 0243 } 0244 else if(m_nrOfSelectedFiles == 3) 0245 { 0246 m_id_Diff3 = insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "3 way comparison"))); 0247 } 0248 else 0249 { 0250 // More than 3 files selected? 0251 } 0252 m_id_About = insertMenuItemHelper(subMenu, id++, pos2++, fromQString(i18nc("Contexualmenu option", "About Diff-Ext ..."))); 0253 0254 insertMenuItemHelper(menu, id++, position++, TEXT("KDiff3"), MFS_ENABLED, subMenu); 0255 0256 insertMenuItemHelper(menu, id++, position++, TEXT("")); // final separator 0257 0258 ret = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, id - first_cmd); 0259 } 0260 0261 return ret; 0262 } 0263 0264 STDMETHODIMP 0265 DIFF_EXT::InvokeCommand(LPCMINVOKECOMMANDINFO ici) 0266 { 0267 HRESULT ret = NOERROR; 0268 0269 _hwnd = ici->hwnd; 0270 0271 if(HIWORD(ici->lpVerb) == 0) 0272 { 0273 UINT id = m_id_FirstCmd + LOWORD(ici->lpVerb); 0274 if(id == m_id_Diff) 0275 { 0276 LOG(); 0277 diff(TEXT("\"") + _file_name1 + TEXT("\" \"") + _file_name2 + TEXT("\"")); 0278 } 0279 else if(id == m_id_Diff3) 0280 { 0281 LOG(); 0282 diff(TEXT("\"") + _file_name1 + TEXT("\" \"") + _file_name2 + TEXT("\" \"") + _file_name3 + TEXT("\"")); 0283 } 0284 else if(id == m_id_Merge3) 0285 { 0286 LOG(); 0287 std::list<tstring>::iterator iFrom = m_recentFiles.begin(); 0288 std::list<tstring>::iterator iBase = iFrom; 0289 ++iBase; 0290 diff(TEXT("-m \"") + *iBase + TEXT("\" \"") + *iFrom + TEXT("\" \"") + _file_name1 + TEXT("\"")); 0291 } 0292 else if(id == m_id_DiffWith) 0293 { 0294 LOG(); 0295 diff_with(0, false); 0296 } 0297 else if(id == m_id_MergeWith) 0298 { 0299 LOG(); 0300 diff_with(0, true); 0301 } 0302 else if(id == m_id_ClearList) 0303 { 0304 LOG(); 0305 m_recentFiles.clear(); 0306 SERVER::instance()->save_history(); 0307 } 0308 else if(id == m_id_DiffLater) 0309 { 0310 MESSAGELOG(TEXT("Diff Later: ") + _file_name1); 0311 m_recentFiles.remove(_file_name1); 0312 m_recentFiles.push_front(_file_name1); 0313 SERVER::instance()->save_history(); 0314 } 0315 else if(id >= m_id_DiffWith_Base && id < m_id_DiffWith_Base + m_recentFiles.size()) 0316 { 0317 LOG(); 0318 diff_with(id - m_id_DiffWith_Base, false); 0319 } 0320 else if(id == m_id_About) 0321 { 0322 LOG(); 0323 0324 MessageBox(_hwnd, (fromQString(i18n("Diff-Ext Copyright (c) 2003-2006, Sergey Zorin. All rights reserved.\n") + i18n("This software is distributable under the BSD-2-Clause license.\n") + i18n("Some extensions for KDiff3 (c) 2006-2013 by Joachim Eibl.\n") + i18n("Ported to Qt5/Kf5 by Michael Reeves\n") + i18n("Homepage for Diff-Ext: http://diff-ext.sourceforge.net\n"))).c_str(), fromQString(i18n("About Diff-Ext for KDiff3 (64 Bit)")).c_str(), MB_OK); 0325 } 0326 else 0327 { 0328 ret = E_INVALIDARG; 0329 TCHAR verb[80]; 0330 _sntprintf(verb, 79, TEXT("Command id: %d"), LOWORD(ici->lpVerb)); 0331 verb[79] = 0; 0332 ERRORLOG(verb); 0333 } 0334 } 0335 else 0336 { 0337 ret = E_INVALIDARG; 0338 } 0339 0340 return ret; 0341 } 0342 0343 STDMETHODIMP 0344 DIFF_EXT::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT*, LPSTR pszName, UINT cchMax) 0345 { 0346 HRESULT ret = NOERROR; 0347 0348 if(uFlags == GCS_HELPTEXT) 0349 { 0350 QString helpString; 0351 if(idCmd == m_id_Diff) 0352 { 0353 helpString = i18nc("Contexualmenu option", "Compare selected files"); 0354 } 0355 else if(idCmd == m_id_DiffWith) 0356 { 0357 if(!m_recentFiles.empty()) 0358 { 0359 helpString = i18nc("Contexualmenu option", "Compare '%1' with '%2'", toQString(_file_name1), toQString(m_recentFiles.front())); 0360 } 0361 } 0362 else if(idCmd == m_id_DiffLater) 0363 { 0364 helpString = i18nc("Contexualmenu option", "Save '%1' for later operation", toQString(_file_name1)); 0365 } 0366 else if((idCmd >= m_id_DiffWith_Base) && (idCmd < m_id_DiffWith_Base + m_recentFiles.size())) 0367 { 0368 if(!m_recentFiles.empty()) 0369 { 0370 unsigned long long num = idCmd - m_id_DiffWith_Base; 0371 std::list<tstring>::iterator i = m_recentFiles.begin(); 0372 for(unsigned long long j = 0; j < num && i != m_recentFiles.end(); j++) 0373 i++; 0374 0375 if(i != m_recentFiles.end()) 0376 { 0377 helpString = i18nc("Contexualmenu option", "Compare '%1' with '%2'", toQString(_file_name1), toQString(*i)); 0378 } 0379 } 0380 } 0381 lstrcpyn((LPTSTR)pszName, fromQString(helpString).c_str(), cchMax); 0382 } 0383 else 0384 { 0385 ret = E_INVALIDARG; 0386 } 0387 0388 return ret; 0389 } 0390 0391 void DIFF_EXT::diff(const tstring& arguments) 0392 { 0393 LOG(); 0394 STARTUPINFO si; 0395 PROCESS_INFORMATION pi; 0396 bool bError = true; 0397 tstring command = SERVER::instance()->getRegistryKeyString(TEXT(""), TEXT("diffcommand")); 0398 tstring commandLine = TEXT("\"") + command + TEXT("\" ") + arguments; 0399 if(!command.empty()) 0400 { 0401 ZeroMemory(&si, sizeof(si)); 0402 si.cb = sizeof(si); 0403 if(CreateProcess(command.c_str(), (LPTSTR)commandLine.c_str(), 0, 0, FALSE, 0, 0, 0, &si, &pi) == 0) 0404 { 0405 SYSERRORLOG(TEXT("CreateProcess") + command); 0406 } 0407 else 0408 { 0409 bError = false; 0410 CloseHandle(pi.hProcess); 0411 CloseHandle(pi.hThread); 0412 } 0413 } 0414 0415 if(bError) 0416 { 0417 tstring message = fromQString(i18n("Could not start KDiff3. Please rerun KDiff3 installation.")); 0418 message += TEXT("\n") + fromQString(i18n("Command")) + TEXT(": ") + command; 0419 message += TEXT("\n") + fromQString(i18n("CommandLine")) + TEXT(": ") + commandLine; 0420 MessageBox(_hwnd, message.c_str(), fromQString(i18n("Diff-Ext For KDiff3")).c_str(), MB_OK); 0421 } 0422 } 0423 0424 void DIFF_EXT::diff_with(unsigned int num, bool bMerge) 0425 { 0426 LOG(); 0427 std::list<tstring>::iterator i = m_recentFiles.begin(); 0428 for(unsigned int j = 0; j < num && i != m_recentFiles.end(); j++) 0429 { 0430 i++; 0431 } 0432 0433 if(i != m_recentFiles.end()) 0434 _file_name2 = *i; 0435 0436 diff((bMerge ? TEXT("-m \"") : TEXT("\"")) + _file_name2 + TEXT("\" \"") + _file_name1 + TEXT("\"")); 0437 } 0438 0439 tstring 0440 DIFF_EXT::cut_to_length(const tstring& in, size_t max_len) 0441 { 0442 tstring ret; 0443 if(in.length() > max_len) 0444 { 0445 ret = in.substr(0, (max_len - 3) / 2); 0446 ret += TEXT("..."); 0447 ret += in.substr(in.length() - (max_len - 3) / 2); 0448 } 0449 else 0450 { 0451 ret = in; 0452 } 0453 0454 return ret; 0455 }