Fixed issue #1789: Tooltips not properly displayed in Log List if that commit has...
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blobb748e01deac0bf4085acbdd6fc7c11adaa34c304
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)
476 CString str;
477 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
478 encode = CUnicodeUtils::GetCPCode(str);
481 const git_signature * author = git_commit_author(commit);
482 CString authorName;
483 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
485 CString message;
486 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
488 int start = 0;
489 message = message.Tokenize(L"\n", start);
491 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
492 SetDlgItemText(m_hwnd, subjectLabel, message);
493 SetDlgItemText(m_hwnd, authorLabel, authorName);
495 CString authorDate;
496 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
497 SetDlgItemText(m_hwnd, dateLabel, authorDate);
500 int CGitPropertyPage::LogThread()
502 CTGitPath path(filenames.front().c_str());
504 CAutoLocker lock(g_Git.m_critGitDllSec); // HACK for libgit2
506 CString ProjectTopDir;
507 if(!path.HasAdminDir(&ProjectTopDir))
508 return 0;
510 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
511 if (!repository)
512 return 0;
514 int stripLength = ProjectTopDir.GetLength();
515 if (ProjectTopDir[stripLength - 1] != _T('\\'))
516 ++stripLength;
518 CTGitPath relatepath;
519 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
521 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
522 if (commit)
524 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
526 else
528 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
531 return 0;
534 void CGitPropertyPage::LogThreadEntry(void *param)
536 ((CGitPropertyPage*)param)->LogThread();
539 void CGitPropertyPage::InitWorkfileView()
541 if (filenames.empty())
542 return;
544 CTGitPath path(filenames.front().c_str());
546 CString ProjectTopDir;
547 if(!path.HasAdminDir(&ProjectTopDir))
548 return;
550 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
551 if (!repository)
552 return;
554 CString username;
555 CString useremail;
556 CString autocrlf;
557 CString safecrlf;
559 CAutoConfig config(repository);
560 if (config)
562 config.GetString(L"user.name", username);
563 config.GetString(L"user.email", useremail);
564 config.GetString(L"core.autocrlf", autocrlf);
565 config.GetString(L"core.safecrlf", safecrlf);
568 CString branch;
569 CString remotebranch;
570 if (!git_repository_head_detached(repository))
572 CAutoReference head;
573 if (git_repository_head_unborn(repository))
575 git_reference_lookup(head.GetPointer(), repository, "HEAD");
576 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
577 if (branch.Find(_T("refs/heads/")) == 0)
578 branch = branch.Mid(11); // 11 = len("refs/heads/")
580 else if (!git_repository_head(head.GetPointer(), repository))
582 const char * branchChar = git_reference_shorthand(head);
583 branch = CUnicodeUtils::GetUnicode(branchChar);
585 const char * branchFullChar = git_reference_name(head);
586 CAutoBuf upstreambranchname;
587 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
589 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
590 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
594 else
595 branch = _T("detached HEAD");
597 if (autocrlf.Trim().IsEmpty())
598 autocrlf = _T("false");
599 if (safecrlf.Trim().IsEmpty())
600 safecrlf = _T("false");
602 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
603 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
604 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
605 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
607 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
608 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
610 git_oid oid;
611 CAutoCommit HEADcommit;
612 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
613 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
616 int stripLength = ProjectTopDir.GetLength();
617 if (ProjectTopDir[stripLength - 1] != _T('\\'))
618 ++stripLength;
620 bool allAreFiles = true;
621 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
623 if (PathIsDirectory(it->c_str()))
625 allAreFiles = false;
626 break;
629 if (allAreFiles)
631 size_t assumevalid = 0;
632 size_t skipworktree = 0;
633 size_t executable = 0;
634 size_t symlink = 0;
637 CAutoIndex index;
638 if (git_repository_index(index.GetPointer(), repository))
639 break;
641 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
643 CTGitPath file;
644 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
645 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
646 size_t idx;
647 if (!git_index_find(&idx, index, pathA))
649 const git_index_entry *e = git_index_get_byindex(index, idx);
651 if (e->flags & GIT_IDXENTRY_VALID)
652 ++assumevalid;
654 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
655 ++skipworktree;
657 if (e->mode & 0111)
658 ++executable;
660 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
661 ++symlink;
663 else
665 // do not show checkboxes for unversioned files
666 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
667 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
668 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
669 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
670 break;
673 } while (0);
675 if (assumevalid != 0 && assumevalid != filenames.size())
677 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
678 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
680 else
681 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
683 if (skipworktree != 0 && skipworktree != filenames.size())
685 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
686 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
688 else
689 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
691 if (executable != 0 && executable != filenames.size())
693 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
694 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
695 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
697 else
699 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
700 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
703 if (symlink != 0 && symlink != filenames.size())
705 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
706 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
707 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
709 else
711 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
712 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
715 else
717 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
718 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
719 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
720 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
724 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
726 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
727 _beginthread(LogThreadEntry, 0, this);
729 else
730 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
734 // CShellExt member functions (needed for IShellPropSheetExt)
735 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
737 __try
739 return AddPages_Wrap(lpfnAddPage, lParam);
741 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
744 return E_FAIL;
747 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
749 CString ProjectTopDir;
751 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
754 GitStatus svn = GitStatus();
755 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
756 return S_OK; // file/directory not under version control
758 if (svn.status->entry == NULL)
759 return S_OK;
761 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
762 break;
763 else
764 return S_OK;
767 if (files_.empty())
768 return S_OK;
770 LoadLangDll();
771 PROPSHEETPAGE psp;
772 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
773 HPROPSHEETPAGE hPage;
774 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
776 psp.dwSize = sizeof (psp);
777 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
778 psp.hInstance = g_hResInst;
779 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
780 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
781 psp.pszTitle = _T("Git");
782 psp.pfnDlgProc = (DLGPROC) PageProc;
783 psp.lParam = (LPARAM) sheetpage;
784 psp.pfnCallback = PropPageCallbackProc;
785 psp.pcRefParent = (UINT*)&g_cRefThisDll;
787 hPage = CreatePropertySheetPage (&psp);
789 if (hPage != NULL)
791 if (!lpfnAddPage (hPage, lParam))
793 delete sheetpage;
794 DestroyPropertySheetPage (hPage);
798 return S_OK;
801 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
803 return E_FAIL;