RepoBrowser: Allow to open files
[TortoiseGit.git] / src / TortoiseProc / RepositoryBrowser.cpp
blob78270751d3a86fe48daba54cadd052a05c0c43f3
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - TortoiseGit
4 // Copyright (C) 2012 Sven Strickroth <email@cs-ware.de>
5 // Copyright (C) 2003-2012 - TortoiseSVN
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 // RepositoryBrowser.cpp : implementation file
24 #include "stdafx.h"
25 #include "TortoiseProc.h"
26 #include "RepositoryBrowser.h"
27 #include "LogDlg.h"
28 #include "AppUtils.h"
29 #include "IconMenu.h"
30 #include "UnicodeUtils.h"
31 #include "SysImageList.h"
32 #include <sys/stat.h>
33 #include "SysInfo.h"
34 #include "registry.h"
35 #include "PathUtils.h"
36 #include "StringUtils.h"
38 void SetSortArrowA(CListCtrl * control, int nColumn, bool bAscending)
40 if (control == NULL)
41 return;
43 // set the sort arrow
44 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
45 HDITEM HeaderItem = {0};
46 HeaderItem.mask = HDI_FORMAT;
47 for (int i = 0; i < pHeader->GetItemCount(); ++i)
49 pHeader->GetItem(i, &HeaderItem);
50 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
51 pHeader->SetItem(i, &HeaderItem);
53 if (nColumn >= 0)
55 pHeader->GetItem(nColumn, &HeaderItem);
56 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
57 pHeader->SetItem(nColumn, &HeaderItem);
61 class CRepoListCompareFunc
63 public:
64 CRepoListCompareFunc(CListCtrl* pList, int col, bool desc)
65 : m_col(col)
66 , m_desc(desc)
67 , m_pList(pList)
70 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
72 return ((CRepoListCompareFunc *) lParamSort)->Compare(lParam1, lParam2);
75 int Compare(LPARAM lParam1, LPARAM lParam2)
77 CShadowFilesTree * pLeft = (CShadowFilesTree *)m_pList->GetItemData(lParam1);
78 CShadowFilesTree * pRight = (CShadowFilesTree *)m_pList->GetItemData(lParam2);
80 int result = 0;
81 switch(m_col)
83 case CRepositoryBrowser::eCol_Name:
84 result = SortStrCmp(pLeft->m_sName, pRight->m_sName);
85 if (result != 0)
86 break;
87 case CRepositoryBrowser::eCol_Extension:
88 result = m_pList->GetItemText(static_cast<int>(lParam1), 1).CompareNoCase(m_pList->GetItemText(static_cast<int>(lParam2), 1));
89 if (result == 0) // if extensions are the same, use the filename to sort
90 result = SortStrCmp(pRight->m_sName, pRight->m_sName);
91 if (result != 0)
92 break;
93 case CRepositoryBrowser::eCol_FileSize:
94 if (pLeft->m_iSize > pRight->m_iSize)
95 result = 1;
96 else if (pLeft->m_iSize < pRight->m_iSize)
97 result = -1;
98 else // fallback
99 result = SortStrCmp(pLeft->m_sName, pRight->m_sName);
102 if (m_desc)
103 result = -result;
105 if (pLeft->m_bFolder != pRight->m_bFolder)
107 if (pRight->m_bFolder)
108 result = 1;
109 else
110 result = -1;
113 return result;
115 int SortStrCmp(CString &left, CString &right)
117 if (CRepositoryBrowser::s_bSortLogical)
118 return StrCmpLogicalW(left, right);
119 return StrCmpI(left, right);
122 int m_col;
123 bool m_desc;
124 CListCtrl* m_pList;
127 // CRepositoryBrowser dialog
129 bool CRepositoryBrowser::s_bSortLogical = true;
131 IMPLEMENT_DYNAMIC(CRepositoryBrowser, CResizableStandAloneDialog)
133 CRepositoryBrowser::CRepositoryBrowser(CString rev, CWnd* pParent /*=NULL*/)
134 : CResizableStandAloneDialog(CRepositoryBrowser::IDD, pParent)
135 , m_currSortCol(0)
136 , m_currSortDesc(false)
137 , m_sRevision(rev)
141 CRepositoryBrowser::~CRepositoryBrowser()
145 void CRepositoryBrowser::DoDataExchange(CDataExchange* pDX)
147 CDialog::DoDataExchange(pDX);
148 DDX_Control(pDX, IDC_REPOTREE, m_RepoTree);
149 DDX_Control(pDX, IDC_REPOLIST, m_RepoList);
153 BEGIN_MESSAGE_MAP(CRepositoryBrowser, CResizableStandAloneDialog)
154 ON_NOTIFY(TVN_SELCHANGED, IDC_REPOTREE, &CRepositoryBrowser::OnTvnSelchangedRepoTree)
155 ON_WM_CONTEXTMENU()
156 ON_NOTIFY(LVN_COLUMNCLICK, IDC_REPOLIST, &CRepositoryBrowser::OnLvnColumnclickRepoList)
157 ON_NOTIFY(NM_DBLCLK, IDC_REPOLIST, &CRepositoryBrowser::OnNMDblclk_RepoList)
158 ON_BN_CLICKED(IDC_BUTTON_REVISION, &CRepositoryBrowser::OnBnClickedButtonRevision)
159 ON_WM_SETCURSOR()
160 ON_WM_MOUSEMOVE()
161 ON_WM_LBUTTONDOWN()
162 ON_WM_LBUTTONUP()
163 END_MESSAGE_MAP()
166 // CRepositoryBrowser message handlers
168 BOOL CRepositoryBrowser::OnInitDialog()
170 CResizableStandAloneDialog::OnInitDialog();
171 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
173 AddAnchor(IDC_STATIC, TOP_LEFT);
174 AddAnchor(IDC_BUTTON_REVISION, TOP_LEFT, TOP_RIGHT);
175 AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);
176 AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);
177 AddAnchor(IDHELP, BOTTOM_RIGHT);
178 AddAnchor(IDOK, BOTTOM_RIGHT);
179 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
181 CRepositoryBrowser::s_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
182 if (CRepositoryBrowser::s_bSortLogical)
183 CRepositoryBrowser::s_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
185 CString temp;
186 temp.LoadString(IDS_STATUSLIST_COLFILENAME);
187 m_RepoList.InsertColumn(eCol_Name, temp, 0, 150);
188 temp.LoadString(IDS_STATUSLIST_COLEXT);
189 m_RepoList.InsertColumn(eCol_Extension, temp, 0, 100);
190 temp.LoadString(IDS_LOG_SIZE);
191 m_RepoList.InsertColumn(eCol_FileSize, temp, 0, 100);
193 // set up the list control
194 // set the extended style of the list control
195 // 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.
196 CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);
197 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;
198 if (DWORD(regFullRowSelect))
199 exStyle |= LVS_EX_FULLROWSELECT;
200 m_RepoList.SetExtendedStyle(exStyle);
201 m_RepoList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
202 CAppUtils::SetListCtrlBackgroundImage(m_RepoList.GetSafeHwnd(), IDI_REPOBROWSER_BKG);
204 m_RepoTree.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL);
205 if (SysInfo::Instance().IsVistaOrLater())
207 DWORD exStyle = TVS_EX_FADEINOUTEXPANDOS | TVS_EX_AUTOHSCROLL | TVS_EX_DOUBLEBUFFER;
208 m_RepoTree.SetExtendedStyle(exStyle, exStyle);
211 SetWindowTheme(m_RepoTree.GetSafeHwnd(), L"Explorer", NULL);
212 SetWindowTheme(m_RepoList.GetSafeHwnd(), L"Explorer", NULL);
214 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
215 m_nOpenIconFolder = SYS_IMAGE_LIST().GetDirOpenIconIndex();
217 EnableSaveRestore(L"Reposbrowser");
219 DWORD xPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"), 0);
220 if (xPos == 0)
222 RECT rc;
223 GetDlgItem(IDC_REPOTREE)->GetClientRect(&rc);
224 xPos = rc.right - rc.left;
226 bDragMode = true;
227 HandleDividerMove(CPoint(xPos + 20, 10), false);
229 CString sWindowTitle;
230 GetWindowText(sWindowTitle);
231 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
233 Refresh();
235 m_RepoList.SetFocus();
237 return FALSE;
240 void CRepositoryBrowser::OnOK()
242 SaveDividerPosition();
243 CResizableStandAloneDialog::OnOK();
246 void CRepositoryBrowser::OnCancel()
248 SaveDividerPosition();
249 CResizableStandAloneDialog::OnCancel();
252 void CRepositoryBrowser::OnNMDblclk_RepoList(NMHDR *pNMHDR, LRESULT *pResult)
254 UNREFERENCED_PARAMETER(pNMHDR);
255 *pResult = 0;
257 LPNMITEMACTIVATE pNmItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
258 if (pNmItemActivate->iItem < 0)
259 return;
261 CShadowFilesTree * pItem = (CShadowFilesTree *)m_RepoList.GetItemData(pNmItemActivate->iItem);
262 if (pItem == NULL)
263 return;
265 if (!pItem->m_bFolder)
267 OpenFile(pItem->GetFullName(), OPEN);
268 return;
270 else
272 FillListCtrlForShadowTree(pItem);
273 m_RepoTree.SelectItem(pItem->m_hTree);
277 void CRepositoryBrowser::Refresh()
279 m_RepoTree.DeleteAllItems();
280 m_RepoList.DeleteAllItems();
281 m_TreeRoot.m_ShadowTree.clear();
282 m_TreeRoot.m_sName = "";
283 m_TreeRoot.m_bFolder = true;
285 TVINSERTSTRUCT tvinsert = {0};
286 tvinsert.hParent = TVI_ROOT;
287 tvinsert.hInsertAfter = TVI_ROOT;
288 tvinsert.itemex.mask = TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
289 tvinsert.itemex.pszText = L"/";
290 tvinsert.itemex.lParam = (LPARAM)&m_TreeRoot;
291 tvinsert.itemex.iImage = m_nIconFolder;
292 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;
293 m_TreeRoot.m_hTree= m_RepoTree.InsertItem(&tvinsert);
295 ReadTree(&m_TreeRoot);
296 m_RepoTree.Expand(m_TreeRoot.m_hTree, TVE_EXPAND);
297 FillListCtrlForShadowTree(&m_TreeRoot);
298 m_RepoTree.SelectItem(m_TreeRoot.m_hTree);
301 int CRepositoryBrowser::ReadTreeRecursive(git_repository &repo, git_tree * tree, CShadowFilesTree * treeroot)
303 size_t count = git_tree_entrycount(tree);
305 for (int i = 0; i < count; i++)
307 const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
308 if (entry == NULL)
309 continue;
310 int mode = git_tree_entry_attributes(entry);
312 CString base = CUnicodeUtils::GetUnicode(git_tree_entry_name(entry), CP_UTF8);
314 git_object *object = NULL;
315 git_tree_entry_2object(&object, &repo, entry);
316 if (object == NULL)
317 continue;
319 CShadowFilesTree * pNextTree = &treeroot->m_ShadowTree[base];
320 pNextTree->m_sName = base;
321 pNextTree->m_pParent = treeroot;
323 if (mode & S_IFDIR)
325 pNextTree->m_bFolder = true;
327 TVINSERTSTRUCT tvinsert = {0};
328 tvinsert.hParent = treeroot->m_hTree;
329 tvinsert.hInsertAfter = TVI_SORT;
330 tvinsert.itemex.mask = TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;
331 tvinsert.itemex.pszText = base.GetBuffer(base.GetLength());
332 tvinsert.itemex.lParam = (LPARAM)pNextTree;
333 tvinsert.itemex.iImage = m_nIconFolder;
334 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;
335 pNextTree->m_hTree = m_RepoTree.InsertItem(&tvinsert);
336 base.ReleaseBuffer();
338 ReadTreeRecursive(repo, (git_tree*)object, pNextTree);
340 else
342 const git_oid * oid = git_object_id(object);
344 git_blob * blob;
345 git_blob_lookup(&blob, &repo, oid);
346 if (blob == NULL)
347 continue;
349 pNextTree->m_iSize = git_blob_rawsize(blob);
350 git_blob_free(blob);
353 git_object_free(object);
356 return 0;
359 int CRepositoryBrowser::ReadTree(CShadowFilesTree * treeroot)
361 CStringA gitdir = CUnicodeUtils::GetMulti(g_Git.m_CurrentDir, CP_UTF8);
362 git_repository *repository = NULL;
363 git_commit *commit = NULL;
364 git_tree * tree = NULL;
365 int ret = 0;
368 ret = git_repository_open(&repository, gitdir.GetBuffer());
369 if(ret)
370 break;
372 CGitHash hash = g_Git.GetHash(m_sRevision);
373 ret = git_commit_lookup(&commit, repository, (git_oid *) hash.m_hash);
374 if(ret)
375 break;
377 ret = git_commit_tree(&tree, commit);
378 if(ret)
379 break;
381 ret = ReadTreeRecursive(*repository, tree, treeroot);
382 if(ret)
383 break;
385 this->GetDlgItem(IDC_BUTTON_REVISION)->SetWindowText(m_sRevision);
386 } while(0);
388 if (tree)
389 git_tree_free(tree);
391 if (commit)
392 git_commit_free(commit);
394 if (repository)
395 git_repository_free(repository);
397 return ret;
400 void CRepositoryBrowser::OnTvnSelchangedRepoTree(NMHDR *pNMHDR, LRESULT *pResult)
402 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
403 *pResult = 0;
405 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
408 void CRepositoryBrowser::FillListCtrlForTreeNode(HTREEITEM treeNode)
410 m_RepoList.DeleteAllItems();
412 CShadowFilesTree* pTree = (CShadowFilesTree*)(m_RepoTree.GetItemData(treeNode));
413 if (pTree == NULL)
415 ASSERT(FALSE);
416 return;
418 FillListCtrlForShadowTree(pTree);
421 void CRepositoryBrowser::FillListCtrlForShadowTree(CShadowFilesTree* pTree)
423 for (TShadowFilesTreeMap::iterator itShadowTree = pTree->m_ShadowTree.begin(); itShadowTree != pTree->m_ShadowTree.end(); ++itShadowTree)
425 int icon = m_nIconFolder;
426 if (!(*itShadowTree).second.m_bFolder)
427 icon = SYS_IMAGE_LIST().GetFileIconIndex((*itShadowTree).second.m_sName);
429 int indexItem = m_RepoList.InsertItem(m_RepoList.GetItemCount(), (*itShadowTree).second.m_sName, icon);
431 m_RepoList.SetItemData(indexItem, (DWORD_PTR)&(*itShadowTree).second);
432 if (!(*itShadowTree).second.m_bFolder)
434 CString temp;
436 temp = CPathUtils::GetFileExtFromPath((*itShadowTree).second.m_sName);
437 m_RepoList.SetItemText(indexItem, eCol_Extension, temp);
439 StrFormatByteSize((*itShadowTree).second.m_iSize, temp.GetBuffer(20), 20);
440 temp.ReleaseBuffer();
441 m_RepoList.SetItemText(indexItem, eCol_FileSize, temp);
445 CRepoListCompareFunc compareFunc(&m_RepoList, m_currSortCol, m_currSortDesc);
446 m_RepoList.SortItemsEx(&CRepoListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
448 SetSortArrowA(&m_RepoList, m_currSortCol, !m_currSortDesc);
451 void CRepositoryBrowser::OnContextMenu(CWnd* pWndFrom, CPoint point)
453 if (pWndFrom == &m_RepoList)
454 OnContextMenu_RepoList(point);
457 void CRepositoryBrowser::OnContextMenu_RepoList(CPoint point)
459 TShadowFilesTreeList selectedLeafs;
460 selectedLeafs.reserve(m_RepoList.GetSelectedCount());
461 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
462 while (pos)
464 selectedLeafs.push_back((CShadowFilesTree *)m_RepoList.GetItemData(m_RepoList.GetNextSelectedItem(pos)));
467 CIconMenu popupMenu;
468 popupMenu.CreatePopupMenu();
470 bool bAddSeparator = false;
472 if (selectedLeafs.size() == 1)
474 popupMenu.AppendMenuIcon(eCmd_Open, IDS_REPOBROWSE_OPEN, IDI_OPEN);
475 popupMenu.SetDefaultItem(eCmd_Open, FALSE);
476 if (!selectedLeafs.at(0)->m_bFolder)
478 popupMenu.AppendMenuIcon(eCmd_OpenWith, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
479 popupMenu.AppendMenuIcon(eCmd_OpenWithAlternativeEditor, IDS_LOG_POPUP_VIEWREV);
482 popupMenu.AppendMenu(MF_SEPARATOR);
484 CString temp;
485 temp.LoadString(IDS_MENULOG);
486 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
488 if (!selectedLeafs.at(0)->m_bFolder)
490 popupMenu.AppendMenu(MF_SEPARATOR);
491 temp.LoadString(IDS_LOG_POPUP_SAVE);
492 popupMenu.AppendMenuIcon(eCmd_SaveAs, temp, IDI_SAVEAS);
495 bAddSeparator = true;
498 if (bAddSeparator)
499 popupMenu.AppendMenu(MF_SEPARATOR);
500 bAddSeparator = false;
502 popupMenu.AppendMenuIcon(eCmd_CopyPath, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
504 eCmd cmd = (eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
505 switch(cmd)
507 case eCmd_ViewLog:
509 CString sCmd;
510 sCmd.Format(_T("/command:log /path:\"%s\\%s\""), g_Git.m_CurrentDir, selectedLeafs.at(0)->GetFullName());
511 CAppUtils::RunTortoiseProc(sCmd);
513 break;
514 case eCmd_Open:
515 OpenFile(selectedLeafs.at(0)->GetFullName(), OPEN);
516 break;
517 case eCmd_OpenWith:
518 OpenFile(selectedLeafs.at(0)->GetFullName(), OPEN_WITH);
519 break;
520 case eCmd_OpenWithAlternativeEditor:
521 OpenFile(selectedLeafs.at(0)->GetFullName(), ALTERNATIVEEDITOR);
522 break;
523 case eCmd_SaveAs:
524 FileSaveAs(selectedLeafs.at(0)->GetFullName());
525 break;
526 case eCmd_CopyPath:
528 CString sClipboard;
529 for (TShadowFilesTreeList::iterator itShadowTree = selectedLeafs.begin(); itShadowTree != selectedLeafs.end(); ++itShadowTree)
531 sClipboard += (*itShadowTree)->m_sName + _T("\r\n");
533 CStringUtils::WriteAsciiStringToClipboard(sClipboard);
535 break;
539 BOOL CRepositoryBrowser::PreTranslateMessage(MSG* pMsg)
541 if (pMsg->message == WM_KEYDOWN)
543 switch (pMsg->wParam)
545 case VK_F5:
547 Refresh();
549 break;
553 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
556 void CRepositoryBrowser::OnLvnColumnclickRepoList(NMHDR *pNMHDR, LRESULT *pResult)
558 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
559 if (pResult)
560 *pResult = 0;
562 if (m_currSortCol == pNMLV->iSubItem)
563 m_currSortDesc = !m_currSortDesc;
564 else
566 m_currSortCol = pNMLV->iSubItem;
567 m_currSortDesc = false;
570 CRepoListCompareFunc compareFunc(&m_RepoList, m_currSortCol, m_currSortDesc);
571 m_RepoList.SortItemsEx(&CRepoListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
573 SetSortArrowA(&m_RepoList, m_currSortCol, !m_currSortDesc);
576 void CRepositoryBrowser::OnBnClickedButtonRevision()
578 // use the git log to allow selection of a version
579 CLogDlg dlg;
580 // tell the dialog to use mode for selecting revisions
581 dlg.SetSelect(true);
582 // only one revision must be selected however
583 dlg.SingleSelection(true);
584 if (dlg.DoModal() == IDOK)
586 // get selected hash if any
587 m_sRevision = dlg.GetSelectedHash();
588 Refresh();
592 void CRepositoryBrowser::SaveDividerPosition()
594 RECT rc;
595 GetDlgItem(IDC_REPOTREE)->GetClientRect(&rc);
596 CRegDWORD xPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RepobrowserDivider"));
597 xPos = rc.right - rc.left;
600 void CRepositoryBrowser::HandleDividerMove(CPoint point, bool bDraw)
602 RECT rect, tree, list, treelist, treelistclient;
604 // create an union of the tree and list control rectangle
605 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
606 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
607 UnionRect(&treelist, &tree, &list);
608 treelistclient = treelist;
609 ScreenToClient(&treelistclient);
611 ClientToScreen(&point);
612 GetClientRect(&rect);
613 ClientToScreen(&rect);
615 CPoint point2 = point;
616 if (point2.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
617 point2.x = treelist.left + REPOBROWSER_CTRL_MIN_WIDTH;
618 if (point2.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
619 point2.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
621 point.x -= rect.left;
622 point.y -= treelist.top;
624 OffsetRect(&treelist, -treelist.left, -treelist.top);
626 if (point.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)
627 point.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;
628 if (point.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH)
629 point.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;
631 if (bDraw)
633 CDC * pDC = GetDC();
634 DrawXorBar(pDC, oldx + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
635 ReleaseDC(pDC);
638 oldx = point.x;
639 oldy = point.y;
641 //position the child controls
642 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&treelist);
643 treelist.right = point2.x - 2;
644 ScreenToClient(&treelist);
645 RemoveAnchor(IDC_REPOTREE);
646 GetDlgItem(IDC_REPOTREE)->MoveWindow(&treelist);
647 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&treelist);
648 treelist.left = point2.x + 2;
649 ScreenToClient(&treelist);
650 RemoveAnchor(IDC_REPOLIST);
651 GetDlgItem(IDC_REPOLIST)->MoveWindow(&treelist);
653 AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);
654 AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);
657 void CRepositoryBrowser::OnMouseMove(UINT nFlags, CPoint point)
659 if (bDragMode == FALSE)
660 return;
662 RECT rect, tree, list, treelist, treelistclient;
663 // create an union of the tree and list control rectangle
664 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
665 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
666 UnionRect(&treelist, &tree, &list);
667 treelistclient = treelist;
668 ScreenToClient(&treelistclient);
670 //convert the mouse coordinates relative to the top-left of
671 //the window
672 ClientToScreen(&point);
673 GetClientRect(&rect);
674 ClientToScreen(&rect);
675 point.x -= rect.left;
676 point.y -= treelist.top;
678 //same for the window coordinates - make them relative to 0,0
679 OffsetRect(&treelist, -treelist.left, -treelist.top);
681 if (point.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
682 point.x = treelist.left + REPOBROWSER_CTRL_MIN_WIDTH;
683 if (point.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
684 point.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
686 if ((nFlags & MK_LBUTTON) && (point.x != oldx))
688 CDC * pDC = GetDC();
690 if (pDC)
692 DrawXorBar(pDC, oldx + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
693 DrawXorBar(pDC, point.x + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
695 ReleaseDC(pDC);
698 oldx = point.x;
699 oldy = point.y;
702 CStandAloneDialogTmpl<CResizableDialog>::OnMouseMove(nFlags, point);
705 void CRepositoryBrowser::OnLButtonDown(UINT nFlags, CPoint point)
707 RECT rect, tree, list, treelist, treelistclient;
709 // create an union of the tree and list control rectangle
710 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);
711 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);
712 UnionRect(&treelist, &tree, &list);
713 treelistclient = treelist;
714 ScreenToClient(&treelistclient);
716 //convert the mouse coordinates relative to the top-left of
717 //the window
718 ClientToScreen(&point);
719 GetClientRect(&rect);
720 ClientToScreen(&rect);
721 point.x -= rect.left;
722 point.y -= treelist.top;
724 //same for the window coordinates - make them relative to 0,0
725 OffsetRect(&treelist, -treelist.left, -treelist.top);
727 if (point.x < treelist.left + REPOBROWSER_CTRL_MIN_WIDTH)
728 return CStandAloneDialogTmpl < CResizableDialog>::OnLButtonDown(nFlags, point);
729 if (point.x > treelist.right - 3)
730 return CStandAloneDialogTmpl < CResizableDialog>::OnLButtonDown(nFlags, point);
731 if (point.x > treelist.right - REPOBROWSER_CTRL_MIN_WIDTH)
732 point.x = treelist.right - REPOBROWSER_CTRL_MIN_WIDTH;
734 if ((point.y < treelist.top + 3) || (point.y > treelist.bottom - 3))
735 return CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
737 bDragMode = true;
739 SetCapture();
741 CDC * pDC = GetDC();
742 DrawXorBar(pDC, point.x + 2, treelistclient.top, 4, treelistclient.bottom - treelistclient.top - 2);
743 ReleaseDC(pDC);
745 oldx = point.x;
746 oldy = point.y;
748 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
751 void CRepositoryBrowser::OnLButtonUp(UINT nFlags, CPoint point)
753 if (bDragMode == FALSE)
754 return;
756 HandleDividerMove(point, true);
758 bDragMode = false;
759 ReleaseCapture();
761 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonUp(nFlags, point);
764 void CRepositoryBrowser::OnCaptureChanged(CWnd *pWnd)
766 bDragMode = false;
768 __super::OnCaptureChanged(pWnd);
771 void CRepositoryBrowser::DrawXorBar(CDC * pDC, int x1, int y1, int width, int height)
773 static WORD _dotPatternBmp[8] =
775 0x0055, 0x00aa, 0x0055, 0x00aa,
776 0x0055, 0x00aa, 0x0055, 0x00aa
779 HBITMAP hbm;
780 HBRUSH hbr, hbrushOld;
782 hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
783 hbr = CreatePatternBrush(hbm);
785 pDC->SetBrushOrg(x1, y1);
786 hbrushOld = (HBRUSH)pDC->SelectObject(hbr);
788 PatBlt(pDC->GetSafeHdc(), x1, y1, width, height, PATINVERT);
790 pDC->SelectObject(hbrushOld);
792 DeleteObject(hbr);
793 DeleteObject(hbm);
796 BOOL CRepositoryBrowser::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
798 if (pWnd == this)
800 RECT rect;
801 POINT pt;
802 GetClientRect(&rect);
803 GetCursorPos(&pt);
804 ScreenToClient(&pt);
805 if (PtInRect(&rect, pt))
807 ClientToScreen(&pt);
808 // are we right of the tree control?
809 GetDlgItem(IDC_REPOTREE)->GetWindowRect(&rect);
810 if ((pt.x > rect.right) && (pt.y >= rect.top + 3) && (pt.y <= rect.bottom - 3))
812 // but left of the list control?
813 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&rect);
814 if (pt.x < rect.left)
816 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE));
817 SetCursor(hCur);
818 return TRUE;
823 return CStandAloneDialogTmpl<CResizableDialog>::OnSetCursor(pWnd, nHitTest, message);
826 void CRepositoryBrowser::FileSaveAs(const CString path)
828 CTGitPath gitPath(path);
830 CString filename;
831 filename.Format(_T("%s-%s%s"), gitPath.GetBaseFilename(), m_sRevision.Left(g_Git.GetShortHASHLength()), gitPath.GetFileExtension());
832 CFileDialog dlg(FALSE, NULL, filename, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL);
834 CString cmd, out;
835 if (dlg.DoModal() == IDOK)
837 filename = dlg.GetPathName();
838 if (g_Git.GetOneFile(m_sRevision, gitPath, filename))
840 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, gitPath.GetGitPathString(), m_sRevision, filename);
841 MessageBox(out, _T("TortoiseGit"), MB_OK);
842 return;
847 void CRepositoryBrowser::OpenFile(const CString path, eOpenType mode)
849 CTGitPath gitPath(path);
851 CString temppath;
852 CString file;
853 GetTempPath(temppath);
854 file.Format(_T("%s%s_%s%s"), temppath, gitPath.GetBaseFilename(), m_sRevision.Left(g_Git.GetShortHASHLength()), gitPath.GetFileExtension());
856 CString out;
857 if(g_Git.GetOneFile(m_sRevision, gitPath, file))
859 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, gitPath.GetGitPathString(), m_sRevision, file);
860 MessageBox(out, _T("TortoiseGit"), MB_OK);
861 return;
864 if (mode == ALTERNATIVEEDITOR)
866 CAppUtils::LaunchAlternativeEditor(file);
867 return;
869 else if (mode == OPEN)
871 int ret = HINSTANCE_ERROR;
872 ret = (int)ShellExecute(this->m_hWnd, NULL, file, NULL, NULL, SW_SHOW);
874 if (ret > HINSTANCE_ERROR)
875 return;
878 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ") + file;
879 CAppUtils::LaunchApplication(cmd, NULL, false);