Add lock for tgitcache and fix ignore entry miss
[TortoiseGit.git] / src / TortoiseMerge / TortoiseMerge.cpp
blob151883d27dd3d9026a733231ef8ff26c3de9347b
1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2006-2009 - 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"
31 #ifdef _DEBUG
32 #define new DEBUG_NEW
33 #endif
35 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
37 BEGIN_MESSAGE_MAP(CTortoiseMergeApp, CWinAppEx)
38 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
39 END_MESSAGE_MAP()
42 CTortoiseMergeApp::CTortoiseMergeApp()
44 EnableHtmlHelp();
45 m_bLoadUserToolbars = FALSE;
46 m_bSaveState = FALSE;
49 // The one and only CTortoiseMergeApp object
50 CTortoiseMergeApp theApp;
51 CCrashReport g_crasher("tortoisesvn@gmail.com", "Crash Report for TortoiseMerge " APP_X64_STRING " : " STRPRODUCTVER, TRUE);
53 // CTortoiseMergeApp initialization
54 BOOL CTortoiseMergeApp::InitInstance()
56 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
57 CMFCButton::EnableWindowsTheming();
58 //set the resource dll for the required language
59 CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseSVN\\LanguageID"), 1033);
60 long langId = loc;
61 CString langDll;
62 HINSTANCE hInst = NULL;
65 langDll.Format(_T("..\\Languages\\TortoiseMerge%d.dll"), langId);
67 hInst = LoadLibrary(langDll);
68 CString sVer = _T(STRPRODUCTVER);
69 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
70 if (sFileVer.Compare(sVer)!=0)
72 FreeLibrary(hInst);
73 hInst = NULL;
75 if (hInst != NULL)
76 AfxSetResourceHandle(hInst);
77 else
79 DWORD lid = SUBLANGID(langId);
80 lid--;
81 if (lid > 0)
83 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
85 else
86 langId = 0;
88 } while ((hInst == NULL) && (langId != 0));
89 TCHAR buf[6];
90 _tcscpy_s(buf, _T("en"));
91 langId = loc;
92 CString sHelppath;
93 sHelppath = this->m_pszHelpFilePath;
94 sHelppath = sHelppath.MakeLower();
95 sHelppath.Replace(_T(".chm"), _T("_en.chm"));
96 free((void*)m_pszHelpFilePath);
97 m_pszHelpFilePath=_tcsdup(sHelppath);
98 sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseMerge_en.chm");
101 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, sizeof(buf)/sizeof(TCHAR));
102 CString sLang = _T("_");
103 sLang += buf;
104 sHelppath.Replace(_T("_en"), sLang);
105 if (PathFileExists(sHelppath))
107 free((void*)m_pszHelpFilePath);
108 m_pszHelpFilePath=_tcsdup(sHelppath);
109 break;
111 sHelppath.Replace(sLang, _T("_en"));
112 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, sizeof(buf)/sizeof(TCHAR));
113 sLang += _T("_");
114 sLang += buf;
115 sHelppath.Replace(_T("_en"), sLang);
116 if (PathFileExists(sHelppath))
118 free((void*)m_pszHelpFilePath);
119 m_pszHelpFilePath=_tcsdup(sHelppath);
120 break;
122 sHelppath.Replace(sLang, _T("_en"));
124 DWORD lid = SUBLANGID(langId);
125 lid--;
126 if (lid > 0)
128 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
130 else
131 langId = 0;
132 } while (langId);
133 setlocale(LC_ALL, "");
134 // We need to explicitly set the thread locale to the system default one to avoid possible problems with saving files in its original codepage
135 // The problems occures when the language of OS differs from the regional settings
136 // See the details here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887
137 SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
139 // InitCommonControls() is required on Windows XP if an application
140 // manifest specifies use of ComCtl32.dll version 6 or later to enable
141 // visual styles. Otherwise, any window creation will fail.
142 InitCommonControls();
144 // Initialize all Managers for usage. They are automatically constructed
145 // if not yet present
146 InitContextMenuManager();
147 InitKeyboardManager();
149 CCmdLineParser parser = CCmdLineParser(this->m_lpCmdLine);
151 if (parser.HasKey(_T("?")) || parser.HasKey(_T("help")))
153 CString sHelpText;
154 sHelpText.LoadString(IDS_COMMANDLINEHELP);
155 MessageBox(NULL, sHelpText, _T("TortoiseMerge"), MB_ICONINFORMATION);
156 return FALSE;
159 // Initialize OLE libraries
160 if (!AfxOleInit())
162 AfxMessageBox(IDP_OLE_INIT_FAILED);
163 return FALSE;
165 AfxEnableControlContainer();
166 // Standard initialization
167 // If you are not using these features and wish to reduce the size
168 // of your final executable, you should remove from the following
169 // the specific initialization routines you do not need
170 // Change the registry key under which our settings are stored
171 SetRegistryKey(_T("TortoiseMerge"));
173 if (CRegDWORD(_T("Software\\TortoiseMerge\\Debug"), FALSE)==TRUE)
174 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
176 // To create the main window, this code creates a new frame window
177 // object and then sets it as the application's main window object
178 CMainFrame* pFrame = new CMainFrame;
179 if (pFrame == NULL)
180 return FALSE;
181 m_pMainWnd = pFrame;
183 // create and load the frame with its resources
184 pFrame->LoadFrame(IDR_MAINFRAME,
185 WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
186 NULL);
188 // Fill in the command line options
189 pFrame->m_Data.m_baseFile.SetFileName(parser.GetVal(_T("base")));
190 pFrame->m_Data.m_baseFile.SetDescriptiveName(parser.GetVal(_T("basename")));
191 pFrame->m_Data.m_theirFile.SetFileName(parser.GetVal(_T("theirs")));
192 pFrame->m_Data.m_theirFile.SetDescriptiveName(parser.GetVal(_T("theirsname")));
193 pFrame->m_Data.m_yourFile.SetFileName(parser.GetVal(_T("mine")));
194 pFrame->m_Data.m_yourFile.SetDescriptiveName(parser.GetVal(_T("minename")));
195 pFrame->m_Data.m_mergedFile.SetFileName(parser.GetVal(_T("merged")));
196 pFrame->m_Data.m_mergedFile.SetDescriptiveName(parser.GetVal(_T("mergedname")));
197 pFrame->m_Data.m_sPatchPath = parser.HasVal(_T("patchpath")) ? parser.GetVal(_T("patchpath")) : _T("");
198 pFrame->m_Data.m_sPatchPath.Replace('/', '\\');
199 if (parser.HasKey(_T("patchoriginal")))
200 pFrame->m_Data.m_sPatchOriginal = parser.GetVal(_T("patchoriginal"));
201 if (parser.HasKey(_T("patchpatched")))
202 pFrame->m_Data.m_sPatchPatched = parser.GetVal(_T("patchpatched"));
203 pFrame->m_Data.m_sDiffFile = parser.GetVal(_T("diff"));
204 pFrame->m_Data.m_sDiffFile.Replace('/', '\\');
205 if (parser.HasKey(_T("oneway")))
206 pFrame->m_bOneWay = TRUE;
207 if (parser.HasKey(_T("diff")))
208 pFrame->m_bOneWay = FALSE;
209 if (parser.HasKey(_T("reversedpatch")))
210 pFrame->m_bReversedPatch = TRUE;
211 if (pFrame->m_Data.IsBaseFileInUse() && !pFrame->m_Data.IsYourFileInUse() && pFrame->m_Data.IsTheirFileInUse())
213 pFrame->m_Data.m_yourFile.TransferDetailsFrom(pFrame->m_Data.m_theirFile);
216 if ((!parser.HasKey(_T("patchpath")))&&(parser.HasVal(_T("diff"))))
218 // a patchfile was given, but not folder path to apply the patch to
219 // If the patchfile is located inside a working copy, then use the parent directory
220 // of the patchfile as the target directory, otherwise ask the user for a path.
221 if (parser.HasKey(_T("wc")))
222 pFrame->m_Data.m_sPatchPath = pFrame->m_Data.m_sDiffFile.Left(pFrame->m_Data.m_sDiffFile.ReverseFind('\\'));
223 else
225 CBrowseFolder fbrowser;
226 fbrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
227 if (fbrowser.Show(NULL, pFrame->m_Data.m_sPatchPath)==CBrowseFolder::CANCEL)
228 return FALSE;
232 if ((parser.HasKey(_T("patchpath")))&&(!parser.HasVal(_T("diff"))))
234 // A path was given for applying a patchfile, but
235 // the patchfile itself was not.
236 // So ask the user for that patchfile
238 OPENFILENAME ofn = {0}; // common dialog box structure
239 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
240 // Initialize OPENFILENAME
241 ofn.lStructSize = sizeof(OPENFILENAME);
242 ofn.hwndOwner = pFrame->m_hWnd;
243 ofn.lpstrFile = szFile;
244 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
245 CString temp;
246 temp.LoadString(IDS_OPENDIFFFILETITLE);
247 if (temp.IsEmpty())
248 ofn.lpstrTitle = NULL;
249 else
250 ofn.lpstrTitle = temp;
252 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
253 // check if there's a patchfile in the clipboard
254 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
255 if (cFormat)
257 if (OpenClipboard(NULL))
259 UINT enumFormat = 0;
262 if (enumFormat == cFormat)
264 // yes, there's a patchfile in the clipboard
265 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER;
267 ofn.hInstance = AfxGetResourceHandle();
268 ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PATCH_FILE_OPEN_CUSTOM);
269 ofn.lpfnHook = CreatePatchFileOpenHook;
271 } while((enumFormat = EnumClipboardFormats(enumFormat))!=0);
272 CloseClipboard();
276 CString sFilter;
277 sFilter.LoadString(IDS_PATCHFILEFILTER);
278 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];
279 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);
280 // Replace '|' delimiters with '\0's
281 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
282 while (ptr != pszFilters)
284 if (*ptr == '|')
285 *ptr = '\0';
286 ptr--;
288 ofn.lpstrFilter = pszFilters;
289 ofn.nFilterIndex = 1;
291 // Display the Open dialog box.
292 CString tempfile;
293 if (GetOpenFileName(&ofn)==FALSE)
295 delete [] pszFilters;
296 return FALSE;
298 delete [] pszFilters;
299 pFrame->m_Data.m_sDiffFile = ofn.lpstrFile;
302 if ( pFrame->m_Data.m_baseFile.GetFilename().IsEmpty() && pFrame->m_Data.m_yourFile.GetFilename().IsEmpty() )
304 LPWSTR *szArglist;
305 int nArgs;
307 szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
308 if( NULL == szArglist )
310 TRACE("CommandLineToArgvW failed\n");
312 else
314 if ( nArgs==3 || nArgs==4 )
316 // Four parameters:
317 // [0]: Program name
318 // [1]: BASE file
319 // [2]: my file
320 // [3]: THEIR file (optional)
321 // This is the same format CAppUtils::StartExtDiff
322 // uses if %base and %mine are not set and most
323 // other diff tools use it too.
324 if ( PathFileExists(szArglist[1]) && PathFileExists(szArglist[2]) )
326 pFrame->m_Data.m_baseFile.SetFileName(szArglist[1]);
327 pFrame->m_Data.m_yourFile.SetFileName(szArglist[2]);
328 if ( nArgs == 4 && PathFileExists(szArglist[3]) )
330 pFrame->m_Data.m_theirFile.SetFileName(szArglist[3]);
336 // Free memory allocated for CommandLineToArgvW arguments.
337 LocalFree(szArglist);
340 pFrame->m_bReadOnly = !!parser.HasKey(_T("readonly"));
341 if (GetFileAttributes(pFrame->m_Data.m_yourFile.GetFilename()) & FILE_ATTRIBUTE_READONLY)
342 pFrame->m_bReadOnly = true;
343 pFrame->m_bBlame = !!parser.HasKey(_T("blame"));
344 // diffing a blame means no editing!
345 if (pFrame->m_bBlame)
346 pFrame->m_bReadOnly = true;
348 // try to find a suitable window title
349 CString sYour = pFrame->m_Data.m_yourFile.GetDescriptiveName();
350 if (sYour.Find(_T(" - "))>=0)
351 sYour = sYour.Left(sYour.Find(_T(" - ")));
352 if (sYour.Find(_T(" : "))>=0)
353 sYour = sYour.Left(sYour.Find(_T(" : ")));
354 CString sTheir = pFrame->m_Data.m_theirFile.GetDescriptiveName();
355 if (sTheir.Find(_T(" - "))>=0)
356 sTheir = sTheir.Left(sTheir.Find(_T(" - ")));
357 if (sTheir.Find(_T(" : "))>=0)
358 sTheir = sTheir.Left(sTheir.Find(_T(" : ")));
360 if (!sYour.IsEmpty() && !sTheir.IsEmpty())
362 if (sYour.CompareNoCase(sTheir)==0)
363 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
364 else if ((sYour.GetLength() < 10) &&
365 (sTheir.GetLength() < 10))
366 pFrame->SetWindowText(sYour + _T(" - ") + sTheir + _T(" - TortoiseMerge"));
367 else
369 // we have two very long descriptive texts here, which
370 // means we have to find a way to use them as a window
371 // title in a shorter way.
372 // for simplicity, we just use the one from "yourfile"
373 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
376 else if (!sYour.IsEmpty())
377 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
378 else if (!sTheir.IsEmpty())
379 pFrame->SetWindowText(sTheir + _T(" - TortoiseMerge"));
381 if (parser.HasKey(_T("createunifieddiff")))
383 // user requested to create a unified diff file
384 CString origFile = parser.GetVal(_T("origfile"));
385 CString modifiedFile = parser.GetVal(_T("modifiedfile"));
386 if (!origFile.IsEmpty() && !modifiedFile.IsEmpty())
388 CString outfile = parser.GetVal(_T("outfile"));
389 if (outfile.IsEmpty())
391 OPENFILENAME ofn = {0}; // common dialog box structure
392 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
393 ofn.lStructSize = sizeof(OPENFILENAME);
394 ofn.lpstrFile = szFile;
395 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
396 CString temp;
397 temp.LoadString(IDS_SAVEASTITLE);
398 if (!temp.IsEmpty())
399 ofn.lpstrTitle = temp;
400 ofn.Flags = OFN_OVERWRITEPROMPT;
401 CString sFilter;
402 sFilter.LoadString(IDS_COMMONFILEFILTER);
403 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];
404 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);
405 // Replace '|' delimiters with '\0's
406 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
407 while (ptr != pszFilters)
409 if (*ptr == '|')
410 *ptr = '\0';
411 ptr--;
413 ofn.lpstrFilter = pszFilters;
414 ofn.nFilterIndex = 1;
416 // Display the Save dialog box.
417 CString sFile;
418 if (GetSaveFileName(&ofn)==TRUE)
420 outfile = CString(ofn.lpstrFile);
422 delete [] pszFilters;
424 if (!outfile.IsEmpty())
426 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outfile, false);
427 return FALSE;
431 // The one and only window has been initialized, so show and update it
432 pFrame->ActivateFrame();
433 pFrame->ShowWindow(SW_SHOW);
434 pFrame->UpdateWindow();
435 pFrame->ShowDiffBar(!pFrame->m_bOneWay);
436 if (!pFrame->m_Data.IsBaseFileInUse() && pFrame->m_Data.m_sPatchPath.IsEmpty() && pFrame->m_Data.m_sDiffFile.IsEmpty())
438 pFrame->OnFileOpen();
439 return TRUE;
442 return pFrame->LoadViews();
445 // CTortoiseMergeApp message handlers
447 void CTortoiseMergeApp::OnAppAbout()
449 CAboutDlg aboutDlg;
450 aboutDlg.DoModal();
453 UINT_PTR CALLBACK
454 CTortoiseMergeApp::CreatePatchFileOpenHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM /*lParam*/)
456 if(uiMsg == WM_COMMAND && LOWORD(wParam) == IDC_PATCH_TO_CLIPBOARD)
458 HWND hFileDialog = GetParent(hDlg);
460 // if there's a patchfile in the clipboard, we save it
461 // to a temporary file and tell TortoiseMerge to use that one
462 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
463 if ((cFormat)&&(OpenClipboard(NULL)))
465 HGLOBAL hglb = GetClipboardData(cFormat);
466 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
468 DWORD len = GetTempPath(0, NULL);
469 TCHAR * path = new TCHAR[len+1];
470 TCHAR * tempF = new TCHAR[len+100];
471 GetTempPath (len+1, path);
472 GetTempFileName (path, TEXT("tsm"), 0, tempF);
473 std::wstring sTempFile = std::wstring(tempF);
474 delete [] path;
475 delete [] tempF;
477 FILE * outFile;
478 size_t patchlen = strlen(lpstr);
479 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));
480 if(outFile)
482 size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);
483 if (size == patchlen)
485 CommDlg_OpenSave_SetControlText(hFileDialog, edt1, sTempFile.c_str());
486 PostMessage(hFileDialog, WM_COMMAND, MAKEWPARAM(IDOK, BM_CLICK), (LPARAM)(GetDlgItem(hDlg, IDOK)));
488 fclose(outFile);
490 GlobalUnlock(hglb);
491 CloseClipboard();
494 return 0;
497 int CTortoiseMergeApp::ExitInstance()
499 // Look for temporary files left around by TortoiseMerge and
500 // remove them. But only delete 'old' files
501 DWORD len = ::GetTempPath(0, NULL);
502 TCHAR * path = new TCHAR[len + 100];
503 len = ::GetTempPath (len+100, path);
504 if (len != 0)
506 CSimpleFileFind finder = CSimpleFileFind(path, _T("*tsm*.*"));
507 FILETIME systime_;
508 ::GetSystemTimeAsFileTime(&systime_);
509 __int64 systime = (((_int64)systime_.dwHighDateTime)<<32) | ((__int64)systime_.dwLowDateTime);
510 while (finder.FindNextFileNoDirectories())
512 CString filepath = finder.GetFilePath();
513 HANDLE hFile = ::CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
514 if (hFile != INVALID_HANDLE_VALUE)
516 FILETIME createtime_;
517 if (::GetFileTime(hFile, &createtime_, NULL, NULL))
519 ::CloseHandle(hFile);
520 __int64 createtime = (((_int64)createtime_.dwHighDateTime)<<32) | ((__int64)createtime_.dwLowDateTime);
521 if ((createtime + 864000000000) < systime) //only delete files older than a day
523 ::SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL);
524 ::DeleteFile(filepath);
527 else
528 ::CloseHandle(hFile);
532 delete[] path;
534 return CWinAppEx::ExitInstance();