renamed ITEMIS_SUBMODULE to ITEMIS_SUBMODULECONTAINER
[TortoiseGit.git] / src / TortoiseMerge / TortoiseMerge.cpp
blob22a5a958b4f72afd892b62e08c63e662f6e2afd1
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 SetDllDirectory(L"");
45 EnableHtmlHelp();
46 m_bLoadUserToolbars = FALSE;
47 m_bSaveState = FALSE;
50 // The one and only CTortoiseMergeApp object
51 CTortoiseMergeApp theApp;
52 CCrashReport g_crasher("tortoisesvn@gmail.com", "Crash Report for TortoiseMerge " APP_X64_STRING " : " STRPRODUCTVER, TRUE);
54 // CTortoiseMergeApp initialization
55 BOOL CTortoiseMergeApp::InitInstance()
57 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
58 CMFCButton::EnableWindowsTheming();
59 //set the resource dll for the required language
60 CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033);
61 long langId = loc;
62 CString langDll;
63 HINSTANCE hInst = NULL;
66 langDll.Format(_T("..\\Languages\\TortoiseMerge%d.dll"), langId);
68 hInst = LoadLibrary(langDll);
69 CString sVer = _T(STRPRODUCTVER);
70 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
71 if (sFileVer.Compare(sVer)!=0)
73 FreeLibrary(hInst);
74 hInst = NULL;
76 if (hInst != NULL)
77 AfxSetResourceHandle(hInst);
78 else
80 DWORD lid = SUBLANGID(langId);
81 lid--;
82 if (lid > 0)
84 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
86 else
87 langId = 0;
89 } while ((hInst == NULL) && (langId != 0));
90 TCHAR buf[6];
91 _tcscpy_s(buf, _T("en"));
92 langId = loc;
93 CString sHelppath;
94 sHelppath = this->m_pszHelpFilePath;
95 sHelppath = sHelppath.MakeLower();
96 sHelppath.Replace(_T(".chm"), _T("_en.chm"));
97 free((void*)m_pszHelpFilePath);
98 m_pszHelpFilePath=_tcsdup(sHelppath);
99 sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseMerge_en.chm");
102 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf));
103 CString sLang = _T("_");
104 sLang += buf;
105 sHelppath.Replace(_T("_en"), sLang);
106 if (PathFileExists(sHelppath))
108 free((void*)m_pszHelpFilePath);
109 m_pszHelpFilePath=_tcsdup(sHelppath);
110 break;
112 sHelppath.Replace(sLang, _T("_en"));
113 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf));
114 sLang += _T("_");
115 sLang += buf;
116 sHelppath.Replace(_T("_en"), sLang);
117 if (PathFileExists(sHelppath))
119 free((void*)m_pszHelpFilePath);
120 m_pszHelpFilePath=_tcsdup(sHelppath);
121 break;
123 sHelppath.Replace(sLang, _T("_en"));
125 DWORD lid = SUBLANGID(langId);
126 lid--;
127 if (lid > 0)
129 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
131 else
132 langId = 0;
133 } while (langId);
134 setlocale(LC_ALL, "");
135 // We need to explicitly set the thread locale to the system default one to avoid possible problems with saving files in its original codepage
136 // The problems occures when the language of OS differs from the regional settings
137 // See the details here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887
138 SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
140 // InitCommonControls() is required on Windows XP if an application
141 // manifest specifies use of ComCtl32.dll version 6 or later to enable
142 // visual styles. Otherwise, any window creation will fail.
143 InitCommonControls();
145 // Initialize all Managers for usage. They are automatically constructed
146 // if not yet present
147 InitContextMenuManager();
148 InitKeyboardManager();
150 CCmdLineParser parser = CCmdLineParser(this->m_lpCmdLine);
152 if (parser.HasKey(_T("?")) || parser.HasKey(_T("help")))
154 CString sHelpText;
155 sHelpText.LoadString(IDS_COMMANDLINEHELP);
156 MessageBox(NULL, sHelpText, _T("TortoiseMerge"), MB_ICONINFORMATION);
157 return FALSE;
160 // Initialize OLE libraries
161 if (!AfxOleInit())
163 AfxMessageBox(IDP_OLE_INIT_FAILED);
164 return FALSE;
166 AfxEnableControlContainer();
167 // Standard initialization
168 // If you are not using these features and wish to reduce the size
169 // of your final executable, you should remove from the following
170 // the specific initialization routines you do not need
171 // Change the registry key under which our settings are stored
172 SetRegistryKey(_T("TortoiseMerge"));
174 if (CRegDWORD(_T("Software\\TortoiseMerge\\Debug"), FALSE)==TRUE)
175 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
177 // To create the main window, this code creates a new frame window
178 // object and then sets it as the application's main window object
179 CMainFrame* pFrame = new CMainFrame;
180 if (pFrame == NULL)
181 return FALSE;
182 m_pMainWnd = pFrame;
184 // create and load the frame with its resources
185 pFrame->LoadFrame(IDR_MAINFRAME,
186 WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
187 NULL);
189 // Fill in the command line options
190 pFrame->m_Data.m_baseFile.SetFileName(parser.GetVal(_T("base")));
191 pFrame->m_Data.m_baseFile.SetDescriptiveName(parser.GetVal(_T("basename")));
192 pFrame->m_Data.m_theirFile.SetFileName(parser.GetVal(_T("theirs")));
193 pFrame->m_Data.m_theirFile.SetDescriptiveName(parser.GetVal(_T("theirsname")));
194 pFrame->m_Data.m_yourFile.SetFileName(parser.GetVal(_T("mine")));
195 pFrame->m_Data.m_yourFile.SetDescriptiveName(parser.GetVal(_T("minename")));
196 pFrame->m_Data.m_mergedFile.SetFileName(parser.GetVal(_T("merged")));
197 pFrame->m_Data.m_mergedFile.SetDescriptiveName(parser.GetVal(_T("mergedname")));
198 pFrame->m_Data.m_sPatchPath = parser.HasVal(_T("patchpath")) ? parser.GetVal(_T("patchpath")) : _T("");
199 pFrame->m_Data.m_sPatchPath.Replace('/', '\\');
200 if (parser.HasKey(_T("patchoriginal")))
201 pFrame->m_Data.m_sPatchOriginal = parser.GetVal(_T("patchoriginal"));
202 if (parser.HasKey(_T("patchpatched")))
203 pFrame->m_Data.m_sPatchPatched = parser.GetVal(_T("patchpatched"));
204 pFrame->m_Data.m_sDiffFile = parser.GetVal(_T("diff"));
205 pFrame->m_Data.m_sDiffFile.Replace('/', '\\');
206 if (parser.HasKey(_T("oneway")))
207 pFrame->m_bOneWay = TRUE;
208 if (parser.HasKey(_T("diff")))
209 pFrame->m_bOneWay = FALSE;
210 if (parser.HasKey(_T("reversedpatch")))
211 pFrame->m_bReversedPatch = TRUE;
212 if (pFrame->m_Data.IsBaseFileInUse() && !pFrame->m_Data.IsYourFileInUse() && pFrame->m_Data.IsTheirFileInUse())
214 pFrame->m_Data.m_yourFile.TransferDetailsFrom(pFrame->m_Data.m_theirFile);
217 if ((!parser.HasKey(_T("patchpath")))&&(parser.HasVal(_T("diff"))))
219 // a patchfile was given, but not folder path to apply the patch to
220 // If the patchfile is located inside a working copy, then use the parent directory
221 // of the patchfile as the target directory, otherwise ask the user for a path.
222 if (parser.HasKey(_T("wc")))
223 pFrame->m_Data.m_sPatchPath = pFrame->m_Data.m_sDiffFile.Left(pFrame->m_Data.m_sDiffFile.ReverseFind('\\'));
224 else
226 CBrowseFolder fbrowser;
227 fbrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
228 if (fbrowser.Show(NULL, pFrame->m_Data.m_sPatchPath)==CBrowseFolder::CANCEL)
229 return FALSE;
233 if ((parser.HasKey(_T("patchpath")))&&(!parser.HasVal(_T("diff"))))
235 // A path was given for applying a patchfile, but
236 // the patchfile itself was not.
237 // So ask the user for that patchfile
239 OPENFILENAME ofn = {0}; // common dialog box structure
240 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
241 // Initialize OPENFILENAME
242 ofn.lStructSize = sizeof(OPENFILENAME);
243 ofn.hwndOwner = pFrame->m_hWnd;
244 ofn.lpstrFile = szFile;
245 ofn.nMaxFile = _countof(szFile);
246 CString temp;
247 temp.LoadString(IDS_OPENDIFFFILETITLE);
248 if (temp.IsEmpty())
249 ofn.lpstrTitle = NULL;
250 else
251 ofn.lpstrTitle = temp;
253 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
254 // check if there's a patchfile in the clipboard
255 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
256 if (cFormat)
258 if (OpenClipboard(NULL))
260 UINT enumFormat = 0;
263 if (enumFormat == cFormat)
265 // yes, there's a patchfile in the clipboard
266 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER;
268 ofn.hInstance = AfxGetResourceHandle();
269 ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PATCH_FILE_OPEN_CUSTOM);
270 ofn.lpfnHook = CreatePatchFileOpenHook;
272 } while((enumFormat = EnumClipboardFormats(enumFormat))!=0);
273 CloseClipboard();
277 CString sFilter;
278 sFilter.LoadString(IDS_PATCHFILEFILTER);
279 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];
280 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);
281 // Replace '|' delimiters with '\0's
282 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
283 while (ptr != pszFilters)
285 if (*ptr == '|')
286 *ptr = '\0';
287 ptr--;
289 ofn.lpstrFilter = pszFilters;
290 ofn.nFilterIndex = 1;
292 // Display the Open dialog box.
293 CString tempfile;
294 if (GetOpenFileName(&ofn)==FALSE)
296 delete [] pszFilters;
297 return FALSE;
299 delete [] pszFilters;
300 pFrame->m_Data.m_sDiffFile = ofn.lpstrFile;
303 if ( pFrame->m_Data.m_baseFile.GetFilename().IsEmpty() && pFrame->m_Data.m_yourFile.GetFilename().IsEmpty() )
305 LPWSTR *szArglist;
306 int nArgs;
308 szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
309 if( NULL == szArglist )
311 TRACE("CommandLineToArgvW failed\n");
313 else
315 if ( nArgs==3 || nArgs==4 )
317 // Four parameters:
318 // [0]: Program name
319 // [1]: BASE file
320 // [2]: my file
321 // [3]: THEIR file (optional)
322 // This is the same format CAppUtils::StartExtDiff
323 // uses if %base and %mine are not set and most
324 // other diff tools use it too.
325 if ( PathFileExists(szArglist[1]) && PathFileExists(szArglist[2]) )
327 pFrame->m_Data.m_baseFile.SetFileName(szArglist[1]);
328 pFrame->m_Data.m_yourFile.SetFileName(szArglist[2]);
329 if ( nArgs == 4 && PathFileExists(szArglist[3]) )
331 pFrame->m_Data.m_theirFile.SetFileName(szArglist[3]);
337 // Free memory allocated for CommandLineToArgvW arguments.
338 LocalFree(szArglist);
341 pFrame->m_bReadOnly = !!parser.HasKey(_T("readonly"));
342 if (GetFileAttributes(pFrame->m_Data.m_yourFile.GetFilename()) & FILE_ATTRIBUTE_READONLY)
343 pFrame->m_bReadOnly = true;
344 pFrame->m_bBlame = !!parser.HasKey(_T("blame"));
345 // diffing a blame means no editing!
346 if (pFrame->m_bBlame)
347 pFrame->m_bReadOnly = true;
349 // try to find a suitable window title
350 CString sYour = pFrame->m_Data.m_yourFile.GetDescriptiveName();
351 if (sYour.Find(_T(" - "))>=0)
352 sYour = sYour.Left(sYour.Find(_T(" - ")));
353 if (sYour.Find(_T(" : "))>=0)
354 sYour = sYour.Left(sYour.Find(_T(" : ")));
355 CString sTheir = pFrame->m_Data.m_theirFile.GetDescriptiveName();
356 if (sTheir.Find(_T(" - "))>=0)
357 sTheir = sTheir.Left(sTheir.Find(_T(" - ")));
358 if (sTheir.Find(_T(" : "))>=0)
359 sTheir = sTheir.Left(sTheir.Find(_T(" : ")));
361 if (!sYour.IsEmpty() && !sTheir.IsEmpty())
363 if (sYour.CompareNoCase(sTheir)==0)
364 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
365 else if ((sYour.GetLength() < 10) &&
366 (sTheir.GetLength() < 10))
367 pFrame->SetWindowText(sYour + _T(" - ") + sTheir + _T(" - TortoiseMerge"));
368 else
370 // we have two very long descriptive texts here, which
371 // means we have to find a way to use them as a window
372 // title in a shorter way.
373 // for simplicity, we just use the one from "yourfile"
374 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
377 else if (!sYour.IsEmpty())
378 pFrame->SetWindowText(sYour + _T(" - TortoiseMerge"));
379 else if (!sTheir.IsEmpty())
380 pFrame->SetWindowText(sTheir + _T(" - TortoiseMerge"));
382 if (parser.HasKey(_T("createunifieddiff")))
384 // user requested to create a unified diff file
385 CString origFile = parser.GetVal(_T("origfile"));
386 CString modifiedFile = parser.GetVal(_T("modifiedfile"));
387 if (!origFile.IsEmpty() && !modifiedFile.IsEmpty())
389 CString outfile = parser.GetVal(_T("outfile"));
390 if (outfile.IsEmpty())
392 OPENFILENAME ofn = {0}; // common dialog box structure
393 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
394 ofn.lStructSize = sizeof(OPENFILENAME);
395 ofn.lpstrFile = szFile;
396 ofn.nMaxFile = _countof(szFile);
397 CString temp;
398 temp.LoadString(IDS_SAVEASTITLE);
399 if (!temp.IsEmpty())
400 ofn.lpstrTitle = temp;
401 ofn.Flags = OFN_OVERWRITEPROMPT;
402 CString sFilter;
403 sFilter.LoadString(IDS_COMMONFILEFILTER);
404 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];
405 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);
406 // Replace '|' delimiters with '\0's
407 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
408 while (ptr != pszFilters)
410 if (*ptr == '|')
411 *ptr = '\0';
412 ptr--;
414 ofn.lpstrFilter = pszFilters;
415 ofn.nFilterIndex = 1;
417 // Display the Save dialog box.
418 CString sFile;
419 if (GetSaveFileName(&ofn)==TRUE)
421 outfile = CString(ofn.lpstrFile);
423 delete [] pszFilters;
425 if (!outfile.IsEmpty())
427 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outfile, false);
428 return FALSE;
432 // The one and only window has been initialized, so show and update it
433 pFrame->ActivateFrame();
434 pFrame->ShowWindow(SW_SHOW);
435 pFrame->UpdateWindow();
436 pFrame->ShowDiffBar(!pFrame->m_bOneWay);
437 if (!pFrame->m_Data.IsBaseFileInUse() && pFrame->m_Data.m_sPatchPath.IsEmpty() && pFrame->m_Data.m_sDiffFile.IsEmpty())
439 pFrame->OnFileOpen();
440 return TRUE;
443 return pFrame->LoadViews();
446 // CTortoiseMergeApp message handlers
448 void CTortoiseMergeApp::OnAppAbout()
450 CAboutDlg aboutDlg;
451 aboutDlg.DoModal();
454 UINT_PTR CALLBACK
455 CTortoiseMergeApp::CreatePatchFileOpenHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM /*lParam*/)
457 if(uiMsg == WM_COMMAND && LOWORD(wParam) == IDC_PATCH_TO_CLIPBOARD)
459 HWND hFileDialog = GetParent(hDlg);
461 // if there's a patchfile in the clipboard, we save it
462 // to a temporary file and tell TortoiseMerge to use that one
463 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
464 if ((cFormat)&&(OpenClipboard(NULL)))
466 HGLOBAL hglb = GetClipboardData(cFormat);
467 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
469 DWORD len = GetTempPath(0, NULL);
470 TCHAR * path = new TCHAR[len+1];
471 TCHAR * tempF = new TCHAR[len+100];
472 GetTempPath (len+1, path);
473 GetTempFileName (path, TEXT("tsm"), 0, tempF);
474 std::wstring sTempFile = std::wstring(tempF);
475 delete [] path;
476 delete [] tempF;
478 FILE * outFile;
479 size_t patchlen = strlen(lpstr);
480 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));
481 if(outFile)
483 size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);
484 if (size == patchlen)
486 CommDlg_OpenSave_SetControlText(hFileDialog, edt1, sTempFile.c_str());
487 PostMessage(hFileDialog, WM_COMMAND, MAKEWPARAM(IDOK, BM_CLICK), (LPARAM)(GetDlgItem(hDlg, IDOK)));
489 fclose(outFile);
491 GlobalUnlock(hglb);
492 CloseClipboard();
495 return 0;
498 int CTortoiseMergeApp::ExitInstance()
500 // Look for temporary files left around by TortoiseMerge and
501 // remove them. But only delete 'old' files
502 DWORD len = ::GetTempPath(0, NULL);
503 TCHAR * path = new TCHAR[len + 100];
504 len = ::GetTempPath (len+100, path);
505 if (len != 0)
507 CSimpleFileFind finder = CSimpleFileFind(path, _T("*tsm*.*"));
508 FILETIME systime_;
509 ::GetSystemTimeAsFileTime(&systime_);
510 __int64 systime = (((_int64)systime_.dwHighDateTime)<<32) | ((__int64)systime_.dwLowDateTime);
511 while (finder.FindNextFileNoDirectories())
513 CString filepath = finder.GetFilePath();
514 HANDLE hFile = ::CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
515 if (hFile != INVALID_HANDLE_VALUE)
517 FILETIME createtime_;
518 if (::GetFileTime(hFile, &createtime_, NULL, NULL))
520 ::CloseHandle(hFile);
521 __int64 createtime = (((_int64)createtime_.dwHighDateTime)<<32) | ((__int64)createtime_.dwLowDateTime);
522 if ((createtime + 864000000000) < systime) //only delete files older than a day
524 ::SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL);
525 ::DeleteFile(filepath);
528 else
529 ::CloseHandle(hFile);
533 delete[] path;
535 return CWinAppEx::ExitInstance();