OSDN Git Service

Progress Bar Show Animate
[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 \r
34 CAppUtils::CAppUtils(void)\r
35 {\r
36 }\r
37 \r
38 CAppUtils::~CAppUtils(void)\r
39 {\r
40 }\r
41 \r
42 bool CAppUtils::GetMimeType(const CTGitPath& file, CString& mimetype)\r
43 {\r
44 #if 0\r
45         GitProperties props(file, GitRev::REV_WC, false);\r
46         for (int i = 0; i < props.GetCount(); ++i)\r
47         {\r
48                 if (props.GetItemName(i).compare(_T("svn:mime-type"))==0)\r
49                 {\r
50                         mimetype = props.GetItemValue(i).c_str();\r
51                         return true;\r
52                 }\r
53         }\r
54 #endif\r
55         return false;\r
56 }\r
57 \r
58 BOOL CAppUtils::StartExtMerge(\r
59         const CTGitPath& basefile, const CTGitPath& theirfile, const CTGitPath& yourfile, const CTGitPath& mergedfile,\r
60         const CString& basename, const CString& theirname, const CString& yourname, const CString& mergedname, bool bReadOnly)\r
61 {\r
62 \r
63         CRegString regCom = CRegString(_T("Software\\TortoiseGit\\Merge"));\r
64         CString ext = mergedfile.GetFileExtension();\r
65         CString com = regCom;\r
66         bool bInternal = false;\r
67 \r
68         CString mimetype;\r
69         if (ext != "")\r
70         {\r
71                 // is there an extension specific merge tool?\r
72                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + ext.MakeLower());\r
73                 if (CString(mergetool) != "")\r
74                 {\r
75                         com = mergetool;\r
76                 }\r
77         }\r
78         if (GetMimeType(yourfile, mimetype) || GetMimeType(theirfile, mimetype) || GetMimeType(basefile, mimetype))\r
79         {\r
80                 // is there a mime type specific merge tool?\r
81                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + mimetype);\r
82                 if (CString(mergetool) != "")\r
83                 {\r
84                         com = mergetool;\r
85                 }\r
86         }\r
87         \r
88         if (com.IsEmpty()||(com.Left(1).Compare(_T("#"))==0))\r
89         {\r
90                 // use TortoiseMerge\r
91                 bInternal = true;\r
92                 CRegString tortoiseMergePath(_T("Software\\TortoiseGit\\TMergePath"), _T(""), false, HKEY_LOCAL_MACHINE);\r
93                 com = tortoiseMergePath;\r
94                 if (com.IsEmpty())\r
95                 {\r
96                         com = CPathUtils::GetAppDirectory();\r
97                         com += _T("TortoiseMerge.exe");\r
98                 }\r
99                 com = _T("\"") + com + _T("\"");\r
100                 com = com + _T(" /base:%base /theirs:%theirs /mine:%mine /merged:%merged");\r
101                 com = com + _T(" /basename:%bname /theirsname:%tname /minename:%yname /mergedname:%mname");\r
102         }\r
103         // check if the params are set. If not, just add the files to the command line\r
104         if ((com.Find(_T("%merged"))<0)&&(com.Find(_T("%base"))<0)&&(com.Find(_T("%theirs"))<0)&&(com.Find(_T("%mine"))<0))\r
105         {\r
106                 com += _T(" \"")+basefile.GetWinPathString()+_T("\"");\r
107                 com += _T(" \"")+theirfile.GetWinPathString()+_T("\"");\r
108                 com += _T(" \"")+yourfile.GetWinPathString()+_T("\"");\r
109                 com += _T(" \"")+mergedfile.GetWinPathString()+_T("\"");\r
110         }\r
111         if (basefile.IsEmpty())\r
112         {\r
113                 com.Replace(_T("/base:%base"), _T(""));\r
114                 com.Replace(_T("%base"), _T(""));               \r
115         }\r
116         else\r
117                 com.Replace(_T("%base"), _T("\"") + basefile.GetWinPathString() + _T("\""));\r
118         if (theirfile.IsEmpty())\r
119         {\r
120                 com.Replace(_T("/theirs:%theirs"), _T(""));\r
121                 com.Replace(_T("%theirs"), _T(""));\r
122         }\r
123         else\r
124                 com.Replace(_T("%theirs"), _T("\"") + theirfile.GetWinPathString() + _T("\""));\r
125         if (yourfile.IsEmpty())\r
126         {\r
127                 com.Replace(_T("/mine:%mine"), _T(""));\r
128                 com.Replace(_T("%mine"), _T(""));\r
129         }\r
130         else\r
131                 com.Replace(_T("%mine"), _T("\"") + yourfile.GetWinPathString() + _T("\""));\r
132         if (mergedfile.IsEmpty())\r
133         {\r
134                 com.Replace(_T("/merged:%merged"), _T(""));\r
135                 com.Replace(_T("%merged"), _T(""));\r
136         }\r
137         else\r
138                 com.Replace(_T("%merged"), _T("\"") + mergedfile.GetWinPathString() + _T("\""));\r
139         if (basename.IsEmpty())\r
140         {\r
141                 if (basefile.IsEmpty())\r
142                 {\r
143                         com.Replace(_T("/basename:%bname"), _T(""));\r
144                         com.Replace(_T("%bname"), _T(""));\r
145                 }\r
146                 else\r
147                 {\r
148                         com.Replace(_T("%bname"), _T("\"") + basefile.GetUIFileOrDirectoryName() + _T("\""));\r
149                 }\r
150         }\r
151         else\r
152                 com.Replace(_T("%bname"), _T("\"") + basename + _T("\""));\r
153         if (theirname.IsEmpty())\r
154         {\r
155                 if (theirfile.IsEmpty())\r
156                 {\r
157                         com.Replace(_T("/theirsname:%tname"), _T(""));\r
158                         com.Replace(_T("%tname"), _T(""));\r
159                 }\r
160                 else\r
161                 {\r
162                         com.Replace(_T("%tname"), _T("\"") + theirfile.GetUIFileOrDirectoryName() + _T("\""));\r
163                 }\r
164         }\r
165         else\r
166                 com.Replace(_T("%tname"), _T("\"") + theirname + _T("\""));\r
167         if (yourname.IsEmpty())\r
168         {\r
169                 if (yourfile.IsEmpty())\r
170                 {\r
171                         com.Replace(_T("/minename:%yname"), _T(""));\r
172                         com.Replace(_T("%yname"), _T(""));\r
173                 }\r
174                 else\r
175                 {\r
176                         com.Replace(_T("%yname"), _T("\"") + yourfile.GetUIFileOrDirectoryName() + _T("\""));\r
177                 }\r
178         }\r
179         else\r
180                 com.Replace(_T("%yname"), _T("\"") + yourname + _T("\""));\r
181         if (mergedname.IsEmpty())\r
182         {\r
183                 if (mergedfile.IsEmpty())\r
184                 {\r
185                         com.Replace(_T("/mergedname:%mname"), _T(""));\r
186                         com.Replace(_T("%mname"), _T(""));\r
187                 }\r
188                 else\r
189                 {\r
190                         com.Replace(_T("%mname"), _T("\"") + mergedfile.GetUIFileOrDirectoryName() + _T("\""));\r
191                 }\r
192         }\r
193         else\r
194                 com.Replace(_T("%mname"), _T("\"") + mergedname + _T("\""));\r
195 \r
196         if ((bReadOnly)&&(bInternal))\r
197                 com += _T(" /readonly");\r
198 \r
199         if(!LaunchApplication(com, IDS_ERR_EXTMERGESTART, false))\r
200         {\r
201                 return FALSE;\r
202         }\r
203 \r
204         return TRUE;\r
205 }\r
206 \r
207 BOOL CAppUtils::StartExtPatch(const CTGitPath& patchfile, const CTGitPath& dir, const CString& sOriginalDescription, const CString& sPatchedDescription, BOOL bReversed, BOOL bWait)\r
208 {\r
209         CString viewer;\r
210         // use TortoiseMerge\r
211         viewer = CPathUtils::GetAppDirectory();\r
212         viewer += _T("TortoiseMerge.exe");\r
213 \r
214         viewer = _T("\"") + viewer + _T("\"");\r
215         viewer = viewer + _T(" /diff:\"") + patchfile.GetWinPathString() + _T("\"");\r
216         viewer = viewer + _T(" /patchpath:\"") + dir.GetWinPathString() + _T("\"");\r
217         if (bReversed)\r
218                 viewer += _T(" /reversedpatch");\r
219         if (!sOriginalDescription.IsEmpty())\r
220                 viewer = viewer + _T(" /patchoriginal:\"") + sOriginalDescription + _T("\"");\r
221         if (!sPatchedDescription.IsEmpty())\r
222                 viewer = viewer + _T(" /patchpatched:\"") + sPatchedDescription + _T("\"");\r
223         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
224         {\r
225                 return FALSE;\r
226         }\r
227         return TRUE;\r
228 }\r
229 \r
230 CString CAppUtils::PickDiffTool(const CTGitPath& file1, const CTGitPath& file2)\r
231 {\r
232         // Is there a mime type specific diff tool?\r
233         CString mimetype;\r
234         if (GetMimeType(file1, mimetype) ||  GetMimeType(file2, mimetype))\r
235         {\r
236                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + mimetype);\r
237                 if (!difftool.IsEmpty())\r
238                         return difftool;\r
239         }\r
240         \r
241         // Is there an extension specific diff tool?\r
242         CString ext = file2.GetFileExtension().MakeLower();\r
243         if (!ext.IsEmpty())\r
244         {\r
245                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + ext);\r
246                 if (!difftool.IsEmpty())\r
247                         return difftool;\r
248                 // Maybe we should use TortoiseIDiff?\r
249                 if ((ext == _T(".jpg")) || (ext == _T(".jpeg")) ||\r
250                         (ext == _T(".bmp")) || (ext == _T(".gif"))  ||\r
251                         (ext == _T(".png")) || (ext == _T(".ico"))  ||\r
252                         (ext == _T(".dib")) || (ext == _T(".emf")))\r
253                 {\r
254                         return\r
255                                 _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseIDiff.exe") + _T("\"") +\r
256                                 _T(" /left:%base /right:%mine /lefttitle:%bname /righttitle:%yname");\r
257                 }\r
258         }\r
259         \r
260         // Finally, pick a generic external diff tool\r
261         CString difftool = CRegString(_T("Software\\TortoiseGit\\Diff"));\r
262         return difftool;\r
263 }\r
264 \r
265 bool CAppUtils::StartExtDiff(\r
266         const CString& file1,  const CString& file2,\r
267         const CString& sName1, const CString& sName2, \r
268         const DiffFlags& flags)\r
269 {\r
270         CString viewer;\r
271 \r
272         CRegDWORD blamediff(_T("Software\\TortoiseGit\\DiffBlamesWithTortoiseMerge"), FALSE);\r
273         if (!flags.bBlame || !(DWORD)blamediff)\r
274         {\r
275                 viewer = PickDiffTool(file1, file2);\r
276                 // If registry entry for a diff program is commented out, use TortoiseMerge.\r
277                 bool bCommentedOut = viewer.Left(1) == _T("#");\r
278                 if (flags.bAlternativeTool)\r
279                 {\r
280                         // Invert external vs. internal diff tool selection.\r
281                         if (bCommentedOut)\r
282                                 viewer.Delete(0); // uncomment\r
283                         else\r
284                                 viewer = "";\r
285                 }\r
286                 else if (bCommentedOut)\r
287                         viewer = "";\r
288         }\r
289 \r
290         bool bInternal = viewer.IsEmpty();\r
291         if (bInternal)\r
292         {\r
293                 viewer =\r
294                         _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseMerge.exe") + _T("\"") +\r
295                         _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
296                 if (flags.bBlame)\r
297                         viewer += _T(" /blame");\r
298         }\r
299         // check if the params are set. If not, just add the files to the command line\r
300         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
301         {\r
302                 viewer += _T(" \"")+file1+_T("\"");\r
303                 viewer += _T(" \"")+file2+_T("\"");\r
304         }\r
305         if (viewer.Find(_T("%base")) >= 0)\r
306         {\r
307                 viewer.Replace(_T("%base"),  _T("\"")+file1+_T("\""));\r
308         }\r
309         if (viewer.Find(_T("%mine")) >= 0)\r
310         {\r
311                 viewer.Replace(_T("%mine"),  _T("\"")+file2+_T("\""));\r
312         }\r
313 \r
314         if (sName1.IsEmpty())\r
315                 viewer.Replace(_T("%bname"), _T("\"") + file1 + _T("\""));\r
316         else\r
317                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
318 \r
319         if (sName2.IsEmpty())\r
320                 viewer.Replace(_T("%yname"), _T("\"") + file2 + _T("\""));\r
321         else\r
322                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
323 \r
324         if (flags.bReadOnly && bInternal)\r
325                 viewer += _T(" /readonly");\r
326 \r
327         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, flags.bWait);\r
328 }\r
329 \r
330 BOOL CAppUtils::StartExtDiffProps(const CTGitPath& file1, const CTGitPath& file2, const CString& sName1, const CString& sName2, BOOL bWait, BOOL bReadOnly)\r
331 {\r
332         CRegString diffpropsexe(_T("Software\\TortoiseGit\\DiffProps"));\r
333         CString viewer = diffpropsexe;\r
334         bool bInternal = false;\r
335         if (viewer.IsEmpty()||(viewer.Left(1).Compare(_T("#"))==0))\r
336         {\r
337                 //no registry entry (or commented out) for a diff program\r
338                 //use TortoiseMerge\r
339                 bInternal = true;\r
340                 viewer = CPathUtils::GetAppDirectory();\r
341                 viewer += _T("TortoiseMerge.exe");\r
342                 viewer = _T("\"") + viewer + _T("\"");\r
343                 viewer = viewer + _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
344         }\r
345         // check if the params are set. If not, just add the files to the command line\r
346         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
347         {\r
348                 viewer += _T(" \"")+file1.GetWinPathString()+_T("\"");\r
349                 viewer += _T(" \"")+file2.GetWinPathString()+_T("\"");\r
350         }\r
351         if (viewer.Find(_T("%base")) >= 0)\r
352         {\r
353                 viewer.Replace(_T("%base"),  _T("\"")+file1.GetWinPathString()+_T("\""));\r
354         }\r
355         if (viewer.Find(_T("%mine")) >= 0)\r
356         {\r
357                 viewer.Replace(_T("%mine"),  _T("\"")+file2.GetWinPathString()+_T("\""));\r
358         }\r
359 \r
360         if (sName1.IsEmpty())\r
361                 viewer.Replace(_T("%bname"), _T("\"") + file1.GetUIFileOrDirectoryName() + _T("\""));\r
362         else\r
363                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
364 \r
365         if (sName2.IsEmpty())\r
366                 viewer.Replace(_T("%yname"), _T("\"") + file2.GetUIFileOrDirectoryName() + _T("\""));\r
367         else\r
368                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
369 \r
370         if ((bReadOnly)&&(bInternal))\r
371                 viewer += _T(" /readonly");\r
372 \r
373         if(!LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, !!bWait))\r
374         {\r
375                 return FALSE;\r
376         }\r
377         return TRUE;\r
378 }\r
379 \r
380 BOOL CAppUtils::StartUnifiedDiffViewer(const CString& patchfile, const CString& title, BOOL bWait)\r
381 {\r
382         CString viewer;\r
383         CRegString v = CRegString(_T("Software\\TortoiseGit\\DiffViewer"));\r
384         viewer = v;\r
385         if (viewer.IsEmpty() || (viewer.Left(1).Compare(_T("#"))==0))\r
386         {\r
387                 // use TortoiseUDiff\r
388                 viewer = CPathUtils::GetAppDirectory();\r
389                 viewer += _T("TortoiseUDiff.exe");\r
390                 // enquote the path to TortoiseUDiff\r
391                 viewer = _T("\"") + viewer + _T("\"");\r
392                 // add the params\r
393                 viewer = viewer + _T(" /patchfile:%1 /title:\"%title\"");\r
394 \r
395         }\r
396         if (viewer.Find(_T("%1"))>=0)\r
397         {\r
398                 if (viewer.Find(_T("\"%1\"")) >= 0)\r
399                         viewer.Replace(_T("%1"), patchfile);\r
400                 else\r
401                         viewer.Replace(_T("%1"), _T("\"") + patchfile + _T("\""));\r
402         }\r
403         else\r
404                 viewer += _T(" \"") + patchfile + _T("\"");\r
405         if (viewer.Find(_T("%title")) >= 0)\r
406         {\r
407                 viewer.Replace(_T("%title"), title);\r
408         }\r
409 \r
410         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
411         {\r
412                 return FALSE;\r
413         }\r
414         return TRUE;\r
415 }\r
416 \r
417 BOOL CAppUtils::StartTextViewer(CString file)\r
418 {\r
419         CString viewer;\r
420         CRegString txt = CRegString(_T(".txt\\"), _T(""), FALSE, HKEY_CLASSES_ROOT);\r
421         viewer = txt;\r
422         viewer = viewer + _T("\\Shell\\Open\\Command\\");\r
423         CRegString txtexe = CRegString(viewer, _T(""), FALSE, HKEY_CLASSES_ROOT);\r
424         viewer = txtexe;\r
425 \r
426         DWORD len = ExpandEnvironmentStrings(viewer, NULL, 0);\r
427         TCHAR * buf = new TCHAR[len+1];\r
428         ExpandEnvironmentStrings(viewer, buf, len);\r
429         viewer = buf;\r
430         delete [] buf;\r
431         len = ExpandEnvironmentStrings(file, NULL, 0);\r
432         buf = new TCHAR[len+1];\r
433         ExpandEnvironmentStrings(file, buf, len);\r
434         file = buf;\r
435         delete [] buf;\r
436         file = _T("\"")+file+_T("\"");\r
437         if (viewer.IsEmpty())\r
438         {\r
439                 OPENFILENAME ofn = {0};                         // common dialog box structure\r
440                 TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
441                 // Initialize OPENFILENAME\r
442                 ofn.lStructSize = sizeof(OPENFILENAME);\r
443                 ofn.hwndOwner = NULL;\r
444                 ofn.lpstrFile = szFile;\r
445                 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
446                 CString sFilter;\r
447                 sFilter.LoadString(IDS_PROGRAMSFILEFILTER);\r
448                 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];\r
449                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
450                 // Replace '|' delimiters with '\0's\r
451                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
452                 while (ptr != pszFilters)\r
453                 {\r
454                         if (*ptr == '|')\r
455                                 *ptr = '\0';\r
456                         ptr--;\r
457                 }\r
458                 ofn.lpstrFilter = pszFilters;\r
459                 ofn.nFilterIndex = 1;\r
460                 ofn.lpstrFileTitle = NULL;\r
461                 ofn.nMaxFileTitle = 0;\r
462                 ofn.lpstrInitialDir = NULL;\r
463                 CString temp;\r
464                 temp.LoadString(IDS_UTILS_SELECTTEXTVIEWER);\r
465                 CStringUtils::RemoveAccelerators(temp);\r
466                 ofn.lpstrTitle = temp;\r
467                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;\r
468 \r
469                 // Display the Open dialog box. \r
470 \r
471                 if (GetOpenFileName(&ofn)==TRUE)\r
472                 {\r
473                         delete [] pszFilters;\r
474                         viewer = CString(ofn.lpstrFile);\r
475                 }\r
476                 else\r
477                 {\r
478                         delete [] pszFilters;\r
479                         return FALSE;\r
480                 }\r
481         }\r
482         if (viewer.Find(_T("\"%1\"")) >= 0)\r
483         {\r
484                 viewer.Replace(_T("\"%1\""), file);\r
485         }\r
486         else if (viewer.Find(_T("%1")) >= 0)\r
487         {\r
488                 viewer.Replace(_T("%1"),  file);\r
489         }\r
490         else\r
491         {\r
492                 viewer += _T(" ");\r
493                 viewer += file;\r
494         }\r
495 \r
496         if(!LaunchApplication(viewer, IDS_ERR_TEXTVIEWSTART, false))\r
497         {\r
498                 return FALSE;\r
499         }\r
500         return TRUE;\r
501 }\r
502 \r
503 BOOL CAppUtils::CheckForEmptyDiff(const CTGitPath& sDiffPath)\r
504 {\r
505         DWORD length = 0;\r
506         HANDLE hFile = ::CreateFile(sDiffPath.GetWinPath(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);\r
507         if (hFile == INVALID_HANDLE_VALUE)\r
508                 return TRUE;\r
509         length = ::GetFileSize(hFile, NULL);\r
510         ::CloseHandle(hFile);\r
511         if (length < 4)\r
512                 return TRUE;\r
513         return FALSE;\r
514 \r
515 }\r
516 \r
517 void CAppUtils::CreateFontForLogs(CFont& fontToCreate)\r
518 {\r
519         LOGFONT logFont;\r
520         HDC hScreenDC = ::GetDC(NULL);\r
521         logFont.lfHeight         = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8), GetDeviceCaps(hScreenDC, LOGPIXELSY), 72);\r
522         ::ReleaseDC(NULL, hScreenDC);\r
523         logFont.lfWidth          = 0;\r
524         logFont.lfEscapement     = 0;\r
525         logFont.lfOrientation    = 0;\r
526         logFont.lfWeight         = FW_NORMAL;\r
527         logFont.lfItalic         = 0;\r
528         logFont.lfUnderline      = 0;\r
529         logFont.lfStrikeOut      = 0;\r
530         logFont.lfCharSet        = DEFAULT_CHARSET;\r
531         logFont.lfOutPrecision   = OUT_DEFAULT_PRECIS;\r
532         logFont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;\r
533         logFont.lfQuality        = DRAFT_QUALITY;\r
534         logFont.lfPitchAndFamily = FF_DONTCARE | FIXED_PITCH;\r
535         _tcscpy_s(logFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")));\r
536         VERIFY(fontToCreate.CreateFontIndirect(&logFont));\r
537 }\r
538 \r
539 bool CAppUtils::LaunchApplication(const CString& sCommandLine, UINT idErrMessageFormat, bool bWaitForStartup)\r
540 {\r
541         STARTUPINFO startup;\r
542         PROCESS_INFORMATION process;\r
543         memset(&startup, 0, sizeof(startup));\r
544         startup.cb = sizeof(startup);\r
545         memset(&process, 0, sizeof(process));\r
546 \r
547         CString cleanCommandLine(sCommandLine);\r
548 \r
549         if (CreateProcess(NULL, const_cast<TCHAR*>((LPCTSTR)cleanCommandLine), NULL, NULL, FALSE, 0, 0, sOrigCWD, &startup, &process)==0)\r
550         {\r
551                 if(idErrMessageFormat != 0)\r
552                 {\r
553                         LPVOID lpMsgBuf;\r
554                         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
555                                 FORMAT_MESSAGE_FROM_SYSTEM | \r
556                                 FORMAT_MESSAGE_IGNORE_INSERTS,\r
557                                 NULL,\r
558                                 GetLastError(),\r
559                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
560                                 (LPTSTR) &lpMsgBuf,\r
561                                 0,\r
562                                 NULL \r
563                                 );\r
564                         CString temp;\r
565                         temp.Format(idErrMessageFormat, lpMsgBuf);\r
566                         CMessageBox::Show(NULL, temp, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);\r
567                         LocalFree( lpMsgBuf );\r
568                 }\r
569                 return false;\r
570         }\r
571 \r
572         if (bWaitForStartup)\r
573         {\r
574                 WaitForInputIdle(process.hProcess, 10000);\r
575         }\r
576 \r
577         CloseHandle(process.hThread);\r
578         CloseHandle(process.hProcess);\r
579         return true;\r
580 }\r
581 \r
582 /**\r
583 * Launch the external blame viewer\r
584 */\r
585 bool CAppUtils::LaunchTortoiseBlame(const CString& sBlameFile, const CString& sLogFile, const CString& sOriginalFile, const CString& sParams)\r
586 {\r
587         CString viewer = CPathUtils::GetAppDirectory();\r
588         viewer += _T("TortoiseBlame.exe");\r
589         viewer += _T(" \"") + sBlameFile + _T("\"");\r
590         viewer += _T(" \"") + sLogFile + _T("\"");\r
591         viewer += _T(" \"") + sOriginalFile + _T("\"");\r
592         viewer += _T(" ")+sParams;\r
593         \r
594         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, false);\r
595 }\r
596 \r
597 void CAppUtils::ResizeAllListCtrlCols(CListCtrl * pListCtrl)\r
598 {\r
599         int maxcol = ((CHeaderCtrl*)(pListCtrl->GetDlgItem(0)))->GetItemCount()-1;\r
600         int nItemCount = pListCtrl->GetItemCount();\r
601         TCHAR textbuf[MAX_PATH];\r
602         CHeaderCtrl * pHdrCtrl = (CHeaderCtrl*)(pListCtrl->GetDlgItem(0));\r
603         if (pHdrCtrl)\r
604         {\r
605                 for (int col = 0; col <= maxcol; col++)\r
606                 {\r
607                         HDITEM hdi = {0};\r
608                         hdi.mask = HDI_TEXT;\r
609                         hdi.pszText = textbuf;\r
610                         hdi.cchTextMax = sizeof(textbuf);\r
611                         pHdrCtrl->GetItem(col, &hdi);\r
612                         int cx = pListCtrl->GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin\r
613                         for (int index = 0; index<nItemCount; ++index)\r
614                         {\r
615                                 // get the width of the string and add 14 pixels for the column separator and margins\r
616                                 int linewidth = pListCtrl->GetStringWidth(pListCtrl->GetItemText(index, col)) + 14;\r
617                                 if (index == 0)\r
618                                 {\r
619                                         // add the image size\r
620                                         CImageList * pImgList = pListCtrl->GetImageList(LVSIL_SMALL);\r
621                                         if ((pImgList)&&(pImgList->GetImageCount()))\r
622                                         {\r
623                                                 IMAGEINFO imginfo;\r
624                                                 pImgList->GetImageInfo(0, &imginfo);\r
625                                                 linewidth += (imginfo.rcImage.right - imginfo.rcImage.left);\r
626                                                 linewidth += 3; // 3 pixels between icon and text\r
627                                         }\r
628                                 }\r
629                                 if (cx < linewidth)\r
630                                         cx = linewidth;\r
631                         }\r
632                         pListCtrl->SetColumnWidth(col, cx);\r
633 \r
634                 }\r
635         }\r
636 }\r
637 \r
638 bool CAppUtils::FormatTextInRichEditControl(CWnd * pWnd)\r
639 {\r
640         CString sText;\r
641         if (pWnd == NULL)\r
642                 return false;\r
643         bool bStyled = false;\r
644         pWnd->GetWindowText(sText);\r
645         // the rich edit control doesn't count the CR char!\r
646         // to be exact: CRLF is treated as one char.\r
647         sText.Replace(_T("\r"), _T(""));\r
648 \r
649         // style each line separately\r
650         int offset = 0;\r
651         int nNewlinePos;\r
652         do \r
653         {\r
654                 nNewlinePos = sText.Find('\n', offset);\r
655                 CString sLine = sText.Mid(offset);\r
656                 if (nNewlinePos>=0)\r
657                         sLine = sLine.Left(nNewlinePos-offset);\r
658                 int start = 0;\r
659                 int end = 0;\r
660                 while (FindStyleChars(sLine, '*', start, end))\r
661                 {\r
662                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
663                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
664                         CHARFORMAT2 format;\r
665                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
666                         format.cbSize = sizeof(CHARFORMAT2);\r
667                         format.dwMask = CFM_BOLD;\r
668                         format.dwEffects = CFE_BOLD;\r
669                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
670                         bStyled = true;\r
671                         start = end;\r
672                 }\r
673                 start = 0;\r
674                 end = 0;\r
675                 while (FindStyleChars(sLine, '^', start, end))\r
676                 {\r
677                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
678                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
679                         CHARFORMAT2 format;\r
680                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
681                         format.cbSize = sizeof(CHARFORMAT2);\r
682                         format.dwMask = CFM_ITALIC;\r
683                         format.dwEffects = CFE_ITALIC;\r
684                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
685                         bStyled = true;\r
686                         start = end;\r
687                 }\r
688                 start = 0;\r
689                 end = 0;\r
690                 while (FindStyleChars(sLine, '_', start, end))\r
691                 {\r
692                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
693                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
694                         CHARFORMAT2 format;\r
695                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
696                         format.cbSize = sizeof(CHARFORMAT2);\r
697                         format.dwMask = CFM_UNDERLINE;\r
698                         format.dwEffects = CFE_UNDERLINE;\r
699                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
700                         bStyled = true;\r
701                         start = end;\r
702                 }\r
703                 offset = nNewlinePos+1;\r
704         } while(nNewlinePos>=0);\r
705         return bStyled; \r
706 }\r
707 \r
708 bool CAppUtils::FindStyleChars(const CString& sText, TCHAR stylechar, int& start, int& end)\r
709 {\r
710         int i=start;\r
711         bool bFoundMarker = false;\r
712         // find a starting marker\r
713         while (sText[i] != 0)\r
714         {\r
715                 if (sText[i] == stylechar)\r
716                 {\r
717                         if (((i+1)<sText.GetLength())&&(IsCharAlphaNumeric(sText[i+1])) &&\r
718                                 (((i>0)&&(!IsCharAlphaNumeric(sText[i-1])))||(i==0)))\r
719                         {\r
720                                 start = i+1;\r
721                                 i++;\r
722                                 bFoundMarker = true;\r
723                                 break;\r
724                         }\r
725                 }\r
726                 i++;\r
727         }\r
728         if (!bFoundMarker)\r
729                 return false;\r
730         // find ending marker\r
731         bFoundMarker = false;\r
732         while (sText[i] != 0)\r
733         {\r
734                 if (sText[i] == stylechar)\r
735                 {\r
736                         if ((IsCharAlphaNumeric(sText[i-1])) &&\r
737                                 ((((i+1)<sText.GetLength())&&(!IsCharAlphaNumeric(sText[i+1])))||(i+1)==sText.GetLength()))\r
738                         {\r
739                                 end = i;\r
740                                 i++;\r
741                                 bFoundMarker = true;\r
742                                 break;\r
743                         }\r
744                 }\r
745                 i++;\r
746         }\r
747         return bFoundMarker;\r
748 }\r
749 \r
750 bool CAppUtils::BrowseRepository(CHistoryCombo& combo, CWnd * pParent, GitRev& rev)\r
751 {\r
752 #if 0\r
753         CString strUrl;\r
754         combo.GetWindowText(strUrl);\r
755         strUrl.Replace('\\', '/');\r
756         strUrl.Replace(_T("%"), _T("%25"));\r
757         strUrl = CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(strUrl)));\r
758         if (strUrl.Left(7) == _T("file://"))\r
759         {\r
760                 CString strFile(strUrl);\r
761                 Git::UrlToPath(strFile);\r
762 \r
763                 Git svn;\r
764                 if (svn.IsRepository(CTGitPath(strFile)))\r
765                 {\r
766                         // browse repository - show repository browser\r
767                         Git::preparePath(strUrl);\r
768                         CRepositoryBrowser browser(strUrl, rev, pParent);\r
769                         if (browser.DoModal() == IDOK)\r
770                         {\r
771                                 combo.SetCurSel(-1);\r
772                                 combo.SetWindowText(browser.GetPath());\r
773                                 rev = browser.GetRevision();\r
774                                 return true;\r
775                         }\r
776                 }\r
777                 else\r
778                 {\r
779                         // browse local directories\r
780                         CBrowseFolder folderBrowser;\r
781                         folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
782                         // remove the 'file:///' so the shell can recognize the local path\r
783                         Git::UrlToPath(strUrl);\r
784                         if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
785                         {\r
786                                 Git::PathToUrl(strUrl);\r
787 \r
788                                 combo.SetCurSel(-1);\r
789                                 combo.SetWindowText(strUrl);\r
790                                 return true;\r
791                         }\r
792                 }\r
793         }\r
794         else if ((strUrl.Left(7) == _T("http://")\r
795                 ||(strUrl.Left(8) == _T("https://"))\r
796                 ||(strUrl.Left(6) == _T("svn://"))\r
797                 ||(strUrl.Left(4) == _T("svn+"))) && strUrl.GetLength() > 6)\r
798         {\r
799                 // browse repository - show repository browser\r
800                 CRepositoryBrowser browser(strUrl, rev, pParent);\r
801                 if (browser.DoModal() == IDOK)\r
802                 {\r
803                         combo.SetCurSel(-1);\r
804                         combo.SetWindowText(browser.GetPath());\r
805                         rev = browser.GetRevision();\r
806                         return true;\r
807                 }\r
808         }\r
809         else\r
810         {\r
811                 // browse local directories\r
812                 CBrowseFolder folderBrowser;\r
813                 folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
814                 if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
815                 {\r
816                         Git::PathToUrl(strUrl);\r
817 \r
818                         combo.SetCurSel(-1);\r
819                         combo.SetWindowText(strUrl);\r
820                         return true;\r
821                 }\r
822         }\r
823 #endif\r
824         return false;\r
825 }\r
826 \r
827 bool CAppUtils::FileOpenSave(CString& path, int * filterindex, UINT title, UINT filter, bool bOpen, HWND hwndOwner)\r
828 {\r
829         OPENFILENAME ofn = {0};                         // common dialog box structure\r
830         TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
831         ofn.lStructSize = sizeof(OPENFILENAME);\r
832         ofn.hwndOwner = hwndOwner;\r
833         _tcscpy_s(szFile, MAX_PATH, (LPCTSTR)path);\r
834         ofn.lpstrFile = szFile;\r
835         ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
836         CString sFilter;\r
837         TCHAR * pszFilters = NULL;\r
838         if (filter)\r
839         {\r
840                 sFilter.LoadString(filter);\r
841                 pszFilters = new TCHAR[sFilter.GetLength()+4];\r
842                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
843                 // Replace '|' delimiters with '\0's\r
844                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
845                 while (ptr != pszFilters)\r
846                 {\r
847                         if (*ptr == '|')\r
848                                 *ptr = '\0';\r
849                         ptr--;\r
850                 }\r
851                 ofn.lpstrFilter = pszFilters;\r
852         }\r
853         ofn.nFilterIndex = 1;\r
854         ofn.lpstrFileTitle = NULL;\r
855         ofn.nMaxFileTitle = 0;\r
856         ofn.lpstrInitialDir = NULL;\r
857         CString temp;\r
858         if (title)\r
859         {\r
860                 temp.LoadString(title);\r
861                 CStringUtils::RemoveAccelerators(temp);\r
862         }\r
863         ofn.lpstrTitle = temp;\r
864         if (bOpen)\r
865                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;\r
866         else\r
867                 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER;\r
868 \r
869 \r
870         // Display the Open dialog box. \r
871         bool bRet = false;\r
872         if (bOpen)\r
873         {\r
874                 bRet = !!GetOpenFileName(&ofn);\r
875         }\r
876         else\r
877         {\r
878                 bRet = !!GetSaveFileName(&ofn);\r
879         }\r
880         if (bRet)\r
881         {\r
882                 if (pszFilters)\r
883                         delete [] pszFilters;\r
884                 path = CString(ofn.lpstrFile);\r
885                 if (filterindex)\r
886                         *filterindex = ofn.nFilterIndex;\r
887                 return true;\r
888         }\r
889         if (pszFilters)\r
890                 delete [] pszFilters;\r
891         return false;\r
892 }\r
893 \r
894 bool CAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID, int width /* = 128 */, int height /* = 128 */)\r
895 {\r
896         ListView_SetTextBkColor(hListCtrl, CLR_NONE);\r
897         COLORREF bkColor = ListView_GetBkColor(hListCtrl);\r
898         // create a bitmap from the icon\r
899         HICON hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), IMAGE_ICON, width, height, LR_DEFAULTCOLOR);\r
900         if (!hIcon)\r
901                 return false;\r
902 \r
903         RECT rect = {0};\r
904         rect.right = width;\r
905         rect.bottom = height;\r
906         HBITMAP bmp = NULL;\r
907 \r
908         HWND desktop = ::GetDesktopWindow();\r
909         if (desktop)\r
910         {\r
911                 HDC screen_dev = ::GetDC(desktop);\r
912                 if (screen_dev)\r
913                 {\r
914                         // Create a compatible DC\r
915                         HDC dst_hdc = ::CreateCompatibleDC(screen_dev);\r
916                         if (dst_hdc)\r
917                         {\r
918                                 // Create a new bitmap of icon size\r
919                                 bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);\r
920                                 if (bmp)\r
921                                 {\r
922                                         // Select it into the compatible DC\r
923                                         HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);\r
924                                         // Fill the background of the compatible DC with the given color\r
925                                         ::SetBkColor(dst_hdc, bkColor);\r
926                                         ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
927 \r
928                                         // Draw the icon into the compatible DC\r
929                                         ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);\r
930                                         ::SelectObject(dst_hdc, old_dst_bmp);\r
931                                 }\r
932                                 ::DeleteDC(dst_hdc);\r
933                         }\r
934                 }\r
935                 ::ReleaseDC(desktop, screen_dev); \r
936         }\r
937 \r
938         // Restore settings\r
939         DestroyIcon(hIcon);\r
940 \r
941         if (bmp == NULL)\r
942                 return false;\r
943 \r
944         LVBKIMAGE lv;\r
945         lv.ulFlags = LVBKIF_TYPE_WATERMARK;\r
946         lv.hbm = bmp;\r
947         lv.xOffsetPercent = 100;\r
948         lv.yOffsetPercent = 100;\r
949         ListView_SetBkImage(hListCtrl, &lv);\r
950         return true;\r
951 }\r
952 \r
953 CString CAppUtils::GetProjectNameFromURL(CString url)\r
954 {\r
955         CString name;\r
956         while (name.IsEmpty() || (name.CompareNoCase(_T("branches"))==0) ||\r
957                 (name.CompareNoCase(_T("tags"))==0) ||\r
958                 (name.CompareNoCase(_T("trunk"))==0))\r
959         {\r
960                 name = url.Mid(url.ReverseFind('/')+1);\r
961                 url = url.Left(url.ReverseFind('/'));\r
962         }\r
963         if ((name.Compare(_T("svn")) == 0)||(name.Compare(_T("svnroot")) == 0))\r
964         {\r
965                 // a name of svn or svnroot indicates that it's not really the project name. In that\r
966                 // case, we try the first part of the URL\r
967                 // of course, this won't work in all cases (but it works for Google project hosting)\r
968                 url.Replace(_T("http://"), _T(""));\r
969                 url.Replace(_T("https://"), _T(""));\r
970                 url.Replace(_T("svn://"), _T(""));\r
971                 url.Replace(_T("svn+ssh://"), _T(""));\r
972                 url.TrimLeft(_T("/"));\r
973                 name = url.Left(url.Find('.'));\r
974         }\r
975         return name;\r
976 }\r
977 \r
978 bool CAppUtils::StartShowUnifiedDiff(HWND hWnd, const CTGitPath& url1, const git_revnum_t& rev1, \r
979                                                                                                 const CTGitPath& url2, const git_revnum_t& rev2, \r
980                                                                          //const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */,  \r
981                                                                                                 bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool /* blame = false */)\r
982 {\r
983 \r
984         CString tempfile=GetTempFile();\r
985         CString cmd;\r
986         if(rev1 == GitRev::GetWorkingCopy())\r
987         {\r
988                 cmd.Format(_T("git.cmd diff --stat -p %s"),rev2);\r
989         }else\r
990         {       \r
991                 cmd.Format(_T("git.cmd diff-tree -r -p --stat %s %s"),rev1,rev2);\r
992         }\r
993         g_Git.RunLogFile(cmd,tempfile);\r
994         CAppUtils::StartUnifiedDiffViewer(tempfile,rev1.Left(6)+_T(":")+rev2.Left(6));\r
995 \r
996 \r
997 #if 0\r
998         CString sCmd;\r
999         sCmd.Format(_T("%s /command:showcompare /unified"),\r
1000                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
1001         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
1002         if (rev1.IsValid())\r
1003                 sCmd += _T(" /revision1:") + rev1.ToString();\r
1004         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
1005         if (rev2.IsValid())\r
1006                 sCmd += _T(" /revision2:") + rev2.ToString();\r
1007         if (peg.IsValid())\r
1008                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
1009         if (headpeg.IsValid())\r
1010                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
1011 \r
1012         if (bAlternateDiff)\r
1013                 sCmd += _T(" /alternatediff");\r
1014 \r
1015         if (bIgnoreAncestry)\r
1016                 sCmd += _T(" /ignoreancestry");\r
1017 \r
1018         if (hWnd)\r
1019         {\r
1020                 sCmd += _T(" /hwnd:");\r
1021                 TCHAR buf[30];\r
1022                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
1023                 sCmd += buf;\r
1024         }\r
1025 \r
1026         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1027 #endif\r
1028         return TRUE;\r
1029 }\r
1030 \r
1031 bool CAppUtils::StartShowCompare(HWND hWnd, const CTGitPath& url1, const GitRev& rev1, \r
1032                                                                  const CTGitPath& url2, const GitRev& rev2, \r
1033                                                                  const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */, \r
1034                                                                  bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool blame /* = false */)\r
1035 {\r
1036 #if 0\r
1037         CString sCmd;\r
1038         sCmd.Format(_T("%s /command:showcompare"),\r
1039                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
1040         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
1041         if (rev1.IsValid())\r
1042                 sCmd += _T(" /revision1:") + rev1.ToString();\r
1043         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
1044         if (rev2.IsValid())\r
1045                 sCmd += _T(" /revision2:") + rev2.ToString();\r
1046         if (peg.IsValid())\r
1047                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
1048         if (headpeg.IsValid())\r
1049                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
1050         if (bAlternateDiff)\r
1051                 sCmd += _T(" /alternatediff");\r
1052         if (bIgnoreAncestry)\r
1053                 sCmd += _T(" /ignoreancestry");\r
1054         if (blame)\r
1055                 sCmd += _T(" /blame");\r
1056 \r
1057         if (hWnd)\r
1058         {\r
1059                 sCmd += _T(" /hwnd:");\r
1060                 TCHAR buf[30];\r
1061                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
1062                 sCmd += buf;\r
1063         }\r
1064 \r
1065         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1066 #endif\r
1067         return true;\r
1068 }