Don't pass read-only buffer to CreateProcess as lpCommandLine
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
bloba633f6b7ac8bbe7ecdc9b223ec20ce91e73f75d2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2016 - 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"
29 #include "StringUtils.h"
31 #define MAX_STRING_LENGTH 4096 //should be big enough
33 // Nonmember function prototypes
34 BOOL CALLBACK PageProc (HWND, UINT, WPARAM, LPARAM);
35 UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
37 /////////////////////////////////////////////////////////////////////////////
38 // Dialog procedures and other callback functions
40 BOOL CALLBACK PageProc (HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
42 CGitPropertyPage * sheetpage;
44 if (uMessage == WM_INITDIALOG)
46 sheetpage = (CGitPropertyPage*) ((LPPROPSHEETPAGE) lParam)->lParam;
47 SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) sheetpage);
48 sheetpage->SetHwnd(hwnd);
50 else
51 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
53 if (sheetpage)
54 return sheetpage->PageProc(hwnd, uMessage, wParam, lParam);
55 else
56 return FALSE;
59 UINT CALLBACK PropPageCallbackProc ( HWND /*hwnd*/, UINT uMsg, LPPROPSHEETPAGE ppsp )
61 // Delete the page before closing.
62 if (PSPCB_RELEASE == uMsg)
64 CGitPropertyPage* sheetpage = (CGitPropertyPage*) ppsp->lParam;
65 delete sheetpage;
67 return 1;
70 // *********************** CGitPropertyPage *************************
71 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
73 CGitPropertyPage::CGitPropertyPage(const std::vector<tstring>& newFilenames)
74 :filenames(newFilenames)
75 ,m_bChanged(false)
76 , m_hwnd(nullptr)
80 CGitPropertyPage::~CGitPropertyPage(void)
84 void CGitPropertyPage::SetHwnd(HWND newHwnd)
86 m_hwnd = newHwnd;
89 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
91 switch (uMessage)
93 case WM_INITDIALOG:
95 InitWorkfileView();
96 return TRUE;
98 case WM_NOTIFY:
100 LPNMHDR point = (LPNMHDR)lParam;
101 int code = point->code;
103 // Respond to notifications.
105 if (code == PSN_APPLY && m_bChanged)
109 CTGitPath path(filenames.front().c_str());
110 CString projectTopDir;
111 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
112 break;
114 int stripLength = projectTopDir.GetLength();
115 if (projectTopDir[stripLength - 1] != _T('\\'))
116 ++stripLength;
118 CAutoRepository repository(CUnicodeUtils::GetUTF8(projectTopDir));
119 if (!repository)
120 break;
122 CAutoIndex index;
123 if (git_repository_index(index.GetPointer(), repository))
124 break;
126 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
127 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
128 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
129 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
131 bool changed = false;
133 for (const auto& filename : filenames)
135 CTGitPath file;
136 file.SetFromWin(CString(filename.c_str()).Mid(stripLength));
137 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
138 size_t idx;
139 if (!git_index_find(&idx, index, pathA))
141 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
143 if (assumeValid == BST_CHECKED)
145 if (!(e->flags & GIT_IDXENTRY_VALID))
147 e->flags |= GIT_IDXENTRY_VALID;
148 changed = true;
151 else if (assumeValid != BST_INDETERMINATE)
153 if (e->flags & GIT_IDXENTRY_VALID)
155 e->flags &= ~GIT_IDXENTRY_VALID;
156 changed = true;
159 if (skipWorktree == BST_CHECKED)
161 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
163 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
164 changed = true;
167 else if (skipWorktree != BST_INDETERMINATE)
169 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
171 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
172 changed = true;
175 if (executable == BST_CHECKED)
177 if (!(e->mode & 0111))
179 e->mode = GIT_FILEMODE_BLOB_EXECUTABLE;
180 changed = true;
183 else if (executable != BST_INDETERMINATE)
185 if (e->mode & 0111)
187 e->mode = GIT_FILEMODE_BLOB;
188 changed = true;
191 if (symlink == BST_CHECKED)
193 if ((e->mode & GIT_FILEMODE_LINK) != GIT_FILEMODE_LINK)
195 e->mode = GIT_FILEMODE_LINK;
196 changed = true;
199 else if (symlink != BST_INDETERMINATE)
201 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
203 e->mode = GIT_FILEMODE_BLOB;
204 changed = true;
207 if (changed)
208 git_index_add(index, e);
212 if (changed)
214 if (!git_index_write(index))
215 m_bChanged = false;
217 } while (0);
219 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
220 return TRUE;
222 case WM_DESTROY:
223 return TRUE;
225 case WM_COMMAND:
226 PageProcOnCommand(wParam);
227 break;
228 } // switch (uMessage)
230 if (uMessage == m_UpdateLastCommit)
232 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
233 return TRUE;
236 return FALSE;
238 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
240 if(HIWORD(wParam) != BN_CLICKED)
241 return;
243 switch (LOWORD(wParam))
245 case IDC_SHOWLOG:
247 tstring gitCmd = _T(" /command:");
248 gitCmd += _T("log /path:\"");
249 gitCmd += filenames.front().c_str();
250 gitCmd += _T("\"");
251 RunCommand(gitCmd);
253 break;
254 case IDC_SHOWSETTINGS:
256 CTGitPath path(filenames.front().c_str());
257 CString projectTopDir;
258 if(!path.HasAdminDir(&projectTopDir))
259 return;
261 tstring gitCmd = _T(" /command:");
262 gitCmd += _T("settings /path:\"");
263 gitCmd += projectTopDir;
264 gitCmd += _T("\"");
265 RunCommand(gitCmd);
267 break;
268 case IDC_ASSUMEVALID:
269 case IDC_SKIPWORKTREE:
270 case IDC_EXECUTABLE:
271 case IDC_SYMLINK:
272 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
273 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
274 if (executable == BST_CHECKED)
276 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), FALSE);
277 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_UNCHECKED, 0);
279 else
280 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
281 if (symlink == BST_CHECKED)
283 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), FALSE);
284 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_UNCHECKED, 0);
286 else
287 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
288 m_bChanged = true;
289 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
290 break;
294 void CGitPropertyPage::RunCommand(const tstring& command)
296 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
297 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), command.c_str()))
299 // process started - exit
300 return;
303 MessageBox(nullptr, CFormatMessageWrapper(), _T("TortoiseGitProc launch failed"), MB_OK | MB_ICONERROR);
306 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen) const
308 struct tm newtime;
309 SYSTEMTIME systime;
311 LCID locale = LOCALE_USER_DEFAULT;
312 if (!CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))
313 locale = MAKELCID((WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), SORT_DEFAULT);
315 *buf = '\0';
316 if (time)
318 TCHAR timebuf[MAX_STRING_LENGTH] = { 0 };
319 TCHAR datebuf[MAX_STRING_LENGTH] = { 0 };
320 _localtime64_s(&newtime, &time);
322 systime.wDay = (WORD)newtime.tm_mday;
323 systime.wDayOfWeek = (WORD)newtime.tm_wday;
324 systime.wHour = (WORD)newtime.tm_hour;
325 systime.wMilliseconds = 0;
326 systime.wMinute = (WORD)newtime.tm_min;
327 systime.wMonth = (WORD)newtime.tm_mon+1;
328 systime.wSecond = (WORD)newtime.tm_sec;
329 systime.wYear = (WORD)newtime.tm_year+1900;
330 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
331 GetDateFormat(locale, DATE_SHORTDATE, &systime, nullptr, datebuf, MAX_STRING_LENGTH);
332 else
333 GetDateFormat(locale, DATE_LONGDATE, &systime, nullptr, datebuf, MAX_STRING_LENGTH);
334 GetTimeFormat(locale, 0, &systime, nullptr, timebuf, MAX_STRING_LENGTH);
335 *buf = '\0';
336 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
337 _tcsncat_s(buf, buflen, _T(" "), MAX_STRING_LENGTH-1);
338 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
342 struct TreewalkStruct
344 const char *folder;
345 const char *name;
346 git_oid oid;
349 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
351 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
352 char folder[MAX_PATH] = {0};
353 strcpy_s(folder, root);
354 strcat_s(folder, git_tree_entry_name(entry));
355 strcat_s(folder, "/");
356 if (strstr(treewalkstruct->folder, folder))
357 return 0;
359 if (!strcmp(treewalkstruct->folder, root))
361 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
363 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
364 return GIT_EUSER;
367 return 1;
370 return 1;
373 static git_commit* FindFileRecentCommit(git_repository* repository, const CString& path)
375 CAutoRevwalk walk;
376 if (git_revwalk_new(walk.GetPointer(), repository))
377 return nullptr;
379 CStringA pathA = CUnicodeUtils::GetUTF8(path);
380 if (pathA.GetLength() >= MAX_PATH)
381 return nullptr;
382 const char *pathC = pathA;
383 char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
384 const char *slash = strrchr(pathC, '/');
385 if (slash)
387 strncpy(folder, pathC, slash - pathC + 1);
388 folder[slash - pathC + 1] = '\0';
389 strcpy(file, slash + 1);
391 else
393 folder[0] = '\0';
394 strcpy(file, pathC);
397 TreewalkStruct treewalkstruct = { folder, file };
399 if (git_revwalk_push_head(walk))
400 return nullptr;
402 git_oid oid;
403 CAutoCommit commit;
404 while (!git_revwalk_next(&oid, walk))
406 if (git_commit_lookup(commit.GetPointer(), repository, &oid))
407 return nullptr;
409 CAutoTree tree;
410 if (git_commit_tree(tree.GetPointer(), commit))
411 return nullptr;
413 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
414 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
416 if (ret < 0 && ret != GIT_EUSER)
417 return nullptr;
419 // check if file not found
420 if (git_oid_iszero(&treewalkstruct.oid))
421 return nullptr;
423 bool diff = true;
424 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
425 // if no parent then of course it is different
426 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
428 CAutoCommit commit2;
429 if (git_commit_parent(commit2.GetPointer(), commit, i))
430 return nullptr;
432 CAutoTree tree2;
433 if (git_commit_tree(tree2.GetPointer(), commit2))
434 return nullptr;
436 TreewalkStruct treewalkstruct2 = { folder, file };
437 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
438 ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
440 if (ret < 0 && ret != GIT_EUSER)
441 return nullptr;
443 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
444 diff = false;
445 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
446 return nullptr;
449 if (diff)
450 break;
453 return commit.Detach();
456 void CGitPropertyPage::DisplayCommit(const git_commit* commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
458 if (!commit)
460 SetDlgItemText(m_hwnd, hashLabel, _T(""));
461 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
462 SetDlgItemText(m_hwnd, authorLabel, _T(""));
463 SetDlgItemText(m_hwnd, dateLabel, _T(""));
464 return;
467 int encode = CP_UTF8;
468 const char * encodingString = git_commit_message_encoding(commit);
469 if (encodingString)
470 encode = CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString));
472 const git_signature * author = git_commit_author(commit);
473 CString authorName = CUnicodeUtils::GetUnicode(author->name, encode);
475 CString message = CUnicodeUtils::GetUnicode(git_commit_message(commit), encode);
477 int start = 0;
478 message = message.Tokenize(L"\n", start);
480 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
481 SetDlgItemText(m_hwnd, subjectLabel, message);
482 SetDlgItemText(m_hwnd, authorLabel, authorName);
484 CString authorDate;
485 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
486 SetDlgItemText(m_hwnd, dateLabel, authorDate);
489 int CGitPropertyPage::LogThread()
491 CTGitPath path(filenames.front().c_str());
493 CString ProjectTopDir;
494 if(!path.HasAdminDir(&ProjectTopDir))
495 return 0;
497 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
498 if (!repository)
499 return 0;
501 int stripLength = ProjectTopDir.GetLength();
502 if (ProjectTopDir[stripLength - 1] != _T('\\'))
503 ++stripLength;
505 CTGitPath relatepath;
506 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
508 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
509 if (commit)
511 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
513 else
515 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
518 return 0;
521 void CGitPropertyPage::LogThreadEntry(void *param)
523 ((CGitPropertyPage*)param)->LogThread();
526 void CGitPropertyPage::InitWorkfileView()
528 if (filenames.empty())
529 return;
531 CTGitPath path(filenames.front().c_str());
533 CString ProjectTopDir;
534 if(!path.HasAdminDir(&ProjectTopDir))
535 return;
537 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
538 if (!repository)
539 return;
541 CString username;
542 CString useremail;
543 CString autocrlf;
544 CString safecrlf;
546 CAutoConfig config(repository);
547 if (config)
549 config.GetString(L"user.name", username);
550 config.GetString(L"user.email", useremail);
551 config.GetString(L"core.autocrlf", autocrlf);
552 config.GetString(L"core.safecrlf", safecrlf);
555 CString branch;
556 CString remotebranch;
557 CString remoteUrl;
558 if (!git_repository_head_detached(repository))
560 CAutoReference head;
561 if (git_repository_head_unborn(repository))
563 git_reference_lookup(head.GetPointer(), repository, "HEAD");
564 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
565 if (CStringUtils::StartsWith(branch, L"refs/heads/"))
566 branch = branch.Mid(11); // 11 = len("refs/heads/")
568 else if (!git_repository_head(head.GetPointer(), repository))
570 const char * branchChar = git_reference_shorthand(head);
571 branch = CUnicodeUtils::GetUnicode(branchChar);
573 const char * branchFullChar = git_reference_name(head);
574 CAutoBuf upstreambranchname;
575 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
577 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
578 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
579 int pos = remotebranch.Find(L'/');
580 if (pos > 0)
582 CString remoteName;
583 remoteName.Format(L"remote.%s.url", (LPCTSTR)remotebranch.Left(pos));
584 config.GetString(remoteName, remoteUrl);
589 else
590 branch = _T("detached HEAD");
592 if (autocrlf.Trim().IsEmpty())
593 autocrlf = _T("false");
594 if (safecrlf.Trim().IsEmpty())
595 safecrlf = _T("false");
597 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
598 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
599 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
600 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
602 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
603 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
604 SetDlgItemText(m_hwnd, IDC_SHELL_REMOTE_URL, remoteUrl);
606 git_oid oid;
607 CAutoCommit HEADcommit;
608 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
609 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
612 int stripLength = ProjectTopDir.GetLength();
613 if (ProjectTopDir[stripLength - 1] != _T('\\'))
614 ++stripLength;
616 bool allAreFiles = true;
617 for (const auto& filename : filenames)
619 if (PathIsDirectory(filename.c_str()))
621 allAreFiles = false;
622 break;
625 if (allAreFiles)
627 size_t assumevalid = 0;
628 size_t skipworktree = 0;
629 size_t executable = 0;
630 size_t symlink = 0;
633 CAutoIndex index;
634 if (git_repository_index(index.GetPointer(), repository))
635 break;
637 for (const auto& filename : filenames)
639 CTGitPath file;
640 file.SetFromWin(CString(filename.c_str()).Mid(stripLength));
641 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
642 size_t idx;
643 if (!git_index_find(&idx, index, pathA))
645 const git_index_entry *e = git_index_get_byindex(index, idx);
647 if (e->flags & GIT_IDXENTRY_VALID)
648 ++assumevalid;
650 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
651 ++skipworktree;
653 if (e->mode & 0111)
654 ++executable;
656 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
657 ++symlink;
659 else
661 // do not show checkboxes for unversioned files
662 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
663 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
664 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
665 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
666 break;
669 } while (0);
671 if (assumevalid != 0 && assumevalid != filenames.size())
673 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
674 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
676 else
677 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
679 if (skipworktree != 0 && skipworktree != filenames.size())
681 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
682 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
684 else
685 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
687 if (executable != 0 && executable != filenames.size())
689 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
690 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
691 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
693 else
695 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
696 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
699 if (symlink != 0 && symlink != filenames.size())
701 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
702 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
703 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
705 else
707 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
708 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
711 else
713 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
714 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
715 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
716 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
720 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
722 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
723 _beginthread(LogThreadEntry, 0, this);
725 else
726 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
730 // CShellExt member functions (needed for IShellPropSheetExt)
731 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
733 __try
735 return AddPages_Wrap(lpfnAddPage, lParam);
737 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
740 return E_FAIL;
743 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
745 CString ProjectTopDir;
747 if (files_.empty())
748 return S_OK;
750 for (const auto file_ : files_)
753 GitStatus svn = GitStatus();
754 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
755 return S_OK; // file/directory not under version control
757 if (!svn.status->entry)
758 return S_OK;
760 if (CTGitPath(file_.c_str()).HasAdminDir(&ProjectTopDir))
761 break;
762 else
763 return S_OK;
766 LoadLangDll();
767 PROPSHEETPAGE psp = { 0 };
768 HPROPSHEETPAGE hPage;
769 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
771 psp.dwSize = sizeof (psp);
772 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
773 psp.hInstance = g_hResInst;
774 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
775 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
776 psp.pszTitle = _T("Git");
777 psp.pfnDlgProc = (DLGPROC) PageProc;
778 psp.lParam = (LPARAM) sheetpage;
779 psp.pfnCallback = PropPageCallbackProc;
780 psp.pcRefParent = (UINT*)&g_cRefThisDll;
782 hPage = CreatePropertySheetPage (&psp);
784 if (hPage)
786 if (!lpfnAddPage (hPage, lParam))
788 delete sheetpage;
789 DestroyPropertySheetPage (hPage);
793 return S_OK;
796 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
798 return E_FAIL;