LogDlg: When filtering only for paths log entries might contain invalid data
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blobd8166776c75a2f87eefce455200e0f45958cf189
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2015 - 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 CAutoRepository repository(CUnicodeUtils::GetUTF8(projectTopDir));
121 if (!repository)
122 break;
124 CAutoIndex index;
125 if (git_repository_index(index.GetPointer(), repository))
126 break;
128 BOOL assumeValid = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_GETCHECK, 0, 0);
129 BOOL skipWorktree = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_GETCHECK, 0, 0);
130 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
131 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
133 bool changed = false;
135 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
137 CTGitPath file;
138 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
139 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
140 size_t idx;
141 if (!git_index_find(&idx, index, pathA))
143 git_index_entry *e = const_cast<git_index_entry *>(git_index_get_byindex(index, idx)); // HACK
145 if (assumeValid == BST_CHECKED)
147 if (!(e->flags & GIT_IDXENTRY_VALID))
149 e->flags |= GIT_IDXENTRY_VALID;
150 changed = true;
153 else if (assumeValid != BST_INDETERMINATE)
155 if (e->flags & GIT_IDXENTRY_VALID)
157 e->flags &= ~GIT_IDXENTRY_VALID;
158 changed = true;
161 if (skipWorktree == BST_CHECKED)
163 if (!(e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE))
165 e->flags_extended |= GIT_IDXENTRY_SKIP_WORKTREE;
166 changed = true;
169 else if (skipWorktree != BST_INDETERMINATE)
171 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
173 e->flags_extended &= ~GIT_IDXENTRY_SKIP_WORKTREE;
174 changed = true;
177 if (executable == BST_CHECKED)
179 if (!(e->mode & 0111))
181 e->mode |= 0111;
182 changed = true;
185 else if (executable != BST_INDETERMINATE)
187 if (e->mode & 0111)
189 e->mode &= ~0111;
190 changed = true;
193 if (symlink == BST_CHECKED)
195 if ((e->mode & GIT_FILEMODE_LINK) != GIT_FILEMODE_LINK)
197 e->mode |= GIT_FILEMODE_LINK;
198 changed = true;
201 else if (symlink != BST_INDETERMINATE)
203 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
205 e->mode &= ~GIT_FILEMODE_LINK;
206 changed = true;
209 if (changed)
210 git_index_add(index, e);
214 if (changed)
216 if (!git_index_write(index))
217 m_bChanged = false;
219 } while (0);
221 SetWindowLongPtr (m_hwnd, DWLP_MSGRESULT, FALSE);
222 return TRUE;
224 case WM_DESTROY:
225 return TRUE;
227 case WM_COMMAND:
228 PageProcOnCommand(wParam);
229 break;
230 } // switch (uMessage)
232 if (uMessage == m_UpdateLastCommit)
234 DisplayCommit((git_commit *)lParam, IDC_LAST_HASH, IDC_LAST_SUBJECT, IDC_LAST_AUTHOR, IDC_LAST_DATE);
235 return TRUE;
238 return FALSE;
240 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam)
242 if(HIWORD(wParam) != BN_CLICKED)
243 return;
245 switch (LOWORD(wParam))
247 case IDC_SHOWLOG:
249 tstring gitCmd = _T(" /command:");
250 gitCmd += _T("log /path:\"");
251 gitCmd += filenames.front().c_str();
252 gitCmd += _T("\"");
253 RunCommand(gitCmd);
255 break;
256 case IDC_SHOWSETTINGS:
258 CTGitPath path(filenames.front().c_str());
259 CString projectTopDir;
260 if(!path.HasAdminDir(&projectTopDir))
261 return;
263 tstring gitCmd = _T(" /command:");
264 gitCmd += _T("settings /path:\"");
265 gitCmd += projectTopDir;
266 gitCmd += _T("\"");
267 RunCommand(gitCmd);
269 break;
270 case IDC_ASSUMEVALID:
271 case IDC_SKIPWORKTREE:
272 case IDC_EXECUTABLE:
273 case IDC_SYMLINK:
274 BOOL executable = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_GETCHECK, 0, 0);
275 BOOL symlink = (BOOL)SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_GETCHECK, 0, 0);
276 if (executable == BST_CHECKED)
278 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), FALSE);
279 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_UNCHECKED, 0);
281 else
282 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
283 if (symlink == BST_CHECKED)
285 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), FALSE);
286 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_UNCHECKED, 0);
288 else
289 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
290 m_bChanged = true;
291 SendMessage(GetParent(m_hwnd), PSM_CHANGED, (WPARAM)m_hwnd, 0);
292 break;
296 void CGitPropertyPage::RunCommand(const tstring& command)
298 tstring tortoiseProcPath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TortoiseGitProc.exe");
299 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str())))
301 // process started - exit
302 return;
305 MessageBox(NULL, CFormatMessageWrapper(), _T("TortoiseGitProc launch failed"), MB_OK | MB_ICONINFORMATION);
308 void CGitPropertyPage::Time64ToTimeString(__time64_t time, TCHAR * buf, size_t buflen) const
310 struct tm newtime;
311 SYSTEMTIME systime;
313 LCID locale = LOCALE_USER_DEFAULT;
314 if (!CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))
315 locale = MAKELCID((WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), SORT_DEFAULT);
317 *buf = '\0';
318 if (time)
320 TCHAR timebuf[MAX_STRING_LENGTH] = { 0 };
321 TCHAR datebuf[MAX_STRING_LENGTH] = { 0 };
322 _localtime64_s(&newtime, &time);
324 systime.wDay = (WORD)newtime.tm_mday;
325 systime.wDayOfWeek = (WORD)newtime.tm_wday;
326 systime.wHour = (WORD)newtime.tm_hour;
327 systime.wMilliseconds = 0;
328 systime.wMinute = (WORD)newtime.tm_min;
329 systime.wMonth = (WORD)newtime.tm_mon+1;
330 systime.wSecond = (WORD)newtime.tm_sec;
331 systime.wYear = (WORD)newtime.tm_year+1900;
332 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
333 GetDateFormat(locale, DATE_SHORTDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
334 else
335 GetDateFormat(locale, DATE_LONGDATE, &systime, NULL, datebuf, MAX_STRING_LENGTH);
336 GetTimeFormat(locale, 0, &systime, NULL, timebuf, MAX_STRING_LENGTH);
337 *buf = '\0';
338 _tcsncat_s(buf, buflen, datebuf, MAX_STRING_LENGTH-1);
339 _tcsncat_s(buf, buflen, _T(" "), MAX_STRING_LENGTH-1);
340 _tcsncat_s(buf, buflen, timebuf, MAX_STRING_LENGTH-1);
344 struct TreewalkStruct
346 const char *folder;
347 const char *name;
348 git_oid oid;
351 static int TreewalkCB_FindFileRecentCommit(const char *root, const git_tree_entry *entry, void *payload)
353 TreewalkStruct *treewalkstruct = (TreewalkStruct *)payload;
354 char folder[MAX_PATH] = {0};
355 strcpy_s(folder, root);
356 strcat_s(folder, git_tree_entry_name(entry));
357 strcat_s(folder, "/");
358 if (strstr(treewalkstruct->folder, folder))
359 return 0;
361 if (!strcmp(treewalkstruct->folder, root))
363 if (!strcmp(git_tree_entry_name(entry), treewalkstruct->name))
365 git_oid_cpy(&treewalkstruct->oid, git_tree_entry_id(entry));
366 return GIT_EUSER;
369 return 1;
372 return 1;
375 static git_commit* FindFileRecentCommit(git_repository* repository, const CString& path)
377 CAutoRevwalk walk;
378 if (git_revwalk_new(walk.GetPointer(), repository))
379 return nullptr;
381 CStringA pathA = CUnicodeUtils::GetUTF8(path);
382 if (pathA.GetLength() >= MAX_PATH)
383 return nullptr;
384 const char *pathC = pathA;
385 char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
386 const char *slash = strrchr(pathC, '/');
387 if (slash)
389 strncpy(folder, pathC, slash - pathC + 1);
390 folder[slash - pathC + 1] = '\0';
391 strcpy(file, slash + 1);
393 else
395 folder[0] = '\0';
396 strcpy(file, pathC);
399 TreewalkStruct treewalkstruct = { folder, file };
401 if (git_revwalk_push_head(walk))
402 return nullptr;
404 git_oid oid;
405 CAutoCommit commit;
406 while (!git_revwalk_next(&oid, walk))
408 if (git_commit_lookup(commit.GetPointer(), repository, &oid))
409 return nullptr;
411 CAutoTree tree;
412 if (git_commit_tree(tree.GetPointer(), commit))
413 return nullptr;
415 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
416 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
418 if (ret < 0 && ret != GIT_EUSER)
419 return nullptr;
421 // check if file not found
422 if (git_oid_iszero(&treewalkstruct.oid))
423 return nullptr;
425 bool diff = true;
426 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
427 // if no parent then of course it is different
428 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
430 CAutoCommit commit2;
431 if (git_commit_parent(commit2.GetPointer(), commit, i))
432 return nullptr;
434 CAutoTree tree2;
435 if (git_commit_tree(tree2.GetPointer(), commit2))
436 return nullptr;
438 TreewalkStruct treewalkstruct2 = { folder, file };
439 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
440 ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
442 if (ret < 0 && ret != GIT_EUSER)
443 return nullptr;
445 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
446 diff = false;
447 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
448 return nullptr;
451 if (diff)
452 break;
455 return commit.Detach();
458 void CGitPropertyPage::DisplayCommit(const git_commit* commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
460 if (commit == NULL)
462 SetDlgItemText(m_hwnd, hashLabel, _T(""));
463 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
464 SetDlgItemText(m_hwnd, authorLabel, _T(""));
465 SetDlgItemText(m_hwnd, dateLabel, _T(""));
466 return;
469 int encode = CP_UTF8;
470 const char * encodingString = git_commit_message_encoding(commit);
471 if (encodingString != NULL)
472 encode = CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString));
474 const git_signature * author = git_commit_author(commit);
475 CString authorName = CUnicodeUtils::GetUnicode(author->name, encode);
477 CString message = CUnicodeUtils::GetUnicode(git_commit_message(commit), encode);
479 int start = 0;
480 message = message.Tokenize(L"\n", start);
482 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
483 SetDlgItemText(m_hwnd, subjectLabel, message);
484 SetDlgItemText(m_hwnd, authorLabel, authorName);
486 CString authorDate;
487 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
488 SetDlgItemText(m_hwnd, dateLabel, authorDate);
491 int CGitPropertyPage::LogThread()
493 CTGitPath path(filenames.front().c_str());
495 CString ProjectTopDir;
496 if(!path.HasAdminDir(&ProjectTopDir))
497 return 0;
499 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
500 if (!repository)
501 return 0;
503 int stripLength = ProjectTopDir.GetLength();
504 if (ProjectTopDir[stripLength - 1] != _T('\\'))
505 ++stripLength;
507 CTGitPath relatepath;
508 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
510 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
511 if (commit)
513 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
515 else
517 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
520 return 0;
523 void CGitPropertyPage::LogThreadEntry(void *param)
525 ((CGitPropertyPage*)param)->LogThread();
528 void CGitPropertyPage::InitWorkfileView()
530 if (filenames.empty())
531 return;
533 CTGitPath path(filenames.front().c_str());
535 CString ProjectTopDir;
536 if(!path.HasAdminDir(&ProjectTopDir))
537 return;
539 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
540 if (!repository)
541 return;
543 CString username;
544 CString useremail;
545 CString autocrlf;
546 CString safecrlf;
548 CAutoConfig config(repository);
549 if (config)
551 config.GetString(L"user.name", username);
552 config.GetString(L"user.email", useremail);
553 config.GetString(L"core.autocrlf", autocrlf);
554 config.GetString(L"core.safecrlf", safecrlf);
557 CString branch;
558 CString remotebranch;
559 if (!git_repository_head_detached(repository))
561 CAutoReference head;
562 if (git_repository_head_unborn(repository))
564 git_reference_lookup(head.GetPointer(), repository, "HEAD");
565 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
566 if (branch.Find(_T("refs/heads/")) == 0)
567 branch = branch.Mid(11); // 11 = len("refs/heads/")
569 else if (!git_repository_head(head.GetPointer(), repository))
571 const char * branchChar = git_reference_shorthand(head);
572 branch = CUnicodeUtils::GetUnicode(branchChar);
574 const char * branchFullChar = git_reference_name(head);
575 CAutoBuf upstreambranchname;
576 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
578 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
579 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
583 else
584 branch = _T("detached HEAD");
586 if (autocrlf.Trim().IsEmpty())
587 autocrlf = _T("false");
588 if (safecrlf.Trim().IsEmpty())
589 safecrlf = _T("false");
591 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
592 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
593 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
594 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
596 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
597 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
599 git_oid oid;
600 CAutoCommit HEADcommit;
601 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
602 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
605 int stripLength = ProjectTopDir.GetLength();
606 if (ProjectTopDir[stripLength - 1] != _T('\\'))
607 ++stripLength;
609 bool allAreFiles = true;
610 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
612 if (PathIsDirectory(it->c_str()))
614 allAreFiles = false;
615 break;
618 if (allAreFiles)
620 size_t assumevalid = 0;
621 size_t skipworktree = 0;
622 size_t executable = 0;
623 size_t symlink = 0;
626 CAutoIndex index;
627 if (git_repository_index(index.GetPointer(), repository))
628 break;
630 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
632 CTGitPath file;
633 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
634 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
635 size_t idx;
636 if (!git_index_find(&idx, index, pathA))
638 const git_index_entry *e = git_index_get_byindex(index, idx);
640 if (e->flags & GIT_IDXENTRY_VALID)
641 ++assumevalid;
643 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
644 ++skipworktree;
646 if (e->mode & 0111)
647 ++executable;
649 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
650 ++symlink;
652 else
654 // do not show checkboxes for unversioned files
655 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
656 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
657 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
658 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
659 break;
662 } while (0);
664 if (assumevalid != 0 && assumevalid != filenames.size())
666 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
667 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
669 else
670 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
672 if (skipworktree != 0 && skipworktree != filenames.size())
674 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
675 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
677 else
678 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
680 if (executable != 0 && executable != filenames.size())
682 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
683 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
684 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
686 else
688 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
689 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
692 if (symlink != 0 && symlink != filenames.size())
694 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
695 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
696 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
698 else
700 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
701 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
704 else
706 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
707 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
708 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
709 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
713 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
715 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
716 _beginthread(LogThreadEntry, 0, this);
718 else
719 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
723 // CShellExt member functions (needed for IShellPropSheetExt)
724 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
726 __try
728 return AddPages_Wrap(lpfnAddPage, lParam);
730 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
733 return E_FAIL;
736 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
738 CString ProjectTopDir;
740 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
743 GitStatus svn = GitStatus();
744 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
745 return S_OK; // file/directory not under version control
747 if (svn.status->entry == NULL)
748 return S_OK;
750 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
751 break;
752 else
753 return S_OK;
756 if (files_.empty())
757 return S_OK;
759 LoadLangDll();
760 PROPSHEETPAGE psp;
761 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
762 HPROPSHEETPAGE hPage;
763 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
765 psp.dwSize = sizeof (psp);
766 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
767 psp.hInstance = g_hResInst;
768 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
769 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
770 psp.pszTitle = _T("Git");
771 psp.pfnDlgProc = (DLGPROC) PageProc;
772 psp.lParam = (LPARAM) sheetpage;
773 psp.pfnCallback = PropPageCallbackProc;
774 psp.pcRefParent = (UINT*)&g_cRefThisDll;
776 hPage = CreatePropertySheetPage (&psp);
778 if (hPage != NULL)
780 if (!lpfnAddPage (hPage, lParam))
782 delete sheetpage;
783 DestroyPropertySheetPage (hPage);
787 return S_OK;
790 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
792 return E_FAIL;