Git Property Page: get latest change in new thread
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob5963bceef963bf243146155b09845815d973378f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2013 - 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 "UnicodeUtils.h"
27 #include "CreateProcessHelper.h"
28 #include "FormatMessageWrapper.h"
30 #define MAX_STRING_LENGTH 4096 //should be big enough
32 // Nonmember function prototypes
33 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
34 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
36 /////////////////////////////////////////////////////////////////////////////
37 // Dialog procedures and other callback functions
39 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
41 CGitPropertyPage * sheetpage;
43 if (uMessage == WM_INITDIALOG)
45 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
46 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
47 sheetpage->SetHwnd(hwnd);
49 else
51 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
54 if (sheetpage != 0L)
55 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
56 else
57 return FALSE;
60 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
62 // Delete the page before closing.
63 if (PSPCB_RELEASE == uMsg)
65 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
66 if (sheetpage != NULL)
67 delete sheetpage;
69 return 1;
72 // *********************** CGitPropertyPage *************************
73 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
75 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
76 :filenames(newFilenames)
77 ,m_bChanged(false)
78 , m_hwnd(NULL)
82 CGitPropertyPage::~CGitPropertyPage(void)
86 void CGitPropertyPage::SetHwnd(HWND newHwnd)
88 m_hwnd = newHwnd;
91 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
93 switch (uMessage)
95 case WM_INITDIALOG:
97 InitWorkfileView();
98 return TRUE;
100 case WM_NOTIFY:
102 LPNMHDR point = (LPNMHDR)lParam;
103 int code = point->code;
105 // Respond to notifications.
107 if (code == PSN_APPLY && m_bChanged)
111 CTGitPath path(filenames.front().c_str());
112 CString projectTopDir;
113 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
114 break;
116 int stripLength = projectTopDir.GetLength();
117 if (projectTopDir[stripLength - 1] != _T('\\'))
118 stripLength++;
120 CStringA gitdir = CUnicodeUtils::GetMulti(projectTopDir, CP_UTF8);
121 git_repository *repository = NULL;
122 git_index *index = NULL;
124 int ret = git_repository_open(&repository, gitdir.GetBuffer());
125 gitdir.ReleaseBuffer();
126 if (ret)
127 break;
129 if (git_repository_index(&index, repository))
131 git_repository_free(repository);
132 break;
135 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
136 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
137 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
139 bool changed = false;
141 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
143 CTGitPath file;
144 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
145 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
146 int idx = git_index_find(index, pathA);
147 if (idx >= 0)
149 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
151 if (assumeValid == BST_CHECKED)
153 if (!(e->flags & GIT_IDXENTRY_VALID))
155 e->flags |= GIT_IDXENTRY_VALID;
156 changed = true;
159 else if (assumeValid != BST_INDETERMINATE)
161 if (e->flags & GIT_IDXENTRY_VALID)
163 e->flags &= ~GIT_IDXENTRY_VALID;
164 changed = true;
167 if (skipWorktree == BST_CHECKED)
169 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
171 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
172 changed = true;
175 else if (skipWorktree != BST_INDETERMINATE)
177 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
179 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
180 changed = true;
183 if (executable == BST_CHECKED)
185 if (!(e->mode & 0111))
187 e->mode |= 0111;
188 changed = true;
191 else if (executable != BST_INDETERMINATE)
193 if (e->mode & 0111)
195 e->mode &= ~0111;
196 changed = true;
199 if (changed)
200 git_index_add(index, e);
204 if (changed)
206 if (!git_index_write(index))
207 m_bChanged = false;
210 git_index_free(index);
211 } while (0);
213 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
214 return TRUE;
216 case WM_DESTROY:
217 return TRUE;
219 case WM_COMMAND:
220 PageProcOnCommand(wParam);
221 break;
222 } // switch (uMessage)
224 if (uMessage == m_UpdateLastCommit)
226 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
227 return TRUE;
230 return FALSE;
232 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
234 if(HIWORD(wParam) != BN_CLICKED)
235 return;
237 switch (LOWORD(wParam))
239 case IDC_SHOWLOG:
241 tstring gitCmd = _T(" /command:");
242 gitCmd += _T("log /path:\"");
243 gitCmd += filenames.front().c_str();
244 gitCmd += _T("\"");
245 RunCommand(gitCmd);
247 break;
248 case IDC_SHOWSETTINGS:
250 CTGitPath path(filenames.front().c_str());
251 CString projectTopDir;
252 if(!path.HasAdminDir(&projectTopDir))
253 return;
255 tstring gitCmd = _T(" /command:");
256 gitCmd += _T("settings /path:\"");
257 gitCmd += projectTopDir;
258 gitCmd += _T("\"");
259 RunCommand(gitCmd);
261 break;
262 case IDC_ASSUMEVALID:
263 case IDC_SKIPWORKTREE:
264 case IDC_EXECUTABLE:
265 m_bChanged = true;
266 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
267 break;
271 void CGitPropertyPage::RunCommand(const tstring& command)
273 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
274 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
276 // process started - exit
277 return;
280 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseProc launch failed"), MB_OK | MB_ICONINFORMATION);
283 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen)
285 struct tm newtime;
286 SYSTEMTIME systime;
287 TCHAR timebuf[MAX_STRING_LENGTH];
288 TCHAR datebuf[MAX_STRING_LENGTH];
290 LCID locale = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
291 locale = MAKELCID(locale, SORT_DEFAULT);
293 *buf = '\0';
294 if (time)
296 _localtime64_s(&newtime, &time);
298 systime.wDay = (WORD)newtime.tm_mday;
299 systime.wDayOfWeek = (WORD)newtime.tm_wday;
300 systime.wHour = (WORD)newtime.tm_hour;
301 systime.wMilliseconds = 0;
302 systime.wMinute = (WORD)newtime.tm_min;
303 systime.wMonth = (WORD)newtime.tm_mon+1;
304 systime.wSecond = (WORD)newtime.tm_sec;
305 systime.wYear = (WORD)newtime.tm_year+1900;
306 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
307 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
308 else
309 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
310 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
311 *buf = '\0';
312 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
313 _tcsncat_s(buf, buflen, _T(", "), MAX_STRING_LENGTH-1);
314 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
318 struct TreewalkStruct
320 const char *folder;
321 const char *name;
322 git_oid oid;
325 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
327 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
328 char folder[MAX_PATH];
329 strcpy(folder, root);
330 strcat(folder, git_tree_entry_name(entry));
331 strcat(folder, "/");
332 if (strstr(treewalkstruct->folder, folder))
333 return 0;
335 if (!strcmp(treewalkstruct->folder, root))
337 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
339 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
340 return -1;
343 return 1;
346 return 1;
349 static git_commit * FindFileRecentCommit(git_repository *repository, CString path)
351 git_commit *commit = NULL, *commit2 = NULL;
352 git_revwalk *walk;
353 if (git_revwalk_new(&walk, repository))
354 return NULL;
356 CStringA pathA = CUnicodeUtils::GetUTF8(path);
357 const char *pathC = pathA.GetBuffer();
358 char folder[MAX_PATH], file[MAX_PATH];
359 const char *slash = strrchr(pathC, '/');
360 if (slash)
362 strncpy(folder, pathC, slash - pathC + 1);
363 folder[slash - pathC + 1] = '\0';
364 strcpy(file, slash + 1);
366 else
368 folder[0] = '\0';
369 strcpy(file, pathC);
371 pathA.ReleaseBuffer();
373 TreewalkStruct treewalkstruct = { folder, file };
374 TreewalkStruct treewalkstruct2 = { folder, file };
378 if (git_revwalk_push_head(walk))
379 throw "git_revwalk_push_head";
381 git_oid oid;
382 while (!git_revwalk_next(&oid, walk))
384 if (commit != NULL)
386 git_commit_free(commit);
387 commit = NULL;
390 if (git_commit_lookup(&commit, repository, &oid))
392 commit = NULL;
393 throw "git_commit_lookup 1";
396 git_tree *tree;
397 if (git_commit_tree(&tree, commit))
398 throw "git_commit_tree 1";
400 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
401 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
402 git_tree_free(tree);
403 if (ret < 0 && ret != GIT_EUSER)
404 throw "git_tree_walk 1";
406 // check if file not found
407 if (git_oid_iszero(&treewalkstruct.oid))
409 git_commit_free(commit);
410 commit = NULL;
411 break;
414 bool diff = true;
415 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
416 // if no parent then of course it is different
417 for (unsigned int i = 0; i < git_commit_parentcount(commit); i++)
419 if (git_commit_parent(&commit2, commit, i))
421 commit2 = NULL;
422 throw "git_commit_parent";
425 if (git_commit_tree(&tree, commit2))
426 throw "git_commit_tree 2";
428 git_commit_free(commit2);
429 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
430 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
431 git_tree_free(tree);
432 if (ret < 0 && ret != GIT_EUSER)
433 throw "git_tree_walk 2";
435 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
436 diff = false;
437 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
438 throw "git_revwalk_hide";
441 if (diff)
442 break;
445 catch (...)
447 if (commit != NULL)
449 git_commit_free(commit);
450 commit = NULL;
452 if (commit2 != NULL)
454 git_commit_free(commit2);
455 commit2 = NULL;
459 git_revwalk_free(walk);
460 return commit;
463 void CGitPropertyPage::DisplayCommit(git_commit * commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
465 if (commit == NULL)
467 SetDlgItemText(m_hwnd, hashLabel, _T(""));
468 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
469 SetDlgItemText(m_hwnd, authorLabel, _T(""));
470 SetDlgItemText(m_hwnd, dateLabel, _T(""));
471 return;
474 int encode = CP_UTF8;
475 const char * encodingString = git_commit_message_encoding(commit);
476 if (encodingString != NULL)
478 CString str;
479 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
480 encode = CUnicodeUtils::GetCPCode(str);
483 const git_signature * author = git_commit_author(commit);
484 CString authorName;
485 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
487 CString message;
488 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
490 int start = 0;
491 message = message.Tokenize(L"\n", start);
493 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
494 SetDlgItemText(m_hwnd, subjectLabel, message);
495 SetDlgItemText(m_hwnd, authorLabel, authorName);
497 CString authorDate;
498 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
499 SetDlgItemText(m_hwnd, dateLabel, authorDate);
502 int CGitPropertyPage::LogThread()
504 CTGitPath path(filenames.front().c_str());
506 CString ProjectTopDir;
507 if(!path.HasAdminDir(&ProjectTopDir))
508 return 0;
510 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
511 git_repository *repository = NULL;
513 int ret = git_repository_open(&repository, gitdir.GetBuffer());
514 gitdir.ReleaseBuffer();
515 if (ret)
516 return 0;
518 int stripLength = ProjectTopDir.GetLength();
519 if (ProjectTopDir[stripLength - 1] != _T('\\'))
520 stripLength++;
522 CTGitPath relatepath;
523 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
525 git_commit *commit = FindFileRecentCommit(repository, relatepath.GetGitPathString());
526 if (commit != NULL)
528 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)commit);
529 git_commit_free(commit);
531 else
533 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
536 git_repository_free(repository);
538 return 0;
541 void CGitPropertyPage::LogThreadEntry(void *param)
543 ((CGitPropertyPage*)param)->LogThread();
546 void CGitPropertyPage::InitWorkfileView()
548 if (filenames.empty())
549 return;
551 CTGitPath path(filenames.front().c_str());
553 CString ProjectTopDir;
554 if(!path.HasAdminDir(&ProjectTopDir))
555 return;
557 CStringA gitdir = CUnicodeUtils::GetMulti(ProjectTopDir, CP_UTF8);
558 git_repository *repository = NULL;
560 int ret = git_repository_open(&repository, gitdir.GetBuffer());
561 gitdir.ReleaseBuffer();
562 if (ret)
563 return;
565 CGit git;
567 git.SetCurrentDir(ProjectTopDir);
568 CString username;
569 git.Run(_T("git.exe config user.name"), &username, CP_UTF8);
570 CString useremail;
571 git.Run(_T("git.exe config user.email"), &useremail, CP_UTF8);
572 CString autocrlf;
573 git.Run(_T("git.exe config core.autocrlf"), &autocrlf, CP_UTF8);
574 CString safecrlf;
575 git.Run(_T("git.exe config core.safecrlf"), &safecrlf, CP_UTF8);
577 CString branch;
578 CString remotebranch;
580 if (!g_Git.GetCurrentBranchFromFile(ProjectTopDir, branch))
582 CString cmd, remote;
583 cmd.Format(_T("git.exe config branch.%s.merge"), branch.Trim());
584 git.Run(cmd, &remotebranch, CP_UTF8);
585 cmd.Format(_T("git.exe config branch.%s.remote"), branch.Trim());
586 git.Run(cmd, &remote, CP_UTF8);
587 remote.Trim();
588 remotebranch.Trim();
589 if((!remote.IsEmpty()) && (!remotebranch.IsEmpty()))
591 remotebranch = remotebranch.Mid(11);
592 remotebranch = remote + _T("/") + remotebranch;
595 else
596 branch = _T("detached HEAD");
598 if (autocrlf.Trim().IsEmpty())
599 autocrlf = _T("false");
600 if (safecrlf.Trim().IsEmpty())
601 safecrlf = _T("false");
603 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
604 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
605 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
606 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
608 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
609 remotebranch.Trim().Replace(_T("\n"), _T("; "));
610 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
612 git_oid oid;
613 git_commit *HEADcommit = NULL;
614 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(&HEADcommit, repository, &oid) && HEADcommit != NULL)
616 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
617 git_commit_free(HEADcommit);
621 int stripLength = ProjectTopDir.GetLength();
622 if (ProjectTopDir[stripLength - 1] != _T('\\'))
623 stripLength++;
625 bool allAreFiles = true;
626 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
628 if (PathIsDirectory(it->c_str()))
630 allAreFiles = false;
631 break;
634 if (allAreFiles)
636 int assumevalid = 0;
637 int skipworktree = 0;
638 int executable = 0;
641 git_index *index = NULL;
643 if (git_repository_index(&index, repository))
644 break;
646 for (auto it = filenames.cbegin(); it < filenames.cend(); it++)
648 CTGitPath file;
649 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
650 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
651 int idx = git_index_find(index, pathA);
652 if (idx >= 0)
654 const git_index_entry *e = git_index_get_byindex(index, idx);
656 if (e->flags & GIT_IDXENTRY_VALID)
657 ++assumevalid;
659 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
660 ++skipworktree;
662 if (e->mode & 0111)
663 ++executable;
665 else
667 // do not show checkboxes for unversioned files
668 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
669 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
670 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
671 break;
674 git_index_free(index);
675 } while (0);
677 if (assumevalid != 0 && assumevalid != filenames.size())
679 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
680 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
682 else
683 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
685 if (skipworktree != 0 && skipworktree != filenames.size())
687 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
688 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
690 else
691 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
693 if (executable != 0 && executable != filenames.size())
695 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
696 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
698 else
699 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
701 else
703 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
704 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
705 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
708 git_repository_free(repository);
710 if (filenames.size() == 1)
712 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, _T("Loading ..."));
713 _beginthread(LogThreadEntry, 0, this);
718 // CShellExt member functions (needed for IShellPropSheetExt)
719 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
721 __try
723 return AddPages_Wrap(lpfnAddPage, lParam);
725 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
728 return E_FAIL;
731 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
733 CString ProjectTopDir;
735 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
738 GitStatus svn = GitStatus();
739 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
740 return S_OK; // file/directory not under version control
742 if (svn.status->entry == NULL)
743 return S_OK;
745 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
746 break;
747 else
748 return S_OK;
751 if (files_.empty())
752 return S_OK;
754 LoadLangDll();
755 PROPSHEETPAGE psp;
756 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
757 HPROPSHEETPAGE hPage;
758 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
760 psp.dwSize = sizeof (psp);
761 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
762 psp.hInstance = g_hResInst;
763 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
764 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
765 psp.pszTitle = _T("Git");
766 psp.pfnDlgProc = (DLGPROC) PageProc;
767 psp.lParam = (LPARAM) sheetpage;
768 psp.pfnCallback = PropPageCallbackProc;
769 psp.pcRefParent = (UINT*)&g_cRefThisDll;
771 hPage = CreatePropertySheetPage (&psp);
773 if (hPage != NULL)
775 if (!lpfnAddPage (hPage, lParam))
777 delete sheetpage;
778 DestroyPropertySheetPage (hPage);
782 return S_OK;
785 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
787 return E_FAIL;