Took all code from TortoiseMerge and made it compile again
[TortoiseGit.git] / src / TortoiseMerge / TortoiseMerge.cpp
blob387fb1f0d06fe2a99aa79e85fa79702ffb9dfa2d
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006-2012 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include <dlgs.h>
21 #include "TortoiseMerge.h"
22 #include "MainFrm.h"
23 #include "AboutDlg.h"
24 #include "CmdLineParser.h"
25 #include "version.h"
26 #include "AppUtils.h"
27 #include "PathUtils.h"
28 #include "BrowseFolder.h"
29 #include "DirFileEnum.h"
30 #include "SelectFileFilter.h"
31 #include "FileDlgEventHandler.h"
32 #include "TempFile.h"
33 #include "TaskbarUUID.h"
35 #ifdef _DEBUG
36 #define new DEBUG_NEW
37 #endif
39 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
41 BEGIN_MESSAGE_MAP(CTortoiseMergeApp, CWinAppEx)
42 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
43 END_MESSAGE_MAP()
45 class PatchOpenDlgEventHandler : public CFileDlgEventHandler
47 public:
48 PatchOpenDlgEventHandler() {}
49 ~PatchOpenDlgEventHandler() {}
51 virtual STDMETHODIMP OnButtonClicked(IFileDialogCustomize* pfdc, DWORD dwIDCtl)
53 if (dwIDCtl == 101)
55 CComQIPtr<IFileOpenDialog> pDlg = pfdc;
56 if (pDlg)
58 pDlg->Close(S_OK);
61 return S_OK;
66 CTortoiseMergeApp::CTortoiseMergeApp()
67 : m_nAppLook(0)
69 EnableHtmlHelp();
70 m_bHiColorIcons = TRUE;
73 // The one and only CTortoiseMergeApp object
74 CTortoiseMergeApp theApp;
75 CString sOrigCWD;
76 CCrashReportTGit g_crasher(L"TortoiseGitMerge " _T(APP_X64_STRING));
78 CString g_sGroupingUUID;
80 // CTortoiseMergeApp initialization
81 BOOL CTortoiseMergeApp::InitInstance()
83 SetDllDirectory(L"");
84 SetTaskIDPerUUID();
85 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
88 DWORD len = GetCurrentDirectory(0, NULL);
89 if (len)
91 std::unique_ptr<TCHAR[]> originalCurrentDirectory(new TCHAR[len]);
92 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
94 sOrigCWD = originalCurrentDirectory.get();
95 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
100 //set the resource dll for the required language
101 CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033);
102 long langId = loc;
103 CString langDll;
104 HINSTANCE hInst = NULL;
107 langDll.Format(_T("%sLanguages\\TortoiseMerge%d.dll"), (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId);
109 hInst = LoadLibrary(langDll);
110 CString sVer = _T(STRPRODUCTVER);
111 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
112 if (sFileVer.Compare(sVer)!=0)
114 FreeLibrary(hInst);
115 hInst = NULL;
117 if (hInst != NULL)
118 AfxSetResourceHandle(hInst);
119 else
121 DWORD lid = SUBLANGID(langId);
122 lid--;
123 if (lid > 0)
125 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
127 else
128 langId = 0;
130 } while ((hInst == NULL) && (langId != 0));
131 TCHAR buf[6];
132 _tcscpy_s(buf, _T("en"));
133 langId = loc;
134 CString sHelppath = CPathUtils::GetAppDirectory() + _T("TortoiseMerge_en.chm");
135 free((void*)m_pszHelpFilePath);
136 m_pszHelpFilePath=_tcsdup(sHelppath);
137 sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseMerge_en.chm");
140 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf));
141 CString sLang = _T("_");
142 sLang += buf;
143 sHelppath.Replace(_T("_en"), sLang);
144 if (PathFileExists(sHelppath))
146 free((void*)m_pszHelpFilePath);
147 m_pszHelpFilePath=_tcsdup(sHelppath);
148 break;
150 sHelppath.Replace(sLang, _T("_en"));
151 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf));
152 sLang += _T("_");
153 sLang += buf;
154 sHelppath.Replace(_T("_en"), sLang);
155 if (PathFileExists(sHelppath))
157 free((void*)m_pszHelpFilePath);
158 m_pszHelpFilePath=_tcsdup(sHelppath);
159 break;
161 sHelppath.Replace(sLang, _T("_en"));
163 DWORD lid = SUBLANGID(langId);
164 lid--;
165 if (lid > 0)
167 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
169 else
170 langId = 0;
171 } while (langId);
172 setlocale(LC_ALL, "");
173 // We need to explicitly set the thread locale to the system default one to avoid possible problems with saving files in its original codepage
174 // The problems occures when the language of OS differs from the regional settings
175 // See the details here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887
176 SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
178 // InitCommonControls() is required on Windows XP if an application
179 // manifest specifies use of ComCtl32.dll version 6 or later to enable
180 // visual styles. Otherwise, any window creation will fail.
181 InitCommonControls();
183 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
184 CMFCButton::EnableWindowsTheming();
185 EnableTaskbarInteraction(FALSE);
187 // Initialize all Managers for usage. They are automatically constructed
188 // if not yet present
189 InitContextMenuManager();
190 InitKeyboardManager();
191 InitTooltipManager ();
192 CMFCToolTipInfo params;
193 params.m_bVislManagerTheme = TRUE;
195 GetTooltipManager ()->SetTooltipParams (
196 AFX_TOOLTIP_TYPE_ALL,
197 RUNTIME_CLASS (CMFCToolTipCtrl),
198 &params);
200 CCmdLineParser parser = CCmdLineParser(this->m_lpCmdLine);
202 g_sGroupingUUID = parser.GetVal(L"groupuuid");
204 if (parser.HasKey(_T("?")) || parser.HasKey(_T("help")))
206 CString sHelpText;
207 sHelpText.LoadString(IDS_COMMANDLINEHELP);
208 MessageBox(NULL, sHelpText, _T("TortoiseGitMerge"), MB_ICONINFORMATION);
209 return FALSE;
212 // Initialize OLE libraries
213 if (!AfxOleInit())
215 AfxMessageBox(IDP_OLE_INIT_FAILED);
216 return FALSE;
218 AfxEnableControlContainer();
219 // Standard initialization
220 // If you are not using these features and wish to reduce the size
221 // of your final executable, you should remove from the following
222 // the specific initialization routines you do not need
223 // Change the registry key under which our settings are stored
224 SetRegistryKey(_T("TortoiseGitMerge"));
226 if (CRegDWORD(_T("Software\\TortoiseGitMerge\\Debug"), FALSE)==TRUE)
227 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
229 // To create the main window, this code creates a new frame window
230 // object and then sets it as the application's main window object
231 CMainFrame* pFrame = new CMainFrame;
232 if (pFrame == NULL)
233 return FALSE;
234 m_pMainWnd = pFrame;
236 // create and load the frame with its resources
237 if (!pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL))
238 return FALSE;
240 // Fill in the command line options
241 pFrame->m_Data.m_baseFile.SetFileName(parser.GetVal(_T("base")));
242 pFrame->m_Data.m_baseFile.SetDescriptiveName(parser.GetVal(_T("basename")));
243 pFrame->m_Data.m_theirFile.SetFileName(parser.GetVal(_T("theirs")));
244 pFrame->m_Data.m_theirFile.SetDescriptiveName(parser.GetVal(_T("theirsname")));
245 pFrame->m_Data.m_yourFile.SetFileName(parser.GetVal(_T("mine")));
246 pFrame->m_Data.m_yourFile.SetDescriptiveName(parser.GetVal(_T("minename")));
247 pFrame->m_Data.m_mergedFile.SetFileName(parser.GetVal(_T("merged")));
248 pFrame->m_Data.m_mergedFile.SetDescriptiveName(parser.GetVal(_T("mergedname")));
249 pFrame->m_Data.m_sPatchPath = parser.HasVal(_T("patchpath")) ? parser.GetVal(_T("patchpath")) : _T("");
250 pFrame->m_Data.m_sPatchPath.Replace('/', '\\');
251 if (parser.HasKey(_T("patchoriginal")))
252 pFrame->m_Data.m_sPatchOriginal = parser.GetVal(_T("patchoriginal"));
253 if (parser.HasKey(_T("patchpatched")))
254 pFrame->m_Data.m_sPatchPatched = parser.GetVal(_T("patchpatched"));
255 pFrame->m_Data.m_sDiffFile = parser.GetVal(_T("diff"));
256 pFrame->m_Data.m_sDiffFile.Replace('/', '\\');
257 if (parser.HasKey(_T("oneway")))
258 pFrame->m_bOneWay = TRUE;
259 if (parser.HasKey(_T("diff")))
260 pFrame->m_bOneWay = FALSE;
261 if (parser.HasKey(_T("reversedpatch")))
262 pFrame->m_bReversedPatch = TRUE;
263 if (parser.HasKey(_T("saverequired")))
264 pFrame->m_bSaveRequired = true;
265 if (pFrame->m_Data.IsBaseFileInUse() && !pFrame->m_Data.IsYourFileInUse() && pFrame->m_Data.IsTheirFileInUse())
267 pFrame->m_Data.m_yourFile.TransferDetailsFrom(pFrame->m_Data.m_theirFile);
270 if ((!parser.HasKey(_T("patchpath")))&&(parser.HasVal(_T("diff"))))
272 // a patchfile was given, but not folder path to apply the patch to
273 // If the patchfile is located inside a working copy, then use the parent directory
274 // of the patchfile as the target directory, otherwise ask the user for a path.
275 if (parser.HasKey(_T("wc")))
276 pFrame->m_Data.m_sPatchPath = pFrame->m_Data.m_sDiffFile.Left(pFrame->m_Data.m_sDiffFile.ReverseFind('\\'));
277 else
279 CBrowseFolder fbrowser;
280 fbrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
281 if (fbrowser.Show(NULL, pFrame->m_Data.m_sPatchPath)==CBrowseFolder::CANCEL)
282 return FALSE;
286 if ((parser.HasKey(_T("patchpath")))&&(!parser.HasVal(_T("diff"))))
288 // A path was given for applying a patchfile, but
289 // the patchfile itself was not.
290 // So ask the user for that patchfile
292 HRESULT hr;
293 // Create a new common save file dialog
294 CComPtr<IFileOpenDialog> pfd = NULL;
295 hr = pfd.CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER);
296 if (SUCCEEDED(hr))
298 // Set the dialog options
299 DWORD dwOptions;
300 if (SUCCEEDED(hr = pfd->GetOptions(&dwOptions)))
302 hr = pfd->SetOptions(dwOptions | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
305 // Set a title
306 if (SUCCEEDED(hr))
308 CString temp;
309 temp.LoadString(IDS_OPENDIFFFILETITLE);
310 pfd->SetTitle(temp);
312 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
313 hr = pfd->SetFileTypes(fileFilter.GetCount(), fileFilter);
314 bool bAdvised = false;
315 DWORD dwCookie = 0;
316 CComObjectStackEx<PatchOpenDlgEventHandler> cbk;
317 CComQIPtr<IFileDialogEvents> pEvents = cbk.GetUnknown();
320 CComPtr<IFileDialogCustomize> pfdCustomize;
321 hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdCustomize));
322 if (SUCCEEDED(hr))
324 // check if there's a unified diff on the clipboard and
325 // add a button to the fileopen dialog if there is.
326 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
327 if ((cFormat)&&(OpenClipboard(NULL)))
329 HGLOBAL hglb = GetClipboardData(cFormat);
330 if (hglb)
332 pfdCustomize->AddPushButton(101, CString(MAKEINTRESOURCE(IDS_PATCH_COPYFROMCLIPBOARD)));
333 hr = pfd->Advise(pEvents, &dwCookie);
334 bAdvised = SUCCEEDED(hr);
336 CloseClipboard();
341 // Show the save file dialog
342 if (SUCCEEDED(hr) && SUCCEEDED(hr = pfd->Show(pFrame->m_hWnd)))
344 // Get the selection from the user
345 CComPtr<IShellItem> psiResult = NULL;
346 hr = pfd->GetResult(&psiResult);
347 if (bAdvised)
348 pfd->Unadvise(dwCookie);
349 if (SUCCEEDED(hr))
351 PWSTR pszPath = NULL;
352 hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
353 if (SUCCEEDED(hr))
355 pFrame->m_Data.m_sDiffFile = pszPath;
356 CoTaskMemFree(pszPath);
359 else
361 // no result, which means we closed the dialog in our button handler
362 std::wstring sTempFile;
363 if (TrySavePatchFromClipboard(sTempFile))
364 pFrame->m_Data.m_sDiffFile = sTempFile.c_str();
367 else
369 if (bAdvised)
370 pfd->Unadvise(dwCookie);
371 return FALSE;
374 else
376 OPENFILENAME ofn = {0}; // common dialog box structure
377 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
378 // Initialize OPENFILENAME
379 ofn.lStructSize = sizeof(OPENFILENAME);
380 ofn.hwndOwner = pFrame->m_hWnd;
381 ofn.lpstrFile = szFile;
382 ofn.nMaxFile = _countof(szFile);
383 CString temp;
384 temp.LoadString(IDS_OPENDIFFFILETITLE);
385 if (temp.IsEmpty())
386 ofn.lpstrTitle = NULL;
387 else
388 ofn.lpstrTitle = temp;
390 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
391 if( HasClipboardPatch() ) {
392 ofn.Flags |= ( OFN_ENABLETEMPLATE | OFN_ENABLEHOOK );
393 ofn.hInstance = AfxGetResourceHandle();
394 ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PATCH_FILE_OPEN_CUSTOM);
395 ofn.lpfnHook = CreatePatchFileOpenHook;
398 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
399 ofn.lpstrFilter = fileFilter;
400 ofn.nFilterIndex = 1;
402 // Display the Open dialog box.
403 if (GetOpenFileName(&ofn)==FALSE)
405 return FALSE;
407 pFrame->m_Data.m_sDiffFile = ofn.lpstrFile;
411 if ( pFrame->m_Data.m_baseFile.GetFilename().IsEmpty() && pFrame->m_Data.m_yourFile.GetFilename().IsEmpty() )
413 int nArgs;
414 LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
415 if( NULL == szArglist )
417 TRACE("CommandLineToArgvW failed\n");
419 else
421 if ( nArgs==3 || nArgs==4 )
423 // Four parameters:
424 // [0]: Program name
425 // [1]: BASE file
426 // [2]: my file
427 // [3]: THEIR file (optional)
428 // This is the same format CAppUtils::StartExtDiff
429 // uses if %base and %mine are not set and most
430 // other diff tools use it too.
431 if ( PathFileExists(szArglist[1]) && PathFileExists(szArglist[2]) )
433 pFrame->m_Data.m_baseFile.SetFileName(szArglist[1]);
434 pFrame->m_Data.m_yourFile.SetFileName(szArglist[2]);
435 if ( nArgs == 4 && PathFileExists(szArglist[3]) )
437 pFrame->m_Data.m_theirFile.SetFileName(szArglist[3]);
443 // Free memory allocated for CommandLineToArgvW arguments.
444 LocalFree(szArglist);
447 pFrame->m_bReadOnly = !!parser.HasKey(_T("readonly"));
448 if (GetFileAttributes(pFrame->m_Data.m_yourFile.GetFilename()) & FILE_ATTRIBUTE_READONLY)
449 pFrame->m_bReadOnly = true;
450 pFrame->m_bBlame = !!parser.HasKey(_T("blame"));
451 // diffing a blame means no editing!
452 if (pFrame->m_bBlame)
453 pFrame->m_bReadOnly = true;
455 pFrame->SetWindowTitle();
457 if (parser.HasKey(_T("createunifieddiff")))
459 // user requested to create a unified diff file
460 CString origFile = parser.GetVal(_T("origfile"));
461 CString modifiedFile = parser.GetVal(_T("modifiedfile"));
462 if (!origFile.IsEmpty() && !modifiedFile.IsEmpty())
464 CString outfile = parser.GetVal(_T("outfile"));
465 if (outfile.IsEmpty())
467 CCommonAppUtils::FileOpenSave(outfile, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, NULL);
469 if (!outfile.IsEmpty())
471 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outfile, false);
472 return FALSE;
477 #if 0
478 pFrame->resolveMsgWnd = parser.HasVal(L"resolvemsghwnd") ? (HWND)parser.GetLongLongVal(L"resolvemsghwnd") : 0;
479 pFrame->resolveMsgWParam = parser.HasVal(L"resolvemsgwparam") ? (WPARAM)parser.GetLongLongVal(L"resolvemsgwparam") : 0;
480 pFrame->resolveMsgLParam = parser.HasVal(L"resolvemsglparam") ? (LPARAM)parser.GetLongLongVal(L"resolvemsglparam") : 0;
481 #endif
483 // The one and only window has been initialized, so show and update it
484 pFrame->ActivateFrame();
485 pFrame->ShowWindow(SW_SHOW);
486 pFrame->UpdateWindow();
487 pFrame->ShowDiffBar(!pFrame->m_bOneWay);
488 if (!pFrame->m_Data.IsBaseFileInUse() && pFrame->m_Data.m_sPatchPath.IsEmpty() && pFrame->m_Data.m_sDiffFile.IsEmpty())
490 pFrame->OnFileOpen();
491 return TRUE;
494 int line = -2;
495 if (parser.HasVal(_T("line")))
497 line = parser.GetLongVal(_T("line"));
498 line--; // we need the index
501 return pFrame->LoadViews(line);
504 // CTortoiseMergeApp message handlers
506 void CTortoiseMergeApp::OnAppAbout()
508 CAboutDlg aboutDlg;
509 aboutDlg.DoModal();
512 UINT_PTR CALLBACK
513 CTortoiseMergeApp::CreatePatchFileOpenHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM /*lParam*/)
515 if(uiMsg == WM_COMMAND && LOWORD(wParam) == IDC_PATCH_TO_CLIPBOARD)
517 HWND hFileDialog = GetParent(hDlg);
519 // if there's a patchfile in the clipboard, we save it
520 // to a temporary file and tell TortoiseMerge to use that one
521 std::wstring sTempFile;
522 if (TrySavePatchFromClipboard(sTempFile))
524 CommDlg_OpenSave_SetControlText(hFileDialog, edt1, sTempFile.c_str());
525 PostMessage(hFileDialog, WM_COMMAND, MAKEWPARAM(IDOK, BM_CLICK), (LPARAM)(GetDlgItem(hDlg, IDOK)));
528 return 0;
531 int CTortoiseMergeApp::ExitInstance()
533 // Look for temporary files left around by TortoiseMerge and
534 // remove them. But only delete 'old' files
535 CTempFiles::DeleteOldTempFiles(_T("*tsm*.*"));
537 return CWinAppEx::ExitInstance();
540 bool CTortoiseMergeApp::HasClipboardPatch()
542 // check if there's a patchfile in the clipboard
543 const UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
544 if (cFormat == 0)
545 return false;
547 if (OpenClipboard(NULL) == 0)
548 return false;
550 bool containsPatch = false;
551 UINT enumFormat = 0;
554 if (enumFormat == cFormat)
556 containsPatch = true; // yes, there's a patchfile in the clipboard
558 } while((enumFormat = EnumClipboardFormats(enumFormat))!=0);
559 CloseClipboard();
561 return containsPatch;
564 bool CTortoiseMergeApp::TrySavePatchFromClipboard(std::wstring& resultFile)
566 resultFile.clear();
568 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
569 if (cFormat == 0)
570 return false;
571 if (OpenClipboard(NULL) == 0)
572 return false;
574 HGLOBAL hglb = GetClipboardData(cFormat);
575 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
577 DWORD len = GetTempPath(0, NULL);
578 std::unique_ptr<TCHAR[]> path(new TCHAR[len+1]);
579 std::unique_ptr<TCHAR[]> tempF(new TCHAR[len+100]);
580 GetTempPath (len+1, path.get());
581 GetTempFileName (path.get(), _T("tsm"), 0, tempF.get());
582 std::wstring sTempFile = std::wstring(tempF.get());
584 FILE* outFile = 0;
585 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));
586 if (outFile != 0)
588 size_t patchlen = strlen(lpstr);
589 size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);
590 if (size == patchlen)
591 resultFile = sTempFile;
593 fclose(outFile);
595 GlobalUnlock(hglb);
596 CloseClipboard();
598 return !resultFile.empty();