Merge branch 'bare-repo'
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
bloba68c0cfae8784117d6cdf9b8cd391cbf699dfc2f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2003-2011 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
22 #include "ShellExt.h"
23 #include "gitpropertypage.h"
24 #include "UnicodeUtils.h"
25 #include "PathUtils.h"
26 #include "GitStatus.h"
28 #define MAX_STRING_LENGTH 4096 //should be big enough
30 // Nonmember function prototypes
31 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
32 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
34 /////////////////////////////////////////////////////////////////////////////
35 // Dialog procedures and other callback functions
37 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
39 CGitPropertyPage * sheetpage;
41 if (uMessage == WM_INITDIALOG)
43 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
44 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
45 sheetpage->SetHwnd(hwnd);
47 else
49 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
52 if (sheetpage != 0L)
53 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
54 else
55 return FALSE;
58 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
60 // Delete the page before closing.
61 if (PSPCB_RELEASE == uMsg)
63 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
64 if (sheetpage != NULL)
65 delete sheetpage;
67 return 1;
70 // *********************** CGitPropertyPage *************************
72 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
73 :filenames(newFilenames)
77 CGitPropertyPage::~CGitPropertyPage(void)
81 void CGitPropertyPage::SetHwnd(HWND newHwnd)
83 m_hwnd = newHwnd;
86 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
88 switch (uMessage)
90 case WM_INITDIALOG:
92 InitWorkfileView();
93 return TRUE;
95 case WM_NOTIFY:
97 LPNMHDR point = (LPNMHDR)lParam;
98 int code = point->code;
100 // Respond to notifications.
102 if (code == PSN_APPLY)
105 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
106 return TRUE;
109 case WM_DESTROY:
110 return TRUE;
112 case WM_COMMAND:
113 switch (HIWORD(wParam))
115 case BN_CLICKED:
116 if (LOWORD(wParam) == IDC_SHOWLOG)
118 STARTUPINFO startup;
119 PROCESS_INFORMATION process;
120 memset(&startup, 0, sizeof(startup));
121 startup.cb = sizeof(startup);
122 memset(&process, 0, sizeof(process));
123 CRegStdString tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE);
124 stdstring gitCmd = _T(" /command:");
125 gitCmd += _T("log /path:\"");
126 gitCmd += filenames.front().c_str();
127 gitCmd += _T("\"");
128 if (CreateProcess(((stdstring)tortoiseProcPath).c_str(), const_cast<TCHAR*>(gitCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process))
130 CloseHandle(process.hThread);
131 CloseHandle(process.hProcess);
134 if (LOWORD(wParam) == IDC_EDITPROPERTIES)
136 DWORD pathlength = GetTempPath(0, NULL);
137 TCHAR * path = new TCHAR[pathlength+1];
138 TCHAR * tempFile = new TCHAR[pathlength + 100];
139 GetTempPath (pathlength+1, path);
140 GetTempFileName (path, _T("git"), 0, tempFile);
141 stdstring retFilePath = stdstring(tempFile);
143 HANDLE file = ::CreateFile (tempFile,
144 GENERIC_WRITE,
145 FILE_SHARE_READ,
147 CREATE_ALWAYS,
148 FILE_ATTRIBUTE_TEMPORARY,
151 delete [] path;
152 delete [] tempFile;
153 if (file != INVALID_HANDLE_VALUE)
155 DWORD written = 0;
156 for (std::vector<stdstring>::iterator I = filenames.begin(); I != filenames.end(); ++I)
158 ::WriteFile (file, I->c_str(), I->size()*sizeof(TCHAR), &written, 0);
159 ::WriteFile (file, _T("\n"), 2, &written, 0);
161 ::CloseHandle(file);
163 STARTUPINFO startup;
164 PROCESS_INFORMATION process;
165 memset(&startup, 0, sizeof(startup));
166 startup.cb = sizeof(startup);
167 memset(&process, 0, sizeof(process));
168 CRegStdString tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE);
169 stdstring gitCmd = _T(" /command:");
170 gitCmd += _T("properties /pathfile:\"");
171 gitCmd += retFilePath.c_str();
172 gitCmd += _T("\"");
173 gitCmd += _T(" /deletepathfile");
174 if (CreateProcess(((stdstring)tortoiseProcPath).c_str(), const_cast<TCHAR*>(gitCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process))
176 CloseHandle(process.hThread);
177 CloseHandle(process.hProcess);
181 break;
182 } // switch (HIWORD(wParam))
183 } // switch (uMessage)
184 return FALSE;
186 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen)
188 struct tm newtime;
189 SYSTEMTIME systime;
190 TCHAR timebuf[MAX_STRING_LENGTH];
191 TCHAR datebuf[MAX_STRING_LENGTH];
193 LCID locale = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
194 locale = MAKELCID(locale, SORT_DEFAULT);
196 *buf = '\0';
197 if (time)
199 _localtime64_s(&newtime, &time);
201 systime.wDay = (WORD)newtime.tm_mday;
202 systime.wDayOfWeek = (WORD)newtime.tm_wday;
203 systime.wHour = (WORD)newtime.tm_hour;
204 systime.wMilliseconds = 0;
205 systime.wMinute = (WORD)newtime.tm_min;
206 systime.wMonth = (WORD)newtime.tm_mon+1;
207 systime.wSecond = (WORD)newtime.tm_sec;
208 systime.wYear = (WORD)newtime.tm_year+1900;
209 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
210 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
211 else
212 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
213 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
214 *buf = '\0';
215 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
216 _tcsncat_s(buf, buflen, _T(", "), MAX_STRING_LENGTH-1);
217 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
221 void CGitPropertyPage::InitWorkfileView()
223 CString username;
224 CGit git;
225 if( filenames.size() ==0)
226 return;
228 CTGitPath path(filenames.front().c_str());
229 CString ProjectTopDir;
231 if(!path.HasAdminDir(&ProjectTopDir))
232 return;
234 git.SetCurrentDir(ProjectTopDir);
235 //can't git.exe when create process
236 git.Run(_T("git.exe config user.name"),&username,CP_ACP);
237 CString useremail;
238 git.Run(_T("git.exe config user.email"),&useremail,CP_ACP);
239 CString autocrlf;
240 git.Run(_T("git.exe config core.autocrlf"),&autocrlf,CP_ACP);
241 CString safecrlf;
242 git.Run(_T("git.exe config core.safecrlf"),&safecrlf,CP_ACP);
244 CString branch;
245 CString headhash;
246 CString remotebranch;
248 git.Run(_T("git.exe symbolic-ref HEAD"),&branch,CP_ACP);
249 CString cmd,log;
251 if(!branch.IsEmpty())
253 if( branch.Find(_T("refs/heads/"),0) >=0 )
255 CString remote;
256 branch=branch.Mid(11);
257 int start=0;
258 branch=branch.Tokenize(_T("\n"),start);
259 start=0;
260 branch=branch.Tokenize(_T("\r"),start);
261 cmd.Format(_T("git.exe config branch.%s.merge"),branch);
262 git.Run(cmd,&remotebranch,CP_ACP);
263 cmd.Format(_T("git.exe config branch.%s.remote"),branch);
264 git.Run(cmd,&remote,CP_ACP);
265 if((!remote.IsEmpty()) && (!remotebranch.IsEmpty()))
267 remotebranch=remotebranch.Mid(11);
268 remotebranch=remote+_T("/")+remotebranch;
273 TCHAR oldpath[MAX_PATH+1];
275 ::GetCurrentDirectory(MAX_PATH, oldpath);
277 ::SetCurrentDirectory(ProjectTopDir);
279 GitRev rev;
281 if (autocrlf.Trim().IsEmpty())
282 autocrlf = _T("false");
283 if (safecrlf.Trim().IsEmpty())
284 safecrlf = _T("false");
286 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
287 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
288 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
289 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
291 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
292 remotebranch.Trim().Replace(_T("\n"), _T("; "));
293 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
297 AutoLocker lock(g_Git.m_critGitDllSec);
298 g_Git.CheckAndInitDll();
299 rev.GetCommit(CString(_T("HEAD")));
301 SetDlgItemText(m_hwnd,IDC_HEAD_HASH,rev.m_CommitHash.ToString());
302 SetDlgItemText(m_hwnd,IDC_HEAD_SUBJECT,rev.GetSubject());
303 SetDlgItemText(m_hwnd,IDC_HEAD_AUTHOR,rev.GetAuthorName());
304 SetDlgItemText(m_hwnd,IDC_HEAD_DATE,rev.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
306 if (filenames.size() == 1)
308 CTGitPath path(filenames.front().c_str());
309 CTGitPath relatepath;
310 CString strpath=path.GetWinPathString();
311 CString ProjectTopDir;
313 if(!path.HasAdminDir(&ProjectTopDir))
314 return;
316 if(ProjectTopDir[ProjectTopDir.GetLength()-1] == _T('\\'))
318 relatepath.SetFromWin( strpath.Right(strpath.GetLength()-ProjectTopDir.GetLength()));
320 else
322 relatepath.SetFromWin( strpath.Right(strpath.GetLength()-ProjectTopDir.GetLength()-1));
326 if(! relatepath.GetGitPathString().IsEmpty())
328 cmd=_T("-z --topo-order -n1 --parents -- \"");
329 cmd+=relatepath.GetGitPathString();
330 cmd+=_T("\"");
332 GIT_LOG handle;
335 if(git_open_log(&handle, CUnicodeUtils::GetUTF8(cmd).GetBuffer()))
336 break;
337 if(git_get_log_firstcommit(handle))
338 break;
340 GIT_COMMIT commit;
341 if (git_get_log_nextcommit(handle, &commit, 0))
342 break;
344 git_close_log(handle);
345 handle = NULL;
346 rev.ParserFromCommit(&commit);
347 git_free_commit(&commit);
349 }while(0);
350 if (handle != NULL) {
351 git_close_log(handle);
355 SetDlgItemText(m_hwnd,IDC_LAST_HASH,rev.m_CommitHash.ToString());
356 SetDlgItemText(m_hwnd,IDC_LAST_SUBJECT,rev.GetSubject());
357 SetDlgItemText(m_hwnd,IDC_LAST_AUTHOR,rev.GetAuthorName());
358 SetDlgItemText(m_hwnd,IDC_LAST_DATE,rev.GetAuthorDate().Format(_T("%Y-%m-%d %H:%M:%S")));
361 }catch(...)
364 ::SetCurrentDirectory(oldpath);
369 // CShellExt member functions (needed for IShellPropSheetExt)
370 STDMETHODIMP CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage,
371 LPARAM lParam)
374 CString ProjectTopDir;
376 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
379 GitStatus svn = GitStatus();
380 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
381 return NOERROR; // file/directory not under version control
383 if (svn.status->entry == NULL)
384 return NOERROR;
386 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
387 break;
388 else
389 return NOERROR;
392 if (files_.size() == 0)
393 return NOERROR;
395 LoadLangDll();
396 PROPSHEETPAGE psp;
397 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
398 HPROPSHEETPAGE hPage;
399 CGitPropertyPage *sheetpage = new CGitPropertyPage(files_);
401 psp.dwSize = sizeof (psp);
402 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
403 psp.hInstance = g_hResInst;
404 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
405 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
406 psp.pszTitle = _T("Git");
407 psp.pfnDlgProc = (DLGPROC) PageProc;
408 psp.lParam = (LPARAM) sheetpage;
409 psp.pfnCallback = PropPageCallbackProc;
410 psp.pcRefParent = &g_cRefThisDll;
412 hPage = CreatePropertySheetPage (&psp);
414 if (hPage != NULL)
416 if (!lpfnAddPage (hPage, lParam))
418 delete sheetpage;
419 DestroyPropertySheetPage (hPage);
423 return NOERROR;
426 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
428 return E_FAIL;