1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2016 - TortoiseGit
4 // Copyright (C) 2003-2013 - TortoiseSVN
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 // RepositoryBrowser.cpp : implementation file
24 #include "TortoiseProc.h"
25 #include "RepositoryBrowser.h"
29 #include "UnicodeUtils.h"
30 #include "SysImageList.h"
33 #include "PathUtils.h"
34 #include "StringUtils.h"
37 #define OVERLAY_EXTERNAL 1
38 #define OVERLAY_EXECUTABLE 2
39 #define OVERLAY_SYMLINK 3
41 void SetSortArrowA(CListCtrl
* control
, int nColumn
, bool bAscending
)
47 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
48 HDITEM HeaderItem
= {0};
49 HeaderItem
.mask
= HDI_FORMAT
;
50 for (int i
= 0; i
< pHeader
->GetItemCount(); ++i
)
52 pHeader
->GetItem(i
, &HeaderItem
);
53 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
54 pHeader
->SetItem(i
, &HeaderItem
);
58 pHeader
->GetItem(nColumn
, &HeaderItem
);
59 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
60 pHeader
->SetItem(nColumn
, &HeaderItem
);
64 class CRepoListCompareFunc
67 CRepoListCompareFunc(CListCtrl
* pList
, int col
, bool desc
)
73 static int CALLBACK
StaticCompare(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
75 return ((CRepoListCompareFunc
*) lParamSort
)->Compare(lParam1
, lParam2
);
78 int Compare(LPARAM lParam1
, LPARAM lParam2
)
80 CShadowFilesTree
* pLeft
= (CShadowFilesTree
*)m_pList
->GetItemData((int)lParam1
);
81 CShadowFilesTree
* pRight
= (CShadowFilesTree
*)m_pList
->GetItemData((int)lParam2
);
86 case CRepositoryBrowser::eCol_Name
:
87 result
= SortStrCmp(pLeft
->m_sName
, pRight
->m_sName
);
90 case CRepositoryBrowser::eCol_Extension
:
91 result
= m_pList
->GetItemText(static_cast<int>(lParam1
), 1).CompareNoCase(m_pList
->GetItemText(static_cast<int>(lParam2
), 1));
92 if (result
== 0) // if extensions are the same, use the filename to sort
93 result
= SortStrCmp(pRight
->m_sName
, pRight
->m_sName
);
96 case CRepositoryBrowser::eCol_FileSize
:
97 if (pLeft
->m_iSize
> pRight
->m_iSize
)
99 else if (pLeft
->m_iSize
< pRight
->m_iSize
)
102 result
= SortStrCmp(pLeft
->m_sName
, pRight
->m_sName
);
108 if (pLeft
->m_bFolder
!= pRight
->m_bFolder
)
110 if (pRight
->m_bFolder
)
118 int SortStrCmp(const CString
&left
, const CString
&right
)
120 if (CRepositoryBrowser::s_bSortLogical
)
121 return StrCmpLogicalW(left
, right
);
122 return StrCmpI(left
, right
);
130 // CRepositoryBrowser dialog
132 bool CRepositoryBrowser::s_bSortLogical
= true;
134 IMPLEMENT_DYNAMIC(CRepositoryBrowser
, CResizableStandAloneDialog
)
136 CRepositoryBrowser::CRepositoryBrowser(CString rev
, CWnd
* pParent
/*=nullptr*/)
137 : CResizableStandAloneDialog(CRepositoryBrowser::IDD
, pParent
)
139 , m_currSortDesc(false)
142 , m_ColumnManager(&m_RepoList
)
144 , m_nOpenIconFolder(0)
146 , m_nExecutableOvl(0)
154 CRepositoryBrowser::~CRepositoryBrowser()
158 void CRepositoryBrowser::DoDataExchange(CDataExchange
* pDX
)
160 CDialog::DoDataExchange(pDX
);
161 DDX_Control(pDX
, IDC_REPOTREE
, m_RepoTree
);
162 DDX_Control(pDX
, IDC_REPOLIST
, m_RepoList
);
166 BEGIN_MESSAGE_MAP(CRepositoryBrowser
, CResizableStandAloneDialog
)
167 ON_NOTIFY(TVN_SELCHANGED
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnSelchangedRepoTree
)
168 ON_NOTIFY(TVN_ITEMEXPANDING
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnItemExpandingRepoTree
)
170 ON_NOTIFY(LVN_COLUMNCLICK
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnColumnclickRepoList
)
171 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnItemchangedRepolist
)
172 ON_NOTIFY(NM_DBLCLK
, IDC_REPOLIST
, &CRepositoryBrowser::OnNMDblclk_RepoList
)
173 ON_BN_CLICKED(IDC_BUTTON_REVISION
, &CRepositoryBrowser::OnBnClickedButtonRevision
)
182 // CRepositoryBrowser message handlers
184 BOOL
CRepositoryBrowser::OnInitDialog()
186 CResizableStandAloneDialog::OnInitDialog();
187 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
189 AddAnchor(IDC_STATIC_REPOURL
, TOP_LEFT
);
190 AddAnchor(IDC_REPOBROWSER_URL
, TOP_LEFT
, TOP_RIGHT
);
191 AddAnchor(IDC_STATIC_REF
, TOP_RIGHT
);
192 AddAnchor(IDC_BUTTON_REVISION
, TOP_RIGHT
);
193 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
194 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
195 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
196 AddAnchor(IDC_INFOLABEL
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
197 AddAnchor(IDOK
, BOTTOM_RIGHT
);
198 AddAnchor(IDCANCEL
, BOTTOM_RIGHT
);
200 CRepositoryBrowser::s_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER
);
201 if (CRepositoryBrowser::s_bSortLogical
)
202 CRepositoryBrowser::s_bSortLogical
= !CRegDWORD(L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE
);
204 static UINT columnNames
[] = { IDS_STATUSLIST_COLFILENAME
, IDS_STATUSLIST_COLEXT
, IDS_LOG_SIZE
};
205 static int columnWidths
[] = { 150, 100, 100 };
206 DWORD dwDefaultColumns
= (1 << eCol_Name
) | (1 << eCol_Extension
) | (1 << eCol_FileSize
);
207 m_ColumnManager
.SetNames(columnNames
, _countof(columnNames
));
208 m_ColumnManager
.ReadSettings(dwDefaultColumns
, 0, _T("RepoBrowser"), _countof(columnNames
), columnWidths
);
209 m_ColumnManager
.SetRightAlign(2);
211 // set up the list control
212 // set the extended style of the list control
213 // the style LVS_EX_FULLROWSELECT interferes with the background watermark image but it's more important to be able to select in the whole row.
214 CRegDWORD
regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE
);
215 DWORD exStyle
= LVS_EX_HEADERDRAGDROP
| LVS_EX_DOUBLEBUFFER
| LVS_EX_INFOTIP
| LVS_EX_SUBITEMIMAGES
;
216 if (DWORD(regFullRowSelect
))
217 exStyle
|= LVS_EX_FULLROWSELECT
;
218 m_RepoList
.SetExtendedStyle(exStyle
);
219 m_RepoList
.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL
);
220 CAppUtils::SetListCtrlBackgroundImage(m_RepoList
.GetSafeHwnd(), IDI_REPOBROWSER_BKG
);
222 m_RepoTree
.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL
);
223 exStyle
= TVS_EX_FADEINOUTEXPANDOS
| TVS_EX_AUTOHSCROLL
| TVS_EX_DOUBLEBUFFER
;
224 m_RepoTree
.SetExtendedStyle(exStyle
, exStyle
);
226 m_nExternalOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXTERNALOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
227 m_nExecutableOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXECUTABLEOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
228 m_nSymlinkOvl
= SYS_IMAGE_LIST().AddIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SYMLINKOVL
), IMAGE_ICON
, 0, 0, LR_DEFAULTSIZE
));
229 // set externaloverlay in SYS_IMAGE_LIST() in Refresh method, so that it is updated after every launch of the logdialog
231 SetWindowTheme(m_RepoTree
.GetSafeHwnd(), L
"Explorer", nullptr);
232 SetWindowTheme(m_RepoList
.GetSafeHwnd(), L
"Explorer", nullptr);
237 HTHEME hTheme
= OpenThemeData(m_RepoTree
, L
"TREEVIEW");
238 GetThemeMetric(hTheme
, NULL
, TVP_TREEITEM
, TREIS_NORMAL
, TMT_BORDERSIZE
, &borderWidth
);
239 CloseThemeData(hTheme
);
242 borderWidth
= GetSystemMetrics(SM_CYBORDER
);
243 m_RepoTree
.SetItemHeight((SHORT
)(m_RepoTree
.GetItemHeight() + 2 * borderWidth
));
245 m_nIconFolder
= SYS_IMAGE_LIST().GetDirIconIndex();
246 m_nOpenIconFolder
= SYS_IMAGE_LIST().GetDirOpenIconIndex();
248 EnableSaveRestore(L
"Reposbrowser");
250 DWORD xPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"), 0);
254 GetDlgItem(IDC_REPOTREE
)->GetClientRect(&rc
);
255 xPos
= rc
.right
- rc
.left
;
257 HandleDividerMove(CPoint(xPos
+ 20, 10), false);
259 CString sWindowTitle
;
260 GetWindowText(sWindowTitle
);
261 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
263 m_bHasWC
= !GitAdminDir::IsBareRepo(g_Git
.m_CurrentDir
);
267 m_RepoList
.SetFocus();
272 void CRepositoryBrowser::OnDestroy()
274 int maxcol
= m_ColumnManager
.GetColumnCount();
275 for (int col
= 0; col
< maxcol
; ++col
)
276 if (m_ColumnManager
.IsVisible(col
))
277 m_ColumnManager
.ColumnResized(col
);
278 m_ColumnManager
.WriteSettings();
280 CResizableStandAloneDialog::OnDestroy();
283 void CRepositoryBrowser::OnOK()
285 if (GetFocus() == &m_RepoList
&& (GetKeyState(VK_MENU
) & 0x8000) == 0)
287 // list control has focus: 'enter' the folder
288 if (m_RepoList
.GetSelectedCount() != 1)
291 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
294 CShadowFilesTree
*item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(m_RepoList
.GetNextSelectedItem(pos
));
297 FillListCtrlForShadowTree(item
);
298 m_RepoTree
.SelectItem(item
->m_hTree
);
301 OpenFile(item
->GetFullName(), OPEN
, item
->m_bSubmodule
, item
->m_hash
);
306 SaveDividerPosition();
307 CResizableStandAloneDialog::OnOK();
310 void CRepositoryBrowser::OnCancel()
312 SaveDividerPosition();
313 CResizableStandAloneDialog::OnCancel();
316 void CRepositoryBrowser::OnNMDblclk_RepoList(NMHDR
*pNMHDR
, LRESULT
*pResult
)
320 LPNMITEMACTIVATE pNmItemActivate
= reinterpret_cast<LPNMITEMACTIVATE
>(pNMHDR
);
321 if (pNmItemActivate
->iItem
< 0)
324 CShadowFilesTree
* pItem
= (CShadowFilesTree
*)m_RepoList
.GetItemData(pNmItemActivate
->iItem
);
328 if (!pItem
->m_bFolder
)
330 OpenFile(pItem
->GetFullName(), OPEN
, pItem
->m_bSubmodule
, pItem
->m_hash
);
335 FillListCtrlForShadowTree(pItem
);
336 m_RepoTree
.SelectItem(pItem
->m_hTree
);
340 void CRepositoryBrowser::Refresh()
343 if (m_nExternalOvl
>= 0)
344 SYS_IMAGE_LIST().SetOverlayImage(m_nExternalOvl
, OVERLAY_EXTERNAL
);
345 if (m_nExecutableOvl
>= 0)
346 SYS_IMAGE_LIST().SetOverlayImage(m_nExecutableOvl
, OVERLAY_EXECUTABLE
);
347 if (m_nSymlinkOvl
>= 0)
348 SYS_IMAGE_LIST().SetOverlayImage(m_nSymlinkOvl
, OVERLAY_SYMLINK
);
350 m_RepoTree
.DeleteAllItems();
351 m_RepoList
.DeleteAllItems();
352 m_TreeRoot
.m_ShadowTree
.clear();
353 m_TreeRoot
.m_sName
.Empty();
354 m_TreeRoot
.m_bFolder
= true;
356 TVINSERTSTRUCT tvinsert
= {0};
357 tvinsert
.hParent
= TVI_ROOT
;
358 tvinsert
.hInsertAfter
= TVI_ROOT
;
359 tvinsert
.itemex
.mask
= TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
;
360 tvinsert
.itemex
.pszText
= L
"/";
361 tvinsert
.itemex
.lParam
= (LPARAM
)&m_TreeRoot
;
362 tvinsert
.itemex
.iImage
= m_nIconFolder
;
363 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
364 m_TreeRoot
.m_hTree
= m_RepoTree
.InsertItem(&tvinsert
);
366 ReadTree(&m_TreeRoot
);
367 m_RepoTree
.Expand(m_TreeRoot
.m_hTree
, TVE_EXPAND
);
368 FillListCtrlForShadowTree(&m_TreeRoot
);
369 m_RepoTree
.SelectItem(m_TreeRoot
.m_hTree
);
373 int CRepositoryBrowser::ReadTreeRecursive(git_repository
&repo
, const git_tree
* tree
, CShadowFilesTree
* treeroot
)
375 size_t count
= git_tree_entrycount(tree
);
376 bool hasSubfolders
= false;
378 for (size_t i
= 0; i
< count
; ++i
)
380 const git_tree_entry
*entry
= git_tree_entry_byindex(tree
, i
);
384 const int mode
= git_tree_entry_filemode(entry
);
386 CString base
= CUnicodeUtils::GetUnicode(git_tree_entry_name(entry
), CP_UTF8
);
388 const git_oid
*oid
= git_tree_entry_id(entry
);
389 CShadowFilesTree
* pNextTree
= &treeroot
->m_ShadowTree
[base
];
390 pNextTree
->m_sName
= base
;
391 pNextTree
->m_pParent
= treeroot
;
392 pNextTree
->m_hash
= CGitHash((char *)oid
->id
);
394 if (mode
== GIT_FILEMODE_COMMIT
)
395 pNextTree
->m_bSubmodule
= true;
396 else if (mode
& S_IFDIR
)
398 hasSubfolders
= true;
399 pNextTree
->m_bFolder
= true;
400 pNextTree
->m_bLoaded
= false;
402 TVINSERTSTRUCT tvinsert
= {0};
403 tvinsert
.hParent
= treeroot
->m_hTree
;
404 tvinsert
.hInsertAfter
= TVI_SORT
;
405 tvinsert
.itemex
.mask
= TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
| TVIF_CHILDREN
;
406 tvinsert
.itemex
.pszText
= base
.GetBuffer(base
.GetLength());
407 tvinsert
.itemex
.cChildren
= 1;
408 tvinsert
.itemex
.lParam
= (LPARAM
)pNextTree
;
409 tvinsert
.itemex
.iImage
= m_nIconFolder
;
410 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
411 pNextTree
->m_hTree
= m_RepoTree
.InsertItem(&tvinsert
);
412 base
.ReleaseBuffer();
416 if (mode
== GIT_FILEMODE_BLOB_EXECUTABLE
)
417 pNextTree
->m_bExecutable
= true;
418 if (mode
== GIT_FILEMODE_LINK
)
419 pNextTree
->m_bSymlink
= true;
421 git_blob_lookup(blob
.GetPointer(), &repo
, oid
);
425 pNextTree
->m_iSize
= git_blob_rawsize(blob
);
431 TVITEM tvitem
= { 0 };
432 tvitem
.hItem
= treeroot
->m_hTree
;
433 tvitem
.mask
= TVIF_CHILDREN
;
434 tvitem
.cChildren
= 0;
435 m_RepoTree
.SetItem(&tvitem
);
441 int CRepositoryBrowser::ReadTree(CShadowFilesTree
* treeroot
, const CString
& root
)
444 CAutoRepository
repository(g_Git
.GetGitRepository());
447 MessageBox(CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR
);
451 if (m_sRevision
== _T("HEAD"))
453 int ret
= git_repository_head_unborn(repository
);
454 if (ret
== 1) // is orphan
458 MessageBox(g_Git
.GetLibGit2LastErr(_T("Could not check HEAD.")), _T("TortoiseGit"), MB_ICONERROR
);
464 if (CGit::GetHash(repository
, hash
, m_sRevision
))
466 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
471 if (git_commit_lookup(commit
.GetPointer(), repository
, (git_oid
*)hash
.m_hash
))
473 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup commit.")), _T("TortoiseGit"), MB_ICONERROR
);
478 if (git_commit_tree(tree
.GetPointer(), commit
))
480 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get tree of commit.")), _T("TortoiseGit"), MB_ICONERROR
);
486 CAutoTreeEntry treeEntry
;
487 if (git_tree_entry_bypath(treeEntry
.GetPointer(), tree
, CUnicodeUtils::GetUTF8(root
)))
489 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
492 if (git_tree_entry_type(treeEntry
) != GIT_OBJ_TREE
)
494 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
499 if (git_tree_entry_to_object(object
.GetPointer(), repository
, treeEntry
))
501 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR
);
505 tree
= (git_tree
*)object
.Detach();
508 treeroot
->m_hash
= CGitHash((char *)git_tree_id(tree
)->id
);
509 ReadTreeRecursive(*repository
, tree
, treeroot
);
511 // try to resolve hash to a branch name
512 if (m_sRevision
== hash
.ToString())
515 if (CGit::GetMapHashToFriendName(repository
, map
))
516 MessageBox(g_Git
.GetLibGit2LastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR
);
517 if (!map
[hash
].empty())
518 m_sRevision
= map
[hash
].at(0);
520 this->GetDlgItem(IDC_BUTTON_REVISION
)->SetWindowText(m_sRevision
);
525 void CRepositoryBrowser::OnTvnSelchangedRepoTree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
527 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
530 FillListCtrlForTreeNode(pNMTreeView
->itemNew
.hItem
);
533 void CRepositoryBrowser::OnTvnItemExpandingRepoTree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
535 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
538 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)(m_RepoTree
.GetItemData(pNMTreeView
->itemNew
.hItem
));
545 if (!pTree
->m_bLoaded
)
547 pTree
->m_bLoaded
= true;
548 ReadTree(pTree
, pTree
->GetFullName());
552 void CRepositoryBrowser::FillListCtrlForTreeNode(HTREEITEM treeNode
)
554 m_RepoList
.DeleteAllItems();
556 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)(m_RepoTree
.GetItemData(treeNode
));
563 CString url
= _T("/") + pTree
->GetFullName();
564 GetDlgItem(IDC_REPOBROWSER_URL
)->SetWindowText(url
);
566 if (!pTree
->m_bLoaded
)
568 pTree
->m_bLoaded
= true;
569 ReadTree(pTree
, pTree
->GetFullName());
572 FillListCtrlForShadowTree(pTree
);
575 void CRepositoryBrowser::FillListCtrlForShadowTree(CShadowFilesTree
* pTree
)
577 for (auto itShadowTree
= pTree
->m_ShadowTree
.cbegin(); itShadowTree
!= pTree
->m_ShadowTree
.cend(); ++itShadowTree
)
579 int icon
= m_nIconFolder
;
580 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
582 icon
= SYS_IMAGE_LIST().GetFileIconIndex((*itShadowTree
).second
.m_sName
);
585 int indexItem
= m_RepoList
.InsertItem(m_RepoList
.GetItemCount(), (*itShadowTree
).second
.m_sName
, icon
);
587 if ((*itShadowTree
).second
.m_bSubmodule
)
589 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_EXTERNAL
), LVIS_OVERLAYMASK
);
591 if ((*itShadowTree
).second
.m_bExecutable
)
592 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_EXECUTABLE
), LVIS_OVERLAYMASK
);
593 if ((*itShadowTree
).second
.m_bSymlink
)
594 m_RepoList
.SetItemState(indexItem
, INDEXTOOVERLAYMASK(OVERLAY_SYMLINK
), LVIS_OVERLAYMASK
);
595 m_RepoList
.SetItemData(indexItem
, (DWORD_PTR
)&(*itShadowTree
).second
);
596 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
600 temp
= CPathUtils::GetFileExtFromPath((*itShadowTree
).second
.m_sName
);
601 m_RepoList
.SetItemText(indexItem
, eCol_Extension
, temp
);
603 StrFormatByteSize64((*itShadowTree
).second
.m_iSize
, temp
.GetBuffer(20), 20);
604 temp
.ReleaseBuffer();
605 m_RepoList
.SetItemText(indexItem
, eCol_FileSize
, temp
);
609 CRepoListCompareFunc
compareFunc(&m_RepoList
, m_currSortCol
, m_currSortDesc
);
610 m_RepoList
.SortItemsEx(&CRepoListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
612 SetSortArrowA(&m_RepoList
, m_currSortCol
, !m_currSortDesc
);
617 void CRepositoryBrowser::UpdateInfoLabel()
620 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
623 if (m_RepoList
.GetSelectedCount() > 1)
625 temp
.FormatMessage(IDS_REPOBROWSE_INFOMULTI
, m_RepoList
.GetSelectedCount());
629 int index
= m_RepoList
.GetNextSelectedItem(pos
);
630 CShadowFilesTree
*item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(index
);
631 if (item
->m_bSubmodule
)
632 temp
.FormatMessage(IDS_REPOBROWSE_INFOEXT
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
), item
->m_hash
.ToString());
633 else if (item
->m_bFolder
)
634 temp
.FormatMessage(IDS_REPOBROWSE_INFODIR
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
));
636 temp
.FormatMessage(IDS_REPOBROWSE_INFOFILE
, (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_Name
), (LPCTSTR
)m_RepoList
.GetItemText(index
, eCol_FileSize
));
641 HTREEITEM hTreeItem
= m_RepoTree
.GetSelectedItem();
642 if (hTreeItem
!= nullptr)
644 CShadowFilesTree
* pTree
= (CShadowFilesTree
*)m_RepoTree
.GetItemData(hTreeItem
);
645 if (pTree
!= nullptr)
647 size_t files
= 0, submodules
= 0;
648 for (auto itShadowTree
= pTree
->m_ShadowTree
.cbegin(); itShadowTree
!= pTree
->m_ShadowTree
.cend(); ++itShadowTree
)
650 if (!(*itShadowTree
).second
.m_bFolder
&& !(*itShadowTree
).second
.m_bSubmodule
)
652 if ((*itShadowTree
).second
.m_bSubmodule
)
655 temp
.FormatMessage(IDS_REPOBROWSE_INFO
, (LPCTSTR
)pTree
->m_sName
, files
, submodules
, pTree
->m_ShadowTree
.size() - files
- submodules
, pTree
->m_ShadowTree
.size());
659 SetDlgItemText(IDC_INFOLABEL
, temp
);
662 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR
* /* pNMHDR */, LRESULT
*pResult
)
668 void CRepositoryBrowser::OnContextMenu(CWnd
* pWndFrom
, CPoint point
)
670 if (pWndFrom
== &m_RepoList
)
672 CRect headerPosition
;
673 m_RepoList
.GetHeaderCtrl()->GetWindowRect(headerPosition
);
674 if (!headerPosition
.PtInRect(point
))
675 OnContextMenu_RepoList(point
);
677 else if (pWndFrom
== &m_RepoTree
)
678 OnContextMenu_RepoTree(point
);
681 void CRepositoryBrowser::OnContextMenu_RepoTree(CPoint point
)
683 CPoint clientPoint
= point
;
684 m_RepoTree
.ScreenToClient(&clientPoint
);
686 HTREEITEM hTreeItem
= m_RepoTree
.HitTest(clientPoint
);
690 TShadowFilesTreeList selectedLeafs
;
691 selectedLeafs
.push_back((CShadowFilesTree
*)m_RepoTree
.GetItemData(hTreeItem
));
693 ShowContextMenu(point
, selectedLeafs
, ONLY_FOLDERS
);
696 void CRepositoryBrowser::OnContextMenu_RepoList(CPoint point
)
698 TShadowFilesTreeList selectedLeafs
;
699 selectedLeafs
.reserve(m_RepoList
.GetSelectedCount());
701 bool folderSelected
= false;
702 bool filesSelected
= false;
703 bool submodulesSelected
= false;
705 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
708 CShadowFilesTree
* item
= (CShadowFilesTree
*)m_RepoList
.GetItemData(m_RepoList
.GetNextSelectedItem(pos
));
709 if (item
->m_bSubmodule
)
710 submodulesSelected
= true;
712 folderSelected
= true;
714 filesSelected
= true;
715 selectedLeafs
.push_back(item
);
718 eSelectionType selType
= ONLY_FILES
;
719 if (folderSelected
&& filesSelected
)
720 selType
= MIXED_FOLDERS_FILES
;
721 else if (folderSelected
)
722 selType
= ONLY_FOLDERS
;
723 else if (submodulesSelected
)
724 selType
= ONLY_FILESSUBMODULES
;
725 ShowContextMenu(point
, selectedLeafs
, selType
);
728 void CRepositoryBrowser::ShowContextMenu(CPoint point
, TShadowFilesTreeList
&selectedLeafs
, eSelectionType selType
)
731 popupMenu
.CreatePopupMenu();
733 bool bAddSeparator
= false;
735 if (selectedLeafs
.size() == 1)
737 popupMenu
.AppendMenuIcon(eCmd_Open
, IDS_REPOBROWSE_OPEN
, IDI_OPEN
);
738 popupMenu
.SetDefaultItem(eCmd_Open
, FALSE
);
739 if (selType
== ONLY_FILES
|| selType
== ONLY_FILESSUBMODULES
)
741 popupMenu
.AppendMenuIcon(eCmd_OpenWith
, IDS_LOG_POPUP_OPENWITH
, IDI_OPEN
);
742 popupMenu
.AppendMenuIcon(eCmd_OpenWithAlternativeEditor
, IDS_LOG_POPUP_VIEWREV
);
745 popupMenu
.AppendMenu(MF_SEPARATOR
);
747 if (m_bHasWC
&& (selType
== ONLY_FILES
|| selType
== ONLY_FILESSUBMODULES
))
749 popupMenu
.AppendMenuIcon(eCmd_CompareWC
, IDS_LOG_POPUP_COMPARE
, IDI_DIFF
);
750 bAddSeparator
= true;
754 popupMenu
.AppendMenu(MF_SEPARATOR
);
755 bAddSeparator
= false;
758 temp
.LoadString(IDS_MENULOG
);
759 popupMenu
.AppendMenuIcon(eCmd_ViewLog
, temp
, IDI_LOG
);
760 if (selectedLeafs
[0]->m_bSubmodule
)
762 temp
.LoadString(IDS_MENULOGSUBMODULE
);
763 popupMenu
.AppendMenuIcon(eCmd_ViewLogSubmodule
, temp
, IDI_LOG
);
766 if (selType
== ONLY_FILES
)
769 popupMenu
.AppendMenuIcon(eCmd_Blame
, IDS_LOG_POPUP_BLAME
, IDI_BLAME
);
771 popupMenu
.AppendMenu(MF_SEPARATOR
);
772 temp
.LoadString(IDS_LOG_POPUP_SAVE
);
773 popupMenu
.AppendMenuIcon(eCmd_SaveAs
, temp
, IDI_SAVEAS
);
776 bAddSeparator
= true;
779 if (!selectedLeafs
.empty() && selType
== ONLY_FILES
&& m_bHasWC
)
781 popupMenu
.AppendMenuIcon(eCmd_Revert
, IDS_LOG_POPUP_REVERTTOREV
, IDI_REVERT
);
782 bAddSeparator
= true;
786 popupMenu
.AppendMenu(MF_SEPARATOR
);
787 bAddSeparator
= false;
789 if (selectedLeafs
.size() == 1 && selType
== ONLY_FILES
)
791 popupMenu
.AppendMenuIcon(eCmd_PrepareDiff
, IDS_PREPAREDIFF
, IDI_DIFF
);
792 if (!m_sMarkForDiffFilename
.IsEmpty())
795 if (selectedLeafs
.at(0)->GetFullName() == m_sMarkForDiffFilename
)
796 diffWith
= m_sMarkForDiffVersion
;
799 PathCompactPathEx(diffWith
.GetBuffer(40), m_sMarkForDiffFilename
, 39, 0);
800 diffWith
.ReleaseBuffer();
801 diffWith
+= _T(":") + m_sMarkForDiffVersion
.ToString().Left(g_Git
.GetShortHASHLength());
804 menuEntry
.Format(IDS_MENUDIFFNOW
, (LPCTSTR
)diffWith
);
805 popupMenu
.AppendMenuIcon(eCmd_PrepareDiff_Compare
, menuEntry
, IDI_DIFF
);
807 popupMenu
.AppendMenu(MF_SEPARATOR
);
810 if (!selectedLeafs
.empty())
812 popupMenu
.AppendMenuIcon(eCmd_CopyPath
, IDS_STATUSLIST_CONTEXT_COPY
, IDI_COPYCLIP
);
813 popupMenu
.AppendMenuIcon(eCmd_CopyHash
, IDS_COPY_COMMIT_HASH
, IDI_COPYCLIP
);
816 eCmd cmd
= (eCmd
)popupMenu
.TrackPopupMenuEx(TPM_LEFTALIGN
|TPM_RETURNCMD
, point
.x
, point
.y
, this, 0);
820 case eCmd_ViewLogSubmodule
:
823 sCmd
.Format(_T("/command:log /path:\"%s\""), (LPCTSTR
)g_Git
.CombinePath(selectedLeafs
.at(0)->GetFullName()));
824 if (cmd
== eCmd_ViewLog
&& selectedLeafs
.at(0)->m_bSubmodule
)
825 sCmd
+= _T(" /submodule");
826 CAppUtils::RunTortoiseGitProc(sCmd
);
831 CAppUtils::LaunchTortoiseBlame(g_Git
.CombinePath(selectedLeafs
.at(0)->GetFullName()), m_sRevision
);
835 if (!selectedLeafs
.at(0)->m_bSubmodule
&& selectedLeafs
.at(0)->m_bFolder
)
837 FillListCtrlForTreeNode(selectedLeafs
.at(0)->m_hTree
);
838 m_RepoTree
.SelectItem(selectedLeafs
.at(0)->m_hTree
);
841 OpenFile(selectedLeafs
.at(0)->GetFullName(), OPEN
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
844 OpenFile(selectedLeafs
.at(0)->GetFullName(), OPEN_WITH
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
846 case eCmd_OpenWithAlternativeEditor
:
847 OpenFile(selectedLeafs
.at(0)->GetFullName(), ALTERNATIVEEDITOR
, selectedLeafs
.at(0)->m_bSubmodule
, selectedLeafs
.at(0)->m_hash
);
851 CTGitPath
file(selectedLeafs
.at(0)->GetFullName());
852 CGitDiff::Diff(&file
, &file
, GIT_REV_ZERO
, m_sRevision
);
858 for (auto itShadowTree
= selectedLeafs
.cbegin(); itShadowTree
!= selectedLeafs
.cend(); ++itShadowTree
)
860 if (RevertItemToVersion((*itShadowTree
)->GetFullName()))
866 msg
.Format(IDS_STATUSLIST_FILESREVERTED
, count
, (LPCTSTR
)m_sRevision
);
867 MessageBox(msg
, _T("TortoiseGit"), MB_OK
);
871 FileSaveAs(selectedLeafs
.at(0)->GetFullName());
876 for (auto itShadowTree
= selectedLeafs
.cbegin(); itShadowTree
!= selectedLeafs
.cend(); ++itShadowTree
)
878 sClipboard
+= (*itShadowTree
)->m_sName
+ _T("\r\n");
880 CStringUtils::WriteAsciiStringToClipboard(sClipboard
);
885 CopyHashToClipboard(selectedLeafs
);
888 case eCmd_PrepareDiff
:
889 m_sMarkForDiffFilename
= selectedLeafs
.at(0)->GetFullName();
890 if (g_Git
.GetHash(m_sMarkForDiffVersion
, m_sRevision
))
892 m_sMarkForDiffFilename
.Empty();
893 MessageBox(g_Git
.GetGitLastErr(_T("Could not get SHA-1 for ") + m_sRevision
), _T("TortoiseGit"), MB_ICONERROR
);
896 case eCmd_PrepareDiff_Compare
:
898 CTGitPath
savedFile(m_sMarkForDiffFilename
);
899 CTGitPath
selectedFile(selectedLeafs
.at(0)->GetFullName());
900 CGitHash currentHash
;
901 if (g_Git
.GetHash(currentHash
, m_sRevision
))
903 MessageBox(g_Git
.GetGitLastErr(_T("Could not get SHA-1 for ") + m_sRevision
), _T("TortoiseGit"), MB_ICONERROR
);
906 CGitDiff::Diff(&selectedFile
, &savedFile
, currentHash
, m_sMarkForDiffVersion
);
912 BOOL
CRepositoryBrowser::PreTranslateMessage(MSG
* pMsg
)
914 if (pMsg
->message
== WM_KEYDOWN
)
916 switch (pMsg
->wParam
)
926 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
929 void CRepositoryBrowser::OnLvnColumnclickRepoList(NMHDR
*pNMHDR
, LRESULT
*pResult
)
931 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
935 if (m_currSortCol
== pNMLV
->iSubItem
)
936 m_currSortDesc
= !m_currSortDesc
;
939 m_currSortCol
= pNMLV
->iSubItem
;
940 m_currSortDesc
= false;
943 CRepoListCompareFunc
compareFunc(&m_RepoList
, m_currSortCol
, m_currSortDesc
);
944 m_RepoList
.SortItemsEx(&CRepoListCompareFunc::StaticCompare
, (DWORD_PTR
)&compareFunc
);
946 SetSortArrowA(&m_RepoList
, m_currSortCol
, !m_currSortDesc
);
949 void CRepositoryBrowser::OnBnClickedButtonRevision()
951 // use the git log to allow selection of a version
953 dlg
.SetParams(CTGitPath(), CTGitPath(), m_sRevision
, m_sRevision
, 0);
954 // tell the dialog to use mode for selecting revisions
956 dlg
.ShowWorkingTreeChanges(false);
957 // only one revision must be selected however
958 dlg
.SingleSelection(true);
959 if (dlg
.DoModal() == IDOK
)
961 m_sRevision
= dlg
.GetSelectedHash().at(0).ToString();
966 void CRepositoryBrowser::SaveDividerPosition()
969 GetDlgItem(IDC_REPOTREE
)->GetClientRect(&rc
);
970 CRegDWORD
xPos(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"));
971 xPos
= rc
.right
- rc
.left
;
974 void CRepositoryBrowser::HandleDividerMove(CPoint point
, bool bDraw
)
976 RECT rect
, tree
, list
, treelist
, treelistclient
;
978 // create an union of the tree and list control rectangle
979 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
980 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
981 UnionRect(&treelist
, &tree
, &list
);
982 treelistclient
= treelist
;
983 ScreenToClient(&treelistclient
);
985 ClientToScreen(&point
);
986 GetClientRect(&rect
);
987 ClientToScreen(&rect
);
989 CPoint point2
= point
;
990 if (point2
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
991 point2
.x
= treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
;
992 if (point2
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
993 point2
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
995 point
.x
-= rect
.left
;
996 point
.y
-= treelist
.top
;
998 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
1000 if (point
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
1001 point
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
1002 if (point
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
1003 point
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
1007 CDC
* pDC
= GetDC();
1008 DrawXorBar(pDC
, oldx
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1015 //position the child controls
1016 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&treelist
);
1017 treelist
.right
= point2
.x
- 2;
1018 ScreenToClient(&treelist
);
1019 RemoveAnchor(IDC_REPOTREE
);
1020 GetDlgItem(IDC_REPOTREE
)->MoveWindow(&treelist
);
1021 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&treelist
);
1022 treelist
.left
= point2
.x
+ 2;
1023 ScreenToClient(&treelist
);
1024 RemoveAnchor(IDC_REPOLIST
);
1025 GetDlgItem(IDC_REPOLIST
)->MoveWindow(&treelist
);
1027 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
1028 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
1031 void CRepositoryBrowser::OnMouseMove(UINT nFlags
, CPoint point
)
1033 if (bDragMode
== FALSE
)
1036 RECT rect
, tree
, list
, treelist
, treelistclient
;
1037 // create an union of the tree and list control rectangle
1038 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
1039 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
1040 UnionRect(&treelist
, &tree
, &list
);
1041 treelistclient
= treelist
;
1042 ScreenToClient(&treelistclient
);
1044 //convert the mouse coordinates relative to the top-left of
1046 ClientToScreen(&point
);
1047 GetClientRect(&rect
);
1048 ClientToScreen(&rect
);
1049 point
.x
-= rect
.left
;
1050 point
.y
-= treelist
.top
;
1052 //same for the window coordinates - make them relative to 0,0
1053 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
1055 if (point
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
1056 point
.x
= treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
;
1057 if (point
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
1058 point
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
1060 if ((nFlags
& MK_LBUTTON
) && (point
.x
!= oldx
))
1062 CDC
* pDC
= GetDC();
1066 DrawXorBar(pDC
, oldx
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1067 DrawXorBar(pDC
, point
.x
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1076 CStandAloneDialogTmpl
<CResizableDialog
>::OnMouseMove(nFlags
, point
);
1079 void CRepositoryBrowser::OnLButtonDown(UINT nFlags
, CPoint point
)
1081 RECT rect
, tree
, list
, treelist
, treelistclient
;
1083 // create an union of the tree and list control rectangle
1084 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
1085 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
1086 UnionRect(&treelist
, &tree
, &list
);
1087 treelistclient
= treelist
;
1088 ScreenToClient(&treelistclient
);
1090 //convert the mouse coordinates relative to the top-left of
1092 ClientToScreen(&point
);
1093 GetClientRect(&rect
);
1094 ClientToScreen(&rect
);
1095 point
.x
-= rect
.left
;
1096 point
.y
-= treelist
.top
;
1098 //same for the window coordinates - make them relative to 0,0
1099 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
1101 if (point
.x
< treelist
.left
+ REPOBROWSER_CTRL_MIN_WIDTH
)
1102 return CStandAloneDialogTmpl
< CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1103 if (point
.x
> treelist
.right
- 3)
1104 return CStandAloneDialogTmpl
< CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1105 if (point
.x
> treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
)
1106 point
.x
= treelist
.right
- REPOBROWSER_CTRL_MIN_WIDTH
;
1108 if ((point
.y
< treelist
.top
+ 3) || (point
.y
> treelist
.bottom
- 3))
1109 return CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1115 CDC
* pDC
= GetDC();
1116 DrawXorBar(pDC
, point
.x
+ 2, treelistclient
.top
, 4, treelistclient
.bottom
- treelistclient
.top
- 2);
1122 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
1125 void CRepositoryBrowser::OnLButtonUp(UINT nFlags
, CPoint point
)
1127 if (bDragMode
== FALSE
)
1130 HandleDividerMove(point
, true);
1135 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonUp(nFlags
, point
);
1138 void CRepositoryBrowser::OnCaptureChanged(CWnd
*pWnd
)
1142 __super::OnCaptureChanged(pWnd
);
1145 void CRepositoryBrowser::DrawXorBar(CDC
* pDC
, int x1
, int y1
, int width
, int height
)
1147 static WORD _dotPatternBmp
[8] =
1149 0x0055, 0x00aa, 0x0055, 0x00aa,
1150 0x0055, 0x00aa, 0x0055, 0x00aa
1154 HBRUSH hbr
, hbrushOld
;
1156 hbm
= CreateBitmap(8, 8, 1, 1, _dotPatternBmp
);
1157 hbr
= CreatePatternBrush(hbm
);
1159 pDC
->SetBrushOrg(x1
, y1
);
1160 hbrushOld
= (HBRUSH
)pDC
->SelectObject(hbr
);
1162 PatBlt(pDC
->GetSafeHdc(), x1
, y1
, width
, height
, PATINVERT
);
1164 pDC
->SelectObject(hbrushOld
);
1170 BOOL
CRepositoryBrowser::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1176 GetClientRect(&rect
);
1178 ScreenToClient(&pt
);
1179 if (PtInRect(&rect
, pt
))
1181 ClientToScreen(&pt
);
1182 // are we right of the tree control?
1183 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&rect
);
1184 if ((pt
.x
> rect
.right
) && (pt
.y
>= rect
.top
+ 3) && (pt
.y
<= rect
.bottom
- 3))
1186 // but left of the list control?
1187 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&rect
);
1188 if (pt
.x
< rect
.left
)
1190 HCURSOR hCur
= LoadCursor(nullptr, IDC_SIZEWE
);
1197 return CStandAloneDialogTmpl
<CResizableDialog
>::OnSetCursor(pWnd
, nHitTest
, message
);
1200 void CRepositoryBrowser::FileSaveAs(const CString path
)
1202 CTGitPath
gitPath(path
);
1205 if (g_Git
.GetHash(hash
, m_sRevision
))
1207 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
1212 filename
.Format(_T("%s-%s%s"), (LPCTSTR
)gitPath
.GetBaseFilename(), (LPCTSTR
)hash
.ToString().Left(g_Git
.GetShortHASHLength()), (LPCTSTR
)gitPath
.GetFileExtension());
1213 CFileDialog
dlg(FALSE
, nullptr, filename
, OFN_HIDEREADONLY
| OFN_OVERWRITEPROMPT
, nullptr);
1215 CString
currentpath(g_Git
.CombinePath(gitPath
.GetContainingDirectory()));
1216 dlg
.m_ofn
.lpstrInitialDir
= currentpath
;
1219 INT_PTR ret
= dlg
.DoModal();
1220 SetCurrentDirectory(g_Git
.m_CurrentDir
);
1223 filename
= dlg
.GetPathName();
1224 if (g_Git
.GetOneFile(m_sRevision
, gitPath
, filename
))
1226 out
.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED
, (LPCTSTR
)gitPath
.GetGitPathString(), (LPCTSTR
)m_sRevision
, (LPCTSTR
)filename
);
1227 MessageBox(g_Git
.GetGitLastErr(out
, CGit::GIT_CMD_GETONEFILE
), _T("TortoiseGit"), MB_ICONERROR
);
1233 void CRepositoryBrowser::OpenFile(const CString path
, eOpenType mode
, bool isSubmodule
, CGitHash itemHash
)
1235 CTGitPath
gitPath(path
);
1239 GetTempPath(temppath
);
1241 if (g_Git
.GetHash(hash
, m_sRevision
))
1243 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of ") + m_sRevision
+ _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
1247 file
.Format(_T("%s%s_%s%s"), (LPCTSTR
)temppath
, (LPCTSTR
)gitPath
.GetBaseFilename(), (LPCTSTR
)hash
.ToString().Left(g_Git
.GetShortHASHLength()), (LPCTSTR
)gitPath
.GetFileExtension());
1251 if (mode
== OPEN
&& !GitAdminDir::IsBareRepo(g_Git
.m_CurrentDir
))
1253 CTGitPath subPath
= CTGitPath(g_Git
.m_CurrentDir
);
1254 subPath
.AppendPathString(gitPath
.GetWinPathString());
1255 CAutoRepository
repo(subPath
.GetGitPathString());
1257 if (!repo
|| git_commit_lookup(commit
.GetPointer(), repo
, (const git_oid
*)itemHash
.m_hash
))
1260 out
.Format(IDS_REPOBROWSEASKSUBMODULEUPDATE
, (LPCTSTR
)itemHash
.ToString(), (LPCTSTR
)gitPath
.GetGitPathString());
1261 if (MessageBox(out
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) != IDYES
)
1265 sCmd
.Format(_T("/command:subupdate /bkpath:\"%s\" /selectedpath:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)gitPath
.GetGitPathString());
1266 CAppUtils::RunTortoiseGitProc(sCmd
);
1271 cmd
.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), (LPCTSTR
)g_Git
.CombinePath(path
), (LPCTSTR
)itemHash
.ToString());
1272 CAppUtils::RunTortoiseGitProc(cmd
);
1277 CFile
submoduleCommit(file
, CFile::modeCreate
| CFile::modeWrite
);
1278 CStringA commitInfo
= "Subproject commit " + CStringA(itemHash
.ToString());
1279 submoduleCommit
.Write(commitInfo
, commitInfo
.GetLength());
1281 else if (g_Git
.GetOneFile(m_sRevision
, gitPath
, file
))
1284 out
.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED
, (LPCTSTR
)gitPath
.GetGitPathString(), (LPCTSTR
)m_sRevision
, (LPCTSTR
)file
);
1285 MessageBox(g_Git
.GetGitLastErr(out
, CGit::GIT_CMD_GETONEFILE
), _T("TortoiseGit"), MB_ICONERROR
);
1289 if (mode
== ALTERNATIVEEDITOR
)
1291 CAppUtils::LaunchAlternativeEditor(file
);
1294 else if (mode
== OPEN
)
1296 CAppUtils::ShellOpen(file
);
1300 CAppUtils::ShowOpenWithDialog(file
);
1302 bool CRepositoryBrowser::RevertItemToVersion(const CString
&path
)
1305 cmd
.Format(_T("git.exe checkout %s -- \"%s\""), (LPCTSTR
)m_sRevision
, (LPCTSTR
)path
);
1306 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
1308 if (MessageBox(out
, _T("TortoiseGit"), MB_ICONEXCLAMATION
| MB_OKCANCEL
) == IDCANCEL
)
1315 void CRepositoryBrowser::CopyHashToClipboard(TShadowFilesTreeList
&selectedLeafs
)
1317 if (!selectedLeafs
.empty())
1321 for (size_t i
= 0; i
< selectedLeafs
.size(); ++i
)
1324 sClipdata
+= _T("\r\n");
1325 sClipdata
+= selectedLeafs
[i
]->m_hash
;
1328 CStringUtils::WriteAsciiStringToClipboard(sClipdata
, GetSafeHwnd());