Do not use GitAdminDir objects
[TortoiseGit.git] / src / TortoiseProc / RepositoryBrowser.cpp
blobd36e203d72b5eada81b446e5f10feb0260e37655
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2015 - 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
23 #include "stdafx.h"
24 #include "TortoiseProc.h"
25 #include "RepositoryBrowser.h"
26 #include "LogDlg.h"
27 #include "AppUtils.h"
28 #include "IconMenu.h"
29 #include "UnicodeUtils.h"
30 #include "SysImageList.h"
31 #include <sys/stat.h>
32 #include "SysInfo.h"
33 #include "registry.h"
34 #include "PathUtils.h"
35 #include "StringUtils.h"
36 #include "GitDiff.h"
38 #define OVERLAY_EXTERNAL 1
39 #define OVERLAY_EXECUTABLE 2
40 #define OVERLAY_SYMLINK 3
42 void SetSortArrowA(CListCtrl * control, int nColumn, bool bAscending)
44 if (control == NULL)
45 return;
47 // set the sort arrow
48 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
49 HDITEM HeaderItem = {0};
50 HeaderItem.mask = HDI_FORMAT;
51 for (int i = 0; i < pHeader->GetItemCount(); ++i)
53 pHeader->GetItem(i, &HeaderItem);
54 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
55 pHeader->SetItem(i, &HeaderItem);
57 if (nColumn >= 0)
59 pHeader->GetItem(nColumn, &HeaderItem);
60 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
61 pHeader->SetItem(nColumn, &HeaderItem);
65 class CRepoListCompareFunc
67 public:
68 CRepoListCompareFunc(CListCtrl* pList, int col, bool desc)
69 : m_col(col)
70 , m_desc(desc)
71 , m_pList(pList)
74 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
76 return ((CRepoListCompareFunc *) lParamSort)->Compare(lParam1, lParam2);
79 int Compare(LPARAM lParam1, LPARAM lParam2)
81 CShadowFilesTree * pLeft = (CShadowFilesTree *)m_pList->GetItemData((int)lParam1);
82 CShadowFilesTree * pRight = (CShadowFilesTree *)m_pList->GetItemData((int)lParam2);
84 int result = 0;
85 switch(m_col)
87 case CRepositoryBrowser::eCol_Name:
88 result = SortStrCmp(pLeft->m_sName, pRight->m_sName);
89 if (result != 0)
90 break;
91 case CRepositoryBrowser::eCol_Extension:
92 result = m_pList->GetItemText(static_cast<int>(lParam1), 1).CompareNoCase(m_pList->GetItemText(static_cast<int>(lParam2), 1));
93 if (result == 0) // if extensions are the same, use the filename to sort
94 result = SortStrCmp(pRight->m_sName, pRight->m_sName);
95 if (result != 0)
96 break;
97 case CRepositoryBrowser::eCol_FileSize:
98 if (pLeft->m_iSize > pRight->m_iSize)
99 result = 1;
100 else if (pLeft->m_iSize < pRight->m_iSize)
101 result = -1;
102 else // fallback
103 result = SortStrCmp(pLeft->m_sName, pRight->m_sName);
106 if (m_desc)
107 result = -result;
109 if (pLeft->m_bFolder != pRight->m_bFolder)
111 if (pRight->m_bFolder)
112 result = 1;
113 else
114 result = -1;
117 return result;
119 int SortStrCmp(const CString &left, const CString &right)
121 if (CRepositoryBrowser::s_bSortLogical)
122 return StrCmpLogicalW(left, right);
123 return StrCmpI(left, right);
126 int m_col;
127 bool m_desc;
128 CListCtrl* m_pList;
131 // CRepositoryBrowser dialog
133 bool CRepositoryBrowser::s_bSortLogical = true;
135 IMPLEMENT_DYNAMIC(CRepositoryBrowser, CResizableStandAloneDialog)
137 CRepositoryBrowser::CRepositoryBrowser(CString rev, CWnd* pParent /*=NULL*/)
138 : CResizableStandAloneDialog(CRepositoryBrowser::IDD, pParent)
139 , m_currSortCol(0)
140 , m_currSortDesc(false)
141 , m_sRevision(rev)
142 , m_bHasWC(true)
143 , m_ColumnManager(&m_RepoList)
144 , m_nIconFolder(0)
145 , m_nOpenIconFolder(0)
146 , m_nExternalOvl(0)
147 , m_nExecutableOvl(0)
148 , m_nSymlinkOvl(0)
149 , bDragMode(false)
150 , oldy(0)
151 , oldx(0)
155 CRepositoryBrowser::~CRepositoryBrowser()
159 void CRepositoryBrowser::DoDataExchange(CDataExchange* pDX)
161 CDialog::DoDataExchange(pDX);
162 DDX_Control(pDX, IDC_REPOTREE, m_RepoTree);
163 DDX_Control(pDX, IDC_REPOLIST, m_RepoList);
167 BEGIN_MESSAGE_MAP(CRepositoryBrowser, CResizableStandAloneDialog)
168 ON_NOTIFY(TVN_SELCHANGED, IDC_REPOTREE, &CRepositoryBrowser::OnTvnSelchangedRepoTree)
169 ON_NOTIFY(TVN_ITEMEXPANDING, IDC_REPOTREE, &CRepositoryBrowser::OnTvnItemExpandingRepoTree)
170 ON_WM_CONTEXTMENU()
171 ON_NOTIFY(LVN_COLUMNCLICK, IDC_REPOLIST, &CRepositoryBrowser::OnLvnColumnclickRepoList)
172 ON_NOTIFY(LVN_ITEMCHANGED, IDC_REPOLIST, &CRepositoryBrowser::OnLvnItemchangedRepolist)
173 ON_NOTIFY(NM_DBLCLK, IDC_REPOLIST, &CRepositoryBrowser::OnNMDblclk_RepoList)
174 ON_BN_CLICKED(IDC_BUTTON_REVISION, &CRepositoryBrowser::OnBnClickedButtonRevision)
175 ON_WM_SETCURSOR()
176 ON_WM_DESTROY()
177 ON_WM_MOUSEMOVE()
178 ON_WM_LBUTTONDOWN()
179 ON_WM_LBUTTONUP()
180 END_MESSAGE_MAP()
183 // CRepositoryBrowser message handlers
185 BOOL CRepositoryBrowser::OnInitDialog()
187 CResizableStandAloneDialog::OnInitDialog();
188 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
190 AddAnchor(IDC_STATIC_REPOURL, TOP_LEFT);
191 AddAnchor(IDC_REPOBROWSER_URL, TOP_LEFT, TOP_RIGHT);
192 AddAnchor(IDC_STATIC_REF, TOP_RIGHT);
193 AddAnchor(IDC_BUTTON_REVISION, TOP_RIGHT);
194 AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);
195 AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);
196 AddAnchor(IDHELP, BOTTOM_RIGHT);
197 AddAnchor(IDC_INFOLABEL, BOTTOM_LEFT, BOTTOM_RIGHT);
198 AddAnchor(IDOK, BOTTOM_RIGHT);
199 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
201 CRepositoryBrowser::s_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
202 if (CRepositoryBrowser::s_bSortLogical)
203 CRepositoryBrowser::s_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
205 static UINT columnNames[] = { IDS_STATUSLIST_COLFILENAME, IDS_STATUSLIST_COLEXT, IDS_LOG_SIZE };
206 static int columnWidths[] = { 150, 100, 100 };
207 DWORD dwDefaultColumns = (1 << eCol_Name) | (1 << eCol_Extension) | (1 << eCol_FileSize);
208 m_ColumnManager.SetNames(columnNames, _countof(columnNames));
209 m_ColumnManager.ReadSettings(dwDefaultColumns, 0, _T("RepoBrowser"), _countof(columnNames), columnWidths);
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 if (SysInfo::Instance().IsVistaOrLater())
225 DWORD exStyle = TVS_EX_FADEINOUTEXPANDOS | TVS_EX_AUTOHSCROLL | TVS_EX_DOUBLEBUFFER;
226 m_RepoTree.SetExtendedStyle(exStyle, exStyle);
229 m_nExternalOvl = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXTERNALOVL), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
230 m_nExecutableOvl = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EXECUTABLEOVL), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
231 m_nSymlinkOvl = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SYMLINKOVL), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
232 // set externaloverlay in SYS_IMAGE_LIST() in Refresh method, so that it is updated after every launch of the logdialog
234 SetWindowTheme(m_RepoTree.GetSafeHwnd(), L"Explorer", NULL);
235 SetWindowTheme(m_RepoList.GetSafeHwnd(), L"Explorer", NULL);
237 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
238 m_nOpenIconFolder = SYS_IMAGE_LIST().GetDirOpenIconIndex();
240 EnableSaveRestore(L"Reposbrowser");
242 DWORD xPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"), 0);
243 if (xPos == 0)
245 RECT rc;
246 GetDlgItem(IDC_REPOTREE)->GetClientRect(&rc);
247 xPos = rc.right - rc.left;
249 HandleDividerMove(CPoint(xPos + 20, 10), false);
251 CString sWindowTitle;
252 GetWindowText(sWindowTitle);
253 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
255 m_bHasWC = !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
257 Refresh();
259 m_RepoList.SetFocus();
261 return FALSE;
264 void CRepositoryBrowser::OnDestroy()
266 int maxcol = m_ColumnManager.GetColumnCount();
267 for (int col = 0; col < maxcol; ++col)
268 if (m_ColumnManager.IsVisible(col))
269 m_ColumnManager.ColumnResized(col);
270 m_ColumnManager.WriteSettings();
272 CResizableStandAloneDialog::OnDestroy();
275 void CRepositoryBrowser::OnOK()
277 if (GetFocus() == &m_RepoList && (GetKeyState(VK_MENU) & 0x8000) == 0)
279 // list control has focus: 'enter' the folder
280 if (m_RepoList.GetSelectedCount() != 1)
281 return;
283 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
284 if (pos)
286 CShadowFilesTree *item = (CShadowFilesTree *)m_RepoList.GetItemData(m_RepoList.GetNextSelectedItem(pos));
287 if (item->m_bFolder)
289 FillListCtrlForShadowTree(item);
290 m_RepoTree.SelectItem(item->m_hTree);
292 else
293 OpenFile(item->GetFullName(), OPEN, item->m_bSubmodule, item->m_hash);
295 return;
298 SaveDividerPosition();
299 CResizableStandAloneDialog::OnOK();
302 void CRepositoryBrowser::OnCancel()
304 SaveDividerPosition();
305 CResizableStandAloneDialog::OnCancel();
308 void CRepositoryBrowser::OnNMDblclk_RepoList(NMHDR *pNMHDR, LRESULT *pResult)
310 *pResult = 0;
312 LPNMITEMACTIVATE pNmItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
313 if (pNmItemActivate->iItem < 0)
314 return;
316 CShadowFilesTree * pItem = (CShadowFilesTree *)m_RepoList.GetItemData(pNmItemActivate->iItem);
317 if (pItem == NULL)
318 return;
320 if (!pItem->m_bFolder)
322 OpenFile(pItem->GetFullName(), OPEN, pItem->m_bSubmodule, pItem->m_hash);
323 return;
325 else
327 FillListCtrlForShadowTree(pItem);
328 m_RepoTree.SelectItem(pItem->m_hTree);
332 void CRepositoryBrowser::Refresh()
334 BeginWaitCursor();
335 if (m_nExternalOvl >= 0)
336 SYS_IMAGE_LIST().SetOverlayImage(m_nExternalOvl, OVERLAY_EXTERNAL);
337 if (m_nExecutableOvl >= 0)
338 SYS_IMAGE_LIST().SetOverlayImage(m_nExecutableOvl, OVERLAY_EXECUTABLE);
339 if (m_nSymlinkOvl >= 0)
340 SYS_IMAGE_LIST().SetOverlayImage(m_nSymlinkOvl, OVERLAY_SYMLINK);
342 m_RepoTree.DeleteAllItems();
343 m_RepoList.DeleteAllItems();
344 m_TreeRoot.m_ShadowTree.clear();
345 m_TreeRoot.m_sName = "";
346 m_TreeRoot.m_bFolder = true;
348 TVINSERTSTRUCT tvinsert = {0};
349 tvinsert.hParent = TVI_ROOT;
350 tvinsert.hInsertAfter = TVI_ROOT;
351 tvinsert.itemex.mask = TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
352 tvinsert.itemex.pszText = L"/";
353 tvinsert.itemex.lParam = (LPARAM)&m_TreeRoot;
354 tvinsert.itemex.iImage = m_nIconFolder;
355 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;
356 m_TreeRoot.m_hTree= m_RepoTree.InsertItem(&tvinsert);
358 ReadTree(&m_TreeRoot);
359 m_RepoTree.Expand(m_TreeRoot.m_hTree, TVE_EXPAND);
360 FillListCtrlForShadowTree(&m_TreeRoot);
361 m_RepoTree.SelectItem(m_TreeRoot.m_hTree);
362 EndWaitCursor();
365 int CRepositoryBrowser::ReadTreeRecursive(git_repository &repo, const git_tree * tree, CShadowFilesTree * treeroot)
367 size_t count = git_tree_entrycount(tree);
368 bool hasSubfolders = false;
370 for (size_t i = 0; i < count; ++i)
372 const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
373 if (entry == NULL)
374 continue;
376 const int mode = git_tree_entry_filemode(entry);
378 CString base = CUnicodeUtils::GetUnicode(git_tree_entry_name(entry), CP_UTF8);
380 const git_oid *oid = git_tree_entry_id(entry);
381 CShadowFilesTree * pNextTree = &treeroot->m_ShadowTree[base];
382 pNextTree->m_sName = base;
383 pNextTree->m_pParent = treeroot;
384 pNextTree->m_hash = CGitHash((char *)oid->id);
386 if (mode == GIT_FILEMODE_COMMIT)
387 pNextTree->m_bSubmodule = true;
388 else if (mode & S_IFDIR)
390 hasSubfolders = true;
391 pNextTree->m_bFolder = true;
392 pNextTree->m_bLoaded = false;
394 TVINSERTSTRUCT tvinsert = {0};
395 tvinsert.hParent = treeroot->m_hTree;
396 tvinsert.hInsertAfter = TVI_SORT;
397 tvinsert.itemex.mask = TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_CHILDREN;
398 tvinsert.itemex.pszText = base.GetBuffer(base.GetLength());
399 tvinsert.itemex.cChildren = 1;
400 tvinsert.itemex.lParam = (LPARAM)pNextTree;
401 tvinsert.itemex.iImage = m_nIconFolder;
402 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;
403 pNextTree->m_hTree = m_RepoTree.InsertItem(&tvinsert);
404 base.ReleaseBuffer();
406 else
408 if (mode == GIT_FILEMODE_BLOB_EXECUTABLE)
409 pNextTree->m_bExecutable = true;
410 if (mode == GIT_FILEMODE_LINK)
411 pNextTree->m_bSymlink = true;
412 CAutoBlob blob;
413 git_blob_lookup(blob.GetPointer(), &repo, oid);
414 if (!blob)
415 continue;
417 pNextTree->m_iSize = git_blob_rawsize(blob);
421 if (!hasSubfolders)
423 TVITEM tvitem = { 0 };
424 tvitem.hItem = treeroot->m_hTree;
425 tvitem.mask = TVIF_CHILDREN;
426 tvitem.cChildren = 0;
427 m_RepoTree.SetItem(&tvitem);
430 return 0;
433 int CRepositoryBrowser::ReadTree(CShadowFilesTree * treeroot, const CString& root)
435 CWaitCursor wait;
436 CAutoRepository repository(g_Git.GetGitRepository());
437 if (!repository)
439 MessageBox(CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR);
440 return -1;
443 if (m_sRevision == _T("HEAD"))
445 int ret = git_repository_head_unborn(repository);
446 if (ret == 1) // is orphan
447 return ret;
448 else if (ret != 0)
450 MessageBox(g_Git.GetLibGit2LastErr(_T("Could not check HEAD.")), _T("TortoiseGit"), MB_ICONERROR);
451 return ret;
455 CGitHash hash;
456 if (CGit::GetHash(repository, hash, m_sRevision))
458 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
459 return -1;
462 CAutoCommit commit;
463 if (git_commit_lookup(commit.GetPointer(), repository, (git_oid *)hash.m_hash))
465 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup commit.")), _T("TortoiseGit"), MB_ICONERROR);
466 return -1;
469 CAutoTree tree;
470 if (git_commit_tree(tree.GetPointer(), commit))
472 MessageBox(CGit::GetLibGit2LastErr(_T("Could not get tree of commit.")), _T("TortoiseGit"), MB_ICONERROR);
473 return -1;
476 if (!root.IsEmpty())
478 CAutoTreeEntry treeEntry;
479 if (git_tree_entry_bypath(treeEntry.GetPointer(), tree, CUnicodeUtils::GetUTF8(root)))
481 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
482 return -1;
484 if (git_tree_entry_type(treeEntry) != GIT_OBJ_TREE)
486 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
487 return -1;
490 CAutoObject object;
491 if (git_tree_entry_to_object(object.GetPointer(), repository, treeEntry))
493 MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
494 return -1;
497 tree = (git_tree*)object.Detach();
500 treeroot->m_hash = CGitHash((char *)git_tree_id(tree)->id);
501 ReadTreeRecursive(*repository, tree, treeroot);
503 // try to resolve hash to a branch name
504 if (m_sRevision == hash.ToString())
506 MAP_HASH_NAME map;
507 if (CGit::GetMapHashToFriendName(repository, map))
508 MessageBox(g_Git.GetLibGit2LastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
509 if (!map[hash].empty())
510 m_sRevision = map[hash].at(0);
512 this->GetDlgItem(IDC_BUTTON_REVISION)->SetWindowText(m_sRevision);
514 return 0;
517 void CRepositoryBrowser::OnTvnSelchangedRepoTree(NMHDR *pNMHDR, LRESULT *pResult)
519 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
520 *pResult = 0;
522 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
525 void CRepositoryBrowser::OnTvnItemExpandingRepoTree(NMHDR *pNMHDR, LRESULT *pResult)
527 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
528 *pResult = 0;
530 CShadowFilesTree* pTree = (CShadowFilesTree*)(m_RepoTree.GetItemData(pNMTreeView->itemNew.hItem));
531 if (pTree == NULL)
533 ASSERT(FALSE);
534 return;
537 if (!pTree->m_bLoaded)
539 pTree->m_bLoaded = true;
540 ReadTree(pTree, pTree->GetFullName());
544 void CRepositoryBrowser::FillListCtrlForTreeNode(HTREEITEM treeNode)
546 m_RepoList.DeleteAllItems();
548 CShadowFilesTree* pTree = (CShadowFilesTree*)(m_RepoTree.GetItemData(treeNode));
549 if (pTree == NULL)
551 ASSERT(FALSE);
552 return;
555 CString url = _T("/") + pTree->GetFullName();
556 GetDlgItem(IDC_REPOBROWSER_URL)->SetWindowText(url);
558 if (!pTree->m_bLoaded)
560 pTree->m_bLoaded = true;
561 ReadTree(pTree, pTree->GetFullName());
564 FillListCtrlForShadowTree(pTree);
567 void CRepositoryBrowser::FillListCtrlForShadowTree(CShadowFilesTree* pTree)
569 for (TShadowFilesTreeMap::iterator itShadowTree = pTree->m_ShadowTree.begin(); itShadowTree != pTree->m_ShadowTree.end(); ++itShadowTree)
571 int icon = m_nIconFolder;
572 if (!(*itShadowTree).second.m_bFolder && !(*itShadowTree).second.m_bSubmodule)
574 icon = SYS_IMAGE_LIST().GetFileIconIndex((*itShadowTree).second.m_sName);
577 int indexItem = m_RepoList.InsertItem(m_RepoList.GetItemCount(), (*itShadowTree).second.m_sName, icon);
579 if ((*itShadowTree).second.m_bSubmodule)
581 m_RepoList.SetItemState(indexItem, INDEXTOOVERLAYMASK(OVERLAY_EXTERNAL), LVIS_OVERLAYMASK);
583 if ((*itShadowTree).second.m_bExecutable)
584 m_RepoList.SetItemState(indexItem, INDEXTOOVERLAYMASK(OVERLAY_EXECUTABLE), LVIS_OVERLAYMASK);
585 if ((*itShadowTree).second.m_bSymlink)
586 m_RepoList.SetItemState(indexItem, INDEXTOOVERLAYMASK(OVERLAY_SYMLINK), LVIS_OVERLAYMASK);
587 m_RepoList.SetItemData(indexItem, (DWORD_PTR)&(*itShadowTree).second);
588 if (!(*itShadowTree).second.m_bFolder && !(*itShadowTree).second.m_bSubmodule)
590 CString temp;
592 temp = CPathUtils::GetFileExtFromPath((*itShadowTree).second.m_sName);
593 m_RepoList.SetItemText(indexItem, eCol_Extension, temp);
595 StrFormatByteSize64((*itShadowTree).second.m_iSize, temp.GetBuffer(20), 20);
596 temp.ReleaseBuffer();
597 m_RepoList.SetItemText(indexItem, eCol_FileSize, temp);
601 CRepoListCompareFunc compareFunc(&m_RepoList, m_currSortCol, m_currSortDesc);
602 m_RepoList.SortItemsEx(&CRepoListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
604 SetSortArrowA(&m_RepoList, m_currSortCol, !m_currSortDesc);
606 UpdateInfoLabel();
609 void CRepositoryBrowser::UpdateInfoLabel()
611 CString temp;
612 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
613 if (pos)
615 if (m_RepoList.GetSelectedCount() > 1)
617 temp.FormatMessage(IDS_REPOBROWSE_INFOMULTI, m_RepoList.GetSelectedCount());
619 else
621 int index = m_RepoList.GetNextSelectedItem(pos);
622 CShadowFilesTree *item = (CShadowFilesTree *)m_RepoList.GetItemData(index);
623 if (item->m_bSubmodule)
624 temp.FormatMessage(IDS_REPOBROWSE_INFOEXT, (LPCTSTR)m_RepoList.GetItemText(index, eCol_Name), item->m_hash.ToString());
625 else if (item->m_bFolder)
626 temp.FormatMessage(IDS_REPOBROWSE_INFODIR, (LPCTSTR)m_RepoList.GetItemText(index, eCol_Name));
627 else
628 temp.FormatMessage(IDS_REPOBROWSE_INFOFILE, (LPCTSTR)m_RepoList.GetItemText(index, eCol_Name), (LPCTSTR)m_RepoList.GetItemText(index, eCol_FileSize));
631 else
633 HTREEITEM hTreeItem = m_RepoTree.GetSelectedItem();
634 if (hTreeItem != nullptr)
636 CShadowFilesTree* pTree = (CShadowFilesTree*)m_RepoTree.GetItemData(hTreeItem);
637 if (pTree != nullptr)
639 size_t files = 0, submodules = 0;
640 for (TShadowFilesTreeMap::iterator itShadowTree = pTree->m_ShadowTree.begin(); itShadowTree != pTree->m_ShadowTree.end(); ++itShadowTree)
642 if (!(*itShadowTree).second.m_bFolder && !(*itShadowTree).second.m_bSubmodule)
643 ++files;
644 if ((*itShadowTree).second.m_bSubmodule)
645 ++submodules;
647 temp.FormatMessage(IDS_REPOBROWSE_INFO, (LPCTSTR)pTree->m_sName, files, submodules, pTree->m_ShadowTree.size() - files - submodules, pTree->m_ShadowTree.size());
651 SetDlgItemText(IDC_INFOLABEL, temp);
654 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR * /* pNMHDR */, LRESULT *pResult)
656 *pResult = 0;
657 UpdateInfoLabel();
660 void CRepositoryBrowser::OnContextMenu(CWnd* pWndFrom, CPoint point)
662 if (pWndFrom == &m_RepoList)
664 CRect headerPosition;
665 m_RepoList.GetHeaderCtrl()->GetWindowRect(headerPosition);
666 if (!headerPosition.PtInRect(point))
667 OnContextMenu_RepoList(point);
669 else if (pWndFrom == &m_RepoTree)
670 OnContextMenu_RepoTree(point);
673 void CRepositoryBrowser::OnContextMenu_RepoTree(CPoint point)
675 CPoint clientPoint = point;
676 m_RepoTree.ScreenToClient(&clientPoint);
678 HTREEITEM hTreeItem = m_RepoTree.HitTest(clientPoint);
679 if (hTreeItem == NULL)
680 return;
682 TShadowFilesTreeList selectedLeafs;
683 selectedLeafs.push_back((CShadowFilesTree *)m_RepoTree.GetItemData(hTreeItem));
685 ShowContextMenu(point, selectedLeafs, ONLY_FOLDERS);
688 void CRepositoryBrowser::OnContextMenu_RepoList(CPoint point)
690 TShadowFilesTreeList selectedLeafs;
691 selectedLeafs.reserve(m_RepoList.GetSelectedCount());
693 bool folderSelected = false;
694 bool filesSelected = false;
695 bool submodulesSelected = false;
697 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
698 while (pos)
700 CShadowFilesTree * item = (CShadowFilesTree *)m_RepoList.GetItemData(m_RepoList.GetNextSelectedItem(pos));
701 if (item->m_bSubmodule)
702 submodulesSelected = true;
703 if (item->m_bFolder)
704 folderSelected = true;
705 else
706 filesSelected = true;
707 selectedLeafs.push_back(item);
710 eSelectionType selType = ONLY_FILES;
711 if (folderSelected && filesSelected)
712 selType = MIXED_FOLDERS_FILES;
713 else if (folderSelected)
714 selType = ONLY_FOLDERS;
715 else if (submodulesSelected)
716 selType = ONLY_FILESSUBMODULES;
717 ShowContextMenu(point, selectedLeafs, selType);
720 void CRepositoryBrowser::ShowContextMenu(CPoint point, TShadowFilesTreeList &selectedLeafs, eSelectionType selType)
722 CIconMenu popupMenu;
723 popupMenu.CreatePopupMenu();
725 bool bAddSeparator = false;
727 if (selectedLeafs.size() == 1)
729 popupMenu.AppendMenuIcon(eCmd_Open, IDS_REPOBROWSE_OPEN, IDI_OPEN);
730 popupMenu.SetDefaultItem(eCmd_Open, FALSE);
731 if (selType == ONLY_FILES || selType == ONLY_FILESSUBMODULES)
733 popupMenu.AppendMenuIcon(eCmd_OpenWith, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
734 popupMenu.AppendMenuIcon(eCmd_OpenWithAlternativeEditor, IDS_LOG_POPUP_VIEWREV);
737 popupMenu.AppendMenu(MF_SEPARATOR);
739 if (m_bHasWC && (selType == ONLY_FILES || selType == ONLY_FILESSUBMODULES))
741 popupMenu.AppendMenuIcon(eCmd_CompareWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
742 bAddSeparator = true;
745 if (bAddSeparator)
746 popupMenu.AppendMenu(MF_SEPARATOR);
747 bAddSeparator = false;
749 CString temp;
750 temp.LoadString(IDS_MENULOG);
751 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
752 if (selectedLeafs[0]->m_bSubmodule)
754 temp.LoadString(IDS_MENULOGSUBMODULE);
755 popupMenu.AppendMenuIcon(eCmd_ViewLogSubmodule, temp, IDI_LOG);
758 if (selType == ONLY_FILES)
760 if (m_bHasWC)
761 popupMenu.AppendMenuIcon(eCmd_Blame, IDS_LOG_POPUP_BLAME, IDI_BLAME);
763 popupMenu.AppendMenu(MF_SEPARATOR);
764 temp.LoadString(IDS_LOG_POPUP_SAVE);
765 popupMenu.AppendMenuIcon(eCmd_SaveAs, temp, IDI_SAVEAS);
768 bAddSeparator = true;
771 if (!selectedLeafs.empty() && selType == ONLY_FILES && m_bHasWC)
773 popupMenu.AppendMenuIcon(eCmd_Revert, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
774 bAddSeparator = true;
777 if (bAddSeparator)
778 popupMenu.AppendMenu(MF_SEPARATOR);
779 bAddSeparator = false;
781 if (!selectedLeafs.empty())
783 popupMenu.AppendMenuIcon(eCmd_CopyPath, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
784 popupMenu.AppendMenuIcon(eCmd_CopyHash, IDS_COPY_COMMIT_HASH, IDI_COPYCLIP);
787 eCmd cmd = (eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
788 switch(cmd)
790 case eCmd_ViewLog:
791 case eCmd_ViewLogSubmodule:
793 CString sCmd;
794 sCmd.Format(_T("/command:log /path:\"%s\\%s\""), g_Git.m_CurrentDir, selectedLeafs.at(0)->GetFullName());
795 if (cmd == eCmd_ViewLog && selectedLeafs.at(0)->m_bSubmodule)
796 sCmd += _T(" /submodule");
797 CAppUtils::RunTortoiseGitProc(sCmd);
799 break;
800 case eCmd_Blame:
802 CAppUtils::LaunchTortoiseBlame(g_Git.CombinePath(selectedLeafs.at(0)->GetFullName()), m_sRevision);
804 break;
805 case eCmd_Open:
806 if (!selectedLeafs.at(0)->m_bSubmodule && selectedLeafs.at(0)->m_bFolder)
808 FillListCtrlForTreeNode(selectedLeafs.at(0)->m_hTree);
809 m_RepoTree.SelectItem(selectedLeafs.at(0)->m_hTree);
810 return;
812 OpenFile(selectedLeafs.at(0)->GetFullName(), OPEN, selectedLeafs.at(0)->m_bSubmodule, selectedLeafs.at(0)->m_hash);
813 break;
814 case eCmd_OpenWith:
815 OpenFile(selectedLeafs.at(0)->GetFullName(), OPEN_WITH, selectedLeafs.at(0)->m_bSubmodule, selectedLeafs.at(0)->m_hash);
816 break;
817 case eCmd_OpenWithAlternativeEditor:
818 OpenFile(selectedLeafs.at(0)->GetFullName(), ALTERNATIVEEDITOR, selectedLeafs.at(0)->m_bSubmodule, selectedLeafs.at(0)->m_hash);
819 break;
820 case eCmd_CompareWC:
822 CTGitPath file(selectedLeafs.at(0)->GetFullName());
823 CGitDiff::Diff(&file, &file, GIT_REV_ZERO, m_sRevision);
825 break;
826 case eCmd_Revert:
828 int count = 0;
829 for (TShadowFilesTreeList::iterator itShadowTree = selectedLeafs.begin(); itShadowTree != selectedLeafs.end(); ++itShadowTree)
831 if (RevertItemToVersion((*itShadowTree)->GetFullName()))
832 ++count;
833 else
834 break;
836 CString msg;
837 msg.Format(IDS_STATUSLIST_FILESREVERTED, count, m_sRevision);
838 MessageBox(msg, _T("TortoiseGit"), MB_OK);
840 break;
841 case eCmd_SaveAs:
842 FileSaveAs(selectedLeafs.at(0)->GetFullName());
843 break;
844 case eCmd_CopyPath:
846 CString sClipboard;
847 for (TShadowFilesTreeList::iterator itShadowTree = selectedLeafs.begin(); itShadowTree != selectedLeafs.end(); ++itShadowTree)
849 sClipboard += (*itShadowTree)->m_sName + _T("\r\n");
851 CStringUtils::WriteAsciiStringToClipboard(sClipboard);
853 break;
854 case eCmd_CopyHash:
856 CopyHashToClipboard(selectedLeafs);
858 break;
862 BOOL CRepositoryBrowser::PreTranslateMessage(MSG* pMsg)
864 if (pMsg->message == WM_KEYDOWN)
866 switch (pMsg->wParam)
868 case VK_F5:
870 Refresh();
872 break;
876 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
879 void CRepositoryBrowser::OnLvnColumnclickRepoList(NMHDR *pNMHDR, LRESULT *pResult)
881 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
882 if (pResult)
883 *pResult = 0;
885 if (m_currSortCol == pNMLV->iSubItem)
886 m_currSortDesc = !m_currSortDesc;
887 else
889 m_currSortCol = pNMLV->iSubItem;
890 m_currSortDesc = false;
893 CRepoListCompareFunc compareFunc(&m_RepoList, m_currSortCol, m_currSortDesc);
894 m_RepoList.SortItemsEx(&CRepoListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
896 SetSortArrowA(&m_RepoList, m_currSortCol, !m_currSortDesc);
899 void CRepositoryBrowser::OnBnClickedButtonRevision()
901 // use the git log to allow selection of a version
902 CLogDlg dlg;
903 dlg.SetParams(CTGitPath(), CTGitPath(), m_sRevision, m_sRevision, 0);
904 // tell the dialog to use mode for selecting revisions
905 dlg.SetSelect(true);
906 // only one revision must be selected however
907 dlg.SingleSelection(true);
908 if (dlg.DoModal() == IDOK)
910 // get selected hash if any
911 m_sRevision = dlg.GetSelectedHash();
912 Refresh();
916 void CRepositoryBrowser::SaveDividerPosition()
918 RECT rc;
919 GetDlgItem(IDC_REPOTREE)->GetClientRect(&rc);
920 CRegDWORD xPos(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"));
921 xPos = rc.right - rc.left;
924 void CRepositoryBrowser::HandleDividerMove(CPoint point, bool bDraw)
926 RECT rect, tree, list, treelist, treelistclient;
928 // create an union of the tree and list control rectangle
929 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
930 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
931 UnionRect(&treelist, &tree, &list);
932 treelistclient = treelist;
933 ScreenToClient(&treelistclient);
935 ClientToScreen(&point);
936 GetClientRect(&rect);
937 ClientToScreen(&rect);
939 CPoint point2 = point;
940 if (point2.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
941 point2.x = treelist.left + REPOBROWSER_CTRL_MIN_WIDTH;
942 if (point2.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
943 point2.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
945 point.x -= rect.left;
946 point.y -= treelist.top;
948 OffsetRect(&treelist, -treelist.left, -treelist.top);
950 if (point.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)
951 point.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;
952 if (point.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH)
953 point.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;
955 if (bDraw)
957 CDC * pDC = GetDC();
958 DrawXorBar(pDC, oldx + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
959 ReleaseDC(pDC);
962 oldx = point.x;
963 oldy = point.y;
965 //position the child controls
966 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&treelist);
967 treelist.right = point2.x - 2;
968 ScreenToClient(&treelist);
969 RemoveAnchor(IDC_REPOTREE);
970 GetDlgItem(IDC_REPOTREE)->MoveWindow(&treelist);
971 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&treelist);
972 treelist.left = point2.x + 2;
973 ScreenToClient(&treelist);
974 RemoveAnchor(IDC_REPOLIST);
975 GetDlgItem(IDC_REPOLIST)->MoveWindow(&treelist);
977 AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);
978 AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);
981 void CRepositoryBrowser::OnMouseMove(UINT nFlags, CPoint point)
983 if (bDragMode == FALSE)
984 return;
986 RECT rect, tree, list, treelist, treelistclient;
987 // create an union of the tree and list control rectangle
988 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
989 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
990 UnionRect(&treelist, &tree, &list);
991 treelistclient = treelist;
992 ScreenToClient(&treelistclient);
994 //convert the mouse coordinates relative to the top-left of
995 //the window
996 ClientToScreen(&point);
997 GetClientRect(&rect);
998 ClientToScreen(&rect);
999 point.x -= rect.left;
1000 point.y -= treelist.top;
1002 //same for the window coordinates - make them relative to 0,0
1003 OffsetRect(&treelist, -treelist.left, -treelist.top);
1005 if (point.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
1006 point.x = treelist.left + REPOBROWSER_CTRL_MIN_WIDTH;
1007 if (point.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
1008 point.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
1010 if ((nFlags & MK_LBUTTON) && (point.x != oldx))
1012 CDC * pDC = GetDC();
1014 if (pDC)
1016 DrawXorBar(pDC, oldx + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
1017 DrawXorBar(pDC, point.x + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
1019 ReleaseDC(pDC);
1022 oldx = point.x;
1023 oldy = point.y;
1026 CStandAloneDialogTmpl<CResizableDialog>::OnMouseMove(nFlags, point);
1029 void CRepositoryBrowser::OnLButtonDown(UINT nFlags, CPoint point)
1031 RECT rect, tree, list, treelist, treelistclient;
1033 // create an union of the tree and list control rectangle
1034 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
1035 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
1036 UnionRect(&treelist, &tree, &list);
1037 treelistclient = treelist;
1038 ScreenToClient(&treelistclient);
1040 //convert the mouse coordinates relative to the top-left of
1041 //the window
1042 ClientToScreen(&point);
1043 GetClientRect(&rect);
1044 ClientToScreen(&rect);
1045 point.x -= rect.left;
1046 point.y -= treelist.top;
1048 //same for the window coordinates - make them relative to 0,0
1049 OffsetRect(&treelist, -treelist.left, -treelist.top);
1051 if (point.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
1052 return CStandAloneDialogTmpl < CResizableDialog>::OnLButtonDown(nFlags, point);
1053 if (point.x > treelist.right - 3)
1054 return CStandAloneDialogTmpl < CResizableDialog>::OnLButtonDown(nFlags, point);
1055 if (point.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
1056 point.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
1058 if ((point.y < treelist.top + 3) || (point.y > treelist.bottom - 3))
1059 return CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
1061 bDragMode = true;
1063 SetCapture();
1065 CDC * pDC = GetDC();
1066 DrawXorBar(pDC, point.x + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
1067 ReleaseDC(pDC);
1069 oldx = point.x;
1070 oldy = point.y;
1072 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
1075 void CRepositoryBrowser::OnLButtonUp(UINT nFlags, CPoint point)
1077 if (bDragMode == FALSE)
1078 return;
1080 HandleDividerMove(point, true);
1082 bDragMode = false;
1083 ReleaseCapture();
1085 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonUp(nFlags, point);
1088 void CRepositoryBrowser::OnCaptureChanged(CWnd *pWnd)
1090 bDragMode = false;
1092 __super::OnCaptureChanged(pWnd);
1095 void CRepositoryBrowser::DrawXorBar(CDC * pDC, int x1, int y1, int width, int height)
1097 static WORD _dotPatternBmp[8] =
1099 0x0055, 0x00aa, 0x0055, 0x00aa,
1100 0x0055, 0x00aa, 0x0055, 0x00aa
1103 HBITMAP hbm;
1104 HBRUSH hbr, hbrushOld;
1106 hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
1107 hbr = CreatePatternBrush(hbm);
1109 pDC->SetBrushOrg(x1, y1);
1110 hbrushOld = (HBRUSH)pDC->SelectObject(hbr);
1112 PatBlt(pDC->GetSafeHdc(), x1, y1, width, height, PATINVERT);
1114 pDC->SelectObject(hbrushOld);
1116 DeleteObject(hbr);
1117 DeleteObject(hbm);
1120 BOOL CRepositoryBrowser::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1122 if (pWnd == this)
1124 RECT rect;
1125 POINT pt;
1126 GetClientRect(&rect);
1127 GetCursorPos(&pt);
1128 ScreenToClient(&pt);
1129 if (PtInRect(&rect, pt))
1131 ClientToScreen(&pt);
1132 // are we right of the tree control?
1133 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&rect);
1134 if ((pt.x > rect.right) && (pt.y >= rect.top + 3) && (pt.y <= rect.bottom - 3))
1136 // but left of the list control?
1137 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&rect);
1138 if (pt.x < rect.left)
1140 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE));
1141 SetCursor(hCur);
1142 return TRUE;
1147 return CStandAloneDialogTmpl<CResizableDialog>::OnSetCursor(pWnd, nHitTest, message);
1150 void CRepositoryBrowser::FileSaveAs(const CString path)
1152 CTGitPath gitPath(path);
1154 CGitHash hash;
1155 if (g_Git.GetHash(hash, m_sRevision))
1157 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
1158 return;
1161 CString filename;
1162 filename.Format(_T("%s-%s%s"), gitPath.GetBaseFilename(), hash.ToString().Left(g_Git.GetShortHASHLength()), gitPath.GetFileExtension());
1163 CFileDialog dlg(FALSE, NULL, filename, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL);
1165 CString currentpath(g_Git.CombinePath(gitPath.GetContainingDirectory()));
1166 dlg.m_ofn.lpstrInitialDir = currentpath.GetBuffer();
1168 CString cmd, out;
1169 INT_PTR ret = dlg.DoModal();
1170 SetCurrentDirectory(g_Git.m_CurrentDir);
1171 if (ret == IDOK)
1173 filename = dlg.GetPathName();
1174 if (g_Git.GetOneFile(m_sRevision, gitPath, filename))
1176 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, gitPath.GetGitPathString(), m_sRevision, filename);
1177 MessageBox(g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), MB_ICONERROR);
1178 return;
1183 void CRepositoryBrowser::OpenFile(const CString path, eOpenType mode, bool isSubmodule, CGitHash itemHash)
1185 CTGitPath gitPath(path);
1187 CString temppath;
1188 CString file;
1189 GetTempPath(temppath);
1190 CGitHash hash;
1191 if (g_Git.GetHash(hash, m_sRevision))
1193 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
1194 return;
1197 file.Format(_T("%s%s_%s%s"), temppath, gitPath.GetBaseFilename(), hash.ToString().Left(g_Git.GetShortHASHLength()), gitPath.GetFileExtension());
1199 if (isSubmodule)
1201 if (mode == OPEN && !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
1203 CTGitPath subPath = CTGitPath(g_Git.m_CurrentDir);
1204 subPath.AppendPathString(gitPath.GetWinPathString());
1205 CAutoRepository repo(subPath.GetGitPathString());
1206 CAutoCommit commit;
1207 if (!repo || git_commit_lookup(commit.GetPointer(), repo, (const git_oid *)itemHash.m_hash))
1209 CString out;
1210 out.Format(IDS_REPOBROWSEASKSUBMODULEUPDATE, itemHash.ToString(), gitPath.GetGitPathString());
1211 if (MessageBox(out, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) != IDYES)
1212 return;
1214 CString sCmd;
1215 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\" /selectedpath:\"%s\""), g_Git.m_CurrentDir, gitPath.GetGitPathString());
1216 CAppUtils::RunTortoiseGitProc(sCmd);
1217 return;
1220 CString cmd;
1221 cmd.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), g_Git.CombinePath(path), itemHash.ToString());
1222 CAppUtils::RunTortoiseGitProc(cmd);
1223 return;
1226 file += _T(".txt");
1227 CFile submoduleCommit(file, CFile::modeCreate | CFile::modeWrite);
1228 CStringA commitInfo = "Subproject commit " + CStringA(itemHash.ToString());
1229 submoduleCommit.Write(commitInfo, commitInfo.GetLength());
1231 else if (g_Git.GetOneFile(m_sRevision, gitPath, file))
1233 CString out;
1234 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, gitPath.GetGitPathString(), m_sRevision, file);
1235 MessageBox(g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), MB_ICONERROR);
1236 return;
1239 if (mode == ALTERNATIVEEDITOR)
1241 CAppUtils::LaunchAlternativeEditor(file);
1242 return;
1244 else if (mode == OPEN)
1246 CAppUtils::ShellOpen(file);
1247 return;
1250 CAppUtils::ShowOpenWithDialog(file);
1252 bool CRepositoryBrowser::RevertItemToVersion(const CString &path)
1254 CString cmd, out;
1255 cmd.Format(_T("git.exe checkout %s -- \"%s\""), m_sRevision, path);
1256 if (g_Git.Run(cmd, &out, CP_UTF8))
1258 if (MessageBox(out, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
1259 return false;
1262 return true;
1265 void CRepositoryBrowser::CopyHashToClipboard(TShadowFilesTreeList &selectedLeafs)
1267 if (!selectedLeafs.empty())
1269 CString sClipdata;
1270 bool first = true;
1271 for (size_t i = 0; i < selectedLeafs.size(); ++i)
1273 if (!first)
1274 sClipdata += _T("\r\n");
1275 sClipdata += selectedLeafs[i]->m_hash;
1276 first = false;
1278 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());