Show copy icon in log message context menu
[TortoiseGit.git] / src / TortoiseShell / GITPropertyPage.cpp
blob03940d8b0638073464bb204a86e0740dfe69801b
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, CString path)
379 if (path.GetLength() >= MAX_PATH)
380 return nullptr;
382 CAutoRevwalk walk;
383 if (git_revwalk_new(walk.GetPointer(), repository))
384 return nullptr;
386 CStringA pathA = CUnicodeUtils::GetUTF8(path);
387 const char *pathC = pathA;
388 char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
389 const char *slash = strrchr(pathC, '/');
390 if (slash)
392 strncpy(folder, pathC, slash - pathC + 1);
393 folder[slash - pathC + 1] = '\0';
394 strcpy(file, slash + 1);
396 else
398 folder[0] = '\0';
399 strcpy(file, pathC);
402 TreewalkStruct treewalkstruct = { folder, file };
404 if (git_revwalk_push_head(walk))
405 return nullptr;
407 git_oid oid;
408 CAutoCommit commit;
409 while (!git_revwalk_next(&oid, walk))
411 commit.Free();
412 if (git_commit_lookup(commit.GetPointer(), repository, &oid))
413 return nullptr;
415 CAutoTree tree;
416 if (git_commit_tree(tree.GetPointer(), commit))
417 return nullptr;
419 memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
420 int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);
422 if (ret < 0 && ret != GIT_EUSER)
423 return nullptr;
425 // check if file not found
426 if (git_oid_iszero(&treewalkstruct.oid))
427 return nullptr;
429 bool diff = true;
430 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
431 // if no parent then of course it is different
432 for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
434 CAutoCommit commit2;
435 if (git_commit_parent(commit2.GetPointer(), commit, i))
436 return nullptr;
438 CAutoTree tree2;
439 if (git_commit_tree(tree2.GetPointer(), commit2))
440 return nullptr;
442 TreewalkStruct treewalkstruct2 = { folder, file };
443 memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
444 int ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);
446 if (ret < 0 && ret != GIT_EUSER)
447 return nullptr;
449 if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
450 diff = false;
451 else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
452 return nullptr;
455 if (diff)
456 break;
459 return commit.Detach();
462 void CGitPropertyPage::DisplayCommit(git_commit * commit, UINT hashLabel, UINT subjectLabel, UINT authorLabel, UINT dateLabel)
464 if (commit == NULL)
466 SetDlgItemText(m_hwnd, hashLabel, _T(""));
467 SetDlgItemText(m_hwnd, subjectLabel, _T(""));
468 SetDlgItemText(m_hwnd, authorLabel, _T(""));
469 SetDlgItemText(m_hwnd, dateLabel, _T(""));
470 return;
473 int encode = CP_UTF8;
474 const char * encodingString = git_commit_message_encoding(commit);
475 if (encodingString != NULL)
477 CString str;
478 g_Git.StringAppend(&str, (BYTE*)encodingString, CP_UTF8);
479 encode = CUnicodeUtils::GetCPCode(str);
482 const git_signature * author = git_commit_author(commit);
483 CString authorName;
484 g_Git.StringAppend(&authorName, (BYTE*)author->name, encode);
486 CString message;
487 g_Git.StringAppend(&message, (BYTE*)git_commit_message(commit), encode);
489 int start = 0;
490 message = message.Tokenize(L"\n", start);
492 SetDlgItemText(m_hwnd, hashLabel, CGitHash((char*)(git_commit_id(commit)->id)).ToString());
493 SetDlgItemText(m_hwnd, subjectLabel, message);
494 SetDlgItemText(m_hwnd, authorLabel, authorName);
496 CString authorDate;
497 Time64ToTimeString(author->when.time, authorDate.GetBufferSetLength(200), 200);
498 SetDlgItemText(m_hwnd, dateLabel, authorDate);
501 int CGitPropertyPage::LogThread()
503 CTGitPath path(filenames.front().c_str());
505 CAutoLocker lock(g_Git.m_critGitDllSec); // HACK for libgit2
507 CString ProjectTopDir;
508 if(!path.HasAdminDir(&ProjectTopDir))
509 return 0;
511 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
512 if (!repository)
513 return 0;
515 int stripLength = ProjectTopDir.GetLength();
516 if (ProjectTopDir[stripLength - 1] != _T('\\'))
517 ++stripLength;
519 CTGitPath relatepath;
520 relatepath.SetFromWin(path.GetWinPathString().Mid(stripLength));
522 CAutoCommit commit(FindFileRecentCommit(repository, relatepath.GetGitPathString()));
523 if (commit)
525 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, (LPARAM)(git_commit*)commit);
527 else
529 SendMessage(m_hwnd, m_UpdateLastCommit, NULL, NULL);
532 return 0;
535 void CGitPropertyPage::LogThreadEntry(void *param)
537 ((CGitPropertyPage*)param)->LogThread();
540 void CGitPropertyPage::InitWorkfileView()
542 if (filenames.empty())
543 return;
545 CTGitPath path(filenames.front().c_str());
547 CString ProjectTopDir;
548 if(!path.HasAdminDir(&ProjectTopDir))
549 return;
551 CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
552 if (!repository)
553 return;
555 CString username;
556 CString useremail;
557 CString autocrlf;
558 CString safecrlf;
560 CAutoConfig config(repository);
561 if (config)
563 config.GetString(L"user.name", username);
564 config.GetString(L"user.email", useremail);
565 config.GetString(L"core.autocrlf", autocrlf);
566 config.GetString(L"core.safecrlf", safecrlf);
569 CString branch;
570 CString remotebranch;
571 if (!git_repository_head_detached(repository))
573 CAutoReference head;
574 if (git_repository_head_unborn(repository))
576 git_reference_lookup(head.GetPointer(), repository, "HEAD");
577 branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
578 if (branch.Find(_T("refs/heads/")) == 0)
579 branch = branch.Mid(11); // 11 = len("refs/heads/")
581 else if (!git_repository_head(head.GetPointer(), repository))
583 const char * branchChar = git_reference_shorthand(head);
584 branch = CUnicodeUtils::GetUnicode(branchChar);
586 const char * branchFullChar = git_reference_name(head);
587 CAutoBuf upstreambranchname;
588 if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
590 remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
591 remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
595 else
596 branch = _T("detached HEAD");
598 if (autocrlf.Trim().IsEmpty())
599 autocrlf = _T("false");
600 if (safecrlf.Trim().IsEmpty())
601 safecrlf = _T("false");
603 SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
604 SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
605 SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
606 SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());
608 SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
609 SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);
611 git_oid oid;
612 CAutoCommit HEADcommit;
613 if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
614 DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);
617 int stripLength = ProjectTopDir.GetLength();
618 if (ProjectTopDir[stripLength - 1] != _T('\\'))
619 ++stripLength;
621 bool allAreFiles = true;
622 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
624 if (PathIsDirectory(it->c_str()))
626 allAreFiles = false;
627 break;
630 if (allAreFiles)
632 size_t assumevalid = 0;
633 size_t skipworktree = 0;
634 size_t executable = 0;
635 size_t symlink = 0;
638 CAutoIndex index;
639 if (git_repository_index(index.GetPointer(), repository))
640 break;
642 for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
644 CTGitPath file;
645 file.SetFromWin(CString(it->c_str()).Mid(stripLength));
646 CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
647 size_t idx;
648 if (!git_index_find(&idx, index, pathA))
650 const git_index_entry *e = git_index_get_byindex(index, idx);
652 if (e->flags & GIT_IDXENTRY_VALID)
653 ++assumevalid;
655 if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
656 ++skipworktree;
658 if (e->mode & 0111)
659 ++executable;
661 if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
662 ++symlink;
664 else
666 // do not show checkboxes for unversioned files
667 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
668 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
669 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
670 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
671 break;
674 } while (0);
676 if (assumevalid != 0 && assumevalid != filenames.size())
678 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
679 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
681 else
682 SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
684 if (skipworktree != 0 && skipworktree != filenames.size())
686 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
687 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
689 else
690 SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
692 if (executable != 0 && executable != filenames.size())
694 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
695 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
696 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
698 else
700 SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
701 EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
704 if (symlink != 0 && symlink != filenames.size())
706 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
707 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
708 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
710 else
712 SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
713 EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
716 else
718 ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
719 ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
720 ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
721 ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
725 if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
727 SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
728 _beginthread(LogThreadEntry, 0, this);
730 else
731 ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
735 // CShellExt member functions (needed for IShellPropSheetExt)
736 STDMETHODIMP CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
738 __try
740 return AddPages_Wrap(lpfnAddPage, lParam);
742 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
745 return E_FAIL;
748 STDMETHODIMP CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
750 CString ProjectTopDir;
752 for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)
755 GitStatus svn = GitStatus();
756 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
757 return S_OK; // file/directory not under version control
759 if (svn.status->entry == NULL)
760 return S_OK;
762 if( CTGitPath(I->c_str()).HasAdminDir(&ProjectTopDir))
763 break;
764 else
765 return S_OK;
768 if (files_.empty())
769 return S_OK;
771 LoadLangDll();
772 PROPSHEETPAGE psp;
773 SecureZeroMemory(&psp, sizeof(PROPSHEETPAGE));
774 HPROPSHEETPAGE hPage;
775 CGitPropertyPage *sheetpage = new (std::nothrow) CGitPropertyPage(files_);
777 psp.dwSize = sizeof (psp);
778 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USEICONID | PSP_USECALLBACK;
779 psp.hInstance = g_hResInst;
780 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE);
781 psp.pszIcon = MAKEINTRESOURCE(IDI_APPSMALL);
782 psp.pszTitle = _T("Git");
783 psp.pfnDlgProc = (DLGPROC) PageProc;
784 psp.lParam = (LPARAM) sheetpage;
785 psp.pfnCallback = PropPageCallbackProc;
786 psp.pcRefParent = (UINT*)&g_cRefThisDll;
788 hPage = CreatePropertySheetPage (&psp);
790 if (hPage != NULL)
792 if (!lpfnAddPage (hPage, lParam))
794 delete sheetpage;
795 DestroyPropertySheetPage (hPage);
799 return S_OK;
802 STDMETHODIMP CShellExt::ReplacePage (UINT /*uPageID*/, LPFNADDPROPSHEETPAGE /*lpfnReplaceWith*/, LPARAM /*lParam*/)
804 return E_FAIL;