OSDN Git Service

Correct Handle case when patch file is not in repository path.
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / AppUtils.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "resource.h"\r
21 #include "TortoiseProc.h"\r
22 #include "PathUtils.h"\r
23 #include "AppUtils.h"\r
24 //#include "GitProperties.h"\r
25 #include "StringUtils.h"\r
26 #include "MessageBox.h"\r
27 #include "Registry.h"\r
28 #include "TGitPath.h"\r
29 #include "Git.h"\r
30 //#include "RepositoryBrowser.h"\r
31 //#include "BrowseFolder.h"\r
32 #include "UnicodeUtils.h"\r
33 #include "ExportDlg.h"\r
34 #include "ProgressDlg.h"\r
35 #include "GitAdminDir.h"\r
36 #include "ProgressDlg.h"\r
37 #include "BrowseFolder.h"\r
38 #include "DirFileEnum.h"\r
39 #include "MessageBox.h"\r
40 #include "GitStatus.h"\r
41 #include "CreateBranchTagDlg.h"\r
42 #include "GitSwitchDlg.h"\r
43 #include "ResetDlg.h"\r
44 #include "DeleteConflictDlg.h"\r
45 #include "ChangedDlg.h"\r
46 \r
47 CAppUtils::CAppUtils(void)\r
48 {\r
49 }\r
50 \r
51 CAppUtils::~CAppUtils(void)\r
52 {\r
53 }\r
54 \r
55 int      CAppUtils::StashApply(CString ref)\r
56 {\r
57         CString cmd,out;\r
58         cmd=_T("git.exe stash apply ");\r
59         cmd+=ref;\r
60         \r
61         if(g_Git.Run(cmd,&out,CP_ACP))\r
62         {\r
63                 CMessageBox::Show(NULL,CString(_T("<ct=0x0000FF>Stash Apply Fail!!!</ct>\n"))+out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);\r
64 \r
65         }else\r
66         {\r
67                 if(CMessageBox::Show(NULL,CString(_T("<ct=0xff0000>Stash Apply Success</ct>\nDo you want to show change?"))\r
68                         ,_T("TortoiseGit"),MB_YESNO|MB_ICONINFORMATION) == IDYES)\r
69                 {\r
70                         CChangedDlg dlg;\r
71                         dlg.m_pathList.AddPath(CTGitPath());\r
72                         dlg.DoModal();                  \r
73                 }\r
74                 return 0;\r
75         }\r
76         return -1;\r
77 }\r
78 bool CAppUtils::GetMimeType(const CTGitPath& file, CString& mimetype)\r
79 {\r
80 #if 0\r
81         GitProperties props(file, GitRev::REV_WC, false);\r
82         for (int i = 0; i < props.GetCount(); ++i)\r
83         {\r
84                 if (props.GetItemName(i).compare(_T("svn:mime-type"))==0)\r
85                 {\r
86                         mimetype = props.GetItemValue(i).c_str();\r
87                         return true;\r
88                 }\r
89         }\r
90 #endif\r
91         return false;\r
92 }\r
93 \r
94 BOOL CAppUtils::StartExtMerge(\r
95         const CTGitPath& basefile, const CTGitPath& theirfile, const CTGitPath& yourfile, const CTGitPath& mergedfile,\r
96         const CString& basename, const CString& theirname, const CString& yourname, const CString& mergedname, bool bReadOnly)\r
97 {\r
98 \r
99         CRegString regCom = CRegString(_T("Software\\TortoiseGit\\Merge"));\r
100         CString ext = mergedfile.GetFileExtension();\r
101         CString com = regCom;\r
102         bool bInternal = false;\r
103 \r
104         CString mimetype;\r
105         if (ext != "")\r
106         {\r
107                 // is there an extension specific merge tool?\r
108                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + ext.MakeLower());\r
109                 if (CString(mergetool) != "")\r
110                 {\r
111                         com = mergetool;\r
112                 }\r
113         }\r
114         if (GetMimeType(yourfile, mimetype) || GetMimeType(theirfile, mimetype) || GetMimeType(basefile, mimetype))\r
115         {\r
116                 // is there a mime type specific merge tool?\r
117                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + mimetype);\r
118                 if (CString(mergetool) != "")\r
119                 {\r
120                         com = mergetool;\r
121                 }\r
122         }\r
123         \r
124         if (com.IsEmpty()||(com.Left(1).Compare(_T("#"))==0))\r
125         {\r
126                 // use TortoiseMerge\r
127                 bInternal = true;\r
128                 CRegString tortoiseMergePath(_T("Software\\TortoiseGit\\TMergePath"), _T(""), false, HKEY_LOCAL_MACHINE);\r
129                 com = tortoiseMergePath;\r
130                 if (com.IsEmpty())\r
131                 {\r
132                         com = CPathUtils::GetAppDirectory();\r
133                         com += _T("TortoiseMerge.exe");\r
134                 }\r
135                 com = _T("\"") + com + _T("\"");\r
136                 com = com + _T(" /base:%base /theirs:%theirs /mine:%mine /merged:%merged");\r
137                 com = com + _T(" /basename:%bname /theirsname:%tname /minename:%yname /mergedname:%mname");\r
138         }\r
139         // check if the params are set. If not, just add the files to the command line\r
140         if ((com.Find(_T("%merged"))<0)&&(com.Find(_T("%base"))<0)&&(com.Find(_T("%theirs"))<0)&&(com.Find(_T("%mine"))<0))\r
141         {\r
142                 com += _T(" \"")+basefile.GetWinPathString()+_T("\"");\r
143                 com += _T(" \"")+theirfile.GetWinPathString()+_T("\"");\r
144                 com += _T(" \"")+yourfile.GetWinPathString()+_T("\"");\r
145                 com += _T(" \"")+mergedfile.GetWinPathString()+_T("\"");\r
146         }\r
147         if (basefile.IsEmpty())\r
148         {\r
149                 com.Replace(_T("/base:%base"), _T(""));\r
150                 com.Replace(_T("%base"), _T(""));               \r
151         }\r
152         else\r
153                 com.Replace(_T("%base"), _T("\"") + basefile.GetWinPathString() + _T("\""));\r
154         if (theirfile.IsEmpty())\r
155         {\r
156                 com.Replace(_T("/theirs:%theirs"), _T(""));\r
157                 com.Replace(_T("%theirs"), _T(""));\r
158         }\r
159         else\r
160                 com.Replace(_T("%theirs"), _T("\"") + theirfile.GetWinPathString() + _T("\""));\r
161         if (yourfile.IsEmpty())\r
162         {\r
163                 com.Replace(_T("/mine:%mine"), _T(""));\r
164                 com.Replace(_T("%mine"), _T(""));\r
165         }\r
166         else\r
167                 com.Replace(_T("%mine"), _T("\"") + yourfile.GetWinPathString() + _T("\""));\r
168         if (mergedfile.IsEmpty())\r
169         {\r
170                 com.Replace(_T("/merged:%merged"), _T(""));\r
171                 com.Replace(_T("%merged"), _T(""));\r
172         }\r
173         else\r
174                 com.Replace(_T("%merged"), _T("\"") + mergedfile.GetWinPathString() + _T("\""));\r
175         if (basename.IsEmpty())\r
176         {\r
177                 if (basefile.IsEmpty())\r
178                 {\r
179                         com.Replace(_T("/basename:%bname"), _T(""));\r
180                         com.Replace(_T("%bname"), _T(""));\r
181                 }\r
182                 else\r
183                 {\r
184                         com.Replace(_T("%bname"), _T("\"") + basefile.GetUIFileOrDirectoryName() + _T("\""));\r
185                 }\r
186         }\r
187         else\r
188                 com.Replace(_T("%bname"), _T("\"") + basename + _T("\""));\r
189         if (theirname.IsEmpty())\r
190         {\r
191                 if (theirfile.IsEmpty())\r
192                 {\r
193                         com.Replace(_T("/theirsname:%tname"), _T(""));\r
194                         com.Replace(_T("%tname"), _T(""));\r
195                 }\r
196                 else\r
197                 {\r
198                         com.Replace(_T("%tname"), _T("\"") + theirfile.GetUIFileOrDirectoryName() + _T("\""));\r
199                 }\r
200         }\r
201         else\r
202                 com.Replace(_T("%tname"), _T("\"") + theirname + _T("\""));\r
203         if (yourname.IsEmpty())\r
204         {\r
205                 if (yourfile.IsEmpty())\r
206                 {\r
207                         com.Replace(_T("/minename:%yname"), _T(""));\r
208                         com.Replace(_T("%yname"), _T(""));\r
209                 }\r
210                 else\r
211                 {\r
212                         com.Replace(_T("%yname"), _T("\"") + yourfile.GetUIFileOrDirectoryName() + _T("\""));\r
213                 }\r
214         }\r
215         else\r
216                 com.Replace(_T("%yname"), _T("\"") + yourname + _T("\""));\r
217         if (mergedname.IsEmpty())\r
218         {\r
219                 if (mergedfile.IsEmpty())\r
220                 {\r
221                         com.Replace(_T("/mergedname:%mname"), _T(""));\r
222                         com.Replace(_T("%mname"), _T(""));\r
223                 }\r
224                 else\r
225                 {\r
226                         com.Replace(_T("%mname"), _T("\"") + mergedfile.GetUIFileOrDirectoryName() + _T("\""));\r
227                 }\r
228         }\r
229         else\r
230                 com.Replace(_T("%mname"), _T("\"") + mergedname + _T("\""));\r
231 \r
232         if ((bReadOnly)&&(bInternal))\r
233                 com += _T(" /readonly");\r
234 \r
235         if(!LaunchApplication(com, IDS_ERR_EXTMERGESTART, false))\r
236         {\r
237                 return FALSE;\r
238         }\r
239 \r
240         return TRUE;\r
241 }\r
242 \r
243 BOOL CAppUtils::StartExtPatch(const CTGitPath& patchfile, const CTGitPath& dir, const CString& sOriginalDescription, const CString& sPatchedDescription, BOOL bReversed, BOOL bWait)\r
244 {\r
245         CString viewer;\r
246         // use TortoiseMerge\r
247         viewer = CPathUtils::GetAppDirectory();\r
248         viewer += _T("TortoiseMerge.exe");\r
249 \r
250         viewer = _T("\"") + viewer + _T("\"");\r
251         viewer = viewer + _T(" /diff:\"") + patchfile.GetWinPathString() + _T("\"");\r
252         viewer = viewer + _T(" /patchpath:\"") + dir.GetWinPathString() + _T("\"");\r
253         if (bReversed)\r
254                 viewer += _T(" /reversedpatch");\r
255         if (!sOriginalDescription.IsEmpty())\r
256                 viewer = viewer + _T(" /patchoriginal:\"") + sOriginalDescription + _T("\"");\r
257         if (!sPatchedDescription.IsEmpty())\r
258                 viewer = viewer + _T(" /patchpatched:\"") + sPatchedDescription + _T("\"");\r
259         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
260         {\r
261                 return FALSE;\r
262         }\r
263         return TRUE;\r
264 }\r
265 \r
266 CString CAppUtils::PickDiffTool(const CTGitPath& file1, const CTGitPath& file2)\r
267 {\r
268         // Is there a mime type specific diff tool?\r
269         CString mimetype;\r
270         if (GetMimeType(file1, mimetype) ||  GetMimeType(file2, mimetype))\r
271         {\r
272                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + mimetype);\r
273                 if (!difftool.IsEmpty())\r
274                         return difftool;\r
275         }\r
276         \r
277         // Is there an extension specific diff tool?\r
278         CString ext = file2.GetFileExtension().MakeLower();\r
279         if (!ext.IsEmpty())\r
280         {\r
281                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + ext);\r
282                 if (!difftool.IsEmpty())\r
283                         return difftool;\r
284                 // Maybe we should use TortoiseIDiff?\r
285                 if ((ext == _T(".jpg")) || (ext == _T(".jpeg")) ||\r
286                         (ext == _T(".bmp")) || (ext == _T(".gif"))  ||\r
287                         (ext == _T(".png")) || (ext == _T(".ico"))  ||\r
288                         (ext == _T(".dib")) || (ext == _T(".emf")))\r
289                 {\r
290                         return\r
291                                 _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseIDiff.exe") + _T("\"") +\r
292                                 _T(" /left:%base /right:%mine /lefttitle:%bname /righttitle:%yname");\r
293                 }\r
294         }\r
295         \r
296         // Finally, pick a generic external diff tool\r
297         CString difftool = CRegString(_T("Software\\TortoiseGit\\Diff"));\r
298         return difftool;\r
299 }\r
300 \r
301 bool CAppUtils::StartExtDiff(\r
302         const CString& file1,  const CString& file2,\r
303         const CString& sName1, const CString& sName2, \r
304         const DiffFlags& flags)\r
305 {\r
306         CString viewer;\r
307 \r
308         CRegDWORD blamediff(_T("Software\\TortoiseGit\\DiffBlamesWithTortoiseMerge"), FALSE);\r
309         if (!flags.bBlame || !(DWORD)blamediff)\r
310         {\r
311                 viewer = PickDiffTool(file1, file2);\r
312                 // If registry entry for a diff program is commented out, use TortoiseMerge.\r
313                 bool bCommentedOut = viewer.Left(1) == _T("#");\r
314                 if (flags.bAlternativeTool)\r
315                 {\r
316                         // Invert external vs. internal diff tool selection.\r
317                         if (bCommentedOut)\r
318                                 viewer.Delete(0); // uncomment\r
319                         else\r
320                                 viewer = "";\r
321                 }\r
322                 else if (bCommentedOut)\r
323                         viewer = "";\r
324         }\r
325 \r
326         bool bInternal = viewer.IsEmpty();\r
327         if (bInternal)\r
328         {\r
329                 viewer =\r
330                         _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseMerge.exe") + _T("\"") +\r
331                         _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
332                 if (flags.bBlame)\r
333                         viewer += _T(" /blame");\r
334         }\r
335         // check if the params are set. If not, just add the files to the command line\r
336         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
337         {\r
338                 viewer += _T(" \"")+file1+_T("\"");\r
339                 viewer += _T(" \"")+file2+_T("\"");\r
340         }\r
341         if (viewer.Find(_T("%base")) >= 0)\r
342         {\r
343                 viewer.Replace(_T("%base"),  _T("\"")+file1+_T("\""));\r
344         }\r
345         if (viewer.Find(_T("%mine")) >= 0)\r
346         {\r
347                 viewer.Replace(_T("%mine"),  _T("\"")+file2+_T("\""));\r
348         }\r
349 \r
350         if (sName1.IsEmpty())\r
351                 viewer.Replace(_T("%bname"), _T("\"") + file1 + _T("\""));\r
352         else\r
353                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
354 \r
355         if (sName2.IsEmpty())\r
356                 viewer.Replace(_T("%yname"), _T("\"") + file2 + _T("\""));\r
357         else\r
358                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
359 \r
360         if (flags.bReadOnly && bInternal)\r
361                 viewer += _T(" /readonly");\r
362 \r
363         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, flags.bWait);\r
364 }\r
365 \r
366 BOOL CAppUtils::StartExtDiffProps(const CTGitPath& file1, const CTGitPath& file2, const CString& sName1, const CString& sName2, BOOL bWait, BOOL bReadOnly)\r
367 {\r
368         CRegString diffpropsexe(_T("Software\\TortoiseGit\\DiffProps"));\r
369         CString viewer = diffpropsexe;\r
370         bool bInternal = false;\r
371         if (viewer.IsEmpty()||(viewer.Left(1).Compare(_T("#"))==0))\r
372         {\r
373                 //no registry entry (or commented out) for a diff program\r
374                 //use TortoiseMerge\r
375                 bInternal = true;\r
376                 viewer = CPathUtils::GetAppDirectory();\r
377                 viewer += _T("TortoiseMerge.exe");\r
378                 viewer = _T("\"") + viewer + _T("\"");\r
379                 viewer = viewer + _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
380         }\r
381         // check if the params are set. If not, just add the files to the command line\r
382         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
383         {\r
384                 viewer += _T(" \"")+file1.GetWinPathString()+_T("\"");\r
385                 viewer += _T(" \"")+file2.GetWinPathString()+_T("\"");\r
386         }\r
387         if (viewer.Find(_T("%base")) >= 0)\r
388         {\r
389                 viewer.Replace(_T("%base"),  _T("\"")+file1.GetWinPathString()+_T("\""));\r
390         }\r
391         if (viewer.Find(_T("%mine")) >= 0)\r
392         {\r
393                 viewer.Replace(_T("%mine"),  _T("\"")+file2.GetWinPathString()+_T("\""));\r
394         }\r
395 \r
396         if (sName1.IsEmpty())\r
397                 viewer.Replace(_T("%bname"), _T("\"") + file1.GetUIFileOrDirectoryName() + _T("\""));\r
398         else\r
399                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
400 \r
401         if (sName2.IsEmpty())\r
402                 viewer.Replace(_T("%yname"), _T("\"") + file2.GetUIFileOrDirectoryName() + _T("\""));\r
403         else\r
404                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
405 \r
406         if ((bReadOnly)&&(bInternal))\r
407                 viewer += _T(" /readonly");\r
408 \r
409         if(!LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, !!bWait))\r
410         {\r
411                 return FALSE;\r
412         }\r
413         return TRUE;\r
414 }\r
415 \r
416 BOOL CAppUtils::StartUnifiedDiffViewer(const CString& patchfile, const CString& title, BOOL bWait)\r
417 {\r
418         CString viewer;\r
419         CRegString v = CRegString(_T("Software\\TortoiseGit\\DiffViewer"));\r
420         viewer = v;\r
421         if (viewer.IsEmpty() || (viewer.Left(1).Compare(_T("#"))==0))\r
422         {\r
423                 // use TortoiseUDiff\r
424                 viewer = CPathUtils::GetAppDirectory();\r
425                 viewer += _T("TortoiseUDiff.exe");\r
426                 // enquote the path to TortoiseUDiff\r
427                 viewer = _T("\"") + viewer + _T("\"");\r
428                 // add the params\r
429                 viewer = viewer + _T(" /patchfile:%1 /title:\"%title\"");\r
430 \r
431         }\r
432         if (viewer.Find(_T("%1"))>=0)\r
433         {\r
434                 if (viewer.Find(_T("\"%1\"")) >= 0)\r
435                         viewer.Replace(_T("%1"), patchfile);\r
436                 else\r
437                         viewer.Replace(_T("%1"), _T("\"") + patchfile + _T("\""));\r
438         }\r
439         else\r
440                 viewer += _T(" \"") + patchfile + _T("\"");\r
441         if (viewer.Find(_T("%title")) >= 0)\r
442         {\r
443                 viewer.Replace(_T("%title"), title);\r
444         }\r
445 \r
446         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
447         {\r
448                 return FALSE;\r
449         }\r
450         return TRUE;\r
451 }\r
452 \r
453 BOOL CAppUtils::StartTextViewer(CString file)\r
454 {\r
455         CString viewer;\r
456         CRegString txt = CRegString(_T(".txt\\"), _T(""), FALSE, HKEY_CLASSES_ROOT);\r
457         viewer = txt;\r
458         viewer = viewer + _T("\\Shell\\Open\\Command\\");\r
459         CRegString txtexe = CRegString(viewer, _T(""), FALSE, HKEY_CLASSES_ROOT);\r
460         viewer = txtexe;\r
461 \r
462         DWORD len = ExpandEnvironmentStrings(viewer, NULL, 0);\r
463         TCHAR * buf = new TCHAR[len+1];\r
464         ExpandEnvironmentStrings(viewer, buf, len);\r
465         viewer = buf;\r
466         delete [] buf;\r
467         len = ExpandEnvironmentStrings(file, NULL, 0);\r
468         buf = new TCHAR[len+1];\r
469         ExpandEnvironmentStrings(file, buf, len);\r
470         file = buf;\r
471         delete [] buf;\r
472         file = _T("\"")+file+_T("\"");\r
473         if (viewer.IsEmpty())\r
474         {\r
475                 OPENFILENAME ofn = {0};                         // common dialog box structure\r
476                 TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
477                 // Initialize OPENFILENAME\r
478                 ofn.lStructSize = sizeof(OPENFILENAME);\r
479                 ofn.hwndOwner = NULL;\r
480                 ofn.lpstrFile = szFile;\r
481                 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
482                 CString sFilter;\r
483                 sFilter.LoadString(IDS_PROGRAMSFILEFILTER);\r
484                 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];\r
485                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
486                 // Replace '|' delimiters with '\0's\r
487                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
488                 while (ptr != pszFilters)\r
489                 {\r
490                         if (*ptr == '|')\r
491                                 *ptr = '\0';\r
492                         ptr--;\r
493                 }\r
494                 ofn.lpstrFilter = pszFilters;\r
495                 ofn.nFilterIndex = 1;\r
496                 ofn.lpstrFileTitle = NULL;\r
497                 ofn.nMaxFileTitle = 0;\r
498                 ofn.lpstrInitialDir = NULL;\r
499                 CString temp;\r
500                 temp.LoadString(IDS_UTILS_SELECTTEXTVIEWER);\r
501                 CStringUtils::RemoveAccelerators(temp);\r
502                 ofn.lpstrTitle = temp;\r
503                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;\r
504 \r
505                 // Display the Open dialog box. \r
506 \r
507                 if (GetOpenFileName(&ofn)==TRUE)\r
508                 {\r
509                         delete [] pszFilters;\r
510                         viewer = CString(ofn.lpstrFile);\r
511                 }\r
512                 else\r
513                 {\r
514                         delete [] pszFilters;\r
515                         return FALSE;\r
516                 }\r
517         }\r
518         if (viewer.Find(_T("\"%1\"")) >= 0)\r
519         {\r
520                 viewer.Replace(_T("\"%1\""), file);\r
521         }\r
522         else if (viewer.Find(_T("%1")) >= 0)\r
523         {\r
524                 viewer.Replace(_T("%1"),  file);\r
525         }\r
526         else\r
527         {\r
528                 viewer += _T(" ");\r
529                 viewer += file;\r
530         }\r
531 \r
532         if(!LaunchApplication(viewer, IDS_ERR_TEXTVIEWSTART, false))\r
533         {\r
534                 return FALSE;\r
535         }\r
536         return TRUE;\r
537 }\r
538 \r
539 BOOL CAppUtils::CheckForEmptyDiff(const CTGitPath& sDiffPath)\r
540 {\r
541         DWORD length = 0;\r
542         HANDLE hFile = ::CreateFile(sDiffPath.GetWinPath(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);\r
543         if (hFile == INVALID_HANDLE_VALUE)\r
544                 return TRUE;\r
545         length = ::GetFileSize(hFile, NULL);\r
546         ::CloseHandle(hFile);\r
547         if (length < 4)\r
548                 return TRUE;\r
549         return FALSE;\r
550 \r
551 }\r
552 \r
553 void CAppUtils::CreateFontForLogs(CFont& fontToCreate)\r
554 {\r
555         LOGFONT logFont;\r
556         HDC hScreenDC = ::GetDC(NULL);\r
557         logFont.lfHeight         = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8), GetDeviceCaps(hScreenDC, LOGPIXELSY), 72);\r
558         ::ReleaseDC(NULL, hScreenDC);\r
559         logFont.lfWidth          = 0;\r
560         logFont.lfEscapement     = 0;\r
561         logFont.lfOrientation    = 0;\r
562         logFont.lfWeight         = FW_NORMAL;\r
563         logFont.lfItalic         = 0;\r
564         logFont.lfUnderline      = 0;\r
565         logFont.lfStrikeOut      = 0;\r
566         logFont.lfCharSet        = DEFAULT_CHARSET;\r
567         logFont.lfOutPrecision   = OUT_DEFAULT_PRECIS;\r
568         logFont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;\r
569         logFont.lfQuality        = DRAFT_QUALITY;\r
570         logFont.lfPitchAndFamily = FF_DONTCARE | FIXED_PITCH;\r
571         _tcscpy_s(logFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")));\r
572         VERIFY(fontToCreate.CreateFontIndirect(&logFont));\r
573 }\r
574 \r
575 bool CAppUtils::LaunchApplication(const CString& sCommandLine, UINT idErrMessageFormat, bool bWaitForStartup)\r
576 {\r
577         STARTUPINFO startup;\r
578         PROCESS_INFORMATION process;\r
579         memset(&startup, 0, sizeof(startup));\r
580         startup.cb = sizeof(startup);\r
581         memset(&process, 0, sizeof(process));\r
582 \r
583         CString cleanCommandLine(sCommandLine);\r
584 \r
585         if (CreateProcess(NULL, const_cast<TCHAR*>((LPCTSTR)cleanCommandLine), NULL, NULL, FALSE, 0, 0, sOrigCWD, &startup, &process)==0)\r
586         {\r
587                 if(idErrMessageFormat != 0)\r
588                 {\r
589                         LPVOID lpMsgBuf;\r
590                         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
591                                 FORMAT_MESSAGE_FROM_SYSTEM | \r
592                                 FORMAT_MESSAGE_IGNORE_INSERTS,\r
593                                 NULL,\r
594                                 GetLastError(),\r
595                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
596                                 (LPTSTR) &lpMsgBuf,\r
597                                 0,\r
598                                 NULL \r
599                                 );\r
600                         CString temp;\r
601                         temp.Format(idErrMessageFormat, lpMsgBuf);\r
602                         CMessageBox::Show(NULL, temp, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);\r
603                         LocalFree( lpMsgBuf );\r
604                 }\r
605                 return false;\r
606         }\r
607 \r
608         if (bWaitForStartup)\r
609         {\r
610                 WaitForInputIdle(process.hProcess, 10000);\r
611         }\r
612 \r
613         CloseHandle(process.hThread);\r
614         CloseHandle(process.hProcess);\r
615         return true;\r
616 }\r
617 bool CAppUtils::LaunchPAgent(CString *keyfile,CString * pRemote)\r
618 {\r
619         CString key,remote;\r
620         CString cmd,out;\r
621         if( pRemote == NULL)\r
622         {\r
623                 remote=_T("origin");\r
624         }else\r
625         {\r
626                 remote=*pRemote;\r
627         }\r
628         if(keyfile == NULL)\r
629         {\r
630                 cmd.Format(_T("git.exe config remote.%s.puttykeyfile"),remote);\r
631                 g_Git.Run(cmd,&key,CP_ACP);\r
632                 int start=0;\r
633                 key = key.Tokenize(_T("\n"),start);\r
634         }\r
635         else\r
636                 key=*keyfile;\r
637 \r
638         if(key.IsEmpty())\r
639                 return false;\r
640 \r
641         CString proc=CPathUtils::GetAppDirectory();\r
642     proc += _T("pageant.exe \"");\r
643         proc += key;\r
644         proc += _T("\"");\r
645 \r
646     return LaunchApplication(proc, IDS_ERR_EXTDIFFSTART, false);\r
647 }\r
648 bool CAppUtils::LaunchRemoteSetting()\r
649 {\r
650     CString proc=CPathUtils::GetAppDirectory();\r
651     proc += _T("TortoiseProc.exe /command:settings");\r
652     proc += _T(" /path:\"");\r
653     proc += g_Git.m_CurrentDir;\r
654     proc += _T("\" /page:remote");\r
655     return LaunchApplication(proc, IDS_ERR_EXTDIFFSTART, false);\r
656 }\r
657 /**\r
658 * Launch the external blame viewer\r
659 */\r
660 bool CAppUtils::LaunchTortoiseBlame(const CString& sBlameFile,CString Rev,const CString& sParams)\r
661 {\r
662         CString viewer = CPathUtils::GetAppDirectory();\r
663         viewer += _T("TortoiseGitBlame.exe");\r
664         viewer += _T(" \"") + sBlameFile + _T("\"");\r
665         //viewer += _T(" \"") + sLogFile + _T("\"");\r
666         //viewer += _T(" \"") + sOriginalFile + _T("\"");\r
667         if(!Rev.IsEmpty())\r
668                 viewer += CString(_T(" /rev:"))+Rev;\r
669         viewer += _T(" ")+sParams;\r
670         \r
671         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, false);\r
672 }\r
673 \r
674 bool CAppUtils::FormatTextInRichEditControl(CWnd * pWnd)\r
675 {\r
676         CString sText;\r
677         if (pWnd == NULL)\r
678                 return false;\r
679         bool bStyled = false;\r
680         pWnd->GetWindowText(sText);\r
681         // the rich edit control doesn't count the CR char!\r
682         // to be exact: CRLF is treated as one char.\r
683         sText.Replace(_T("\r"), _T(""));\r
684 \r
685         // style each line separately\r
686         int offset = 0;\r
687         int nNewlinePos;\r
688         do \r
689         {\r
690                 nNewlinePos = sText.Find('\n', offset);\r
691                 CString sLine = sText.Mid(offset);\r
692                 if (nNewlinePos>=0)\r
693                         sLine = sLine.Left(nNewlinePos-offset);\r
694                 int start = 0;\r
695                 int end = 0;\r
696                 while (FindStyleChars(sLine, '*', start, end))\r
697                 {\r
698                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
699                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
700                         CHARFORMAT2 format;\r
701                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
702                         format.cbSize = sizeof(CHARFORMAT2);\r
703                         format.dwMask = CFM_BOLD;\r
704                         format.dwEffects = CFE_BOLD;\r
705                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
706                         bStyled = true;\r
707                         start = end;\r
708                 }\r
709                 start = 0;\r
710                 end = 0;\r
711                 while (FindStyleChars(sLine, '^', start, end))\r
712                 {\r
713                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
714                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
715                         CHARFORMAT2 format;\r
716                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
717                         format.cbSize = sizeof(CHARFORMAT2);\r
718                         format.dwMask = CFM_ITALIC;\r
719                         format.dwEffects = CFE_ITALIC;\r
720                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
721                         bStyled = true;\r
722                         start = end;\r
723                 }\r
724                 start = 0;\r
725                 end = 0;\r
726                 while (FindStyleChars(sLine, '_', start, end))\r
727                 {\r
728                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
729                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
730                         CHARFORMAT2 format;\r
731                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
732                         format.cbSize = sizeof(CHARFORMAT2);\r
733                         format.dwMask = CFM_UNDERLINE;\r
734                         format.dwEffects = CFE_UNDERLINE;\r
735                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
736                         bStyled = true;\r
737                         start = end;\r
738                 }\r
739                 offset = nNewlinePos+1;\r
740         } while(nNewlinePos>=0);\r
741         return bStyled; \r
742 }\r
743 \r
744 bool CAppUtils::FindStyleChars(const CString& sText, TCHAR stylechar, int& start, int& end)\r
745 {\r
746         int i=start;\r
747         bool bFoundMarker = false;\r
748         // find a starting marker\r
749         while (sText[i] != 0)\r
750         {\r
751                 if (sText[i] == stylechar)\r
752                 {\r
753                         if (((i+1)<sText.GetLength())&&(IsCharAlphaNumeric(sText[i+1])) &&\r
754                                 (((i>0)&&(!IsCharAlphaNumeric(sText[i-1])))||(i==0)))\r
755                         {\r
756                                 start = i+1;\r
757                                 i++;\r
758                                 bFoundMarker = true;\r
759                                 break;\r
760                         }\r
761                 }\r
762                 i++;\r
763         }\r
764         if (!bFoundMarker)\r
765                 return false;\r
766         // find ending marker\r
767         bFoundMarker = false;\r
768         while (sText[i] != 0)\r
769         {\r
770                 if (sText[i] == stylechar)\r
771                 {\r
772                         if ((IsCharAlphaNumeric(sText[i-1])) &&\r
773                                 ((((i+1)<sText.GetLength())&&(!IsCharAlphaNumeric(sText[i+1])))||(i+1)==sText.GetLength()))\r
774                         {\r
775                                 end = i;\r
776                                 i++;\r
777                                 bFoundMarker = true;\r
778                                 break;\r
779                         }\r
780                 }\r
781                 i++;\r
782         }\r
783         return bFoundMarker;\r
784 }\r
785 \r
786 bool CAppUtils::BrowseRepository(CHistoryCombo& combo, CWnd * pParent, GitRev& rev)\r
787 {\r
788 #if 0\r
789         CString strUrl;\r
790         combo.GetWindowText(strUrl);\r
791         strUrl.Replace('\\', '/');\r
792         strUrl.Replace(_T("%"), _T("%25"));\r
793         strUrl = CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(strUrl)));\r
794         if (strUrl.Left(7) == _T("file://"))\r
795         {\r
796                 CString strFile(strUrl);\r
797                 Git::UrlToPath(strFile);\r
798 \r
799                 Git svn;\r
800                 if (svn.IsRepository(CTGitPath(strFile)))\r
801                 {\r
802                         // browse repository - show repository browser\r
803                         Git::preparePath(strUrl);\r
804                         CRepositoryBrowser browser(strUrl, rev, pParent);\r
805                         if (browser.DoModal() == IDOK)\r
806                         {\r
807                                 combo.SetCurSel(-1);\r
808                                 combo.SetWindowText(browser.GetPath());\r
809                                 rev = browser.GetRevision();\r
810                                 return true;\r
811                         }\r
812                 }\r
813                 else\r
814                 {\r
815                         // browse local directories\r
816                         CBrowseFolder folderBrowser;\r
817                         folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
818                         // remove the 'file:///' so the shell can recognize the local path\r
819                         Git::UrlToPath(strUrl);\r
820                         if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
821                         {\r
822                                 Git::PathToUrl(strUrl);\r
823 \r
824                                 combo.SetCurSel(-1);\r
825                                 combo.SetWindowText(strUrl);\r
826                                 return true;\r
827                         }\r
828                 }\r
829         }\r
830         else if ((strUrl.Left(7) == _T("http://")\r
831                 ||(strUrl.Left(8) == _T("https://"))\r
832                 ||(strUrl.Left(6) == _T("svn://"))\r
833                 ||(strUrl.Left(4) == _T("svn+"))) && strUrl.GetLength() > 6)\r
834         {\r
835                 // browse repository - show repository browser\r
836                 CRepositoryBrowser browser(strUrl, rev, pParent);\r
837                 if (browser.DoModal() == IDOK)\r
838                 {\r
839                         combo.SetCurSel(-1);\r
840                         combo.SetWindowText(browser.GetPath());\r
841                         rev = browser.GetRevision();\r
842                         return true;\r
843                 }\r
844         }\r
845         else\r
846         {\r
847                 // browse local directories\r
848                 CBrowseFolder folderBrowser;\r
849                 folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
850                 if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
851                 {\r
852                         Git::PathToUrl(strUrl);\r
853 \r
854                         combo.SetCurSel(-1);\r
855                         combo.SetWindowText(strUrl);\r
856                         return true;\r
857                 }\r
858         }\r
859 #endif\r
860         return false;\r
861 }\r
862 \r
863 bool CAppUtils::FileOpenSave(CString& path, int * filterindex, UINT title, UINT filter, bool bOpen, HWND hwndOwner)\r
864 {\r
865         OPENFILENAME ofn = {0};                         // common dialog box structure\r
866         TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
867         ofn.lStructSize = sizeof(OPENFILENAME);\r
868         ofn.hwndOwner = hwndOwner;\r
869         _tcscpy_s(szFile, MAX_PATH, (LPCTSTR)path);\r
870         ofn.lpstrFile = szFile;\r
871         ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
872         CString sFilter;\r
873         TCHAR * pszFilters = NULL;\r
874         if (filter)\r
875         {\r
876                 sFilter.LoadString(filter);\r
877                 pszFilters = new TCHAR[sFilter.GetLength()+4];\r
878                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
879                 // Replace '|' delimiters with '\0's\r
880                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
881                 while (ptr != pszFilters)\r
882                 {\r
883                         if (*ptr == '|')\r
884                                 *ptr = '\0';\r
885                         ptr--;\r
886                 }\r
887                 ofn.lpstrFilter = pszFilters;\r
888         }\r
889         ofn.nFilterIndex = 1;\r
890         ofn.lpstrFileTitle = NULL;\r
891         ofn.nMaxFileTitle = 0;\r
892         ofn.lpstrInitialDir = NULL;\r
893         CString temp;\r
894         if (title)\r
895         {\r
896                 temp.LoadString(title);\r
897                 CStringUtils::RemoveAccelerators(temp);\r
898         }\r
899         ofn.lpstrTitle = temp;\r
900         if (bOpen)\r
901                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;\r
902         else\r
903                 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER;\r
904 \r
905 \r
906         // Display the Open dialog box. \r
907         bool bRet = false;\r
908         if (bOpen)\r
909         {\r
910                 bRet = !!GetOpenFileName(&ofn);\r
911         }\r
912         else\r
913         {\r
914                 bRet = !!GetSaveFileName(&ofn);\r
915         }\r
916         if (bRet)\r
917         {\r
918                 if (pszFilters)\r
919                         delete [] pszFilters;\r
920                 path = CString(ofn.lpstrFile);\r
921                 if (filterindex)\r
922                         *filterindex = ofn.nFilterIndex;\r
923                 return true;\r
924         }\r
925         if (pszFilters)\r
926                 delete [] pszFilters;\r
927         return false;\r
928 }\r
929 \r
930 bool CAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID, int width /* = 128 */, int height /* = 128 */)\r
931 {\r
932         ListView_SetTextBkColor(hListCtrl, CLR_NONE);\r
933         COLORREF bkColor = ListView_GetBkColor(hListCtrl);\r
934         // create a bitmap from the icon\r
935         HICON hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), IMAGE_ICON, width, height, LR_DEFAULTCOLOR);\r
936         if (!hIcon)\r
937                 return false;\r
938 \r
939         RECT rect = {0};\r
940         rect.right = width;\r
941         rect.bottom = height;\r
942         HBITMAP bmp = NULL;\r
943 \r
944         HWND desktop = ::GetDesktopWindow();\r
945         if (desktop)\r
946         {\r
947                 HDC screen_dev = ::GetDC(desktop);\r
948                 if (screen_dev)\r
949                 {\r
950                         // Create a compatible DC\r
951                         HDC dst_hdc = ::CreateCompatibleDC(screen_dev);\r
952                         if (dst_hdc)\r
953                         {\r
954                                 // Create a new bitmap of icon size\r
955                                 bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);\r
956                                 if (bmp)\r
957                                 {\r
958                                         // Select it into the compatible DC\r
959                                         HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);\r
960                                         // Fill the background of the compatible DC with the given color\r
961                                         ::SetBkColor(dst_hdc, bkColor);\r
962                                         ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
963 \r
964                                         // Draw the icon into the compatible DC\r
965                                         ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);\r
966                                         ::SelectObject(dst_hdc, old_dst_bmp);\r
967                                 }\r
968                                 ::DeleteDC(dst_hdc);\r
969                         }\r
970                 }\r
971                 ::ReleaseDC(desktop, screen_dev); \r
972         }\r
973 \r
974         // Restore settings\r
975         DestroyIcon(hIcon);\r
976 \r
977         if (bmp == NULL)\r
978                 return false;\r
979 \r
980         LVBKIMAGE lv;\r
981         lv.ulFlags = LVBKIF_TYPE_WATERMARK;\r
982         lv.hbm = bmp;\r
983         lv.xOffsetPercent = 100;\r
984         lv.yOffsetPercent = 100;\r
985         ListView_SetBkImage(hListCtrl, &lv);\r
986         return true;\r
987 }\r
988 \r
989 CString CAppUtils::GetProjectNameFromURL(CString url)\r
990 {\r
991         CString name;\r
992         while (name.IsEmpty() || (name.CompareNoCase(_T("branches"))==0) ||\r
993                 (name.CompareNoCase(_T("tags"))==0) ||\r
994                 (name.CompareNoCase(_T("trunk"))==0))\r
995         {\r
996                 name = url.Mid(url.ReverseFind('/')+1);\r
997                 url = url.Left(url.ReverseFind('/'));\r
998         }\r
999         if ((name.Compare(_T("svn")) == 0)||(name.Compare(_T("svnroot")) == 0))\r
1000         {\r
1001                 // a name of svn or svnroot indicates that it's not really the project name. In that\r
1002                 // case, we try the first part of the URL\r
1003                 // of course, this won't work in all cases (but it works for Google project hosting)\r
1004                 url.Replace(_T("http://"), _T(""));\r
1005                 url.Replace(_T("https://"), _T(""));\r
1006                 url.Replace(_T("svn://"), _T(""));\r
1007                 url.Replace(_T("svn+ssh://"), _T(""));\r
1008                 url.TrimLeft(_T("/"));\r
1009                 name = url.Left(url.Find('.'));\r
1010         }\r
1011         return name;\r
1012 }\r
1013 \r
1014 bool CAppUtils::StartShowUnifiedDiff(HWND hWnd, const CTGitPath& url1, const git_revnum_t& rev1, \r
1015                                                                                                 const CTGitPath& url2, const git_revnum_t& rev2, \r
1016                                                                          //const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */,  \r
1017                                                                                                 bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool /* blame = false */)\r
1018 {\r
1019 \r
1020         CString tempfile=GetTempFile();\r
1021         CString cmd;\r
1022         if(rev1 == GitRev::GetWorkingCopy())\r
1023         {\r
1024                 cmd.Format(_T("git.exe diff --stat -p %s"),rev2);\r
1025         }else\r
1026         {       \r
1027                 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),rev1,rev2);\r
1028         }\r
1029         g_Git.RunLogFile(cmd,tempfile);\r
1030         CAppUtils::StartUnifiedDiffViewer(tempfile,rev1.Left(6)+_T(":")+rev2.Left(6));\r
1031 \r
1032 \r
1033 #if 0\r
1034         CString sCmd;\r
1035         sCmd.Format(_T("%s /command:showcompare /unified"),\r
1036                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
1037         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
1038         if (rev1.IsValid())\r
1039                 sCmd += _T(" /revision1:") + rev1.ToString();\r
1040         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
1041         if (rev2.IsValid())\r
1042                 sCmd += _T(" /revision2:") + rev2.ToString();\r
1043         if (peg.IsValid())\r
1044                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
1045         if (headpeg.IsValid())\r
1046                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
1047 \r
1048         if (bAlternateDiff)\r
1049                 sCmd += _T(" /alternatediff");\r
1050 \r
1051         if (bIgnoreAncestry)\r
1052                 sCmd += _T(" /ignoreancestry");\r
1053 \r
1054         if (hWnd)\r
1055         {\r
1056                 sCmd += _T(" /hwnd:");\r
1057                 TCHAR buf[30];\r
1058                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
1059                 sCmd += buf;\r
1060         }\r
1061 \r
1062         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1063 #endif\r
1064         return TRUE;\r
1065 }\r
1066 \r
1067 bool CAppUtils::StartShowCompare(HWND hWnd, const CTGitPath& url1, const GitRev& rev1, \r
1068                                                                  const CTGitPath& url2, const GitRev& rev2, \r
1069                                                                  const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */, \r
1070                                                                  bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool blame /* = false */)\r
1071 {\r
1072 #if 0\r
1073         CString sCmd;\r
1074         sCmd.Format(_T("%s /command:showcompare"),\r
1075                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
1076         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
1077         if (rev1.IsValid())\r
1078                 sCmd += _T(" /revision1:") + rev1.ToString();\r
1079         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
1080         if (rev2.IsValid())\r
1081                 sCmd += _T(" /revision2:") + rev2.ToString();\r
1082         if (peg.IsValid())\r
1083                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
1084         if (headpeg.IsValid())\r
1085                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
1086         if (bAlternateDiff)\r
1087                 sCmd += _T(" /alternatediff");\r
1088         if (bIgnoreAncestry)\r
1089                 sCmd += _T(" /ignoreancestry");\r
1090         if (blame)\r
1091                 sCmd += _T(" /blame");\r
1092 \r
1093         if (hWnd)\r
1094         {\r
1095                 sCmd += _T(" /hwnd:");\r
1096                 TCHAR buf[30];\r
1097                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
1098                 sCmd += buf;\r
1099         }\r
1100 \r
1101         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1102 #endif\r
1103         return true;\r
1104 }\r
1105 \r
1106 bool CAppUtils::Export(CString *BashHash)\r
1107 {\r
1108         bool bRet = false;\r
1109 \r
1110                 // ask from where the export has to be done\r
1111         CExportDlg dlg;\r
1112         if(BashHash)\r
1113                 dlg.m_Revision=*BashHash;\r
1114 \r
1115         if (dlg.DoModal() == IDOK)\r
1116         {\r
1117                 CString cmd;\r
1118                 cmd.Format(_T("git.exe archive --format=zip --verbose %s"),\r
1119                                         dlg.m_VersionName);\r
1120 \r
1121                 //g_Git.RunLogFile(cmd,dlg.m_strExportDirectory);\r
1122                 CProgressDlg pro;\r
1123                 pro.m_GitCmd=cmd;\r
1124                 pro.m_LogFile=dlg.m_strExportDirectory;\r
1125                 pro.DoModal();\r
1126                 return TRUE;\r
1127         }\r
1128         return bRet;\r
1129 }\r
1130 \r
1131 bool CAppUtils::CreateBranchTag(bool IsTag,CString *CommitHash)\r
1132 {\r
1133         CCreateBranchTagDlg dlg;\r
1134         dlg.m_bIsTag=IsTag;\r
1135         if(CommitHash)\r
1136                 dlg.m_Base = *CommitHash;\r
1137 \r
1138         if(dlg.DoModal()==IDOK)\r
1139         {\r
1140                 CString cmd;\r
1141                 CString force;\r
1142                 CString track;\r
1143                 if(dlg.m_bTrack)\r
1144                         track=_T(" --track ");\r
1145 \r
1146                 if(dlg.m_bForce)\r
1147                         force=_T(" -f ");\r
1148 \r
1149                 if(IsTag)\r
1150                 {\r
1151                         cmd.Format(_T("git.exe tag %s %s %s %s"),\r
1152                                 track,\r
1153                                 force,\r
1154                                 dlg.m_BranchTagName,\r
1155                                 dlg.m_VersionName\r
1156                                 );\r
1157 \r
1158         \r
1159                 }else\r
1160                 {\r
1161                         cmd.Format(_T("git.exe branch %s %s %s %s"),\r
1162                                 track,\r
1163                                 force,\r
1164                                 dlg.m_BranchTagName,\r
1165                                 dlg.m_VersionName\r
1166                                 );\r
1167                 }\r
1168                 CString out;\r
1169                 if(g_Git.Run(cmd,&out,CP_UTF8))\r
1170                 {\r
1171                         CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1172                 }\r
1173                 if( !IsTag  &&  dlg.m_bSwitch )\r
1174                 {\r
1175                         // it is a new branch and the user has requested to switch to it\r
1176                         cmd.Format(_T("git.exe checkout %s"), dlg.m_BranchTagName);\r
1177                         g_Git.Run(cmd,&out,CP_UTF8);\r
1178                         CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1179                 }\r
1180                 \r
1181                 return TRUE;\r
1182                 \r
1183         }\r
1184         return FALSE;\r
1185 }\r
1186 \r
1187 bool CAppUtils::Switch(CString *CommitHash)\r
1188 {\r
1189         CGitSwitchDlg dlg;\r
1190         if(CommitHash)\r
1191                 dlg.m_Base=*CommitHash;\r
1192         \r
1193         if (dlg.DoModal() == IDOK)\r
1194         {\r
1195                 CString cmd;\r
1196                 CString track;\r
1197                 CString base;\r
1198                 CString force;\r
1199                 CString branch;\r
1200 \r
1201                 if(dlg.m_bBranch)\r
1202                         branch.Format(_T("-b %s"),dlg.m_NewBranch);\r
1203                 if(dlg.m_bForce)\r
1204                         force=_T("-f");\r
1205                 if(dlg.m_bTrack)\r
1206                         track=_T("--track");\r
1207 \r
1208                 cmd.Format(_T("git.exe checkout %s %s %s %s"),\r
1209                          force,\r
1210                          track,\r
1211                          branch,\r
1212                          dlg.m_VersionName);\r
1213 \r
1214                 CProgressDlg progress;\r
1215                 progress.m_GitCmd=cmd;\r
1216                 if(progress.DoModal()==IDOK)\r
1217                         return TRUE;\r
1218 \r
1219         }\r
1220         return FALSE;\r
1221 }\r
1222 \r
1223 bool CAppUtils::IgnoreFile(CTGitPathList &path,bool IsMask)\r
1224 {\r
1225         CString ignorefile;\r
1226         ignorefile=g_Git.m_CurrentDir+_T("\\");\r
1227 \r
1228         if(IsMask)\r
1229         {\r
1230                 ignorefile+=path.GetCommonRoot().GetDirectory().GetWinPathString()+_T("\\.gitignore");\r
1231 \r
1232         }else\r
1233         {\r
1234                 ignorefile+=_T("\\.gitignore");\r
1235         }\r
1236 \r
1237         CStdioFile file;\r
1238         if(!file.Open(ignorefile,CFile::modeCreate|CFile::modeReadWrite|CFile::modeNoTruncate))\r
1239         {\r
1240                 CMessageBox::Show(NULL,ignorefile+_T(" Open Failure"),_T("TortoiseGit"),MB_OK);\r
1241                 return FALSE;\r
1242         }\r
1243 \r
1244         CString ignorelist;\r
1245         CString mask;\r
1246         try\r
1247         {\r
1248                 //file.ReadString(ignorelist);\r
1249                 file.SeekToEnd();\r
1250                 for(int i=0;i<path.GetCount();i++)\r
1251                 {\r
1252                         if(IsMask)\r
1253                         {\r
1254                                 mask=_T("*")+path[i].GetFileExtension();\r
1255                                 if(ignorelist.Find(mask)<0)\r
1256                                         ignorelist+=_T("\n")+mask;\r
1257                                 \r
1258                         }else\r
1259                         {\r
1260                                 ignorelist+=_T("\n/")+path[i].GetGitPathString();\r
1261                         }\r
1262                 }\r
1263                 file.WriteString(ignorelist);\r
1264 \r
1265                 file.Close();\r
1266 \r
1267         }catch(...)\r
1268         {\r
1269                 file.Close();\r
1270                 return FALSE;\r
1271         }\r
1272         \r
1273         return TRUE;\r
1274 }\r
1275 \r
1276 \r
1277 bool CAppUtils::GitReset(CString *CommitHash,int type)\r
1278 {\r
1279         CResetDlg dlg;\r
1280         dlg.m_ResetType=type;\r
1281         if (dlg.DoModal() == IDOK)\r
1282         {\r
1283                 CString cmd;\r
1284                 CString type;\r
1285                 switch(dlg.m_ResetType)\r
1286                 {\r
1287                 case 0:\r
1288                         type=_T("--soft");\r
1289                         break;\r
1290                 case 1:\r
1291                         type=_T("--mixed");\r
1292                         break;\r
1293                 case 2:\r
1294                         type=_T("--hard");\r
1295                         break;\r
1296                 default:\r
1297                         type=_T("--mixed");\r
1298                         break;\r
1299                 }\r
1300                 cmd.Format(_T("git.exe reset %s %s"),type, *CommitHash);\r
1301 \r
1302                 CProgressDlg progress;\r
1303                 progress.m_GitCmd=cmd;\r
1304                 if(progress.DoModal()==IDOK)\r
1305                         return TRUE;\r
1306 \r
1307         }\r
1308         return FALSE;\r
1309 }\r
1310 \r
1311 void CAppUtils::DescribeFile(bool mode, bool base,CString &descript)\r
1312 {\r
1313         if(mode == FALSE)\r
1314         {\r
1315                 descript=_T("Deleted");\r
1316                 return;\r
1317         }\r
1318         if(base)\r
1319         {\r
1320                 descript=_T("Modified");\r
1321                 return;\r
1322         }\r
1323         descript=_T("Created");\r
1324         return;\r
1325 }\r
1326 \r
1327 CString CAppUtils::GetMergeTempFile(CString type,CTGitPath &merge)\r
1328 {\r
1329         CString file;\r
1330         file=g_Git.m_CurrentDir+_T("\\")+merge.GetDirectory().GetWinPathString()+_T("\\")+merge.GetFilename()+_T(".")+type+merge.GetFileExtension();\r
1331 \r
1332         return file;\r
1333 }\r
1334 \r
1335 bool CAppUtils::ConflictEdit(CTGitPath &path,bool bAlternativeTool)\r
1336 {\r
1337         bool bRet = false;\r
1338 \r
1339         CTGitPath merge=path;\r
1340         CTGitPath directory = merge.GetDirectory();\r
1341         \r
1342         \r
1343 \r
1344         // we have the conflicted file (%merged)\r
1345         // now look for the other required files\r
1346         //GitStatus stat;\r
1347         //stat.GetStatus(merge);\r
1348         //if (stat.status == NULL)\r
1349         //      return false;\r
1350 \r
1351         BYTE_VECTOR vector;\r
1352 \r
1353         CString cmd;\r
1354         cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),merge.GetGitPathString());\r
1355 \r
1356         if(g_Git.Run(cmd,&vector))\r
1357         {\r
1358                 return FALSE;\r
1359         }\r
1360 \r
1361         CTGitPathList list;\r
1362         list.ParserFromLsFile(vector);\r
1363 \r
1364         if(list.GetCount() == 0)\r
1365                 return FALSE;\r
1366 \r
1367         TCHAR szTempName[512];  \r
1368         GetTempFileName(_T(""),_T(""),0,szTempName);\r
1369         CString temp(szTempName);\r
1370         temp=temp.Mid(1,temp.GetLength()-5);\r
1371 \r
1372         CTGitPath theirs;\r
1373         CTGitPath mine;\r
1374         CTGitPath base;\r
1375 \r
1376         \r
1377         mine.SetFromGit(GetMergeTempFile(_T("LOCAL"),merge));\r
1378         theirs.SetFromGit(GetMergeTempFile(_T("REMOTE"),merge));\r
1379         base.SetFromGit(GetMergeTempFile(_T("BASE"),merge));\r
1380 \r
1381         CString format;\r
1382 \r
1383         format=_T("git.exe cat-file blob \":%d:%s\"");\r
1384         CFile tempfile;\r
1385         //create a empty file, incase stage is not three\r
1386         tempfile.Open(mine.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1387         tempfile.Close();\r
1388         tempfile.Open(theirs.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1389         tempfile.Close();\r
1390         tempfile.Open(base.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1391         tempfile.Close();\r
1392 \r
1393         bool b_base=false, b_local=false, b_remote=false;\r
1394 \r
1395         for(int i=0;i<list.GetCount();i++)\r
1396         {\r
1397                 CString cmd;\r
1398                 CString outfile;\r
1399                 cmd.Format(format,list[i].m_Stage,list[i].GetGitPathString());\r
1400                 \r
1401                 if( list[i].m_Stage == 1)\r
1402                 {\r
1403                         b_base = true;\r
1404                         outfile=base.GetWinPathString();\r
1405                 }\r
1406                 if( list[i].m_Stage == 2 )\r
1407                 {\r
1408                         b_local = true;\r
1409                         outfile=mine.GetWinPathString();\r
1410                 }\r
1411                 if( list[i].m_Stage == 3 )\r
1412                 {\r
1413                         b_remote = true;\r
1414                         outfile=theirs.GetWinPathString();\r
1415                 }       \r
1416                 g_Git.RunLogFile(cmd,outfile);\r
1417         }\r
1418 \r
1419         if(b_local && b_remote )\r
1420         {\r
1421                 merge.SetFromWin(g_Git.m_CurrentDir+_T("\\")+merge.GetWinPathString());\r
1422                 bRet = !!CAppUtils::StartExtMerge(base, theirs, mine, merge,_T("BASE"),_T("REMOTE"),_T("LOCAL"));\r
1423         \r
1424         }else\r
1425         {\r
1426                 CFile::Remove(mine.GetWinPathString());\r
1427                 CFile::Remove(theirs.GetWinPathString());\r
1428                 CFile::Remove(base.GetWinPathString());\r
1429 \r
1430                 CDeleteConflictDlg dlg;\r
1431                 DescribeFile(b_local, b_base,dlg.m_LocalStatus);\r
1432                 DescribeFile(b_remote,b_base,dlg.m_RemoteStatus);\r
1433                 dlg.m_bShowModifiedButton=b_base;\r
1434                 dlg.m_File=merge.GetGitPathString();\r
1435                 if(dlg.DoModal() == IDOK)\r
1436                 {\r
1437                         CString cmd,out;\r
1438                         if(dlg.m_bIsDelete)\r
1439                         {\r
1440                                 cmd.Format(_T("git.exe rm \"%s\""),merge.GetGitPathString());\r
1441                         }else\r
1442                                 cmd.Format(_T("git.exe add \"%s\""),merge.GetGitPathString());\r
1443 \r
1444                         if(g_Git.Run(cmd,&out,CP_ACP))\r
1445                         {\r
1446                                 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1447                                 return FALSE;\r
1448                         }\r
1449                         return TRUE;\r
1450                 }\r
1451                 else \r
1452                         return FALSE;\r
1453 \r
1454                 \r
1455 \r
1456         }\r
1457 \r
1458 #if 0\r
1459 \r
1460         CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool), \r
1461                         base, theirs, mine, merge);\r
1462 #endif\r
1463 #if 0\r
1464         if (stat.status->text_status == svn_wc_status_conflicted)\r
1465         {\r
1466                 // we have a text conflict, use our merge tool to resolve the conflict\r
1467 \r
1468                 CTSVNPath theirs(directory);\r
1469                 CTSVNPath mine(directory);\r
1470                 CTSVNPath base(directory);\r
1471                 bool bConflictData = false;\r
1472 \r
1473                 if ((stat.status->entry)&&(stat.status->entry->conflict_new))\r
1474                 {\r
1475                         theirs.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_new));\r
1476                         bConflictData = true;\r
1477                 }\r
1478                 if ((stat.status->entry)&&(stat.status->entry->conflict_old))\r
1479                 {\r
1480                         base.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_old));\r
1481                         bConflictData = true;\r
1482                 }\r
1483                 if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))\r
1484                 {\r
1485                         mine.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_wrk));\r
1486                         bConflictData = true;\r
1487                 }\r
1488                 else\r
1489                 {\r
1490                         mine = merge;\r
1491                 }\r
1492                 if (bConflictData)\r
1493                         bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool), \r
1494                                                                                                 base, theirs, mine, merge);\r
1495         }\r
1496 \r
1497         if (stat.status->prop_status == svn_wc_status_conflicted)\r
1498         {\r
1499                 // we have a property conflict\r
1500                 CTSVNPath prej(directory);\r
1501                 if ((stat.status->entry)&&(stat.status->entry->prejfile))\r
1502                 {\r
1503                         prej.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->prejfile));\r
1504                         // there's a problem: the prej file contains a _description_ of the conflict, and\r
1505                         // that description string might be translated. That means we have no way of parsing\r
1506                         // the file to find out the conflicting values.\r
1507                         // The only thing we can do: show a dialog with the conflict description, then\r
1508                         // let the user either accept the existing property or open the property edit dialog\r
1509                         // to manually change the properties and values. And a button to mark the conflict as\r
1510                         // resolved.\r
1511                         CEditPropConflictDlg dlg;\r
1512                         dlg.SetPrejFile(prej);\r
1513                         dlg.SetConflictedItem(merge);\r
1514                         bRet = (dlg.DoModal() != IDCANCEL);\r
1515                 }\r
1516         }\r
1517 \r
1518         if (stat.status->tree_conflict)\r
1519         {\r
1520                 // we have a tree conflict\r
1521                 SVNInfo info;\r
1522                 const SVNInfoData * pInfoData = info.GetFirstFileInfo(merge, SVNRev(), SVNRev());\r
1523                 if (pInfoData)\r
1524                 {\r
1525                         if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_text)\r
1526                         {\r
1527                                 CTSVNPath theirs(directory);\r
1528                                 CTSVNPath mine(directory);\r
1529                                 CTSVNPath base(directory);\r
1530                                 bool bConflictData = false;\r
1531 \r
1532                                 if (pInfoData->treeconflict_theirfile)\r
1533                                 {\r
1534                                         theirs.AppendPathString(pInfoData->treeconflict_theirfile);\r
1535                                         bConflictData = true;\r
1536                                 }\r
1537                                 if (pInfoData->treeconflict_basefile)\r
1538                                 {\r
1539                                         base.AppendPathString(pInfoData->treeconflict_basefile);\r
1540                                         bConflictData = true;\r
1541                                 }\r
1542                                 if (pInfoData->treeconflict_myfile)\r
1543                                 {\r
1544                                         mine.AppendPathString(pInfoData->treeconflict_myfile);\r
1545                                         bConflictData = true;\r
1546                                 }\r
1547                                 else\r
1548                                 {\r
1549                                         mine = merge;\r
1550                                 }\r
1551                                 if (bConflictData)\r
1552                                         bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool),\r
1553                                                                                                                 base, theirs, mine, merge);\r
1554                         }\r
1555                         else if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_tree)\r
1556                         {\r
1557                                 CString sConflictAction;\r
1558                                 CString sConflictReason;\r
1559                                 CString sResolveTheirs;\r
1560                                 CString sResolveMine;\r
1561                                 CTSVNPath treeConflictPath = CTSVNPath(pInfoData->treeconflict_path);\r
1562                                 CString sItemName = treeConflictPath.GetUIFileOrDirectoryName();\r
1563                                 \r
1564                                 if (pInfoData->treeconflict_nodekind == svn_node_file)\r
1565                                 {\r
1566                                         switch (pInfoData->treeconflict_operation)\r
1567                                         {\r
1568                                         case svn_wc_operation_update:\r
1569                                                 switch (pInfoData->treeconflict_action)\r
1570                                                 {\r
1571                                                 case svn_wc_conflict_action_edit:\r
1572                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEEDIT, (LPCTSTR)sItemName);\r
1573                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1574                                                         break;\r
1575                                                 case svn_wc_conflict_action_add:\r
1576                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEADD, (LPCTSTR)sItemName);\r
1577                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1578                                                         break;\r
1579                                                 case svn_wc_conflict_action_delete:\r
1580                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEDELETE, (LPCTSTR)sItemName);\r
1581                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1582                                                         break;\r
1583                                                 }\r
1584                                                 break;\r
1585                                         case svn_wc_operation_switch:\r
1586                                                 switch (pInfoData->treeconflict_action)\r
1587                                                 {\r
1588                                                 case svn_wc_conflict_action_edit:\r
1589                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHEDIT, (LPCTSTR)sItemName);\r
1590                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1591                                                         break;\r
1592                                                 case svn_wc_conflict_action_add:\r
1593                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHADD, (LPCTSTR)sItemName);\r
1594                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1595                                                         break;\r
1596                                                 case svn_wc_conflict_action_delete:\r
1597                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHDELETE, (LPCTSTR)sItemName);\r
1598                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1599                                                         break;\r
1600                                                 }\r
1601                                                 break;\r
1602                                         case svn_wc_operation_merge:\r
1603                                                 switch (pInfoData->treeconflict_action)\r
1604                                                 {\r
1605                                                 case svn_wc_conflict_action_edit:\r
1606                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEEDIT, (LPCTSTR)sItemName);\r
1607                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1608                                                         break;\r
1609                                                 case svn_wc_conflict_action_add:\r
1610                                                         sResolveTheirs.Format(IDS_TREECONFLICT_FILEMERGEADD, (LPCTSTR)sItemName);\r
1611                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1612                                                         break;\r
1613                                                 case svn_wc_conflict_action_delete:\r
1614                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEDELETE, (LPCTSTR)sItemName);\r
1615                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1616                                                         break;\r
1617                                                 }\r
1618                                                 break;\r
1619                                         }\r
1620                                 }\r
1621                                 else if (pInfoData->treeconflict_nodekind == svn_node_dir)\r
1622                                 {\r
1623                                         switch (pInfoData->treeconflict_operation)\r
1624                                         {\r
1625                                         case svn_wc_operation_update:\r
1626                                                 switch (pInfoData->treeconflict_action)\r
1627                                                 {\r
1628                                                 case svn_wc_conflict_action_edit:\r
1629                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEEDIT, (LPCTSTR)sItemName);\r
1630                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1631                                                         break;\r
1632                                                 case svn_wc_conflict_action_add:\r
1633                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEADD, (LPCTSTR)sItemName);\r
1634                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1635                                                         break;\r
1636                                                 case svn_wc_conflict_action_delete:\r
1637                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEDELETE, (LPCTSTR)sItemName);\r
1638                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1639                                                         break;\r
1640                                                 }\r
1641                                                 break;\r
1642                                         case svn_wc_operation_switch:\r
1643                                                 switch (pInfoData->treeconflict_action)\r
1644                                                 {\r
1645                                                 case svn_wc_conflict_action_edit:\r
1646                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHEDIT, (LPCTSTR)sItemName);\r
1647                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1648                                                         break;\r
1649                                                 case svn_wc_conflict_action_add:\r
1650                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHADD, (LPCTSTR)sItemName);\r
1651                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1652                                                         break;\r
1653                                                 case svn_wc_conflict_action_delete:\r
1654                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHDELETE, (LPCTSTR)sItemName);\r
1655                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1656                                                         break;\r
1657                                                 }\r
1658                                                 break;\r
1659                                         case svn_wc_operation_merge:\r
1660                                                 switch (pInfoData->treeconflict_action)\r
1661                                                 {\r
1662                                                 case svn_wc_conflict_action_edit:\r
1663                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEEDIT, (LPCTSTR)sItemName);\r
1664                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1665                                                         break;\r
1666                                                 case svn_wc_conflict_action_add:\r
1667                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEADD, (LPCTSTR)sItemName);\r
1668                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1669                                                         break;\r
1670                                                 case svn_wc_conflict_action_delete:\r
1671                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEDELETE, (LPCTSTR)sItemName);\r
1672                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1673                                                         break;\r
1674                                                 }\r
1675                                                 break;\r
1676                                         }\r
1677                                 }\r
1678 \r
1679                                 UINT uReasonID = 0;\r
1680                                 switch (pInfoData->treeconflict_reason)\r
1681                                 { \r
1682                                 case svn_wc_conflict_reason_edited:\r
1683                                         uReasonID = IDS_TREECONFLICT_REASON_EDITED;\r
1684                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1685                                         break;\r
1686                                 case svn_wc_conflict_reason_obstructed:\r
1687                                         uReasonID = IDS_TREECONFLICT_REASON_OBSTRUCTED;\r
1688                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1689                                         break;\r
1690                                 case svn_wc_conflict_reason_deleted:\r
1691                                         uReasonID = IDS_TREECONFLICT_REASON_DELETED;\r
1692                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1693                                         break;\r
1694                                 case svn_wc_conflict_reason_added:\r
1695                                         uReasonID = IDS_TREECONFLICT_REASON_ADDED;\r
1696                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1697                                         break;\r
1698                                 case svn_wc_conflict_reason_missing:\r
1699                                         uReasonID = IDS_TREECONFLICT_REASON_MISSING;\r
1700                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1701                                         break;\r
1702                                 case svn_wc_conflict_reason_unversioned:\r
1703                                         uReasonID = IDS_TREECONFLICT_REASON_UNVERSIONED;\r
1704                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1705                                         break;\r
1706                                 }\r
1707                                 sConflictReason.Format(uReasonID, (LPCTSTR)sConflictAction);\r
1708 \r
1709                                 CTreeConflictEditorDlg dlg;\r
1710                                 dlg.SetConflictInfoText(sConflictReason);\r
1711                                 dlg.SetResolveTexts(sResolveTheirs, sResolveMine);\r
1712                                 dlg.SetPath(treeConflictPath);\r
1713                                 INT_PTR dlgRet = dlg.DoModal();\r
1714                                 bRet = (dlgRet != IDCANCEL);\r
1715                         }\r
1716                 }\r
1717         }\r
1718 #endif\r
1719         return bRet;\r
1720 }\r
1721 \r
1722 /**\r
1723  * FUNCTION    :   FormatDateAndTime\r
1724  * DESCRIPTION :   Generates a displayable string from a CTime object in\r
1725  *                 system short or long format  or as a relative value\r
1726  *                                 cTime - the time\r
1727  *                                 option - DATE_SHORTDATE or DATE_LONGDATE\r
1728  *                                 bIncluedeTime - whether to show time as well as date\r
1729  *                                 bRelative - if true then relative time is shown if reasonable \r
1730  *                                 If HKCU\Software\TortoiseGit\UseSystemLocaleForDates is 0 then use fixed format\r
1731  *                                 rather than locale\r
1732  * RETURN      :   CString containing date/time\r
1733  */\r
1734 CString CAppUtils::FormatDateAndTime( const CTime& cTime, DWORD option, bool bIncludeTime /*=true*/,\r
1735         bool bRelative /*=false*/)\r
1736 {\r
1737         CString datetime;\r
1738         if ( bRelative )\r
1739         {\r
1740                 datetime = ToRelativeTimeString( cTime );\r
1741         }\r
1742         else\r
1743         {\r
1744                 // should we use the locale settings for formatting the date/time?\r
1745                 if (CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))\r
1746                 {\r
1747                         // yes\r
1748                         SYSTEMTIME sysTime;\r
1749                         cTime.GetAsSystemTime( sysTime );\r
1750                         \r
1751                         TCHAR buf[100];\r
1752                         \r
1753                         GetDateFormat(LOCALE_USER_DEFAULT, option, &sysTime, NULL, buf, \r
1754                                 sizeof(buf)/sizeof(TCHAR)-1);\r
1755                         datetime = buf;\r
1756                         if ( bIncludeTime )\r
1757                         {\r
1758                                 datetime += _T(" ");\r
1759                                 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, buf, sizeof(buf)/sizeof(TCHAR)-1);\r
1760                                 datetime += buf;\r
1761                         }\r
1762                 }\r
1763                 else\r
1764                 {\r
1765                         // no, so fixed format\r
1766                         if ( bIncludeTime )\r
1767                         {\r
1768                                 datetime = cTime.Format(_T("%Y-%m-%d %H:%M:%S"));\r
1769                         }\r
1770                         else\r
1771                         {\r
1772                                 datetime = cTime.Format(_T("%Y-%m-%d"));\r
1773                         }\r
1774                 }\r
1775         }\r
1776         return datetime;\r
1777 }\r
1778 \r
1779 /**\r
1780  *      Converts a given time to a relative display string (relative to current time)\r
1781  *      Given time must be in local timezone\r
1782  */\r
1783 CString CAppUtils::ToRelativeTimeString(CTime time)\r
1784 {\r
1785     CString answer;\r
1786         // convert to COleDateTime\r
1787         SYSTEMTIME sysTime;\r
1788         time.GetAsSystemTime( sysTime );\r
1789         COleDateTime oleTime( sysTime );\r
1790         answer = ToRelativeTimeString(oleTime, COleDateTime::GetCurrentTime());\r
1791         return answer;\r
1792 }\r
1793 \r
1794 /**\r
1795  *      Generates a display string showing the relative time between the two given times as COleDateTimes\r
1796  */\r
1797 CString CAppUtils::ToRelativeTimeString(COleDateTime time,COleDateTime RelativeTo)\r
1798 {\r
1799     CString answer;\r
1800         COleDateTimeSpan ts = RelativeTo - time;\r
1801     //years\r
1802         if(fabs(ts.GetTotalDays()) >= 3*365)\r
1803     {\r
1804                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/365, IDS_YEAR_AGO, IDS_YEARS_AGO );\r
1805         }\r
1806         //Months\r
1807         if(fabs(ts.GetTotalDays()) >= 60)\r
1808         {\r
1809                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/30, IDS_MONTH_AGO, IDS_MONTHS_AGO );\r
1810                 return answer;\r
1811         }\r
1812         //Weeks\r
1813         if(fabs(ts.GetTotalDays()) >= 14)\r
1814         {\r
1815                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/7, IDS_WEEK_AGO, IDS_WEEKS_AGO );\r
1816                 return answer;\r
1817         }\r
1818         //Days\r
1819         if(fabs(ts.GetTotalDays()) >= 2)\r
1820         {\r
1821                 answer = ExpandRelativeTime( (int)ts.GetTotalDays(), IDS_DAY_AGO, IDS_DAYS_AGO );\r
1822                 return answer;\r
1823         }\r
1824         //hours\r
1825         if(fabs(ts.GetTotalHours()) >= 2)\r
1826         {\r
1827                 answer = ExpandRelativeTime( (int)ts.GetTotalHours(), IDS_HOUR_AGO, IDS_HOURS_AGO );\r
1828                 return answer;\r
1829         }\r
1830         //minutes\r
1831         if(fabs(ts.GetTotalMinutes()) >= 2)\r
1832         {\r
1833                 answer = ExpandRelativeTime( (int)ts.GetTotalMinutes(), IDS_MINUTE_AGO, IDS_MINUTES_AGO );\r
1834                 return answer;\r
1835         }\r
1836         //seconds\r
1837                 answer = ExpandRelativeTime( (int)ts.GetTotalSeconds(), IDS_SECOND_AGO, IDS_SECONDS_AGO );\r
1838     return answer;\r
1839 }\r
1840 \r
1841 /** \r
1842  * Passed a value and two resource string ids\r
1843  * if count is 1 then FormatString is called with format_1 and the value\r
1844  * otherwise format_2 is used\r
1845  * the formatted string is returned\r
1846 */\r
1847 CString CAppUtils::ExpandRelativeTime( int count, UINT format_1, UINT format_n )\r
1848 {\r
1849         CString answer;\r
1850         if ( count == 1 )\r
1851         {\r
1852                 answer.FormatMessage( format_1, count );\r
1853         }\r
1854         else\r
1855         {\r
1856                 answer.FormatMessage( format_n, count );\r
1857         }\r
1858         return answer;\r
1859 }\r
1860 \r
1861 bool CAppUtils::IsSSHPutty()\r
1862 {\r
1863     CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));\r
1864     sshclient=sshclient.MakeLower();\r
1865     if(sshclient.Find(_T("plink.exe"),0)>=0)\r
1866     {\r
1867         return true;\r
1868     }\r
1869     return false;\r
1870 }\r
1871 \r
1872 CString CAppUtils::GetClipboardLink()\r
1873 {\r
1874         if (!OpenClipboard(NULL))\r
1875                 return CString();\r
1876 \r
1877         CString sClipboardText;\r
1878         HGLOBAL hglb = GetClipboardData(CF_TEXT);\r
1879         if (hglb)\r
1880         {\r
1881                 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);\r
1882                 sClipboardText = CString(lpstr);\r
1883                 GlobalUnlock(hglb); \r
1884         }\r
1885         hglb = GetClipboardData(CF_UNICODETEXT);\r
1886         if (hglb)\r
1887         {\r
1888                 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);\r
1889                 sClipboardText = lpstr;\r
1890                 GlobalUnlock(hglb); \r
1891         }\r
1892         CloseClipboard();\r
1893 \r
1894         if(!sClipboardText.IsEmpty())\r
1895         {\r
1896                 if(sClipboardText[0] == _T('\"') && sClipboardText[sClipboardText.GetLength()-1] == _T('\"'))\r
1897                         sClipboardText=sClipboardText.Mid(1,sClipboardText.GetLength()-2);\r
1898 \r
1899                 if(sClipboardText.Find( _T("http://")) == 0)\r
1900                         return sClipboardText;\r
1901                 \r
1902                 if(sClipboardText.Find( _T("https://")) == 0)\r
1903                         return sClipboardText;\r
1904 \r
1905                 if(sClipboardText.Find( _T("git://")) == 0)\r
1906                         return sClipboardText;\r
1907 \r
1908                 if(sClipboardText.Find( _T("ssh://")) == 0)\r
1909                         return sClipboardText;\r
1910 \r
1911                 if(sClipboardText.GetLength()>=2)\r
1912                         if( sClipboardText[1] == _T(':') )\r
1913                                 if( (sClipboardText[0] >= 'A' &&  sClipboardText[0] <= 'Z') \r
1914                                         || (sClipboardText[0] >= 'a' &&  sClipboardText[0] <= 'z') )\r
1915                                         return sClipboardText;\r
1916         }\r
1917 \r
1918         return CString(_T(""));\r
1919 }\r
1920 \r
1921 CString CAppUtils::ChooseRepository(CString *path)\r
1922 {\r
1923         CBrowseFolder browseFolder;\r
1924         browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
1925         CString strCloneDirectory;\r
1926         if(path)\r
1927                 strCloneDirectory=*path;\r
1928 \r
1929         CString title;\r
1930         title.LoadString(IDS_CHOOSE_REPOSITORY);\r
1931 \r
1932         browseFolder.SetInfo(title);\r
1933 \r
1934         if (browseFolder.Show(NULL, strCloneDirectory) == CBrowseFolder::OK) \r
1935         {\r
1936                 return strCloneDirectory;\r
1937                 \r
1938         }else\r
1939         {\r
1940                 return CString();\r
1941         }\r
1942         \r
1943 }