1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "svnpropertypage.h"
23 #include "UnicodeUtils.h"
24 #include "PathUtils.h"
25 #include "GitStatus.h"
27 #define MAX_STRING_LENGTH 4096 //should be big enough
29 // Nonmember function prototypes
30 BOOL CALLBACK
PageProc (HWND
, UINT
, WPARAM
, LPARAM
);
31 UINT CALLBACK
PropPageCallbackProc ( HWND hwnd
, UINT uMsg
, LPPROPSHEETPAGE ppsp
);
33 /////////////////////////////////////////////////////////////////////////////
34 // Dialog procedures and other callback functions
36 BOOL CALLBACK
PageProc (HWND hwnd
, UINT uMessage
, WPARAM wParam
, LPARAM lParam
)
38 CGitPropertyPage
* sheetpage
;
40 if (uMessage
== WM_INITDIALOG
)
42 sheetpage
= (CGitPropertyPage
*) ((LPPROPSHEETPAGE
) lParam
)->lParam
;
43 SetWindowLongPtr (hwnd
, GWLP_USERDATA
, (LONG_PTR
) sheetpage
);
44 sheetpage
->SetHwnd(hwnd
);
48 sheetpage
= (CGitPropertyPage
*) GetWindowLongPtr (hwnd
, GWLP_USERDATA
);
52 return sheetpage
->PageProc(hwnd
, uMessage
, wParam
, lParam
);
57 UINT CALLBACK
PropPageCallbackProc ( HWND
/*hwnd*/, UINT uMsg
, LPPROPSHEETPAGE ppsp
)
59 // Delete the page before closing.
60 if (PSPCB_RELEASE
== uMsg
)
62 CGitPropertyPage
* sheetpage
= (CGitPropertyPage
*) ppsp
->lParam
;
63 if (sheetpage
!= NULL
)
69 // *********************** CGitPropertyPage *************************
71 CGitPropertyPage::CGitPropertyPage(const std::vector
<stdstring
> &newFilenames
)
72 :filenames(newFilenames
)
76 CGitPropertyPage::~CGitPropertyPage(void)
80 void CGitPropertyPage::SetHwnd(HWND newHwnd
)
85 BOOL
CGitPropertyPage::PageProc (HWND
/*hwnd*/, UINT uMessage
, WPARAM wParam
, LPARAM lParam
)
96 LPNMHDR point
= (LPNMHDR
)lParam
;
97 int code
= point
->code
;
99 // Respond to notifications.
101 if (code
== PSN_APPLY
)
104 SetWindowLongPtr (m_hwnd
, DWLP_MSGRESULT
, FALSE
);
112 switch (HIWORD(wParam
))
115 if (LOWORD(wParam
) == IDC_SHOWLOG
)
118 PROCESS_INFORMATION process
;
119 memset(&startup
, 0, sizeof(startup
));
120 startup
.cb
= sizeof(startup
);
121 memset(&process
, 0, sizeof(process
));
122 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
123 stdstring svnCmd
= _T(" /command:");
124 svnCmd
+= _T("log /path:\"");
125 svnCmd
+= filenames
.front().c_str();
127 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
129 CloseHandle(process
.hThread
);
130 CloseHandle(process
.hProcess
);
133 if (LOWORD(wParam
) == IDC_EDITPROPERTIES
)
135 DWORD pathlength
= GetTempPath(0, NULL
);
136 TCHAR
* path
= new TCHAR
[pathlength
+1];
137 TCHAR
* tempFile
= new TCHAR
[pathlength
+ 100];
138 GetTempPath (pathlength
+1, path
);
139 GetTempFileName (path
, _T("svn"), 0, tempFile
);
140 stdstring retFilePath
= stdstring(tempFile
);
142 HANDLE file
= ::CreateFile (tempFile
,
147 FILE_ATTRIBUTE_TEMPORARY
,
152 if (file
!= INVALID_HANDLE_VALUE
)
155 for (std::vector
<stdstring
>::iterator I
= filenames
.begin(); I
!= filenames
.end(); ++I
)
157 ::WriteFile (file
, I
->c_str(), I
->size()*sizeof(TCHAR
), &written
, 0);
158 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
163 PROCESS_INFORMATION process
;
164 memset(&startup
, 0, sizeof(startup
));
165 startup
.cb
= sizeof(startup
);
166 memset(&process
, 0, sizeof(process
));
167 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
168 stdstring svnCmd
= _T(" /command:");
169 svnCmd
+= _T("properties /pathfile:\"");
170 svnCmd
+= retFilePath
.c_str();
172 svnCmd
+= _T(" /deletepathfile");
173 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
175 CloseHandle(process
.hThread
);
176 CloseHandle(process
.hProcess
);
181 } // switch (HIWORD(wParam))
182 } // switch (uMessage)
185 void CGitPropertyPage::Time64ToTimeString(__time64_t time
, TCHAR
* buf
, size_t buflen
)
189 TCHAR timebuf
[MAX_STRING_LENGTH
];
190 TCHAR datebuf
[MAX_STRING_LENGTH
];
192 LCID locale
= (WORD
)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
));
193 locale
= MAKELCID(locale
, SORT_DEFAULT
);
198 _localtime64_s(&newtime
, &time
);
200 systime
.wDay
= (WORD
)newtime
.tm_mday
;
201 systime
.wDayOfWeek
= (WORD
)newtime
.tm_wday
;
202 systime
.wHour
= (WORD
)newtime
.tm_hour
;
203 systime
.wMilliseconds
= 0;
204 systime
.wMinute
= (WORD
)newtime
.tm_min
;
205 systime
.wMonth
= (WORD
)newtime
.tm_mon
+1;
206 systime
.wSecond
= (WORD
)newtime
.tm_sec
;
207 systime
.wYear
= (WORD
)newtime
.tm_year
+1900;
208 if (CRegStdWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
209 GetDateFormat(locale
, DATE_SHORTDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
211 GetDateFormat(locale
, DATE_LONGDATE
, &systime
, NULL
, datebuf
, MAX_STRING_LENGTH
);
212 GetTimeFormat(locale
, 0, &systime
, NULL
, timebuf
, MAX_STRING_LENGTH
);
214 _tcsncat_s(buf
, buflen
, timebuf
, MAX_STRING_LENGTH
-1);
215 _tcsncat_s(buf
, buflen
, _T(", "), MAX_STRING_LENGTH
-1);
216 _tcsncat_s(buf
, buflen
, datebuf
, MAX_STRING_LENGTH
-1);
220 void CGitPropertyPage::InitWorkfileView()
224 if( filenames
.size() ==0)
227 CTGitPath
path(filenames
.front().c_str());
228 CString ProjectTopDir
;
230 if(!path
.HasAdminDir(&ProjectTopDir
))
233 git
.SetCurrentDir(ProjectTopDir
);
234 //can't git.exe when create process
235 git
.Run(_T("git.exe config user.name"),&username
,CP_ACP
);
237 git
.Run(_T("git.exe config user.email"),&useremail
,CP_ACP
);
239 git
.Run(_T("git.exe config core.autocrlf"),&autocrlf
,CP_ACP
);
241 git
.Run(_T("git.exe config core.safecrlf"),&safecrlf
,CP_ACP
);
245 CString remotebranch
;
247 git
.Run(_T("git.exe symbolic-ref HEAD"),&branch
,CP_ACP
);
250 if(!branch
.IsEmpty())
252 if( branch
.Find(_T("refs/heads/"),0) >=0 )
255 branch
=branch
.Mid(11);
257 branch
=branch
.Tokenize(_T("\n"),start
);
259 branch
=branch
.Tokenize(_T("\r"),start
);
260 cmd
.Format(_T("git.exe config branch.%s.merge"),branch
);
261 git
.Run(cmd
,&remotebranch
,CP_ACP
);
262 cmd
.Format(_T("git.exe config branch.%s.remote"),branch
);
263 git
.Run(cmd
,&remote
,CP_ACP
);
264 if((!remote
.IsEmpty()) && (!remotebranch
.IsEmpty()))
266 remotebranch
=remotebranch
.Mid(11);
267 remotebranch
=remote
+_T("/")+remotebranch
;
276 cmd
=_T("git.exe log -z --topo-order -n1 --parents --pretty=format:\"");
278 git
.BuildOutputFormat(log
,true);
281 cmd
+= CString(_T("\" "));
283 git
.Run(cmd
,&logout
);
285 rev
.ParserFromLog(logout
);
287 SetDlgItemText(m_hwnd
,IDC_CONFIG_USERNAME
,username
);
288 SetDlgItemText(m_hwnd
,IDC_CONFIG_USEREMAIL
,useremail
);
289 SetDlgItemText(m_hwnd
,IDC_CONFIG_AUTOCRLF
,autocrlf
);
290 SetDlgItemText(m_hwnd
,IDC_CONFIG_SAFECRLF
,safecrlf
);
292 SetDlgItemText(m_hwnd
,IDC_SHELL_CURRENT_BRANCH
,branch
);
293 SetDlgItemText(m_hwnd
,IDC_SHELL_REMOTE_BRANCH
,remotebranch
);
295 SetDlgItemText(m_hwnd
,IDC_HEAD_HASH
,rev
.m_CommitHash
.ToString());
296 SetDlgItemText(m_hwnd
,IDC_HEAD_SUBJECT
,rev
.m_Subject
);
297 SetDlgItemText(m_hwnd
,IDC_HEAD_AUTHOR
,rev
.m_AuthorName
);
298 SetDlgItemText(m_hwnd
,IDC_HEAD_DATE
,rev
.m_AuthorDate
.Format(_T("%Y-%m-%d %H:%M:%S")));
300 if (filenames
.size() == 1)
302 CTGitPath
path(filenames
.front().c_str());
303 CTGitPath relatepath
;
304 CString strpath
=path
.GetWinPathString();
305 CString ProjectTopDir
;
307 if(!path
.HasAdminDir(&ProjectTopDir
))
310 if(ProjectTopDir
[ProjectTopDir
.GetLength()-1] == _T('\\'))
312 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()));
315 relatepath
.SetFromWin( strpath
.Right(strpath
.GetLength()-ProjectTopDir
.GetLength()-1));
319 cmd
+=relatepath
.GetGitPathString();
322 if(! relatepath
.GetGitPathString().IsEmpty())
325 git
.Run(cmd
,&logout
);
327 rev
.ParserFromLog(logout
);
330 SetDlgItemText(m_hwnd
,IDC_LAST_HASH
,rev
.m_CommitHash
.ToString());
331 SetDlgItemText(m_hwnd
,IDC_LAST_SUBJECT
,rev
.m_Subject
);
332 SetDlgItemText(m_hwnd
,IDC_LAST_AUTHOR
,rev
.m_AuthorName
);
333 SetDlgItemText(m_hwnd
,IDC_LAST_DATE
,rev
.m_AuthorDate
.Format(_T("%Y-%m-%d %H:%M:%S")));
343 // CShellExt member functions (needed for IShellPropSheetExt)
344 STDMETHODIMP
CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage
,
348 CString ProjectTopDir
;
350 for (std::vector
<stdstring
>::iterator I
= files_
.begin(); I
!= files_
.end(); ++I
)
353 GitStatus svn = GitStatus();
354 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
355 return NOERROR; // file/directory not under version control
357 if (svn.status->entry == NULL)
360 if( CTGitPath(I
->c_str()).HasAdminDir(&ProjectTopDir
))
366 if (files_
.size() == 0)
371 SecureZeroMemory(&psp
, sizeof(PROPSHEETPAGE
));
372 HPROPSHEETPAGE hPage
;
373 CGitPropertyPage
*sheetpage
= new CGitPropertyPage(files_
);
375 psp
.dwSize
= sizeof (psp
);
376 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
| PSP_USEICONID
| PSP_USECALLBACK
;
377 psp
.hInstance
= g_hResInst
;
378 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_PROPPAGE
);
379 psp
.pszIcon
= MAKEINTRESOURCE(IDI_APPSMALL
);
380 psp
.pszTitle
= _T("Git");
381 psp
.pfnDlgProc
= (DLGPROC
) PageProc
;
382 psp
.lParam
= (LPARAM
) sheetpage
;
383 psp
.pfnCallback
= PropPageCallbackProc
;
384 psp
.pcRefParent
= &g_cRefThisDll
;
386 hPage
= CreatePropertySheetPage (&psp
);
390 if (!lpfnAddPage (hPage
, lParam
))
393 DestroyPropertySheetPage (hPage
);
402 STDMETHODIMP
CShellExt::ReplacePage (UINT
/*uPageID*/, LPFNADDPROPSHEETPAGE
/*lpfnReplaceWith*/, LPARAM
/*lParam*/)