Use wcsncmp instead of Find for StartsWith checks
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob13bb9365f07c7983c4b682624b14635482595800
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"
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
50 sheetpage = (CGitPropertyPage*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
52 if (sheetpage)
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 delete sheetpage;
66 return 1;
69 // *********************** CGitPropertyPage *************************
70 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
72 CGitPropertyPage::CGitPropertyPage(const std::vector<tstring>& newFilenames)
73 :filenames(newFilenames)
74 ,m_bChanged(false)
75 , m_hwnd(nullptr)
79 CGitPropertyPage::~CGitPropertyPage(void)
83 void CGitPropertyPage::SetHwnd(HWND newHwnd)
85 m_hwnd = newHwnd;
88 BOOL CGitPropertyPage::PageProc (HWND /*hwnd*/, UINT uMessage, WPARAM wParam, LPARAM lParam)
90 switch (uMessage)
92 case WM_INITDIALOG:
94 InitWorkfileView();
95 return TRUE;
97 case WM_NOTIFY:
99 LPNMHDR point = (LPNMHDR)lParam;
100 int code = point->code;
102 // Respond to notifications.
104 if (code == PSN_APPLY && m_bChanged)
108 CTGitPath path(filenames.front().c_str());
109 CString projectTopDir;
110 if(!path.HasAdminDir(&projectTopDir) || path.IsDirectory())
111 break;
113 int stripLength = projectTopDir.GetLength();
114 if (projectTopDir[stripLength - 1] != _T('\\'))
115 ++stripLength;
117 CAutoRepository repository(CUnicodeUtils::GetUTF8(projectTopDir));
118 if (!repository)
119 break;
121 CAutoIndex index;
122 if (git_repository_index(index.GetPointer(), repository))
123 break;
125 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
126 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
127 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
128 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
130 bool changed = false;
132 for (const auto& filename : filenames)
134 CTGitPath file;
135 file.SetFromWin(CString(filename.c_str()).Mid(stripLength));
136 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
137 size_t idx;
138 if (!git_index_find(&idx, index, pathA))
140 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
142 if (assumeValid == BST_CHECKED)
144 if (!(e->flags & GIT_IDXENTRY_VALID))
146 e->flags |= GIT_IDXENTRY_VALID;
147 changed = true;
150 else if (assumeValid != BST_INDETERMINATE)
152 if (e->flags & GIT_IDXENTRY_VALID)
154 e->flags &= ~GIT_IDXENTRY_VALID;
155 changed = true;
158 if (skipWorktree == BST_CHECKED)
160 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
162 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
163 changed = true;
166 else if (skipWorktree != BST_INDETERMINATE)
168 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
170 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
171 changed = true;
174 if (executable == BST_CHECKED)
176 if (!(e->mode & 0111))
178 e->mode = GIT_FILEMODE_BLOB_EXECUTABLE;
179 changed = true;
182 else if (executable != BST_INDETERMINATE)
184 if (e->mode & 0111)
186 e->mode = GIT_FILEMODE_BLOB;
187 changed = true;
190 if (symlink == BST_CHECKED)
192 if ((e->mode & GIT_FILEMODE_LINK) != GIT_FILEMODE_LINK)
194 e->mode = GIT_FILEMODE_LINK;
195 changed = true;
198 else if (symlink != BST_INDETERMINATE)
200 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
202 e->mode = GIT_FILEMODE_BLOB;
203 changed = true;
206 if (changed)
207 git_index_add(index, e);
211 if (changed)
213 if (!git_index_write(index))
214 m_bChanged = false;
216 } while (0);
218 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
219 return TRUE;
221 case WM_DESTROY:
222 return TRUE;
224 case WM_COMMAND:
225 PageProcOnCommand(wParam);
226 break;
227 } // switch (uMessage)
229 if (uMessage == m_UpdateLastCommit)
231 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
232 return TRUE;
235 return FALSE;
237 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
239 if(HIWORD(wParam) != BN_CLICKED)
240 return;
242 switch (LOWORD(wParam))
244 case IDC_SHOWLOG:
246 tstring gitCmd = _T(" /command:");
247 gitCmd += _T("log /path:\"");
248 gitCmd += filenames.front().c_str();
249 gitCmd += _T("\"");
250 RunCommand(gitCmd);
252 break;
253 case IDC_SHOWSETTINGS:
255 CTGitPath path(filenames.front().c_str());
256 CString projectTopDir;
257 if(!path.HasAdminDir(&projectTopDir))
258 return;
260 tstring gitCmd = _T(" /command:");
261 gitCmd += _T("settings /path:\"");
262 gitCmd += projectTopDir;
263 gitCmd += _T("\"");
264 RunCommand(gitCmd);
266 break;
267 case IDC_ASSUMEVALID:
268 case IDC_SKIPWORKTREE:
269 case IDC_EXECUTABLE:
270 case IDC_SYMLINK:
271 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
272 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
273 if (executable == BST_CHECKED)
275 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), FALSE);
276 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_UNCHECKED, 0);
278 else
279 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
280 if (symlink == BST_CHECKED)
282 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), FALSE);
283 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_UNCHECKED, 0);
285 else
286 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
287 m_bChanged = true;
288 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
289 break;
293 void CGitPropertyPage::RunCommand(const tstring& command)
295 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
296 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
298 // process started - exit
299 return;
302 MessageBox(nullptr, CFormatMessageWrapper(), _T("TortoiseGitProc launch failed"), MB_OK | MB_ICONERROR);
305 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen) const
307 struct tm newtime;
308 SYSTEMTIME systime;
310 LCID locale = LOCALE_USER_DEFAULT;
311 if (!CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))
312 locale = MAKELCID((WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), SORT_DEFAULT);
314 *buf = '\0';
315 if (time)
317 TCHAR timebuf[MAX_STRING_LENGTH] = { 0 };
318 TCHAR datebuf[MAX_STRING_LENGTH] = { 0 };
319 _localtime64_s(&newtime, &time);
321 systime.wDay = (WORD)newtime.tm_mday;
322 systime.wDayOfWeek = (WORD)newtime.tm_wday;
323 systime.wHour = (WORD)newtime.tm_hour;
324 systime.wMilliseconds = 0;
325 systime.wMinute = (WORD)newtime.tm_min;
326 systime.wMonth = (WORD)newtime.tm_mon+1;
327 systime.wSecond = (WORD)newtime.tm_sec;
328 systime.wYear = (WORD)newtime.tm_year+1900;
329 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
330 GetDateFormat(locale, DATE_SHORTDATE, &systime, nullptr, datebuf, MAX_STRING_LENGTH);
331 else
332 GetDateFormat(locale, DATE_LONGDATE, &systime, nullptr, datebuf, MAX_STRING_LENGTH);
333 GetTimeFormat(locale, 0, &systime, nullptr, timebuf, MAX_STRING_LENGTH);
334 *buf = '\0';
335 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
336 _tcsncat_s(buf, buflen, _T(" "), MAX_STRING_LENGTH-1);
337 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
341 struct TreewalkStruct
343 const char *folder;
344 const char *name;
345 git_oid oid;
348 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
350 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
351 char folder[MAX_PATH] = {0};
352 strcpy_s(folder, root);
353 strcat_s(folder, git_tree_entry_name(entry));
354 strcat_s(folder, "/");
355 if (strstr(treewalkstruct->folder, folder))
356 return 0;
358 if (!strcmp(treewalkstruct->folder, root))
360 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
362 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
363 return GIT_EUSER;
366 return 1;
369 return 1;
372 static git_commit* FindFileRecentCommit(git_repository* repository, const CString& path)
374 CAutoRevwalk walk;
375 if (git_revwalk_new(walk.GetPointer(), repository))
376 return nullptr;
378 CStringA pathA = CUnicodeUtils::GetUTF8(path);
379 if (pathA.GetLength() >= MAX_PATH)
380 return nullptr;
381 const char *pathC = pathA;
382 char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
383 const char *slash = strrchr(pathC, '/');
384 if (slash)
386 strncpy(folder, pathC, slash - pathC + 1);
387 folder[slash - pathC + 1] = '\0';
388 strcpy(file, slash + 1);
390 else
392 folder[0] = '\0';
393 strcpy(file, pathC);
396 TreewalkStruct treewalkstruct = { folder, file };
398 if (git_revwalk_push_head(walk))
399 return nullptr;
401 git_oid oid;
402 CAutoCommit commit;
403 while (!git_revwalk_next(&oid, walk))
405 if (git_commit_lookup(commit.GetPointer(), repository, &oid))
406 return nullptr;
408 CAutoTree tree;
409 if (git_commit_tree(tree.GetPointer(), commit))
410 return nullptr;
412 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
413 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
415 if (ret < 0 && ret != GIT_EUSER)
416 return nullptr;
418 // check if file not found
419 if (git_oid_iszero(&treewalkstruct.oid))
420 return nullptr;
422 bool diff = true;
423 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
424 // if no parent then of course it is different
425 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
427 CAutoCommit commit2;
428 if (git_commit_parent(commit2.GetPointer(), commit, i))
429 return nullptr;
431 CAutoTree tree2;
432 if (git_commit_tree(tree2.GetPointer(), commit2))
433 return nullptr;
435 TreewalkStruct treewalkstruct2 = { folder, file };
436 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
437 ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
439 if (ret < 0 && ret != GIT_EUSER)
440 return nullptr;
442 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
443 diff = false;
444 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
445 return nullptr;
448 if (diff)
449 break;
452 return commit.Detach();
455 void CGitPropertyPage::DisplayCommit(const git_commit* commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
457 if (!commit)
459 SetDlgItemText(m_hwnd, hashLabel, _T(""));
460 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
461 SetDlgItemText(m_hwnd, authorLabel, _T(""));
462 SetDlgItemText(m_hwnd, dateLabel, _T(""));
463 return;
466 int encode = CP_UTF8;
467 const char * encodingString = git_commit_message_encoding(commit);
468 if (encodingString)
469 encode = CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString));
471 const git_signature * author = git_commit_author(commit);
472 CString authorName = CUnicodeUtils::GetUnicode(author->name, encode);
474 CString message = CUnicodeUtils::GetUnicode(git_commit_message(commit), encode);
476 int start = 0;
477 message = message.Tokenize(L"\n", start);
479 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
480 SetDlgItemText(m_hwnd, subjectLabel, message);
481 SetDlgItemText(m_hwnd, authorLabel, authorName);
483 CString authorDate;
484 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
485 SetDlgItemText(m_hwnd, dateLabel, authorDate);
488 int CGitPropertyPage::LogThread()
490 CTGitPath path(filenames.front().c_str());
492 CString ProjectTopDir;
493 if(!path.HasAdminDir(&ProjectTopDir))
494 return 0;
496 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
497 if (!repository)
498 return 0;
500 int stripLength = ProjectTopDir.GetLength();
501 if (ProjectTopDir[stripLength - 1] != _T('\\'))
502 ++stripLength;
504 CTGitPath relatepath;
505 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
507 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
508 if (commit)
510 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
512 else
514 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
517 return 0;
520 void CGitPropertyPage::LogThreadEntry(void *param)
522 ((CGitPropertyPage*)param)->LogThread();
525 void CGitPropertyPage::InitWorkfileView()
527 if (filenames.empty())
528 return;
530 CTGitPath path(filenames.front().c_str());
532 CString ProjectTopDir;
533 if(!path.HasAdminDir(&ProjectTopDir))
534 return;
536 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
537 if (!repository)
538 return;
540 CString username;
541 CString useremail;
542 CString autocrlf;
543 CString safecrlf;
545 CAutoConfig config(repository);
546 if (config)
548 config.GetString(L"user.name", username);
549 config.GetString(L"user.email", useremail);
550 config.GetString(L"core.autocrlf", autocrlf);
551 config.GetString(L"core.safecrlf", safecrlf);
554 CString branch;
555 CString remotebranch;
556 CString remoteUrl;
557 if (!git_repository_head_detached(repository))
559 CAutoReference head;
560 if (git_repository_head_unborn(repository))
562 git_reference_lookup(head.GetPointer(), repository, "HEAD");
563 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
564 if (wcsncmp(branch, L"refs/heads/", 11) == 0)
565 branch = branch.Mid(11); // 11 = len("refs/heads/")
567 else if (!git_repository_head(head.GetPointer(), repository))
569 const char * branchChar = git_reference_shorthand(head);
570 branch = CUnicodeUtils::GetUnicode(branchChar);
572 const char * branchFullChar = git_reference_name(head);
573 CAutoBuf upstreambranchname;
574 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
576 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
577 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
578 int pos = remotebranch.Find(L'/');
579 if (pos > 0)
581 CString remoteName;
582 remoteName.Format(L"remote.%s.url", (LPCTSTR)remotebranch.Left(pos));
583 config.GetString(remoteName, remoteUrl);
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);
603 SetDlgItemText(m_hwnd, IDC_SHELL_REMOTE_URL, remoteUrl);
605 git_oid oid;
606 CAutoCommit HEADcommit;
607 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
608 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
611 int stripLength = ProjectTopDir.GetLength();
612 if (ProjectTopDir[stripLength - 1] != _T('\\'))
613 ++stripLength;
615 bool allAreFiles = true;
616 for (const auto& filename : filenames)
618 if (PathIsDirectory(filename.c_str()))
620 allAreFiles = false;
621 break;
624 if (allAreFiles)
626 size_t assumevalid = 0;
627 size_t skipworktree = 0;
628 size_t executable = 0;
629 size_t symlink = 0;
632 CAutoIndex index;
633 if (git_repository_index(index.GetPointer(), repository))
634 break;
636 for (const auto& filename : filenames)
638 CTGitPath file;
639 file.SetFromWin(CString(filename.c_str()).Mid(stripLength));
640 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
641 size_t idx;
642 if (!git_index_find(&idx, index, pathA))
644 const git_index_entry *e = git_index_get_byindex(index, idx);
646 if (e->flags & GIT_IDXENTRY_VALID)
647 ++assumevalid;
649 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
650 ++skipworktree;
652 if (e->mode & 0111)
653 ++executable;
655 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
656 ++symlink;
658 else
660 // do not show checkboxes for unversioned files
661 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
662 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
663 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
664 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
665 break;
668 } while (0);
670 if (assumevalid != 0 && assumevalid != filenames.size())
672 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
673 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
675 else
676 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
678 if (skipworktree != 0 && skipworktree != filenames.size())
680 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
681 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
683 else
684 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
686 if (executable != 0 && executable != filenames.size())
688 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
689 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
690 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
692 else
694 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
695 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
698 if (symlink != 0 && symlink != filenames.size())
700 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
701 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
702 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
704 else
706 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
707 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
710 else
712 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
713 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
714 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
715 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
719 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
721 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
722 _beginthread(LogThreadEntry, 0, this);
724 else
725 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
729 // CShellExt member functions (needed for IShellPropSheetExt)
730 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
732 __try
734 return AddPages_Wrap(lpfnAddPage, lParam);
736 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
739 return E_FAIL;
742 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
744 CString ProjectTopDir;
746 if (files_.empty())
747 return S_OK;
749 for (const auto file_ : files_)
752 GitStatus svn = GitStatus();
753 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
754 return S_OK; // file/directory not under version control
756 if (!svn.status->entry)
757 return S_OK;
759 if (CTGitPath(file_.c_str()).HasAdminDir(&ProjectTopDir))
760 break;
761 else
762 return S_OK;
765 LoadLangDll();
766 PROPSHEETPAGE psp = { 0 };
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)
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;