Fixed issue #1205: Add "Last modified" column to commit dialog
[TortoiseGit.git] / src / Git / GitStatusListCtrl.cpp
blob57f323bca9018777e117b96947eb80dd6c03de21
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - TortoiseGit
4 // Copyright (C) 2003-2008 - TortoiseSVN
5 // Copyright (C) 2010-2012 Sven Strickroth <email@cs-ware.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "StdAfx.h"
23 #include "resource.h"
24 #include "..\\TortoiseShell\resource.h"
25 #include "GitStatusListCtrl.h"
26 #include "MessageBox.h"
27 #include "MyMemDC.h"
28 #include "UnicodeUtils.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "TempFile.h"
32 #include "StringUtils.h"
33 #include "DirFileEnum.h"
34 #include "GitConfig.h"
35 #include "LoglistUtils.h"
36 //#include "SVNProperties.h"
37 #include "Git.h"
38 #include "GitDiff.h"
39 //#include "LogDlg.h"
40 //#include "SVNProgressDlg.h"
41 #include "SysImageList.h"
42 //#include "Svnstatuslistctrl.h"
43 #include "TGitPath.h"
44 #include "Registry.h"
45 #include "GitStatus.h"
46 //#include "SVNHelpers.h"
47 #include "InputDlg.h"
48 #include "ShellUpdater.h"
49 #include "GitAdminDir.h"
50 //#include "DropFiles.h"
51 #include "IconMenu.h"
52 //#include "AddDlg.h"
53 //#include "EditPropertiesDlg.h"
54 //#include "CreateChangelistDlg.h"
55 #include "FormatMessageWrapper.h"
56 #include "MassiveGitTask.h"
58 const UINT CGitStatusListCtrl::GITSLNM_ITEMCOUNTCHANGED
59 = ::RegisterWindowMessage(_T("GITSLNM_ITEMCOUNTCHANGED"));
60 const UINT CGitStatusListCtrl::GITSLNM_NEEDSREFRESH
61 = ::RegisterWindowMessage(_T("GITSLNM_NEEDSREFRESH"));
62 const UINT CGitStatusListCtrl::GITSLNM_ADDFILE
63 = ::RegisterWindowMessage(_T("GITSLNM_ADDFILE"));
64 const UINT CGitStatusListCtrl::GITSLNM_CHECKCHANGED
65 = ::RegisterWindowMessage(_T("GITSLNM_CHECKCHANGED"));
66 const UINT CGitStatusListCtrl::GITSLNM_ITEMCHANGED
67 = ::RegisterWindowMessage(_T("GITSLNM_ITEMCHANGED"));
72 BEGIN_MESSAGE_MAP(CGitStatusListCtrl, CListCtrl)
73 ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHdnItemclick)
74 ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHdnItemclick)
75 ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)
76 ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)
77 ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnLvnItemchanged)
78 ON_WM_CONTEXTMENU()
79 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclk)
80 ON_NOTIFY_REFLECT(LVN_GETINFOTIP, OnLvnGetInfoTip)
81 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)
82 ON_WM_SETCURSOR()
83 ON_WM_GETDLGCODE()
84 ON_NOTIFY_REFLECT(NM_RETURN, OnNMReturn)
85 ON_WM_KEYDOWN()
86 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
87 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
88 ON_WM_PAINT()
89 ON_NOTIFY(HDN_BEGINTRACKA, 0, &CGitStatusListCtrl::OnHdnBegintrack)
90 ON_NOTIFY(HDN_BEGINTRACKW, 0, &CGitStatusListCtrl::OnHdnBegintrack)
91 ON_NOTIFY(HDN_ITEMCHANGINGA, 0, &CGitStatusListCtrl::OnHdnItemchanging)
92 ON_NOTIFY(HDN_ITEMCHANGINGW, 0, &CGitStatusListCtrl::OnHdnItemchanging)
93 ON_WM_DESTROY()
94 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
95 ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CGitStatusListCtrl::OnLvnItemchanging)
96 END_MESSAGE_MAP()
100 CGitStatusListCtrl::CGitStatusListCtrl() : CListCtrl()
101 //, m_HeadRev(GitRev::REV_HEAD)
102 , m_pbCanceled(NULL)
103 , m_pStatLabel(NULL)
104 , m_pSelectButton(NULL)
105 , m_pConfirmButton(NULL)
106 , m_bBusy(false)
107 , m_bEmpty(false)
108 , m_bShowIgnores(false)
109 , m_pDropTarget(NULL)
110 , m_bIgnoreRemoveOnly(false)
111 , m_bCheckChildrenWithParent(false)
112 , m_bUnversionedLast(true)
113 , m_bHasChangeLists(false)
114 , m_bHasLocks(false)
115 , m_bBlock(false)
116 , m_bBlockUI(false)
117 , m_bHasCheckboxes(false)
118 , m_bCheckIfGroupsExist(true)
119 , m_bFileDropsEnabled(false)
120 , m_bOwnDrag(false)
121 , m_dwDefaultColumns(0)
122 , m_ColumnManager(this)
123 , m_bAscending(false)
124 , m_nSortedColumn(-1)
125 , m_bHasExternalsFromDifferentRepos(false)
126 , m_sNoPropValueText(MAKEINTRESOURCE(IDS_STATUSLIST_NOPROPVALUE))
127 , m_amend(false)
128 , m_bDoNotAutoselectSubmodules(false)
129 , m_bHasWC(true)
131 m_FileLoaded=0;
132 m_critSec.Init();
133 m_bIsRevertTheirMy = false;
134 this->m_nLineAdded =this->m_nLineDeleted =0;
137 CGitStatusListCtrl::~CGitStatusListCtrl()
139 // if (m_pDropTarget)
140 // delete m_pDropTarget;
141 ClearStatusArray();
144 void CGitStatusListCtrl::ClearStatusArray()
146 #if 0
147 Locker lock(m_critSec);
148 for (size_t i=0; i < m_arStatusArray.size(); i++)
150 delete m_arStatusArray[i];
152 m_arStatusArray.clear();
153 #endif
155 #if 0
156 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(UINT_PTR index)
159 if (index >= (UINT_PTR)m_arListArray.size())
160 return NULL;
161 if (m_arListArray[index] >= m_arStatusArray.size())
162 return NULL;
163 return m_arStatusArray[m_arListArray[index]];
165 return NULL;
167 #endif
168 #if 0
169 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetVisibleListEntry(const CTGitPath& path)
171 int itemCount = GetItemCount();
172 for (int i=0; i < itemCount; i++)
174 FileEntry * entry = GetListEntry(i);
175 if (entry->GetPath().IsEquivalentTo(path))
176 return entry;
178 return NULL;
180 #endif
181 #if 0
182 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(const CTGitPath& path)
185 for (size_t i=0; i < m_arStatusArray.size(); i++)
187 FileEntry * entry = m_arStatusArray[i];
188 if (entry->GetPath().IsEquivalentTo(path))
189 return entry;
192 return NULL;
194 #endif
196 #if 0
197 int CGitStatusListCtrl::GetIndex(const CTGitPath& path)
199 int itemCount = GetItemCount();
200 for (int i=0; i < itemCount; i++)
202 FileEntry * entry = GetListEntry(i);
203 if (entry->GetPath().IsEquivalentTo(path))
204 return i;
206 return -1;
208 #endif
210 void CGitStatusListCtrl::Init(DWORD dwColumns, const CString& sColumnInfoContainer, unsigned __int64 dwContextMenus /* = GitSLC_POPALL */, bool bHasCheckboxes /* = true */, bool bHasWC /* = true */, DWORD allowedColumns /* = 0xffffffff */)
212 Locker lock(m_critSec);
214 m_dwDefaultColumns = dwColumns | 1;
215 m_dwContextMenus = dwContextMenus;
216 m_bHasCheckboxes = bHasCheckboxes;
217 m_bHasWC = bHasWC;
219 // set the extended style of the listcontrol
220 // 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.
221 CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);
222 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;
223 if (DWORD(regFullRowSelect))
224 exStyle |= LVS_EX_FULLROWSELECT;
225 exStyle |= (bHasCheckboxes ? LVS_EX_CHECKBOXES : 0);
226 SetRedraw(false);
227 SetExtendedStyle(exStyle);
229 SetWindowTheme(m_hWnd, L"Explorer", NULL);
231 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
232 m_nRestoreOvl = SYS_IMAGE_LIST().AddIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_RESTOREOVL), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
233 SYS_IMAGE_LIST().SetOverlayImage(m_nRestoreOvl, OVL_RESTORE);
234 SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
236 // keep CSorter::operator() in sync!!
237 static UINT standardColumnNames[GITSLC_NUMCOLUMNS]
238 = { IDS_STATUSLIST_COLFILE
239 , IDS_STATUSLIST_COLFILENAME
240 , IDS_STATUSLIST_COLEXT
241 , IDS_STATUSLIST_COLSTATUS
242 , IDS_STATUSLIST_COLADD
243 , IDS_STATUSLIST_COLDEL
244 , IDS_STATUSLIST_COLLASTMODIFIED
247 m_ColumnManager.SetNames(standardColumnNames,GITSLC_NUMCOLUMNS);
248 m_ColumnManager.ReadSettings(m_dwDefaultColumns, 0xffffffff & ~(allowedColumns | m_dwDefaultColumns), sColumnInfoContainer, GITSLC_NUMCOLUMNS);
250 // enable file drops
251 #if 0
252 if (m_pDropTarget == NULL)
254 m_pDropTarget = new CGitStatusListCtrlDropTarget(this);
255 RegisterDragDrop(m_hWnd,m_pDropTarget);
256 // create the supported formats:
257 FORMATETC ftetc={0};
258 ftetc.dwAspect = DVASPECT_CONTENT;
259 ftetc.lindex = -1;
260 ftetc.tymed = TYMED_HGLOBAL;
261 ftetc.cfFormat=CF_HDROP;
262 m_pDropTarget->AddSuportedFormat(ftetc);
264 #endif
266 SetRedraw(true);
269 bool CGitStatusListCtrl::SetBackgroundImage(UINT nID)
271 return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);
274 BOOL CGitStatusListCtrl::GetStatus ( const CTGitPathList* pathList
275 , bool bUpdate /* = FALSE */
276 , bool bShowIgnores /* = false */
277 , bool bShowUnRev
278 , bool /*bShowUserProps*/ /* = false */)
280 Locker lock(m_critSec);
281 int mask= CGitStatusListCtrl::FILELIST_MODIFY;
282 if(bShowIgnores)
283 mask|= CGitStatusListCtrl::FILELIST_IGNORE;
284 if(bShowUnRev)
285 mask|= CGitStatusListCtrl::FILELIST_UNVER;
286 this->UpdateFileList(mask,bUpdate,(CTGitPathList*)pathList);
289 #if 0
290 int refetchcounter = 0;
291 BOOL bRet = TRUE;
292 Invalidate();
293 // force the cursor to change
294 POINT pt;
295 GetCursorPos(&pt);
296 SetCursorPos(pt.x, pt.y);
298 m_mapFilenameToChecked.clear();
299 //m_StatusUrlList.Clear();
300 bool bHasChangelists = (m_changelists.size()>1 || (m_changelists.size()>0 && !m_bHasIgnoreGroup));
301 m_changelists.clear();
302 for (size_t i=0; i < m_arStatusArray.size(); i++)
304 FileEntry * entry = m_arStatusArray[i];
305 if ( bHasChangelists && entry->checked)
307 // If change lists are present, remember all checked entries
308 CString path = entry->GetPath().GetGitPathString();
309 m_mapFilenameToChecked[path] = true;
311 if ( (entry->status==git_wc_status_unversioned || entry->status==git_wc_status_missing ) && entry->checked )
313 // The user manually selected an unversioned or missing file. We remember
314 // this so that the selection can be restored when refreshing.
315 CString path = entry->GetPath().GetGitPathString();
316 m_mapFilenameToChecked[path] = true;
318 else if ( entry->status > git_wc_status_normal && !entry->checked )
320 // The user manually deselected a versioned file. We remember
321 // this so that the deselection can be restored when refreshing.
322 CString path = entry->GetPath().GetGitPathString();
323 m_mapFilenameToChecked[path] = false;
327 // use a sorted path list to make sure we fetch the status of
328 // parent items first, *then* the child items (if any)
329 CTGitPathList sortedPathList = pathList;
330 sortedPathList.SortByPathname();
333 bRet = TRUE;
334 m_nTargetCount = 0;
335 m_bHasExternalsFromDifferentRepos = FALSE;
336 m_bHasExternals = FALSE;
337 m_bHasUnversionedItems = FALSE;
338 m_bHasLocks = false;
339 m_bHasChangeLists = false;
340 m_bShowIgnores = bShowIgnores;
341 m_nSortedColumn = 0;
342 m_bBlock = TRUE;
343 m_bBusy = true;
344 m_bEmpty = false;
345 Invalidate();
347 // first clear possible status data left from
348 // previous GetStatus() calls
349 ClearStatusArray();
351 m_StatusFileList = sortedPathList;
353 // Since Git_client_status() returns all files, even those in
354 // folders included with Git:externals we need to check if all
355 // files we get here belongs to the same repository.
356 // It is possible to commit changes in an external folder, as long
357 // as the folder belongs to the same repository (but another path),
358 // but it is not possible to commit all files if the externals are
359 // from a different repository.
361 // To check if all files belong to the same repository, we compare the
362 // UUID's - if they're identical then the files belong to the same
363 // repository and can be committed. But if they're different, then
364 // tell the user to commit all changes in the external folders
365 // first and exit.
366 CStringA sUUID; // holds the repo UUID
367 CTGitPathList arExtPaths; // list of Git:external paths
369 GitConfig config;
371 m_sURL.Empty();
373 m_nTargetCount = sortedPathList.GetCount();
375 GitStatus status(m_pbCanceled);
376 if(m_nTargetCount > 1 && sortedPathList.AreAllPathsFilesInOneDirectory())
378 // This is a special case, where we've been given a list of files
379 // all from one folder
380 // We handle them by setting a status filter, then requesting the Git status of
381 // all the files in the directory, filtering for the ones we're interested in
382 status.SetFilter(sortedPathList);
384 // if all selected entries are files, we don't do a recursive status
385 // fetching. But if only one is a directory, we have to recurse.
386 git_depth_t depth = git_depth_files;
387 // We have set a filter. If all selected items were files or we fetch
388 // the status not recursively, then we have to include
389 // ignored items too - the user has selected them
390 bool bShowIgnoresRight = true;
391 for (int fcindex=0; fcindex<sortedPathList.GetCount(); ++fcindex)
393 if (sortedPathList[fcindex].IsDirectory())
395 depth = git_depth_infinity;
396 bShowIgnoresRight = false;
397 break;
400 if(!FetchStatusForSingleTarget(config, status, sortedPathList.GetCommonDirectory(), bUpdate, sUUID, arExtPaths, true, depth, bShowIgnoresRight))
402 bRet = FALSE;
405 else
407 for(int nTarget = 0; nTarget < m_nTargetCount; nTarget++)
409 // check whether the path we want the status for is already fetched due to status-fetching
410 // of a parent path.
411 // this check is only done for file paths, because folder paths could be included already
412 // but not recursively
413 if (sortedPathList[nTarget].IsDirectory() || GetListEntry(sortedPathList[nTarget]) == NULL)
415 if(!FetchStatusForSingleTarget(config, status, sortedPathList[nTarget], bUpdate, sUUID,
416 arExtPaths, false, git_depth_infinity, bShowIgnores))
418 bRet = FALSE;
424 // remove the 'helper' files of conflicted items from the list.
425 // otherwise they would appear as unversioned files.
426 for (INT_PTR cind = 0; cind < m_ConflictFileList.GetCount(); ++cind)
428 for (size_t i=0; i < m_arStatusArray.size(); i++)
430 if (m_arStatusArray[i]->GetPath().IsEquivalentTo(m_ConflictFileList[cind]))
432 delete m_arStatusArray[i];
433 m_arStatusArray.erase(m_arStatusArray.begin()+i);
434 break;
438 refetchcounter++;
439 } while(!BuildStatistics() && (refetchcounter < 2) && (*m_pbCanceled==false));
441 if (bShowUserProps)
442 FetchUserProperties();
444 m_ColumnManager.UpdateUserPropList (m_arStatusArray);
446 m_bBlock = FALSE;
447 m_bBusy = false;
448 GetCursorPos(&pt);
449 SetCursorPos(pt.x, pt.y);
450 return bRet;
451 #endif
452 BuildStatistics();
453 return TRUE;
457 // Fetch all local properties for all elements in the status array
459 void CGitStatusListCtrl::FetchUserProperties()
461 #if 0
462 GitPool globalPool;
464 for (size_t i = 0, count = m_arStatusArray.size(); i < count; ++i)
466 // local / temp pool to hold parameters and props for a single item
468 GitPool localPool ((apr_pool_t*)globalPool);
470 // open working copy for this path
472 const char* path = m_arStatusArray[i]->path.GetGitApiPath (localPool);
474 Git_wc_adm_access_t *adm_access = NULL;
475 Git_error_t * error = Git_wc_adm_probe_open3 ( &adm_access
476 , NULL
477 , path
478 , FALSE // no write lock
479 , 0 // lock just the directory/file itself
480 , NULL
481 , NULL
482 , localPool);
483 if (error == NULL)
485 // get the props and add them to the status info
487 apr_hash_t* props = NULL;
488 Git_error_t * error = Git_wc_prop_list ( &props
489 , path
490 , adm_access
491 , localPool);
492 if (error == NULL)
494 for ( apr_hash_index_t *index
495 = apr_hash_first (localPool, props)
496 ; index != NULL
497 ; index = apr_hash_next (index))
499 // extract next entry from hash
501 const char* key = NULL;
502 ptrdiff_t keyLen;
503 const char** val = NULL;
505 apr_hash_this ( index
506 , reinterpret_cast<const void**>(&key)
507 , &keyLen
508 , reinterpret_cast<void**>(&val));
510 // decode / dispatch it
512 CString name = CUnicodeUtils::GetUnicode (key);
513 CString value = CUnicodeUtils::GetUnicode (*val);
515 // store in property container (truncate it after ~100 chars)
517 m_arStatusArray[i]->present_props[name]
518 = value.Left (GitSLC_MAXUSERPROPLENGTH);
521 error = Git_wc_adm_close2 (adm_access, localPool);
523 Git_error_clear (error);
525 #endif
528 // Get the show-flags bitmap value which corresponds to a particular Git status
529 DWORD CGitStatusListCtrl::GetShowFlagsFromGitStatus(git_wc_status_kind status)
531 switch (status)
533 case git_wc_status_none:
534 case git_wc_status_unversioned:
535 return GITSLC_SHOWUNVERSIONED;
536 case git_wc_status_ignored:
537 if (!m_bShowIgnores)
538 return GITSLC_SHOWDIRECTS;
539 return GITSLC_SHOWDIRECTS|GITSLC_SHOWIGNORED;
540 case git_wc_status_incomplete:
541 return GITSLC_SHOWINCOMPLETE;
542 case git_wc_status_normal:
543 return GITSLC_SHOWNORMAL;
544 case git_wc_status_external:
545 return GITSLC_SHOWEXTERNAL;
546 case git_wc_status_added:
547 return GITSLC_SHOWADDED;
548 case git_wc_status_missing:
549 return GITSLC_SHOWMISSING;
550 case git_wc_status_deleted:
551 return GITSLC_SHOWREMOVED;
552 case git_wc_status_replaced:
553 return GITSLC_SHOWREPLACED;
554 case git_wc_status_modified:
555 return GITSLC_SHOWMODIFIED;
556 case git_wc_status_merged:
557 return GITSLC_SHOWMERGED;
558 case git_wc_status_conflicted:
559 return GITSLC_SHOWCONFLICTED;
560 case git_wc_status_obstructed:
561 return GITSLC_SHOWOBSTRUCTED;
562 default:
563 // we should NEVER get here!
564 ASSERT(FALSE);
565 break;
567 return 0;
570 void CGitStatusListCtrl::Show(unsigned int dwShow, unsigned int dwCheck /*=0*/, bool /*bShowFolders*/ /* = true */,BOOL UpdateStatusList,bool UseStoredCheckStatus)
572 CWinApp * pApp = AfxGetApp();
573 if (pApp)
574 pApp->DoWaitCursor(1);
576 Locker lock(m_critSec);
577 WORD langID = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());
579 //SetItemCount(listIndex);
580 SetRedraw(FALSE);
581 DeleteAllItems();
582 m_nSelected = 0;
584 m_nShownUnversioned = 0;
585 m_nShownModified = 0;
586 m_nShownAdded = 0;
587 m_nShownDeleted = 0;
588 m_nShownConflicted = 0;
589 m_nShownFiles = 0;
590 m_nShownSubmodules = 0;
592 if(UpdateStatusList)
594 m_arStatusArray.clear();
595 for(int i=0;i<this->m_StatusFileList.GetCount();i++)
597 m_arStatusArray.push_back((CTGitPath*)&m_StatusFileList[i]);
600 for(int i=0;i<this->m_UnRevFileList.GetCount();i++)
602 m_arStatusArray.push_back((CTGitPath*)&m_UnRevFileList[i]);
605 for(int i=0;i<this->m_IgnoreFileList.GetCount();i++)
607 m_arStatusArray.push_back((CTGitPath*)&m_IgnoreFileList[i]);
610 PrepareGroups();
611 if (m_nSortedColumn >= 0)
613 CSorter predicate (&m_ColumnManager, m_nSortedColumn, m_bAscending);
614 std::sort(m_arStatusArray.begin(), m_arStatusArray.end(), predicate);
617 int index =0;
618 for(int i=0;i<this->m_arStatusArray.size();i++)
620 //set default checkbox status
621 CTGitPath* entry = ((CTGitPath*)m_arStatusArray[i]);
622 CString path = entry->GetGitPathString();
623 if (m_mapFilenameToChecked.size()!=0 && m_mapFilenameToChecked.find(path) != m_mapFilenameToChecked.end())
625 entry->m_Checked=m_mapFilenameToChecked[path];
627 else if (!UseStoredCheckStatus)
629 bool autoSelectSubmodules = !(entry->IsDirectory() && m_bDoNotAutoselectSubmodules);
630 if(entry->m_Action & dwCheck && autoSelectSubmodules)
631 entry->m_Checked=true;
632 else
633 entry->m_Checked=false;
634 m_mapFilenameToChecked[path] = entry->m_Checked;
637 if(entry->m_Action & dwShow)
639 AddEntry(entry,langID,index);
640 index++;
644 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
645 for (int col = 0; col <= maxcol; col++)
646 SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));
648 SetRedraw(TRUE);
649 GetStatisticsString();
651 CHeaderCtrl * pHeader = GetHeaderCtrl();
652 HDITEM HeaderItem = {0};
653 HeaderItem.mask = HDI_FORMAT;
654 for (int i=0; i<pHeader->GetItemCount(); ++i)
656 pHeader->GetItem(i, &HeaderItem);
657 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
658 pHeader->SetItem(i, &HeaderItem);
660 if (m_nSortedColumn >= 0)
662 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
663 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
664 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
667 #if 0
668 if (nSelectedEntry)
670 SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);
671 EnsureVisible(nSelectedEntry, false);
673 else
675 // Restore the item at the top of the list.
676 for (int i=0;GetTopIndex() != nTopIndex;i++)
678 if ( !EnsureVisible(nTopIndex+i,false) )
680 break;
684 #endif
685 if (pApp)
686 pApp->DoWaitCursor(-1);
688 Invalidate();
690 m_dwShow = dwShow;
692 this->BuildStatistics();
694 #if 0
696 CWinApp * pApp = AfxGetApp();
697 if (pApp)
698 pApp->DoWaitCursor(1);
700 m_bShowFolders = bShowFolders;
702 int nTopIndex = GetTopIndex();
703 POSITION posSelectedEntry = GetFirstSelectedItemPosition();
704 int nSelectedEntry = 0;
705 if (posSelectedEntry)
706 nSelectedEntry = GetNextSelectedItem(posSelectedEntry);
707 SetRedraw(FALSE);
708 DeleteAllItems();
710 PrepareGroups();
712 m_arListArray.clear();
714 m_arListArray.reserve(m_arStatusArray.size());
715 SetItemCount (static_cast<int>(m_arStatusArray.size()));
717 int listIndex = 0;
718 for (size_t i=0; i < m_arStatusArray.size(); ++i)
720 FileEntry * entry = m_arStatusArray[i];
721 if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))
722 continue;
723 if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))
724 continue;
725 if (entry->IsFolder() && (!bShowFolders))
726 continue; // don't show folders if they're not wanted.
728 #if 0
729 git_wc_status_kind status = GitStatus::GetMoreImportant(entry->status, entry->remotestatus);
730 DWORD showFlags = GetShowFlagsFromGitStatus(status);
731 if (entry->IsLocked())
732 showFlags |= SVNSLC_SHOWLOCKS;
733 if (entry->switched)
734 showFlags |= SVNSLC_SHOWSWITCHED;
735 if (!entry->changelist.IsEmpty())
736 showFlags |= SVNSLC_SHOWINCHANGELIST;
737 #endif
738 bool bAllowCheck = ((entry->changelist.Compare(GITSLC_IGNORECHANGELIST) != 0)
739 && (m_bCheckIfGroupsExist || (m_changelists.size()==0 || (m_changelists.size()==1 && m_bHasIgnoreGroup))));
741 // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item
742 #if 0
743 if (status != Git_wc_status_ignored || (entry->direct) || (dwShow & GitSLC_SHOWIGNORED))
745 if ((!entry->IsFolder()) && (status == Git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))
747 if (PathFileExists(entry->GetPath().GetWinPath()))
749 m_arListArray.push_back(i);
750 if ((dwCheck & SVNSLC_SHOWREMOVEDANDPRESENT)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))
752 if (bAllowCheck)
753 entry->checked = true;
755 AddEntry(entry, langID, listIndex++);
758 else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))
760 m_arListArray.push_back(i);
761 if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))
763 if (bAllowCheck)
764 entry->checked = true;
766 AddEntry(entry, langID, listIndex++);
768 else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))
770 m_arListArray.push_back(i);
771 if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))
773 if (bAllowCheck)
774 entry->checked = true;
776 AddEntry(entry, langID, listIndex++);
779 #endif
782 SetItemCount(listIndex);
784 m_ColumnManager.UpdateRelevance (m_arStatusArray, m_arListArray);
786 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
787 for (int col = 0; col <= maxcol; col++)
788 SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));
790 SetRedraw(TRUE);
791 GetStatisticsString();
793 CHeaderCtrl * pHeader = GetHeaderCtrl();
794 HDITEM HeaderItem = {0};
795 HeaderItem.mask = HDI_FORMAT;
796 for (int i=0; i<pHeader->GetItemCount(); ++i)
798 pHeader->GetItem(i, &HeaderItem);
799 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
800 pHeader->SetItem(i, &HeaderItem);
802 if (m_nSortedColumn)
804 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
805 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
806 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
809 if (nSelectedEntry)
811 SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);
812 EnsureVisible(nSelectedEntry, false);
814 else
816 // Restore the item at the top of the list.
817 for (int i=0;GetTopIndex() != nTopIndex;i++)
819 if ( !EnsureVisible(nTopIndex+i,false) )
821 break;
826 if (pApp)
827 pApp->DoWaitCursor(-1);
829 m_bEmpty = (GetItemCount() == 0);
830 Invalidate();
831 #endif
835 void CGitStatusListCtrl::Show(unsigned int /*dwShow*/, const CTGitPathList& checkedList, bool /*bShowFolders*/ /* = true */)
837 DeleteAllItems();
838 for(int i=0;i<checkedList.GetCount();i++)
839 this->AddEntry((CTGitPath *)&checkedList[i],0,i);
840 return ;
841 #if 0
843 Locker lock(m_critSec);
844 WORD langID = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());
846 CWinApp * pApp = AfxGetApp();
847 if (pApp)
848 pApp->DoWaitCursor(1);
849 m_dwShow = dwShow;
850 m_bShowFolders = bShowFolders;
851 m_nSelected = 0;
852 int nTopIndex = GetTopIndex();
853 POSITION posSelectedEntry = GetFirstSelectedItemPosition();
854 int nSelectedEntry = 0;
855 if (posSelectedEntry)
856 nSelectedEntry = GetNextSelectedItem(posSelectedEntry);
857 SetRedraw(FALSE);
858 DeleteAllItems();
860 PrepareGroups();
862 m_arListArray.clear();
864 m_arListArray.reserve(m_arStatusArray.size());
865 SetItemCount (static_cast<int>(m_arStatusArray.size()));
867 int listIndex = 0;
868 for (size_t i=0; i < m_arStatusArray.size(); ++i)
870 FileEntry * entry = m_arStatusArray[i];
871 if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))
872 continue;
873 if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))
874 continue;
875 if (entry->IsFolder() && (!bShowFolders))
876 continue; // don't show folders if they're not wanted.
877 #if 0
878 git_wc_status_kind status = SVNStatus::GetMoreImportant(entry->status, entry->remotestatus);
879 DWORD showFlags = GetShowFlagsFromSVNStatus(status);
880 if (entry->IsLocked())
881 showFlags |= SVNSLC_SHOWLOCKS;
882 if (!entry->changelist.IsEmpty())
883 showFlags |= SVNSLC_SHOWINCHANGELIST;
885 // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item
886 if (status != git_wc_status_ignored || (entry->direct) || (dwShow & SVNSLC_SHOWIGNORED))
888 for (int npath = 0; npath < checkedList.GetCount(); ++npath)
890 if (entry->GetPath().IsEquivalentTo(checkedList[npath]))
892 entry->checked = true;
893 break;
896 if ((!entry->IsFolder()) && (status == git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))
898 if (PathFileExists(entry->GetPath().GetWinPath()))
900 m_arListArray.push_back(i);
901 AddEntry(entry, langID, listIndex++);
904 else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))
906 m_arListArray.push_back(i);
907 AddEntry(entry, langID, listIndex++);
909 else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))
911 m_arListArray.push_back(i);
912 AddEntry(entry, langID, listIndex++);
914 else if (entry->switched)
916 m_arListArray.push_back(i);
917 AddEntry(entry, langID, listIndex++);
920 #endif
923 SetItemCount(listIndex);
925 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
926 for (int col = 0; col <= maxcol; col++)
927 SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));
929 SetRedraw(TRUE);
930 GetStatisticsString();
932 CHeaderCtrl * pHeader = GetHeaderCtrl();
933 HDITEM HeaderItem = {0};
934 HeaderItem.mask = HDI_FORMAT;
935 for (int i=0; i<pHeader->GetItemCount(); ++i)
937 pHeader->GetItem(i, &HeaderItem);
938 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
939 pHeader->SetItem(i, &HeaderItem);
941 if (m_nSortedColumn)
943 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
944 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
945 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
948 if (nSelectedEntry)
950 SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);
951 EnsureVisible(nSelectedEntry, false);
953 else
955 // Restore the item at the top of the list.
956 for (int i=0;GetTopIndex() != nTopIndex;i++)
958 if ( !EnsureVisible(nTopIndex+i,false) )
960 break;
965 if (pApp)
966 pApp->DoWaitCursor(-1);
968 m_bEmpty = (GetItemCount() == 0);
969 Invalidate();
970 #endif
973 int CGitStatusListCtrl::GetColumnIndex(int mask)
975 int i=0;
976 for(i=0;i<32;i++)
977 if(mask&0x1)
978 return i;
979 else
980 mask=mask>>1;
981 return -1;
983 void CGitStatusListCtrl::AddEntry(CTGitPath * GitPath, WORD /*langID*/, int listIndex)
985 static CString from(MAKEINTRESOURCE(IDS_STATUSLIST_FROM));
986 static HINSTANCE hResourceHandle(AfxGetResourceHandle());
987 static bool abbreviateRenamings(((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\AbbreviateRenamings"), FALSE)) == TRUE);
988 static bool relativeTimes = (CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE) != FALSE);
990 CString path = GitPath->GetGitPathString();
992 m_bBlock = TRUE;
993 int index = listIndex;
994 int nCol = 1;
995 CString entryname = GitPath->GetGitPathString();
996 int icon_idx = 0;
997 if (GitPath->IsDirectory())
999 icon_idx = m_nIconFolder;
1000 m_nShownSubmodules++;
1002 else
1004 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(*GitPath);
1005 m_nShownFiles++;
1007 switch (GitPath->m_Action)
1009 case CTGitPath::LOGACTIONS_ADDED:
1010 case CTGitPath::LOGACTIONS_COPY:
1011 m_nShownAdded++;
1012 break;
1013 case CTGitPath::LOGACTIONS_DELETED:
1014 m_nShownDeleted++;
1015 break;
1016 case CTGitPath::LOGACTIONS_REPLACED:
1017 case CTGitPath::LOGACTIONS_MODIFIED:
1018 case CTGitPath::LOGACTIONS_MERGED:
1019 m_nShownModified++;
1020 break;
1021 case CTGitPath::LOGACTIONS_UNMERGED:
1022 m_nShownConflicted++;
1023 break;
1024 case CTGitPath::LOGACTIONS_UNVER:
1025 m_nShownUnversioned++;
1026 break;
1027 default:
1028 m_nShownUnversioned++;
1029 break;
1031 if(GitPath->m_Action & (CTGitPath::LOGACTIONS_REPLACED|CTGitPath::LOGACTIONS_COPY) && !GitPath->GetGitOldPathString().IsEmpty())
1033 if (!abbreviateRenamings)
1035 // relative path
1036 CString rename;
1037 rename.Format(from, GitPath->GetGitOldPathString());
1038 entryname += _T(" ") + rename;
1040 else
1042 CTGitPathList tgpl;
1043 tgpl.AddPath(*GitPath);
1044 CTGitPath old(GitPath->GetGitOldPathString());
1045 tgpl.AddPath(old);
1046 CString commonRoot = tgpl.GetCommonRoot().GetGitPathString();
1047 if (!commonRoot.IsEmpty())
1048 commonRoot += _T("/");
1049 if (old.GetFileOrDirectoryName() == GitPath->GetFileOrDirectoryName() && old.GetContainingDirectory().GetGitPathString() != "" && GitPath->GetContainingDirectory().GetGitPathString())
1051 entryname = commonRoot + _T("{") + GitPath->GetGitOldPathString().Mid(commonRoot.GetLength(), old.GetGitPathString().GetLength() - commonRoot.GetLength() - old.GetFileOrDirectoryName().GetLength() - 1) + _T(" => ") + GitPath->GetGitPathString().Mid(commonRoot.GetLength(), GitPath->GetGitPathString().GetLength() - commonRoot.GetLength() - old.GetFileOrDirectoryName().GetLength() - 1) + _T("}/") + old.GetFileOrDirectoryName();
1053 else if (!commonRoot.IsEmpty())
1055 entryname = commonRoot + _T("{") + GitPath->GetGitOldPathString().Mid(commonRoot.GetLength()) + _T(" => ") + GitPath->GetGitPathString().Mid(commonRoot.GetLength()) + _T("}");
1057 else
1059 entryname = GitPath->GetGitOldPathString().Mid(commonRoot.GetLength()) + _T(" => ") + GitPath->GetGitPathString().Mid(commonRoot.GetLength());
1064 InsertItem(index, entryname, icon_idx);
1065 if (m_restorepaths.find(GitPath->GetWinPathString()) != m_restorepaths.end())
1066 SetItemState(index, INDEXTOOVERLAYMASK(OVL_RESTORE), TVIS_OVERLAYMASK);
1068 this->SetItemData(index, (DWORD_PTR)GitPath);
1069 // SVNSLC_COLFILENAME
1070 SetItemText(index, nCol++, GitPath->GetFileOrDirectoryName());
1071 // SVNSLC_COLEXT
1072 SetItemText(index, nCol++, GitPath->GetFileExtension());
1073 // SVNSLC_COLSTATUS
1074 SetItemText(index, nCol++, GitPath->GetActionName());
1076 SetItemText(index, GetColumnIndex(GITSLC_COLADD),GitPath->m_StatAdd);
1077 SetItemText(index, GetColumnIndex(GITSLC_COLDEL),GitPath->m_StatDel);
1080 CString modificationDate;
1081 __int64 filetime = GitPath->GetLastWriteTime();
1082 if (filetime && (GitPath->m_Action != CTGitPath::LOGACTIONS_DELETED))
1084 FILETIME* f = (FILETIME*)(__int64*)&filetime;
1085 modificationDate = CLoglistUtils::FormatDateAndTime(CTime(g_Git.filetime_to_time_t(f)), DATE_SHORTDATE, true, relativeTimes);
1087 SetItemText(index, GetColumnIndex(GITSLC_COLMODIFICATIONDATE), modificationDate);
1090 SetCheck(index, GitPath->m_Checked);
1091 if (GitPath->m_Checked)
1092 m_nSelected++;
1095 if( GitPath->m_Action & CTGitPath::LOGACTIONS_IGNORE)
1096 SetItemGroup(index, 2);
1097 else if( GitPath->m_Action & CTGitPath::LOGACTIONS_UNVER)
1098 SetItemGroup(index,1);
1099 else
1101 SetItemGroup(index, GitPath->m_ParentNo&(PARENT_MASK|MERGE_MASK));
1104 m_bBlock = FALSE;
1108 #if 0
1109 void CGitStatusListCtrl::AddEntry(FileEntry * entry, WORD langID, int listIndex)
1111 static HINSTANCE hResourceHandle(AfxGetResourceHandle());
1113 CString path = entry->GetPath().GetGitPathString();
1114 if ( m_mapFilenameToChecked.size()!=0 && m_mapFilenameToChecked.find(path) != m_mapFilenameToChecked.end() )
1116 // The user manually de-/selected an item. We now restore this status
1117 // when refreshing.
1118 entry->checked = m_mapFilenameToChecked[path];
1121 m_bBlock = TRUE;
1122 TCHAR buf[100];
1123 int index = listIndex;
1124 int nCol = 1;
1125 CString entryname = entry->GetDisplayName();
1126 int icon_idx = 0;
1127 if (entry->isfolder)
1128 icon_idx = m_nIconFolder;
1129 else
1131 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(entry->path);
1133 // relative path
1134 InsertItem(index, entryname, icon_idx);
1135 // SVNSLC_COLFILENAME
1136 SetItemText(index, nCol++, entry->path.GetFileOrDirectoryName());
1137 // SVNSLC_COLEXT
1138 SetItemText(index, nCol++, entry->path.GetFileExtension());
1139 // SVNSLC_COLSTATUS
1140 if (entry->isNested)
1142 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));
1143 SetItemText(index, nCol++, sTemp);
1145 else
1147 GitStatus::GetStatusString(hResourceHandle, entry->status, buf, _countof(buf), (WORD)langID);
1148 if ((entry->copied)&&(_tcslen(buf)>1))
1149 _tcscat_s(buf, 100, _T(" (+)"));
1150 if ((entry->switched)&&(_tcslen(buf)>1))
1151 _tcscat_s(buf, 100, _T(" (s)"));
1152 SetItemText(index, nCol++, buf);
1154 // SVNSLC_COLREMOTESTATUS
1155 if (entry->isNested)
1157 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));
1158 SetItemText(index, nCol++, sTemp);
1160 else
1162 #if 0
1163 SVNStatus::GetStatusString(hResourceHandle, entry->remotestatus, buf, _countof(buf), (WORD)langID);
1164 if ((entry->copied)&&(_tcslen(buf)>1))
1165 _tcscat_s(buf, 100, _T(" (+)"));
1166 if ((entry->switched)&&(_tcslen(buf)>1))
1167 _tcscat_s(buf, 100, _T(" (s)"));
1168 #endif
1169 SetItemText(index, nCol++, buf);
1171 // SVNSLC_COLTEXTSTATUS
1172 if (entry->isNested)
1174 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));
1175 SetItemText(index, nCol++, sTemp);
1177 else
1179 #if 0
1180 SVNStatus::GetStatusString(hResourceHandle, entry->textstatus, buf, _countof(buf), (WORD)langID);
1181 if ((entry->copied)&&(_tcslen(buf)>1))
1182 _tcscat_s(buf, 100, _T(" (+)"));
1183 if ((entry->switched)&&(_tcslen(buf)>1))
1184 _tcscat_s(buf, 100, _T(" (s)"));
1185 #endif
1186 SetItemText(index, nCol++, buf);
1188 // SVNSLC_COLPROPSTATUS
1189 if (entry->isNested)
1191 SetItemText(index, nCol++, _T(""));
1193 else
1195 #if 0
1196 SVNStatus::GetStatusString(hResourceHandle, entry->propstatus, buf, _countof(buf), (WORD)langID);
1197 if ((entry->copied)&&(_tcslen(buf)>1))
1198 _tcscat_s(buf, 100, _T(" (+)"));
1199 if ((entry->switched)&&(_tcslen(buf)>1))
1200 _tcscat_s(buf, 100, _T(" (s)"));
1201 #endif
1202 SetItemText(index, nCol++, buf);
1204 // SVNSLC_COLREMOTETEXT
1205 if (entry->isNested)
1207 SetItemText(index, nCol++, _T(""));
1209 else
1211 #if 0
1212 SVNStatus::GetStatusString(hResourceHandle, entry->remotetextstatus, buf, _countof(buf), (WORD)langID);
1213 SetItemText(index, nCol++, buf);
1214 #endif
1216 // SVNSLC_COLREMOTEPROP
1217 if (entry->isNested)
1219 SetItemText(index, nCol++, _T(""));
1221 else
1223 // SVNStatus::GetStatusString(hResourceHandle, entry->remotepropstatus, buf, _countof(buf), (WORD)langID);
1224 SetItemText(index, nCol++, buf);
1226 // SVNSLC_COLURL
1227 // SetItemText(index, nCol++, entry->url);
1228 // SVNSLC_COLLOCK
1229 #if 0
1230 if (!m_HeadRev.IsHead())
1232 // we have contacted the repository
1234 // decision-matrix
1235 // wc repository text
1236 // "" "" ""
1237 // "" UID1 owner
1238 // UID1 UID1 owner
1239 // UID1 "" lock has been broken
1240 // UID1 UID2 lock has been stolen
1241 if (entry->lock_token.IsEmpty() || (entry->lock_token.Compare(entry->lock_remotetoken)==0))
1243 if (entry->lock_owner.IsEmpty())
1244 SetItemText(index, nCol++, entry->lock_remoteowner);
1245 else
1246 SetItemText(index, nCol++, entry->lock_owner);
1248 else if (entry->lock_remotetoken.IsEmpty())
1250 // broken lock
1251 CString temp(MAKEINTRESOURCE(IDS_STATUSLIST_LOCKBROKEN));
1252 SetItemText(index, nCol++, temp);
1254 else
1256 // stolen lock
1257 CString temp;
1258 temp.Format(IDS_STATUSLIST_LOCKSTOLEN, (LPCTSTR)entry->lock_remoteowner);
1259 SetItemText(index, nCol++, temp);
1262 else
1263 SetItemText(index, nCol++, entry->lock_owner);
1264 // SVNSLC_COLLOCKCOMMENT
1265 SetItemText(index, nCol++, entry->lock_comment);
1266 // SVNSLC_COLAUTHOR
1267 SetItemText(index, nCol++, entry->last_commit_author);
1268 // SVNSLC_COLREVISION
1269 CString temp;
1270 temp.Format(_T("%ld"), entry->last_commit_rev);
1271 if (entry->last_commit_rev > 0)
1272 SetItemText(index, nCol++, temp);
1273 else
1274 SetItemText(index, nCol++, _T(""));
1275 // SVNSLC_COLREMOTEREVISION
1276 temp.Format(_T("%ld"), entry->remoterev);
1277 if (entry->remoterev > 0)
1278 SetItemText(index, nCol++, temp);
1279 else
1280 SetItemText(index, nCol++, _T(""));
1281 // SVNSLC_COLDATE
1282 TCHAR datebuf[SVN_DATE_BUFFER];
1283 apr_time_t date = entry->last_commit_date;
1284 SVN::formatDate(datebuf, date, true);
1285 if (date)
1286 SetItemText(index, nCol++, datebuf);
1287 else
1288 SetItemText(index, nCol++, _T(""));
1289 // SVNSLC_COLSVNNEEDSLOCK
1290 BOOL bFoundSVNNeedsLock = entry->present_props.IsNeedsLockSet();
1291 CString strSVNNeedsLock = (bFoundSVNNeedsLock) ? _T("*") : _T("");
1292 SetItemText(index, nCol++, strSVNNeedsLock);
1293 // SVNSLC_COLCOPYFROM
1294 if (m_sURL.Compare(entry->copyfrom_url.Left(m_sURL.GetLength()))==0)
1295 temp = entry->copyfrom_url.Mid(m_sURL.GetLength());
1296 else
1297 temp = entry->copyfrom_url;
1298 SetItemText(index, nCol++, temp);
1299 // SVNSLC_COLMODIFICATIONDATE
1300 __int64 filetime = entry->GetPath().GetLastWriteTime();
1301 if ( (filetime) && (entry->status!=git_wc_status_deleted) )
1303 FILETIME* f = (FILETIME*)(__int64*)&filetime;
1304 TCHAR datebuf[SVN_DATE_BUFFER];
1305 SVN::formatDate(datebuf,*f,true);
1306 SetItemText(index, nCol++, datebuf);
1308 else
1310 SetItemText(index, nCol++, _T(""));
1313 // user-defined properties
1314 for ( int i = SVNSLC_NUMCOLUMNS, count = m_ColumnManager.GetColumnCount()
1315 ; i < count
1316 ; ++i)
1318 assert (i == nCol++);
1319 assert (m_ColumnManager.IsUserProp (i));
1321 CString name = m_ColumnManager.GetName(i);
1322 if (entry->present_props.HasProperty (name))
1324 const CString& propVal = entry->present_props [name];
1325 if (propVal.IsEmpty())
1326 SetItemText(index, i, m_sNoPropValueText);
1327 else
1328 SetItemText(index, i, propVal);
1330 else
1331 SetItemText(index, i, _T(""));
1334 SetCheck(index, entry->checked);
1335 if (entry->checked)
1336 m_nSelected++;
1337 if (m_changelists.find(entry->changelist) != m_changelists.end())
1338 SetItemGroup(index, m_changelists[entry->changelist]);
1339 else
1340 SetItemGroup(index, 0);
1341 m_bBlock = FALSE;
1342 #endif
1344 #endif
1345 bool CGitStatusListCtrl::SetItemGroup(int item, int groupindex)
1347 // if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS) == NULL)
1348 // return false;
1349 if (groupindex < 0)
1350 return false;
1351 LVITEM i = {0};
1352 i.mask = LVIF_GROUPID;
1353 i.iItem = item;
1354 i.iSubItem = 0;
1355 i.iGroupId = groupindex;
1357 return !!SetItem(&i);
1360 void CGitStatusListCtrl::OnHdnItemclick(NMHDR *pNMHDR, LRESULT *pResult)
1362 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
1363 *pResult = 0;
1364 if (m_bBlock)
1365 return;
1366 m_bBlock = TRUE;
1367 if (m_nSortedColumn == phdr->iItem)
1368 m_bAscending = !m_bAscending;
1369 else
1370 m_bAscending = TRUE;
1371 m_nSortedColumn = phdr->iItem;
1372 Show(m_dwShow, 0, m_bShowFolders,false,true);
1374 m_bBlock = FALSE;
1377 void CGitStatusListCtrl::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
1379 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1380 *pResult = 0;
1382 #define ISCHECKED(x) ((x) ? ((((x)&LVIS_STATEIMAGEMASK)>>12)-1) : FALSE)
1383 if ((m_bBlock)&&(m_bBlockUI))
1385 // if we're blocked, prevent changing of the check state
1386 if ((!ISCHECKED(pNMLV->uOldState) && ISCHECKED(pNMLV->uNewState))||
1387 (ISCHECKED(pNMLV->uOldState) && !ISCHECKED(pNMLV->uNewState)))
1388 *pResult = TRUE;
1392 BOOL CGitStatusListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
1394 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1395 *pResult = 0;
1396 CWnd* pParent = GetParent();
1397 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
1399 pParent->SendMessage(GITSLNM_ITEMCHANGED, pNMLV->iItem);
1402 if ((pNMLV->uNewState==0)||(pNMLV->uNewState & LVIS_SELECTED)||(pNMLV->uNewState & LVIS_FOCUSED))
1403 return FALSE;
1405 if (m_bBlock)
1406 return FALSE;
1408 bool bSelected = !!(ListView_GetItemState(m_hWnd, pNMLV->iItem, LVIS_SELECTED) & LVIS_SELECTED);
1409 int nListItems = GetItemCount();
1411 m_bBlock = TRUE;
1412 // was the item checked?
1414 //CTGitPath *gitpath=(CTGitPath*)GetItemData(pNMLV->iItem);
1415 //gitpath->m_Checked=GetCheck(pNMLV->iItem);
1417 if (GetCheck(pNMLV->iItem))
1419 CheckEntry(pNMLV->iItem, nListItems);
1420 if (bSelected)
1422 POSITION pos = GetFirstSelectedItemPosition();
1423 int index;
1424 while ((index = GetNextSelectedItem(pos)) >= 0)
1426 if (index != pNMLV->iItem)
1427 CheckEntry(index, nListItems);
1431 else
1433 UncheckEntry(pNMLV->iItem, nListItems);
1434 if (bSelected)
1436 POSITION pos = GetFirstSelectedItemPosition();
1437 int index;
1438 while ((index = GetNextSelectedItem(pos)) >= 0)
1440 if (index != pNMLV->iItem)
1441 UncheckEntry(index, nListItems);
1446 GetStatisticsString();
1447 m_bBlock = FALSE;
1448 NotifyCheck();
1450 return FALSE;
1453 void CGitStatusListCtrl::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)
1455 m_ColumnManager.OnColumnResized(pNMHDR,pResult);
1457 *pResult = FALSE;
1460 void CGitStatusListCtrl::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)
1462 m_ColumnManager.OnColumnMoved(pNMHDR, pResult);
1464 Invalidate(FALSE);
1467 void CGitStatusListCtrl::CheckEntry(int index, int /*nListItems*/)
1469 Locker lock(m_critSec);
1470 //FileEntry * entry = GetListEntry(index);
1471 CTGitPath *path=(CTGitPath*)GetItemData(index);
1472 ASSERT(path != NULL);
1473 if (path == NULL)
1474 return;
1475 m_mapFilenameToChecked[path->GetGitPathString()] = true;
1476 SetCheck(index, TRUE);
1477 //entry = GetListEntry(index);
1478 // if an unversioned item was checked, then we need to check if
1479 // the parent folders are unversioned too. If the parent folders actually
1480 // are unversioned, then check those too.
1481 #if 0
1482 if (entry->status == git_wc_status_unversioned)
1484 // we need to check the parent folder too
1485 const CTGitPath& folderpath = entry->path;
1486 for (int i=0; i< nListItems; ++i)
1488 FileEntry * testEntry = GetListEntry(i);
1489 ASSERT(testEntry != NULL);
1490 if (testEntry == NULL)
1491 continue;
1492 if (!testEntry->checked)
1494 if (testEntry->path.IsAncestorOf(folderpath) && (!testEntry->path.IsEquivalentTo(folderpath)))
1496 SetEntryCheck(testEntry,i,true);
1497 m_nSelected++;
1502 bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
1503 if ( (entry->status == git_wc_status_deleted) || (m_bCheckChildrenWithParent) || (bShift) )
1505 // if a deleted folder gets checked, we have to check all
1506 // children of that folder too.
1507 if (entry->path.IsDirectory())
1509 SetCheckOnAllDescendentsOf(entry, true);
1512 // if a deleted file or folder gets checked, we have to
1513 // check all parents of this item too.
1514 for (int i=0; i<nListItems; ++i)
1516 FileEntry * testEntry = GetListEntry(i);
1517 ASSERT(testEntry != NULL);
1518 if (testEntry == NULL)
1519 continue;
1520 if (!testEntry->checked)
1522 if (testEntry->path.IsAncestorOf(entry->path) && (!testEntry->path.IsEquivalentTo(entry->path)))
1524 if ((testEntry->status == git_wc_status_deleted)||(m_bCheckChildrenWithParent))
1526 SetEntryCheck(testEntry,i,true);
1527 m_nSelected++;
1528 // now we need to check all children of this parent folder
1529 SetCheckOnAllDescendentsOf(testEntry, true);
1535 #endif
1536 if ( !path->m_Checked )
1538 path->m_Checked = TRUE;
1539 m_nSelected++;
1543 void CGitStatusListCtrl::UncheckEntry(int index, int /*nListItems*/)
1545 Locker lock(m_critSec);
1546 CTGitPath *path=(CTGitPath*)GetItemData(index);
1547 ASSERT(path != NULL);
1548 if (path == NULL)
1549 return;
1550 SetCheck(index, FALSE);
1551 m_mapFilenameToChecked[path->GetGitPathString()] = false;
1552 //entry = GetListEntry(index);
1553 // item was unchecked
1554 #if 0
1555 if (entry->path.IsDirectory())
1557 // disable all files within an unselected folder, except when unchecking a folder with property changes
1558 bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
1559 if ( (entry->status != git_wc_status_modified) || (bShift) )
1561 SetCheckOnAllDescendentsOf(entry, false);
1564 else if (entry->status == git_wc_status_deleted)
1566 // a "deleted" file was unchecked, so uncheck all parent folders
1567 // and all children of those parents
1568 for (int i=0; i<nListItems; i++)
1570 FileEntry * testEntry = GetListEntry(i);
1571 ASSERT(testEntry != NULL);
1572 if (testEntry == NULL)
1573 continue;
1574 if (testEntry->checked)
1576 if (testEntry->path.IsAncestorOf(entry->path))
1578 if (testEntry->status == git_wc_status_deleted)
1580 SetEntryCheck(testEntry,i,false);
1581 m_nSelected--;
1583 SetCheckOnAllDescendentsOf(testEntry, false);
1589 #endif
1590 if ( path->m_Checked )
1592 path->m_Checked = FALSE;
1593 m_nSelected--;
1596 #if 0
1597 bool CGitStatusListCtrl::EntryPathCompareNoCase(const FileEntry* pEntry1, const FileEntry* pEntry2)
1599 return pEntry1->path < pEntry2->path;
1602 bool CGitStatusListCtrl::IsEntryVersioned(const FileEntry* pEntry1)
1604 return pEntry1->status != git_wc_status_unversioned;
1606 #endif
1607 bool CGitStatusListCtrl::BuildStatistics()
1610 bool bRefetchStatus = false;
1612 // now gather some statistics
1613 m_nUnversioned = 0;
1614 m_nNormal = 0;
1615 m_nModified = 0;
1616 m_nAdded = 0;
1617 m_nDeleted = 0;
1618 m_nConflicted = 0;
1619 m_nTotal = 0;
1620 m_nSelected = 0;
1621 m_nLineAdded = 0;
1622 m_nLineDeleted = 0;
1623 m_nRenamed = 0;
1625 for (int i=0; i < (int)m_arStatusArray.size(); ++i)
1627 int status=((CTGitPath*)m_arStatusArray[i])->m_Action;
1629 m_nLineAdded += _tstol(((CTGitPath*)m_arStatusArray[i])->m_StatAdd);
1630 m_nLineDeleted += _tstol(((CTGitPath*)m_arStatusArray[i])->m_StatDel);
1632 if(status&(CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY))
1633 m_nAdded++;
1635 if(status&CTGitPath::LOGACTIONS_DELETED)
1636 m_nDeleted++;
1638 if(status&(CTGitPath::LOGACTIONS_REPLACED|CTGitPath::LOGACTIONS_MODIFIED))
1639 m_nModified++;
1641 if(status&CTGitPath::LOGACTIONS_UNMERGED)
1642 m_nConflicted++;
1644 if(status&(CTGitPath::LOGACTIONS_IGNORE|CTGitPath::LOGACTIONS_UNVER))
1645 m_nUnversioned++;
1647 if(status&(CTGitPath::LOGACTIONS_REPLACED))
1648 m_nRenamed++;
1650 if(((CTGitPath*)m_arStatusArray[i])->m_Checked)
1651 m_nSelected++;
1653 // } // switch (entry->status)
1654 // } // if (entry)
1655 } // for (int i=0; i < (int)m_arStatusArray.size(); ++i)
1656 return !bRefetchStatus;
1658 return FALSE;
1662 int CGitStatusListCtrl::GetGroupFromPoint(POINT * ppt)
1664 // the point must be relative to the upper left corner of the control
1666 if (ppt == NULL)
1667 return -1;
1668 if (!IsGroupViewEnabled())
1669 return -1;
1671 POINT pt = *ppt;
1672 pt.x = 10;
1673 UINT flags = 0;
1674 int nItem = -1;
1675 RECT rc;
1676 GetWindowRect(&rc);
1677 while (((flags & LVHT_BELOW) == 0)&&(pt.y < rc.bottom))
1679 nItem = HitTest(pt, &flags);
1680 if ((flags & LVHT_ONITEM)||(flags & LVHT_EX_GROUP_HEADER))
1682 // the first item below the point
1684 // check if the point is too much right (i.e. if the point
1685 // is farther to the right than the width of the item)
1686 RECT r;
1687 GetItemRect(nItem, &r, LVIR_LABEL);
1688 if (ppt->x > r.right)
1689 return -1;
1691 LVITEM lv = {0};
1692 lv.mask = LVIF_GROUPID;
1693 lv.iItem = nItem;
1694 GetItem(&lv);
1695 int groupID = lv.iGroupId;
1696 // now we search upwards and check if the item above this one
1697 // belongs to another group. If it belongs to the same group,
1698 // we're not over a group header
1699 while (pt.y >= 0)
1701 pt.y -= 2;
1702 nItem = HitTest(pt, &flags);
1703 if ((flags & LVHT_ONITEM)&&(nItem >= 0))
1705 // the first item below the point
1706 LVITEM lv = {0};
1707 lv.mask = LVIF_GROUPID;
1708 lv.iItem = nItem;
1709 GetItem(&lv);
1710 if (lv.iGroupId != groupID)
1711 return groupID;
1712 else
1713 return -1;
1716 if (pt.y < 0)
1717 return groupID;
1718 return -1;
1720 pt.y += 2;
1722 return -1;
1725 void CGitStatusListCtrl::OnContextMenuGroup(CWnd * /*pWnd*/, CPoint point)
1727 POINT clientpoint = point;
1728 ScreenToClient(&clientpoint);
1729 if ((IsGroupViewEnabled())&&(GetGroupFromPoint(&clientpoint) >= 0))
1731 CMenu popup;
1732 if (popup.CreatePopupMenu())
1734 CString temp;
1735 temp.LoadString(IDS_STATUSLIST_CHECKGROUP);
1736 popup.AppendMenu(MF_STRING | MF_ENABLED, IDGITLC_CHECKGROUP, temp);
1737 temp.LoadString(IDS_STATUSLIST_UNCHECKGROUP);
1738 popup.AppendMenu(MF_STRING | MF_ENABLED, IDGITLC_UNCHECKGROUP, temp);
1739 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1740 bool bCheck = false;
1741 switch (cmd)
1743 case IDGITLC_CHECKGROUP:
1744 bCheck = true;
1745 // fall through here
1746 case IDGITLC_UNCHECKGROUP:
1748 int group = GetGroupFromPoint(&clientpoint);
1749 // go through all items and check/uncheck those assigned to the group
1750 // but block the OnLvnItemChanged handler
1751 m_bBlock = true;
1752 LVITEM lv;
1753 for (int i=0; i<GetItemCount(); ++i)
1755 SecureZeroMemory(&lv, sizeof(LVITEM));
1756 lv.mask = LVIF_GROUPID;
1757 lv.iItem = i;
1758 GetItem(&lv);
1760 if (lv.iGroupId == group)
1762 CTGitPath * entry = (CTGitPath*)GetItemData(i);
1763 if (entry)
1765 bool bOldCheck = entry->m_Checked;
1766 SetEntryCheck(entry, i, bCheck);
1767 if (bCheck != bOldCheck)
1769 if (bCheck)
1770 m_nSelected++;
1771 else
1772 m_nSelected--;
1778 GetStatisticsString();
1779 NotifyCheck();
1780 m_bBlock = false;
1782 break;
1788 void CGitStatusListCtrl::OnContextMenuList(CWnd * pWnd, CPoint point)
1791 //WORD langID = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());
1793 //bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
1794 CTGitPath * filepath;
1796 int selIndex = GetSelectionMark();
1797 if ((point.x == -1) && (point.y == -1))
1799 CRect rect;
1800 GetItemRect(selIndex, &rect, LVIR_LABEL);
1801 ClientToScreen(&rect);
1802 point = rect.CenterPoint();
1804 if ((GetSelectedCount() == 0) && (m_bHasCheckboxes))
1806 // nothing selected could mean the context menu is requested for
1807 // a group header
1808 OnContextMenuGroup(pWnd, point);
1810 else if (selIndex >= 0)
1812 //FileEntry * entry = GetListEntry(selIndex);
1814 filepath = (CTGitPath * )GetItemData(selIndex);
1816 ASSERT(filepath != NULL);
1817 if (filepath == NULL)
1818 return;
1820 //const CTGitPath& filepath = entry->path;
1821 int wcStatus = filepath->m_Action;
1822 // entry is selected, now show the popup menu
1823 Locker lock(m_critSec);
1824 CIconMenu popup;
1825 CMenu changelistSubMenu;
1826 CMenu ignoreSubMenu;
1827 if (popup.CreatePopupMenu())
1829 //Add Menu for version controlled file
1831 if (GetSelectedCount() > 0 && wcStatus & CTGitPath::LOGACTIONS_UNMERGED)
1833 if (GetSelectedCount() == 1 && (m_dwContextMenus & GITSLC_POPCONFLICT)/*&&(entry->textstatus == git_wc_status_conflicted)*/)
1835 popup.AppendMenuIcon(IDGITLC_EDITCONFLICT, IDS_MENUCONFLICT, IDI_CONFLICT);
1837 if (m_dwContextMenus & GITSLC_POPRESOLVE)
1839 popup.AppendMenuIcon(IDGITLC_RESOLVECONFLICT, IDS_STATUSLIST_CONTEXT_RESOLVED, IDI_RESOLVE);
1841 if ((m_dwContextMenus & GITSLC_POPRESOLVE)/*&&(entry->textstatus == git_wc_status_conflicted)*/)
1843 popup.AppendMenuIcon(IDGITLC_RESOLVETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS, IDI_RESOLVE);
1844 popup.AppendMenuIcon(IDGITLC_RESOLVEMINE, IDS_SVNPROGRESS_MENUUSEMINE, IDI_RESOLVE);
1846 if ((m_dwContextMenus & GITSLC_POPCONFLICT)||(m_dwContextMenus & GITSLC_POPRESOLVE))
1847 popup.AppendMenu(MF_SEPARATOR);
1850 if (GetSelectedCount() > 0)
1852 if (wcStatus & CTGitPath::LOGACTIONS_UNVER)
1853 if (m_dwContextMenus & GITSLC_POPADD)
1855 //if ( entry->IsFolder() )
1857 // popup.AppendMenuIcon(IDSVNLC_ADD_RECURSIVE, IDS_STATUSLIST_CONTEXT_ADD_RECURSIVE, IDI_ADD);
1859 //else
1861 popup.AppendMenuIcon(IDGITLC_ADD, IDS_STATUSLIST_CONTEXT_ADD, IDI_ADD);
1866 if (!(wcStatus &CTGitPath::LOGACTIONS_UNVER))
1868 if ( (m_dwContextMenus & GITSLC_POPCOMPAREWITHBASE) && GetSelectedCount()>0)
1870 if(filepath->m_ParentNo & MERGE_MASK)
1871 popup.AppendMenuIcon(IDGITLC_COMPARE, IDS_TREE_DIFF, IDI_DIFF);
1872 else
1873 popup.AppendMenuIcon(IDGITLC_COMPARE, IDS_LOG_COMPAREWITHBASE, IDI_DIFF);
1875 popup.SetDefaultItem(IDGITLC_COMPARE, FALSE);
1878 if ((m_dwContextMenus & this->GetContextMenuBit(IDGITLC_COMPAREWC)) && GetSelectedCount()>0 && m_bHasWC)
1880 if( (!m_CurrentVersion.IsEmpty()) && m_CurrentVersion != GIT_REV_ZERO)
1881 popup.AppendMenuIcon(IDGITLC_COMPAREWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1883 //Select one items
1884 if (GetSelectedCount() == 1)
1886 bool bEntryAdded = false;
1887 //if (entry->remotestatus <= git_wc_status_normal)
1889 // if (wcStatus > git_wc_status_normal)
1890 // {
1891 // if ((m_dwContextMenus & SVNSLC_POPGNUDIFF)&&(wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing))
1892 // {
1893 if(!g_Git.IsInitRepos() && (m_dwContextMenus&GITSLC_POPGNUDIFF))
1895 popup.AppendMenuIcon(IDGITLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1897 bEntryAdded = true;
1899 // }
1900 // }
1903 //else if (wcStatus != git_wc_status_deleted)
1905 // if (m_dwContextMenus & SVNSLC_POPCOMPARE)
1906 // {
1907 // popup.AppendMenuIcon(IDSVNLC_COMPAREWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1908 // popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);
1909 // bEntryAdded = true;
1910 // }
1911 // if (m_dwContextMenus & SVNSLC_POPGNUDIFF)
1912 // {
1913 // popup.AppendMenuIcon(IDSVNLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1914 // bEntryAdded = true;
1915 // }
1917 if (bEntryAdded)
1918 popup.AppendMenu(MF_SEPARATOR);
1920 //else if (GetSelectedCount() > 1)
1922 // if (m_dwContextMenus & SVNSLC_POPCOMMIT)
1923 // {
1924 // popup.AppendMenuIcon(IDSVNLC_COMMIT, IDS_STATUSLIST_CONTEXT_COMMIT, IDI_COMMIT);
1925 // popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);
1926 // }
1930 if( (!this->m_Rev1.IsEmpty()) || (!this->m_Rev1.IsEmpty()) )
1932 if(GetSelectedCount() == 1)
1934 if (m_dwContextMenus & this->GetContextMenuBit(IDGITLC_COMPARETWO))
1936 popup.AppendMenuIcon(IDGITLC_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
1937 popup.SetDefaultItem(IDGITLC_COMPARETWO, FALSE);
1939 if (m_dwContextMenus & this->GetContextMenuBit(IDGITLC_GNUDIFF2))
1941 popup.AppendMenuIcon(IDGITLC_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1946 //Select Multi item
1947 if (GetSelectedCount() > 0)
1949 if ((GetSelectedCount() == 2) && (m_dwContextMenus & this->GetContextMenuBit(GITSLC_POPCOMPARETWOFILES)) && (this->m_CurrentVersion.IsEmpty() || this->m_CurrentVersion == GIT_REV_ZERO))
1951 POSITION pos = GetFirstSelectedItemPosition();
1952 int index = GetNextSelectedItem(pos);
1953 if (index >= 0)
1955 CTGitPath * entry2 = NULL;
1956 bool bothItemsAreExistingFiles = true;
1957 entry2 = (CTGitPath * )GetItemData(index);
1958 if (entry2)
1959 bothItemsAreExistingFiles = !entry2->IsDirectory() && entry2->Exists();
1960 index = GetNextSelectedItem(pos);
1961 if (index >= 0)
1963 entry2 = (CTGitPath * )GetItemData(index);
1964 if (entry2)
1965 bothItemsAreExistingFiles = bothItemsAreExistingFiles && !entry2->IsDirectory() && entry2->Exists();
1966 if (bothItemsAreExistingFiles)
1967 popup.AppendMenuIcon(IDGITLC_COMPARETWOFILES, IDS_STATUSLIST_CONTEXT_COMPARETWOFILES, IDI_DIFF);
1971 // if ((GetSelectedCount() == 2)&&(m_dwContextMenus & SVNSLC_POPREPAIRMOVE))
1972 // {
1973 // POSITION pos = GetFirstSelectedItemPosition();
1974 // int index = GetNextSelectedItem(pos);
1975 // if (index >= 0)
1976 // {
1977 // FileEntry * entry = GetListEntry(index);
1978 // git_wc_status_kind status1 = git_wc_status_none;
1979 // git_wc_status_kind status2 = git_wc_status_none;
1980 // if (entry)
1981 // status1 = entry->status;
1982 // index = GetNextSelectedItem(pos);
1983 // if (index >= 0)
1984 // {
1985 // entry = GetListEntry(index);
1986 // if (entry)
1987 // status2 = entry->status;
1988 // if ((status1 == git_wc_status_missing && status2 == git_wc_status_unversioned) ||
1989 // (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned))
1990 // {
1991 // popup.AppendMenuIcon(IDSVNLC_REPAIRMOVE, IDS_STATUSLIST_CONTEXT_REPAIRMOVE);
1992 // }
1993 // }
1994 // }
1995 // }
1996 // if (wcStatus > git_wc_status_normal)
1997 // {
1998 // if (m_dwContextMenus & SVNSLC_POPREVERT)
1999 // {
2000 // // reverting missing folders is not possible
2001 // if (!entry->IsFolder() || (wcStatus != git_wc_status_missing))
2002 // {
2003 // popup.AppendMenuIcon(IDSVNLC_REVERT, IDS_MENUREVERT, IDI_REVERT);
2004 // }
2005 // }
2006 // }
2007 // if (entry->remotestatus > git_wc_status_normal)
2008 // {
2009 // if (m_dwContextMenus & SVNSLC_POPUPDATE)
2010 // {
2011 // popup.AppendMenuIcon(IDSVNLC_UPDATE, IDS_MENUUPDATE, IDI_UPDATE);
2012 // }
2013 // }
2016 if ( (GetSelectedCount() >0 ) && (!(wcStatus & CTGitPath::LOGACTIONS_UNVER)) && m_bHasWC)
2018 if ((m_dwContextMenus & GITSLC_POPREVERT) && (this->m_CurrentVersion.IsEmpty() || this->m_CurrentVersion == GIT_REV_ZERO))
2020 popup.AppendMenuIcon(IDGITLC_REVERT, IDS_MENUREVERT, IDI_REVERT);
2023 if (m_dwContextMenus & GITSLC_POPRESTORE && !filepath->IsDirectory())
2025 if (m_restorepaths.find(filepath->GetWinPathString()) == m_restorepaths.end())
2026 popup.AppendMenuIcon(IDGITLC_CREATERESTORE, IDS_MENUCREATERESTORE, IDI_RESTORE);
2027 else
2028 popup.AppendMenuIcon(IDGITLC_RESTOREPATH, IDS_MENURESTORE, IDI_RESTORE);
2031 if ((m_dwContextMenus & GetContextMenuBit(IDGITLC_REVERTTOREV)) && ( !this->m_CurrentVersion.IsEmpty() )
2032 && this->m_CurrentVersion != GIT_REV_ZERO && !(wcStatus & CTGitPath::LOGACTIONS_DELETED))
2034 popup.AppendMenuIcon(IDGITLC_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
2038 if ((GetSelectedCount() == 1)&&(!(wcStatus & CTGitPath::LOGACTIONS_UNVER))
2039 &&(!(wcStatus & CTGitPath::LOGACTIONS_IGNORE)))
2041 if (m_dwContextMenus & GITSLC_POPSHOWLOG)
2043 popup.AppendMenuIcon(IDGITLC_LOG, IDS_REPOBROWSE_SHOWLOG, IDI_LOG);
2045 if (m_dwContextMenus & GITSLC_POPSHOWLOGOLDNAME && (wcStatus & (CTGitPath::LOGACTIONS_REPLACED|CTGitPath::LOGACTIONS_COPY) && !filepath->GetGitOldPathString().IsEmpty()))
2047 popup.AppendMenuIcon(IDGITLC_LOGOLDNAME, IDS_STATUSLIST_SHOWLOGOLDNAME, IDI_LOG);
2049 if (m_dwContextMenus & GITSLC_POPBLAME && ! filepath->IsDirectory() && !(wcStatus & CTGitPath::LOGACTIONS_DELETED) && m_bHasWC)
2051 popup.AppendMenuIcon(IDGITLC_BLAME, IDS_MENUBLAME, IDI_BLAME);
2054 // if ((wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing) && (GetSelectedCount() == 1))
2055 if ( (GetSelectedCount() == 1) )
2057 if (m_dwContextMenus & this->GetContextMenuBit(IDGITLC_SAVEAS) && ! filepath->IsDirectory() && !(wcStatus & CTGitPath::LOGACTIONS_DELETED))
2059 popup.AppendMenuIcon(IDGITLC_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);
2062 if (m_dwContextMenus & GITSLC_POPOPEN && ! filepath->IsDirectory() && !(wcStatus & CTGitPath::LOGACTIONS_DELETED))
2064 popup.AppendMenuIcon(IDGITLC_VIEWREV, IDS_LOG_POPUP_VIEWREV);
2065 popup.AppendMenuIcon(IDGITLC_OPEN, IDS_REPOBROWSE_OPEN, IDI_OPEN);
2066 popup.AppendMenuIcon(IDGITLC_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
2067 if (wcStatus &CTGitPath::LOGACTIONS_UNVER) {
2068 popup.SetDefaultItem(IDGITLC_OPEN, FALSE);
2072 if (m_dwContextMenus & GITSLC_POPEXPLORE && !(wcStatus & CTGitPath::LOGACTIONS_DELETED) && m_bHasWC)
2074 popup.AppendMenuIcon(IDGITLC_EXPLORE, IDS_STATUSLIST_CONTEXT_EXPLORE, IDI_EXPLORER);
2078 if (GetSelectedCount() > 0)
2080 // if (((wcStatus == git_wc_status_unversioned)||(wcStatus == git_wc_status_ignored))&&(m_dwContextMenus & SVNSLC_POPDELETE))
2081 // {
2082 // popup.AppendMenuIcon(IDSVNLC_DELETE, IDS_MENUREMOVE, IDI_DELETE);
2083 // }
2084 // 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))
2085 // {
2086 // if (bShift)
2087 // popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVEKEEP, IDI_DELETE);
2088 // else
2089 // popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVE, IDI_DELETE);
2090 // }
2091 if ((wcStatus & CTGitPath::LOGACTIONS_UNVER)/*||(wcStatus == git_wc_status_deleted)*/)
2093 if (m_dwContextMenus & GITSLC_POPDELETE)
2095 popup.AppendMenuIcon(IDGITLC_DELETE, IDS_MENUREMOVE, IDI_DELETE);
2098 if ( (wcStatus & CTGitPath::LOGACTIONS_UNVER || wcStatus & CTGitPath::LOGACTIONS_DELETED) )
2100 if (m_dwContextMenus & GITSLC_POPIGNORE)
2103 CTGitPathList ignorelist;
2104 FillListOfSelectedItemPaths(ignorelist);
2105 //check if all selected entries have the same extension
2106 bool bSameExt = true;
2107 CString sExt;
2108 for (int i=0; i<ignorelist.GetCount(); ++i)
2110 if (sExt.IsEmpty() && (i==0))
2111 sExt = ignorelist[i].GetFileExtension();
2112 else if (sExt.CompareNoCase(ignorelist[i].GetFileExtension())!=0)
2113 bSameExt = false;
2115 if (bSameExt)
2117 if (ignoreSubMenu.CreateMenu())
2119 CString ignorepath;
2120 if (ignorelist.GetCount()==1)
2121 ignorepath = ignorelist[0].GetFileOrDirectoryName();
2122 else
2123 ignorepath.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());
2124 ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDGITLC_IGNORE, ignorepath);
2125 ignorepath = _T("*")+sExt;
2126 ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDGITLC_IGNOREMASK, ignorepath);
2127 CString temp;
2128 temp.LoadString(IDS_MENUIGNORE);
2129 popup.InsertMenu((UINT)-1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)ignoreSubMenu.m_hMenu, temp);
2132 else
2134 CString temp;
2135 if (ignorelist.GetCount()==1)
2137 temp.LoadString(IDS_MENUIGNORE);
2139 else
2141 temp.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());
2143 popup.AppendMenuIcon(IDGITLC_IGNORE, temp, IDI_IGNORE);
2151 if (GetSelectedCount() > 0)
2153 popup.AppendMenu(MF_SEPARATOR);
2154 popup.AppendMenuIcon(IDGITLC_COPY, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
2155 popup.AppendMenuIcon(IDGITLC_COPYEXT, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
2156 #if 0
2157 if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS))
2158 &&(wcStatus != git_wc_status_unversioned)&&(wcStatus != git_wc_status_none))
2160 popup.AppendMenu(MF_SEPARATOR);
2161 // changelist commands
2162 size_t numChangelists = GetNumberOfChangelistsInSelection();
2163 if (numChangelists > 0)
2165 popup.AppendMenuIcon(IDSVNLC_REMOVEFROMCS, IDS_STATUSLIST_CONTEXT_REMOVEFROMCS);
2167 if ((!entry->IsFolder())&&(changelistSubMenu.CreateMenu()))
2169 CString temp;
2170 temp.LoadString(IDS_STATUSLIST_CONTEXT_CREATECS);
2171 changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATECS, temp);
2173 if (entry->changelist.Compare(SVNSLC_IGNORECHANGELIST))
2175 changelistSubMenu.AppendMenu(MF_SEPARATOR);
2176 changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATEIGNORECS, SVNSLC_IGNORECHANGELIST);
2179 if (m_changelists.size() > 0)
2181 // find the changelist names
2182 bool bNeedSeparator = true;
2183 int cmdID = IDSVNLC_MOVETOCS;
2184 for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)
2186 if ((entry->changelist.Compare(it->first))&&(it->first.Compare(SVNSLC_IGNORECHANGELIST)))
2188 if (bNeedSeparator)
2190 changelistSubMenu.AppendMenu(MF_SEPARATOR);
2191 bNeedSeparator = false;
2193 changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, cmdID, it->first);
2194 cmdID++;
2198 temp.LoadString(IDS_STATUSLIST_CONTEXT_MOVETOCS);
2199 popup.AppendMenu(MF_POPUP|MF_STRING, (UINT_PTR)changelistSubMenu.GetSafeHmenu(), temp);
2202 #endif
2205 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
2207 m_bBlock = TRUE;
2208 AfxGetApp()->DoWaitCursor(1);
2209 //int iItemCountBeforeMenuCmd = GetItemCount();
2210 //bool bForce = false;
2211 switch (cmd)
2213 case IDGITLC_VIEWREV:
2214 OpenFile(filepath, ALTERNATIVEEDITOR);
2215 break;
2216 case IDGITLC_OPEN:
2217 OpenFile(filepath,OPEN);
2218 break;
2219 case IDGITLC_OPENWITH:
2220 OpenFile(filepath,OPEN_WITH);
2221 break;
2222 case IDGITLC_EXPLORE:
2224 ShellExecute(this->m_hWnd, _T("explore"), filepath->GetDirectory().GetWinPath(), NULL, NULL, SW_SHOW);
2226 break;
2227 case IDGITLC_CREATERESTORE:
2229 POSITION pos = GetFirstSelectedItemPosition();
2230 while (pos)
2232 int index = GetNextSelectedItem(pos);
2233 CTGitPath * entry2 = (CTGitPath * )GetItemData(index);
2234 ASSERT(entry2 != NULL);
2235 if (entry2 == NULL)
2236 continue;
2237 if (m_restorepaths.find(entry2->GetWinPathString()) != m_restorepaths.end())
2238 continue;
2239 CTGitPath tempFile = CTempFiles::Instance().GetTempFilePath(false);
2240 if (CopyFile(g_Git.m_CurrentDir + _T("\\") + entry2->GetWinPathString(), tempFile.GetWinPath(), FALSE))
2242 m_restorepaths[entry2->GetWinPathString()] = tempFile.GetWinPathString();
2243 SetItemState(index, INDEXTOOVERLAYMASK(OVL_RESTORE), LVIS_OVERLAYMASK);
2246 Invalidate();
2248 break;
2249 case IDGITLC_RESTOREPATH:
2251 if (CMessageBox::Show(m_hWnd, IDS_STATUSLIST_RESTOREPATH, IDS_APPNAME, 2, IDI_QUESTION, IDS_RESTOREBUTTON, IDS_ABORTBUTTON) == 2)
2252 break;
2253 POSITION pos = GetFirstSelectedItemPosition();
2254 while (pos)
2256 int index = GetNextSelectedItem(pos);
2257 CTGitPath * entry2 = (CTGitPath * )GetItemData(index);
2258 ASSERT(entry2 != NULL);
2259 if (entry2 == NULL)
2260 continue;
2261 if (m_restorepaths.find(entry2->GetWinPathString()) == m_restorepaths.end())
2262 continue;
2263 if (CopyFile(m_restorepaths[entry2->GetWinPathString()], g_Git.m_CurrentDir + _T("\\") + entry2->GetWinPathString(), FALSE))
2265 m_restorepaths.erase(entry2->GetWinPathString());
2266 SetItemState(index, 0, LVIS_OVERLAYMASK);
2269 Invalidate();
2271 break;
2272 // Compare current version and work copy.
2273 case IDGITLC_COMPAREWC:
2275 POSITION pos = GetFirstSelectedItemPosition();
2276 while ( pos )
2278 int index = GetNextSelectedItem(pos);
2279 StartDiffWC(index);
2282 break;
2283 // Compare with base version. when current version is zero, compare workcopy and HEAD.
2284 case IDGITLC_COMPARE:
2286 POSITION pos = GetFirstSelectedItemPosition();
2287 while ( pos )
2289 int index = GetNextSelectedItem(pos);
2290 StartDiff(index);
2293 break;
2294 case IDGITLC_COMPARETWO:
2296 POSITION pos = GetFirstSelectedItemPosition();
2297 while ( pos )
2299 int index = GetNextSelectedItem(pos);
2300 StartDiffTwo(index);
2303 break;
2304 case IDGITLC_COMPARETWOFILES:
2306 POSITION pos = GetFirstSelectedItemPosition();
2307 CTGitPath * firstfilepath = NULL, * secondfilepath = NULL;
2308 if (pos)
2310 firstfilepath = (CTGitPath * )GetItemData(GetNextSelectedItem(pos));
2311 ASSERT(firstfilepath != NULL);
2312 if (firstfilepath == NULL)
2313 break;
2315 secondfilepath = (CTGitPath * )GetItemData(GetNextSelectedItem(pos));
2316 ASSERT(secondfilepath != NULL);
2317 if (secondfilepath == NULL)
2318 break;
2320 CString sCmd;
2321 sCmd.Format(_T("/command:diff /path:\"%s\" /path2:\"%s\" /hwnd:%ld"), firstfilepath->GetWinPath(), secondfilepath->GetWinPath(), (unsigned long)m_hWnd);
2322 CAppUtils::RunTortoiseProc(sCmd);
2325 break;
2326 case IDGITLC_GNUDIFF1:
2328 // SVNDiff diff(NULL, this->m_hWnd, true);
2330 // if (entry->remotestatus <= git_wc_status_normal)
2331 // CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_BASE, entry->path, SVNRev::REV_WC);
2332 // else
2333 // CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_WC, entry->path, SVNRev::REV_HEAD);
2334 if(m_CurrentVersion.IsEmpty() || m_CurrentVersion == GIT_REV_ZERO)
2336 CString fromwhere;
2337 if(m_amend)
2338 fromwhere = _T("~1");
2339 CAppUtils::StartShowUnifiedDiff(m_hWnd,*filepath,GitRev::GetHead()+fromwhere,
2340 *filepath,GitRev::GetWorkingCopy());
2342 else
2344 if((filepath->m_ParentNo&(PARENT_MASK|MERGE_MASK)) ==0)
2345 CAppUtils::StartShowUnifiedDiff(m_hWnd,*filepath,m_CurrentVersion+_T("~1"),
2346 *filepath,m_CurrentVersion);
2347 else
2349 CString str;
2350 if(filepath->m_ParentNo & MERGE_MASK)
2353 else
2355 str.Format(_T("%s^%d"),m_CurrentVersion,(filepath->m_ParentNo&PARENT_MASK)+1);
2358 CAppUtils::StartShowUnifiedDiff(m_hWnd,*filepath, str,
2359 *filepath, m_CurrentVersion, false, false, false,
2360 !!(filepath->m_ParentNo & MERGE_MASK));
2364 break;
2365 case IDGITLC_GNUDIFF2:
2367 CAppUtils::StartShowUnifiedDiff(m_hWnd, *filepath, m_Rev2, *filepath, m_Rev1);
2369 break;
2371 case IDGITLC_ADD:
2372 { // The add went ok, but we now need to run through the selected items again
2373 // and update their status
2374 std::vector<int> selectIndex;
2376 POSITION pos = GetFirstSelectedItemPosition();
2377 int index;
2378 CMassiveGitTask mgt(L"add -f");
2379 while ((index = GetNextSelectedItem(pos)) >= 0)
2381 CTGitPath * path = (CTGitPath *)GetItemData(index);
2382 ASSERT(path);
2383 if(path == NULL)
2384 continue;
2386 selectIndex.push_back(index);
2387 mgt.AddFile(*path);
2389 BOOL cancel = FALSE;
2390 mgt.Execute(cancel);
2392 if (NULL != GetParent() && NULL != GetParent()->GetSafeHwnd())
2393 GetParent()->SendMessage(GITSLNM_NEEDSREFRESH);
2395 SetRedraw(TRUE);
2397 break;
2399 case IDGITLC_DELETE:
2401 //Collect paths
2402 std::vector<int> selectIndex;
2404 POSITION pos = GetFirstSelectedItemPosition();
2405 int index;
2406 while ((index = GetNextSelectedItem(pos)) >= 0)
2408 selectIndex.push_back(index);
2411 //Create file-list ('\0' separated) for SHFileOperation
2412 CString filelist;
2413 for(int i=0;i<selectIndex.size();i++)
2415 index=selectIndex[i];
2417 CTGitPath * path=(CTGitPath*)GetItemData(index);
2418 ASSERT(path);
2419 if(path == NULL)
2420 continue;
2422 filelist += path->GetWinPathString();
2423 filelist += _T("|");
2425 filelist += _T("|");
2426 int len = filelist.GetLength();
2427 TCHAR * buf = new TCHAR[len+2];
2428 _tcscpy_s(buf, len+2, filelist);
2429 for (int i=0; i<len; ++i)
2430 if (buf[i] == '|')
2431 buf[i] = 0;
2432 SHFILEOPSTRUCT fileop;
2433 fileop.hwnd = this->m_hWnd;
2434 fileop.wFunc = FO_DELETE;
2435 fileop.pFrom = buf;
2436 fileop.pTo = NULL;
2437 fileop.fFlags = FOF_NO_CONNECTED_ELEMENTS | ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : FOF_ALLOWUNDO);
2438 fileop.lpszProgressTitle = _T("deleting file");
2439 int result = SHFileOperation(&fileop);
2440 delete [] buf;
2442 if ( (result==0) && (!fileop.fAnyOperationsAborted) )
2444 SetRedraw(FALSE);
2445 POSITION pos = NULL;
2446 while ((pos = GetFirstSelectedItemPosition()) != 0)
2448 int index = GetNextSelectedItem(pos);
2449 if (GetCheck(index))
2450 m_nSelected--;
2451 m_nTotal--;
2453 RemoveListEntry(index);
2455 SetRedraw(TRUE);
2458 break;
2460 case IDGITLC_BLAME:
2462 CAppUtils::LaunchTortoiseBlame(g_Git.m_CurrentDir+_T("\\")+filepath->GetWinPath(), m_CurrentVersion);
2464 break;
2466 case IDGITLC_LOG:
2468 CString sCmd;
2469 sCmd.Format(_T("/command:log /path:\"%s\""), g_Git.m_CurrentDir + _T("\\") + filepath->GetWinPath());
2470 CAppUtils::RunTortoiseProc(sCmd);
2472 break;
2474 case IDGITLC_LOGOLDNAME:
2476 CTGitPath oldName(filepath->GetGitOldPathString());
2477 CString sCmd;
2478 sCmd.Format(_T("/command:log /path:\"%s\""), g_Git.m_CurrentDir + _T("\\") + oldName.GetWinPath());
2479 CAppUtils::RunTortoiseProc(sCmd);
2481 break;
2484 case IDGITLC_EDITCONFLICT:
2486 CAppUtils::ConflictEdit(*filepath,false,this->m_bIsRevertTheirMy);
2487 break;
2489 case IDGITLC_RESOLVETHEIRS: //follow up
2490 case IDGITLC_RESOLVEMINE: //follow up
2491 case IDGITLC_RESOLVECONFLICT:
2493 if (CMessageBox::Show(m_hWnd, IDS_PROC_RESOLVE, IDS_APPNAME, MB_ICONQUESTION | MB_YESNO)==IDYES)
2495 POSITION pos = GetFirstSelectedItemPosition();
2496 while (pos != 0)
2498 int index;
2499 index = GetNextSelectedItem(pos);
2500 CTGitPath * fentry =(CTGitPath*) this->GetItemData(index);
2501 if(fentry == NULL)
2502 continue;
2503 CString gitcmd,output;
2504 output.Empty();
2506 if ( ((!this->m_bIsRevertTheirMy)&&cmd == IDGITLC_RESOLVETHEIRS) ||
2507 ((this->m_bIsRevertTheirMy)&&cmd == IDGITLC_RESOLVEMINE) )
2509 gitcmd.Format(_T("git.exe checkout-index -f --stage=3 -- \"%s\""),fentry->GetGitPathString());
2510 if (g_Git.Run(gitcmd, &output,CP_UTF8))
2512 CMessageBox::Show(m_hWnd, output, _T("TortoiseGit"), MB_ICONERROR);
2513 continue;
2516 output.Empty();
2517 if ( ((!this->m_bIsRevertTheirMy)&&cmd == IDGITLC_RESOLVEMINE) ||
2518 ((this->m_bIsRevertTheirMy)&&cmd == IDGITLC_RESOLVETHEIRS) )
2520 gitcmd.Format(_T("git.exe checkout-index -f --stage=2 -- \"%s\""),fentry->GetGitPathString());
2521 if (g_Git.Run(gitcmd, &output,CP_UTF8))
2523 CMessageBox::Show(m_hWnd, output, _T("TortoiseGit"), MB_ICONERROR);
2524 continue;
2528 output.Empty();
2529 if ( fentry->m_Action & CTGitPath::LOGACTIONS_UNMERGED)
2531 gitcmd.Format(_T("git.exe add -f -- \"%s\""),fentry->GetGitPathString());
2532 if (g_Git.Run(gitcmd, &output,CP_UTF8))
2534 CMessageBox::Show(m_hWnd, output, _T("TortoiseGit"), MB_ICONERROR);
2536 else
2538 fentry->m_Action |= CTGitPath::LOGACTIONS_MODIFIED;
2539 fentry->m_Action &=~CTGitPath::LOGACTIONS_UNMERGED;
2543 CAppUtils::RemoveTempMergeFile(*fentry);
2546 Show(m_dwShow, 0, m_bShowFolders,0,true);
2549 break;
2551 case IDGITLC_IGNORE:
2553 CTGitPathList ignorelist;
2554 //std::vector<CString> toremove;
2555 FillListOfSelectedItemPaths(ignorelist, true);
2556 SetRedraw(FALSE);
2558 if(!CAppUtils::IgnoreFile(ignorelist,false))
2559 break;
2561 for(int i=0;i<ignorelist.GetCount();i++)
2563 int nListboxEntries = GetItemCount();
2564 for (int nItem=0; nItem<nListboxEntries; ++nItem)
2566 CTGitPath *path=(CTGitPath*)GetItemData(nItem);
2567 if (path->GetGitPathString()==ignorelist[i].GetGitPathString())
2569 RemoveListEntry(nItem);
2570 break;
2574 SetRedraw(TRUE);
2576 #if 0
2577 CTSVNPathList ignorelist;
2578 std::vector<CString> toremove;
2579 FillListOfSelectedItemPaths(ignorelist, true);
2580 SetRedraw(FALSE);
2581 for (int j=0; j<ignorelist.GetCount(); ++j)
2583 int nListboxEntries = GetItemCount();
2584 for (int i=0; i<nListboxEntries; ++i)
2586 if (GetListEntry(i)->GetPath().IsEquivalentTo(ignorelist[j]))
2588 selIndex = i;
2589 break;
2592 CString name = CPathUtils::PathPatternEscape(ignorelist[j].GetFileOrDirectoryName());
2593 CTSVNPath parentfolder = ignorelist[j].GetContainingDirectory();
2594 SVNProperties props(parentfolder, SVNRev::REV_WC, false);
2595 CStringA value;
2596 for (int i=0; i<props.GetCount(); i++)
2598 CString propname(props.GetItemName(i).c_str());
2599 if (propname.CompareNoCase(_T("git:ignore"))==0)
2601 stdstring stemp;
2602 // treat values as normal text even if they're not
2603 value = (char *)props.GetItemValue(i).c_str();
2606 if (value.IsEmpty())
2607 value = name;
2608 else
2610 value = value.Trim("\n\r");
2611 value += "\n";
2612 value += name;
2613 value.Remove('\r');
2615 if (!props.Add(_T("git:ignore"), (LPCSTR)value))
2617 CString temp;
2618 temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);
2619 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2620 break;
2622 if (GetCheck(selIndex))
2623 m_nSelected--;
2624 m_nTotal--;
2626 // now, if we ignored a folder, remove all its children
2627 if (ignorelist[j].IsDirectory())
2629 for (int i=0; i<(int)m_arListArray.size(); ++i)
2631 FileEntry * entry = GetListEntry(i);
2632 if (entry->status == git_wc_status_unversioned)
2634 if (!ignorelist[j].IsEquivalentTo(entry->GetPath())&&(ignorelist[j].IsAncestorOf(entry->GetPath())))
2636 entry->status = git_wc_status_ignored;
2637 entry->textstatus = git_wc_status_ignored;
2638 if (GetCheck(i))
2639 m_nSelected--;
2640 toremove.push_back(entry->GetPath().GetSVNPathString());
2646 CTSVNPath basepath = m_arStatusArray[m_arListArray[selIndex]]->basepath;
2648 FileEntry * entry = m_arStatusArray[m_arListArray[selIndex]];
2649 if ( entry->status == git_wc_status_unversioned ) // keep "deleted" items
2650 toremove.push_back(entry->GetPath().GetSVNPathString());
2652 if (!m_bIgnoreRemoveOnly)
2654 SVNStatus status;
2655 git_wc_status2_t * s;
2656 CTSVNPath gitPath;
2657 s = status.GetFirstFileStatus(parentfolder, gitPath, false, git_depth_empty);
2658 // first check if the folder isn't already present in the list
2659 bool bFound = false;
2660 nListboxEntries = GetItemCount();
2661 for (int i=0; i<nListboxEntries; ++i)
2663 FileEntry * entry = GetListEntry(i);
2664 if (entry->path.IsEquivalentTo(gitPath))
2666 bFound = true;
2667 break;
2670 if (!bFound)
2672 if (s!=0)
2674 FileEntry * entry = new FileEntry();
2675 entry->path = gitPath;
2676 entry->basepath = basepath;
2677 entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);
2678 entry->textstatus = s->text_status;
2679 entry->propstatus = s->prop_status;
2680 entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);
2681 entry->remotetextstatus = s->repos_text_status;
2682 entry->remotepropstatus = s->repos_prop_status;
2683 entry->inunversionedfolder = FALSE;
2684 entry->checked = true;
2685 entry->inexternal = false;
2686 entry->direct = false;
2687 entry->isfolder = true;
2688 entry->last_commit_date = 0;
2689 entry->last_commit_rev = 0;
2690 entry->remoterev = 0;
2691 if (s->entry)
2693 if (s->entry->url)
2695 entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));
2698 if (s->entry && s->entry->present_props)
2700 entry->present_props = s->entry->present_props;
2702 m_arStatusArray.push_back(entry);
2703 m_arListArray.push_back(m_arStatusArray.size()-1);
2704 AddEntry(entry, langID, GetItemCount());
2709 for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)
2711 int nListboxEntries = GetItemCount();
2712 for (int i=0; i<nListboxEntries; ++i)
2714 if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)
2716 RemoveListEntry(i);
2717 break;
2721 SetRedraw(TRUE);
2723 #endif
2724 break;
2725 case IDGITLC_IGNOREMASK:
2727 CString common;
2728 CString ext=filepath->GetFileExtension();
2729 CTGitPathList ignorelist;
2730 FillListOfSelectedItemPaths(ignorelist, true);
2731 SetRedraw(FALSE);
2733 CAppUtils::IgnoreFile(ignorelist,true);
2735 common=ignorelist.GetCommonRoot().GetGitPathString();
2737 for (int i=0; i< GetItemCount(); ++i)
2739 CTGitPath *path=(CTGitPath*)GetItemData(i);
2740 if(!( path->m_Action & CTGitPath::LOGACTIONS_UNVER))
2741 continue;
2742 if( path->GetGitPathString().Left(common.GetLength()) == common )
2744 if (path->GetFileExtension()==ext)
2746 RemoveListEntry(i);
2747 i--; // remove index i at item, new one will replace.
2752 CWnd* pParent = GetParent();
2753 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
2755 pParent->SendMessage(GITSLNM_NEEDSREFRESH);
2758 SetRedraw(TRUE);
2760 #if 0
2761 std::set<CTSVNPath> parentlist;
2762 for (int i=0; i<ignorelist.GetCount(); ++i)
2764 parentlist.insert(ignorelist[i].GetContainingDirectory());
2766 std::set<CTSVNPath>::iterator it;
2767 std::vector<CString> toremove;
2769 for (it = parentlist.begin(); it != parentlist.end(); ++it)
2771 CTSVNPath parentFolder = (*it).GetDirectory();
2772 SVNProperties props(parentFolder, SVNRev::REV_WC, false);
2773 CStringA value;
2774 for (int i=0; i<props.GetCount(); i++)
2776 CString propname(props.GetItemName(i).c_str());
2777 if (propname.CompareNoCase(_T("git:ignore"))==0)
2779 stdstring stemp;
2780 // treat values as normal text even if they're not
2781 value = (char *)props.GetItemValue(i).c_str();
2784 if (value.IsEmpty())
2785 value = name;
2786 else
2788 value = value.Trim("\n\r");
2789 value += "\n";
2790 value += name;
2791 value.Remove('\r');
2793 if (!props.Add(_T("git:ignore"), (LPCSTR)value))
2795 CString temp;
2796 temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);
2797 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2799 else
2801 CTSVNPath basepath;
2802 int nListboxEntries = GetItemCount();
2803 for (int i=0; i<nListboxEntries; ++i)
2805 FileEntry * entry = GetListEntry(i);
2806 ASSERT(entry != NULL);
2807 if (entry == NULL)
2808 continue;
2809 if (basepath.IsEmpty())
2810 basepath = entry->basepath;
2811 // since we ignored files with a mask (e.g. *.exe)
2812 // we have to find find all files in the same
2813 // folder (IsAncestorOf() returns TRUE for _all_ children,
2814 // not just the immediate ones) which match the
2815 // mask and remove them from the list too.
2816 if ((entry->status == git_wc_status_unversioned)&&(parentFolder.IsAncestorOf(entry->path)))
2818 CString f = entry->path.GetSVNPathString();
2819 if (f.Mid(parentFolder.GetSVNPathString().GetLength()).Find('/')<=0)
2821 if (CStringUtils::WildCardMatch(name, f))
2823 if (GetCheck(i))
2824 m_nSelected--;
2825 m_nTotal--;
2826 toremove.push_back(f);
2831 if (!m_bIgnoreRemoveOnly)
2833 SVNStatus status;
2834 git_wc_status2_t * s;
2835 CTSVNPath gitPath;
2836 s = status.GetFirstFileStatus(parentFolder, gitPath, false, git_depth_empty);
2837 if (s!=0)
2839 // first check if the folder isn't already present in the list
2840 bool bFound = false;
2841 for (int i=0; i<nListboxEntries; ++i)
2843 FileEntry * entry = GetListEntry(i);
2844 if (entry->path.IsEquivalentTo(gitPath))
2846 bFound = true;
2847 break;
2850 if (!bFound)
2852 FileEntry * entry = new FileEntry();
2853 entry->path = gitPath;
2854 entry->basepath = basepath;
2855 entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);
2856 entry->textstatus = s->text_status;
2857 entry->propstatus = s->prop_status;
2858 entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);
2859 entry->remotetextstatus = s->repos_text_status;
2860 entry->remotepropstatus = s->repos_prop_status;
2861 entry->inunversionedfolder = false;
2862 entry->checked = true;
2863 entry->inexternal = false;
2864 entry->direct = false;
2865 entry->isfolder = true;
2866 entry->last_commit_date = 0;
2867 entry->last_commit_rev = 0;
2868 entry->remoterev = 0;
2869 if (s->entry)
2871 if (s->entry->url)
2873 entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));
2876 if (s->entry && s->entry->present_props)
2878 entry->present_props = s->entry->present_props;
2880 m_arStatusArray.push_back(entry);
2881 m_arListArray.push_back(m_arStatusArray.size()-1);
2882 AddEntry(entry, langID, GetItemCount());
2888 for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)
2890 int nListboxEntries = GetItemCount();
2891 for (int i=0; i<nListboxEntries; ++i)
2893 if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)
2895 RemoveListEntry(i);
2896 break;
2900 SetRedraw(TRUE);
2902 #endif
2903 break;
2905 case IDGITLC_REVERT:
2907 // If at least one item is not in the status "added"
2908 // we ask for a confirmation
2909 BOOL bConfirm = FALSE;
2910 POSITION pos = GetFirstSelectedItemPosition();
2911 int index;
2912 while ((index = GetNextSelectedItem(pos)) >= 0)
2914 //FileEntry * fentry = GetListEntry(index);
2915 CTGitPath *fentry=(CTGitPath*)GetItemData(index);
2916 if(fentry && fentry->m_Action &CTGitPath::LOGACTIONS_MODIFIED )
2918 bConfirm = TRUE;
2919 break;
2923 CString str;
2924 str.Format(IDS_PROC_WARNREVERT,GetSelectedCount());
2926 if (!bConfirm || CMessageBox::Show(this->m_hWnd, str, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION)==IDYES)
2928 CTGitPathList targetList;
2929 FillListOfSelectedItemPaths(targetList);
2931 // make sure that the list is reverse sorted, so that
2932 // children are removed before any parents
2933 targetList.SortByPathname(true);
2935 // put all reverted files in the trashbin, except the ones with 'added'
2936 // status because they are not restored by the revert.
2937 CTGitPathList delList;
2938 POSITION pos = GetFirstSelectedItemPosition();
2939 int index;
2940 while ((index = GetNextSelectedItem(pos)) >= 0)
2942 CTGitPath *entry=(CTGitPath *)GetItemData(index);
2943 if (entry&&(!(entry->m_Action& CTGitPath::LOGACTIONS_ADDED))
2944 && (!(entry->m_Action& CTGitPath::LOGACTIONS_REPLACED)))
2946 CTGitPath fullpath;
2947 fullpath.SetFromWin(g_Git.m_CurrentDir+_T("\\")+entry->GetWinPath());
2948 delList.AddPath(fullpath);
2951 if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RevertWithRecycleBin"), TRUE)))
2952 delList.DeleteAllFiles(true);
2954 CString revertToCommit = _T("HEAD");
2955 if (m_amend)
2956 revertToCommit = _T("HEAD~1");
2957 if (g_Git.Revert(revertToCommit, targetList))
2959 CMessageBox::Show(this->m_hWnd, _T("Revert Fail"), _T("TortoiseGit"), MB_ICONERROR);
2961 else
2963 bool updateStatusList = false;
2964 for(int i=0;i<targetList.GetCount();i++)
2966 int nListboxEntries = GetItemCount();
2967 for (int nItem=0; nItem<nListboxEntries; ++nItem)
2969 CTGitPath *path=(CTGitPath*)GetItemData(nItem);
2970 if (path->GetGitPathString()==targetList[i].GetGitPathString())
2972 if(path->m_Action & CTGitPath::LOGACTIONS_ADDED)
2974 path->m_Action = CTGitPath::LOGACTIONS_UNVER;
2975 SetEntryCheck(path,nItem,false);
2976 updateStatusList = true;
2977 SetItemGroup(nItem,1);
2978 this->m_StatusFileList.RemoveItem(*path);
2979 this->m_UnRevFileList.AddPath(*path);
2980 //this->m_IgnoreFileList.RemoveItem(*path);
2983 else
2985 RemoveListEntry(nItem);
2987 break;
2991 SetRedraw(TRUE);
2992 SaveColumnWidths();
2993 Show(m_dwShow, 0, m_bShowFolders,updateStatusList,true);
2994 NotifyCheck();
2998 break;
3000 case IDGITLC_COPY:
3001 CopySelectedEntriesToClipboard(0);
3002 break;
3003 case IDGITLC_COPYEXT:
3004 CopySelectedEntriesToClipboard((DWORD)-1);
3005 break;
3007 case IDGITLC_SAVEAS:
3008 FileSaveAs(filepath);
3009 break;
3011 case IDGITLC_REVERTTOREV:
3012 RevertSelectedItemToVersion();
3013 break;
3014 #if 0
3015 case IDSVNLC_PROPERTIES:
3017 CTSVNPathList targetList;
3018 FillListOfSelectedItemPaths(targetList);
3019 CEditPropertiesDlg dlg;
3020 dlg.SetPathList(targetList);
3021 dlg.DoModal();
3022 if (dlg.HasChanged())
3024 // since the user might have changed/removed/added
3025 // properties recursively, we don't really know
3026 // which items have changed their status.
3027 // So tell the parent to do a refresh.
3028 CWnd* pParent = GetParent();
3029 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
3031 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);
3035 break;
3036 case IDSVNLC_COMMIT:
3038 CTSVNPathList targetList;
3039 FillListOfSelectedItemPaths(targetList);
3040 CTSVNPath tempFile = CTempFiles::Instance().GetTempFilePath(false);
3041 VERIFY(targetList.WriteToFile(tempFile.GetWinPathString()));
3042 CString commandline = CPathUtils::GetAppDirectory();
3043 commandline += _T("TortoiseProc.exe /command:commit /pathfile:\"");
3044 commandline += tempFile.GetWinPathString();
3045 commandline += _T("\"");
3046 commandline += _T(" /deletepathfile");
3047 CAppUtils::LaunchApplication(commandline, NULL, false);
3049 break;
3051 case IDSVNLC_COMPAREWC:
3053 POSITION pos = GetFirstSelectedItemPosition();
3054 while ( pos )
3056 int index = GetNextSelectedItem(pos);
3057 FileEntry * entry = GetListEntry(index);
3058 ASSERT(entry != NULL);
3059 if (entry == NULL)
3060 continue;
3061 SVNDiff diff(NULL, m_hWnd, true);
3062 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
3063 git_revnum_t baseRev = entry->Revision;
3064 diff.DiffFileAgainstBase(
3065 entry->path, baseRev, entry->textstatus, entry->propstatus);
3068 break;
3069 case IDSVNLC_GNUDIFF1:
3071 SVNDiff diff(NULL, this->m_hWnd, true);
3073 if (entry->remotestatus <= git_wc_status_normal)
3074 CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_BASE, entry->path, SVNRev::REV_WC);
3075 else
3076 CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_WC, entry->path, SVNRev::REV_HEAD);
3078 break;
3079 case IDSVNLC_UPDATE:
3081 CTSVNPathList targetList;
3082 FillListOfSelectedItemPaths(targetList);
3083 bool bAllExist = true;
3084 for (int i=0; i<targetList.GetCount(); ++i)
3086 if (!targetList[i].Exists())
3088 bAllExist = false;
3089 break;
3092 if (bAllExist)
3094 CSVNProgressDlg dlg;
3095 dlg.SetCommand(CSVNProgressDlg::SVNProgress_Update);
3096 dlg.SetPathList(targetList);
3097 dlg.SetRevision(SVNRev::REV_HEAD);
3098 dlg.DoModal();
3100 else
3102 CString sTempFile = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();
3103 targetList.WriteToFile(sTempFile, false);
3104 CString sCmd;
3105 sCmd.Format(_T("\"%s\" /command:update /rev /pathfile:\"%s\" /deletepathfile"),
3106 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)sTempFile);
3108 CAppUtils::LaunchApplication(sCmd, NULL, false);
3111 break;
3113 case IDSVNLC_REMOVE:
3115 SVN git;
3116 CTSVNPathList itemsToRemove;
3117 FillListOfSelectedItemPaths(itemsToRemove);
3119 // We must sort items before removing, so that files are always removed
3120 // *before* their parents
3121 itemsToRemove.SortByPathname(true);
3123 bool bSuccess = false;
3124 if (git.Remove(itemsToRemove, FALSE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))
3126 bSuccess = true;
3128 else
3130 if ((git.Err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE) ||
3131 (git.Err->apr_err == SVN_ERR_CLIENT_MODIFIED))
3133 CString msg, yes, no, yestoall;
3134 msg.Format(IDS_PROC_REMOVEFORCE, (LPCTSTR)git.GetLastErrorMessage());
3135 yes.LoadString(IDS_MSGBOX_YES);
3136 no.LoadString(IDS_MSGBOX_NO);
3137 yestoall.LoadString(IDS_PROC_YESTOALL);
3138 UINT ret = CMessageBox::Show(m_hWnd, msg, _T("TortoiseGit"), 2, IDI_ERROR, yes, no, yestoall);
3139 if ((ret == 1)||(ret==3))
3141 if (!git.Remove(itemsToRemove, TRUE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))
3143 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3145 else
3146 bSuccess = true;
3149 else
3150 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3152 if (bSuccess)
3154 // The remove went ok, but we now need to run through the selected items again
3155 // and update their status
3156 POSITION pos = GetFirstSelectedItemPosition();
3157 int index;
3158 std::vector<int> entriesToRemove;
3159 while ((index = GetNextSelectedItem(pos)) >= 0)
3161 FileEntry * e = GetListEntry(index);
3162 if (!bShift &&
3163 ((e->textstatus == git_wc_status_unversioned)||
3164 (e->textstatus == git_wc_status_none)||
3165 (e->textstatus == git_wc_status_ignored)))
3167 if (GetCheck(index))
3168 m_nSelected--;
3169 m_nTotal--;
3170 entriesToRemove.push_back(index);
3172 else
3174 e->textstatus = git_wc_status_deleted;
3175 e->status = git_wc_status_deleted;
3176 SetEntryCheck(e,index,true);
3179 for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)
3181 RemoveListEntry(*it);
3184 SaveColumnWidths();
3185 Show(m_dwShow, 0, m_bShowFolders);
3186 NotifyCheck();
3188 break;
3189 case IDSVNLC_DELETE:
3191 CTSVNPathList pathlist;
3192 FillListOfSelectedItemPaths(pathlist);
3193 pathlist.RemoveChildren();
3194 CString filelist;
3195 for (INT_PTR i=0; i<pathlist.GetCount(); ++i)
3197 filelist += pathlist[i].GetWinPathString();
3198 filelist += _T("|");
3200 filelist += _T("|");
3201 int len = filelist.GetLength();
3202 TCHAR * buf = new TCHAR[len+2];
3203 _tcscpy_s(buf, len+2, filelist);
3204 for (int i=0; i<len; ++i)
3205 if (buf[i] == '|')
3206 buf[i] = 0;
3207 SHFILEOPSTRUCT fileop;
3208 fileop.hwnd = this->m_hWnd;
3209 fileop.wFunc = FO_DELETE;
3210 fileop.pFrom = buf;
3211 fileop.pTo = NULL;
3212 fileop.fFlags = FOF_NO_CONNECTED_ELEMENTS | ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : FOF_ALLOWUNDO);
3213 fileop.lpszProgressTitle = _T("deleting file");
3214 int result = SHFileOperation(&fileop);
3215 delete [] buf;
3217 if ( (result==0) && (!fileop.fAnyOperationsAborted) )
3219 SetRedraw(FALSE);
3220 POSITION pos = NULL;
3221 CTSVNPathList deletedlist; // to store list of deleted folders
3222 while ((pos = GetFirstSelectedItemPosition()) != 0)
3224 int index = GetNextSelectedItem(pos);
3225 if (GetCheck(index))
3226 m_nSelected--;
3227 m_nTotal--;
3228 FileEntry * fentry = GetListEntry(index);
3229 if ((fentry)&&(fentry->isfolder))
3230 deletedlist.AddPath(fentry->path);
3231 RemoveListEntry(index);
3233 // now go through the list of deleted folders
3234 // and remove all their children from the list too!
3235 int nListboxEntries = GetItemCount();
3236 for (int folderindex = 0; folderindex < deletedlist.GetCount(); ++folderindex)
3238 CTSVNPath folderpath = deletedlist[folderindex];
3239 for (int i=0; i<nListboxEntries; ++i)
3241 FileEntry * entry = GetListEntry(i);
3242 if (folderpath.IsAncestorOf(entry->path))
3244 RemoveListEntry(i--);
3245 nListboxEntries--;
3249 SetRedraw(TRUE);
3252 break;
3255 case IDSVNLC_ADD:
3257 SVN git;
3258 CTSVNPathList itemsToAdd;
3259 FillListOfSelectedItemPaths(itemsToAdd);
3261 // We must sort items before adding, so that folders are always added
3262 // *before* any of their children
3263 itemsToAdd.SortByPathname();
3265 ProjectProperties props;
3266 props.ReadPropsPathList(itemsToAdd);
3267 if (git.Add(itemsToAdd, &props, git_depth_empty, TRUE, TRUE, TRUE))
3269 // The add went ok, but we now need to run through the selected items again
3270 // and update their status
3271 POSITION pos = GetFirstSelectedItemPosition();
3272 int index;
3273 while ((index = GetNextSelectedItem(pos)) >= 0)
3275 FileEntry * e = GetListEntry(index);
3276 e->textstatus = git_wc_status_added;
3277 e->propstatus = git_wc_status_none;
3278 e->status = git_wc_status_added;
3279 SetEntryCheck(e,index,true);
3282 else
3284 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3286 SaveColumnWidths();
3287 Show(m_dwShow, 0, m_bShowFolders);
3288 NotifyCheck();
3290 break;
3291 case IDSVNLC_ADD_RECURSIVE:
3293 CTSVNPathList itemsToAdd;
3294 FillListOfSelectedItemPaths(itemsToAdd);
3296 CAddDlg dlg;
3297 dlg.m_pathList = itemsToAdd;
3298 if (dlg.DoModal() == IDOK)
3300 if (dlg.m_pathList.GetCount() == 0)
3301 break;
3302 CSVNProgressDlg progDlg;
3303 progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Add);
3304 progDlg.SetPathList(dlg.m_pathList);
3305 ProjectProperties props;
3306 props.ReadPropsPathList(dlg.m_pathList);
3307 progDlg.SetProjectProperties(props);
3308 progDlg.SetItemCount(dlg.m_pathList.GetCount());
3309 progDlg.DoModal();
3311 // refresh!
3312 CWnd* pParent = GetParent();
3313 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
3315 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);
3319 break;
3321 case IDSVNLC_REPAIRMOVE:
3323 POSITION pos = GetFirstSelectedItemPosition();
3324 int index = GetNextSelectedItem(pos);
3325 FileEntry * entry1 = NULL;
3326 FileEntry * entry2 = NULL;
3327 if (index >= 0)
3329 entry1 = GetListEntry(index);
3330 git_wc_status_kind status1 = git_wc_status_none;
3331 git_wc_status_kind status2 = git_wc_status_none;
3332 if (entry1)
3334 status1 = entry1->status;
3335 index = GetNextSelectedItem(pos);
3336 if (index >= 0)
3338 entry2 = GetListEntry(index);
3339 if (entry2)
3341 status2 = entry2->status;
3342 if (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned)
3344 FileEntry * tempentry = entry1;
3345 entry1 = entry2;
3346 entry2 = tempentry;
3348 // entry1 was renamed to entry2 but outside of Subversion
3349 // fix this by moving entry2 back to entry1 first,
3350 // then do an git-move from entry1 to entry2
3351 if (MoveFile(entry2->GetPath().GetWinPath(), entry1->GetPath().GetWinPath()))
3353 SVN git;
3354 if (!git.Move(CTSVNPathList(entry1->GetPath()), entry2->GetPath(), TRUE))
3356 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3358 else
3360 // check the previously unversioned item
3361 entry1->checked = true;
3362 // fixing the move was successful. We have to adjust the new status of the
3363 // files.
3364 // Since we don't know if the moved/renamed file had local modifications or not,
3365 // we can't guess the new status. That means we have to refresh...
3366 CWnd* pParent = GetParent();
3367 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
3369 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);
3373 else
3375 LPVOID lpMsgBuf;
3376 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
3377 FORMAT_MESSAGE_FROM_SYSTEM |
3378 FORMAT_MESSAGE_IGNORE_INSERTS,
3379 NULL,
3380 GetLastError(),
3381 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
3382 (LPTSTR) &lpMsgBuf,
3384 NULL
3386 MessageBox((LPCTSTR)lpMsgBuf, _T("Error"), MB_OK | MB_ICONINFORMATION );
3387 LocalFree( lpMsgBuf );
3394 break;
3395 case IDSVNLC_REMOVEFROMCS:
3397 CTSVNPathList changelistItems;
3398 FillListOfSelectedItemPaths(changelistItems);
3399 SVN git;
3400 SetRedraw(FALSE);
3401 if (git.RemoveFromChangeList(changelistItems, CStringArray(), git_depth_empty))
3403 // The changelists were removed, but we now need to run through the selected items again
3404 // and update their changelist
3405 POSITION pos = GetFirstSelectedItemPosition();
3406 int index;
3407 std::vector<int> entriesToRemove;
3408 while ((index = GetNextSelectedItem(pos)) >= 0)
3410 FileEntry * e = GetListEntry(index);
3411 if (e)
3413 e->changelist.Empty();
3414 if (e->status == git_wc_status_normal)
3416 // remove the entry completely
3417 entriesToRemove.push_back(index);
3419 else
3420 SetItemGroup(index, 0);
3423 for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)
3425 RemoveListEntry(*it);
3427 // TODO: Should we go through all entries here and check if we also could
3428 // remove the changelist from m_changelists ?
3430 else
3432 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3434 SetRedraw(TRUE);
3436 break;
3437 case IDSVNLC_CREATEIGNORECS:
3438 CreateChangeList(SVNSLC_IGNORECHANGELIST);
3439 break;
3440 case IDSVNLC_CREATECS:
3442 CCreateChangelistDlg dlg;
3443 if (dlg.DoModal() == IDOK)
3445 CreateChangeList(dlg.m_sName);
3448 break;
3449 default:
3451 if (cmd < IDSVNLC_MOVETOCS)
3452 break;
3453 CTSVNPathList changelistItems;
3454 FillListOfSelectedItemPaths(changelistItems);
3456 // find the changelist name
3457 CString sChangelist;
3458 int cmdID = IDSVNLC_MOVETOCS;
3459 SetRedraw(FALSE);
3460 for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)
3462 if ((it->first.Compare(SVNSLC_IGNORECHANGELIST))&&(entry->changelist.Compare(it->first)))
3464 if (cmd == cmdID)
3466 sChangelist = it->first;
3468 cmdID++;
3471 if (!sChangelist.IsEmpty())
3473 SVN git;
3474 if (git.AddToChangeList(changelistItems, sChangelist, git_depth_empty))
3476 // The changelists were moved, but we now need to run through the selected items again
3477 // and update their changelist
3478 POSITION pos = GetFirstSelectedItemPosition();
3479 int index;
3480 while ((index = GetNextSelectedItem(pos)) >= 0)
3482 FileEntry * e = GetListEntry(index);
3483 e->changelist = sChangelist;
3484 if (!e->IsFolder())
3486 if (m_changelists.find(e->changelist)!=m_changelists.end())
3487 SetItemGroup(index, m_changelists[e->changelist]);
3488 else
3489 SetItemGroup(index, 0);
3493 else
3495 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
3498 SetRedraw(TRUE);
3500 break;
3501 #endif
3503 } // switch (cmd)
3504 m_bBlock = FALSE;
3505 AfxGetApp()->DoWaitCursor(-1);
3506 GetStatisticsString();
3507 //int iItemCountAfterMenuCmd = GetItemCount();
3508 //if (iItemCountAfterMenuCmd != iItemCountBeforeMenuCmd)
3510 // CWnd* pParent = GetParent();
3511 // if (NULL != pParent && NULL != pParent->GetSafeHwnd())
3512 // {
3513 // pParent->SendMessage(SVNSLNM_ITEMCOUNTCHANGED);
3514 // }
3516 } // if (popup.CreatePopupMenu())
3517 } // if (selIndex >= 0)
3521 void CGitStatusListCtrl::OnContextMenuHeader(CWnd * pWnd, CPoint point)
3523 Locker lock(m_critSec);
3524 m_ColumnManager.OnContextMenuHeader(pWnd,point,!!IsGroupViewEnabled());
3527 void CGitStatusListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)
3530 if (pWnd == this)
3532 OnContextMenuList(pWnd, point);
3533 } // if (pWnd == this)
3534 else if (pWnd == GetHeaderCtrl())
3536 OnContextMenuHeader(pWnd, point);
3540 void CGitStatusListCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)
3543 Locker lock(m_critSec);
3544 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
3545 *pResult = 0;
3546 if (m_bBlock)
3547 return;
3549 if (pNMLV->iItem < 0)
3551 if (!IsGroupViewEnabled())
3552 return;
3553 POINT pt;
3554 DWORD ptW = GetMessagePos();
3555 pt.x = GET_X_LPARAM(ptW);
3556 pt.y = GET_Y_LPARAM(ptW);
3557 ScreenToClient(&pt);
3558 int group = GetGroupFromPoint(&pt);
3559 if (group < 0)
3560 return;
3561 // check/uncheck the whole group depending on the check-state
3562 // of the first item in the group
3563 m_bBlock = true;
3564 bool bCheck = false;
3565 bool bFirst = false;
3566 LVITEM lv;
3567 for (int i=0; i<GetItemCount(); ++i)
3569 SecureZeroMemory(&lv, sizeof(LVITEM));
3570 lv.mask = LVIF_GROUPID;
3571 lv.iItem = i;
3572 GetItem(&lv);
3573 if (lv.iGroupId == group)
3575 CTGitPath *entry=(CTGitPath*)GetItemData(i);
3577 if (!bFirst)
3579 bCheck = !GetCheck(i);
3580 bFirst = true;
3582 if (entry)
3584 bool bOldCheck = !!GetCheck(i);
3585 SetEntryCheck(entry, i, bCheck);
3586 if (bCheck != bOldCheck)
3588 if (bCheck)
3589 m_nSelected++;
3590 else
3591 m_nSelected--;
3596 GetStatisticsString();
3597 m_bBlock = false;
3598 NotifyCheck();
3599 return;
3602 CTGitPath *file=(CTGitPath*)GetItemData(pNMLV->iItem);
3604 if (file->m_Action == CTGitPath::LOGACTIONS_UNVER) {
3605 OpenFile(file, OPEN);
3606 return;
3608 if( file->m_Action&CTGitPath::LOGACTIONS_UNMERGED )
3610 CAppUtils::ConflictEdit(*file,false,m_bIsRevertTheirMy);
3613 else
3615 if( (!m_Rev1.IsEmpty()) || (!m_Rev1.IsEmpty())) // m_Rev1 twice???
3616 StartDiffTwo(pNMLV->iItem);
3617 else
3618 StartDiff(pNMLV->iItem);
3622 void CGitStatusListCtrl::StartDiffTwo(int fileindex)
3624 if(fileindex<0)
3625 return;
3627 CTGitPath file1=*(CTGitPath*)GetItemData(fileindex);
3629 CGitDiff::Diff(&file1,&file1,
3630 m_Rev1,
3631 m_Rev2);
3634 void CGitStatusListCtrl::StartDiffWC(int fileindex)
3636 if(fileindex<0)
3637 return;
3639 CString Ver;
3640 if(this->m_CurrentVersion.IsEmpty() || m_CurrentVersion== GIT_REV_ZERO)
3641 return;
3643 CTGitPath file1=*(CTGitPath*)GetItemData(fileindex);
3645 CGitDiff::Diff(&file1,&file1, GIT_REV_ZERO, m_CurrentVersion);
3649 void CGitStatusListCtrl::StartDiff(int fileindex)
3651 if(fileindex<0)
3652 return;
3654 CTGitPath file1=*(CTGitPath*)GetItemData(fileindex);
3655 CTGitPath file2;
3656 if(file1.m_Action & (CTGitPath::LOGACTIONS_REPLACED|CTGitPath::LOGACTIONS_COPY))
3658 file2.SetFromGit(file1.GetGitOldPathString());
3660 else
3662 file2=file1;
3665 if(this->m_CurrentVersion.IsEmpty() || m_CurrentVersion== GIT_REV_ZERO)
3667 CString fromwhere;
3668 if(m_amend && (file1.m_Action & CTGitPath::LOGACTIONS_ADDED) == 0)
3669 fromwhere = _T("~1");
3670 if( g_Git.IsInitRepos())
3671 CGitDiff::DiffNull((CTGitPath*)GetItemData(fileindex),
3672 GIT_REV_ZERO);
3673 else if( file1.m_Action&CTGitPath::LOGACTIONS_ADDED )
3674 CGitDiff::DiffNull((CTGitPath*)GetItemData(fileindex),
3675 m_CurrentVersion+fromwhere,true);
3676 else if( file1.m_Action&CTGitPath::LOGACTIONS_DELETED )
3677 CGitDiff::DiffNull((CTGitPath*)GetItemData(fileindex),
3678 GitRev::GetHead()+fromwhere,false);
3679 else
3680 CGitDiff::Diff(&file1,&file2,
3681 CString(GIT_REV_ZERO),
3682 GitRev::GetHead()+fromwhere);
3684 else
3686 GitRev rev;
3687 CString fromwhere = m_CurrentVersion+_T("~1");
3688 if(m_amend)
3689 fromwhere = m_CurrentVersion+_T("~2");
3690 if(rev.GetCommit(fromwhere) || (file1.m_Action == file1.LOGACTIONS_ADDED))
3692 CGitDiff::DiffNull(&file1,m_CurrentVersion,true);
3695 else if (file1.m_Action == file1.LOGACTIONS_DELETED)
3697 if (file1.m_ParentNo > 0)
3698 fromwhere.Format(_T("%s^%d"), m_CurrentVersion, file1.m_ParentNo + 1);
3700 CGitDiff::DiffNull(&file1,fromwhere,false);
3702 else
3704 if( file1.m_ParentNo & MERGE_MASK)
3707 CTGitPath base, theirs, mine, merge;
3709 CString temppath;
3710 GetTempPath(temppath);
3711 temppath.TrimRight(_T("\\"));
3713 mine.SetFromGit(temppath + _T("\\") + file1.GetFileOrDirectoryName() + _T(".LOCAL") + file1.GetFileExtension());
3714 theirs.SetFromGit(temppath + _T("\\") + file1.GetFileOrDirectoryName() + _T(".REMOTE") + file1.GetFileExtension());
3715 base.SetFromGit(temppath + _T("\\") + file1.GetFileOrDirectoryName() + _T(".BASE") + file1.GetFileExtension());
3717 CFile tempfile;
3718 //create a empty file, incase stage is not three
3719 tempfile.Open(mine.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
3720 tempfile.Close();
3721 tempfile.Open(theirs.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
3722 tempfile.Close();
3723 tempfile.Open(base.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
3724 tempfile.Close();
3726 merge.SetFromGit(temppath + _T("\\") + file1.GetFileOrDirectoryName() + _T(".Merged") + file1.GetFileExtension());
3728 int parent1=-1, parent2 =-1;
3729 for(int i=0;i<this->m_arStatusArray.size();i++)
3731 if(m_arStatusArray[i]->GetGitPathString() == file1.GetGitPathString())
3733 if(m_arStatusArray[i]->m_ParentNo & MERGE_MASK)
3736 else
3738 if(parent1<0)
3740 parent1 = m_arStatusArray[i]->m_ParentNo & PARENT_MASK;
3742 else if (parent2 <0)
3744 parent2 = m_arStatusArray[i]->m_ParentNo & PARENT_MASK;
3750 if(g_Git.GetOneFile(m_CurrentVersion, file1, (CString&)merge.GetWinPathString()))
3752 CMessageBox::Show(NULL, IDS_STATUSLIST_FAILEDGETMERGEFILE, IDS_APPNAME, MB_OK | MB_ICONERROR);
3755 if(parent1>=0)
3757 CString str;
3758 str.Format(_T("%s^%d"),this->m_CurrentVersion, parent1+1);
3760 if(g_Git.GetOneFile(str, file1, (CString&)mine.GetWinPathString()))
3762 CMessageBox::Show(NULL, IDS_STATUSLIST_FAILEDGETMERGEFILE, IDS_APPNAME, MB_OK | MB_ICONERROR);
3766 if(parent2>=0)
3768 CString str;
3769 str.Format(_T("%s^%d"),this->m_CurrentVersion, parent2+1);
3771 if(g_Git.GetOneFile(str, file1, (CString&)theirs.GetWinPathString()))
3773 CMessageBox::Show(NULL, IDS_STATUSLIST_FAILEDGETMERGEFILE, IDS_APPNAME, MB_OK | MB_ICONERROR);
3777 if(parent1>=0 && parent2>=0)
3779 CString cmd, output;
3780 cmd.Format(_T("git.exe merge-base %s^%d %s^%d"), this->m_CurrentVersion, parent1+1,
3781 this->m_CurrentVersion,parent2+1);
3783 if (g_Git.Run(cmd, &output, NULL, CP_UTF8))
3786 else
3788 if(g_Git.GetOneFile(output.Left(40), file1, (CString&)base.GetWinPathString()))
3790 CMessageBox::Show(NULL, IDS_STATUSLIST_FAILEDGETBASEFILE, IDS_APPNAME, MB_OK | MB_ICONERROR);
3794 CAppUtils::StartExtMerge(base, theirs, mine, merge,_T("BASE"),_T("REMOTE"),_T("LOCAL"));
3797 else
3799 CString str;
3800 if( (file1.m_ParentNo&PARENT_MASK) == 0)
3802 str = _T("~1");
3804 else
3806 str.Format(_T("^%d"), (file1.m_ParentNo&PARENT_MASK)+1);
3808 CGitDiff::Diff(&file1,&file2,
3809 m_CurrentVersion,
3810 m_CurrentVersion+str);
3814 #if 0
3815 if (fileindex < 0)
3816 return;
3817 FileEntry * entry = GetListEntry(fileindex);
3818 ASSERT(entry != NULL);
3819 if (entry == NULL)
3820 return;
3821 if (((entry->status == git_wc_status_normal)&&(entry->remotestatus <= git_wc_status_normal))||
3822 (entry->status == git_wc_status_unversioned)||(entry->status == git_wc_status_none))
3824 int ret = (int)ShellExecute(this->m_hWnd, NULL, entry->path.GetWinPath(), NULL, NULL, SW_SHOW);
3825 if (ret <= HINSTANCE_ERROR)
3827 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
3828 cmd += entry->path.GetWinPathString();
3829 CAppUtils::LaunchApplication(cmd, NULL, false);
3831 return;
3834 GitDiff diff(NULL, m_hWnd, true);
3835 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
3836 diff.DiffWCFile(
3837 entry->path, entry->textstatus, entry->propstatus,
3838 entry->remotetextstatus, entry->remotepropstatus);
3839 #endif
3842 CString CGitStatusListCtrl::GetStatisticsString(bool simple)
3845 CString sNormal, sAdded, sDeleted, sModified, sConflicted, sUnversioned, sRenamed;
3846 WORD langID = (WORD)(DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());
3847 TCHAR buf[MAX_STATUS_STRING_LENGTH];
3848 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_normal, buf, _countof(buf), langID);
3849 sNormal = buf;
3850 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_added, buf, _countof(buf), langID);
3851 sAdded = buf;
3852 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_deleted, buf, _countof(buf), langID);
3853 sDeleted = buf;
3854 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_modified, buf, _countof(buf), langID);
3855 sModified = buf;
3856 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_conflicted, buf, _countof(buf), langID);
3857 sConflicted = buf;
3858 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_unversioned, buf, _countof(buf), langID);
3859 sUnversioned = buf;
3860 GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_replaced, buf, _countof(buf), langID);
3861 sRenamed = buf;
3862 CString sToolTip;
3863 if(simple)
3865 sToolTip.Format(IDS_STATUSLIST_STATUSLINE1,
3866 this->m_nLineAdded,this->m_nLineDeleted,
3867 (LPCTSTR)sModified, m_nModified,
3868 (LPCTSTR)sAdded, m_nAdded,
3869 (LPCTSTR)sDeleted, m_nDeleted,
3870 (LPCTSTR)sRenamed, m_nRenamed
3873 else
3875 sToolTip.Format(IDS_STATUSLIST_STATUSLINE2,
3876 this->m_nLineAdded,this->m_nLineDeleted,
3877 (LPCTSTR)sNormal, m_nNormal,
3878 (LPCTSTR)sUnversioned, m_nUnversioned,
3879 (LPCTSTR)sModified, m_nModified,
3880 (LPCTSTR)sAdded, m_nAdded,
3881 (LPCTSTR)sDeleted, m_nDeleted,
3882 (LPCTSTR)sConflicted, m_nConflicted
3885 CString sStats;
3886 sStats.Format(IDS_COMMITDLG_STATISTICSFORMAT, m_nSelected, GetItemCount());
3887 if (m_pStatLabel)
3889 m_pStatLabel->SetWindowText(sStats);
3892 if (m_pSelectButton)
3894 if (m_nSelected == 0)
3895 m_pSelectButton->SetCheck(BST_UNCHECKED);
3896 else if (m_nSelected != GetItemCount())
3897 m_pSelectButton->SetCheck(BST_INDETERMINATE);
3898 else
3899 m_pSelectButton->SetCheck(BST_CHECKED);
3902 if (m_pConfirmButton)
3904 m_pConfirmButton->EnableWindow(m_nSelected>0);
3907 return sToolTip;
3912 CString CGitStatusListCtrl::GetCommonDirectory(bool bStrict)
3914 if (!bStrict)
3916 // not strict means that the selected folder has priority
3917 if (!m_StatusFileList.GetCommonDirectory().IsEmpty())
3918 return m_StatusFileList.GetCommonDirectory().GetWinPath();
3921 CTGitPath commonBaseDirectory;
3922 int nListItems = GetItemCount();
3923 for (int i=0; i<nListItems; ++i)
3925 CTGitPath baseDirectory,*p= (CTGitPath*)this->GetItemData(i);
3926 ASSERT(p);
3927 if(p==NULL)
3928 continue;
3929 baseDirectory = p->GetDirectory();
3931 if(commonBaseDirectory.IsEmpty())
3933 commonBaseDirectory = baseDirectory;
3935 else
3937 if (commonBaseDirectory.GetWinPathString().GetLength() > baseDirectory.GetWinPathString().GetLength())
3939 if (baseDirectory.IsAncestorOf(commonBaseDirectory))
3940 commonBaseDirectory = baseDirectory;
3944 return g_Git.m_CurrentDir+CString(_T("\\"))+commonBaseDirectory.GetWinPath();
3948 void CGitStatusListCtrl::SelectAll(bool bSelect, bool /*bIncludeNoCommits*/)
3950 CWaitCursor waitCursor;
3951 // block here so the LVN_ITEMCHANGED messages
3952 // get ignored
3953 m_bBlock = TRUE;
3954 SetRedraw(FALSE);
3956 int nListItems = GetItemCount();
3957 if (bSelect)
3958 m_nSelected = nListItems;
3959 else
3960 m_nSelected = 0;
3962 for (int i=0; i<nListItems; ++i)
3964 //FileEntry * entry = GetListEntry(i);
3965 //ASSERT(entry != NULL);
3966 CTGitPath *path = (CTGitPath *) GetItemData(i);
3967 if (path == NULL)
3968 continue;
3969 //if ((bIncludeNoCommits)||(entry->GetChangeList().Compare(SVNSLC_IGNORECHANGELIST)))
3970 SetEntryCheck(path,i,bSelect);
3973 // unblock before redrawing
3974 m_bBlock = FALSE;
3975 SetRedraw(TRUE);
3976 GetStatisticsString();
3977 NotifyCheck();
3980 void CGitStatusListCtrl::Check(DWORD dwCheck, bool uncheckNonMatches)
3982 CWaitCursor waitCursor;
3983 // block here so the LVN_ITEMCHANGED messages
3984 // get ignored
3985 m_bBlock = TRUE;
3986 SetRedraw(FALSE);
3988 int nListItems = GetItemCount();
3989 m_nSelected = 0;
3991 for (int i = 0; i < nListItems; ++i)
3993 CTGitPath *entry = (CTGitPath *) GetItemData(i);
3994 if (entry == NULL)
3995 continue;
3997 DWORD showFlags = entry->m_Action;
3998 if (entry->IsDirectory())
3999 showFlags |= GITSLC_SHOWSUBMODULES;
4000 else
4001 showFlags |= GITSLC_SHOWFILES;
4003 if (showFlags & dwCheck && !(entry->IsDirectory() && m_bDoNotAutoselectSubmodules && !(dwCheck & GITSLC_SHOWSUBMODULES)))
4005 SetEntryCheck(entry, i, true);
4006 m_nSelected++;
4008 else if (uncheckNonMatches)
4009 SetEntryCheck(entry, i, false);
4011 // unblock before redrawing
4012 m_bBlock = FALSE;
4013 SetRedraw(TRUE);
4014 GetStatisticsString();
4015 NotifyCheck();
4018 void CGitStatusListCtrl::OnLvnGetInfoTip(NMHDR *pNMHDR, LRESULT *pResult)
4020 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
4021 *pResult = 0;
4022 if (m_bBlock)
4023 return;
4025 CTGitPath *entry=(CTGitPath *)GetItemData(pGetInfoTip->iItem);
4027 if (entry)
4028 if (pGetInfoTip->cchTextMax > entry->GetGitPathString().GetLength() + g_Git.m_CurrentDir.GetLength())
4030 CString str;
4031 str += g_Git.m_CurrentDir;
4032 str += _T("\\");
4033 str += entry->GetWinPathString();
4035 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, str.GetBuffer(), str.GetLength());
4039 void CGitStatusListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
4041 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
4043 // Take the default processing unless we set this to something else below.
4044 *pResult = CDRF_DODEFAULT;
4046 // First thing - check the draw stage. If it's the control's prepaint
4047 // stage, then tell Windows we want messages for every item.
4049 switch (pLVCD->nmcd.dwDrawStage)
4051 case CDDS_PREPAINT:
4052 *pResult = CDRF_NOTIFYITEMDRAW;
4053 break;
4054 case CDDS_ITEMPREPAINT:
4056 // This is the prepaint stage for an item. Here's where we set the
4057 // item's text color. Our return value will tell Windows to draw the
4058 // item itself, but it will use the new color we set here.
4060 // Tell Windows to paint the control itself.
4061 *pResult = CDRF_DODEFAULT;
4062 if (m_bBlock)
4063 return;
4065 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
4067 if (m_arStatusArray.size() > (DWORD_PTR)pLVCD->nmcd.dwItemSpec)
4070 //FileEntry * entry = GetListEntry((int)pLVCD->nmcd.dwItemSpec);
4071 CTGitPath *entry=(CTGitPath *)GetItemData((int)pLVCD->nmcd.dwItemSpec);
4072 if (entry == NULL)
4073 return;
4075 // coloring
4076 // ========
4077 // black : unversioned, normal
4078 // purple : added
4079 // blue : modified
4080 // brown : missing, deleted, replaced
4081 // green : merged (or potential merges)
4082 // red : conflicts or sure conflicts
4083 if(entry->m_Action & CTGitPath::LOGACTIONS_GRAY)
4085 crText = RGB(128,128,128);
4088 else if(entry->m_Action & CTGitPath::LOGACTIONS_UNMERGED)
4090 crText = m_Colors.GetColor(CColors::Conflict);
4093 else if(entry->m_Action & (CTGitPath::LOGACTIONS_MODIFIED))
4095 crText = m_Colors.GetColor(CColors::Modified);
4098 else if(entry->m_Action & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY))
4100 crText = m_Colors.GetColor(CColors::Added);
4102 else if(entry->m_Action & CTGitPath::LOGACTIONS_DELETED)
4104 crText = m_Colors.GetColor(CColors::DeletedNode);
4106 else if(entry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
4108 crText = m_Colors.GetColor(CColors::RenamedNode);
4110 else if(entry->m_Action & CTGitPath::LOGACTIONS_MERGED)
4112 crText = m_Colors.GetColor(CColors::Merged);
4114 else
4116 crText = GetSysColor(COLOR_WINDOWTEXT);
4118 // Store the color back in the NMLVCUSTOMDRAW struct.
4119 pLVCD->clrText = crText;
4122 break;
4126 BOOL CGitStatusListCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
4128 if (pWnd != this)
4129 return CListCtrl::OnSetCursor(pWnd, nHitTest, message);
4130 if (!m_bBlock)
4132 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
4133 SetCursor(hCur);
4134 return CListCtrl::OnSetCursor(pWnd, nHitTest, message);
4136 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
4137 SetCursor(hCur);
4138 return TRUE;
4141 void CGitStatusListCtrl::RemoveListEntry(int index)
4144 Locker lock(m_critSec);
4145 DeleteItem(index);
4147 m_arStatusArray.erase(m_arStatusArray.begin()+index);
4149 #if 0
4150 delete m_arStatusArray[m_arListArray[index]];
4151 m_arStatusArray.erase(m_arStatusArray.begin()+m_arListArray[index]);
4152 m_arListArray.erase(m_arListArray.begin()+index);
4153 for (int i=index; i< (int)m_arListArray.size(); ++i)
4155 m_arListArray[i]--;
4157 #endif
4160 ///< Set a checkbox on an entry in the listbox
4161 // NEVER, EVER call SetCheck directly, because you'll end-up with the checkboxes and the 'checked' flag getting out of sync
4162 void CGitStatusListCtrl::SetEntryCheck(CTGitPath* pEntry, int listboxIndex, bool bCheck)
4164 pEntry->m_Checked = bCheck;
4165 m_mapFilenameToChecked[pEntry->GetGitPathString()] = bCheck;
4166 SetCheck(listboxIndex, bCheck);
4169 #if 0
4170 void CGitStatusListCtrl::SetCheckOnAllDescendentsOf(const FileEntry* parentEntry, bool bCheck)
4173 int nListItems = GetItemCount();
4174 for (int j=0; j< nListItems ; ++j)
4176 FileEntry * childEntry = GetListEntry(j);
4177 ASSERT(childEntry != NULL);
4178 if (childEntry == NULL || childEntry == parentEntry)
4179 continue;
4180 if (childEntry->checked != bCheck)
4182 if (parentEntry->path.IsAncestorOf(childEntry->path))
4184 SetEntryCheck(childEntry,j,bCheck);
4185 if(bCheck)
4187 m_nSelected++;
4189 else
4191 m_nSelected--;
4198 #endif
4200 void CGitStatusListCtrl::WriteCheckedNamesToPathList(CTGitPathList& pathList)
4203 pathList.Clear();
4204 int nListItems = GetItemCount();
4205 for (int i=0; i< nListItems; i++)
4207 CTGitPath * entry = (CTGitPath*)GetItemData(i);
4208 ASSERT(entry != NULL);
4209 if (entry->m_Checked)
4211 pathList.AddPath(*entry);
4214 pathList.SortByPathname();
4219 /// Build a path list of all the selected items in the list (NOTE - SELECTED, not CHECKED)
4220 void CGitStatusListCtrl::FillListOfSelectedItemPaths(CTGitPathList& pathList, bool /*bNoIgnored*/)
4222 pathList.Clear();
4224 POSITION pos = GetFirstSelectedItemPosition();
4225 int index;
4226 while ((index = GetNextSelectedItem(pos)) >= 0)
4228 CTGitPath * entry = (CTGitPath*)GetItemData(index);
4229 //if ((bNoIgnored)&&(entry->status == git_wc_status_ignored))
4230 // continue;
4231 pathList.AddPath(*entry);
4235 UINT CGitStatusListCtrl::OnGetDlgCode()
4237 // we want to process the return key and not have that one
4238 // routed to the default pushbutton
4239 return CListCtrl::OnGetDlgCode() | DLGC_WANTALLKEYS;
4242 void CGitStatusListCtrl::OnNMReturn(NMHDR * /*pNMHDR*/, LRESULT *pResult)
4244 *pResult = 0;
4245 if (m_bBlock)
4246 return;
4247 POSITION pos = GetFirstSelectedItemPosition();
4248 while ( pos )
4250 int index = GetNextSelectedItem(pos);
4251 CTGitPath *file=(CTGitPath*)GetItemData(index);
4252 if (file->m_Action == CTGitPath::LOGACTIONS_UNVER)
4254 OpenFile(file, OPEN);
4256 else
4258 StartDiff(index);
4263 void CGitStatusListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
4265 // Since we catch all keystrokes (to have the enter key processed here instead
4266 // of routed to the default pushbutton) we have to make sure that other
4267 // keys like Tab and Esc still do what they're supposed to do
4268 // Tab = change focus to next/previous control
4269 // Esc = quit the dialog
4270 switch (nChar)
4272 case (VK_TAB):
4274 ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);
4275 return;
4277 break;
4278 case (VK_ESCAPE):
4280 ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);
4282 break;
4285 CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
4288 void CGitStatusListCtrl::PreSubclassWindow()
4290 CListCtrl::PreSubclassWindow();
4291 EnableToolTips(TRUE);
4292 SetWindowTheme(GetSafeHwnd(), L"Explorer", NULL);
4295 INT_PTR CGitStatusListCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
4297 int row, col;
4298 RECT cellrect;
4299 row = CellRectFromPoint(point, &cellrect, &col );
4301 if (row == -1)
4302 return -1;
4305 pTI->hwnd = m_hWnd;
4306 pTI->uId = (UINT)((row<<10)+(col&0x3ff)+1);
4307 pTI->lpszText = LPSTR_TEXTCALLBACK;
4309 pTI->rect = cellrect;
4311 return pTI->uId;
4314 int CGitStatusListCtrl::CellRectFromPoint(CPoint& point, RECT *cellrect, int *col) const
4316 int colnum;
4318 // Make sure that the ListView is in LVS_REPORT
4319 if ((GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT)
4320 return -1;
4322 // Get the top and bottom row visible
4323 int row = GetTopIndex();
4324 int bottom = row + GetCountPerPage();
4325 if (bottom > GetItemCount())
4326 bottom = GetItemCount();
4328 // Get the number of columns
4329 CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
4330 int nColumnCount = pHeader->GetItemCount();
4332 // Loop through the visible rows
4333 for ( ;row <=bottom;row++)
4335 // Get bounding rect of item and check whether point falls in it.
4336 CRect rect;
4337 GetItemRect(row, &rect, LVIR_BOUNDS);
4338 if (rect.PtInRect(point))
4340 // Now find the column
4341 for (colnum = 0; colnum < nColumnCount; colnum++)
4343 int colwidth = GetColumnWidth(colnum);
4344 if (point.x >= rect.left && point.x <= (rect.left + colwidth))
4346 RECT rectClient;
4347 GetClientRect(&rectClient);
4348 if (col)
4349 *col = colnum;
4350 rect.right = rect.left + colwidth;
4352 // Make sure that the right extent does not exceed
4353 // the client area
4354 if (rect.right > rectClient.right)
4355 rect.right = rectClient.right;
4356 *cellrect = rect;
4357 return row;
4359 rect.left += colwidth;
4363 return -1;
4366 BOOL CGitStatusListCtrl::OnToolTipText(UINT /*id*/, NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/)
4368 #if 0
4369 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
4370 CString strTipText;
4371 UINT_PTR nID = pNMHDR->idFrom;
4373 if (nID == 0)
4374 return FALSE;
4376 UINT_PTR row = ((nID-1) >> 10) & 0x3fffff;
4377 UINT_PTR col = (nID-1) & 0x3ff;
4379 if (col == 0)
4380 return FALSE; // no custom tooltip for the path, we use the infotip there!
4382 // get the internal column from the visible columns
4383 int internalcol = 0;
4384 UINT_PTR currentcol = 0;
4385 for (; (currentcol != col)
4386 && (internalcol < m_ColumnManager.GetColumnCount()-1)
4387 ; ++internalcol)
4389 if (m_ColumnManager.IsVisible (internalcol))
4390 currentcol++;
4393 AFX_MODULE_THREAD_STATE* pModuleThreadState = AfxGetModuleThreadState();
4394 CToolTipCtrl* pToolTip = pModuleThreadState->m_pToolTip;
4395 pToolTip->SendMessage(TTM_SETMAXTIPWIDTH, 0, 300);
4397 *pResult = 0;
4398 if ((internalcol == 2)||(internalcol == 4))
4400 FileEntry *fentry = GetListEntry(row);
4401 if (fentry)
4403 if (fentry->copied)
4405 CString url;
4406 url.Format(IDS_STATUSLIST_COPYFROM, (LPCTSTR)CPathUtils::PathUnescape(fentry->copyfrom_url), (LONG)fentry->copyfrom_rev);
4407 lstrcpyn(pTTTW->szText, (LPCTSTR)url, 80);
4408 return TRUE;
4410 if (fentry->switched)
4412 CString url;
4413 url.Format(IDS_STATUSLIST_SWITCHEDTO, (LPCTSTR)CPathUtils::PathUnescape(fentry->url));
4414 lstrcpyn(pTTTW->szText, (LPCTSTR)url, 80);
4415 return TRUE;
4417 if (fentry->keeplocal)
4419 lstrcpyn(pTTTW->szText, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_KEEPLOCAL)), 80);
4420 return TRUE;
4424 #endif
4425 return FALSE;
4428 void CGitStatusListCtrl::OnPaint()
4430 Default();
4431 if ((m_bBusy)||(m_bEmpty))
4433 CString str;
4434 if (m_bBusy)
4436 if (m_sBusy.IsEmpty())
4437 str.LoadString(IDS_STATUSLIST_BUSYMSG);
4438 else
4439 str = m_sBusy;
4441 else
4443 if (m_sEmpty.IsEmpty())
4444 str.LoadString(IDS_STATUSLIST_EMPTYMSG);
4445 else
4446 str = m_sEmpty;
4448 COLORREF clrText = ::GetSysColor(COLOR_WINDOWTEXT);
4449 COLORREF clrTextBk;
4450 if (IsWindowEnabled())
4451 clrTextBk = ::GetSysColor(COLOR_WINDOW);
4452 else
4453 clrTextBk = ::GetSysColor(COLOR_3DFACE);
4455 CRect rc;
4456 GetClientRect(&rc);
4457 CHeaderCtrl* pHC;
4458 pHC = GetHeaderCtrl();
4459 if (pHC != NULL)
4461 CRect rcH;
4462 pHC->GetItemRect(0, &rcH);
4463 rc.top += rcH.bottom;
4465 CDC* pDC = GetDC();
4467 CMyMemDC memDC(pDC, &rc);
4469 memDC.SetTextColor(clrText);
4470 memDC.SetBkColor(clrTextBk);
4471 memDC.FillSolidRect(rc, clrTextBk);
4472 rc.top += 10;
4473 CGdiObject * oldfont = memDC.SelectStockObject(DEFAULT_GUI_FONT);
4474 memDC.DrawText(str, rc, DT_CENTER | DT_VCENTER |
4475 DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);
4476 memDC.SelectObject(oldfont);
4478 ReleaseDC(pDC);
4482 // prevent users from extending our hidden (size 0) columns
4483 void CGitStatusListCtrl::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)
4485 m_ColumnManager.OnHdnBegintrack(pNMHDR, pResult);
4488 // prevent any function from extending our hidden (size 0) columns
4489 void CGitStatusListCtrl::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
4491 if(!m_ColumnManager.OnHdnItemchanging(pNMHDR, pResult))
4492 Default();
4495 void CGitStatusListCtrl::OnDestroy()
4497 SaveColumnWidths(true);
4498 CListCtrl::OnDestroy();
4501 void CGitStatusListCtrl::OnBeginDrag(NMHDR* /*pNMHDR*/, LRESULT* pResult)
4503 #if 0
4504 Locker lock(m_critSec);
4505 CDropFiles dropFiles; // class for creating DROPFILES struct
4507 int index;
4508 POSITION pos = GetFirstSelectedItemPosition();
4509 while ( (index = GetNextSelectedItem(pos)) >= 0 )
4511 FileEntry * fentry = m_arStatusArray[m_arListArray[index]];
4512 CTGitPath path = fentry->GetPath();
4513 dropFiles.AddFile( path.GetWinPathString() );
4516 if ( dropFiles.GetCount()>0 )
4518 m_bOwnDrag = true;
4519 dropFiles.CreateStructure();
4520 m_bOwnDrag = false;
4522 #endif
4523 *pResult = 0;
4526 void CGitStatusListCtrl::SaveColumnWidths(bool bSaveToRegistry /* = false */)
4528 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
4529 for (int col = 0; col <= maxcol; col++)
4530 if (m_ColumnManager.IsVisible (col))
4531 m_ColumnManager.ColumnResized (col);
4533 if (bSaveToRegistry)
4534 m_ColumnManager.WriteSettings();
4537 bool CGitStatusListCtrl::EnableFileDrop()
4539 m_bFileDropsEnabled = true;
4540 return true;
4543 bool CGitStatusListCtrl::HasPath(const CTGitPath& path)
4545 for (size_t i=0; i < m_arStatusArray.size(); i++)
4547 if (m_arStatusArray[i]->IsEquivalentTo(path))
4548 return true;
4551 return false;
4554 BOOL CGitStatusListCtrl::PreTranslateMessage(MSG* pMsg)
4556 if (pMsg->message == WM_KEYDOWN)
4558 switch (pMsg->wParam)
4560 case 'A':
4562 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
4564 // select all entries
4565 for (int i=0; i<GetItemCount(); ++i)
4567 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
4569 return TRUE;
4572 break;
4573 case 'C':
4574 case VK_INSERT:
4576 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
4578 // copy all selected paths to the clipboard
4579 if (GetAsyncKeyState(VK_SHIFT)&0x8000)
4580 CopySelectedEntriesToClipboard(GITSLC_COLSTATUS);
4581 else
4582 CopySelectedEntriesToClipboard(0);
4583 return TRUE;
4586 break;
4590 return CListCtrl::PreTranslateMessage(pMsg);
4593 bool CGitStatusListCtrl::CopySelectedEntriesToClipboard(DWORD dwCols)
4596 static HINSTANCE hResourceHandle(AfxGetResourceHandle());
4597 // WORD langID = (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());
4599 CString sClipboard;
4600 CString temp;
4601 //TCHAR buf[100];
4602 if (GetSelectedCount() == 0)
4603 return false;
4605 // first add the column titles as the first line
4606 // We needn't head when only path copy
4607 //temp.LoadString(IDS_STATUSLIST_COLFILE);
4608 //sClipboard = temp;
4610 DWORD selection = 0;
4611 for (int i = 0, count = m_ColumnManager.GetColumnCount(); i < count; ++i)
4612 if ( ((dwCols == -1) && m_ColumnManager.IsVisible (i))
4613 || ((dwCols != 1) && (i < 32) && ((dwCols & (1 << i)) != 0)))
4615 sClipboard += _T("\t") + m_ColumnManager.GetName(i);
4617 if (i < 32)
4618 selection += 1 << i;
4621 if(dwCols)
4622 sClipboard += _T("\r\n");
4624 POSITION pos = GetFirstSelectedItemPosition();
4625 int index;
4626 while ((index = GetNextSelectedItem(pos)) >= 0)
4628 CTGitPath * entry = (CTGitPath*)GetItemData(index);
4629 if(entry == NULL)
4630 continue;
4632 sClipboard += entry->GetWinPathString();
4633 if (selection & GITSLC_COLFILENAME)
4635 sClipboard += _T("\t")+entry->GetFileOrDirectoryName();
4637 if (selection & GITSLC_COLEXT)
4639 sClipboard += _T("\t")+entry->GetFileExtension();
4642 if (selection & GITSLC_COLSTATUS)
4644 #if 0
4645 if (entry->isNested)
4647 temp.LoadString(IDS_STATUSLIST_NESTED);
4649 else
4651 GitStatus::GetStatusString(hResourceHandle, entry->status, buf, _countof(buf), (WORD)langID);
4652 if ((entry->copied)&&(_tcslen(buf)>1))
4653 _tcscat_s(buf, 100, _T(" (+)"));
4654 if ((entry->switched)&&(_tcslen(buf)>1))
4655 _tcscat_s(buf, 100, _T(" (s)"));
4656 temp = buf;
4658 #endif
4659 sClipboard += _T("\t")+entry->GetActionName();
4661 #if 0
4662 if (selection & SVNSLC_COLTEXTSTATUS)
4665 if (entry->isNested)
4667 temp.LoadString(IDS_STATUSLIST_NESTED);
4669 else
4671 GitStatus::GetStatusString(hResourceHandle, entry->textstatus, buf, _countof(buf), (WORD)langID);
4672 if ((entry->copied)&&(_tcslen(buf)>1))
4673 _tcscat_s(buf, 100, _T(" (+)"));
4674 if ((entry->switched)&&(_tcslen(buf)>1))
4675 _tcscat_s(buf, 100, _T(" (s)"));
4676 temp = buf;
4678 sClipboard += _T("\t")+temp;
4680 #endif
4681 #if 0
4682 if (selection & SVNSLC_COLREMOTESTATUS)
4684 if (entry->isNested)
4686 temp.LoadString(IDS_STATUSLIST_NESTED);
4688 else
4690 GitStatus::GetStatusString(hResourceHandle, entry->remotestatus, buf, _countof(buf), (WORD)langID);
4691 if ((entry->copied)&&(_tcslen(buf)>1))
4692 _tcscat_s(buf, 100, _T(" (+)"));
4693 if ((entry->switched)&&(_tcslen(buf)>1))
4694 _tcscat_s(buf, 100, _T(" (s)"));
4695 temp = buf;
4697 sClipboard += _T("\t")+temp;
4699 if (selection & GitSLC_COLPROPSTATUS)
4701 if (entry->isNested)
4703 temp.Empty();
4705 else
4707 GitStatus::GetStatusString(hResourceHandle, entry->propstatus, buf, _countof(buf), (WORD)langID);
4708 if ((entry->copied)&&(_tcslen(buf)>1))
4709 _tcscat_s(buf, 100, _T(" (+)"));
4710 if ((entry->switched)&&(_tcslen(buf)>1))
4711 _tcscat_s(buf, 100, _T(" (s)"));
4712 temp = buf;
4714 sClipboard += _T("\t")+temp;
4716 if (selection & SVNSLC_COLREMOTETEXT)
4718 if (entry->isNested)
4720 temp.Empty();
4722 else
4724 GitStatus::GetStatusString(hResourceHandle, entry->remotetextstatus, buf, _countof(buf), (WORD)langID);
4725 temp = buf;
4727 sClipboard += _T("\t")+temp;
4729 if (selection & SVNSLC_COLREMOTEPROP)
4731 // SVNSLC_COLREMOTEPROP
4732 if (entry->isNested)
4734 temp.Empty();
4736 else
4738 GitStatus::GetStatusString(hResourceHandle, entry->remotepropstatus, buf, _countof(buf), (WORD)langID);
4739 temp = buf;
4741 sClipboard += _T("\t")+temp;
4743 if (selection & SVNSLC_COLURL)
4744 sClipboard += _T("\t")+entry->url;
4745 if (selection & SVNSLC_COLLOCK)
4747 if (!m_HeadRev.IsHead())
4749 // we have contacted the repository
4751 // decision-matrix
4752 // wc repository text
4753 // "" "" ""
4754 // "" UID1 owner
4755 // UID1 UID1 owner
4756 // UID1 "" lock has been broken
4757 // UID1 UID2 lock has been stolen
4758 if (entry->lock_token.IsEmpty() || (entry->lock_token.Compare(entry->lock_remotetoken)==0))
4760 if (entry->lock_owner.IsEmpty())
4761 temp = entry->lock_remoteowner;
4762 else
4763 temp = entry->lock_owner;
4765 else if (entry->lock_remotetoken.IsEmpty())
4767 // broken lock
4768 temp.LoadString(IDS_STATUSLIST_LOCKBROKEN);
4770 else
4772 // stolen lock
4773 temp.Format(IDS_STATUSLIST_LOCKSTOLEN, (LPCTSTR)entry->lock_remoteowner);
4776 else
4777 temp = entry->lock_owner;
4778 sClipboard += _T("\t")+temp;
4780 if (selection & SVNSLC_COLLOCKCOMMENT)
4781 sClipboard += _T("\t")+entry->lock_comment;
4782 if (selection & SVNSLC_COLAUTHOR)
4783 sClipboard += _T("\t")+entry->last_commit_author;
4784 if (selection & SVNSLC_COLREVISION)
4786 temp.Format(_T("%ld"), entry->last_commit_rev);
4787 if (entry->last_commit_rev == 0)
4788 temp.Empty();
4789 sClipboard += _T("\t")+temp;
4791 if (selection & SVNSLC_COLREMOTEREVISION)
4793 temp.Format(_T("%ld"), entry->remoterev);
4794 if (entry->remoterev == 0)
4795 temp.Empty();
4796 sClipboard += _T("\t")+temp;
4799 if (selection & SVNSLC_COLDATE)
4801 TCHAR datebuf[SVN_DATE_BUFFER];
4802 apr_time_t date = entry->last_commit_date;
4803 SVN::formatDate(datebuf, date, true);
4804 if (date)
4805 temp = datebuf;
4806 else
4807 temp.Empty();
4808 sClipboard += _T("\t")+temp;
4810 if (selection & SVNSLC_COLCOPYFROM)
4812 if (m_sURL.Compare(entry->copyfrom_url.Left(m_sURL.GetLength()))==0)
4813 temp = entry->copyfrom_url.Mid(m_sURL.GetLength());
4814 else
4815 temp = entry->copyfrom_url;
4816 sClipboard += _T("\t")+temp;
4819 for ( int i = SVNSLC_NUMCOLUMNS, count = m_ColumnManager.GetColumnCount()
4820 ; i < count
4821 ; ++i)
4823 if ((dwCols == -1) && m_ColumnManager.IsVisible (i))
4825 CString value
4826 = entry->present_props[m_ColumnManager.GetName(i)];
4827 sClipboard += _T("\t") + value;
4830 #endif
4831 if (selection & GITSLC_COLADD)
4833 sClipboard += _T("\t")+entry->m_StatAdd;
4835 if (selection & GITSLC_COLDEL)
4837 sClipboard += _T("\t")+entry->m_StatDel;
4840 sClipboard += _T("\r\n");
4843 return CStringUtils::WriteAsciiStringToClipboard(sClipboard);
4845 return TRUE;
4849 size_t CGitStatusListCtrl::GetNumberOfChangelistsInSelection()
4851 #if 0
4852 std::set<CString> changelists;
4853 POSITION pos = GetFirstSelectedItemPosition();
4854 int index;
4855 while ((index = GetNextSelectedItem(pos)) >= 0)
4857 FileEntry * entry = GetListEntry(index);
4858 if (!entry->changelist.IsEmpty())
4859 changelists.insert(entry->changelist);
4861 return changelists.size();
4862 #endif
4863 return 0;
4866 bool CGitStatusListCtrl::PrepareGroups(bool bForce /* = false */)
4869 bool bHasGroups=false;
4870 int max =0;
4872 for(int i=0;i< this->m_arStatusArray.size(); i++)
4874 int ParentNo = m_arStatusArray[i]->m_ParentNo&PARENT_MASK;
4875 if( ParentNo > max)
4876 max=m_arStatusArray[i]->m_ParentNo&PARENT_MASK;
4879 if ( this->m_UnRevFileList.GetCount()>0 ||
4880 this->m_IgnoreFileList.GetCount()>0 ||
4881 max>0 || bForce)
4883 bHasGroups = true;
4886 RemoveAllGroups();
4887 EnableGroupView(bHasGroups);
4889 TCHAR groupname[1024];
4890 int groupindex = 0;
4892 if(bHasGroups)
4894 LVGROUP grp = {0};
4895 grp.cbSize = sizeof(LVGROUP);
4896 grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;
4897 groupindex=0;
4899 //if(m_UnRevFileList.GetCount()>0)
4900 if(max >0)
4902 _tcsncpy_s(groupname, 1024, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_GROUP_MERGEDFILES)), 1023);
4903 grp.pszHeader = groupname;
4904 grp.iGroupId = MERGE_MASK;
4905 grp.uAlign = LVGA_HEADER_LEFT;
4906 InsertGroup(0, &grp);
4908 for(int i=0;i<=max;i++)
4910 CString str;
4911 str.Format(IDS_STATUSLIST_GROUP_DIFFWITHPARENT, i+1);
4912 grp.pszHeader = str.GetBuffer();
4913 str.ReleaseBuffer();
4914 grp.iGroupId = i;
4915 grp.uAlign = LVGA_HEADER_LEFT;
4916 InsertGroup(i+1, &grp);
4919 else
4921 _tcsncpy_s(groupname, 1024, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_GROUP_MODIFIEDFILES)), 1023);
4922 grp.pszHeader = groupname;
4923 grp.iGroupId = groupindex;
4924 grp.uAlign = LVGA_HEADER_LEFT;
4925 InsertGroup(groupindex++, &grp);
4929 _tcsncpy_s(groupname, 1024, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_GROUP_NOTVERSIONEDFILES)), 1023);
4930 grp.pszHeader = groupname;
4931 grp.iGroupId = groupindex;
4932 grp.uAlign = LVGA_HEADER_LEFT;
4933 InsertGroup(groupindex++, &grp);
4936 //if(m_IgnoreFileList.GetCount()>0)
4938 _tcsncpy_s(groupname, 1024, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_GROUP_IGNOREDFILES)), 1023);
4939 grp.pszHeader = groupname;
4940 grp.iGroupId = groupindex;
4941 grp.uAlign = LVGA_HEADER_LEFT;
4942 InsertGroup(groupindex++, &grp);
4947 #if 0
4948 m_bHasIgnoreGroup = false;
4950 // now add the items which don't belong to a group
4951 LVGROUP grp = {0};
4952 grp.cbSize = sizeof(LVGROUP);
4953 grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;
4954 CString sUnassignedName(MAKEINTRESOURCE(IDS_STATUSLIST_UNASSIGNED_CHANGESET));
4955 _tcsncpy_s(groupname, 1024, (LPCTSTR)sUnassignedName, 1023);
4956 grp.pszHeader = groupname;
4957 grp.iGroupId = groupindex;
4958 grp.uAlign = LVGA_HEADER_LEFT;
4959 InsertGroup(groupindex++, &grp);
4961 for (std::map<CString,int>::iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)
4963 if (it->first.Compare(SVNSLC_IGNORECHANGELIST)!=0)
4965 LVGROUP grp = {0};
4966 grp.cbSize = sizeof(LVGROUP);
4967 grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;
4968 _tcsncpy_s(groupname, 1024, it->first, 1023);
4969 grp.pszHeader = groupname;
4970 grp.iGroupId = groupindex;
4971 grp.uAlign = LVGA_HEADER_LEFT;
4972 it->second = InsertGroup(groupindex++, &grp);
4974 else
4975 m_bHasIgnoreGroup = true;
4978 if (m_bHasIgnoreGroup)
4980 // and now add the group 'ignore-on-commit'
4981 std::map<CString,int>::iterator it = m_changelists.find(SVNSLC_IGNORECHANGELIST);
4982 if (it != m_changelists.end())
4984 grp.cbSize = sizeof(LVGROUP);
4985 grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;
4986 _tcsncpy_s(groupname, 1024, SVNSLC_IGNORECHANGELIST, 1023);
4987 grp.pszHeader = groupname;
4988 grp.iGroupId = groupindex;
4989 grp.uAlign = LVGA_HEADER_LEFT;
4990 it->second = InsertGroup(groupindex, &grp);
4993 #endif
4994 return bHasGroups;
4997 void CGitStatusListCtrl::NotifyCheck()
4999 CWnd* pParent = GetParent();
5000 if (NULL != pParent && NULL != pParent->GetSafeHwnd())
5002 pParent->SendMessage(GITSLNM_CHECKCHANGED, m_nSelected);
5006 int CGitStatusListCtrl::UpdateFileList(git_revnum_t hash,CTGitPathList *list)
5008 BYTE_VECTOR out;
5009 this->m_bBusy=TRUE;
5010 m_CurrentVersion=hash;
5012 int count = 0;
5013 if(list == NULL)
5014 count = 1;
5015 else
5016 count = list->GetCount();
5018 CString head = _T("HEAD");
5019 if(m_amend)
5020 head = _T("HEAD~1");
5022 if(hash == GIT_REV_ZERO)
5024 for(int i=0;i<count;i++)
5026 BYTE_VECTOR cmdout;
5027 cmdout.clear();
5028 CString cmd;
5029 if(!g_Git.IsInitRepos())
5031 // also list staged files which will be in the commit
5032 cmd=(_T("git.exe diff-index --cached --raw ") + head + _T(" --numstat -C -M -z"));
5033 g_Git.Run(cmd, &cmdout);
5035 if(list == NULL)
5036 cmd=(_T("git.exe diff-index --raw ") + head + _T(" --numstat -C -M -z"));
5037 else
5038 cmd.Format(_T("git.exe diff-index --raw ") + head + _T(" --numstat -C -M -z -- \"%s\""),(*list)[i].GetGitPathString());
5040 BYTE_VECTOR cmdErr;
5041 if(g_Git.Run(cmd, &cmdout, &cmdErr))
5043 int last = cmdErr.RevertFind(0,-1);
5044 CString str;
5045 g_Git.StringAppend(&str, &cmdErr[last + 1], CP_UTF8, cmdErr.size() - last -1);
5046 CMessageBox::Show(NULL, str, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
5049 out.append(cmdout, 0);
5051 else // Init Repository
5053 //We will list all added file for init repository because commit will comit these
5054 //if(list == NULL)
5055 cmd=_T("git.exe ls-files -s -t -z");
5056 //else
5057 // cmd.Format(_T("git.exe ls-files -s -t -z -- \"%s\""),(*list)[i].GetGitPathString());
5059 g_Git.Run(cmd, &cmdout);
5060 //out+=cmdout;
5061 out.append(cmdout,0);
5065 if(g_Git.IsInitRepos())
5067 m_StatusFileList.ParserFromLsFile(out);
5068 for(int i=0;i<m_StatusFileList.GetCount();i++)
5069 ((CTGitPath&)(m_StatusFileList[i])).m_Action=CTGitPath::LOGACTIONS_ADDED;
5071 else
5072 this->m_StatusFileList.ParserFromLog(out);
5074 //handle delete conflict case, when remote : modified, local : deleted.
5075 for(int i=0;i<count;i++)
5077 BYTE_VECTOR cmdout;
5078 CString cmd;
5080 if(list == NULL)
5081 cmd=_T("git.exe ls-files -u -t -z");
5082 else
5083 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),(*list)[i].GetGitPathString());
5085 g_Git.Run(cmd, &cmdout);
5087 CTGitPathList conflictlist;
5088 conflictlist.ParserFromLog(cmdout);
5089 for(int i=0;i<conflictlist.GetCount();i++)
5091 CTGitPath *p=m_StatusFileList.LookForGitPath(conflictlist[i].GetGitPathString());
5092 if(p)
5093 p->m_Action|=CTGitPath::LOGACTIONS_UNMERGED;
5094 else
5095 m_StatusFileList.AddPath(conflictlist[i]);
5099 // handle source files of file renames/moves (issue #860)
5100 // if a file gets renamed and the new file "git add"ed, diff-index doesn't list the source file anymore
5101 for(int i = 0; i < count; i++)
5103 BYTE_VECTOR cmdout;
5104 CString cmd;
5106 if(list == NULL)
5107 cmd = _T("git.exe ls-files -d -z");
5108 else
5109 cmd.Format(_T("git.exe ls-files -d -z -- \"%s\""),(*list)[i].GetGitPathString());
5111 g_Git.Run(cmd, &cmdout);
5113 CTGitPathList deletelist;
5114 deletelist.ParserFromLog(cmdout, true);
5115 for(int i = 0; i < deletelist.GetCount(); i++)
5117 CTGitPath *p = m_StatusFileList.LookForGitPath(deletelist[i].GetGitPathString());
5118 if(!p)
5119 m_StatusFileList.AddPath(deletelist[i]);
5120 else if ((p->m_Action == CTGitPath::LOGACTIONS_ADDED || p->m_Action == CTGitPath::LOGACTIONS_REPLACED) && !p->Exists())
5122 if (CMessageBox::Show(m_hWnd, _T("TortoiseGit detected that the file \"") + p->GetWinPathString() +_T("\" does not exist, but is staged as \"Added\".\nThe commit dialog cannot handle this.\n\nDo you want to remove it from the index?"), _T("TortoiseGit"), 1, IDI_EXCLAMATION, _T("&Remove file from index"), _T("&Ignore")) == 1)
5124 g_Git.Run(_T("git.exe rm -f --cache -- \"") + p->GetWinPathString() + _T("\""), &cmdout);
5125 m_StatusFileList.RemoveItem(*p);
5131 else
5133 int count = 0;
5134 if(list == NULL)
5135 count = 1;
5136 else
5137 count = list->GetCount();
5139 for(int i=0;i<count;i++)
5141 BYTE_VECTOR cmdout;
5142 CString cmd;
5143 if(list == NULL)
5144 cmd.Format(_T("git.exe diff-tree --raw --numstat -C -M -z %s"),hash);
5145 else
5146 cmd.Format(_T("git.exe diff-tree --raw --numstat -C -M %s -z -- \"%s\""),hash,(*list)[i].GetGitPathString());
5148 g_Git.Run(cmd, &cmdout, NULL);
5150 out.append(cmdout);
5152 this->m_StatusFileList.ParserFromLog(out);
5156 for(int i=0;i<m_StatusFileList.GetCount();i++)
5158 CTGitPath * gitpatch=(CTGitPath*)&m_StatusFileList[i];
5159 gitpatch->m_Checked = TRUE;
5160 m_arStatusArray.push_back((CTGitPath*)&m_StatusFileList[i]);
5163 this->m_bBusy=FALSE;
5164 return 0;
5167 int CGitStatusListCtrl::UpdateWithGitPathList(CTGitPathList &list)
5169 m_arStatusArray.clear();
5170 for(int i=0;i<list.GetCount();i++)
5172 CTGitPath * gitpath=(CTGitPath*)&list[i];
5174 if(gitpath ->m_Action & CTGitPath::LOGACTIONS_HIDE)
5175 continue;
5177 gitpath->m_Checked = TRUE;
5178 m_arStatusArray.push_back((CTGitPath*)&list[i]);
5180 return 0;
5183 int CGitStatusListCtrl::UpdateUnRevFileList(CTGitPathList *List)
5185 this->m_UnRevFileList.FillUnRev(CTGitPath::LOGACTIONS_UNVER,List);
5186 for(int i=0;i<m_UnRevFileList.GetCount();i++)
5188 CTGitPath * gitpatch=(CTGitPath*)&m_UnRevFileList[i];
5189 gitpatch->m_Checked = FALSE;
5190 m_arStatusArray.push_back((CTGitPath*)&m_UnRevFileList[i]);
5192 return 0;
5195 int CGitStatusListCtrl::UpdateIgnoreFileList(CTGitPathList *List)
5197 this->m_IgnoreFileList.FillUnRev(CTGitPath::LOGACTIONS_UNVER|CTGitPath::LOGACTIONS_IGNORE,List);
5198 for(int i=0;i<m_IgnoreFileList.GetCount();i++)
5200 CTGitPath * gitpatch=(CTGitPath*)&m_IgnoreFileList[i];
5201 gitpatch->m_Checked = FALSE;
5202 m_arStatusArray.push_back((CTGitPath*)&m_IgnoreFileList[i]);
5204 return 0;
5206 int CGitStatusListCtrl::UpdateFileList(int mask,bool once,CTGitPathList *List)
5208 if(mask&CGitStatusListCtrl::FILELIST_MODIFY)
5210 if(once || (!(m_FileLoaded&CGitStatusListCtrl::FILELIST_MODIFY)))
5212 UpdateFileList(git_revnum_t(GIT_REV_ZERO),List);
5213 m_FileLoaded|=CGitStatusListCtrl::FILELIST_MODIFY;
5216 if(mask&CGitStatusListCtrl::FILELIST_UNVER)
5218 if(once || (!(m_FileLoaded&CGitStatusListCtrl::FILELIST_UNVER)))
5220 UpdateUnRevFileList(List);
5221 m_FileLoaded|=CGitStatusListCtrl::FILELIST_UNVER;
5223 if(mask&CGitStatusListCtrl::FILELIST_IGNORE && (once || (!(m_FileLoaded&CGitStatusListCtrl::FILELIST_IGNORE))))
5225 UpdateIgnoreFileList(List);
5226 m_FileLoaded|=CGitStatusListCtrl::FILELIST_UNVER;
5229 return 0;
5232 void CGitStatusListCtrl::Clear()
5234 m_FileLoaded=0;
5235 this->DeleteAllItems();
5236 this->m_arListArray.clear();
5237 this->m_arStatusArray.clear();
5238 this->m_changelists.clear();
5240 //////////////////////////////////////////////////////////////////////////
5241 #if 0
5242 bool CGitStatusListCtrlDropTarget::OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium, DWORD * /*pdwEffect*/, POINTL pt)
5244 if(pFmtEtc->cfFormat == CF_HDROP && medium.tymed == TYMED_HGLOBAL)
5246 HDROP hDrop = (HDROP)GlobalLock(medium.hGlobal);
5247 if(hDrop != NULL)
5249 TCHAR szFileName[MAX_PATH];
5251 UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
5253 POINT clientpoint;
5254 clientpoint.x = pt.x;
5255 clientpoint.y = pt.y;
5256 ScreenToClient(m_hTargetWnd, &clientpoint);
5257 if ((m_pSVNStatusListCtrl->IsGroupViewEnabled())&&(m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint) >= 0))
5259 CTSVNPathList changelistItems;
5260 for(UINT i = 0; i < cFiles; ++i)
5262 DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));
5263 changelistItems.AddPath(CTSVNPath(szFileName));
5265 // find the changelist name
5266 CString sChangelist;
5267 LONG_PTR nGroup = m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint);
5268 for (std::map<CString, int>::iterator it = m_pSVNStatusListCtrl->m_changelists.begin(); it != m_pSVNStatusListCtrl->m_changelists.end(); ++it)
5269 if (it->second == nGroup)
5270 sChangelist = it->first;
5271 if (!sChangelist.IsEmpty())
5273 SVN git;
5274 if (git.AddToChangeList(changelistItems, sChangelist, git_depth_empty))
5276 for (int l=0; l<changelistItems.GetCount(); ++l)
5278 int index = m_pSVNStatusListCtrl->GetIndex(changelistItems[l]);
5279 if (index >= 0)
5281 CSVNStatusListCtrl::FileEntry * e = m_pSVNStatusListCtrl->GetListEntry(index);
5282 if (e)
5284 e->changelist = sChangelist;
5285 if (!e->IsFolder())
5287 if (m_pSVNStatusListCtrl->m_changelists.find(e->changelist)!=m_pSVNStatusListCtrl->m_changelists.end())
5288 m_pSVNStatusListCtrl->SetItemGroup(index, m_pSVNStatusListCtrl->m_changelists[e->changelist]);
5289 else
5290 m_pSVNStatusListCtrl->SetItemGroup(index, 0);
5294 else
5296 HWND hParentWnd = GetParent(m_hTargetWnd);
5297 if (hParentWnd != NULL)
5298 ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)changelistItems[l].GetWinPath());
5302 else
5304 CMessageBox::Show(m_pSVNStatusListCtrl->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
5307 else
5309 SVN git;
5310 if (git.RemoveFromChangeList(changelistItems, CStringArray(), git_depth_empty))
5312 for (int l=0; l<changelistItems.GetCount(); ++l)
5314 int index = m_pSVNStatusListCtrl->GetIndex(changelistItems[l]);
5315 if (index >= 0)
5317 CSVNStatusListCtrl::FileEntry * e = m_pSVNStatusListCtrl->GetListEntry(index);
5318 if (e)
5320 e->changelist = sChangelist;
5321 m_pSVNStatusListCtrl->SetItemGroup(index, 0);
5324 else
5326 HWND hParentWnd = GetParent(m_hTargetWnd);
5327 if (hParentWnd != NULL)
5328 ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)changelistItems[l].GetWinPath());
5332 else
5334 CMessageBox::Show(m_pSVNStatusListCtrl->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
5338 else
5340 for(UINT i = 0; i < cFiles; ++i)
5342 DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));
5343 HWND hParentWnd = GetParent(m_hTargetWnd);
5344 if (hParentWnd != NULL)
5345 ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)szFileName);
5349 GlobalUnlock(medium.hGlobal);
5351 return true; //let base free the medium
5353 HRESULT STDMETHODCALLTYPE CSVNStatusListCtrlDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect)
5355 CIDropTarget::DragOver(grfKeyState, pt, pdwEffect);
5356 *pdwEffect = DROPEFFECT_COPY;
5357 if (m_pSVNStatusListCtrl)
5359 POINT clientpoint;
5360 clientpoint.x = pt.x;
5361 clientpoint.y = pt.y;
5362 ScreenToClient(m_hTargetWnd, &clientpoint);
5363 if ((m_pSVNStatusListCtrl->IsGroupViewEnabled())&&(m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint) >= 0))
5365 *pdwEffect = DROPEFFECT_MOVE;
5367 else if ((!m_pSVNStatusListCtrl->m_bFileDropsEnabled)||(m_pSVNStatusListCtrl->m_bOwnDrag))
5369 *pdwEffect = DROPEFFECT_NONE;
5372 return S_OK;
5375 #endif
5377 void CGitStatusListCtrl::FileSaveAs(CTGitPath *path)
5379 CString filename;
5380 filename.Format(_T("%s-%s%s"), path->GetBaseFilename(), this->m_CurrentVersion.Left(g_Git.GetShortHASHLength()), path->GetFileExtension());
5381 CFileDialog dlg(FALSE,NULL,
5382 filename,
5383 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
5384 NULL);
5385 CString currentpath;
5386 currentpath=g_Git.m_CurrentDir+_T("\\");
5387 currentpath+=path->GetWinPathString();
5389 dlg.m_ofn.lpstrInitialDir=currentpath.GetBuffer();
5391 CString cmd,out;
5392 if(dlg.DoModal()==IDOK)
5394 filename = dlg.GetPathName();
5395 if(m_CurrentVersion == GIT_REV_ZERO)
5397 if(!CopyFile(g_Git.m_CurrentDir +_T("\\") + path->GetWinPath(),filename,false))
5399 MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
5400 return;
5404 else
5406 if(g_Git.GetOneFile(m_CurrentVersion,*path,filename))
5408 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, path->GetGitPathString(), m_CurrentVersion, filename);
5409 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
5410 return;
5417 int CGitStatusListCtrl::RevertSelectedItemToVersion()
5419 if(this->m_CurrentVersion.IsEmpty())
5420 return 0;
5421 if(this->m_CurrentVersion == GIT_REV_ZERO)
5422 return 0;
5424 POSITION pos = GetFirstSelectedItemPosition();
5425 int index;
5426 CString cmd,out;
5427 int count =0;
5428 while ((index = GetNextSelectedItem(pos)) >= 0)
5430 CTGitPath *fentry=(CTGitPath*)GetItemData(index);
5431 cmd.Format(_T("git.exe checkout %s -- \"%s\""),m_CurrentVersion,fentry->GetGitPathString());
5432 out.Empty();
5433 if (g_Git.Run(cmd, &out, CP_UTF8))
5435 if (MessageBox(out, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
5436 break;
5438 else
5439 count++;
5442 out.Format(IDS_STATUSLIST_FILESREVERTED, count, m_CurrentVersion);
5443 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
5444 return 0;
5447 void CGitStatusListCtrl::OpenFile(CTGitPath*filepath,int mode)
5449 CString file;
5450 if(this->m_CurrentVersion.IsEmpty() || m_CurrentVersion == GIT_REV_ZERO)
5452 file= filepath->GetWinPath();
5454 else
5456 CString temppath;
5457 GetTempPath(temppath);
5458 file.Format(_T("%s%s_%s%s"),
5459 temppath,
5460 filepath->GetBaseFilename(),
5461 m_CurrentVersion.Left(g_Git.GetShortHASHLength()),
5462 filepath->GetFileExtension());
5463 CString cmd,out;
5464 if(g_Git.GetOneFile(m_CurrentVersion, *filepath, file))
5466 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, filepath->GetGitPathString(), m_CurrentVersion, file);
5467 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
5468 return;
5472 if(mode == ALTERNATIVEEDITOR)
5474 CAppUtils::LaunchAlternativeEditor(file);
5475 return;
5477 int ret = HINSTANCE_ERROR;
5479 if(mode == OPEN )
5481 ret = (int)ShellExecute(this->m_hWnd, NULL,file, NULL, NULL, SW_SHOW);
5483 if (ret > HINSTANCE_ERROR)
5485 return;
5490 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
5491 cmd += file;
5492 CAppUtils::LaunchApplication(cmd, NULL, false);