+\r
+\r
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+\r
#include "StdAfx.h"\r
#include "GitStatusListCtrl.h"\r
+#include "resource.h"\r
+#include "MessageBox.h"\r
+#include "MyMemDC.h"\r
+#include "UnicodeUtils.h"\r
+#include "AppUtils.h"\r
+#include "PathUtils.h"\r
+#include "TempFile.h"\r
+#include "StringUtils.h"\r
+#include "DirFileEnum.h"\r
+#include "GitConfig.h"\r
+//#include "SVNProperties.h"\r
+#include "Git.h"\r
+//#include "SVNDiff.h"\r
+//#include "LogDlg.h"\r
+//#include "SVNProgressDlg.h"\r
+#include "SysImageList.h"\r
+//#include "Svnstatuslistctrl.h"\r
+#include "TGitPath.h"\r
+#include "Registry.h"\r
+#include "GitStatus.h"\r
+//#include "SVNHelpers.h"\r
+#include "InputDlg.h"\r
+#include "ShellUpdater.h"\r
+#include "GitAdminDir.h"\r
+//#include "DropFiles.h"\r
+#include "IconMenu.h"\r
+//#include "AddDlg.h"\r
+//#include "EditPropertiesDlg.h"\r
+//#include "CreateChangelistDlg.h"\r
+#include "XPTheme.h"\r
+\r
+const UINT CGitStatusListCtrl::SVNSLNM_ITEMCOUNTCHANGED\r
+ = ::RegisterWindowMessage(_T("GITSLNM_ITEMCOUNTCHANGED"));\r
+const UINT CGitStatusListCtrl::SVNSLNM_NEEDSREFRESH\r
+ = ::RegisterWindowMessage(_T("GITSLNM_NEEDSREFRESH"));\r
+const UINT CGitStatusListCtrl::SVNSLNM_ADDFILE\r
+ = ::RegisterWindowMessage(_T("GITSLNM_ADDFILE"));\r
+const UINT CGitStatusListCtrl::SVNSLNM_CHECKCHANGED\r
+ = ::RegisterWindowMessage(_T("GITSLNM_CHECKCHANGED"));\r
+\r
+#define IDSVNLC_REVERT 1\r
+#define IDSVNLC_COMPARE 2\r
+#define IDSVNLC_OPEN 3\r
+#define IDSVNLC_DELETE 4\r
+#define IDSVNLC_IGNORE 5\r
+#define IDSVNLC_GNUDIFF1 6\r
+#define IDSVNLC_UPDATE 7\r
+#define IDSVNLC_LOG 8\r
+#define IDSVNLC_EDITCONFLICT 9\r
+#define IDSVNLC_IGNOREMASK 10\r
+#define IDSVNLC_ADD 11\r
+#define IDSVNLC_RESOLVECONFLICT 12\r
+#define IDSVNLC_LOCK 13\r
+#define IDSVNLC_LOCKFORCE 14\r
+#define IDSVNLC_UNLOCK 15\r
+#define IDSVNLC_UNLOCKFORCE 16\r
+#define IDSVNLC_OPENWITH 17\r
+#define IDSVNLC_EXPLORE 18\r
+#define IDSVNLC_RESOLVETHEIRS 19\r
+#define IDSVNLC_RESOLVEMINE 20\r
+#define IDSVNLC_REMOVE 21\r
+#define IDSVNLC_COMMIT 22\r
+#define IDSVNLC_PROPERTIES 23\r
+#define IDSVNLC_COPY 24\r
+#define IDSVNLC_COPYEXT 25\r
+#define IDSVNLC_REPAIRMOVE 26\r
+#define IDSVNLC_REMOVEFROMCS 27\r
+#define IDSVNLC_CREATECS 28\r
+#define IDSVNLC_CREATEIGNORECS 29\r
+#define IDSVNLC_CHECKGROUP 30\r
+#define IDSVNLC_UNCHECKGROUP 31\r
+#define IDSVNLC_ADD_RECURSIVE 32\r
+#define IDSVNLC_COMPAREWC 33\r
+#define IDSVNLC_BLAME 34\r
+// the IDSVNLC_MOVETOCS *must* be the last index, because it contains a dynamic submenu where \r
+// the submenu items get command ID's sequent to this number\r
+#define IDSVNLC_MOVETOCS 35\r
+\r
+\r
+BEGIN_MESSAGE_MAP(CGitStatusListCtrl, CListCtrl)\r
+ ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHdnItemclick)\r
+ ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHdnItemclick)\r
+ ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)\r
+ ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)\r
+ ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnLvnItemchanged)\r
+ ON_WM_CONTEXTMENU()\r
+ ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclk)\r
+ ON_NOTIFY_REFLECT(LVN_GETINFOTIP, OnLvnGetInfoTip)\r
+ ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)\r
+ ON_WM_SETCURSOR()\r
+ ON_WM_GETDLGCODE()\r
+ ON_NOTIFY_REFLECT(NM_RETURN, OnNMReturn)\r
+ ON_WM_KEYDOWN()\r
+ ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)\r
+ ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)\r
+ ON_WM_PAINT()\r
+ ON_NOTIFY(HDN_BEGINTRACKA, 0, &CGitStatusListCtrl::OnHdnBegintrack)\r
+ ON_NOTIFY(HDN_BEGINTRACKW, 0, &CGitStatusListCtrl::OnHdnBegintrack)\r
+ ON_NOTIFY(HDN_ITEMCHANGINGA, 0, &CGitStatusListCtrl::OnHdnItemchanging)\r
+ ON_NOTIFY(HDN_ITEMCHANGINGW, 0, &CGitStatusListCtrl::OnHdnItemchanging)\r
+ ON_WM_DESTROY()\r
+ ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)\r
+ ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CGitStatusListCtrl::OnLvnItemchanging)\r
+END_MESSAGE_MAP()\r
+\r
+\r
+\r
+CGitStatusListCtrl::CGitStatusListCtrl() : CListCtrl()\r
+ //, m_HeadRev(GitRev::REV_HEAD)\r
+ , m_pbCanceled(NULL)\r
+ , m_pStatLabel(NULL)\r
+ , m_pSelectButton(NULL)\r
+ , m_pConfirmButton(NULL)\r
+ , m_bBusy(false)\r
+ , m_bEmpty(false)\r
+ , m_bUnversionedRecurse(true)\r
+ , m_bShowIgnores(false)\r
+ , m_pDropTarget(NULL)\r
+ , m_bIgnoreRemoveOnly(false)\r
+ , m_bCheckChildrenWithParent(false)\r
+ , m_bUnversionedLast(true)\r
+ , m_bHasChangeLists(false)\r
+ , m_bHasLocks(false)\r
+ , m_bBlock(false)\r
+ , m_bBlockUI(false)\r
+ , m_bHasCheckboxes(false)\r
+ , m_bCheckIfGroupsExist(true)\r
+ , m_bFileDropsEnabled(false)\r
+ , m_bOwnDrag(false)\r
+ , m_dwDefaultColumns(0)\r
+ , m_ColumnManager(this)\r
+ , m_bAscending(false)\r
+ , m_nSortedColumn(-1)\r
+ , m_sNoPropValueText(MAKEINTRESOURCE(IDS_STATUSLIST_NOPROPVALUE))\r
+{\r
+ m_critSec.Init();\r
+}\r
+\r
+CGitStatusListCtrl::~CGitStatusListCtrl()\r
+{\r
+ if (m_pDropTarget)\r
+ delete m_pDropTarget;\r
+ ClearStatusArray();\r
+}\r
+\r
+void CGitStatusListCtrl::ClearStatusArray()\r
+{\r
+ Locker lock(m_critSec);\r
+ for (size_t i=0; i < m_arStatusArray.size(); i++)\r
+ {\r
+ delete m_arStatusArray[i];\r
+ }\r
+ m_arStatusArray.clear();\r
+}\r
+\r
+CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(UINT_PTR index)\r
+{\r
+ if (index >= (UINT_PTR)m_arListArray.size())\r
+ return NULL;\r
+ if (m_arListArray[index] >= m_arStatusArray.size())\r
+ return NULL;\r
+ return m_arStatusArray[m_arListArray[index]];\r
+}\r
+\r
+CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetVisibleListEntry(const CTGitPath& path)\r
+{\r
+ int itemCount = GetItemCount();\r
+ for (int i=0; i < itemCount; i++)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry->GetPath().IsEquivalentTo(path))\r
+ return entry;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(const CTGitPath& path)\r
+{\r
+ for (size_t i=0; i < m_arStatusArray.size(); i++)\r
+ {\r
+ FileEntry * entry = m_arStatusArray[i];\r
+ if (entry->GetPath().IsEquivalentTo(path))\r
+ return entry;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+int CGitStatusListCtrl::GetIndex(const CTGitPath& path)\r
+{\r
+ int itemCount = GetItemCount();\r
+ for (int i=0; i < itemCount; i++)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry->GetPath().IsEquivalentTo(path))\r
+ return i;\r
+ }\r
+ return -1;\r
+}\r
+\r
+void CGitStatusListCtrl::Init(DWORD dwColumns, const CString& sColumnInfoContainer, DWORD dwContextMenus /* = GitSLC_POPALL */, bool bHasCheckboxes /* = true */)\r
+{\r
+ Locker lock(m_critSec);\r
+\r
+ m_dwDefaultColumns = dwColumns | 1;\r
+ m_dwContextMenus = dwContextMenus;\r
+ m_bHasCheckboxes = bHasCheckboxes;\r
+\r
+ // set the extended style of the listcontrol\r
+ // 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.\r
+ CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);\r
+ DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;\r
+ if (DWORD(regFullRowSelect))\r
+ exStyle |= LVS_EX_FULLROWSELECT;\r
+ exStyle |= (bHasCheckboxes ? LVS_EX_CHECKBOXES : 0);\r
+ SetRedraw(false);\r
+ SetExtendedStyle(exStyle);\r
+\r
+ CXPTheme theme;\r
+ theme.SetWindowTheme(m_hWnd, L"Explorer", NULL);\r
+\r
+ m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();\r
+ SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);\r
+\r
+ m_ColumnManager.ReadSettings (m_dwDefaultColumns, sColumnInfoContainer);\r
+\r
+ // enable file drops\r
+#if 0\r
+ if (m_pDropTarget == NULL)\r
+ {\r
+ m_pDropTarget = new CGitStatusListCtrlDropTarget(this);\r
+ RegisterDragDrop(m_hWnd,m_pDropTarget);\r
+ // create the supported formats:\r
+ FORMATETC ftetc={0};\r
+ ftetc.dwAspect = DVASPECT_CONTENT;\r
+ ftetc.lindex = -1;\r
+ ftetc.tymed = TYMED_HGLOBAL;\r
+ ftetc.cfFormat=CF_HDROP;\r
+ m_pDropTarget->AddSuportedFormat(ftetc);\r
+ }\r
+#endif\r
+\r
+ SetRedraw(true);\r
+\r
+ m_bUnversionedRecurse = !!((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\UnversionedRecurse"), TRUE));\r
+}\r
\r
-CGitStatusListCtrl::CGitStatusListCtrl(void)\r
+bool CGitStatusListCtrl::SetBackgroundImage(UINT nID)\r
{\r
+ return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);\r
}\r
\r
-CGitStatusListCtrl::~CGitStatusListCtrl(void)\r
+BOOL CGitStatusListCtrl::GetStatus ( const CTGitPathList& pathList\r
+ , bool bUpdate /* = FALSE */\r
+ , bool bShowIgnores /* = false */\r
+ , bool bShowUserProps /* = false */)\r
{\r
+ Locker lock(m_critSec);\r
+ int refetchcounter = 0;\r
+ BOOL bRet = TRUE;\r
+ Invalidate();\r
+ // force the cursor to change\r
+ POINT pt;\r
+ GetCursorPos(&pt);\r
+ SetCursorPos(pt.x, pt.y);\r
+\r
+ m_mapFilenameToChecked.clear();\r
+ m_StatusUrlList.Clear();\r
+ bool bHasChangelists = (m_changelists.size()>1 || (m_changelists.size()>0 && !m_bHasIgnoreGroup));\r
+ m_changelists.clear();\r
+ for (size_t i=0; i < m_arStatusArray.size(); i++)\r
+ {\r
+ FileEntry * entry = m_arStatusArray[i];\r
+ if ( bHasChangelists && entry->checked)\r
+ {\r
+ // If change lists are present, remember all checked entries\r
+ CString path = entry->GetPath().GetGitPathString();\r
+ m_mapFilenameToChecked[path] = true;\r
+ }\r
+ if ( (entry->status==git_wc_status_unversioned || entry->status==git_wc_status_missing ) && entry->checked )\r
+ {\r
+ // The user manually selected an unversioned or missing file. We remember\r
+ // this so that the selection can be restored when refreshing.\r
+ CString path = entry->GetPath().GetGitPathString();\r
+ m_mapFilenameToChecked[path] = true;\r
+ }\r
+ else if ( entry->status > git_wc_status_normal && !entry->checked )\r
+ {\r
+ // The user manually deselected a versioned file. We remember\r
+ // this so that the deselection can be restored when refreshing.\r
+ CString path = entry->GetPath().GetGitPathString();\r
+ m_mapFilenameToChecked[path] = false;\r
+ }\r
+ }\r
+\r
+ // use a sorted path list to make sure we fetch the status of\r
+ // parent items first, *then* the child items (if any)\r
+ CTGitPathList sortedPathList = pathList;\r
+ sortedPathList.SortByPathname();\r
+ do\r
+ {\r
+ bRet = TRUE;\r
+ m_nTargetCount = 0;\r
+ m_bHasExternalsFromDifferentRepos = FALSE;\r
+ m_bHasExternals = FALSE;\r
+ m_bHasUnversionedItems = FALSE;\r
+ m_bHasLocks = false;\r
+ m_bHasChangeLists = false;\r
+ m_bShowIgnores = bShowIgnores;\r
+ m_nSortedColumn = 0;\r
+ m_bBlock = TRUE;\r
+ m_bBusy = true;\r
+ m_bEmpty = false;\r
+ Invalidate();\r
+\r
+ // first clear possible status data left from\r
+ // previous GetStatus() calls\r
+ ClearStatusArray();\r
+\r
+ m_StatusFileList = sortedPathList;\r
+\r
+ // Since Git_client_status() returns all files, even those in\r
+ // folders included with Git:externals we need to check if all\r
+ // files we get here belongs to the same repository.\r
+ // It is possible to commit changes in an external folder, as long\r
+ // as the folder belongs to the same repository (but another path),\r
+ // but it is not possible to commit all files if the externals are\r
+ // from a different repository.\r
+ //\r
+ // To check if all files belong to the same repository, we compare the\r
+ // UUID's - if they're identical then the files belong to the same\r
+ // repository and can be committed. But if they're different, then\r
+ // tell the user to commit all changes in the external folders\r
+ // first and exit.\r
+ CStringA sUUID; // holds the repo UUID\r
+ CTGitPathList arExtPaths; // list of Git:external paths\r
+\r
+ GitConfig config;\r
+\r
+ m_sURL.Empty();\r
+\r
+ m_nTargetCount = sortedPathList.GetCount();\r
+\r
+ GitStatus status(m_pbCanceled);\r
+ if(m_nTargetCount > 1 && sortedPathList.AreAllPathsFilesInOneDirectory())\r
+ {\r
+ // This is a special case, where we've been given a list of files\r
+ // all from one folder\r
+ // We handle them by setting a status filter, then requesting the Git status of\r
+ // all the files in the directory, filtering for the ones we're interested in\r
+ status.SetFilter(sortedPathList);\r
+\r
+ // if all selected entries are files, we don't do a recursive status\r
+ // fetching. But if only one is a directory, we have to recurse.\r
+ git_depth_t depth = git_depth_files;\r
+ // We have set a filter. If all selected items were files or we fetch\r
+ // the status not recursively, then we have to include\r
+ // ignored items too - the user has selected them\r
+ bool bShowIgnoresRight = true;\r
+ for (int fcindex=0; fcindex<sortedPathList.GetCount(); ++fcindex)\r
+ {\r
+ if (sortedPathList[fcindex].IsDirectory())\r
+ {\r
+ depth = git_depth_infinity;\r
+ bShowIgnoresRight = false;\r
+ break;\r
+ }\r
+ }\r
+ if(!FetchStatusForSingleTarget(config, status, sortedPathList.GetCommonDirectory(), bUpdate, sUUID, arExtPaths, true, depth, bShowIgnoresRight))\r
+ {\r
+ bRet = FALSE;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for(int nTarget = 0; nTarget < m_nTargetCount; nTarget++)\r
+ {\r
+ // check whether the path we want the status for is already fetched due to status-fetching\r
+ // of a parent path.\r
+ // this check is only done for file paths, because folder paths could be included already\r
+ // but not recursively\r
+ if (sortedPathList[nTarget].IsDirectory() || GetListEntry(sortedPathList[nTarget]) == NULL)\r
+ {\r
+ if(!FetchStatusForSingleTarget(config, status, sortedPathList[nTarget], bUpdate, sUUID, \r
+ arExtPaths, false, git_depth_infinity, bShowIgnores))\r
+ {\r
+ bRet = FALSE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // remove the 'helper' files of conflicted items from the list.\r
+ // otherwise they would appear as unversioned files.\r
+ for (INT_PTR cind = 0; cind < m_ConflictFileList.GetCount(); ++cind)\r
+ {\r
+ for (size_t i=0; i < m_arStatusArray.size(); i++)\r
+ {\r
+ if (m_arStatusArray[i]->GetPath().IsEquivalentTo(m_ConflictFileList[cind]))\r
+ {\r
+ delete m_arStatusArray[i];\r
+ m_arStatusArray.erase(m_arStatusArray.begin()+i);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ refetchcounter++;\r
+ } while(!BuildStatistics() && (refetchcounter < 2) && (*m_pbCanceled==false));\r
+\r
+ if (bShowUserProps)\r
+ FetchUserProperties();\r
+\r
+ m_ColumnManager.UpdateUserPropList (m_arStatusArray);\r
+\r
+ m_bBlock = FALSE;\r
+ m_bBusy = false;\r
+ GetCursorPos(&pt);\r
+ SetCursorPos(pt.x, pt.y);\r
+ return bRet;\r
}\r
+\r
+//\r
+// Fetch all local properties for all elements in the status array\r
+//\r
+void CGitStatusListCtrl::FetchUserProperties()\r
+{\r
+#if 0\r
+ GitPool globalPool;\r
+\r
+ for (size_t i = 0, count = m_arStatusArray.size(); i < count; ++i)\r
+ {\r
+ // local / temp pool to hold parameters and props for a single item\r
+\r
+ GitPool localPool ((apr_pool_t*)globalPool);\r
+\r
+ // open working copy for this path\r
+\r
+ const char* path = m_arStatusArray[i]->path.GetGitApiPath (localPool);\r
+ \r
+ Git_wc_adm_access_t *adm_access = NULL; \r
+ Git_error_t * error = Git_wc_adm_probe_open3 ( &adm_access\r
+ , NULL\r
+ , path\r
+ , FALSE // no write lock\r
+ , 0 // lock just the directory/file itself\r
+ , NULL\r
+ , NULL\r
+ , localPool);\r
+ if (error == NULL)\r
+ {\r
+ // get the props and add them to the status info\r
+\r
+ apr_hash_t* props = NULL;\r
+ Git_error_t * error = Git_wc_prop_list ( &props\r
+ , path\r
+ , adm_access\r
+ , localPool);\r
+ if (error == NULL)\r
+ {\r
+ for ( apr_hash_index_t *index \r
+ = apr_hash_first (localPool, props)\r
+ ; index != NULL\r
+ ; index = apr_hash_next (index))\r
+ {\r
+ // extract next entry from hash\r
+\r
+ const char* key = NULL;\r
+ ptrdiff_t keyLen;\r
+ const char** val = NULL;\r
+\r
+ apr_hash_this ( index\r
+ , reinterpret_cast<const void**>(&key)\r
+ , &keyLen\r
+ , reinterpret_cast<void**>(&val));\r
+\r
+ // decode / dispatch it\r
+\r
+ CString name = CUnicodeUtils::GetUnicode (key);\r
+ CString value = CUnicodeUtils::GetUnicode (*val);\r
+\r
+ // store in property container (truncate it after ~100 chars)\r
+\r
+ m_arStatusArray[i]->present_props[name] \r
+ = value.Left (GitSLC_MAXUSERPROPLENGTH);\r
+ }\r
+ }\r
+ error = Git_wc_adm_close2 (adm_access, localPool);\r
+ }\r
+ Git_error_clear (error);\r
+ }\r
+#endif\r
+}\r
+\r
+\r
+//\r
+// Work on a single item from the list of paths which is provided to us\r
+//\r
+bool CGitStatusListCtrl::FetchStatusForSingleTarget(\r
+ GitConfig& config,\r
+ GitStatus& status,\r
+ const CTGitPath& target,\r
+ bool bFetchStatusFromRepository,\r
+ CStringA& strCurrentRepositoryUUID,\r
+ CTGitPathList& arExtPaths,\r
+ bool bAllDirect,\r
+ git_depth_t depth,\r
+ bool bShowIgnores\r
+ )\r
+{\r
+#if 0\r
+ config.GetDefaultIgnores();\r
+\r
+ CTGitPath workingTarget(target);\r
+\r
+ git_wc_status2_t * s;\r
+ CTGitPath GitPath;\r
+ s = status.GetFirstFileStatus(workingTarget, GitPath, bFetchStatusFromRepository, depth, bShowIgnores);\r
+\r
+ m_HeadRev = SVNRev(status.headrev);\r
+ if (s!=0)\r
+ {\r
+ Git_wc_status_kind wcFileStatus = GitStatus::GetMoreImportant(s->text_status, s->prop_status);\r
+\r
+ // This one fixes a problem with externals:\r
+ // If a strLine is a file, Git:externals and its parent directory\r
+ // will also be returned by GetXXXFileStatus. Hence, we skip all\r
+ // status info until we find the one matching workingTarget.\r
+ if (!workingTarget.IsDirectory())\r
+ {\r
+ if (!workingTarget.IsEquivalentTo(GitPath))\r
+ {\r
+ while (s != 0)\r
+ {\r
+ s = status.GetNextFileStatus(GitPath);\r
+ if(workingTarget.IsEquivalentTo(GitPath))\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ if (s == 0)\r
+ {\r
+ m_sLastError = status.GetLastErrorMsg();\r
+ return false;\r
+ }\r
+ // Now, set working target to be the base folder of this item\r
+ workingTarget = workingTarget.GetDirectory();\r
+ }\r
+ }\r
+ bool bEntryFromDifferentRepo = false;\r
+ // Is this a versioned item with an associated repos UUID?\r
+ if ((s->entry)&&(s->entry->uuid))\r
+ {\r
+ // Have we seen a repos UUID yet?\r
+ if (strCurrentRepositoryUUID.IsEmpty())\r
+ {\r
+ // This is the first repos UUID we've seen - record it\r
+ strCurrentRepositoryUUID = s->entry->uuid;\r
+ m_sUUID = strCurrentRepositoryUUID;\r
+ }\r
+ else\r
+ {\r
+ if (strCurrentRepositoryUUID.Compare(s->entry->uuid)!=0)\r
+ {\r
+ // This item comes from a different repository than our main one\r
+ m_bHasExternalsFromDifferentRepos = TRUE;\r
+ bEntryFromDifferentRepo = true;\r
+ if (s->entry->kind == Git_node_dir)\r
+ arExtPaths.AddPath(workingTarget);\r
+ }\r
+ }\r
+ }\r
+ else if (strCurrentRepositoryUUID.IsEmpty() && (s->text_status == Git_wc_status_added))\r
+ {\r
+ // An added entry doesn't have an UUID assigned to it yet.\r
+ // So we fetch the status of the parent directory instead and\r
+ // check if that one has an UUID assigned to it.\r
+ Git_wc_status2_t * sparent;\r
+ CTGitPath path = workingTarget;\r
+ do\r
+ {\r
+ CTGitPath GitParentPath;\r
+ GitStatus tempstatus;\r
+ sparent = tempstatus.GetFirstFileStatus(path.GetContainingDirectory(), GitParentPath, false, Git_depth_empty, false);\r
+ path = GitParentPath;\r
+ } while ( (sparent) && (sparent->entry) && (!sparent->entry->uuid) && (sparent->text_status==Git_wc_status_added) );\r
+ if (sparent && sparent->entry && sparent->entry->uuid)\r
+ {\r
+ strCurrentRepositoryUUID = sparent->entry->uuid;\r
+ m_sUUID = strCurrentRepositoryUUID;\r
+ }\r
+ }\r
+\r
+ if ((wcFileStatus == Git_wc_status_unversioned)&& GitPath.IsDirectory())\r
+ {\r
+ // check if the unversioned folder is maybe versioned. This\r
+ // could happen with nested layouts\r
+ Git_wc_status_kind st = GitStatus::GetAllStatus(workingTarget);\r
+ if ((st != Git_wc_status_unversioned)&&(st != Git_wc_status_none))\r
+ {\r
+ return true; // ignore nested layouts\r
+ }\r
+ }\r
+ if (status.IsExternal(GitPath))\r
+ {\r
+ m_bHasExternals = TRUE;\r
+ }\r
+\r
+ AddNewFileEntry(s, GitPath, workingTarget, true, m_bHasExternals, bEntryFromDifferentRepo);\r
+\r
+ if (((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none)||((wcFileStatus == Git_wc_status_ignored)&&(m_bShowIgnores))) && GitPath.IsDirectory())\r
+ {\r
+ // we have an unversioned folder -> get all files in it recursively!\r
+ AddUnversionedFolder(GitPath, workingTarget.GetContainingDirectory(), &config);\r
+ }\r
+\r
+ // for folders, get all statuses inside it too\r
+ if(workingTarget.IsDirectory())\r
+ {\r
+ ReadRemainingItemsStatus(status, workingTarget, strCurrentRepositoryUUID, arExtPaths, &config, bAllDirect);\r
+ }\r
+\r
+ } // if (s!=0)\r
+ else\r
+ {\r
+ m_sLastError = status.GetLastErrorMsg();\r
+ return false;\r
+ }\r
+#endif\r
+ return true;\r
+}\r
+\r
+const CGitStatusListCtrl::FileEntry*\r
+CGitStatusListCtrl::AddNewFileEntry(\r
+ const git_wc_status2_t* pGitStatus, // The return from the Git GetStatus functions\r
+ const CTGitPath& path, // The path of the item we're adding\r
+ const CTGitPath& basePath, // The base directory for this status build\r
+ bool bDirectItem, // Was this item the first found by GetFirstFileStatus or by a subsequent GetNextFileStatus call\r
+ bool bInExternal, // Are we in an 'external' folder\r
+ bool bEntryfromDifferentRepo // if the entry is from a different repository\r
+ )\r
+{\r
+ FileEntry * entry = new FileEntry();\r
+#if 0\r
+ \r
+ entry->path = path;\r
+ entry->basepath = basePath;\r
+ entry->status = GitStatus::GetMoreImportant(pGitStatus->text_status, pGitStatus->prop_status);\r
+ entry->textstatus = pGitStatus->text_status;\r
+ entry->propstatus = pGitStatus->prop_status;\r
+// entry->remotestatus = GitStatus::GetMoreImportant(pGitStatus->repos_text_status, pGitStatus->repos_prop_status);\r
+// entry->remotetextstatus = pGitStatus->repos_text_status;\r
+// entry->remotepropstatus = pGitStatus->repos_prop_status;\r
+ entry->inexternal = bInExternal;\r
+ entry->differentrepo = bEntryfromDifferentRepo;\r
+ entry->direct = bDirectItem;\r
+// entry->copied = !!pGitStatus->copied;\r
+// entry->switched = !!pGitStatus->switched;\r
+\r
+ entry->last_commit_date = pGitStatus->ood_last_cmt_date;\r
+ if ((entry->last_commit_date == NULL)&&(pGitStatus->entry))\r
+ entry->last_commit_date = pGitStatus->entry->cmt_date;\r
+ entry->remoterev = pGitStatus->ood_last_cmt_rev;\r
+ if (pGitStatus->entry)\r
+ entry->last_commit_rev = pGitStatus->entry->cmt_rev;\r
+ if (pGitStatus->ood_last_cmt_author)\r
+ entry->last_commit_author = CUnicodeUtils::GetUnicode(pGitStatus->ood_last_cmt_author);\r
+ if ((entry->last_commit_author.IsEmpty())&&(pGitStatus->entry)&&(pGitStatus->entry->cmt_author))\r
+ entry->last_commit_author = CUnicodeUtils::GetUnicode(pGitStatus->entry->cmt_author);\r
+\r
+ if (pGitStatus->entry)\r
+ entry->isConflicted = (pGitStatus->entry->conflict_wrk && PathFileExists(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_wrk))) ? true : false;\r
+\r
+ if ((entry->status == Git_wc_status_conflicted)||(entry->isConflicted))\r
+ {\r
+ entry->isConflicted = true;\r
+ if (pGitStatus->entry)\r
+ {\r
+ CTGitPath cpath;\r
+ if (pGitStatus->entry->conflict_wrk)\r
+ {\r
+ cpath = path.GetDirectory();\r
+ cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_wrk));\r
+ m_ConflictFileList.AddPath(cpath);\r
+ }\r
+ if (pGitStatus->entry->conflict_old)\r
+ {\r
+ cpath = path.GetDirectory();\r
+ cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_old));\r
+ m_ConflictFileList.AddPath(cpath);\r
+ }\r
+ if (pGitStatus->entry->conflict_new)\r
+ {\r
+ cpath = path.GetDirectory();\r
+ cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_new));\r
+ m_ConflictFileList.AddPath(cpath);\r
+ }\r
+ if (pGitStatus->entry->prejfile)\r
+ {\r
+ cpath = path.GetDirectory();\r
+ cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->prejfile));\r
+ m_ConflictFileList.AddPath(cpath);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (pGitStatus->entry)\r
+ {\r
+ entry->isfolder = (pGitStatus->entry->kind == Git_node_dir);\r
+ entry->Revision = pGitStatus->entry->revision;\r
+ entry->keeplocal = !!pGitStatus->entry->keep_local;\r
+ entry->working_size = pGitStatus->entry->working_size;\r
+ entry->depth = pGitStatus->entry->depth;\r
+\r
+ if (pGitStatus->entry->url)\r
+ {\r
+ entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(pGitStatus->entry->url));\r
+ }\r
+ if (pGitStatus->entry->copyfrom_url)\r
+ {\r
+ entry->copyfrom_url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(pGitStatus->entry->copyfrom_url));\r
+ entry->copyfrom_rev = pGitStatus->entry->copyfrom_rev;\r
+ }\r
+ else\r
+ entry->copyfrom_rev = 0;\r
+\r
+ if(bDirectItem)\r
+ {\r
+ if (m_sURL.IsEmpty())\r
+ m_sURL = entry->url;\r
+ else\r
+ m_sURL.LoadString(IDS_STATUSLIST_MULTIPLETARGETS);\r
+ m_StatusUrlList.AddPath(CTGitPath(entry->url));\r
+ }\r
+ if (pGitStatus->entry->lock_owner)\r
+ entry->lock_owner = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_owner);\r
+ if (pGitStatus->entry->lock_token)\r
+ {\r
+ entry->lock_token = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_token);\r
+ m_bHasLocks = true;\r
+ }\r
+ if (pGitStatus->entry->lock_comment)\r
+ entry->lock_comment = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_comment);\r
+\r
+ if (pGitStatus->entry->present_props)\r
+ {\r
+ entry->present_props = pGitStatus->entry->present_props;\r
+ }\r
+\r
+ if (pGitStatus->entry->changelist)\r
+ {\r
+ entry->changelist = CUnicodeUtils::GetUnicode(pGitStatus->entry->changelist);\r
+ m_changelists[entry->changelist] = -1;\r
+ m_bHasChangeLists = true;\r
+ }\r
+ entry->needslock = (pGitStatus->entry->present_props && (strstr(pGitStatus->entry->present_props, "Git:needs-lock")!=NULL) );\r
+ }\r
+ else\r
+ {\r
+ if (pGitStatus->ood_kind == Git_node_none)\r
+ entry->isfolder = path.IsDirectory();\r
+ else\r
+ entry->isfolder = (pGitStatus->ood_kind == Git_node_dir);\r
+ }\r
+ if (pGitStatus->repos_lock)\r
+ {\r
+ if (pGitStatus->repos_lock->owner)\r
+ entry->lock_remoteowner = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->owner);\r
+ if (pGitStatus->repos_lock->token)\r
+ entry->lock_remotetoken = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->token);\r
+ if (pGitStatus->repos_lock->comment)\r
+ entry->lock_comment = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->comment);\r
+ }\r
+\r
+ // Pass ownership of the entry to the array\r
+ m_arStatusArray.push_back(entry);\r
+#endif\r
+ return entry;\r
+}\r
+\r
+void CGitStatusListCtrl::AddUnversionedFolder(const CTGitPath& folderName,\r
+ const CTGitPath& basePath,\r
+ GitConfig * config)\r
+{\r
+#if 0\r
+ if (!m_bUnversionedRecurse)\r
+ return;\r
+ CSimpleFileFind filefinder(folderName.GetWinPathString());\r
+\r
+ CTGitPath filename;\r
+ m_bHasUnversionedItems = TRUE;\r
+ while (filefinder.FindNextFileNoDots())\r
+ {\r
+ filename.SetFromWin(filefinder.GetFilePath(), filefinder.IsDirectory());\r
+\r
+ bool bMatchIgnore = !!config->MatchIgnorePattern(filename.GetFileOrDirectoryName());\r
+ bMatchIgnore = bMatchIgnore || config->MatchIgnorePattern(filename.GetGitPathString());\r
+ if (((bMatchIgnore)&&(m_bShowIgnores))||(!bMatchIgnore))\r
+ {\r
+ FileEntry * entry = new FileEntry();\r
+ entry->path = filename;\r
+ entry->basepath = basePath;\r
+ entry->inunversionedfolder = true;\r
+ entry->isfolder = filefinder.IsDirectory();\r
+\r
+ m_arStatusArray.push_back(entry);\r
+ if (entry->isfolder)\r
+ {\r
+ if (!g_GitAdminDir.HasAdminDir(entry->path.GetWinPathString(), true))\r
+ AddUnversionedFolder(entry->path, basePath, config);\r
+ }\r
+ }\r
+ }\r
+#endif\r
+}\r
+\r
+\r
+void CGitStatusListCtrl::ReadRemainingItemsStatus(GitStatus& status, const CTGitPath& basePath,\r
+ CStringA& strCurrentRepositoryUUID,\r
+ CTGitPathList& arExtPaths, GitConfig * config, bool bAllDirect)\r
+{\r
+#if 0\r
+ git_wc_status2_t * s;\r
+\r
+ CTGitPath lastexternalpath;\r
+ CTGitPath GitPath;\r
+ while ((s = status.GetNextFileStatus(GitPath)) != NULL)\r
+ {\r
+ Git_wc_status_kind wcFileStatus = GitStatus::GetMoreImportant(s->text_status, s->prop_status);\r
+ if ((wcFileStatus == Git_wc_status_unversioned) && (GitPath.IsDirectory()))\r
+ {\r
+ // check if the unversioned folder is maybe versioned. This\r
+ // could happen with nested layouts\r
+ Git_wc_status_kind st = GitStatus::GetAllStatus(GitPath);\r
+ if ((st != Git_wc_status_unversioned)&&(st != Git_wc_status_none))\r
+ {\r
+ FileEntry * entry = new FileEntry();\r
+ entry->path = GitPath;\r
+ entry->basepath = basePath;\r
+ entry->inunversionedfolder = true;\r
+ entry->isfolder = true;\r
+ entry->isNested = true;\r
+ m_arStatusArray.push_back(entry);\r
+ continue;\r
+ }\r
+ }\r
+ bool bDirectoryIsExternal = false;\r
+ bool bEntryfromDifferentRepo = false;\r
+ if (s->entry)\r
+ {\r
+ if (s->entry->uuid)\r
+ {\r
+ if (strCurrentRepositoryUUID.IsEmpty())\r
+ strCurrentRepositoryUUID = s->entry->uuid;\r
+ else\r
+ {\r
+ if (strCurrentRepositoryUUID.Compare(s->entry->uuid)!=0)\r
+ {\r
+ bEntryfromDifferentRepo = true;\r
+ if (GitStatus::IsImportant(wcFileStatus))\r
+ m_bHasExternalsFromDifferentRepos = TRUE;\r
+ if (s->entry->kind == Git_node_dir)\r
+ {\r
+ if ((lastexternalpath.IsEmpty())||(!lastexternalpath.IsAncestorOf(GitPath)))\r
+ {\r
+ arExtPaths.AddPath(GitPath);\r
+ lastexternalpath = GitPath;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // we don't have an UUID - maybe an added file/folder\r
+ if (!strCurrentRepositoryUUID.IsEmpty())\r
+ {\r
+ if ((!lastexternalpath.IsEmpty())&&\r
+ (lastexternalpath.IsAncestorOf(GitPath)))\r
+ {\r
+ bEntryfromDifferentRepo = true;\r
+ m_bHasExternalsFromDifferentRepos = TRUE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // if unversioned items lie around in external\r
+ // directories from different repos, we have to mark them\r
+ // as such too.\r
+ if (!strCurrentRepositoryUUID.IsEmpty())\r
+ {\r
+ if ((!lastexternalpath.IsEmpty())&&\r
+ (lastexternalpath.IsAncestorOf(GitPath)))\r
+ {\r
+ bEntryfromDifferentRepo = true;\r
+ }\r
+ }\r
+ }\r
+ if (status.IsExternal(GitPath))\r
+ {\r
+ arExtPaths.AddPath(GitPath);\r
+ m_bHasExternals = TRUE;\r
+ }\r
+ if ((!bEntryfromDifferentRepo)&&(status.IsInExternal(GitPath)))\r
+ {\r
+ // if the externals are inside an unversioned folder (this happens if\r
+ // the externals are specified with e.g. "ext\folder url" instead of just\r
+ // "folder url"), then a commit won't succeed.\r
+ // therefore, we treat those as if the externals come from a different\r
+ // repository\r
+ CTGitPath extpath = GitPath;\r
+ while (basePath.IsAncestorOf(extpath))\r
+ {\r
+ if (!extpath.HasAdminDir())\r
+ {\r
+ bEntryfromDifferentRepo = true;\r
+ break;\r
+ }\r
+ extpath = extpath.GetContainingDirectory();\r
+ }\r
+ }\r
+ // Do we have any external paths?\r
+ if(arExtPaths.GetCount() > 0)\r
+ {\r
+ // If do have external paths, we need to check if the current item belongs\r
+ // to one of them\r
+ for (int ix=0; ix<arExtPaths.GetCount(); ix++)\r
+ {\r
+ if (arExtPaths[ix].IsAncestorOf(GitPath))\r
+ {\r
+ bDirectoryIsExternal = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if ((wcFileStatus == Git_wc_status_unversioned)&&(!bDirectoryIsExternal))\r
+ m_bHasUnversionedItems = TRUE;\r
+\r
+ const FileEntry* entry = AddNewFileEntry(s, GitPath, basePath, bAllDirect, bDirectoryIsExternal, bEntryfromDifferentRepo);\r
+\r
+ bool bMatchIgnore = !!config->MatchIgnorePattern(entry->path.GetFileOrDirectoryName());\r
+ bMatchIgnore = bMatchIgnore || config->MatchIgnorePattern(entry->path.GetGitPathString());\r
+ if ((((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none))&&(!bMatchIgnore))||\r
+ ((wcFileStatus == Git_wc_status_ignored)&&(m_bShowIgnores))||\r
+ (((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none))&&(bMatchIgnore)&&(m_bShowIgnores)))\r
+ {\r
+ if (entry->isfolder)\r
+ {\r
+ // we have an unversioned folder -> get all files in it recursively!\r
+ AddUnversionedFolder(GitPath, basePath, config);\r
+ }\r
+ }\r
+ } // while ((s = status.GetNextFileStatus(GitPath)) != NULL) \r
+#endif\r
+}\r
+\r
+// Get the show-flags bitmap value which corresponds to a particular Git status\r
+DWORD CGitStatusListCtrl::GetShowFlagsFromGitStatus(git_wc_status_kind status)\r
+{\r
+ switch (status)\r
+ {\r
+ case git_wc_status_none:\r
+ case git_wc_status_unversioned:\r
+ return SVNSLC_SHOWUNVERSIONED;\r
+ case git_wc_status_ignored:\r
+ if (!m_bShowIgnores)\r
+ return SVNSLC_SHOWDIRECTS;\r
+ return SVNSLC_SHOWDIRECTS|SVNSLC_SHOWIGNORED;\r
+ case git_wc_status_incomplete:\r
+ return SVNSLC_SHOWINCOMPLETE;\r
+ case git_wc_status_normal:\r
+ return SVNSLC_SHOWNORMAL;\r
+ case git_wc_status_external:\r
+ return SVNSLC_SHOWEXTERNAL;\r
+ case git_wc_status_added:\r
+ return SVNSLC_SHOWADDED;\r
+ case git_wc_status_missing:\r
+ return SVNSLC_SHOWMISSING;\r
+ case git_wc_status_deleted:\r
+ return SVNSLC_SHOWREMOVED;\r
+ case git_wc_status_replaced:\r
+ return SVNSLC_SHOWREPLACED;\r
+ case git_wc_status_modified:\r
+ return SVNSLC_SHOWMODIFIED;\r
+ case git_wc_status_merged:\r
+ return SVNSLC_SHOWMERGED;\r
+ case git_wc_status_conflicted:\r
+ return SVNSLC_SHOWCONFLICTED;\r
+ case git_wc_status_obstructed:\r
+ return SVNSLC_SHOWOBSTRUCTED;\r
+ default:\r
+ // we should NEVER get here!\r
+ ASSERT(FALSE);\r
+ break;\r
+ }\r
+ return 0;\r
+}\r
+\r
+void CGitStatusListCtrl::Show(DWORD dwShow, DWORD dwCheck /*=0*/, bool bShowFolders /* = true */)\r
+{\r
+#if 0\r
+ Locker lock(m_critSec);\r
+ WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), GetUserDefaultLangID());\r
+\r
+ CWinApp * pApp = AfxGetApp();\r
+ if (pApp)\r
+ pApp->DoWaitCursor(1);\r
+ m_dwShow = dwShow;\r
+ m_bShowFolders = bShowFolders;\r
+ m_nSelected = 0;\r
+ int nTopIndex = GetTopIndex();\r
+ POSITION posSelectedEntry = GetFirstSelectedItemPosition();\r
+ int nSelectedEntry = 0;\r
+ if (posSelectedEntry)\r
+ nSelectedEntry = GetNextSelectedItem(posSelectedEntry);\r
+ SetRedraw(FALSE);\r
+ DeleteAllItems();\r
+\r
+ PrepareGroups();\r
+\r
+ m_arListArray.clear();\r
+\r
+ m_arListArray.reserve(m_arStatusArray.size());\r
+ SetItemCount (static_cast<int>(m_arStatusArray.size()));\r
+\r
+ int listIndex = 0;\r
+ for (size_t i=0; i < m_arStatusArray.size(); ++i)\r
+ {\r
+ FileEntry * entry = m_arStatusArray[i];\r
+ if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))\r
+ continue;\r
+ if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))\r
+ continue;\r
+ if (entry->IsFolder() && (!bShowFolders))\r
+ continue; // don't show folders if they're not wanted.\r
+ git_wc_status_kind status = GitStatus::GetMoreImportant(entry->status, entry->remotestatus);\r
+ DWORD showFlags = GetShowFlagsFromGitStatus(status);\r
+ if (entry->IsLocked())\r
+ showFlags |= SVNSLC_SHOWLOCKS;\r
+ if (entry->switched)\r
+ showFlags |= SVNSLC_SHOWSWITCHED;\r
+ if (!entry->changelist.IsEmpty())\r
+ showFlags |= SVNSLC_SHOWINCHANGELIST;\r
+\r
+ bool bAllowCheck = ((entry->changelist.Compare(SVNSLC_IGNORECHANGELIST) != 0) && (m_bCheckIfGroupsExist || (m_changelists.size()==0 || (m_changelists.size()==1 && m_bHasIgnoreGroup))));\r
+\r
+ // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item\r
+ if (status != Git_wc_status_ignored || (entry->direct) || (dwShow & GitSLC_SHOWIGNORED))\r
+ {\r
+ if ((!entry->IsFolder()) && (status == Git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))\r
+ {\r
+ if (PathFileExists(entry->GetPath().GetWinPath()))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ if ((dwCheck & SVNSLC_SHOWREMOVEDANDPRESENT)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
+ {\r
+ if (bAllowCheck)\r
+ entry->checked = true;\r
+ }\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ }\r
+ else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
+ {\r
+ if (bAllowCheck)\r
+ entry->checked = true;\r
+ }\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
+ {\r
+ if (bAllowCheck)\r
+ entry->checked = true;\r
+ }\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ }\r
+ }\r
+\r
+ SetItemCount(listIndex);\r
+\r
+ m_ColumnManager.UpdateRelevance (m_arStatusArray, m_arListArray);\r
+\r
+ int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
+ for (int col = 0; col <= maxcol; col++)\r
+ SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));\r
+\r
+ SetRedraw(TRUE);\r
+ GetStatisticsString();\r
+\r
+ CHeaderCtrl * pHeader = GetHeaderCtrl();\r
+ HDITEM HeaderItem = {0};\r
+ HeaderItem.mask = HDI_FORMAT;\r
+ for (int i=0; i<pHeader->GetItemCount(); ++i)\r
+ {\r
+ pHeader->GetItem(i, &HeaderItem);\r
+ HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
+ pHeader->SetItem(i, &HeaderItem);\r
+ }\r
+ if (m_nSortedColumn)\r
+ {\r
+ pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
+ HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
+ pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
+ }\r
+\r
+ if (nSelectedEntry)\r
+ {\r
+ SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);\r
+ EnsureVisible(nSelectedEntry, false);\r
+ }\r
+ else\r
+ {\r
+ // Restore the item at the top of the list.\r
+ for (int i=0;GetTopIndex() != nTopIndex;i++)\r
+ {\r
+ if ( !EnsureVisible(nTopIndex+i,false) )\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (pApp)\r
+ pApp->DoWaitCursor(-1);\r
+\r
+ m_bEmpty = (GetItemCount() == 0);\r
+ Invalidate();\r
+#endif\r
+}\r
+\r
+void CGitStatusListCtrl::Show(DWORD dwShow, const CTGitPathList& checkedList, bool bShowFolders /* = true */)\r
+{\r
+#if 0\r
+ Locker lock(m_critSec);\r
+ WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), GetUserDefaultLangID());\r
+\r
+ CWinApp * pApp = AfxGetApp();\r
+ if (pApp)\r
+ pApp->DoWaitCursor(1);\r
+ m_dwShow = dwShow;\r
+ m_bShowFolders = bShowFolders;\r
+ m_nSelected = 0;\r
+ int nTopIndex = GetTopIndex();\r
+ POSITION posSelectedEntry = GetFirstSelectedItemPosition();\r
+ int nSelectedEntry = 0;\r
+ if (posSelectedEntry)\r
+ nSelectedEntry = GetNextSelectedItem(posSelectedEntry);\r
+ SetRedraw(FALSE);\r
+ DeleteAllItems();\r
+\r
+ PrepareGroups();\r
+\r
+ m_arListArray.clear();\r
+\r
+ m_arListArray.reserve(m_arStatusArray.size());\r
+ SetItemCount (static_cast<int>(m_arStatusArray.size()));\r
+\r
+ int listIndex = 0;\r
+ for (size_t i=0; i < m_arStatusArray.size(); ++i)\r
+ {\r
+ FileEntry * entry = m_arStatusArray[i];\r
+ if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))\r
+ continue;\r
+ if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))\r
+ continue;\r
+ if (entry->IsFolder() && (!bShowFolders))\r
+ continue; // don't show folders if they're not wanted.\r
+ git_wc_status_kind status = SVNStatus::GetMoreImportant(entry->status, entry->remotestatus);\r
+ DWORD showFlags = GetShowFlagsFromSVNStatus(status);\r
+ if (entry->IsLocked())\r
+ showFlags |= SVNSLC_SHOWLOCKS;\r
+ if (!entry->changelist.IsEmpty())\r
+ showFlags |= SVNSLC_SHOWINCHANGELIST;\r
+\r
+ // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item\r
+ if (status != git_wc_status_ignored || (entry->direct) || (dwShow & SVNSLC_SHOWIGNORED))\r
+ {\r
+ for (int npath = 0; npath < checkedList.GetCount(); ++npath)\r
+ {\r
+ if (entry->GetPath().IsEquivalentTo(checkedList[npath]))\r
+ {\r
+ entry->checked = true;\r
+ break;\r
+ }\r
+ }\r
+ if ((!entry->IsFolder()) && (status == git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))\r
+ {\r
+ if (PathFileExists(entry->GetPath().GetWinPath()))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ }\r
+ else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))\r
+ {\r
+ m_arListArray.push_back(i);\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ else if (entry->switched)\r
+ {\r
+ m_arListArray.push_back(i);\r
+ AddEntry(entry, langID, listIndex++);\r
+ }\r
+ }\r
+ }\r
+\r
+ SetItemCount(listIndex);\r
+\r
+ int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
+ for (int col = 0; col <= maxcol; col++)\r
+ SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));\r
+\r
+ SetRedraw(TRUE);\r
+ GetStatisticsString();\r
+\r
+ CHeaderCtrl * pHeader = GetHeaderCtrl();\r
+ HDITEM HeaderItem = {0};\r
+ HeaderItem.mask = HDI_FORMAT;\r
+ for (int i=0; i<pHeader->GetItemCount(); ++i)\r
+ {\r
+ pHeader->GetItem(i, &HeaderItem);\r
+ HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
+ pHeader->SetItem(i, &HeaderItem);\r
+ }\r
+ if (m_nSortedColumn)\r
+ {\r
+ pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
+ HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
+ pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
+ }\r
+\r
+ if (nSelectedEntry)\r
+ {\r
+ SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);\r
+ EnsureVisible(nSelectedEntry, false);\r
+ }\r
+ else\r
+ {\r
+ // Restore the item at the top of the list.\r
+ for (int i=0;GetTopIndex() != nTopIndex;i++)\r
+ {\r
+ if ( !EnsureVisible(nTopIndex+i,false) )\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (pApp)\r
+ pApp->DoWaitCursor(-1);\r
+\r
+ m_bEmpty = (GetItemCount() == 0);\r
+ Invalidate();\r
+#endif\r
+}\r
+\r
+void CGitStatusListCtrl::AddEntry(FileEntry * entry, WORD langID, int listIndex)\r
+{\r
+ static CString ponly(MAKEINTRESOURCE(IDS_STATUSLIST_PROPONLY));\r
+ static HINSTANCE hResourceHandle(AfxGetResourceHandle());\r
+\r
+ CString path = entry->GetPath().GetGitPathString();\r
+ if ( m_mapFilenameToChecked.size()!=0 && m_mapFilenameToChecked.find(path) != m_mapFilenameToChecked.end() )\r
+ {\r
+ // The user manually de-/selected an item. We now restore this status\r
+ // when refreshing.\r
+ entry->checked = m_mapFilenameToChecked[path];\r
+ }\r
+\r
+ m_bBlock = TRUE;\r
+ TCHAR buf[100];\r
+ int index = listIndex;\r
+ int nCol = 1;\r
+ CString entryname = entry->GetDisplayName();\r
+ int icon_idx = 0;\r
+ if (entry->isfolder)\r
+ icon_idx = m_nIconFolder;\r
+ else\r
+ {\r
+ icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(entry->path);\r
+ }\r
+ // relative path\r
+ InsertItem(index, entryname, icon_idx);\r
+ // SVNSLC_COLFILENAME\r
+ SetItemText(index, nCol++, entry->path.GetFileOrDirectoryName());\r
+ // SVNSLC_COLEXT\r
+ SetItemText(index, nCol++, entry->path.GetFileExtension());\r
+ // SVNSLC_COLSTATUS\r
+ if (entry->isNested)\r
+ {\r
+ CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
+ SetItemText(index, nCol++, sTemp);\r
+ }\r
+ else\r
+ {\r
+ GitStatus::GetStatusString(hResourceHandle, entry->status, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ if ((entry->copied)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (+)"));\r
+ if ((entry->switched)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (s)"));\r
+#if 0\r
+ if ((entry->status == entry->propstatus)&&\r
+ (entry->status != git_wc_status_normal)&&\r
+ (entry->status != git_wc_status_unversioned)&&\r
+ (!GitStatus::IsImportant(entry->textstatus)))\r
+ _tcscat_s(buf, 100, ponly);\r
+#endif\r
+ SetItemText(index, nCol++, buf);\r
+ }\r
+ // SVNSLC_COLREMOTESTATUS\r
+ if (entry->isNested)\r
+ {\r
+ CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
+ SetItemText(index, nCol++, sTemp);\r
+ }\r
+ else\r
+ {\r
+#if 0\r
+ SVNStatus::GetStatusString(hResourceHandle, entry->remotestatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ if ((entry->copied)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (+)"));\r
+ if ((entry->switched)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (s)"));\r
+ if ((entry->remotestatus == entry->remotepropstatus)&&\r
+ (entry->remotestatus != git_wc_status_none)&&\r
+ (entry->remotestatus != git_wc_status_normal)&&\r
+ (entry->remotestatus != git_wc_status_unversioned)&&\r
+ (!SVNStatus::IsImportant(entry->remotetextstatus)))\r
+ _tcscat_s(buf, 100, ponly);\r
+#endif\r
+ SetItemText(index, nCol++, buf);\r
+ }\r
+ // SVNSLC_COLTEXTSTATUS\r
+ if (entry->isNested)\r
+ {\r
+ CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
+ SetItemText(index, nCol++, sTemp);\r
+ }\r
+ else\r
+ {\r
+#if 0\r
+ SVNStatus::GetStatusString(hResourceHandle, entry->textstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ if ((entry->copied)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (+)"));\r
+ if ((entry->switched)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (s)"));\r
+#endif\r
+ SetItemText(index, nCol++, buf);\r
+ }\r
+ // SVNSLC_COLPROPSTATUS\r
+ if (entry->isNested)\r
+ {\r
+ SetItemText(index, nCol++, _T(""));\r
+ }\r
+ else\r
+ {\r
+#if 0\r
+ SVNStatus::GetStatusString(hResourceHandle, entry->propstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ if ((entry->copied)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (+)"));\r
+ if ((entry->switched)&&(_tcslen(buf)>1))\r
+ _tcscat_s(buf, 100, _T(" (s)"));\r
+#endif\r
+ SetItemText(index, nCol++, buf);\r
+ }\r
+ // SVNSLC_COLREMOTETEXT\r
+ if (entry->isNested)\r
+ {\r
+ SetItemText(index, nCol++, _T(""));\r
+ }\r
+ else\r
+ {\r
+#if 0\r
+ SVNStatus::GetStatusString(hResourceHandle, entry->remotetextstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ SetItemText(index, nCol++, buf);\r
+#endif\r
+ }\r
+ // SVNSLC_COLREMOTEPROP\r
+ if (entry->isNested)\r
+ {\r
+ SetItemText(index, nCol++, _T(""));\r
+ }\r
+ else\r
+ {\r
+// SVNStatus::GetStatusString(hResourceHandle, entry->remotepropstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
+ SetItemText(index, nCol++, buf);\r
+ }\r
+ // SVNSLC_COLURL\r
+// SetItemText(index, nCol++, entry->url);\r
+ // SVNSLC_COLLOCK\r
+#if 0\r
+ if (!m_HeadRev.IsHead())\r
+ {\r
+ // we have contacted the repository\r
+\r
+ // decision-matrix\r
+ // wc repository text\r
+ // "" "" ""\r
+ // "" UID1 owner\r
+ // UID1 UID1 owner\r
+ // UID1 "" lock has been broken\r
+ // UID1 UID2 lock has been stolen\r
+ if (entry->lock_token.IsEmpty() || (entry->lock_token.Compare(entry->lock_remotetoken)==0))\r
+ {\r
+ if (entry->lock_owner.IsEmpty())\r
+ SetItemText(index, nCol++, entry->lock_remoteowner);\r
+ else\r
+ SetItemText(index, nCol++, entry->lock_owner);\r
+ }\r
+ else if (entry->lock_remotetoken.IsEmpty())\r
+ {\r
+ // broken lock\r
+ CString temp(MAKEINTRESOURCE(IDS_STATUSLIST_LOCKBROKEN));\r
+ SetItemText(index, nCol++, temp);\r
+ }\r
+ else\r
+ {\r
+ // stolen lock\r
+ CString temp;\r
+ temp.Format(IDS_STATUSLIST_LOCKSTOLEN, (LPCTSTR)entry->lock_remoteowner);\r
+ SetItemText(index, nCol++, temp);\r
+ }\r
+ }\r
+ else\r
+ SetItemText(index, nCol++, entry->lock_owner);\r
+ // SVNSLC_COLLOCKCOMMENT\r
+ SetItemText(index, nCol++, entry->lock_comment);\r
+ // SVNSLC_COLAUTHOR\r
+ SetItemText(index, nCol++, entry->last_commit_author);\r
+ // SVNSLC_COLREVISION\r
+ CString temp;\r
+ temp.Format(_T("%ld"), entry->last_commit_rev);\r
+ if (entry->last_commit_rev > 0)\r
+ SetItemText(index, nCol++, temp);\r
+ else\r
+ SetItemText(index, nCol++, _T(""));\r
+ // SVNSLC_COLREMOTEREVISION\r
+ temp.Format(_T("%ld"), entry->remoterev);\r
+ if (entry->remoterev > 0)\r
+ SetItemText(index, nCol++, temp);\r
+ else\r
+ SetItemText(index, nCol++, _T(""));\r
+ // SVNSLC_COLDATE\r
+ TCHAR datebuf[SVN_DATE_BUFFER];\r
+ apr_time_t date = entry->last_commit_date;\r
+ SVN::formatDate(datebuf, date, true);\r
+ if (date)\r
+ SetItemText(index, nCol++, datebuf);\r
+ else\r
+ SetItemText(index, nCol++, _T(""));\r
+ // SVNSLC_COLSVNNEEDSLOCK\r
+ BOOL bFoundSVNNeedsLock = entry->present_props.IsNeedsLockSet();\r
+ CString strSVNNeedsLock = (bFoundSVNNeedsLock) ? _T("*") : _T("");\r
+ SetItemText(index, nCol++, strSVNNeedsLock);\r
+ // SVNSLC_COLCOPYFROM\r
+ if (m_sURL.Compare(entry->copyfrom_url.Left(m_sURL.GetLength()))==0)\r
+ temp = entry->copyfrom_url.Mid(m_sURL.GetLength());\r
+ else\r
+ temp = entry->copyfrom_url;\r
+ SetItemText(index, nCol++, temp);\r
+ // SVNSLC_COLMODIFICATIONDATE\r
+ __int64 filetime = entry->GetPath().GetLastWriteTime();\r
+ if ( (filetime) && (entry->status!=git_wc_status_deleted) )\r
+ {\r
+ FILETIME* f = (FILETIME*)(__int64*)&filetime;\r
+ TCHAR datebuf[SVN_DATE_BUFFER];\r
+ SVN::formatDate(datebuf,*f,true);\r
+ SetItemText(index, nCol++, datebuf);\r
+ }\r
+ else\r
+ {\r
+ SetItemText(index, nCol++, _T(""));\r
+ }\r
+\r
+ // user-defined properties\r
+ for ( int i = SVNSLC_NUMCOLUMNS, count = m_ColumnManager.GetColumnCount()\r
+ ; i < count\r
+ ; ++i)\r
+ {\r
+ assert (i == nCol++);\r
+ assert (m_ColumnManager.IsUserProp (i));\r
+\r
+ CString name = m_ColumnManager.GetName(i);\r
+ if (entry->present_props.HasProperty (name))\r
+ {\r
+ const CString& propVal = entry->present_props [name];\r
+ if (propVal.IsEmpty())\r
+ SetItemText(index, i, m_sNoPropValueText);\r
+ else\r
+ SetItemText(index, i, propVal);\r
+ }\r
+ else\r
+ SetItemText(index, i, _T(""));\r
+ }\r
+\r
+ SetCheck(index, entry->checked);\r
+ if (entry->checked)\r
+ m_nSelected++;\r
+ if (m_changelists.find(entry->changelist) != m_changelists.end())\r
+ SetItemGroup(index, m_changelists[entry->changelist]);\r
+ else\r
+ SetItemGroup(index, 0);\r
+ m_bBlock = FALSE;\r
+#endif\r
+}\r
+\r
+bool CGitStatusListCtrl::SetItemGroup(int item, int groupindex)\r
+{\r
+ if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS) == NULL)\r
+ return false;\r
+ if (groupindex < 0)\r
+ return false;\r
+ LVITEM i = {0};\r
+ i.mask = LVIF_GROUPID;\r
+ i.iItem = item;\r
+ i.iSubItem = 0;\r
+ i.iGroupId = groupindex;\r
+\r
+ return !!SetItem(&i);\r
+}\r
+\r
+void CGitStatusListCtrl::Sort()\r
+{\r
+ Locker lock(m_critSec);\r
+\r
+ CSorter predicate (&m_ColumnManager, m_nSortedColumn, m_bAscending);\r
+\r
+ std::sort(m_arStatusArray.begin(), m_arStatusArray.end(), predicate);\r
+ SaveColumnWidths();\r
+ Show(m_dwShow, 0, m_bShowFolders);\r
+}\r
+\r
+void CGitStatusListCtrl::OnHdnItemclick(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+ LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
+ *pResult = 0;\r
+ if (m_bBlock)\r
+ return;\r
+ m_bBlock = TRUE;\r
+ if (m_nSortedColumn == phdr->iItem)\r
+ m_bAscending = !m_bAscending;\r
+ else\r
+ m_bAscending = TRUE;\r
+ m_nSortedColumn = phdr->iItem;\r
+ m_mapFilenameToChecked.clear();\r
+ Sort();\r
+\r
+ CHeaderCtrl * pHeader = GetHeaderCtrl();\r
+ HDITEM HeaderItem = {0};\r
+ HeaderItem.mask = HDI_FORMAT;\r
+ for (int i=0; i<pHeader->GetItemCount(); ++i)\r
+ {\r
+ pHeader->GetItem(i, &HeaderItem);\r
+ HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
+ pHeader->SetItem(i, &HeaderItem);\r
+ }\r
+ pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
+ HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
+ pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
+\r
+ // the checked state of the list control items must be restored\r
+ for (int i=0; i<GetItemCount(); ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ SetCheck(i, entry->IsChecked());\r
+ }\r
+\r
+ m_bBlock = FALSE;\r
+}\r
+\r
+void CGitStatusListCtrl::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
+ *pResult = 0;\r
+\r
+#define ISCHECKED(x) ((x) ? ((((x)&LVIS_STATEIMAGEMASK)>>12)-1) : FALSE)\r
+ if ((m_bBlock)&&(m_bBlockUI))\r
+ {\r
+ // if we're blocked, prevent changing of the check state\r
+ if ((!ISCHECKED(pNMLV->uOldState) && ISCHECKED(pNMLV->uNewState))||\r
+ (ISCHECKED(pNMLV->uOldState) && !ISCHECKED(pNMLV->uNewState)))\r
+ *pResult = TRUE;\r
+ }\r
+}\r
+\r
+BOOL CGitStatusListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
+ *pResult = 0;\r
+ if ((pNMLV->uNewState==0)||(pNMLV->uNewState & LVIS_SELECTED)||(pNMLV->uNewState & LVIS_FOCUSED))\r
+ return FALSE;\r
+\r
+ if (m_bBlock)\r
+ return FALSE;\r
+\r
+ bool bSelected = !!(ListView_GetItemState(m_hWnd, pNMLV->iItem, LVIS_SELECTED) & LVIS_SELECTED);\r
+ int nListItems = GetItemCount();\r
+\r
+ m_bBlock = TRUE;\r
+ // was the item checked?\r
+ if (GetCheck(pNMLV->iItem))\r
+ {\r
+ CheckEntry(pNMLV->iItem, nListItems);\r
+ if (bSelected)\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ if (index != pNMLV->iItem)\r
+ CheckEntry(index, nListItems);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ UncheckEntry(pNMLV->iItem, nListItems);\r
+ if (bSelected)\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ if (index != pNMLV->iItem)\r
+ UncheckEntry(index, nListItems);\r
+ }\r
+ }\r
+ }\r
+ GetStatisticsString();\r
+ m_bBlock = FALSE;\r
+ NotifyCheck();\r
+\r
+ return FALSE;\r
+}\r
+\r
+void CGitStatusListCtrl::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+ LPNMHEADER header = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
+ if ( (header != NULL) \r
+ && (header->iItem >= 0) \r
+ && (header->iItem < m_ColumnManager.GetColumnCount()))\r
+ {\r
+ m_ColumnManager.ColumnResized (header->iItem);\r
+ }\r
+\r
+ *pResult = FALSE;\r
+}\r
+\r
+void CGitStatusListCtrl::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+ LPNMHEADER header = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
+ *pResult = TRUE;\r
+ if ( (header != NULL) \r
+ && (header->iItem >= 0) \r
+ && (header->iItem < m_ColumnManager.GetColumnCount())\r
+ // only allow the reordering if the column was not moved left of the first\r
+ // visible item - otherwise the 'invisible' columns are not at the far left\r
+ // anymore and we get all kinds of redrawing problems.\r
+ && (header->pitem)\r
+ && (header->pitem->iOrder > m_ColumnManager.GetInvisibleCount()))\r
+ {\r
+ m_ColumnManager.ColumnMoved (header->iItem, header->pitem->iOrder);\r
+ *pResult = FALSE;\r
+ }\r
+\r
+ Invalidate(FALSE);\r
+}\r
+\r
+void CGitStatusListCtrl::CheckEntry(int index, int nListItems)\r
+{\r
+ Locker lock(m_critSec);\r
+ FileEntry * entry = GetListEntry(index);\r
+ ASSERT(entry != NULL);\r
+ if (entry == NULL)\r
+ return;\r
+ SetCheck(index, TRUE);\r
+ entry = GetListEntry(index);\r
+ // if an unversioned item was checked, then we need to check if\r
+ // the parent folders are unversioned too. If the parent folders actually\r
+ // are unversioned, then check those too.\r
+ if (entry->status == git_wc_status_unversioned)\r
+ {\r
+ // we need to check the parent folder too\r
+ const CTGitPath& folderpath = entry->path;\r
+ for (int i=0; i< nListItems; ++i)\r
+ {\r
+ FileEntry * testEntry = GetListEntry(i);\r
+ ASSERT(testEntry != NULL);\r
+ if (testEntry == NULL)\r
+ continue;\r
+ if (!testEntry->checked)\r
+ {\r
+ if (testEntry->path.IsAncestorOf(folderpath) && (!testEntry->path.IsEquivalentTo(folderpath)))\r
+ {\r
+ SetEntryCheck(testEntry,i,true);\r
+ m_nSelected++;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
+ if ( (entry->status == git_wc_status_deleted) || (m_bCheckChildrenWithParent) || (bShift) )\r
+ {\r
+ // if a deleted folder gets checked, we have to check all\r
+ // children of that folder too.\r
+ if (entry->path.IsDirectory())\r
+ {\r
+ SetCheckOnAllDescendentsOf(entry, true);\r
+ }\r
+\r
+ // if a deleted file or folder gets checked, we have to\r
+ // check all parents of this item too.\r
+ for (int i=0; i<nListItems; ++i)\r
+ {\r
+ FileEntry * testEntry = GetListEntry(i);\r
+ ASSERT(testEntry != NULL);\r
+ if (testEntry == NULL)\r
+ continue;\r
+ if (!testEntry->checked)\r
+ {\r
+ if (testEntry->path.IsAncestorOf(entry->path) && (!testEntry->path.IsEquivalentTo(entry->path)))\r
+ {\r
+ if ((testEntry->status == git_wc_status_deleted)||(m_bCheckChildrenWithParent))\r
+ {\r
+ SetEntryCheck(testEntry,i,true);\r
+ m_nSelected++;\r
+ // now we need to check all children of this parent folder\r
+ SetCheckOnAllDescendentsOf(testEntry, true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if ( !entry->checked )\r
+ {\r
+ entry->checked = TRUE;\r
+ m_nSelected++;\r
+ }\r
+}\r
+\r
+void CGitStatusListCtrl::UncheckEntry(int index, int nListItems)\r
+{\r
+ Locker lock(m_critSec);\r
+ FileEntry * entry = GetListEntry(index);\r
+ ASSERT(entry != NULL);\r
+ if (entry == NULL)\r
+ return;\r
+ SetCheck(index, FALSE);\r
+ entry = GetListEntry(index);\r
+ // item was unchecked\r
+ if (entry->path.IsDirectory())\r
+ {\r
+ // disable all files within an unselected folder, except when unchecking a folder with property changes\r
+ bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
+ if ( (entry->status != git_wc_status_modified) || (bShift) )\r
+ {\r
+ SetCheckOnAllDescendentsOf(entry, false);\r
+ }\r
+ }\r
+ else if (entry->status == git_wc_status_deleted)\r
+ {\r
+ // a "deleted" file was unchecked, so uncheck all parent folders\r
+ // and all children of those parents\r
+ for (int i=0; i<nListItems; i++)\r
+ {\r
+ FileEntry * testEntry = GetListEntry(i);\r
+ ASSERT(testEntry != NULL);\r
+ if (testEntry == NULL)\r
+ continue;\r
+ if (testEntry->checked)\r
+ {\r
+ if (testEntry->path.IsAncestorOf(entry->path))\r
+ {\r
+ if (testEntry->status == git_wc_status_deleted)\r
+ {\r
+ SetEntryCheck(testEntry,i,false);\r
+ m_nSelected--;\r
+\r
+ SetCheckOnAllDescendentsOf(testEntry, false);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if ( entry->checked )\r
+ {\r
+ entry->checked = FALSE;\r
+ m_nSelected--;\r
+ }\r
+}\r
+\r
+bool CGitStatusListCtrl::EntryPathCompareNoCase(const FileEntry* pEntry1, const FileEntry* pEntry2)\r
+{\r
+ return pEntry1->path < pEntry2->path;\r
+}\r
+\r
+bool CGitStatusListCtrl::IsEntryVersioned(const FileEntry* pEntry1)\r
+{\r
+ return pEntry1->status != git_wc_status_unversioned;\r
+}\r
+\r
+bool CGitStatusListCtrl::BuildStatistics()\r
+{\r
+ bool bRefetchStatus = false;\r
+ FileEntryVector::iterator itFirstUnversionedEntry;\r
+ itFirstUnversionedEntry = std::partition(m_arStatusArray.begin(), m_arStatusArray.end(), IsEntryVersioned);\r
+ if (m_bUnversionedLast)\r
+ {\r
+ // We partition the list of items so that it's arrange with all the versioned items first\r
+ // then all the unversioned items afterwards.\r
+ // Then we sort the versioned part of this, so that we can do quick look-ups in it\r
+ std::sort(m_arStatusArray.begin(), itFirstUnversionedEntry, EntryPathCompareNoCase);\r
+ // Also sort the unversioned section, to make the list look nice...\r
+ std::sort(itFirstUnversionedEntry, m_arStatusArray.end(), EntryPathCompareNoCase);\r
+ }\r
+\r
+ // now gather some statistics\r
+ m_nUnversioned = 0;\r
+ m_nNormal = 0;\r
+ m_nModified = 0;\r
+ m_nAdded = 0;\r
+ m_nDeleted = 0;\r
+ m_nConflicted = 0;\r
+ m_nTotal = 0;\r
+ m_nSelected = 0;\r
+ for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
+ {\r
+ const FileEntry * entry = m_arStatusArray[i];\r
+ if (entry)\r
+ {\r
+ switch (entry->status)\r
+ {\r
+ case git_wc_status_normal:\r
+ m_nNormal++;\r
+ break;\r
+ case git_wc_status_added:\r
+ m_nAdded++;\r
+ break;\r
+ case git_wc_status_missing:\r
+ case git_wc_status_deleted:\r
+ m_nDeleted++;\r
+ break;\r
+ case git_wc_status_replaced:\r
+ case git_wc_status_modified:\r
+ case git_wc_status_merged:\r
+ m_nModified++;\r
+ break;\r
+ case git_wc_status_conflicted:\r
+ case git_wc_status_obstructed:\r
+ m_nConflicted++;\r
+ break;\r
+ case git_wc_status_ignored:\r
+ m_nUnversioned++;\r
+ break;\r
+ default:\r
+#if 0\r
+ {\r
+ if (GitStatus::IsImportant(entry->remotestatus))\r
+ break;\r
+ m_nUnversioned++;\r
+ // If an entry is in an unversioned folder, we don't have to do an expensive array search\r
+ // to find out if it got case-renamed: an unversioned folder can't have versioned files\r
+ // But nested folders are also considered to be in unversioned folders, we have to do the\r
+ // check in that case too, otherwise we would miss case-renamed folders - they show up\r
+ // as nested folders.\r
+ if (((!entry->inunversionedfolder)||(entry->isNested))&&(m_bUnversionedLast))\r
+ {\r
+ // check if the unversioned item is just\r
+ // a file differing in case but still versioned\r
+ FileEntryVector::iterator itMatchingItem;\r
+ if(std::binary_search(m_arStatusArray.begin(), itFirstUnversionedEntry, entry, EntryPathCompareNoCase))\r
+ {\r
+ // We've confirmed that there *is* a matching file\r
+ // Find its exact location\r
+ FileEntryVector::iterator itMatchingItem;\r
+ itMatchingItem = std::lower_bound(m_arStatusArray.begin(), itFirstUnversionedEntry, entry, EntryPathCompareNoCase);\r
+\r
+ // adjust the case of the filename\r
+ if (MoveFileEx(entry->path.GetWinPath(), (*itMatchingItem)->path.GetWinPath(), MOVEFILE_REPLACE_EXISTING))\r
+ {\r
+ // We successfully adjusted the case in the filename. But there is now a file with status 'missing'\r
+ // in the array, because that's the status of the file before we adjusted the case.\r
+ // We have to refetch the status of that file.\r
+ // Since fetching the status of single files/directories is very expensive and there can be\r
+ // multiple case-renames here, we just set a flag and refetch the status at the end from scratch.\r
+ bRefetchStatus = true;\r
+ DeleteItem(i);\r
+ m_arStatusArray.erase(m_arStatusArray.begin()+i);\r
+ delete entry;\r
+ i--;\r
+ m_nUnversioned--;\r
+ // now that we removed an unversioned item from the array, find the first unversioned item in the 'new'\r
+ // list again.\r
+ itFirstUnversionedEntry = std::partition(m_arStatusArray.begin(), m_arStatusArray.end(), IsEntryVersioned);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+#endif\r
+ break;\r
+ } // switch (entry->status)\r
+ } // if (entry)\r
+ } // for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
+ return !bRefetchStatus;\r
+}\r
+\r
+void CGitStatusListCtrl::GetMinMaxRevisions(git_revnum_t& rMin, git_revnum_t& rMax, bool bShownOnly, bool bCheckedOnly)\r
+{\r
+#if 0\r
+ Locker lock(m_critSec);\r
+ rMin = LONG_MAX;\r
+ rMax = 0;\r
+\r
+ if ((bShownOnly)||(bCheckedOnly))\r
+ {\r
+ for (int i=0; i<GetItemCount(); ++i)\r
+ {\r
+ const FileEntry * entry = GetListEntry(i);\r
+\r
+ if ((entry)&&(entry->last_commit_rev))\r
+ {\r
+ if ((!bCheckedOnly)||(entry->IsChecked()))\r
+ {\r
+ if (entry->last_commit_rev >= 0)\r
+ {\r
+ rMin = min(rMin, entry->last_commit_rev);\r
+ rMax = max(rMax, entry->last_commit_rev);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
+ {\r
+ const FileEntry * entry = m_arStatusArray[i];\r
+ if ((entry)&&(entry->last_commit_rev))\r
+ {\r
+ if (entry->last_commit_rev >= 0)\r
+ {\r
+ rMin = min(rMin, entry->last_commit_rev);\r
+ rMax = max(rMax, entry->last_commit_rev);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (rMin == LONG_MAX)\r
+ rMin = 0;\r
+#endif\r
+}\r
+\r
+int CGitStatusListCtrl::GetGroupFromPoint(POINT * ppt)\r
+{\r
+ // the point must be relative to the upper left corner of the control\r
+\r
+ if (ppt == NULL)\r
+ return -1;\r
+ if (!IsGroupViewEnabled())\r
+ return -1;\r
+\r
+ POINT pt = *ppt;\r
+ pt.x = 10;\r
+ UINT flags = 0;\r
+ int nItem = -1;\r
+ RECT rc;\r
+ GetWindowRect(&rc);\r
+ while (((flags & LVHT_BELOW) == 0)&&(pt.y < rc.bottom))\r
+ {\r
+ nItem = HitTest(pt, &flags);\r
+ if ((flags & LVHT_ONITEM)||(flags & LVHT_EX_GROUP_HEADER))\r
+ {\r
+ // the first item below the point\r
+\r
+ // check if the point is too much right (i.e. if the point\r
+ // is farther to the right than the width of the item)\r
+ RECT r;\r
+ GetItemRect(nItem, &r, LVIR_LABEL);\r
+ if (ppt->x > r.right)\r
+ return -1;\r
+\r
+ LVITEM lv = {0};\r
+ lv.mask = LVIF_GROUPID;\r
+ lv.iItem = nItem;\r
+ GetItem(&lv);\r
+ int groupID = lv.iGroupId;\r
+ // now we search upwards and check if the item above this one\r
+ // belongs to another group. If it belongs to the same group,\r
+ // we're not over a group header\r
+ while (pt.y >= 0)\r
+ {\r
+ pt.y -= 2;\r
+ nItem = HitTest(pt, &flags);\r
+ if ((flags & LVHT_ONITEM)&&(nItem >= 0))\r
+ {\r
+ // the first item below the point\r
+ LVITEM lv = {0};\r
+ lv.mask = LVIF_GROUPID;\r
+ lv.iItem = nItem;\r
+ GetItem(&lv);\r
+ if (lv.iGroupId != groupID)\r
+ return groupID;\r
+ else\r
+ return -1;\r
+ }\r
+ }\r
+ if (pt.y < 0)\r
+ return groupID;\r
+ return -1;\r
+ }\r
+ pt.y += 2;\r
+ };\r
+ return -1;\r
+}\r
+\r
+void CGitStatusListCtrl::OnContextMenuGroup(CWnd * /*pWnd*/, CPoint point)\r
+{\r
+ POINT clientpoint = point;\r
+ ScreenToClient(&clientpoint);\r
+ if ((IsGroupViewEnabled())&&(GetGroupFromPoint(&clientpoint) >= 0))\r
+ {\r
+ CMenu popup;\r
+ if (popup.CreatePopupMenu())\r
+ {\r
+ CString temp;\r
+ temp.LoadString(IDS_STATUSLIST_CHECKGROUP);\r
+ popup.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CHECKGROUP, temp);\r
+ temp.LoadString(IDS_STATUSLIST_UNCHECKGROUP);\r
+ popup.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_UNCHECKGROUP, temp);\r
+ int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
+ bool bCheck = false;\r
+ switch (cmd)\r
+ {\r
+ case IDSVNLC_CHECKGROUP:\r
+ bCheck = true;\r
+ // fall through here\r
+ case IDSVNLC_UNCHECKGROUP:\r
+ {\r
+ int group = GetGroupFromPoint(&clientpoint);\r
+ // go through all items and check/uncheck those assigned to the group\r
+ // but block the OnLvnItemChanged handler\r
+ m_bBlock = true;\r
+ LVITEM lv;\r
+ for (int i=0; i<GetItemCount(); ++i)\r
+ {\r
+ SecureZeroMemory(&lv, sizeof(LVITEM));\r
+ lv.mask = LVIF_GROUPID;\r
+ lv.iItem = i;\r
+ GetItem(&lv);\r
+ if (lv.iGroupId == group)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry)\r
+ {\r
+ bool bOldCheck = !!GetCheck(i);\r
+ SetEntryCheck(entry, i, bCheck);\r
+ if (bCheck != bOldCheck)\r
+ {\r
+ if (bCheck)\r
+ m_nSelected++;\r
+ else\r
+ m_nSelected--;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ GetStatisticsString();\r
+ NotifyCheck();\r
+ m_bBlock = false;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+void CGitStatusListCtrl::OnContextMenuList(CWnd * pWnd, CPoint point)\r
+{\r
+#if 0\r
+ WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
+\r
+ bool XPorLater = false;\r
+ OSVERSIONINFOEX inf;\r
+ SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
+ inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
+ GetVersionEx((OSVERSIONINFO *)&inf);\r
+ WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
+ if (fullver >= 0x0501)\r
+ XPorLater = true;\r
+ bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
+\r
+ int selIndex = GetSelectionMark();\r
+ if ((point.x == -1) && (point.y == -1))\r
+ {\r
+ CRect rect;\r
+ GetItemRect(selIndex, &rect, LVIR_LABEL);\r
+ ClientToScreen(&rect);\r
+ point = rect.CenterPoint();\r
+ }\r
+ if ((GetSelectedCount() == 0)&&(XPorLater)&&(m_bHasCheckboxes))\r
+ {\r
+ // nothing selected could mean the context menu is requested for\r
+ // a group header\r
+ OnContextMenuGroup(pWnd, point);\r
+ }\r
+ else if (selIndex >= 0)\r
+ {\r
+ FileEntry * entry = GetListEntry(selIndex);\r
+ ASSERT(entry != NULL);\r
+ if (entry == NULL)\r
+ return;\r
+ const CTGitPath& filepath = entry->path;\r
+ git_wc_status_kind wcStatus = entry->status;\r
+ // entry is selected, now show the popup menu\r
+ Locker lock(m_critSec);\r
+ CIconMenu popup;\r
+ CMenu changelistSubMenu;\r
+ CMenu ignoreSubMenu;\r
+ if (popup.CreatePopupMenu())\r
+ {\r
+ if (wcStatus >= git_wc_status_normal)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPCOMPAREWITHBASE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_COMPARE, IDS_LOG_COMPAREWITHBASE, IDI_DIFF);\r
+ popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
+ }\r
+\r
+ if (GetSelectedCount() == 1)\r
+ {\r
+ bool bEntryAdded = false;\r
+ if (entry->remotestatus <= git_wc_status_normal)\r
+ {\r
+ if (wcStatus > git_wc_status_normal)\r
+ {\r
+ if ((m_dwContextMenus & SVNSLC_POPGNUDIFF)&&(wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing))\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
+ bEntryAdded = true;\r
+ }\r
+ }\r
+ if (wcStatus > git_wc_status_normal)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPCOMMIT)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_COMMIT, IDS_STATUSLIST_CONTEXT_COMMIT, IDI_COMMIT);\r
+ popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
+ bEntryAdded = true;\r
+ }\r
+ }\r
+ }\r
+ else if (wcStatus != git_wc_status_deleted)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPCOMPARE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_COMPAREWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
+ popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
+ bEntryAdded = true;\r
+ }\r
+ if (m_dwContextMenus & SVNSLC_POPGNUDIFF)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
+ bEntryAdded = true;\r
+ }\r
+ }\r
+ if (bEntryAdded)\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+ }\r
+ else if (GetSelectedCount() > 1)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPCOMMIT)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_COMMIT, IDS_STATUSLIST_CONTEXT_COMMIT, IDI_COMMIT);\r
+ popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
+ }\r
+ }\r
+ }\r
+ if (GetSelectedCount() > 0)\r
+ {\r
+ if ((GetSelectedCount() == 2)&&(m_dwContextMenus & SVNSLC_POPREPAIRMOVE))\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index = GetNextSelectedItem(pos);\r
+ if (index >= 0)\r
+ {\r
+ FileEntry * entry = GetListEntry(index);\r
+ git_wc_status_kind status1 = git_wc_status_none;\r
+ git_wc_status_kind status2 = git_wc_status_none;\r
+ if (entry)\r
+ status1 = entry->status;\r
+ index = GetNextSelectedItem(pos);\r
+ if (index >= 0)\r
+ {\r
+ entry = GetListEntry(index);\r
+ if (entry)\r
+ status2 = entry->status;\r
+ if ((status1 == git_wc_status_missing && status2 == git_wc_status_unversioned) ||\r
+ (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned))\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_REPAIRMOVE, IDS_STATUSLIST_CONTEXT_REPAIRMOVE);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (wcStatus > git_wc_status_normal)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPREVERT)\r
+ {\r
+ // reverting missing folders is not possible\r
+ if (!entry->IsFolder() || (wcStatus != git_wc_status_missing))\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_REVERT, IDS_MENUREVERT, IDI_REVERT);\r
+ }\r
+ }\r
+ }\r
+ if (entry->remotestatus > git_wc_status_normal)\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPUPDATE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_UPDATE, IDS_MENUUPDATE, IDI_UPDATE);\r
+ }\r
+ }\r
+ }\r
+ if ((GetSelectedCount() == 1)&&(wcStatus >= git_wc_status_normal)\r
+ &&(wcStatus != git_wc_status_ignored))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPSHOWLOG)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_LOG, IDS_REPOBROWSE_SHOWLOG, IDI_LOG);\r
+ }\r
+ if (m_dwContextMenus & SVNSLC_POPBLAME)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_BLAME, IDS_MENUBLAME, IDI_BLAME);\r
+ }\r
+ }\r
+ if ((wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing) && (GetSelectedCount() == 1))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPOPEN)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_OPEN, IDS_REPOBROWSE_OPEN, IDI_OPEN);\r
+ popup.AppendMenuIcon(IDSVNLC_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);\r
+ }\r
+ if (m_dwContextMenus & SVNSLC_POPEXPLORE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_EXPLORE, IDS_STATUSLIST_CONTEXT_EXPLORE, IDI_EXPLORER);\r
+ }\r
+ }\r
+ if (GetSelectedCount() > 0)\r
+ {\r
+ if (((wcStatus == git_wc_status_unversioned)||(wcStatus == git_wc_status_ignored))&&(m_dwContextMenus & SVNSLC_POPDELETE))\r
+ {\r
+ popup.AppendMenuIcon(IDGitLC_DELETE, IDS_MENUREMOVE, IDI_DELETE);\r
+ }\r
+ if ((wcStatus != Git_wc_status_unversioned)&&(wcStatus != git_wc_status_ignored)&&(wcStatus != Git_wc_status_deleted)&&(wcStatus != Git_wc_status_added)&&(m_dwContextMenus & GitSLC_POPDELETE))\r
+ {\r
+ if (bShift)\r
+ popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVEKEEP, IDI_DELETE);\r
+ else\r
+ popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVE, IDI_DELETE);\r
+ }\r
+ if ((wcStatus == git_wc_status_unversioned)||(wcStatus == git_wc_status_deleted))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPADD)\r
+ {\r
+ if ( entry->IsFolder() )\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_ADD_RECURSIVE, IDS_STATUSLIST_CONTEXT_ADD_RECURSIVE, IDI_ADD);\r
+ }\r
+ else\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_ADD, IDS_STATUSLIST_CONTEXT_ADD, IDI_ADD);\r
+ }\r
+ }\r
+ }\r
+ if ( (wcStatus == git_wc_status_unversioned) || (wcStatus == git_wc_status_deleted) )\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPIGNORE)\r
+ {\r
+ CTSVNPathList ignorelist;\r
+ FillListOfSelectedItemPaths(ignorelist);\r
+ // check if all selected entries have the same extension\r
+ bool bSameExt = true;\r
+ CString sExt;\r
+ for (int i=0; i<ignorelist.GetCount(); ++i)\r
+ {\r
+ if (sExt.IsEmpty() && (i==0))\r
+ sExt = ignorelist[i].GetFileExtension();\r
+ else if (sExt.CompareNoCase(ignorelist[i].GetFileExtension())!=0)\r
+ bSameExt = false;\r
+ }\r
+ if (bSameExt)\r
+ {\r
+ if (ignoreSubMenu.CreateMenu())\r
+ {\r
+ CString ignorepath;\r
+ if (ignorelist.GetCount()==1)\r
+ ignorepath = ignorelist[0].GetFileOrDirectoryName();\r
+ else\r
+ ignorepath.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());\r
+ ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_IGNORE, ignorepath);\r
+ ignorepath = _T("*")+sExt;\r
+ ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_IGNOREMASK, ignorepath);\r
+ CString temp;\r
+ temp.LoadString(IDS_MENUIGNORE);\r
+ popup.InsertMenu((UINT)-1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)ignoreSubMenu.m_hMenu, temp);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ CString temp;\r
+ if (ignorelist.GetCount()==1)\r
+ {\r
+ temp.LoadString(IDS_MENUIGNORE);\r
+ }\r
+ else\r
+ {\r
+ temp.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());\r
+ }\r
+ popup.AppendMenuIcon(IDSVNLC_IGNORE, temp, IDI_IGNORE);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (((wcStatus == git_wc_status_conflicted)||(entry->isConflicted)))\r
+ {\r
+ if ((m_dwContextMenus & SVNSLC_POPCONFLICT)||(m_dwContextMenus & SVNSLC_POPRESOLVE))\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+\r
+ if ((m_dwContextMenus & SVNSLC_POPCONFLICT)&&(entry->textstatus == git_wc_status_conflicted))\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_EDITCONFLICT, IDS_MENUCONFLICT, IDI_CONFLICT);\r
+ }\r
+ if (m_dwContextMenus & SVNSLC_POPRESOLVE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_RESOLVECONFLICT, IDS_STATUSLIST_CONTEXT_RESOLVED, IDI_RESOLVE);\r
+ }\r
+ if ((m_dwContextMenus & SVNSLC_POPRESOLVE)&&(entry->textstatus == git_wc_status_conflicted))\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_RESOLVETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS, IDI_RESOLVE);\r
+ popup.AppendMenuIcon(IDSVNLC_RESOLVEMINE, IDS_SVNPROGRESS_MENUUSEMINE, IDI_RESOLVE);\r
+ }\r
+ }\r
+ if (GetSelectedCount() > 0)\r
+ {\r
+ if ((!entry->IsFolder())&&(wcStatus >= git_wc_status_normal)\r
+ &&(wcStatus!=git_wc_status_missing)&&(wcStatus!=git_wc_status_deleted)\r
+ &&(wcStatus!=git_wc_status_added))\r
+ {\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+ if ((entry->lock_token.IsEmpty())&&(!entry->IsFolder()))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPLOCK)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_LOCK, IDS_MENU_LOCK, IDI_LOCK);\r
+ }\r
+ }\r
+ if ((!entry->lock_token.IsEmpty())&&(!entry->IsFolder()))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPUNLOCK)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_UNLOCK, IDS_MENU_UNLOCK, IDI_UNLOCK);\r
+ }\r
+ }\r
+ }\r
+ if ((!entry->IsFolder())&&((!entry->lock_token.IsEmpty())||(!entry->lock_remotetoken.IsEmpty())))\r
+ {\r
+ if (m_dwContextMenus & SVNSLC_POPUNLOCKFORCE)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_UNLOCKFORCE, IDS_MENU_UNLOCKFORCE, IDI_UNLOCK);\r
+ }\r
+ }\r
+\r
+ if (wcStatus != git_wc_status_missing && wcStatus != git_wc_status_deleted &&wcStatus!=git_wc_status_unversioned)\r
+ {\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+ popup.AppendMenuIcon(IDSVNLC_PROPERTIES, IDS_STATUSLIST_CONTEXT_PROPERTIES, IDI_PROPERTIES);\r
+ }\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+ popup.AppendMenuIcon(IDSVNLC_COPY, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);\r
+ popup.AppendMenuIcon(IDSVNLC_COPYEXT, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);\r
+ if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS)&&(XPorLater)\r
+ &&(wcStatus != git_wc_status_unversioned)&&(wcStatus != git_wc_status_none))\r
+ {\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+ // changelist commands\r
+ size_t numChangelists = GetNumberOfChangelistsInSelection();\r
+ if (numChangelists > 0)\r
+ {\r
+ popup.AppendMenuIcon(IDSVNLC_REMOVEFROMCS, IDS_STATUSLIST_CONTEXT_REMOVEFROMCS);\r
+ }\r
+ if ((!entry->IsFolder())&&(changelistSubMenu.CreateMenu()))\r
+ {\r
+ CString temp;\r
+ temp.LoadString(IDS_STATUSLIST_CONTEXT_CREATECS);\r
+ changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATECS, temp);\r
+\r
+ if (entry->changelist.Compare(SVNSLC_IGNORECHANGELIST))\r
+ {\r
+ changelistSubMenu.AppendMenu(MF_SEPARATOR);\r
+ changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATEIGNORECS, SVNSLC_IGNORECHANGELIST);\r
+ }\r
+\r
+ if (m_changelists.size() > 0)\r
+ {\r
+ // find the changelist names\r
+ bool bNeedSeparator = true;\r
+ int cmdID = IDSVNLC_MOVETOCS;\r
+ for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)\r
+ {\r
+ if ((entry->changelist.Compare(it->first))&&(it->first.Compare(SVNSLC_IGNORECHANGELIST)))\r
+ {\r
+ if (bNeedSeparator)\r
+ {\r
+ changelistSubMenu.AppendMenu(MF_SEPARATOR);\r
+ bNeedSeparator = false;\r
+ }\r
+ changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, cmdID, it->first);\r
+ cmdID++;\r
+ }\r
+ }\r
+ }\r
+ temp.LoadString(IDS_STATUSLIST_CONTEXT_MOVETOCS);\r
+ popup.AppendMenu(MF_POPUP|MF_STRING, (UINT_PTR)changelistSubMenu.GetSafeHmenu(), temp);\r
+ }\r
+ }\r
+ }\r
+\r
+ int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
+ m_bBlock = TRUE;\r
+ AfxGetApp()->DoWaitCursor(1);\r
+ int iItemCountBeforeMenuCmd = GetItemCount();\r
+ bool bForce = false;\r
+ switch (cmd)\r
+ {\r
+ case IDSVNLC_COPY:\r
+ CopySelectedEntriesToClipboard(0);\r
+ break;\r
+ case IDSVNLC_COPYEXT:\r
+ CopySelectedEntriesToClipboard((DWORD)-1);\r
+ break;\r
+ case IDSVNLC_PROPERTIES:\r
+ {\r
+ CTSVNPathList targetList;\r
+ FillListOfSelectedItemPaths(targetList);\r
+ CEditPropertiesDlg dlg;\r
+ dlg.SetPathList(targetList);\r
+ dlg.DoModal();\r
+ if (dlg.HasChanged())\r
+ {\r
+ // since the user might have changed/removed/added\r
+ // properties recursively, we don't really know\r
+ // which items have changed their status.\r
+ // So tell the parent to do a refresh.\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_COMMIT:\r
+ {\r
+ CTSVNPathList targetList;\r
+ FillListOfSelectedItemPaths(targetList);\r
+ CTSVNPath tempFile = CTempFiles::Instance().GetTempFilePath(false);\r
+ VERIFY(targetList.WriteToFile(tempFile.GetWinPathString()));\r
+ CString commandline = CPathUtils::GetAppDirectory();\r
+ commandline += _T("TortoiseProc.exe /command:commit /pathfile:\"");\r
+ commandline += tempFile.GetWinPathString();\r
+ commandline += _T("\"");\r
+ commandline += _T(" /deletepathfile");\r
+ CAppUtils::LaunchApplication(commandline, NULL, false);\r
+ }\r
+ break;\r
+ case IDSVNLC_REVERT:\r
+ {\r
+ // If at least one item is not in the status "added"\r
+ // we ask for a confirmation\r
+ BOOL bConfirm = FALSE;\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * fentry = GetListEntry(index);\r
+ if (fentry->textstatus != git_wc_status_added)\r
+ {\r
+ bConfirm = TRUE;\r
+ break;\r
+ }\r
+ } \r
+\r
+ CString str;\r
+ str.Format(IDS_PROC_WARNREVERT,GetSelectedCount());\r
+\r
+ if (!bConfirm || CMessageBox::Show(this->m_hWnd, str, _T("TortoiseSVN"), MB_YESNO | MB_ICONQUESTION)==IDYES)\r
+ {\r
+ CTSVNPathList targetList;\r
+ FillListOfSelectedItemPaths(targetList);\r
+\r
+ // make sure that the list is reverse sorted, so that\r
+ // children are removed before any parents\r
+ targetList.SortByPathname(true);\r
+\r
+ SVN git;\r
+\r
+ // put all reverted files in the trashbin, except the ones with 'added'\r
+ // status because they are not restored by the revert.\r
+ CTSVNPathList delList;\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * entry = GetListEntry(index);\r
+ if (entry->status != git_wc_status_added)\r
+ delList.AddPath(entry->GetPath());\r
+ }\r
+ if (DWORD(CRegDWORD(_T("Software\\TortoiseSVN\\RevertWithRecycleBin"), TRUE)))\r
+ delList.DeleteAllFiles(true);\r
+\r
+ if (!git.Revert(targetList, CStringArray(), FALSE))\r
+ {\r
+ CMessageBox::Show(this->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ else\r
+ {\r
+ // since the entries got reverted we need to remove\r
+ // them from the list too, if no remote changes are shown,\r
+ // if the unmodified files are not shown\r
+ // and if the item is not part of a changelist\r
+ POSITION pos;\r
+ SetRedraw(FALSE);\r
+ while ((pos = GetFirstSelectedItemPosition())!=0)\r
+ {\r
+ int index;\r
+ index = GetNextSelectedItem(pos);\r
+ FileEntry * fentry = m_arStatusArray[m_arListArray[index]];\r
+ if ( fentry->IsFolder() )\r
+ {\r
+ // refresh!\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ break;\r
+ }\r
+\r
+ BOOL bAdded = (fentry->textstatus == git_wc_status_added);\r
+ fentry->status = git_wc_status_normal;\r
+ fentry->propstatus = git_wc_status_normal;\r
+ fentry->textstatus = git_wc_status_normal;\r
+ fentry->copied = false;\r
+ fentry->isConflicted = false;\r
+ if ((fentry->GetChangeList().IsEmpty()&&(fentry->remotestatus <= git_wc_status_normal))||(m_dwShow & SVNSLC_SHOWNORMAL))\r
+ {\r
+ if ( bAdded )\r
+ {\r
+ // reverting added items makes them unversioned, not 'normal'\r
+ if (fentry->IsFolder())\r
+ fentry->propstatus = git_wc_status_none;\r
+ else\r
+ fentry->propstatus = git_wc_status_unversioned;\r
+ fentry->status = git_wc_status_unversioned;\r
+ fentry->textstatus = git_wc_status_unversioned;\r
+ SetItemState(index, 0, LVIS_SELECTED);\r
+ SetEntryCheck(fentry, index, false);\r
+ }\r
+ else if ((fentry->switched)||(m_dwShow & SVNSLC_SHOWNORMAL))\r
+ {\r
+ SetItemState(index, 0, LVIS_SELECTED);\r
+ }\r
+ else\r
+ {\r
+ m_nTotal--;\r
+ if (GetCheck(index))\r
+ m_nSelected--;\r
+ RemoveListEntry(index);\r
+ Invalidate();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ SetItemState(index, 0, LVIS_SELECTED);\r
+ }\r
+ }\r
+ SetRedraw(TRUE);\r
+ SaveColumnWidths();\r
+ Show(m_dwShow, 0, m_bShowFolders);\r
+ NotifyCheck();\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_COMPARE:\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ while ( pos )\r
+ {\r
+ int index = GetNextSelectedItem(pos);\r
+ StartDiff(index);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_COMPAREWC:\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ while ( pos )\r
+ {\r
+ int index = GetNextSelectedItem(pos);\r
+ FileEntry * entry = GetListEntry(index);\r
+ ASSERT(entry != NULL);\r
+ if (entry == NULL)\r
+ continue;\r
+ SVNDiff diff(NULL, m_hWnd, true);\r
+ diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
+ git_revnum_t baseRev = entry->Revision;\r
+ diff.DiffFileAgainstBase(\r
+ entry->path, baseRev, entry->textstatus, entry->propstatus);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_GNUDIFF1:\r
+ {\r
+ SVNDiff diff(NULL, this->m_hWnd, true);\r
+\r
+ if (entry->remotestatus <= git_wc_status_normal)\r
+ CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_BASE, entry->path, SVNRev::REV_WC);\r
+ else\r
+ CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_WC, entry->path, SVNRev::REV_HEAD);\r
+ }\r
+ break;\r
+ case IDSVNLC_UPDATE:\r
+ {\r
+ CTSVNPathList targetList;\r
+ FillListOfSelectedItemPaths(targetList);\r
+ bool bAllExist = true;\r
+ for (int i=0; i<targetList.GetCount(); ++i)\r
+ {\r
+ if (!targetList[i].Exists())\r
+ {\r
+ bAllExist = false;\r
+ break;\r
+ }\r
+ }\r
+ if (bAllExist)\r
+ {\r
+ CSVNProgressDlg dlg;\r
+ dlg.SetCommand(CSVNProgressDlg::SVNProgress_Update);\r
+ dlg.SetPathList(targetList);\r
+ dlg.SetRevision(SVNRev::REV_HEAD);\r
+ dlg.DoModal();\r
+ }\r
+ else\r
+ {\r
+ CString sTempFile = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();\r
+ targetList.WriteToFile(sTempFile, false);\r
+ CString sCmd;\r
+ sCmd.Format(_T("\"%s\" /command:update /rev /pathfile:\"%s\" /deletepathfile"),\r
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)sTempFile);\r
+\r
+ CAppUtils::LaunchApplication(sCmd, NULL, false);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_LOG:\r
+ {\r
+ CString sCmd;\r
+ sCmd.Format(_T("\"%s\" /command:log /path:\"%s\""),\r
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), filepath.GetWinPath());\r
+\r
+ if (!filepath.IsUrl())\r
+ {\r
+ sCmd += _T(" /propspath:\"");\r
+ sCmd += filepath.GetWinPathString();\r
+ sCmd += _T("\"");\r
+ } \r
+\r
+ CAppUtils::LaunchApplication(sCmd, NULL, false);\r
+ }\r
+ break;\r
+ case IDSVNLC_BLAME:\r
+ {\r
+ CString sCmd;\r
+ sCmd.Format(_T("\"%s\" /command:blame /path:\"%s\""),\r
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), filepath.GetWinPath());\r
+\r
+ if (!filepath.IsUrl())\r
+ {\r
+ sCmd += _T(" /propspath:\"");\r
+ sCmd += filepath.GetWinPathString();\r
+ sCmd += _T("\"");\r
+ } \r
+\r
+ CAppUtils::LaunchApplication(sCmd, NULL, false);\r
+ }\r
+ break;\r
+ case IDSVNLC_OPEN:\r
+ {\r
+ int ret = (int)ShellExecute(this->m_hWnd, NULL, filepath.GetWinPath(), NULL, NULL, SW_SHOW);\r
+ if (ret <= HINSTANCE_ERROR)\r
+ {\r
+ CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
+ cmd += filepath.GetWinPathString();\r
+ CAppUtils::LaunchApplication(cmd, NULL, false);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_OPENWITH:\r
+ {\r
+ CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
+ cmd += filepath.GetWinPathString() + _T(" ");\r
+ CAppUtils::LaunchApplication(cmd, NULL, false);\r
+ }\r
+ break;\r
+ case IDSVNLC_EXPLORE:\r
+ {\r
+ ShellExecute(this->m_hWnd, _T("explore"), filepath.GetDirectory().GetWinPath(), NULL, NULL, SW_SHOW);\r
+ }\r
+ break;\r
+ case IDSVNLC_REMOVE:\r
+ {\r
+ SVN git;\r
+ CTSVNPathList itemsToRemove;\r
+ FillListOfSelectedItemPaths(itemsToRemove);\r
+\r
+ // We must sort items before removing, so that files are always removed\r
+ // *before* their parents\r
+ itemsToRemove.SortByPathname(true);\r
+\r
+ bool bSuccess = false;\r
+ if (git.Remove(itemsToRemove, FALSE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))\r
+ {\r
+ bSuccess = true;\r
+ }\r
+ else\r
+ {\r
+ if ((git.Err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE) ||\r
+ (git.Err->apr_err == SVN_ERR_CLIENT_MODIFIED))\r
+ {\r
+ CString msg, yes, no, yestoall;\r
+ msg.Format(IDS_PROC_REMOVEFORCE, (LPCTSTR)git.GetLastErrorMessage());\r
+ yes.LoadString(IDS_MSGBOX_YES);\r
+ no.LoadString(IDS_MSGBOX_NO);\r
+ yestoall.LoadString(IDS_PROC_YESTOALL);\r
+ UINT ret = CMessageBox::Show(m_hWnd, msg, _T("TortoiseSVN"), 2, IDI_ERROR, yes, no, yestoall);\r
+ if ((ret == 1)||(ret==3))\r
+ {\r
+ if (!git.Remove(itemsToRemove, TRUE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ else\r
+ bSuccess = true;\r
+ }\r
+ }\r
+ else\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ if (bSuccess)\r
+ {\r
+ // The remove went ok, but we now need to run through the selected items again\r
+ // and update their status\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ std::vector<int> entriesToRemove;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ if (!bShift &&\r
+ ((e->textstatus == git_wc_status_unversioned)||\r
+ (e->textstatus == git_wc_status_none)||\r
+ (e->textstatus == git_wc_status_ignored)))\r
+ {\r
+ if (GetCheck(index))\r
+ m_nSelected--;\r
+ m_nTotal--;\r
+ entriesToRemove.push_back(index);\r
+ }\r
+ else\r
+ {\r
+ e->textstatus = git_wc_status_deleted;\r
+ e->status = git_wc_status_deleted;\r
+ SetEntryCheck(e,index,true);\r
+ }\r
+ }\r
+ for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)\r
+ {\r
+ RemoveListEntry(*it);\r
+ }\r
+ }\r
+ SaveColumnWidths();\r
+ Show(m_dwShow, 0, m_bShowFolders);\r
+ NotifyCheck();\r
+ }\r
+ break;\r
+ case IDSVNLC_DELETE:\r
+ {\r
+ CTSVNPathList pathlist;\r
+ FillListOfSelectedItemPaths(pathlist);\r
+ pathlist.RemoveChildren();\r
+ CString filelist;\r
+ for (INT_PTR i=0; i<pathlist.GetCount(); ++i)\r
+ {\r
+ filelist += pathlist[i].GetWinPathString();\r
+ filelist += _T("|");\r
+ }\r
+ filelist += _T("|");\r
+ int len = filelist.GetLength();\r
+ TCHAR * buf = new TCHAR[len+2];\r
+ _tcscpy_s(buf, len+2, filelist);\r
+ for (int i=0; i<len; ++i)\r
+ if (buf[i] == '|')\r
+ buf[i] = 0;\r
+ SHFILEOPSTRUCT fileop;\r
+ fileop.hwnd = this->m_hWnd;\r
+ fileop.wFunc = FO_DELETE;\r
+ fileop.pFrom = buf;\r
+ fileop.pTo = NULL;\r
+ fileop.fFlags = FOF_NO_CONNECTED_ELEMENTS | ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : FOF_ALLOWUNDO);\r
+ fileop.lpszProgressTitle = _T("deleting file");\r
+ int result = SHFileOperation(&fileop);\r
+ delete [] buf;\r
+\r
+ if ( (result==0) && (!fileop.fAnyOperationsAborted) )\r
+ {\r
+ SetRedraw(FALSE);\r
+ POSITION pos = NULL;\r
+ CTSVNPathList deletedlist; // to store list of deleted folders\r
+ while ((pos = GetFirstSelectedItemPosition()) != 0)\r
+ {\r
+ int index = GetNextSelectedItem(pos);\r
+ if (GetCheck(index))\r
+ m_nSelected--;\r
+ m_nTotal--;\r
+ FileEntry * fentry = GetListEntry(index);\r
+ if ((fentry)&&(fentry->isfolder))\r
+ deletedlist.AddPath(fentry->path);\r
+ RemoveListEntry(index);\r
+ }\r
+ // now go through the list of deleted folders\r
+ // and remove all their children from the list too!\r
+ int nListboxEntries = GetItemCount();\r
+ for (int folderindex = 0; folderindex < deletedlist.GetCount(); ++folderindex)\r
+ {\r
+ CTSVNPath folderpath = deletedlist[folderindex];\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (folderpath.IsAncestorOf(entry->path))\r
+ {\r
+ RemoveListEntry(i--);\r
+ nListboxEntries--;\r
+ }\r
+ }\r
+ }\r
+ SetRedraw(TRUE);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_IGNOREMASK:\r
+ {\r
+ CString name = _T("*")+filepath.GetFileExtension();\r
+ CTSVNPathList ignorelist;\r
+ FillListOfSelectedItemPaths(ignorelist, true);\r
+ std::set<CTSVNPath> parentlist;\r
+ for (int i=0; i<ignorelist.GetCount(); ++i)\r
+ {\r
+ parentlist.insert(ignorelist[i].GetContainingDirectory());\r
+ }\r
+ std::set<CTSVNPath>::iterator it;\r
+ std::vector<CString> toremove;\r
+ SetRedraw(FALSE);\r
+ for (it = parentlist.begin(); it != parentlist.end(); ++it)\r
+ {\r
+ CTSVNPath parentFolder = (*it).GetDirectory();\r
+ SVNProperties props(parentFolder, SVNRev::REV_WC, false);\r
+ CStringA value;\r
+ for (int i=0; i<props.GetCount(); i++)\r
+ {\r
+ CString propname(props.GetItemName(i).c_str());\r
+ if (propname.CompareNoCase(_T("git:ignore"))==0)\r
+ {\r
+ stdstring stemp;\r
+ // treat values as normal text even if they're not\r
+ value = (char *)props.GetItemValue(i).c_str();\r
+ }\r
+ }\r
+ if (value.IsEmpty())\r
+ value = name;\r
+ else\r
+ {\r
+ value = value.Trim("\n\r");\r
+ value += "\n";\r
+ value += name;\r
+ value.Remove('\r');\r
+ }\r
+ if (!props.Add(_T("git:ignore"), (LPCSTR)value))\r
+ {\r
+ CString temp;\r
+ temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);\r
+ CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ else\r
+ {\r
+ CTSVNPath basepath;\r
+ int nListboxEntries = GetItemCount();\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ ASSERT(entry != NULL);\r
+ if (entry == NULL)\r
+ continue;\r
+ if (basepath.IsEmpty())\r
+ basepath = entry->basepath;\r
+ // since we ignored files with a mask (e.g. *.exe)\r
+ // we have to find find all files in the same\r
+ // folder (IsAncestorOf() returns TRUE for _all_ children,\r
+ // not just the immediate ones) which match the\r
+ // mask and remove them from the list too.\r
+ if ((entry->status == git_wc_status_unversioned)&&(parentFolder.IsAncestorOf(entry->path)))\r
+ {\r
+ CString f = entry->path.GetSVNPathString();\r
+ if (f.Mid(parentFolder.GetSVNPathString().GetLength()).Find('/')<=0)\r
+ {\r
+ if (CStringUtils::WildCardMatch(name, f))\r
+ {\r
+ if (GetCheck(i))\r
+ m_nSelected--;\r
+ m_nTotal--;\r
+ toremove.push_back(f);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (!m_bIgnoreRemoveOnly)\r
+ {\r
+ SVNStatus status;\r
+ git_wc_status2_t * s;\r
+ CTSVNPath gitPath;\r
+ s = status.GetFirstFileStatus(parentFolder, gitPath, false, git_depth_empty);\r
+ if (s!=0)\r
+ {\r
+ // first check if the folder isn't already present in the list\r
+ bool bFound = false;\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry->path.IsEquivalentTo(gitPath))\r
+ {\r
+ bFound = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!bFound)\r
+ {\r
+ FileEntry * entry = new FileEntry();\r
+ entry->path = gitPath;\r
+ entry->basepath = basepath;\r
+ entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);\r
+ entry->textstatus = s->text_status;\r
+ entry->propstatus = s->prop_status;\r
+ entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);\r
+ entry->remotetextstatus = s->repos_text_status;\r
+ entry->remotepropstatus = s->repos_prop_status;\r
+ entry->inunversionedfolder = false;\r
+ entry->checked = true;\r
+ entry->inexternal = false;\r
+ entry->direct = false;\r
+ entry->isfolder = true;\r
+ entry->last_commit_date = 0;\r
+ entry->last_commit_rev = 0;\r
+ entry->remoterev = 0;\r
+ if (s->entry)\r
+ {\r
+ if (s->entry->url)\r
+ {\r
+ entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));\r
+ }\r
+ }\r
+ if (s->entry && s->entry->present_props)\r
+ {\r
+ entry->present_props = s->entry->present_props;\r
+ }\r
+ m_arStatusArray.push_back(entry);\r
+ m_arListArray.push_back(m_arStatusArray.size()-1);\r
+ AddEntry(entry, langID, GetItemCount());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)\r
+ {\r
+ int nListboxEntries = GetItemCount();\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)\r
+ {\r
+ RemoveListEntry(i);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ SetRedraw(TRUE);\r
+ }\r
+ break;\r
+ case IDSVNLC_IGNORE:\r
+ {\r
+ CTSVNPathList ignorelist;\r
+ std::vector<CString> toremove;\r
+ FillListOfSelectedItemPaths(ignorelist, true);\r
+ SetRedraw(FALSE);\r
+ for (int j=0; j<ignorelist.GetCount(); ++j)\r
+ {\r
+ int nListboxEntries = GetItemCount();\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ if (GetListEntry(i)->GetPath().IsEquivalentTo(ignorelist[j]))\r
+ {\r
+ selIndex = i;\r
+ break;\r
+ }\r
+ }\r
+ CString name = CPathUtils::PathPatternEscape(ignorelist[j].GetFileOrDirectoryName());\r
+ CTSVNPath parentfolder = ignorelist[j].GetContainingDirectory();\r
+ SVNProperties props(parentfolder, SVNRev::REV_WC, false);\r
+ CStringA value;\r
+ for (int i=0; i<props.GetCount(); i++)\r
+ {\r
+ CString propname(props.GetItemName(i).c_str());\r
+ if (propname.CompareNoCase(_T("git:ignore"))==0)\r
+ {\r
+ stdstring stemp;\r
+ // treat values as normal text even if they're not\r
+ value = (char *)props.GetItemValue(i).c_str();\r
+ }\r
+ }\r
+ if (value.IsEmpty())\r
+ value = name;\r
+ else\r
+ {\r
+ value = value.Trim("\n\r");\r
+ value += "\n";\r
+ value += name;\r
+ value.Remove('\r');\r
+ }\r
+ if (!props.Add(_T("git:ignore"), (LPCSTR)value))\r
+ {\r
+ CString temp;\r
+ temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);\r
+ CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseSVN"), MB_ICONERROR);\r
+ break;\r
+ }\r
+ if (GetCheck(selIndex))\r
+ m_nSelected--;\r
+ m_nTotal--;\r
+\r
+ // now, if we ignored a folder, remove all its children\r
+ if (ignorelist[j].IsDirectory())\r
+ {\r
+ for (int i=0; i<(int)m_arListArray.size(); ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry->status == git_wc_status_unversioned)\r
+ {\r
+ if (!ignorelist[j].IsEquivalentTo(entry->GetPath())&&(ignorelist[j].IsAncestorOf(entry->GetPath())))\r
+ {\r
+ entry->status = git_wc_status_ignored;\r
+ entry->textstatus = git_wc_status_ignored;\r
+ if (GetCheck(i))\r
+ m_nSelected--;\r
+ toremove.push_back(entry->GetPath().GetSVNPathString());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ CTSVNPath basepath = m_arStatusArray[m_arListArray[selIndex]]->basepath;\r
+\r
+ FileEntry * entry = m_arStatusArray[m_arListArray[selIndex]];\r
+ if ( entry->status == git_wc_status_unversioned ) // keep "deleted" items\r
+ toremove.push_back(entry->GetPath().GetSVNPathString());\r
+\r
+ if (!m_bIgnoreRemoveOnly)\r
+ {\r
+ SVNStatus status;\r
+ git_wc_status2_t * s;\r
+ CTSVNPath gitPath;\r
+ s = status.GetFirstFileStatus(parentfolder, gitPath, false, git_depth_empty);\r
+ // first check if the folder isn't already present in the list\r
+ bool bFound = false;\r
+ nListboxEntries = GetItemCount();\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ FileEntry * entry = GetListEntry(i);\r
+ if (entry->path.IsEquivalentTo(gitPath))\r
+ {\r
+ bFound = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!bFound)\r
+ {\r
+ if (s!=0)\r
+ {\r
+ FileEntry * entry = new FileEntry();\r
+ entry->path = gitPath;\r
+ entry->basepath = basepath;\r
+ entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);\r
+ entry->textstatus = s->text_status;\r
+ entry->propstatus = s->prop_status;\r
+ entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);\r
+ entry->remotetextstatus = s->repos_text_status;\r
+ entry->remotepropstatus = s->repos_prop_status;\r
+ entry->inunversionedfolder = FALSE;\r
+ entry->checked = true;\r
+ entry->inexternal = false;\r
+ entry->direct = false;\r
+ entry->isfolder = true;\r
+ entry->last_commit_date = 0;\r
+ entry->last_commit_rev = 0;\r
+ entry->remoterev = 0;\r
+ if (s->entry)\r
+ {\r
+ if (s->entry->url)\r
+ {\r
+ entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));\r
+ }\r
+ }\r
+ if (s->entry && s->entry->present_props)\r
+ {\r
+ entry->present_props = s->entry->present_props;\r
+ }\r
+ m_arStatusArray.push_back(entry);\r
+ m_arListArray.push_back(m_arStatusArray.size()-1);\r
+ AddEntry(entry, langID, GetItemCount());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)\r
+ {\r
+ int nListboxEntries = GetItemCount();\r
+ for (int i=0; i<nListboxEntries; ++i)\r
+ {\r
+ if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)\r
+ {\r
+ RemoveListEntry(i);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ SetRedraw(TRUE);\r
+ }\r
+ break;\r
+ case IDSVNLC_EDITCONFLICT:\r
+ SVNDiff::StartConflictEditor(filepath);\r
+ break;\r
+ case IDSVNLC_RESOLVECONFLICT:\r
+ case IDSVNLC_RESOLVEMINE:\r
+ case IDSVNLC_RESOLVETHEIRS:\r
+ {\r
+ git_wc_conflict_choice_t result = git_wc_conflict_choose_merged;\r
+ switch (cmd)\r
+ {\r
+ case IDSVNLC_RESOLVETHEIRS:\r
+ result = git_wc_conflict_choose_theirs_full;\r
+ break;\r
+ case IDSVNLC_RESOLVEMINE:\r
+ result = git_wc_conflict_choose_mine_full;\r
+ break;\r
+ case IDSVNLC_RESOLVECONFLICT:\r
+ result = git_wc_conflict_choose_merged;\r
+ break;\r
+ }\r
+ if (CMessageBox::Show(m_hWnd, IDS_PROC_RESOLVE, IDS_APPNAME, MB_ICONQUESTION | MB_YESNO)==IDYES)\r
+ {\r
+ SVN git;\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ while (pos != 0)\r
+ {\r
+ int index;\r
+ index = GetNextSelectedItem(pos);\r
+ FileEntry * fentry = m_arStatusArray[m_arListArray[index]];\r
+ if (!git.Resolve(fentry->GetPath(), result, FALSE))\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ else\r
+ {\r
+ fentry->status = git_wc_status_modified;\r
+ fentry->textstatus = git_wc_status_modified;\r
+ fentry->isConflicted = false;\r
+ }\r
+ }\r
+ Show(m_dwShow, 0, m_bShowFolders);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_ADD:\r
+ {\r
+ SVN git;\r
+ CTSVNPathList itemsToAdd;\r
+ FillListOfSelectedItemPaths(itemsToAdd);\r
+\r
+ // We must sort items before adding, so that folders are always added\r
+ // *before* any of their children\r
+ itemsToAdd.SortByPathname();\r
+\r
+ ProjectProperties props;\r
+ props.ReadPropsPathList(itemsToAdd);\r
+ if (git.Add(itemsToAdd, &props, git_depth_empty, TRUE, TRUE, TRUE))\r
+ {\r
+ // The add went ok, but we now need to run through the selected items again\r
+ // and update their status\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ e->textstatus = git_wc_status_added;\r
+ e->propstatus = git_wc_status_none;\r
+ e->status = git_wc_status_added;\r
+ SetEntryCheck(e,index,true);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ SaveColumnWidths();\r
+ Show(m_dwShow, 0, m_bShowFolders);\r
+ NotifyCheck();\r
+ }\r
+ break;\r
+ case IDSVNLC_ADD_RECURSIVE:\r
+ {\r
+ CTSVNPathList itemsToAdd;\r
+ FillListOfSelectedItemPaths(itemsToAdd);\r
+\r
+ CAddDlg dlg;\r
+ dlg.m_pathList = itemsToAdd;\r
+ if (dlg.DoModal() == IDOK)\r
+ {\r
+ if (dlg.m_pathList.GetCount() == 0)\r
+ break;\r
+ CSVNProgressDlg progDlg;\r
+ progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Add);\r
+ progDlg.SetPathList(dlg.m_pathList);\r
+ ProjectProperties props;\r
+ props.ReadPropsPathList(dlg.m_pathList);\r
+ progDlg.SetProjectProperties(props);\r
+ progDlg.SetItemCount(dlg.m_pathList.GetCount());\r
+ progDlg.DoModal();\r
+\r
+ // refresh!\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_LOCK:\r
+ {\r
+ CTSVNPathList itemsToLock;\r
+ FillListOfSelectedItemPaths(itemsToLock);\r
+ CInputDlg inpDlg;\r
+ inpDlg.m_sTitle.LoadString(IDS_MENU_LOCK);\r
+ CStringUtils::RemoveAccelerators(inpDlg.m_sTitle);\r
+ inpDlg.m_sHintText.LoadString(IDS_LOCK_MESSAGEHINT);\r
+ inpDlg.m_sCheckText.LoadString(IDS_LOCK_STEALCHECK);\r
+ ProjectProperties props;\r
+ props.ReadPropsPathList(itemsToLock);\r
+ props.nMinLogSize = 0; // the lock message is optional, so no minimum!\r
+ inpDlg.m_pProjectProperties = &props;\r
+ if (inpDlg.DoModal()==IDOK)\r
+ {\r
+ CSVNProgressDlg progDlg;\r
+ progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Lock);\r
+ progDlg.SetOptions(inpDlg.m_iCheck ? ProgOptLockForce : ProgOptNone);\r
+ progDlg.SetPathList(itemsToLock);\r
+ progDlg.SetCommitMessage(inpDlg.m_sInputText);\r
+ progDlg.DoModal();\r
+ // refresh!\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_UNLOCKFORCE:\r
+ bForce = true;\r
+ case IDSVNLC_UNLOCK:\r
+ {\r
+ CTSVNPathList itemsToUnlock;\r
+ FillListOfSelectedItemPaths(itemsToUnlock);\r
+ CSVNProgressDlg progDlg;\r
+ progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Unlock);\r
+ progDlg.SetOptions(bForce ? ProgOptLockForce : ProgOptNone);\r
+ progDlg.SetPathList(itemsToUnlock);\r
+ progDlg.DoModal();\r
+ // refresh!\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_REPAIRMOVE:\r
+ {\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index = GetNextSelectedItem(pos);\r
+ FileEntry * entry1 = NULL;\r
+ FileEntry * entry2 = NULL;\r
+ if (index >= 0)\r
+ {\r
+ entry1 = GetListEntry(index);\r
+ git_wc_status_kind status1 = git_wc_status_none;\r
+ git_wc_status_kind status2 = git_wc_status_none;\r
+ if (entry1)\r
+ {\r
+ status1 = entry1->status;\r
+ index = GetNextSelectedItem(pos);\r
+ if (index >= 0)\r
+ {\r
+ entry2 = GetListEntry(index);\r
+ if (entry2)\r
+ {\r
+ status2 = entry2->status;\r
+ if (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned)\r
+ {\r
+ FileEntry * tempentry = entry1;\r
+ entry1 = entry2;\r
+ entry2 = tempentry;\r
+ }\r
+ // entry1 was renamed to entry2 but outside of Subversion\r
+ // fix this by moving entry2 back to entry1 first,\r
+ // then do an git-move from entry1 to entry2\r
+ if (MoveFile(entry2->GetPath().GetWinPath(), entry1->GetPath().GetWinPath()))\r
+ {\r
+ SVN git;\r
+ if (!git.Move(CTSVNPathList(entry1->GetPath()), entry2->GetPath(), TRUE))\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ else\r
+ {\r
+ // check the previously unversioned item\r
+ entry1->checked = true;\r
+ // fixing the move was successful. We have to adjust the new status of the\r
+ // files.\r
+ // Since we don't know if the moved/renamed file had local modifications or not,\r
+ // we can't guess the new status. That means we have to refresh...\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ LPVOID lpMsgBuf;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+ FORMAT_MESSAGE_FROM_SYSTEM | \r
+ FORMAT_MESSAGE_IGNORE_INSERTS,\r
+ NULL,\r
+ GetLastError(),\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
+ (LPTSTR) &lpMsgBuf,\r
+ 0,\r
+ NULL \r
+ );\r
+ MessageBox((LPCTSTR)lpMsgBuf, _T("Error"), MB_OK | MB_ICONINFORMATION );\r
+ LocalFree( lpMsgBuf );\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case IDSVNLC_REMOVEFROMCS:\r
+ {\r
+ CTSVNPathList changelistItems;\r
+ FillListOfSelectedItemPaths(changelistItems);\r
+ SVN git;\r
+ SetRedraw(FALSE);\r
+ if (git.RemoveFromChangeList(changelistItems, CStringArray(), git_depth_empty))\r
+ {\r
+ // The changelists were removed, but we now need to run through the selected items again\r
+ // and update their changelist\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ std::vector<int> entriesToRemove;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ if (e)\r
+ {\r
+ e->changelist.Empty();\r
+ if (e->status == git_wc_status_normal)\r
+ {\r
+ // remove the entry completely\r
+ entriesToRemove.push_back(index);\r
+ }\r
+ else\r
+ SetItemGroup(index, 0);\r
+ }\r
+ }\r
+ for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)\r
+ {\r
+ RemoveListEntry(*it);\r
+ }\r
+ // TODO: Should we go through all entries here and check if we also could\r
+ // remove the changelist from m_changelists ?\r
+ }\r
+ else\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ SetRedraw(TRUE);\r
+ }\r
+ break;\r
+ case IDSVNLC_CREATEIGNORECS:\r
+ CreateChangeList(SVNSLC_IGNORECHANGELIST);\r
+ break;\r
+ case IDSVNLC_CREATECS:\r
+ {\r
+ CCreateChangelistDlg dlg;\r
+ if (dlg.DoModal() == IDOK)\r
+ {\r
+ CreateChangeList(dlg.m_sName);\r
+ }\r
+ }\r
+ break;\r
+ default:\r
+ {\r
+ if (cmd < IDSVNLC_MOVETOCS)\r
+ break;\r
+ CTSVNPathList changelistItems;\r
+ FillListOfSelectedItemPaths(changelistItems);\r
+\r
+ // find the changelist name\r
+ CString sChangelist;\r
+ int cmdID = IDSVNLC_MOVETOCS;\r
+ SetRedraw(FALSE);\r
+ for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)\r
+ {\r
+ if ((it->first.Compare(SVNSLC_IGNORECHANGELIST))&&(entry->changelist.Compare(it->first)))\r
+ {\r
+ if (cmd == cmdID)\r
+ {\r
+ sChangelist = it->first;\r
+ }\r
+ cmdID++;\r
+ }\r
+ }\r
+ if (!sChangelist.IsEmpty())\r
+ {\r
+ SVN git;\r
+ if (git.AddToChangeList(changelistItems, sChangelist, git_depth_empty))\r
+ {\r
+ // The changelists were moved, but we now need to run through the selected items again\r
+ // and update their changelist\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ e->changelist = sChangelist;\r
+ if (!e->IsFolder())\r
+ {\r
+ if (m_changelists.find(e->changelist)!=m_changelists.end())\r
+ SetItemGroup(index, m_changelists[e->changelist]);\r
+ else\r
+ SetItemGroup(index, 0);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
+ }\r
+ }\r
+ SetRedraw(TRUE);\r
+ }\r
+ break;\r
+ } // switch (cmd)\r
+ m_bBlock = FALSE;\r
+ AfxGetApp()->DoWaitCursor(-1);\r
+ GetStatisticsString();\r
+ int iItemCountAfterMenuCmd = GetItemCount();\r
+ if (iItemCountAfterMenuCmd != iItemCountBeforeMenuCmd)\r
+ {\r
+ CWnd* pParent = GetParent();\r
+ if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
+ {\r
+ pParent->SendMessage(SVNSLNM_ITEMCOUNTCHANGED);\r
+ }\r
+ }\r
+ } // if (popup.CreatePopupMenu())\r
+ } // if (selIndex >= 0)\r
+#endif\r
+}\r
+\r
+void CGitStatusListCtrl::OnContextMenuHeader(CWnd * pWnd, CPoint point)\r
+{\r
+ bool XPorLater = false;\r
+ OSVERSIONINFOEX inf;\r
+ SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
+ inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
+ GetVersionEx((OSVERSIONINFO *)&inf);\r
+ WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
+ if (fullver >= 0x0501)\r
+ XPorLater = true;\r
+\r
+ CHeaderCtrl * pHeaderCtrl = (CHeaderCtrl *)pWnd;\r
+ if ((point.x == -1) && (point.y == -1))\r
+ {\r
+ CRect rect;\r
+ pHeaderCtrl->GetItemRect(0, &rect);\r
+ ClientToScreen(&rect);\r
+ point = rect.CenterPoint();\r
+ }\r
+ Locker lock(m_critSec);\r
+ CMenu popup;\r
+ if (popup.CreatePopupMenu())\r
+ {\r
+ int columnCount = m_ColumnManager.GetColumnCount();\r
+\r
+ CString temp;\r
+ UINT uCheckedFlags = MF_STRING | MF_ENABLED | MF_CHECKED;\r
+ UINT uUnCheckedFlags = MF_STRING | MF_ENABLED;\r
+\r
+ // build control menu\r
+\r
+ if (XPorLater)\r
+ {\r
+ temp.LoadString(IDS_STATUSLIST_SHOWGROUPS);\r
+ popup.AppendMenu(IsGroupViewEnabled() ? uCheckedFlags : uUnCheckedFlags, columnCount, temp);\r
+ }\r
+\r
+ if (m_ColumnManager.AnyUnusedProperties())\r
+ {\r
+ temp.LoadString(IDS_STATUSLIST_REMOVEUNUSEDPROPS);\r
+ popup.AppendMenu(uUnCheckedFlags, columnCount+1, temp);\r
+ }\r
+\r
+ temp.LoadString(IDS_STATUSLIST_RESETCOLUMNORDER);\r
+ popup.AppendMenu(uUnCheckedFlags, columnCount+2, temp);\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+\r
+ // standard columns\r
+\r
+ for (int i = 1; i < SVNSLC_NUMCOLUMNS; ++i)\r
+ {\r
+ popup.AppendMenu ( m_ColumnManager.IsVisible(i) \r
+ ? uCheckedFlags \r
+ : uUnCheckedFlags\r
+ , i\r
+ , m_ColumnManager.GetName(i));\r
+ }\r
+\r
+ // user-prop columns:\r
+ // find relevant ones and sort 'em\r
+\r
+ std::map<CString, int> sortedProps;\r
+ for (int i = SVNSLC_NUMCOLUMNS; i < columnCount; ++i)\r
+ if (m_ColumnManager.IsRelevant(i))\r
+ sortedProps[m_ColumnManager.GetName(i)] = i;\r
+\r
+ if (!sortedProps.empty())\r
+ {\r
+ // add 'em to the menu\r
+\r
+ popup.AppendMenu(MF_SEPARATOR);\r
+\r
+ typedef std::map<CString, int>::const_iterator CIT;\r
+ for ( CIT iter = sortedProps.begin(), end = sortedProps.end()\r
+ ; iter != end\r
+ ; ++iter)\r
+ {\r
+ popup.AppendMenu ( m_ColumnManager.IsVisible(iter->second) \r
+ ? uCheckedFlags \r
+ : uUnCheckedFlags\r
+ , iter->second\r
+ , iter->first);\r
+ }\r
+ }\r
+\r
+ // show menu & let user pick an entry\r
+\r
+ int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
+ if ((cmd >= 1)&&(cmd < columnCount))\r
+ {\r
+ m_ColumnManager.SetVisible (cmd, !m_ColumnManager.IsVisible(cmd));\r
+ } \r
+ else if (cmd == columnCount)\r
+ {\r
+ EnableGroupView(!IsGroupViewEnabled());\r
+ } \r
+ else if (cmd == columnCount+1)\r
+ {\r
+ m_ColumnManager.RemoveUnusedProps();\r
+ } \r
+ else if (cmd == columnCount+2)\r
+ {\r
+ m_ColumnManager.ResetColumns (m_dwDefaultColumns);\r
+ }\r
+ }\r
+}\r
+\r
+void CGitStatusListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)\r
+{\r
+\r
+ if (pWnd == this)\r
+ {\r
+ OnContextMenuList(pWnd, point);\r
+ } // if (pWnd == this)\r
+ else if (pWnd == GetHeaderCtrl())\r
+ {\r
+ OnContextMenuHeader(pWnd, point);\r
+ }\r
+}\r
+\r
+void CGitStatusListCtrl::CreateChangeList(const CString& name)\r
+{\r
+#if 0\r
+ CTGitPathList changelistItems;\r
+ FillListOfSelectedItemPaths(changelistItems);\r
+ Git git;\r
+ if (git.AddToChangeList(changelistItems, name, git_depth_empty))\r
+ {\r
+ TCHAR groupname[1024];\r
+ LVGROUP grp = {0};\r
+ grp.cbSize = sizeof(LVGROUP);\r
+ grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;\r
+ _tcsncpy_s(groupname, 1024, name, 1023);\r
+ grp.pszHeader = groupname;\r
+ grp.iGroupId = (int)m_changelists.size();\r
+ grp.uAlign = LVGA_HEADER_LEFT;\r
+ m_changelists[name] = InsertGroup(-1, &grp);\r
+\r
+ PrepareGroups(true);\r
+\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while ((index = GetNextSelectedItem(pos)) >= 0)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ e->changelist = name;\r
+ SetEntryCheck(e, index, FALSE);\r
+ }\r
+\r
+ for (index = 0; index < GetItemCount(); ++index)\r
+ {\r
+ FileEntry * e = GetListEntry(index);\r
+ if (m_changelists.find(e->changelist)!=m_changelists.end())\r
+ SetItemGroup(index, m_changelists[e->changelist]);\r
+ else\r
+ SetItemGroup(index, 0);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("Tortoisegit"), MB_ICONERROR);\r
+ }\r
+#endif\r
+}\r
+\r
+void CGitStatusListCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)\r
+{\r
+#if 0\r
+ Locker lock(m_critSec);\r
+ LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
+ *pResult = 0;\r
+ if (m_bBlock)\r
+ return;\r
+ if (pNMLV->iItem < 0)\r
+ {\r
+ if (!IsGroupViewEnabled())\r
+ return;\r
+ POINT pt;\r
+ DWORD ptW = GetMessagePos();\r
+ pt.x = GET_X_LPARAM(ptW);\r
+ pt.y = GET_Y_LPARAM(ptW);\r
+ ScreenToClient(&pt);\r
+ int group = GetGroupFromPoint(&pt);\r
+ if (group < 0)\r
+ &n