Use CUnicodeUtils::GetUnicode instead of CGit::StringAppend if possible
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob9bc31e1250f7f14e1cfdb7d0bf83673ae9dd3fb3
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2014 - 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 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
32 #define MAX_STRING_LENGTH 4096 //should be big enough
34 // Nonmember function prototypes
35 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
36 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
38 /////////////////////////////////////////////////////////////////////////////
39 // Dialog procedures and other callback functions
41 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
43 CGitPropertyPage * sheetpage;
45 if (uMessage == WM_INITDIALOG)
47 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
48 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
49 sheetpage->SetHwnd(hwnd);
51 else
53 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
56 if (sheetpage != 0L)
57 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
58 else
59 return FALSE;
62 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
64 // Delete the page before closing.
65 if (PSPCB_RELEASE == uMsg)
67 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
68 if (sheetpage != NULL)
69 delete sheetpage;
71 return 1;
74 // *********************** CGitPropertyPage *************************
75 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
77 CGitPropertyPage::CGitPropertyPage(const std::vector<stdstring> &newFilenames)
78 :filenames(newFilenames)
79 ,m_bChanged(false)
80 , m_hwnd(NULL)
84 CGitPropertyPage::~CGitPropertyPage(void)
88 void CGitPropertyPage::SetHwnd(HWND newHwnd)
90 m_hwnd = newHwnd;
93 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
95 switch (uMessage)
97 case WM_INITDIALOG:
99 InitWorkfileView();
100 return TRUE;
102 case WM_NOTIFY:
104 LPNMHDR point = (LPNMHDR)lParam;
105 int code = point->code;
107 // Respond to notifications.
109 if (code == PSN_APPLY && m_bChanged)
113 CTGitPath path(filenames.front().c_str());
114 CString projectTopDir;
115 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
116 break;
118 int stripLength = projectTopDir.GetLength();
119 if (projectTopDir[stripLength - 1] != _T('\\'))
120 ++stripLength;
122 CAutoRepository repository(CUnicodeUtils::GetUTF8(projectTopDir));
123 if (!repository)
124 break;
126 CAutoIndex index;
127 if (git_repository_index(index.GetPointer(), repository))
128 break;
130 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
131 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
132 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
133 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
135 bool changed = false;
137 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
139 CTGitPath file;
140 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
141 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
142 size_t idx;
143 if (!git_index_find(&idx, index, pathA))
145 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
147 if (assumeValid == BST_CHECKED)
149 if (!(e->flags & GIT_IDXENTRY_VALID))
151 e->flags |= GIT_IDXENTRY_VALID;
152 changed = true;
155 else if (assumeValid != BST_INDETERMINATE)
157 if (e->flags & GIT_IDXENTRY_VALID)
159 e->flags &= ~GIT_IDXENTRY_VALID;
160 changed = true;
163 if (skipWorktree == BST_CHECKED)
165 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
167 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
168 changed = true;
171 else if (skipWorktree != BST_INDETERMINATE)
173 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
175 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
176 changed = true;
179 if (executable == BST_CHECKED)
181 if (!(e->mode & 0111))
183 e->mode |= 0111;
184 changed = true;
187 else if (executable != BST_INDETERMINATE)
189 if (e->mode & 0111)
191 e->mode &= ~0111;
192 changed = true;
195 if (symlink == BST_CHECKED)
197 if ((e->mode & GIT_FILEMODE_LINK) != GIT_FILEMODE_LINK)
199 e->mode |= GIT_FILEMODE_LINK;
200 changed = true;
203 else if (symlink != BST_INDETERMINATE)
205 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
207 e->mode &= ~GIT_FILEMODE_LINK;
208 changed = true;
211 if (changed)
212 git_index_add(index, e);
216 if (changed)
218 if (!git_index_write(index))
219 m_bChanged = false;
221 } while (0);
223 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
224 return TRUE;
226 case WM_DESTROY:
227 return TRUE;
229 case WM_COMMAND:
230 PageProcOnCommand(wParam);
231 break;
232 } // switch (uMessage)
234 if (uMessage == m_UpdateLastCommit)
236 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
237 return TRUE;
240 return FALSE;
242 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
244 if(HIWORD(wParam) != BN_CLICKED)
245 return;
247 switch (LOWORD(wParam))
249 case IDC_SHOWLOG:
251 tstring gitCmd = _T(" /command:");
252 gitCmd += _T("log /path:\"");
253 gitCmd += filenames.front().c_str();
254 gitCmd += _T("\"");
255 RunCommand(gitCmd);
257 break;
258 case IDC_SHOWSETTINGS:
260 CTGitPath path(filenames.front().c_str());
261 CString projectTopDir;
262 if(!path.HasAdminDir(&projectTopDir))
263 return;
265 tstring gitCmd = _T(" /command:");
266 gitCmd += _T("settings /path:\"");
267 gitCmd += projectTopDir;
268 gitCmd += _T("\"");
269 RunCommand(gitCmd);
271 break;
272 case IDC_ASSUMEVALID:
273 case IDC_SKIPWORKTREE:
274 case IDC_EXECUTABLE:
275 case IDC_SYMLINK:
276 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
277 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
278 if (executable == BST_CHECKED)
280 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), FALSE);
281 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_UNCHECKED, 0);
283 else
284 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
285 if (symlink == BST_CHECKED)
287 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), FALSE);
288 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_UNCHECKED, 0);
290 else
291 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
292 m_bChanged = true;
293 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
294 break;
298 void CGitPropertyPage::RunCommand(const tstring& command)
300 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
301 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
303 // process started - exit
304 return;
307 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseGitProc launch failed"), MB_OK | MB_ICONINFORMATION);
310 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen) const
312 struct tm newtime;
313 SYSTEMTIME systime;
315 LCID locale = LOCALE_USER_DEFAULT;
316 if (!CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))
317 locale = MAKELCID((WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), SORT_DEFAULT);
319 *buf = '\0';
320 if (time)
322 TCHAR timebuf[MAX_STRING_LENGTH] = { 0 };
323 TCHAR datebuf[MAX_STRING_LENGTH] = { 0 };
324 _localtime64_s(&newtime, &time);
326 systime.wDay = (WORD)newtime.tm_mday;
327 systime.wDayOfWeek = (WORD)newtime.tm_wday;
328 systime.wHour = (WORD)newtime.tm_hour;
329 systime.wMilliseconds = 0;
330 systime.wMinute = (WORD)newtime.tm_min;
331 systime.wMonth = (WORD)newtime.tm_mon+1;
332 systime.wSecond = (WORD)newtime.tm_sec;
333 systime.wYear = (WORD)newtime.tm_year+1900;
334 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
335 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
336 else
337 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
338 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
339 *buf = '\0';
340 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
341 _tcsncat_s(buf, buflen, _T(" "), MAX_STRING_LENGTH-1);
342 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
346 struct TreewalkStruct
348 const char *folder;
349 const char *name;
350 git_oid oid;
353 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
355 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
356 char folder[MAX_PATH] = {0};
357 strcpy_s(folder, root);
358 strcat_s(folder, git_tree_entry_name(entry));
359 strcat_s(folder, "/");
360 if (strstr(treewalkstruct->folder, folder))
361 return 0;
363 if (!strcmp(treewalkstruct->folder, root))
365 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
367 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
368 return GIT_EUSER;
371 return 1;
374 return 1;
377 static git_commit* FindFileRecentCommit(git_repository* repository, const CString& path)
379 CAutoRevwalk walk;
380 if (git_revwalk_new(walk.GetPointer(), repository))
381 return nullptr;
383 CStringA pathA = CUnicodeUtils::GetUTF8(path);
384 if (pathA.GetLength() >= MAX_PATH)
385 return nullptr;
386 const char *pathC = pathA;
387 char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
388 const char *slash = strrchr(pathC, '/');
389 if (slash)
391 strncpy(folder, pathC, slash - pathC + 1);
392 folder[slash - pathC + 1] = '\0';
393 strcpy(file, slash + 1);
395 else
397 folder[0] = '\0';
398 strcpy(file, pathC);
401 TreewalkStruct treewalkstruct = { folder, file };
403 if (git_revwalk_push_head(walk))
404 return nullptr;
406 git_oid oid;
407 CAutoCommit commit;
408 while (!git_revwalk_next(&oid, walk))
410 commit.Free();
411 if (git_commit_lookup(commit.GetPointer(), repository, &oid))
412 return nullptr;
414 CAutoTree tree;
415 if (git_commit_tree(tree.GetPointer(), commit))
416 return nullptr;
418 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
419 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
421 if (ret < 0 && ret != GIT_EUSER)
422 return nullptr;
424 // check if file not found
425 if (git_oid_iszero(&treewalkstruct.oid))
426 return nullptr;
428 bool diff = true;
429 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
430 // if no parent then of course it is different
431 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
433 CAutoCommit commit2;
434 if (git_commit_parent(commit2.GetPointer(), commit, i))
435 return nullptr;
437 CAutoTree tree2;
438 if (git_commit_tree(tree2.GetPointer(), commit2))
439 return nullptr;
441 TreewalkStruct treewalkstruct2 = { folder, file };
442 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
443 int ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
445 if (ret < 0 && ret != GIT_EUSER)
446 return nullptr;
448 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
449 diff = false;
450 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
451 return nullptr;
454 if (diff)
455 break;
458 return commit.Detach();
461 void CGitPropertyPage::DisplayCommit(const git_commit* commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
463 if (commit == NULL)
465 SetDlgItemText(m_hwnd, hashLabel, _T(""));
466 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
467 SetDlgItemText(m_hwnd, authorLabel, _T(""));
468 SetDlgItemText(m_hwnd, dateLabel, _T(""));
469 return;
472 int encode = CP_UTF8;
473 const char * encodingString = git_commit_message_encoding(commit);
474 if (encodingString != NULL)
475 encode = CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString));
477 const git_signature * author = git_commit_author(commit);
478 CString authorName = CUnicodeUtils::GetUnicode(author->name, encode);
480 CString message = CUnicodeUtils::GetUnicode(git_commit_message(commit), encode);
482 int start = 0;
483 message = message.Tokenize(L"\n", start);
485 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
486 SetDlgItemText(m_hwnd, subjectLabel, message);
487 SetDlgItemText(m_hwnd, authorLabel, authorName);
489 CString authorDate;
490 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
491 SetDlgItemText(m_hwnd, dateLabel, authorDate);
494 int CGitPropertyPage::LogThread()
496 CTGitPath path(filenames.front().c_str());
498 CAutoLocker lock(g_Git.m_critGitDllSec); // HACK for libgit2
500 CString ProjectTopDir;
501 if(!path.HasAdminDir(&ProjectTopDir))
502 return 0;
504 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
505 if (!repository)
506 return 0;
508 int stripLength = ProjectTopDir.GetLength();
509 if (ProjectTopDir[stripLength - 1] != _T('\\'))
510 ++stripLength;
512 CTGitPath relatepath;
513 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
515 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
516 if (commit)
518 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
520 else
522 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
525 return 0;
528 void CGitPropertyPage::LogThreadEntry(void *param)
530 ((CGitPropertyPage*)param)->LogThread();
533 void CGitPropertyPage::InitWorkfileView()
535 if (filenames.empty())
536 return;
538 CTGitPath path(filenames.front().c_str());
540 CString ProjectTopDir;
541 if(!path.HasAdminDir(&ProjectTopDir))
542 return;
544 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
545 if (!repository)
546 return;
548 CString username;
549 CString useremail;
550 CString autocrlf;
551 CString safecrlf;
553 CAutoConfig config(repository);
554 if (config)
556 config.GetString(L"user.name", username);
557 config.GetString(L"user.email", useremail);
558 config.GetString(L"core.autocrlf", autocrlf);
559 config.GetString(L"core.safecrlf", safecrlf);
562 CString branch;
563 CString remotebranch;
564 if (!git_repository_head_detached(repository))
566 CAutoReference head;
567 if (git_repository_head_unborn(repository))
569 git_reference_lookup(head.GetPointer(), repository, "HEAD");
570 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
571 if (branch.Find(_T("refs/heads/")) == 0)
572 branch = branch.Mid(11); // 11 = len("refs/heads/")
574 else if (!git_repository_head(head.GetPointer(), repository))
576 const char * branchChar = git_reference_shorthand(head);
577 branch = CUnicodeUtils::GetUnicode(branchChar);
579 const char * branchFullChar = git_reference_name(head);
580 CAutoBuf upstreambranchname;
581 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
583 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
584 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
588 else
589 branch = _T("detached HEAD");
591 if (autocrlf.Trim().IsEmpty())
592 autocrlf = _T("false");
593 if (safecrlf.Trim().IsEmpty())
594 safecrlf = _T("false");
596 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
597 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
598 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
599 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
601 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
602 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
604 git_oid oid;
605 CAutoCommit HEADcommit;
606 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
607 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
610 int stripLength = ProjectTopDir.GetLength();
611 if (ProjectTopDir[stripLength - 1] != _T('\\'))
612 ++stripLength;
614 bool allAreFiles = true;
615 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
617 if (PathIsDirectory(it->c_str()))
619 allAreFiles = false;
620 break;
623 if (allAreFiles)
625 size_t assumevalid = 0;
626 size_t skipworktree = 0;
627 size_t executable = 0;
628 size_t symlink = 0;
631 CAutoIndex index;
632 if (git_repository_index(index.GetPointer(), repository))
633 break;
635 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
637 CTGitPath file;
638 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
639 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
640 size_t idx;
641 if (!git_index_find(&idx, index, pathA))
643 const git_index_entry *e = git_index_get_byindex(index, idx);
645 if (e->flags & GIT_IDXENTRY_VALID)
646 ++assumevalid;
648 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
649 ++skipworktree;
651 if (e->mode & 0111)
652 ++executable;
654 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
655 ++symlink;
657 else
659 // do not show checkboxes for unversioned files
660 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
661 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
662 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
663 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
664 break;
667 } while (0);
669 if (assumevalid != 0 && assumevalid != filenames.size())
671 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
672 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
674 else
675 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
677 if (skipworktree != 0 && skipworktree != filenames.size())
679 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
680 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
682 else
683 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
685 if (executable != 0 && executable != filenames.size())
687 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
688 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
689 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
691 else
693 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
694 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
697 if (symlink != 0 && symlink != filenames.size())
699 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
700 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
701 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
703 else
705 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
706 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
709 else
711 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
712 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
713 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
714 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
718 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
720 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
721 _beginthread(LogThreadEntry, 0, this);
723 else
724 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
728 // CShellExt member functions (needed for IShellPropSheetExt)
729 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
731 __try
733 return AddPages_Wrap(lpfnAddPage, lParam);
735 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
738 return E_FAIL;
741 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
743 CString ProjectTopDir;
745 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
748 GitStatus svn = GitStatus();
749 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
750 return S_OK; // file/directory not under version control
752 if (svn.status->entry == NULL)
753 return S_OK;
755 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
756 break;
757 else
758 return S_OK;
761 if (files_.empty())
762 return S_OK;
764 LoadLangDll();
765 PROPSHEETPAGE psp;
766 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
767 HPROPSHEETPAGE hPage;
768 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
770 psp.dwSize = sizeof (psp);
771 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
772 psp.hInstance = g_hResInst;
773 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
774 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
775 psp.pszTitle = _T("Git");
776 psp.pfnDlgProc = (DLGPROC) PageProc;
777 psp.lParam = (LPARAM) sheetpage;
778 psp.pfnCallback = PropPageCallbackProc;
779 psp.pcRefParent = (UINT*)&g_cRefThisDll;
781 hPage = CreatePropertySheetPage (&psp);
783 if (hPage != NULL)
785 if (!lpfnAddPage (hPage, lParam))
787 delete sheetpage;
788 DestroyPropertySheetPage (hPage);
792 return S_OK;
795 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
797 return E_FAIL;