Don't set an exception filter for the shell extension
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob755c312a13250c20f003bfe2095e0b90a0d9371b
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2017 - 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 = reinterpret_cast<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 delete reinterpret_cast<CGitPropertyPage*>(ppsp->lParam);
66 return 1;
69 // *********************** CGitPropertyPage *************************
70 const UINT CGitPropertyPage::m_UpdateLastCommit = RegisterWindowMessage(L"TORTOISEGIT_PROP_UPDATELASTCOMMIT");
72 CGitPropertyPage::CGitPropertyPage(const std::vector<std::wstring>& 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] != L'\\')
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 = L" /command:";
247 gitCmd += L"log /path:\"";
248 gitCmd += filenames.front().c_str();
249 gitCmd += L'"';
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 = L" /command:";
261 gitCmd += L"settings /path:\"";
262 gitCmd += projectTopDir;
263 gitCmd += L'"';
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) + L"TortoiseGitProc.exe";
296 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), command.c_str()))
298 // process started - exit
299 return;
302 MessageBox(nullptr, CFormatMessageWrapper(), L"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(L"Software\\TortoiseGit\\UseSystemLocaleForDates", TRUE, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY))
312 locale = MAKELCID((WORD)CRegStdDWORD(L"Software\\TortoiseGit\\LanguageID", MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), false, HKEY_CURRENT_USER, KEY_WOW64_64KEY), 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(L"Software\\TortoiseGit\\LogDateFormat", 0, false, HKEY_CURRENT_USER, KEY_WOW64_64KEY) == 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 wcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH - 1);
336 wcsncat_s(buf, buflen, L" ", MAX_STRING_LENGTH - 1);
337 wcsncat_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 auto treewalkstruct = reinterpret_cast<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, L"");
460 SetDlgItemText(m_hwnd, subjectLabel, L"");
461 SetDlgItemText(m_hwnd, authorLabel, L"");
462 SetDlgItemText(m_hwnd, dateLabel, L"");
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(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] != L'\\')
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, reinterpret_cast<LPARAM>(static_cast<git_commit*>(commit)));
512 else
514 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
517 return 0;
520 void CGitPropertyPage::LogThreadEntry(void *param)
522 reinterpret_cast<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 (CStringUtils::StartsWith(branch, L"refs/heads/"))
565 branch = branch.Mid((int)wcslen(L"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((int)wcslen(L"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 = L"detached HEAD";
591 if (autocrlf.Trim().IsEmpty())
592 autocrlf = L"false";
593 if (safecrlf.Trim().IsEmpty())
594 safecrlf = L"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] != L'\\')
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 if (files_.empty())
733 return S_OK;
735 CString projectTopDir;
736 if (!CTGitPath(files_[0].c_str()).HasAdminDir(&projectTopDir))
737 return S_OK;
739 for (const auto& file_ : files_)
741 CString currentProjectTopDir;
742 if (!CTGitPath(file_.c_str()).HasAdminDir(&currentProjectTopDir) || !CPathUtils::ArePathStringsEqual(projectTopDir, currentProjectTopDir))
743 return S_OK;
746 LoadLangDll();
747 PROPSHEETPAGE psp = { 0 };
748 HPROPSHEETPAGE hPage;
749 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
751 psp.dwSize = sizeof (psp);
752 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
753 psp.hInstance = g_hResInst;
754 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
755 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
756 psp.pszTitle = L"Git";
757 psp.pfnDlgProc = (DLGPROC) PageProc;
758 psp.lParam = (LPARAM) sheetpage;
759 psp.pfnCallback = PropPageCallbackProc;
760 psp.pcRefParent = (UINT*)&g_cRefThisDll;
762 hPage = CreatePropertySheetPage (&psp);
764 if (hPage)
766 if (!lpfnAddPage (hPage, lParam))
768 delete sheetpage;
769 DestroyPropertySheetPage (hPage);
773 return S_OK;
776 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
778 return E_FAIL;