Update libgit2
[TortoiseGit.git] / src / TortoiseMerge / TortoiseMerge.cpp
blob6aabb92e85045f7eb31101029e5f08ec93d12726
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2013-2014 - TortoiseGit
4 // Copyright (C) 2006-2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include <dlgs.h>
22 #include "TortoiseMerge.h"
23 #include "MainFrm.h"
24 #include "AboutDlg.h"
25 #include "CmdLineParser.h"
26 #include "version.h"
27 #include "AppUtils.h"
28 #include "PathUtils.h"
29 #include "BrowseFolder.h"
30 #include "DirFileEnum.h"
31 #include "SelectFileFilter.h"
32 #include "FileDlgEventHandler.h"
33 #include "TempFile.h"
34 #include "TaskbarUUID.h"
35 #include "git2/threads.h"
37 #ifdef _DEBUG
38 #define new DEBUG_NEW
39 #endif
41 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
43 BEGIN_MESSAGE_MAP(CTortoiseMergeApp, CWinAppEx)
44 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
45 END_MESSAGE_MAP()
47 class PatchOpenDlgEventHandler : public CFileDlgEventHandler
49 public:
50 PatchOpenDlgEventHandler() {}
51 ~PatchOpenDlgEventHandler() {}
53 virtual STDMETHODIMP OnButtonClicked(IFileDialogCustomize* pfdc, DWORD dwIDCtl)
55 if (dwIDCtl == 101)
57 CComQIPtr<IFileOpenDialog> pDlg = pfdc;
58 if (pDlg)
60 pDlg->Close(S_OK);
63 return S_OK;
68 CTortoiseMergeApp::CTortoiseMergeApp()
69 : m_nAppLook(0)
71 EnableHtmlHelp();
72 m_bHiColorIcons = TRUE;
73 git_libgit2_init();
76 CTortoiseMergeApp::~CTortoiseMergeApp()
78 git_libgit2_shutdown();
81 // The one and only CTortoiseMergeApp object
82 CTortoiseMergeApp theApp;
83 CString sOrigCWD;
84 #if ENABLE_CRASHHANLDER
85 CCrashReportTGit g_crasher(L"TortoiseGitMerge " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
86 #endif
88 CString g_sGroupingUUID;
89 CString g_sGroupingIcon;
90 bool g_bGroupingRemoveIcon = false;
92 // CTortoiseMergeApp initialization
93 BOOL CTortoiseMergeApp::InitInstance()
95 SetDllDirectory(L"");
96 SetTaskIDPerUUID();
97 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
100 DWORD len = GetCurrentDirectory(0, NULL);
101 if (len)
103 std::unique_ptr<TCHAR[]> originalCurrentDirectory(new TCHAR[len]);
104 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
106 sOrigCWD = originalCurrentDirectory.get();
107 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
112 //set the resource dll for the required language
113 CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033);
114 long langId = loc;
115 CString langDll;
116 HINSTANCE hInst = NULL;
119 langDll.Format(_T("%sLanguages\\TortoiseMerge%ld.dll"), (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId);
121 hInst = LoadLibrary(langDll);
122 CString sVer = _T(STRPRODUCTVER);
123 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
124 if (sFileVer.Compare(sVer)!=0)
126 FreeLibrary(hInst);
127 hInst = NULL;
129 if (hInst != NULL)
130 AfxSetResourceHandle(hInst);
131 else
133 DWORD lid = SUBLANGID(langId);
134 lid--;
135 if (lid > 0)
137 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
139 else
140 langId = 0;
142 } while ((hInst == NULL) && (langId != 0));
143 TCHAR buf[6] = { 0 };
144 _tcscpy_s(buf, _T("en"));
145 langId = loc;
146 CString sHelppath = CPathUtils::GetAppDirectory() + _T("TortoiseMerge_en.chm");
147 free((void*)m_pszHelpFilePath);
148 m_pszHelpFilePath=_tcsdup(sHelppath);
149 sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseMerge_en.chm");
152 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf));
153 CString sLang = _T("_");
154 sLang += buf;
155 sHelppath.Replace(_T("_en"), sLang);
156 if (PathFileExists(sHelppath))
158 free((void*)m_pszHelpFilePath);
159 m_pszHelpFilePath=_tcsdup(sHelppath);
160 break;
162 sHelppath.Replace(sLang, _T("_en"));
163 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf));
164 sLang += _T("_");
165 sLang += buf;
166 sHelppath.Replace(_T("_en"), sLang);
167 if (PathFileExists(sHelppath))
169 free((void*)m_pszHelpFilePath);
170 m_pszHelpFilePath=_tcsdup(sHelppath);
171 break;
173 sHelppath.Replace(sLang, _T("_en"));
175 DWORD lid = SUBLANGID(langId);
176 lid--;
177 if (lid > 0)
179 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
181 else
182 langId = 0;
183 } while (langId);
184 setlocale(LC_ALL, "");
185 // We need to explicitly set the thread locale to the system default one to avoid possible problems with saving files in its original codepage
186 // The problems occures when the language of OS differs from the regional settings
187 // See the details here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887
188 SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
190 // InitCommonControls() is required on Windows XP if an application
191 // manifest specifies use of ComCtl32.dll version 6 or later to enable
192 // visual styles. Otherwise, any window creation will fail.
193 InitCommonControls();
195 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
196 CMFCButton::EnableWindowsTheming();
197 EnableTaskbarInteraction(FALSE);
199 // Initialize all Managers for usage. They are automatically constructed
200 // if not yet present
201 InitContextMenuManager();
202 InitKeyboardManager();
203 InitTooltipManager ();
204 CMFCToolTipInfo params;
205 params.m_bVislManagerTheme = TRUE;
207 GetTooltipManager ()->SetTooltipParams (
208 AFX_TOOLTIP_TYPE_ALL,
209 RUNTIME_CLASS (CMFCToolTipCtrl),
210 &params);
212 CCmdLineParser parser = CCmdLineParser(this->m_lpCmdLine);
214 g_sGroupingUUID = parser.GetVal(L"groupuuid");
216 if (parser.HasKey(_T("?")) || parser.HasKey(_T("help")))
218 CString sHelpText;
219 sHelpText.LoadString(IDS_COMMANDLINEHELP);
220 MessageBox(NULL, sHelpText, _T("TortoiseGitMerge"), MB_ICONINFORMATION);
221 return FALSE;
224 // Initialize OLE libraries
225 if (!AfxOleInit())
227 AfxMessageBox(IDP_OLE_INIT_FAILED);
228 return FALSE;
230 AfxEnableControlContainer();
231 // Standard initialization
232 // If you are not using these features and wish to reduce the size
233 // of your final executable, you should remove from the following
234 // the specific initialization routines you do not need
235 // Change the registry key under which our settings are stored
236 SetRegistryKey(_T("TortoiseGitMerge"));
238 if (CRegDWORD(_T("Software\\TortoiseGitMerge\\Debug"), FALSE)==TRUE)
239 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
241 // To create the main window, this code creates a new frame window
242 // object and then sets it as the application's main window object
243 CMainFrame* pFrame = new CMainFrame;
244 if (pFrame == NULL)
245 return FALSE;
246 m_pMainWnd = pFrame;
248 // create and load the frame with its resources
249 if (!pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL))
250 return FALSE;
252 // Fill in the command line options
253 pFrame->m_Data.m_baseFile.SetFileName(parser.GetVal(_T("base")));
254 pFrame->m_Data.m_baseFile.SetDescriptiveName(parser.GetVal(_T("basename")));
255 pFrame->m_Data.m_baseFile.SetReflectedName(parser.GetVal(_T("basereflectedname")));
256 pFrame->m_Data.m_theirFile.SetFileName(parser.GetVal(_T("theirs")));
257 pFrame->m_Data.m_theirFile.SetDescriptiveName(parser.GetVal(_T("theirsname")));
258 pFrame->m_Data.m_theirFile.SetReflectedName(parser.GetVal(_T("theirsreflectedname")));
259 pFrame->m_Data.m_yourFile.SetFileName(parser.GetVal(_T("mine")));
260 pFrame->m_Data.m_yourFile.SetDescriptiveName(parser.GetVal(_T("minename")));
261 pFrame->m_Data.m_yourFile.SetReflectedName(parser.GetVal(_T("minereflectedname")));
262 pFrame->m_Data.m_mergedFile.SetFileName(parser.GetVal(_T("merged")));
263 pFrame->m_Data.m_mergedFile.SetDescriptiveName(parser.GetVal(_T("mergedname")));
264 pFrame->m_Data.m_mergedFile.SetReflectedName(parser.GetVal(_T("mergedreflectedname")));
265 pFrame->m_Data.m_sPatchPath = parser.HasVal(_T("patchpath")) ? parser.GetVal(_T("patchpath")) : _T("");
266 pFrame->m_Data.m_sPatchPath.Replace('/', '\\');
267 if (parser.HasKey(_T("patchoriginal")))
268 pFrame->m_Data.m_sPatchOriginal = parser.GetVal(_T("patchoriginal"));
269 if (parser.HasKey(_T("patchpatched")))
270 pFrame->m_Data.m_sPatchPatched = parser.GetVal(_T("patchpatched"));
271 pFrame->m_Data.m_sDiffFile = parser.GetVal(_T("diff"));
272 pFrame->m_Data.m_sDiffFile.Replace('/', '\\');
273 if (parser.HasKey(_T("oneway")))
274 pFrame->m_bOneWay = TRUE;
275 if (parser.HasKey(_T("diff")))
276 pFrame->m_bOneWay = FALSE;
277 if (parser.HasKey(_T("reversedpatch")))
278 pFrame->m_bReversedPatch = TRUE;
279 if (parser.HasKey(_T("saverequired")))
280 pFrame->m_bSaveRequired = true;
281 if (parser.HasKey(_T("saverequiredonconflicts")))
282 pFrame->m_bSaveRequiredOnConflicts = true;
283 if (parser.HasKey(_T("deletebasetheirsmineonclose")))
284 pFrame->m_bDeleteBaseTheirsMineOnClose = true;
285 if (pFrame->m_Data.IsBaseFileInUse() && !pFrame->m_Data.IsYourFileInUse() && pFrame->m_Data.IsTheirFileInUse())
287 pFrame->m_Data.m_yourFile.TransferDetailsFrom(pFrame->m_Data.m_theirFile);
290 if ((!parser.HasKey(_T("patchpath")))&&(parser.HasVal(_T("diff"))))
292 // a patchfile was given, but not folder path to apply the patch to
293 // If the patchfile is located inside a working copy, then use the parent directory
294 // of the patchfile as the target directory, otherwise ask the user for a path.
295 if (parser.HasKey(_T("wc")))
296 pFrame->m_Data.m_sPatchPath = pFrame->m_Data.m_sDiffFile.Left(pFrame->m_Data.m_sDiffFile.ReverseFind('\\'));
297 else
299 CBrowseFolder fbrowser;
300 fbrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
301 if (fbrowser.Show(NULL, pFrame->m_Data.m_sPatchPath)==CBrowseFolder::CANCEL)
302 return FALSE;
306 if ((parser.HasKey(_T("patchpath")))&&(!parser.HasVal(_T("diff"))))
308 // A path was given for applying a patchfile, but
309 // the patchfile itself was not.
310 // So ask the user for that patchfile
312 HRESULT hr;
313 // Create a new common save file dialog
314 CComPtr<IFileOpenDialog> pfd = NULL;
315 hr = pfd.CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER);
316 if (SUCCEEDED(hr))
318 // Set the dialog options
319 DWORD dwOptions;
320 if (SUCCEEDED(hr = pfd->GetOptions(&dwOptions)))
322 hr = pfd->SetOptions(dwOptions | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
325 // Set a title
326 if (SUCCEEDED(hr))
328 CString temp;
329 temp.LoadString(IDS_OPENDIFFFILETITLE);
330 pfd->SetTitle(temp);
332 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
333 hr = pfd->SetFileTypes(fileFilter.GetCount(), fileFilter);
334 bool bAdvised = false;
335 DWORD dwCookie = 0;
336 CComObjectStackEx<PatchOpenDlgEventHandler> cbk;
337 CComQIPtr<IFileDialogEvents> pEvents = cbk.GetUnknown();
340 CComPtr<IFileDialogCustomize> pfdCustomize;
341 hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdCustomize));
342 if (SUCCEEDED(hr))
344 // check if there's a unified diff on the clipboard and
345 // add a button to the fileopen dialog if there is.
346 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
347 if ((cFormat)&&(OpenClipboard(NULL)))
349 HGLOBAL hglb = GetClipboardData(cFormat);
350 if (hglb)
352 pfdCustomize->AddPushButton(101, CString(MAKEINTRESOURCE(IDS_PATCH_COPYFROMCLIPBOARD)));
353 hr = pfd->Advise(pEvents, &dwCookie);
354 bAdvised = SUCCEEDED(hr);
356 CloseClipboard();
361 // Show the save file dialog
362 if (SUCCEEDED(hr) && SUCCEEDED(hr = pfd->Show(pFrame->m_hWnd)))
364 // Get the selection from the user
365 CComPtr<IShellItem> psiResult = NULL;
366 hr = pfd->GetResult(&psiResult);
367 if (bAdvised)
368 pfd->Unadvise(dwCookie);
369 if (SUCCEEDED(hr))
371 PWSTR pszPath = NULL;
372 hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
373 if (SUCCEEDED(hr))
375 pFrame->m_Data.m_sDiffFile = pszPath;
376 CoTaskMemFree(pszPath);
379 else
381 // no result, which means we closed the dialog in our button handler
382 std::wstring sTempFile;
383 if (TrySavePatchFromClipboard(sTempFile))
384 pFrame->m_Data.m_sDiffFile = sTempFile.c_str();
387 else
389 if (bAdvised)
390 pfd->Unadvise(dwCookie);
391 return FALSE;
394 else
396 OPENFILENAME ofn = {0}; // common dialog box structure
397 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
398 // Initialize OPENFILENAME
399 ofn.lStructSize = sizeof(OPENFILENAME);
400 ofn.hwndOwner = pFrame->m_hWnd;
401 ofn.lpstrFile = szFile;
402 ofn.nMaxFile = _countof(szFile);
403 CString temp;
404 temp.LoadString(IDS_OPENDIFFFILETITLE);
405 if (temp.IsEmpty())
406 ofn.lpstrTitle = NULL;
407 else
408 ofn.lpstrTitle = temp;
410 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
411 if( HasClipboardPatch() ) {
412 ofn.Flags |= ( OFN_ENABLETEMPLATE | OFN_ENABLEHOOK );
413 ofn.hInstance = AfxGetResourceHandle();
414 ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PATCH_FILE_OPEN_CUSTOM);
415 ofn.lpfnHook = CreatePatchFileOpenHook;
418 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
419 ofn.lpstrFilter = fileFilter;
420 ofn.nFilterIndex = 1;
422 // Display the Open dialog box.
423 if (GetOpenFileName(&ofn)==FALSE)
425 return FALSE;
427 pFrame->m_Data.m_sDiffFile = ofn.lpstrFile;
431 if ( pFrame->m_Data.m_baseFile.GetFilename().IsEmpty() && pFrame->m_Data.m_yourFile.GetFilename().IsEmpty() )
433 int nArgs;
434 LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
435 if( NULL == szArglist )
437 TRACE("CommandLineToArgvW failed\n");
439 else
441 if ( nArgs==3 || nArgs==4 )
443 // Four parameters:
444 // [0]: Program name
445 // [1]: BASE file
446 // [2]: my file
447 // [3]: THEIR file (optional)
448 // This is the same format CAppUtils::StartExtDiff
449 // uses if %base and %mine are not set and most
450 // other diff tools use it too.
451 if ( PathFileExists(szArglist[1]) && PathFileExists(szArglist[2]) )
453 pFrame->m_Data.m_baseFile.SetFileName(szArglist[1]);
454 pFrame->m_Data.m_yourFile.SetFileName(szArglist[2]);
455 if ( nArgs == 4 && PathFileExists(szArglist[3]) )
457 pFrame->m_Data.m_theirFile.SetFileName(szArglist[3]);
461 else if (nArgs == 2)
463 // only one path specified: use it to fill the "open" dialog
464 if (PathFileExists(szArglist[1]))
466 pFrame->m_Data.m_yourFile.SetFileName(szArglist[1]);
467 pFrame->m_Data.m_yourFile.StoreFileAttributes();
472 // Free memory allocated for CommandLineToArgvW arguments.
473 LocalFree(szArglist);
476 pFrame->m_bReadOnly = !!parser.HasKey(_T("readonly"));
477 if (GetFileAttributes(pFrame->m_Data.m_yourFile.GetFilename()) & FILE_ATTRIBUTE_READONLY)
478 pFrame->m_bReadOnly = true;
479 pFrame->m_bBlame = !!parser.HasKey(_T("blame"));
480 // diffing a blame means no editing!
481 if (pFrame->m_bBlame)
482 pFrame->m_bReadOnly = true;
484 pFrame->SetWindowTitle();
486 if (parser.HasKey(_T("createunifieddiff")))
488 // user requested to create a unified diff file
489 CString origFile = parser.GetVal(_T("origfile"));
490 CString modifiedFile = parser.GetVal(_T("modifiedfile"));
491 if (!origFile.IsEmpty() && !modifiedFile.IsEmpty())
493 CString outfile = parser.GetVal(_T("outfile"));
494 if (outfile.IsEmpty())
496 CCommonAppUtils::FileOpenSave(outfile, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, NULL);
498 if (!outfile.IsEmpty())
500 CRegStdDWORD regContextLines(L"Software\\TortoiseGitMerge\\ContextLines", (DWORD)-1);
501 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outfile, regContextLines, false);
502 return FALSE;
507 pFrame->resolveMsgWnd = parser.HasVal(L"resolvemsghwnd") ? (HWND)parser.GetLongLongVal(L"resolvemsghwnd") : 0;
508 pFrame->resolveMsgWParam = parser.HasVal(L"resolvemsgwparam") ? (WPARAM)parser.GetLongLongVal(L"resolvemsgwparam") : 0;
509 pFrame->resolveMsgLParam = parser.HasVal(L"resolvemsglparam") ? (LPARAM)parser.GetLongLongVal(L"resolvemsglparam") : 0;
511 // The one and only window has been initialized, so show and update it
512 pFrame->ActivateFrame();
513 pFrame->ShowWindow(SW_SHOW);
514 pFrame->UpdateWindow();
515 pFrame->ShowDiffBar(!pFrame->m_bOneWay);
516 if (!pFrame->m_Data.IsBaseFileInUse() && pFrame->m_Data.m_sPatchPath.IsEmpty() && pFrame->m_Data.m_sDiffFile.IsEmpty())
518 pFrame->OnFileOpen(pFrame->m_Data.m_yourFile.InUse());
519 return TRUE;
522 int line = -2;
523 if (parser.HasVal(_T("line")))
525 line = parser.GetLongVal(_T("line"));
526 line--; // we need the index
529 return pFrame->LoadViews(line);
532 // CTortoiseMergeApp message handlers
534 void CTortoiseMergeApp::OnAppAbout()
536 CAboutDlg aboutDlg;
537 aboutDlg.DoModal();
540 UINT_PTR CALLBACK
541 CTortoiseMergeApp::CreatePatchFileOpenHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM /*lParam*/)
543 if(uiMsg == WM_COMMAND && LOWORD(wParam) == IDC_PATCH_TO_CLIPBOARD)
545 HWND hFileDialog = GetParent(hDlg);
547 // if there's a patchfile in the clipboard, we save it
548 // to a temporary file and tell TortoiseMerge to use that one
549 std::wstring sTempFile;
550 if (TrySavePatchFromClipboard(sTempFile))
552 CommDlg_OpenSave_SetControlText(hFileDialog, edt1, sTempFile.c_str());
553 PostMessage(hFileDialog, WM_COMMAND, MAKEWPARAM(IDOK, BM_CLICK), (LPARAM)(GetDlgItem(hDlg, IDOK)));
556 return 0;
559 int CTortoiseMergeApp::ExitInstance()
561 // Look for temporary files left around by TortoiseMerge and
562 // remove them. But only delete 'old' files
563 CTempFiles::DeleteOldTempFiles(_T("*tsm*.*"));
565 return CWinAppEx::ExitInstance();
568 bool CTortoiseMergeApp::HasClipboardPatch()
570 // check if there's a patchfile in the clipboard
571 const UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
572 if (cFormat == 0)
573 return false;
575 if (OpenClipboard(NULL) == 0)
576 return false;
578 bool containsPatch = false;
579 UINT enumFormat = 0;
582 if (enumFormat == cFormat)
584 containsPatch = true; // yes, there's a patchfile in the clipboard
586 } while((enumFormat = EnumClipboardFormats(enumFormat))!=0);
587 CloseClipboard();
589 return containsPatch;
592 bool CTortoiseMergeApp::TrySavePatchFromClipboard(std::wstring& resultFile)
594 resultFile.clear();
596 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
597 if (cFormat == 0)
598 return false;
599 if (OpenClipboard(NULL) == 0)
600 return false;
602 HGLOBAL hglb = GetClipboardData(cFormat);
603 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
605 DWORD len = GetTempPath(0, NULL);
606 std::unique_ptr<TCHAR[]> path(new TCHAR[len+1]);
607 std::unique_ptr<TCHAR[]> tempF(new TCHAR[len+100]);
608 GetTempPath (len+1, path.get());
609 GetTempFileName (path.get(), _T("tsm"), 0, tempF.get());
610 std::wstring sTempFile = std::wstring(tempF.get());
612 FILE* outFile = 0;
613 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));
614 if (outFile != 0)
616 size_t patchlen = strlen(lpstr);
617 size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);
618 if (size == patchlen)
619 resultFile = sTempFile;
621 fclose(outFile);
623 GlobalUnlock(hglb);
624 CloseClipboard();
626 return !resultFile.empty();