1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2016 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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
);
51 sheetpage
= (CGitPropertyPage
*) GetWindowLongPtr (hwnd
, GWLP_USERDATA
);
54 return sheetpage
->PageProc(hwnd
, uMessage
, wParam
, lParam
);
59 UINT CALLBACK
PropPageCallbackProc ( HWND
/*hwnd*/, UINT uMsg
, LPPROPSHEETPAGE ppsp
)
61 // Delete the page before closing.
62 if (PSPCB_RELEASE
== uMsg
)
64 CGitPropertyPage
* sheetpage
= (CGitPropertyPage
*) ppsp
->lParam
;
70 // *********************** CGitPropertyPage *************************
71 const UINT
CGitPropertyPage::m_UpdateLastCommit
= RegisterWindowMessage(_T("TORTOISEGIT_PROP_UPDATELASTCOMMIT"));
73 CGitPropertyPage::CGitPropertyPage(const std::vector
<tstring
>& newFilenames
)
74 :filenames(newFilenames
)
80 CGitPropertyPage::~CGitPropertyPage(void)
84 void CGitPropertyPage::SetHwnd(HWND newHwnd
)
89 BOOL
CGitPropertyPage::PageProc (HWND
/*hwnd*/, UINT uMessage
, WPARAM wParam
, LPARAM lParam
)
100 LPNMHDR point
= (LPNMHDR
)lParam
;
101 int code
= point
->code
;
103 // Respond to notifications.
105 if (code
== PSN_APPLY
&& m_bChanged
)
109 CTGitPath
path(filenames
.front().c_str());
110 CString projectTopDir
;
111 if(!path
.HasAdminDir(&projectTopDir
) || path
.IsDirectory())
114 int stripLength
= projectTopDir
.GetLength();
115 if (projectTopDir
[stripLength
- 1] != _T('\\'))
118 CAutoRepository
repository(CUnicodeUtils::GetUTF8(projectTopDir
));
123 if (git_repository_index(index
.GetPointer(), repository
))
126 BOOL assumeValid
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), BM_GETCHECK
, 0, 0);
127 BOOL skipWorktree
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), BM_GETCHECK
, 0, 0);
128 BOOL executable
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_GETCHECK
, 0, 0);
129 BOOL symlink
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_GETCHECK
, 0, 0);
131 bool changed
= false;
133 for (const auto& filename
: filenames
)
136 file
.SetFromWin(CString(filename
.c_str()).Mid(stripLength
));
137 CStringA pathA
= CUnicodeUtils::GetMulti(file
.GetGitPathString(), CP_UTF8
);
139 if (!git_index_find(&idx
, index
, pathA
))
141 git_index_entry
*e
= const_cast<git_index_entry
*>(git_index_get_byindex(index
, idx
)); // HACK
143 if (assumeValid
== BST_CHECKED
)
145 if (!(e
->flags
& GIT_IDXENTRY_VALID
))
147 e
->flags
|= GIT_IDXENTRY_VALID
;
151 else if (assumeValid
!= BST_INDETERMINATE
)
153 if (e
->flags
& GIT_IDXENTRY_VALID
)
155 e
->flags
&= ~GIT_IDXENTRY_VALID
;
159 if (skipWorktree
== BST_CHECKED
)
161 if (!(e
->flags_extended
& GIT_IDXENTRY_SKIP_WORKTREE
))
163 e
->flags_extended
|= GIT_IDXENTRY_SKIP_WORKTREE
;
167 else if (skipWorktree
!= BST_INDETERMINATE
)
169 if (e
->flags_extended
& GIT_IDXENTRY_SKIP_WORKTREE
)
171 e
->flags_extended
&= ~GIT_IDXENTRY_SKIP_WORKTREE
;
175 if (executable
== BST_CHECKED
)
177 if (!(e
->mode
& 0111))
179 e
->mode
= GIT_FILEMODE_BLOB_EXECUTABLE
;
183 else if (executable
!= BST_INDETERMINATE
)
187 e
->mode
= GIT_FILEMODE_BLOB
;
191 if (symlink
== BST_CHECKED
)
193 if ((e
->mode
& GIT_FILEMODE_LINK
) != GIT_FILEMODE_LINK
)
195 e
->mode
= GIT_FILEMODE_LINK
;
199 else if (symlink
!= BST_INDETERMINATE
)
201 if ((e
->mode
& GIT_FILEMODE_LINK
) == GIT_FILEMODE_LINK
)
203 e
->mode
= GIT_FILEMODE_BLOB
;
208 git_index_add(index
, e
);
214 if (!git_index_write(index
))
219 SetWindowLongPtr (m_hwnd
, DWLP_MSGRESULT
, FALSE
);
226 PageProcOnCommand(wParam
);
228 } // switch (uMessage)
230 if (uMessage
== m_UpdateLastCommit
)
232 DisplayCommit((git_commit
*)lParam
, IDC_LAST_HASH
, IDC_LAST_SUBJECT
, IDC_LAST_AUTHOR
, IDC_LAST_DATE
);
238 void CGitPropertyPage::PageProcOnCommand(WPARAM wParam
)
240 if(HIWORD(wParam
) != BN_CLICKED
)
243 switch (LOWORD(wParam
))
247 tstring gitCmd
= _T(" /command:");
248 gitCmd
+= _T("log /path:\"");
249 gitCmd
+= filenames
.front().c_str();
254 case IDC_SHOWSETTINGS
:
256 CTGitPath
path(filenames
.front().c_str());
257 CString projectTopDir
;
258 if(!path
.HasAdminDir(&projectTopDir
))
261 tstring gitCmd
= _T(" /command:");
262 gitCmd
+= _T("settings /path:\"");
263 gitCmd
+= projectTopDir
;
268 case IDC_ASSUMEVALID
:
269 case IDC_SKIPWORKTREE
:
272 BOOL executable
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_GETCHECK
, 0, 0);
273 BOOL symlink
= (BOOL
)SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_GETCHECK
, 0, 0);
274 if (executable
== BST_CHECKED
)
276 EnableWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), FALSE
);
277 SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_SETCHECK
, BST_UNCHECKED
, 0);
280 EnableWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), TRUE
);
281 if (symlink
== BST_CHECKED
)
283 EnableWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), FALSE
);
284 SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_SETCHECK
, BST_UNCHECKED
, 0);
287 EnableWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), TRUE
);
289 SendMessage(GetParent(m_hwnd
), PSM_CHANGED
, (WPARAM
)m_hwnd
, 0);
294 void CGitPropertyPage::RunCommand(const tstring
& command
)
296 tstring tortoiseProcPath
= CPathUtils::GetAppDirectory(g_hmodThisDll
) + _T("TortoiseGitProc.exe");
297 if (CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath
.c_str(), command
.c_str()))
299 // process started - exit
303 MessageBox(nullptr, CFormatMessageWrapper(), _T("TortoiseGitProc launch failed"), MB_OK
| MB_ICONERROR
);
306 void CGitPropertyPage::Time64ToTimeString(__time64_t time
, TCHAR
* buf
, size_t buflen
) const
311 LCID locale
= LOCALE_USER_DEFAULT
;
312 if (!CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE
))
313 locale
= MAKELCID((WORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
)), SORT_DEFAULT
);
318 TCHAR timebuf
[MAX_STRING_LENGTH
] = { 0 };
319 TCHAR datebuf
[MAX_STRING_LENGTH
] = { 0 };
320 _localtime64_s(&newtime
, &time
);
322 systime
.wDay
= (WORD
)newtime
.tm_mday
;
323 systime
.wDayOfWeek
= (WORD
)newtime
.tm_wday
;
324 systime
.wHour
= (WORD
)newtime
.tm_hour
;
325 systime
.wMilliseconds
= 0;
326 systime
.wMinute
= (WORD
)newtime
.tm_min
;
327 systime
.wMonth
= (WORD
)newtime
.tm_mon
+1;
328 systime
.wSecond
= (WORD
)newtime
.tm_sec
;
329 systime
.wYear
= (WORD
)newtime
.tm_year
+1900;
330 if (CRegStdDWORD(_T("Software\\TortoiseGit\\LogDateFormat")) == 1)
331 GetDateFormat(locale
, DATE_SHORTDATE
, &systime
, nullptr, datebuf
, MAX_STRING_LENGTH
);
333 GetDateFormat(locale
, DATE_LONGDATE
, &systime
, nullptr, datebuf
, MAX_STRING_LENGTH
);
334 GetTimeFormat(locale
, 0, &systime
, nullptr, timebuf
, MAX_STRING_LENGTH
);
336 _tcsncat_s(buf
, buflen
, datebuf
, MAX_STRING_LENGTH
-1);
337 _tcsncat_s(buf
, buflen
, _T(" "), MAX_STRING_LENGTH
-1);
338 _tcsncat_s(buf
, buflen
, timebuf
, MAX_STRING_LENGTH
-1);
342 struct TreewalkStruct
349 static int TreewalkCB_FindFileRecentCommit(const char *root
, const git_tree_entry
*entry
, void *payload
)
351 TreewalkStruct
*treewalkstruct
= (TreewalkStruct
*)payload
;
352 char folder
[MAX_PATH
] = {0};
353 strcpy_s(folder
, root
);
354 strcat_s(folder
, git_tree_entry_name(entry
));
355 strcat_s(folder
, "/");
356 if (strstr(treewalkstruct
->folder
, folder
))
359 if (!strcmp(treewalkstruct
->folder
, root
))
361 if (!strcmp(git_tree_entry_name(entry
), treewalkstruct
->name
))
363 git_oid_cpy(&treewalkstruct
->oid
, git_tree_entry_id(entry
));
373 static git_commit
* FindFileRecentCommit(git_repository
* repository
, const CString
& path
)
376 if (git_revwalk_new(walk
.GetPointer(), repository
))
379 CStringA pathA
= CUnicodeUtils::GetUTF8(path
);
380 if (pathA
.GetLength() >= MAX_PATH
)
382 const char *pathC
= pathA
;
383 char folder
[MAX_PATH
] = {0}, file
[MAX_PATH
] = {0};
384 const char *slash
= strrchr(pathC
, '/');
387 strncpy(folder
, pathC
, slash
- pathC
+ 1);
388 folder
[slash
- pathC
+ 1] = '\0';
389 strcpy(file
, slash
+ 1);
397 TreewalkStruct treewalkstruct
= { folder
, file
};
399 if (git_revwalk_push_head(walk
))
404 while (!git_revwalk_next(&oid
, walk
))
406 if (git_commit_lookup(commit
.GetPointer(), repository
, &oid
))
410 if (git_commit_tree(tree
.GetPointer(), commit
))
413 memset(&treewalkstruct
.oid
.id
, 0, sizeof(treewalkstruct
.oid
.id
));
414 int ret
= git_tree_walk(tree
, GIT_TREEWALK_PRE
, TreewalkCB_FindFileRecentCommit
, &treewalkstruct
);
416 if (ret
< 0 && ret
!= GIT_EUSER
)
419 // check if file not found
420 if (git_oid_iszero(&treewalkstruct
.oid
))
424 // for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
425 // if no parent then of course it is different
426 for (unsigned int i
= 0; i
< git_commit_parentcount(commit
); ++i
)
429 if (git_commit_parent(commit2
.GetPointer(), commit
, i
))
433 if (git_commit_tree(tree2
.GetPointer(), commit2
))
436 TreewalkStruct treewalkstruct2
= { folder
, file
};
437 memset(&treewalkstruct2
.oid
.id
, 0, sizeof(treewalkstruct2
.oid
.id
));
438 ret
= git_tree_walk(tree2
, GIT_TREEWALK_PRE
, TreewalkCB_FindFileRecentCommit
, &treewalkstruct2
);
440 if (ret
< 0 && ret
!= GIT_EUSER
)
443 if (!git_oid_cmp(&treewalkstruct
.oid
, &treewalkstruct2
.oid
))
445 else if (git_revwalk_hide(walk
, git_commit_parent_id(commit
, i
)))
453 return commit
.Detach();
456 void CGitPropertyPage::DisplayCommit(const git_commit
* commit
, UINT hashLabel
, UINT subjectLabel
, UINT authorLabel
, UINT dateLabel
)
460 SetDlgItemText(m_hwnd
, hashLabel
, _T(""));
461 SetDlgItemText(m_hwnd
, subjectLabel
, _T(""));
462 SetDlgItemText(m_hwnd
, authorLabel
, _T(""));
463 SetDlgItemText(m_hwnd
, dateLabel
, _T(""));
467 int encode
= CP_UTF8
;
468 const char * encodingString
= git_commit_message_encoding(commit
);
470 encode
= CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString
));
472 const git_signature
* author
= git_commit_author(commit
);
473 CString authorName
= CUnicodeUtils::GetUnicode(author
->name
, encode
);
475 CString message
= CUnicodeUtils::GetUnicode(git_commit_message(commit
), encode
);
478 message
= message
.Tokenize(L
"\n", start
);
480 SetDlgItemText(m_hwnd
, hashLabel
, CGitHash((char*)(git_commit_id(commit
)->id
)).ToString());
481 SetDlgItemText(m_hwnd
, subjectLabel
, message
);
482 SetDlgItemText(m_hwnd
, authorLabel
, authorName
);
485 Time64ToTimeString(author
->when
.time
, authorDate
.GetBufferSetLength(200), 200);
486 SetDlgItemText(m_hwnd
, dateLabel
, authorDate
);
489 int CGitPropertyPage::LogThread()
491 CTGitPath
path(filenames
.front().c_str());
493 CString ProjectTopDir
;
494 if(!path
.HasAdminDir(&ProjectTopDir
))
497 CAutoRepository
repository(CUnicodeUtils::GetUTF8(ProjectTopDir
));
501 int stripLength
= ProjectTopDir
.GetLength();
502 if (ProjectTopDir
[stripLength
- 1] != _T('\\'))
505 CTGitPath relatepath
;
506 relatepath
.SetFromWin(path
.GetWinPathString().Mid(stripLength
));
508 CAutoCommit
commit(FindFileRecentCommit(repository
, relatepath
.GetGitPathString()));
511 SendMessage(m_hwnd
, m_UpdateLastCommit
, NULL
, (LPARAM
)(git_commit
*)commit
);
515 SendMessage(m_hwnd
, m_UpdateLastCommit
, NULL
, NULL
);
521 void CGitPropertyPage::LogThreadEntry(void *param
)
523 ((CGitPropertyPage
*)param
)->LogThread();
526 void CGitPropertyPage::InitWorkfileView()
528 if (filenames
.empty())
531 CTGitPath
path(filenames
.front().c_str());
533 CString ProjectTopDir
;
534 if(!path
.HasAdminDir(&ProjectTopDir
))
537 CAutoRepository
repository(CUnicodeUtils::GetUTF8(ProjectTopDir
));
546 CAutoConfig
config(repository
);
549 config
.GetString(L
"user.name", username
);
550 config
.GetString(L
"user.email", useremail
);
551 config
.GetString(L
"core.autocrlf", autocrlf
);
552 config
.GetString(L
"core.safecrlf", safecrlf
);
556 CString remotebranch
;
558 if (!git_repository_head_detached(repository
))
561 if (git_repository_head_unborn(repository
))
563 git_reference_lookup(head
.GetPointer(), repository
, "HEAD");
564 branch
= CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head
));
565 if (CStringUtils::StartsWith(branch
, L
"refs/heads/"))
566 branch
= branch
.Mid(11); // 11 = len("refs/heads/")
568 else if (!git_repository_head(head
.GetPointer(), repository
))
570 const char * branchChar
= git_reference_shorthand(head
);
571 branch
= CUnicodeUtils::GetUnicode(branchChar
);
573 const char * branchFullChar
= git_reference_name(head
);
574 CAutoBuf upstreambranchname
;
575 if (!git_branch_upstream_name(upstreambranchname
, repository
, branchFullChar
))
577 remotebranch
= CUnicodeUtils::GetUnicode(CStringA(upstreambranchname
->ptr
, (int)upstreambranchname
->size
));
578 remotebranch
= remotebranch
.Mid(13); // 13=len("refs/remotes/")
579 int pos
= remotebranch
.Find(L
'/');
583 remoteName
.Format(L
"remote.%s.url", (LPCTSTR
)remotebranch
.Left(pos
));
584 config
.GetString(remoteName
, remoteUrl
);
590 branch
= _T("detached HEAD");
592 if (autocrlf
.Trim().IsEmpty())
593 autocrlf
= _T("false");
594 if (safecrlf
.Trim().IsEmpty())
595 safecrlf
= _T("false");
597 SetDlgItemText(m_hwnd
,IDC_CONFIG_USERNAME
,username
.Trim());
598 SetDlgItemText(m_hwnd
,IDC_CONFIG_USEREMAIL
,useremail
.Trim());
599 SetDlgItemText(m_hwnd
,IDC_CONFIG_AUTOCRLF
,autocrlf
.Trim());
600 SetDlgItemText(m_hwnd
,IDC_CONFIG_SAFECRLF
,safecrlf
.Trim());
602 SetDlgItemText(m_hwnd
,IDC_SHELL_CURRENT_BRANCH
,branch
.Trim());
603 SetDlgItemText(m_hwnd
,IDC_SHELL_REMOTE_BRANCH
, remotebranch
);
604 SetDlgItemText(m_hwnd
, IDC_SHELL_REMOTE_URL
, remoteUrl
);
607 CAutoCommit HEADcommit
;
608 if (!git_reference_name_to_id(&oid
, repository
, "HEAD") && !git_commit_lookup(HEADcommit
.GetPointer(), repository
, &oid
) && HEADcommit
)
609 DisplayCommit(HEADcommit
, IDC_HEAD_HASH
, IDC_HEAD_SUBJECT
, IDC_HEAD_AUTHOR
, IDC_HEAD_DATE
);
612 int stripLength
= ProjectTopDir
.GetLength();
613 if (ProjectTopDir
[stripLength
- 1] != _T('\\'))
616 bool allAreFiles
= true;
617 for (const auto& filename
: filenames
)
619 if (PathIsDirectory(filename
.c_str()))
627 size_t assumevalid
= 0;
628 size_t skipworktree
= 0;
629 size_t executable
= 0;
634 if (git_repository_index(index
.GetPointer(), repository
))
637 for (const auto& filename
: filenames
)
640 file
.SetFromWin(CString(filename
.c_str()).Mid(stripLength
));
641 CStringA pathA
= CUnicodeUtils::GetMulti(file
.GetGitPathString(), CP_UTF8
);
643 if (!git_index_find(&idx
, index
, pathA
))
645 const git_index_entry
*e
= git_index_get_byindex(index
, idx
);
647 if (e
->flags
& GIT_IDXENTRY_VALID
)
650 if (e
->flags_extended
& GIT_IDXENTRY_SKIP_WORKTREE
)
656 if ((e
->mode
& GIT_FILEMODE_LINK
) == GIT_FILEMODE_LINK
)
661 // do not show checkboxes for unversioned files
662 ShowWindow(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), SW_HIDE
);
663 ShowWindow(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), SW_HIDE
);
664 ShowWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), SW_HIDE
);
665 ShowWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), SW_HIDE
);
671 if (assumevalid
!= 0 && assumevalid
!= filenames
.size())
673 SendMessage(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), BM_SETSTYLE
, (DWORD
)GetWindowLong(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), GWL_STYLE
) & ~BS_AUTOCHECKBOX
| BS_AUTO3STATE
, 0);
674 SendMessage(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), BM_SETCHECK
, BST_INDETERMINATE
, 0);
677 SendMessage(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), BM_SETCHECK
, (assumevalid
== 0) ? BST_UNCHECKED
: BST_CHECKED
, 0);
679 if (skipworktree
!= 0 && skipworktree
!= filenames
.size())
681 SendMessage(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), BM_SETSTYLE
, (DWORD
)GetWindowLong(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), GWL_STYLE
) & ~BS_AUTOCHECKBOX
| BS_AUTO3STATE
, 0);
682 SendMessage(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), BM_SETCHECK
, BST_INDETERMINATE
, 0);
685 SendMessage(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), BM_SETCHECK
, (skipworktree
== 0) ? BST_UNCHECKED
: BST_CHECKED
, 0);
687 if (executable
!= 0 && executable
!= filenames
.size())
689 SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_SETSTYLE
, (DWORD
)GetWindowLong(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), GWL_STYLE
) & ~BS_AUTOCHECKBOX
| BS_AUTO3STATE
, 0);
690 SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_SETCHECK
, BST_INDETERMINATE
, 0);
691 EnableWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), TRUE
);
695 SendMessage(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), BM_SETCHECK
, (executable
== 0) ? BST_UNCHECKED
: BST_CHECKED
, 0);
696 EnableWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), (executable
== 0) ? TRUE
: FALSE
);
699 if (symlink
!= 0 && symlink
!= filenames
.size())
701 SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_SETSTYLE
, (DWORD
)GetWindowLong(GetDlgItem(m_hwnd
, IDC_SYMLINK
), GWL_STYLE
) & ~BS_AUTOCHECKBOX
| BS_AUTO3STATE
, 0);
702 SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_SETCHECK
, BST_INDETERMINATE
, 0);
703 EnableWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), TRUE
);
707 SendMessage(GetDlgItem(m_hwnd
, IDC_SYMLINK
), BM_SETCHECK
, (symlink
== 0) ? BST_UNCHECKED
: BST_CHECKED
, 0);
708 EnableWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), (symlink
== 0) ? TRUE
: FALSE
);
713 ShowWindow(GetDlgItem(m_hwnd
, IDC_ASSUMEVALID
), SW_HIDE
);
714 ShowWindow(GetDlgItem(m_hwnd
, IDC_SKIPWORKTREE
), SW_HIDE
);
715 ShowWindow(GetDlgItem(m_hwnd
, IDC_EXECUTABLE
), SW_HIDE
);
716 ShowWindow(GetDlgItem(m_hwnd
, IDC_SYMLINK
), SW_HIDE
);
720 if (filenames
.size() == 1 && !PathIsDirectory(filenames
[0].c_str()))
722 SetDlgItemText(m_hwnd
, IDC_LAST_SUBJECT
, CString(MAKEINTRESOURCE(IDS_LOADING
)));
723 _beginthread(LogThreadEntry
, 0, this);
726 ShowWindow(GetDlgItem(m_hwnd
, IDC_STATIC_LASTMODIFIED
), SW_HIDE
);
730 // CShellExt member functions (needed for IShellPropSheetExt)
731 STDMETHODIMP
CShellExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage
, LPARAM lParam
)
735 return AddPages_Wrap(lpfnAddPage
, lParam
);
737 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
743 STDMETHODIMP
CShellExt::AddPages_Wrap(LPFNADDPROPSHEETPAGE lpfnAddPage
, LPARAM lParam
)
745 CString ProjectTopDir
;
750 for (const auto file_
: files_
)
753 GitStatus svn = GitStatus();
754 if (svn.GetStatus(CTGitPath(I->c_str())) == (-2))
755 return S_OK; // file/directory not under version control
757 if (!svn.status->entry)
760 if (CTGitPath(file_
.c_str()).HasAdminDir(&ProjectTopDir
))
767 PROPSHEETPAGE psp
= { 0 };
768 HPROPSHEETPAGE hPage
;
769 CGitPropertyPage
*sheetpage
= new (std::nothrow
) CGitPropertyPage(files_
);
771 psp
.dwSize
= sizeof (psp
);
772 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
| PSP_USEICONID
| PSP_USECALLBACK
;
773 psp
.hInstance
= g_hResInst
;
774 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_PROPPAGE
);
775 psp
.pszIcon
= MAKEINTRESOURCE(IDI_APPSMALL
);
776 psp
.pszTitle
= _T("Git");
777 psp
.pfnDlgProc
= (DLGPROC
) PageProc
;
778 psp
.lParam
= (LPARAM
) sheetpage
;
779 psp
.pfnCallback
= PropPageCallbackProc
;
780 psp
.pcRefParent
= (UINT
*)&g_cRefThisDll
;
782 hPage
= CreatePropertySheetPage (&psp
);
786 if (!lpfnAddPage (hPage
, lParam
))
789 DestroyPropertySheetPage (hPage
);
796 STDMETHODIMP
CShellExt::ReplacePage (UINT
/*uPageID*/, LPFNADDPROPSHEETPAGE
/*lpfnReplaceWith*/, LPARAM
/*lParam*/)