OSDN Git Service

9f7507476226959519c5356804bc3b776062da4a
[tortoisegit/TortoiseGitJp.git] / Git / GitStatusListCtrl.cpp
1 \r
2 \r
3 // TortoiseSVN - a Windows shell extension for easy version control\r
4 \r
5 // Copyright (C) 2003-2008 - TortoiseSVN\r
6 \r
7 // This program is free software; you can redistribute it and/or\r
8 // modify it under the terms of the GNU General Public License\r
9 // as published by the Free Software Foundation; either version 2\r
10 // of the License, or (at your option) any later version.\r
11 \r
12 // This program is distributed in the hope that it will be useful,\r
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 // GNU General Public License for more details.\r
16 \r
17 // You should have received a copy of the GNU General Public License\r
18 // along with this program; if not, write to the Free Software Foundation,\r
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
20 //\r
21 \r
22 #include "StdAfx.h"\r
23 #include "GitStatusListCtrl.h"\r
24 #include "resource.h"\r
25 #include "MessageBox.h"\r
26 #include "MyMemDC.h"\r
27 #include "UnicodeUtils.h"\r
28 #include "AppUtils.h"\r
29 #include "PathUtils.h"\r
30 #include "TempFile.h"\r
31 #include "StringUtils.h"\r
32 #include "DirFileEnum.h"\r
33 #include "GitConfig.h"\r
34 //#include "SVNProperties.h"\r
35 #include "Git.h"\r
36 //#include "SVNDiff.h"\r
37 //#include "LogDlg.h"\r
38 //#include "SVNProgressDlg.h"\r
39 #include "SysImageList.h"\r
40 //#include "Svnstatuslistctrl.h"\r
41 #include "TGitPath.h"\r
42 #include "Registry.h"\r
43 #include "GitStatus.h"\r
44 //#include "SVNHelpers.h"\r
45 #include "InputDlg.h"\r
46 #include "ShellUpdater.h"\r
47 #include "GitAdminDir.h"\r
48 //#include "DropFiles.h"\r
49 #include "IconMenu.h"\r
50 //#include "AddDlg.h"\r
51 //#include "EditPropertiesDlg.h"\r
52 //#include "CreateChangelistDlg.h"\r
53 #include "XPTheme.h"\r
54 \r
55 const UINT CGitStatusListCtrl::SVNSLNM_ITEMCOUNTCHANGED\r
56                                         = ::RegisterWindowMessage(_T("GITSLNM_ITEMCOUNTCHANGED"));\r
57 const UINT CGitStatusListCtrl::SVNSLNM_NEEDSREFRESH\r
58                                         = ::RegisterWindowMessage(_T("GITSLNM_NEEDSREFRESH"));\r
59 const UINT CGitStatusListCtrl::SVNSLNM_ADDFILE\r
60                                         = ::RegisterWindowMessage(_T("GITSLNM_ADDFILE"));\r
61 const UINT CGitStatusListCtrl::SVNSLNM_CHECKCHANGED\r
62                                         = ::RegisterWindowMessage(_T("GITSLNM_CHECKCHANGED"));\r
63 \r
64 #define IDSVNLC_REVERT                   1\r
65 #define IDSVNLC_COMPARE                  2\r
66 #define IDSVNLC_OPEN                     3\r
67 #define IDSVNLC_DELETE                   4\r
68 #define IDSVNLC_IGNORE                   5\r
69 #define IDSVNLC_GNUDIFF1                 6\r
70 #define IDSVNLC_UPDATE           7\r
71 #define IDSVNLC_LOG              8\r
72 #define IDSVNLC_EDITCONFLICT     9\r
73 #define IDSVNLC_IGNOREMASK          10\r
74 #define IDSVNLC_ADD                         11\r
75 #define IDSVNLC_RESOLVECONFLICT 12\r
76 #define IDSVNLC_LOCK                    13\r
77 #define IDSVNLC_LOCKFORCE               14\r
78 #define IDSVNLC_UNLOCK                  15\r
79 #define IDSVNLC_UNLOCKFORCE             16\r
80 #define IDSVNLC_OPENWITH                17\r
81 #define IDSVNLC_EXPLORE                 18\r
82 #define IDSVNLC_RESOLVETHEIRS   19\r
83 #define IDSVNLC_RESOLVEMINE             20\r
84 #define IDSVNLC_REMOVE                  21\r
85 #define IDSVNLC_COMMIT                  22\r
86 #define IDSVNLC_PROPERTIES              23\r
87 #define IDSVNLC_COPY                    24\r
88 #define IDSVNLC_COPYEXT                 25\r
89 #define IDSVNLC_REPAIRMOVE              26\r
90 #define IDSVNLC_REMOVEFROMCS    27\r
91 #define IDSVNLC_CREATECS                28\r
92 #define IDSVNLC_CREATEIGNORECS  29\r
93 #define IDSVNLC_CHECKGROUP              30\r
94 #define IDSVNLC_UNCHECKGROUP    31\r
95 #define IDSVNLC_ADD_RECURSIVE   32\r
96 #define IDSVNLC_COMPAREWC               33\r
97 #define IDSVNLC_BLAME                   34\r
98 // the IDSVNLC_MOVETOCS *must* be the last index, because it contains a dynamic submenu where \r
99 // the submenu items get command ID's sequent to this number\r
100 #define IDSVNLC_MOVETOCS                35\r
101 \r
102 \r
103 BEGIN_MESSAGE_MAP(CGitStatusListCtrl, CListCtrl)\r
104         ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHdnItemclick)\r
105         ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHdnItemclick)\r
106         ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)\r
107         ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)\r
108         ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnLvnItemchanged)\r
109         ON_WM_CONTEXTMENU()\r
110         ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclk)\r
111         ON_NOTIFY_REFLECT(LVN_GETINFOTIP, OnLvnGetInfoTip)\r
112         ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)\r
113         ON_WM_SETCURSOR()\r
114         ON_WM_GETDLGCODE()\r
115         ON_NOTIFY_REFLECT(NM_RETURN, OnNMReturn)\r
116         ON_WM_KEYDOWN()\r
117         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)\r
118         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)\r
119         ON_WM_PAINT()\r
120         ON_NOTIFY(HDN_BEGINTRACKA, 0, &CGitStatusListCtrl::OnHdnBegintrack)\r
121         ON_NOTIFY(HDN_BEGINTRACKW, 0, &CGitStatusListCtrl::OnHdnBegintrack)\r
122         ON_NOTIFY(HDN_ITEMCHANGINGA, 0, &CGitStatusListCtrl::OnHdnItemchanging)\r
123         ON_NOTIFY(HDN_ITEMCHANGINGW, 0, &CGitStatusListCtrl::OnHdnItemchanging)\r
124         ON_WM_DESTROY()\r
125         ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)\r
126         ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CGitStatusListCtrl::OnLvnItemchanging)\r
127 END_MESSAGE_MAP()\r
128 \r
129 \r
130 \r
131 CGitStatusListCtrl::CGitStatusListCtrl() : CListCtrl()\r
132         //, m_HeadRev(GitRev::REV_HEAD)\r
133         , m_pbCanceled(NULL)\r
134         , m_pStatLabel(NULL)\r
135         , m_pSelectButton(NULL)\r
136         , m_pConfirmButton(NULL)\r
137         , m_bBusy(false)\r
138         , m_bEmpty(false)\r
139         , m_bUnversionedRecurse(true)\r
140         , m_bShowIgnores(false)\r
141         , m_pDropTarget(NULL)\r
142         , m_bIgnoreRemoveOnly(false)\r
143         , m_bCheckChildrenWithParent(false)\r
144         , m_bUnversionedLast(true)\r
145         , m_bHasChangeLists(false)\r
146         , m_bHasLocks(false)\r
147         , m_bBlock(false)\r
148         , m_bBlockUI(false)\r
149         , m_bHasCheckboxes(false)\r
150         , m_bCheckIfGroupsExist(true)\r
151         , m_bFileDropsEnabled(false)\r
152         , m_bOwnDrag(false)\r
153     , m_dwDefaultColumns(0)\r
154     , m_ColumnManager(this)\r
155     , m_bAscending(false)\r
156     , m_nSortedColumn(-1)\r
157         , m_sNoPropValueText(MAKEINTRESOURCE(IDS_STATUSLIST_NOPROPVALUE))\r
158 {\r
159         m_critSec.Init();\r
160 }\r
161 \r
162 CGitStatusListCtrl::~CGitStatusListCtrl()\r
163 {\r
164         if (m_pDropTarget)\r
165                 delete m_pDropTarget;\r
166         ClearStatusArray();\r
167 }\r
168 \r
169 void CGitStatusListCtrl::ClearStatusArray()\r
170 {\r
171         Locker lock(m_critSec);\r
172         for (size_t i=0; i < m_arStatusArray.size(); i++)\r
173         {\r
174                 delete m_arStatusArray[i];\r
175         }\r
176         m_arStatusArray.clear();\r
177 }\r
178 \r
179 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(UINT_PTR index)\r
180 {\r
181         if (index >= (UINT_PTR)m_arListArray.size())\r
182                 return NULL;\r
183         if (m_arListArray[index] >= m_arStatusArray.size())\r
184                 return NULL;\r
185         return m_arStatusArray[m_arListArray[index]];\r
186 }\r
187 \r
188 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetVisibleListEntry(const CTGitPath& path)\r
189 {\r
190         int itemCount = GetItemCount();\r
191         for (int i=0; i < itemCount; i++)\r
192         {\r
193                 FileEntry * entry = GetListEntry(i);\r
194                 if (entry->GetPath().IsEquivalentTo(path))\r
195                         return entry;\r
196         }\r
197         return NULL;\r
198 }\r
199 \r
200 CGitStatusListCtrl::FileEntry * CGitStatusListCtrl::GetListEntry(const CTGitPath& path)\r
201 {\r
202         for (size_t i=0; i < m_arStatusArray.size(); i++)\r
203         {\r
204                 FileEntry * entry = m_arStatusArray[i];\r
205                 if (entry->GetPath().IsEquivalentTo(path))\r
206                         return entry;\r
207         }\r
208         return NULL;\r
209 }\r
210 \r
211 int CGitStatusListCtrl::GetIndex(const CTGitPath& path)\r
212 {\r
213         int itemCount = GetItemCount();\r
214         for (int i=0; i < itemCount; i++)\r
215         {\r
216                 FileEntry * entry = GetListEntry(i);\r
217                 if (entry->GetPath().IsEquivalentTo(path))\r
218                         return i;\r
219         }\r
220         return -1;\r
221 }\r
222 \r
223 void CGitStatusListCtrl::Init(DWORD dwColumns, const CString& sColumnInfoContainer, DWORD dwContextMenus /* = GitSLC_POPALL */, bool bHasCheckboxes /* = true */)\r
224 {\r
225         Locker lock(m_critSec);\r
226 \r
227     m_dwDefaultColumns = dwColumns | 1;\r
228     m_dwContextMenus = dwContextMenus;\r
229         m_bHasCheckboxes = bHasCheckboxes;\r
230 \r
231     // set the extended style of the listcontrol\r
232         // the style LVS_EX_FULLROWSELECT interferes with the background watermark image but it's more important to be able to select in the whole row.\r
233         CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);\r
234         DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;\r
235         if (DWORD(regFullRowSelect))\r
236                 exStyle |= LVS_EX_FULLROWSELECT;\r
237         exStyle |= (bHasCheckboxes ? LVS_EX_CHECKBOXES : 0);\r
238         SetRedraw(false);\r
239         SetExtendedStyle(exStyle);\r
240 \r
241         CXPTheme theme;\r
242         theme.SetWindowTheme(m_hWnd, L"Explorer", NULL);\r
243 \r
244         m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();\r
245         SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);\r
246 \r
247     m_ColumnManager.ReadSettings (m_dwDefaultColumns, sColumnInfoContainer);\r
248 \r
249         // enable file drops\r
250 #if 0\r
251         if (m_pDropTarget == NULL)\r
252         {\r
253                 m_pDropTarget = new CGitStatusListCtrlDropTarget(this);\r
254                 RegisterDragDrop(m_hWnd,m_pDropTarget);\r
255                 // create the supported formats:\r
256                 FORMATETC ftetc={0};\r
257                 ftetc.dwAspect = DVASPECT_CONTENT;\r
258                 ftetc.lindex = -1;\r
259                 ftetc.tymed = TYMED_HGLOBAL;\r
260                 ftetc.cfFormat=CF_HDROP;\r
261                 m_pDropTarget->AddSuportedFormat(ftetc);\r
262         }\r
263 #endif\r
264 \r
265         SetRedraw(true);\r
266 \r
267         m_bUnversionedRecurse = !!((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\UnversionedRecurse"), TRUE));\r
268 }\r
269 \r
270 bool CGitStatusListCtrl::SetBackgroundImage(UINT nID)\r
271 {\r
272         return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);\r
273 }\r
274 \r
275 BOOL CGitStatusListCtrl::GetStatus ( const CTGitPathList& pathList\r
276                                    , bool bUpdate /* = FALSE */\r
277                                    , bool bShowIgnores /* = false */\r
278                                    , bool bShowUserProps /* = false */)\r
279 {\r
280         Locker lock(m_critSec);\r
281         int refetchcounter = 0;\r
282         BOOL bRet = TRUE;\r
283         Invalidate();\r
284         // force the cursor to change\r
285         POINT pt;\r
286         GetCursorPos(&pt);\r
287         SetCursorPos(pt.x, pt.y);\r
288 \r
289         m_mapFilenameToChecked.clear();\r
290         m_StatusUrlList.Clear();\r
291         bool bHasChangelists = (m_changelists.size()>1 || (m_changelists.size()>0 && !m_bHasIgnoreGroup));\r
292         m_changelists.clear();\r
293         for (size_t i=0; i < m_arStatusArray.size(); i++)\r
294         {\r
295                 FileEntry * entry = m_arStatusArray[i];\r
296                 if ( bHasChangelists && entry->checked)\r
297                 {\r
298                         // If change lists are present, remember all checked entries\r
299                         CString path = entry->GetPath().GetGitPathString();\r
300                         m_mapFilenameToChecked[path] = true;\r
301                 }\r
302                 if ( (entry->status==git_wc_status_unversioned || entry->status==git_wc_status_missing ) && entry->checked )\r
303                 {\r
304                         // The user manually selected an unversioned or missing file. We remember\r
305                         // this so that the selection can be restored when refreshing.\r
306                         CString path = entry->GetPath().GetGitPathString();\r
307                         m_mapFilenameToChecked[path] = true;\r
308                 }\r
309                 else if ( entry->status > git_wc_status_normal && !entry->checked )\r
310                 {\r
311                         // The user manually deselected a versioned file. We remember\r
312                         // this so that the deselection can be restored when refreshing.\r
313                         CString path = entry->GetPath().GetGitPathString();\r
314                         m_mapFilenameToChecked[path] = false;\r
315                 }\r
316         }\r
317 \r
318         // use a sorted path list to make sure we fetch the status of\r
319         // parent items first, *then* the child items (if any)\r
320         CTGitPathList sortedPathList = pathList;\r
321         sortedPathList.SortByPathname();\r
322         do\r
323         {\r
324                 bRet = TRUE;\r
325                 m_nTargetCount = 0;\r
326                 m_bHasExternalsFromDifferentRepos = FALSE;\r
327                 m_bHasExternals = FALSE;\r
328                 m_bHasUnversionedItems = FALSE;\r
329                 m_bHasLocks = false;\r
330                 m_bHasChangeLists = false;\r
331                 m_bShowIgnores = bShowIgnores;\r
332                 m_nSortedColumn = 0;\r
333                 m_bBlock = TRUE;\r
334                 m_bBusy = true;\r
335                 m_bEmpty = false;\r
336                 Invalidate();\r
337 \r
338                 // first clear possible status data left from\r
339                 // previous GetStatus() calls\r
340                 ClearStatusArray();\r
341 \r
342                 m_StatusFileList = sortedPathList;\r
343 \r
344                 // Since Git_client_status() returns all files, even those in\r
345                 // folders included with Git:externals we need to check if all\r
346                 // files we get here belongs to the same repository.\r
347                 // It is possible to commit changes in an external folder, as long\r
348                 // as the folder belongs to the same repository (but another path),\r
349                 // but it is not possible to commit all files if the externals are\r
350                 // from a different repository.\r
351                 //\r
352                 // To check if all files belong to the same repository, we compare the\r
353                 // UUID's - if they're identical then the files belong to the same\r
354                 // repository and can be committed. But if they're different, then\r
355                 // tell the user to commit all changes in the external folders\r
356                 // first and exit.\r
357                 CStringA sUUID;                                 // holds the repo UUID\r
358                 CTGitPathList arExtPaths;               // list of Git:external paths\r
359 \r
360                 GitConfig config;\r
361 \r
362                 m_sURL.Empty();\r
363 \r
364                 m_nTargetCount = sortedPathList.GetCount();\r
365 \r
366                 GitStatus status(m_pbCanceled);\r
367                 if(m_nTargetCount > 1 && sortedPathList.AreAllPathsFilesInOneDirectory())\r
368                 {\r
369                         // This is a special case, where we've been given a list of files\r
370                         // all from one folder\r
371                         // We handle them by setting a status filter, then requesting the Git status of\r
372                         // all the files in the directory, filtering for the ones we're interested in\r
373                         status.SetFilter(sortedPathList);\r
374 \r
375                         // if all selected entries are files, we don't do a recursive status\r
376                         // fetching. But if only one is a directory, we have to recurse.\r
377                         git_depth_t depth = git_depth_files;\r
378                         // We have set a filter. If all selected items were files or we fetch\r
379                         // the status not recursively, then we have to include\r
380                         // ignored items too - the user has selected them\r
381                         bool bShowIgnoresRight = true;\r
382                         for (int fcindex=0; fcindex<sortedPathList.GetCount(); ++fcindex)\r
383                         {\r
384                                 if (sortedPathList[fcindex].IsDirectory())\r
385                                 {\r
386                                         depth = git_depth_infinity;\r
387                                         bShowIgnoresRight = false;\r
388                                         break;\r
389                                 }\r
390                         }\r
391                         if(!FetchStatusForSingleTarget(config, status, sortedPathList.GetCommonDirectory(), bUpdate, sUUID, arExtPaths, true, depth, bShowIgnoresRight))\r
392                         {\r
393                                 bRet = FALSE;\r
394                         }\r
395                 }\r
396                 else\r
397                 {\r
398                         for(int nTarget = 0; nTarget < m_nTargetCount; nTarget++)\r
399                         {\r
400                                 // check whether the path we want the status for is already fetched due to status-fetching\r
401                                 // of a parent path.\r
402                                 // this check is only done for file paths, because folder paths could be included already\r
403                                 // but not recursively\r
404                                 if (sortedPathList[nTarget].IsDirectory() || GetListEntry(sortedPathList[nTarget]) == NULL)\r
405                                 {\r
406                                         if(!FetchStatusForSingleTarget(config, status, sortedPathList[nTarget], bUpdate, sUUID, \r
407                                                 arExtPaths, false, git_depth_infinity, bShowIgnores))\r
408                                         {\r
409                                                 bRet = FALSE;\r
410                                         }\r
411                                 }\r
412                         }\r
413                 }\r
414 \r
415                 // remove the 'helper' files of conflicted items from the list.\r
416                 // otherwise they would appear as unversioned files.\r
417                 for (INT_PTR cind = 0; cind < m_ConflictFileList.GetCount(); ++cind)\r
418                 {\r
419                         for (size_t i=0; i < m_arStatusArray.size(); i++)\r
420                         {\r
421                                 if (m_arStatusArray[i]->GetPath().IsEquivalentTo(m_ConflictFileList[cind]))\r
422                                 {\r
423                                         delete m_arStatusArray[i];\r
424                                         m_arStatusArray.erase(m_arStatusArray.begin()+i);\r
425                                         break;\r
426                                 }\r
427                         }\r
428                 }\r
429                 refetchcounter++;\r
430         } while(!BuildStatistics() && (refetchcounter < 2) && (*m_pbCanceled==false));\r
431 \r
432     if (bShowUserProps)\r
433         FetchUserProperties();\r
434 \r
435     m_ColumnManager.UpdateUserPropList (m_arStatusArray);\r
436 \r
437         m_bBlock = FALSE;\r
438         m_bBusy = false;\r
439         GetCursorPos(&pt);\r
440         SetCursorPos(pt.x, pt.y);\r
441         return bRet;\r
442 }\r
443 \r
444 //\r
445 // Fetch all local properties for all elements in the status array\r
446 //\r
447 void CGitStatusListCtrl::FetchUserProperties()\r
448 {\r
449 #if 0\r
450         GitPool globalPool;\r
451 \r
452     for (size_t i = 0, count = m_arStatusArray.size(); i < count; ++i)\r
453     {\r
454         // local / temp pool to hold parameters and props for a single item\r
455 \r
456         GitPool localPool ((apr_pool_t*)globalPool);\r
457 \r
458         // open working copy for this path\r
459 \r
460         const char* path = m_arStatusArray[i]->path.GetGitApiPath (localPool);\r
461          \r
462             Git_wc_adm_access_t *adm_access = NULL;          \r
463         Git_error_t * error = Git_wc_adm_probe_open3 ( &adm_access\r
464                                                      , NULL\r
465                                                      , path\r
466                                                      , FALSE    // no write lock\r
467                                                                                                          , 0            // lock just the directory/file itself\r
468                                                      , NULL\r
469                                                                                                          , NULL\r
470                                                      , localPool);\r
471         if (error == NULL)\r
472         {\r
473             // get the props and add them to the status info\r
474 \r
475             apr_hash_t* props = NULL;\r
476             Git_error_t * error = Git_wc_prop_list ( &props\r
477                                                    , path\r
478                                                    , adm_access\r
479                                                    , localPool);\r
480             if (error == NULL)\r
481             {\r
482                 for ( apr_hash_index_t *index \r
483                         = apr_hash_first (localPool, props)\r
484                     ; index != NULL\r
485                     ; index = apr_hash_next (index))\r
486                 {\r
487                     // extract next entry from hash\r
488 \r
489                     const char* key = NULL;\r
490                     ptrdiff_t keyLen;\r
491                     const char** val = NULL;\r
492 \r
493                     apr_hash_this ( index\r
494                                   , reinterpret_cast<const void**>(&key)\r
495                                   , &keyLen\r
496                                   , reinterpret_cast<void**>(&val));\r
497 \r
498                     // decode / dispatch it\r
499 \r
500                         CString name = CUnicodeUtils::GetUnicode (key);\r
501                         CString value = CUnicodeUtils::GetUnicode (*val);\r
502 \r
503                     // store in property container (truncate it after ~100 chars)\r
504 \r
505                     m_arStatusArray[i]->present_props[name] \r
506                         = value.Left (GitSLC_MAXUSERPROPLENGTH);\r
507                 }\r
508             }\r
509             error = Git_wc_adm_close2 (adm_access, localPool);\r
510         }\r
511         Git_error_clear (error);\r
512     }\r
513 #endif\r
514 }\r
515 \r
516 \r
517 //\r
518 // Work on a single item from the list of paths which is provided to us\r
519 //\r
520 bool CGitStatusListCtrl::FetchStatusForSingleTarget(\r
521                                                         GitConfig& config,\r
522                                                         GitStatus& status,\r
523                                                         const CTGitPath& target,\r
524                                                         bool bFetchStatusFromRepository,\r
525                                                         CStringA& strCurrentRepositoryUUID,\r
526                                                         CTGitPathList& arExtPaths,\r
527                                                         bool bAllDirect,\r
528                                                         git_depth_t depth,\r
529                                                         bool bShowIgnores\r
530                                                         )\r
531 {\r
532 #if 0\r
533         config.GetDefaultIgnores();\r
534 \r
535         CTGitPath workingTarget(target);\r
536 \r
537         git_wc_status2_t * s;\r
538         CTGitPath GitPath;\r
539         s = status.GetFirstFileStatus(workingTarget, GitPath, bFetchStatusFromRepository, depth, bShowIgnores);\r
540 \r
541         m_HeadRev = SVNRev(status.headrev);\r
542         if (s!=0)\r
543         {\r
544                 Git_wc_status_kind wcFileStatus = GitStatus::GetMoreImportant(s->text_status, s->prop_status);\r
545 \r
546                 // This one fixes a problem with externals:\r
547                 // If a strLine is a file, Git:externals and its parent directory\r
548                 // will also be returned by GetXXXFileStatus. Hence, we skip all\r
549                 // status info until we find the one matching workingTarget.\r
550                 if (!workingTarget.IsDirectory())\r
551                 {\r
552                         if (!workingTarget.IsEquivalentTo(GitPath))\r
553                         {\r
554                                 while (s != 0)\r
555                                 {\r
556                                         s = status.GetNextFileStatus(GitPath);\r
557                                         if(workingTarget.IsEquivalentTo(GitPath))\r
558                                         {\r
559                                                 break;\r
560                                         }\r
561                                 }\r
562                                 if (s == 0)\r
563                                 {\r
564                                         m_sLastError = status.GetLastErrorMsg();\r
565                                         return false;\r
566                                 }\r
567                                 // Now, set working target to be the base folder of this item\r
568                                 workingTarget = workingTarget.GetDirectory();\r
569                         }\r
570                 }\r
571                 bool bEntryFromDifferentRepo = false;\r
572                 // Is this a versioned item with an associated repos UUID?\r
573                 if ((s->entry)&&(s->entry->uuid))\r
574                 {\r
575                         // Have we seen a repos UUID yet?\r
576                         if (strCurrentRepositoryUUID.IsEmpty())\r
577                         {\r
578                                 // This is the first repos UUID we've seen - record it\r
579                                 strCurrentRepositoryUUID = s->entry->uuid;\r
580                                 m_sUUID = strCurrentRepositoryUUID;\r
581                         }\r
582                         else\r
583                         {\r
584                                 if (strCurrentRepositoryUUID.Compare(s->entry->uuid)!=0)\r
585                                 {\r
586                                         // This item comes from a different repository than our main one\r
587                                         m_bHasExternalsFromDifferentRepos = TRUE;\r
588                                         bEntryFromDifferentRepo = true;\r
589                                         if (s->entry->kind == Git_node_dir)\r
590                                                 arExtPaths.AddPath(workingTarget);\r
591                                 }\r
592                         }\r
593                 }\r
594                 else if (strCurrentRepositoryUUID.IsEmpty() && (s->text_status == Git_wc_status_added))\r
595                 {\r
596                         // An added entry doesn't have an UUID assigned to it yet.\r
597                         // So we fetch the status of the parent directory instead and\r
598                         // check if that one has an UUID assigned to it.\r
599                         Git_wc_status2_t * sparent;\r
600                         CTGitPath path = workingTarget;\r
601                         do\r
602                         {\r
603                                 CTGitPath GitParentPath;\r
604                                 GitStatus tempstatus;\r
605                                 sparent = tempstatus.GetFirstFileStatus(path.GetContainingDirectory(), GitParentPath, false, Git_depth_empty, false);\r
606                                 path = GitParentPath;\r
607                         } while ( (sparent) && (sparent->entry) && (!sparent->entry->uuid) && (sparent->text_status==Git_wc_status_added) );\r
608                         if (sparent && sparent->entry && sparent->entry->uuid)\r
609                         {\r
610                                 strCurrentRepositoryUUID = sparent->entry->uuid;\r
611                                 m_sUUID = strCurrentRepositoryUUID;\r
612                         }\r
613                 }\r
614 \r
615                 if ((wcFileStatus == Git_wc_status_unversioned)&& GitPath.IsDirectory())\r
616                 {\r
617                         // check if the unversioned folder is maybe versioned. This\r
618                         // could happen with nested layouts\r
619                         Git_wc_status_kind st = GitStatus::GetAllStatus(workingTarget);\r
620                         if ((st != Git_wc_status_unversioned)&&(st != Git_wc_status_none))\r
621                         {\r
622                                 return true;    // ignore nested layouts\r
623                         }\r
624                 }\r
625                 if (status.IsExternal(GitPath))\r
626                 {\r
627                         m_bHasExternals = TRUE;\r
628                 }\r
629 \r
630                 AddNewFileEntry(s, GitPath, workingTarget, true, m_bHasExternals, bEntryFromDifferentRepo);\r
631 \r
632                 if (((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none)||((wcFileStatus == Git_wc_status_ignored)&&(m_bShowIgnores))) && GitPath.IsDirectory())\r
633                 {\r
634                         // we have an unversioned folder -> get all files in it recursively!\r
635                         AddUnversionedFolder(GitPath, workingTarget.GetContainingDirectory(), &config);\r
636                 }\r
637 \r
638                 // for folders, get all statuses inside it too\r
639                 if(workingTarget.IsDirectory())\r
640                 {\r
641                         ReadRemainingItemsStatus(status, workingTarget, strCurrentRepositoryUUID, arExtPaths, &config, bAllDirect);\r
642                 }\r
643 \r
644         } // if (s!=0)\r
645         else\r
646         {\r
647                 m_sLastError = status.GetLastErrorMsg();\r
648                 return false;\r
649         }\r
650 #endif\r
651         return true;\r
652 }\r
653 \r
654 const CGitStatusListCtrl::FileEntry*\r
655 CGitStatusListCtrl::AddNewFileEntry(\r
656                         const git_wc_status2_t* pGitStatus,  // The return from the Git GetStatus functions\r
657                         const CTGitPath& path,                          // The path of the item we're adding\r
658                         const CTGitPath& basePath,                      // The base directory for this status build\r
659                         bool bDirectItem,                                       // Was this item the first found by GetFirstFileStatus or by a subsequent GetNextFileStatus call\r
660                         bool bInExternal,                                       // Are we in an 'external' folder\r
661                         bool bEntryfromDifferentRepo            // if the entry is from a different repository\r
662                         )\r
663 {\r
664         FileEntry * entry = new FileEntry();\r
665 #if 0\r
666         \r
667         entry->path = path;\r
668         entry->basepath = basePath;\r
669         entry->status = GitStatus::GetMoreImportant(pGitStatus->text_status, pGitStatus->prop_status);\r
670         entry->textstatus = pGitStatus->text_status;\r
671         entry->propstatus = pGitStatus->prop_status;\r
672 //      entry->remotestatus = GitStatus::GetMoreImportant(pGitStatus->repos_text_status, pGitStatus->repos_prop_status);\r
673 //      entry->remotetextstatus = pGitStatus->repos_text_status;\r
674 //      entry->remotepropstatus = pGitStatus->repos_prop_status;\r
675         entry->inexternal = bInExternal;\r
676         entry->differentrepo = bEntryfromDifferentRepo;\r
677         entry->direct = bDirectItem;\r
678 //      entry->copied = !!pGitStatus->copied;\r
679 //      entry->switched = !!pGitStatus->switched;\r
680 \r
681         entry->last_commit_date = pGitStatus->ood_last_cmt_date;\r
682         if ((entry->last_commit_date == NULL)&&(pGitStatus->entry))\r
683                 entry->last_commit_date = pGitStatus->entry->cmt_date;\r
684         entry->remoterev = pGitStatus->ood_last_cmt_rev;\r
685         if (pGitStatus->entry)\r
686                 entry->last_commit_rev = pGitStatus->entry->cmt_rev;\r
687         if (pGitStatus->ood_last_cmt_author)\r
688                 entry->last_commit_author = CUnicodeUtils::GetUnicode(pGitStatus->ood_last_cmt_author);\r
689         if ((entry->last_commit_author.IsEmpty())&&(pGitStatus->entry)&&(pGitStatus->entry->cmt_author))\r
690                 entry->last_commit_author = CUnicodeUtils::GetUnicode(pGitStatus->entry->cmt_author);\r
691 \r
692         if (pGitStatus->entry)\r
693                 entry->isConflicted = (pGitStatus->entry->conflict_wrk && PathFileExists(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_wrk))) ? true : false;\r
694 \r
695         if ((entry->status == Git_wc_status_conflicted)||(entry->isConflicted))\r
696         {\r
697                 entry->isConflicted = true;\r
698                 if (pGitStatus->entry)\r
699                 {\r
700                         CTGitPath cpath;\r
701                         if (pGitStatus->entry->conflict_wrk)\r
702                         {\r
703                                 cpath = path.GetDirectory();\r
704                                 cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_wrk));\r
705                                 m_ConflictFileList.AddPath(cpath);\r
706                         }\r
707                         if (pGitStatus->entry->conflict_old)\r
708                         {\r
709                                 cpath = path.GetDirectory();\r
710                                 cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_old));\r
711                                 m_ConflictFileList.AddPath(cpath);\r
712                         }\r
713                         if (pGitStatus->entry->conflict_new)\r
714                         {\r
715                                 cpath = path.GetDirectory();\r
716                                 cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->conflict_new));\r
717                                 m_ConflictFileList.AddPath(cpath);\r
718                         }\r
719                         if (pGitStatus->entry->prejfile)\r
720                         {\r
721                                 cpath = path.GetDirectory();\r
722                                 cpath.AppendPathString(CUnicodeUtils::GetUnicode(pGitStatus->entry->prejfile));\r
723                                 m_ConflictFileList.AddPath(cpath);\r
724                         }\r
725                 }\r
726         }\r
727 \r
728         if (pGitStatus->entry)\r
729         {\r
730                 entry->isfolder = (pGitStatus->entry->kind == Git_node_dir);\r
731                 entry->Revision = pGitStatus->entry->revision;\r
732                 entry->keeplocal = !!pGitStatus->entry->keep_local;\r
733                 entry->working_size = pGitStatus->entry->working_size;\r
734                 entry->depth = pGitStatus->entry->depth;\r
735 \r
736                 if (pGitStatus->entry->url)\r
737                 {\r
738                         entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(pGitStatus->entry->url));\r
739                 }\r
740                 if (pGitStatus->entry->copyfrom_url)\r
741                 {\r
742                         entry->copyfrom_url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(pGitStatus->entry->copyfrom_url));\r
743                         entry->copyfrom_rev = pGitStatus->entry->copyfrom_rev;\r
744                 }\r
745                 else\r
746                         entry->copyfrom_rev = 0;\r
747 \r
748                 if(bDirectItem)\r
749                 {\r
750                         if (m_sURL.IsEmpty())\r
751                                 m_sURL = entry->url;\r
752                         else\r
753                                 m_sURL.LoadString(IDS_STATUSLIST_MULTIPLETARGETS);\r
754                         m_StatusUrlList.AddPath(CTGitPath(entry->url));\r
755                 }\r
756                 if (pGitStatus->entry->lock_owner)\r
757                         entry->lock_owner = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_owner);\r
758                 if (pGitStatus->entry->lock_token)\r
759                 {\r
760                         entry->lock_token = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_token);\r
761                         m_bHasLocks = true;\r
762                 }\r
763                 if (pGitStatus->entry->lock_comment)\r
764                         entry->lock_comment = CUnicodeUtils::GetUnicode(pGitStatus->entry->lock_comment);\r
765 \r
766                 if (pGitStatus->entry->present_props)\r
767                 {\r
768                         entry->present_props = pGitStatus->entry->present_props;\r
769                 }\r
770 \r
771                 if (pGitStatus->entry->changelist)\r
772                 {\r
773                         entry->changelist = CUnicodeUtils::GetUnicode(pGitStatus->entry->changelist);\r
774                         m_changelists[entry->changelist] = -1;\r
775                         m_bHasChangeLists = true;\r
776                 }\r
777                 entry->needslock = (pGitStatus->entry->present_props && (strstr(pGitStatus->entry->present_props, "Git:needs-lock")!=NULL) );\r
778         }\r
779         else\r
780         {\r
781                 if (pGitStatus->ood_kind == Git_node_none)\r
782                         entry->isfolder = path.IsDirectory();\r
783                 else\r
784                         entry->isfolder = (pGitStatus->ood_kind == Git_node_dir);\r
785         }\r
786         if (pGitStatus->repos_lock)\r
787         {\r
788                 if (pGitStatus->repos_lock->owner)\r
789                         entry->lock_remoteowner = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->owner);\r
790                 if (pGitStatus->repos_lock->token)\r
791                         entry->lock_remotetoken = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->token);\r
792                 if (pGitStatus->repos_lock->comment)\r
793                         entry->lock_comment = CUnicodeUtils::GetUnicode(pGitStatus->repos_lock->comment);\r
794         }\r
795 \r
796         // Pass ownership of the entry to the array\r
797         m_arStatusArray.push_back(entry);\r
798 #endif\r
799         return entry;\r
800 }\r
801 \r
802 void CGitStatusListCtrl::AddUnversionedFolder(const CTGitPath& folderName,\r
803                                                                                                 const CTGitPath& basePath,\r
804                                                                                                 GitConfig * config)\r
805 {\r
806 #if 0\r
807         if (!m_bUnversionedRecurse)\r
808                 return;\r
809         CSimpleFileFind filefinder(folderName.GetWinPathString());\r
810 \r
811         CTGitPath filename;\r
812         m_bHasUnversionedItems = TRUE;\r
813         while (filefinder.FindNextFileNoDots())\r
814         {\r
815                 filename.SetFromWin(filefinder.GetFilePath(), filefinder.IsDirectory());\r
816 \r
817                 bool bMatchIgnore = !!config->MatchIgnorePattern(filename.GetFileOrDirectoryName());\r
818                 bMatchIgnore = bMatchIgnore || config->MatchIgnorePattern(filename.GetGitPathString());\r
819                 if (((bMatchIgnore)&&(m_bShowIgnores))||(!bMatchIgnore))\r
820                 {\r
821                         FileEntry * entry = new FileEntry();\r
822                         entry->path = filename;\r
823                         entry->basepath = basePath;\r
824                         entry->inunversionedfolder = true;\r
825                         entry->isfolder = filefinder.IsDirectory();\r
826 \r
827                         m_arStatusArray.push_back(entry);\r
828                         if (entry->isfolder)\r
829                         {\r
830                                 if (!g_GitAdminDir.HasAdminDir(entry->path.GetWinPathString(), true))\r
831                                         AddUnversionedFolder(entry->path, basePath, config);\r
832                         }\r
833                 }\r
834         }\r
835 #endif\r
836 }\r
837 \r
838 \r
839 void CGitStatusListCtrl::ReadRemainingItemsStatus(GitStatus& status, const CTGitPath& basePath,\r
840                                                                                   CStringA& strCurrentRepositoryUUID,\r
841                                                                                   CTGitPathList& arExtPaths, GitConfig * config, bool bAllDirect)\r
842 {\r
843 #if 0\r
844         git_wc_status2_t * s;\r
845 \r
846         CTGitPath lastexternalpath;\r
847         CTGitPath GitPath;\r
848         while ((s = status.GetNextFileStatus(GitPath)) != NULL)\r
849         {\r
850                 Git_wc_status_kind wcFileStatus = GitStatus::GetMoreImportant(s->text_status, s->prop_status);\r
851                 if ((wcFileStatus == Git_wc_status_unversioned) && (GitPath.IsDirectory()))\r
852                 {\r
853                         // check if the unversioned folder is maybe versioned. This\r
854                         // could happen with nested layouts\r
855                         Git_wc_status_kind st = GitStatus::GetAllStatus(GitPath);\r
856                         if ((st != Git_wc_status_unversioned)&&(st != Git_wc_status_none))\r
857                         {\r
858                                 FileEntry * entry = new FileEntry();\r
859                                 entry->path = GitPath;\r
860                                 entry->basepath = basePath;\r
861                                 entry->inunversionedfolder = true;\r
862                                 entry->isfolder = true;\r
863                                 entry->isNested = true;\r
864                                 m_arStatusArray.push_back(entry);\r
865                                 continue;\r
866                         }\r
867                 }\r
868                 bool bDirectoryIsExternal = false;\r
869                 bool bEntryfromDifferentRepo = false;\r
870                 if (s->entry)\r
871                 {\r
872                         if (s->entry->uuid)\r
873                         {\r
874                                 if (strCurrentRepositoryUUID.IsEmpty())\r
875                                         strCurrentRepositoryUUID = s->entry->uuid;\r
876                                 else\r
877                                 {\r
878                                         if (strCurrentRepositoryUUID.Compare(s->entry->uuid)!=0)\r
879                                         {\r
880                                                 bEntryfromDifferentRepo = true;\r
881                                                 if (GitStatus::IsImportant(wcFileStatus))\r
882                                                         m_bHasExternalsFromDifferentRepos = TRUE;\r
883                                                 if (s->entry->kind == Git_node_dir)\r
884                                                 {\r
885                                                         if ((lastexternalpath.IsEmpty())||(!lastexternalpath.IsAncestorOf(GitPath)))\r
886                                                         {\r
887                                                                 arExtPaths.AddPath(GitPath);\r
888                                                                 lastexternalpath = GitPath;\r
889                                                         }\r
890                                                 }\r
891                                         }\r
892                                 }\r
893                         }\r
894                         else\r
895                         {\r
896                                 // we don't have an UUID - maybe an added file/folder\r
897                                 if (!strCurrentRepositoryUUID.IsEmpty())\r
898                                 {\r
899                                         if ((!lastexternalpath.IsEmpty())&&\r
900                                                 (lastexternalpath.IsAncestorOf(GitPath)))\r
901                                         {\r
902                                                 bEntryfromDifferentRepo = true;\r
903                                                 m_bHasExternalsFromDifferentRepos = TRUE;\r
904                                         }\r
905                                 }\r
906                         }\r
907                 }\r
908                 else\r
909                 {\r
910                         // if unversioned items lie around in external\r
911                         // directories from different repos, we have to mark them\r
912                         // as such too.\r
913                         if (!strCurrentRepositoryUUID.IsEmpty())\r
914                         {\r
915                                 if ((!lastexternalpath.IsEmpty())&&\r
916                                         (lastexternalpath.IsAncestorOf(GitPath)))\r
917                                 {\r
918                                         bEntryfromDifferentRepo = true;\r
919                                 }\r
920                         }\r
921                 }\r
922                 if (status.IsExternal(GitPath))\r
923                 {\r
924                         arExtPaths.AddPath(GitPath);\r
925                         m_bHasExternals = TRUE;\r
926                 }\r
927                 if ((!bEntryfromDifferentRepo)&&(status.IsInExternal(GitPath)))\r
928                 {\r
929                         // if the externals are inside an unversioned folder (this happens if\r
930                         // the externals are specified with e.g. "ext\folder url" instead of just\r
931                         // "folder url"), then a commit won't succeed.\r
932                         // therefore, we treat those as if the externals come from a different\r
933                         // repository\r
934                         CTGitPath extpath = GitPath;\r
935                         while (basePath.IsAncestorOf(extpath))\r
936                         {\r
937                                 if (!extpath.HasAdminDir())\r
938                                 {\r
939                                         bEntryfromDifferentRepo = true;\r
940                                         break;\r
941                                 }\r
942                                 extpath = extpath.GetContainingDirectory();\r
943                         }\r
944                 }\r
945                 // Do we have any external paths?\r
946                 if(arExtPaths.GetCount() > 0)\r
947                 {\r
948                         // If do have external paths, we need to check if the current item belongs\r
949                         // to one of them\r
950                         for (int ix=0; ix<arExtPaths.GetCount(); ix++)\r
951                         {\r
952                                 if (arExtPaths[ix].IsAncestorOf(GitPath))\r
953                                 {\r
954                                         bDirectoryIsExternal = true;\r
955                                         break;\r
956                                 }\r
957                         }\r
958                 }\r
959 \r
960                 if ((wcFileStatus == Git_wc_status_unversioned)&&(!bDirectoryIsExternal))\r
961                         m_bHasUnversionedItems = TRUE;\r
962 \r
963                 const FileEntry* entry = AddNewFileEntry(s, GitPath, basePath, bAllDirect, bDirectoryIsExternal, bEntryfromDifferentRepo);\r
964 \r
965                 bool bMatchIgnore = !!config->MatchIgnorePattern(entry->path.GetFileOrDirectoryName());\r
966                 bMatchIgnore = bMatchIgnore || config->MatchIgnorePattern(entry->path.GetGitPathString());\r
967                 if ((((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none))&&(!bMatchIgnore))||\r
968                         ((wcFileStatus == Git_wc_status_ignored)&&(m_bShowIgnores))||\r
969                         (((wcFileStatus == Git_wc_status_unversioned)||(wcFileStatus == Git_wc_status_none))&&(bMatchIgnore)&&(m_bShowIgnores)))\r
970                 {\r
971                         if (entry->isfolder)\r
972                         {\r
973                                 // we have an unversioned folder -> get all files in it recursively!\r
974                                 AddUnversionedFolder(GitPath, basePath, config);\r
975                         }\r
976                 }\r
977         } // while ((s = status.GetNextFileStatus(GitPath)) != NULL) \r
978 #endif\r
979 }\r
980 \r
981 // Get the show-flags bitmap value which corresponds to a particular Git status\r
982 DWORD CGitStatusListCtrl::GetShowFlagsFromGitStatus(git_wc_status_kind status)\r
983 {\r
984         switch (status)\r
985         {\r
986         case git_wc_status_none:\r
987         case git_wc_status_unversioned:\r
988                 return SVNSLC_SHOWUNVERSIONED;\r
989         case git_wc_status_ignored:\r
990                 if (!m_bShowIgnores)\r
991                         return SVNSLC_SHOWDIRECTS;\r
992                 return SVNSLC_SHOWDIRECTS|SVNSLC_SHOWIGNORED;\r
993         case git_wc_status_incomplete:\r
994                 return SVNSLC_SHOWINCOMPLETE;\r
995         case git_wc_status_normal:\r
996                 return SVNSLC_SHOWNORMAL;\r
997         case git_wc_status_external:\r
998                 return SVNSLC_SHOWEXTERNAL;\r
999         case git_wc_status_added:\r
1000                 return SVNSLC_SHOWADDED;\r
1001         case git_wc_status_missing:\r
1002                 return SVNSLC_SHOWMISSING;\r
1003         case git_wc_status_deleted:\r
1004                 return SVNSLC_SHOWREMOVED;\r
1005         case git_wc_status_replaced:\r
1006                 return SVNSLC_SHOWREPLACED;\r
1007         case git_wc_status_modified:\r
1008                 return SVNSLC_SHOWMODIFIED;\r
1009         case git_wc_status_merged:\r
1010                 return SVNSLC_SHOWMERGED;\r
1011         case git_wc_status_conflicted:\r
1012                 return SVNSLC_SHOWCONFLICTED;\r
1013         case git_wc_status_obstructed:\r
1014                 return SVNSLC_SHOWOBSTRUCTED;\r
1015         default:\r
1016                 // we should NEVER get here!\r
1017                 ASSERT(FALSE);\r
1018                 break;\r
1019         }\r
1020         return 0;\r
1021 }\r
1022 \r
1023 void CGitStatusListCtrl::Show(DWORD dwShow, DWORD dwCheck /*=0*/, bool bShowFolders /* = true */)\r
1024 {\r
1025 #if 0\r
1026         Locker lock(m_critSec);\r
1027         WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), GetUserDefaultLangID());\r
1028 \r
1029         CWinApp * pApp = AfxGetApp();\r
1030         if (pApp)\r
1031                 pApp->DoWaitCursor(1);\r
1032         m_dwShow = dwShow;\r
1033         m_bShowFolders = bShowFolders;\r
1034         m_nSelected = 0;\r
1035         int nTopIndex = GetTopIndex();\r
1036         POSITION posSelectedEntry = GetFirstSelectedItemPosition();\r
1037         int nSelectedEntry = 0;\r
1038         if (posSelectedEntry)\r
1039                 nSelectedEntry = GetNextSelectedItem(posSelectedEntry);\r
1040         SetRedraw(FALSE);\r
1041         DeleteAllItems();\r
1042 \r
1043         PrepareGroups();\r
1044 \r
1045         m_arListArray.clear();\r
1046 \r
1047         m_arListArray.reserve(m_arStatusArray.size());\r
1048         SetItemCount (static_cast<int>(m_arStatusArray.size()));\r
1049 \r
1050         int listIndex = 0;\r
1051         for (size_t i=0; i < m_arStatusArray.size(); ++i)\r
1052         {\r
1053                 FileEntry * entry = m_arStatusArray[i];\r
1054                 if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))\r
1055                         continue;\r
1056                 if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))\r
1057                         continue;\r
1058                 if (entry->IsFolder() && (!bShowFolders))\r
1059                         continue;       // don't show folders if they're not wanted.\r
1060                 git_wc_status_kind status = GitStatus::GetMoreImportant(entry->status, entry->remotestatus);\r
1061                 DWORD showFlags = GetShowFlagsFromGitStatus(status);\r
1062                 if (entry->IsLocked())\r
1063                         showFlags |= SVNSLC_SHOWLOCKS;\r
1064                 if (entry->switched)\r
1065                         showFlags |= SVNSLC_SHOWSWITCHED;\r
1066                 if (!entry->changelist.IsEmpty())\r
1067                         showFlags |= SVNSLC_SHOWINCHANGELIST;\r
1068 \r
1069                 bool bAllowCheck = ((entry->changelist.Compare(SVNSLC_IGNORECHANGELIST) != 0) && (m_bCheckIfGroupsExist || (m_changelists.size()==0 || (m_changelists.size()==1 && m_bHasIgnoreGroup))));\r
1070 \r
1071                 // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item\r
1072                 if (status != Git_wc_status_ignored || (entry->direct) || (dwShow & GitSLC_SHOWIGNORED))\r
1073                 {\r
1074                         if ((!entry->IsFolder()) && (status == Git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))\r
1075                         {\r
1076                                 if (PathFileExists(entry->GetPath().GetWinPath()))\r
1077                                 {\r
1078                                         m_arListArray.push_back(i);\r
1079                                         if ((dwCheck & SVNSLC_SHOWREMOVEDANDPRESENT)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
1080                                         {\r
1081                                                 if (bAllowCheck)\r
1082                                                         entry->checked = true;\r
1083                                         }\r
1084                                         AddEntry(entry, langID, listIndex++);\r
1085                                 }\r
1086                         }\r
1087                         else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))\r
1088                         {\r
1089                                 m_arListArray.push_back(i);\r
1090                                 if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
1091                                 {\r
1092                                         if (bAllowCheck)\r
1093                                                 entry->checked = true;\r
1094                                 }\r
1095                                 AddEntry(entry, langID, listIndex++);\r
1096                         }\r
1097                         else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))\r
1098                         {\r
1099                                 m_arListArray.push_back(i);\r
1100                                 if ((dwCheck & showFlags)||((dwCheck & SVNSLC_SHOWDIRECTS)&&(entry->direct)))\r
1101                                 {\r
1102                                         if (bAllowCheck)\r
1103                                                 entry->checked = true;\r
1104                                 }\r
1105                                 AddEntry(entry, langID, listIndex++);\r
1106                         }\r
1107                 }\r
1108         }\r
1109 \r
1110         SetItemCount(listIndex);\r
1111 \r
1112     m_ColumnManager.UpdateRelevance (m_arStatusArray, m_arListArray);\r
1113 \r
1114         int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
1115         for (int col = 0; col <= maxcol; col++)\r
1116         SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));\r
1117 \r
1118     SetRedraw(TRUE);\r
1119         GetStatisticsString();\r
1120 \r
1121         CHeaderCtrl * pHeader = GetHeaderCtrl();\r
1122         HDITEM HeaderItem = {0};\r
1123         HeaderItem.mask = HDI_FORMAT;\r
1124         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
1125         {\r
1126                 pHeader->GetItem(i, &HeaderItem);\r
1127                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
1128                 pHeader->SetItem(i, &HeaderItem);\r
1129         }\r
1130         if (m_nSortedColumn)\r
1131         {\r
1132                 pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
1133                 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
1134                 pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
1135         }\r
1136 \r
1137         if (nSelectedEntry)\r
1138         {\r
1139                 SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);\r
1140                 EnsureVisible(nSelectedEntry, false);\r
1141         }\r
1142         else\r
1143         {\r
1144                 // Restore the item at the top of the list.\r
1145                 for (int i=0;GetTopIndex() != nTopIndex;i++)\r
1146                 {\r
1147                         if ( !EnsureVisible(nTopIndex+i,false) )\r
1148                         {\r
1149                                 break;\r
1150                         }\r
1151                 }\r
1152         }\r
1153 \r
1154         if (pApp)\r
1155                 pApp->DoWaitCursor(-1);\r
1156 \r
1157         m_bEmpty = (GetItemCount() == 0);\r
1158         Invalidate();\r
1159 #endif\r
1160 }\r
1161 \r
1162 void CGitStatusListCtrl::Show(DWORD dwShow, const CTGitPathList& checkedList, bool bShowFolders /* = true */)\r
1163 {\r
1164 #if 0\r
1165         Locker lock(m_critSec);\r
1166         WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), GetUserDefaultLangID());\r
1167 \r
1168         CWinApp * pApp = AfxGetApp();\r
1169         if (pApp)\r
1170                 pApp->DoWaitCursor(1);\r
1171         m_dwShow = dwShow;\r
1172         m_bShowFolders = bShowFolders;\r
1173         m_nSelected = 0;\r
1174         int nTopIndex = GetTopIndex();\r
1175         POSITION posSelectedEntry = GetFirstSelectedItemPosition();\r
1176         int nSelectedEntry = 0;\r
1177         if (posSelectedEntry)\r
1178                 nSelectedEntry = GetNextSelectedItem(posSelectedEntry);\r
1179         SetRedraw(FALSE);\r
1180         DeleteAllItems();\r
1181 \r
1182         PrepareGroups();\r
1183 \r
1184         m_arListArray.clear();\r
1185 \r
1186         m_arListArray.reserve(m_arStatusArray.size());\r
1187         SetItemCount (static_cast<int>(m_arStatusArray.size()));\r
1188 \r
1189         int listIndex = 0;\r
1190         for (size_t i=0; i < m_arStatusArray.size(); ++i)\r
1191         {\r
1192                 FileEntry * entry = m_arStatusArray[i];\r
1193                 if ((entry->inexternal) && (!(dwShow & SVNSLC_SHOWINEXTERNALS)))\r
1194                         continue;\r
1195                 if ((entry->differentrepo || entry->isNested) && (! (dwShow & SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO)))\r
1196                         continue;\r
1197                 if (entry->IsFolder() && (!bShowFolders))\r
1198                         continue;       // don't show folders if they're not wanted.\r
1199                 git_wc_status_kind status = SVNStatus::GetMoreImportant(entry->status, entry->remotestatus);\r
1200                 DWORD showFlags = GetShowFlagsFromSVNStatus(status);\r
1201                 if (entry->IsLocked())\r
1202                         showFlags |= SVNSLC_SHOWLOCKS;\r
1203                 if (!entry->changelist.IsEmpty())\r
1204                         showFlags |= SVNSLC_SHOWINCHANGELIST;\r
1205 \r
1206                 // status_ignored is a special case - we must have the 'direct' flag set to add a status_ignored item\r
1207                 if (status != git_wc_status_ignored || (entry->direct) || (dwShow & SVNSLC_SHOWIGNORED))\r
1208                 {\r
1209                         for (int npath = 0; npath < checkedList.GetCount(); ++npath)\r
1210                         {\r
1211                                 if (entry->GetPath().IsEquivalentTo(checkedList[npath]))\r
1212                                 {\r
1213                                         entry->checked = true;\r
1214                                         break;\r
1215                                 }\r
1216                         }\r
1217                         if ((!entry->IsFolder()) && (status == git_wc_status_deleted) && (dwShow & SVNSLC_SHOWREMOVEDANDPRESENT))\r
1218                         {\r
1219                                 if (PathFileExists(entry->GetPath().GetWinPath()))\r
1220                                 {\r
1221                                         m_arListArray.push_back(i);\r
1222                                         AddEntry(entry, langID, listIndex++);\r
1223                                 }\r
1224                         }\r
1225                         else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFILES)&&(entry->direct)&&(!entry->IsFolder())))\r
1226                         {\r
1227                                 m_arListArray.push_back(i);\r
1228                                 AddEntry(entry, langID, listIndex++);\r
1229                         }\r
1230                         else if ((dwShow & showFlags)||((dwShow & SVNSLC_SHOWDIRECTFOLDER)&&(entry->direct)&&entry->IsFolder()))\r
1231                         {\r
1232                                 m_arListArray.push_back(i);\r
1233                                 AddEntry(entry, langID, listIndex++);\r
1234                         }\r
1235                         else if (entry->switched)\r
1236                         {\r
1237                                 m_arListArray.push_back(i);\r
1238                                 AddEntry(entry, langID, listIndex++);\r
1239                         }\r
1240                 }\r
1241         }\r
1242 \r
1243         SetItemCount(listIndex);\r
1244 \r
1245         int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
1246         for (int col = 0; col <= maxcol; col++)\r
1247         SetColumnWidth (col, m_ColumnManager.GetWidth (col, true));\r
1248 \r
1249     SetRedraw(TRUE);\r
1250         GetStatisticsString();\r
1251 \r
1252         CHeaderCtrl * pHeader = GetHeaderCtrl();\r
1253         HDITEM HeaderItem = {0};\r
1254         HeaderItem.mask = HDI_FORMAT;\r
1255         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
1256         {\r
1257                 pHeader->GetItem(i, &HeaderItem);\r
1258                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
1259                 pHeader->SetItem(i, &HeaderItem);\r
1260         }\r
1261         if (m_nSortedColumn)\r
1262         {\r
1263                 pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
1264                 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
1265                 pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
1266         }\r
1267 \r
1268         if (nSelectedEntry)\r
1269         {\r
1270                 SetItemState(nSelectedEntry, LVIS_SELECTED, LVIS_SELECTED);\r
1271                 EnsureVisible(nSelectedEntry, false);\r
1272         }\r
1273         else\r
1274         {\r
1275                 // Restore the item at the top of the list.\r
1276                 for (int i=0;GetTopIndex() != nTopIndex;i++)\r
1277                 {\r
1278                         if ( !EnsureVisible(nTopIndex+i,false) )\r
1279                         {\r
1280                                 break;\r
1281                         }\r
1282                 }\r
1283         }\r
1284 \r
1285         if (pApp)\r
1286                 pApp->DoWaitCursor(-1);\r
1287 \r
1288         m_bEmpty = (GetItemCount() == 0);\r
1289         Invalidate();\r
1290 #endif\r
1291 }\r
1292 \r
1293 void CGitStatusListCtrl::AddEntry(FileEntry * entry, WORD langID, int listIndex)\r
1294 {\r
1295         static CString ponly(MAKEINTRESOURCE(IDS_STATUSLIST_PROPONLY));\r
1296         static HINSTANCE hResourceHandle(AfxGetResourceHandle());\r
1297 \r
1298         CString path = entry->GetPath().GetGitPathString();\r
1299         if ( m_mapFilenameToChecked.size()!=0 && m_mapFilenameToChecked.find(path) != m_mapFilenameToChecked.end() )\r
1300         {\r
1301                 // The user manually de-/selected an item. We now restore this status\r
1302                 // when refreshing.\r
1303                 entry->checked = m_mapFilenameToChecked[path];\r
1304         }\r
1305 \r
1306         m_bBlock = TRUE;\r
1307         TCHAR buf[100];\r
1308         int index = listIndex;\r
1309         int nCol = 1;\r
1310         CString entryname = entry->GetDisplayName();\r
1311         int icon_idx = 0;\r
1312         if (entry->isfolder)\r
1313                 icon_idx = m_nIconFolder;\r
1314         else\r
1315         {\r
1316                 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(entry->path);\r
1317         }\r
1318         // relative path\r
1319         InsertItem(index, entryname, icon_idx);\r
1320         // SVNSLC_COLFILENAME\r
1321         SetItemText(index, nCol++, entry->path.GetFileOrDirectoryName());\r
1322         // SVNSLC_COLEXT\r
1323         SetItemText(index, nCol++, entry->path.GetFileExtension());\r
1324         // SVNSLC_COLSTATUS\r
1325         if (entry->isNested)\r
1326         {\r
1327                 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
1328                 SetItemText(index, nCol++, sTemp);\r
1329         }\r
1330         else\r
1331         {\r
1332                 GitStatus::GetStatusString(hResourceHandle, entry->status, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1333                 if ((entry->copied)&&(_tcslen(buf)>1))\r
1334                         _tcscat_s(buf, 100, _T(" (+)"));\r
1335                 if ((entry->switched)&&(_tcslen(buf)>1))\r
1336                         _tcscat_s(buf, 100, _T(" (s)"));\r
1337 #if 0\r
1338                 if ((entry->status == entry->propstatus)&&\r
1339                         (entry->status != git_wc_status_normal)&&\r
1340                         (entry->status != git_wc_status_unversioned)&&\r
1341                         (!GitStatus::IsImportant(entry->textstatus)))\r
1342                         _tcscat_s(buf, 100, ponly);\r
1343 #endif\r
1344                 SetItemText(index, nCol++, buf);\r
1345         }\r
1346         // SVNSLC_COLREMOTESTATUS\r
1347         if (entry->isNested)\r
1348         {\r
1349                 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
1350                 SetItemText(index, nCol++, sTemp);\r
1351         }\r
1352         else\r
1353         {\r
1354 #if 0\r
1355                 SVNStatus::GetStatusString(hResourceHandle, entry->remotestatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1356                 if ((entry->copied)&&(_tcslen(buf)>1))\r
1357                         _tcscat_s(buf, 100, _T(" (+)"));\r
1358                 if ((entry->switched)&&(_tcslen(buf)>1))\r
1359                         _tcscat_s(buf, 100, _T(" (s)"));\r
1360                 if ((entry->remotestatus == entry->remotepropstatus)&&\r
1361                         (entry->remotestatus != git_wc_status_none)&&\r
1362                         (entry->remotestatus != git_wc_status_normal)&&\r
1363                         (entry->remotestatus != git_wc_status_unversioned)&&\r
1364                         (!SVNStatus::IsImportant(entry->remotetextstatus)))\r
1365                         _tcscat_s(buf, 100, ponly);\r
1366 #endif\r
1367                 SetItemText(index, nCol++, buf);\r
1368         }\r
1369         // SVNSLC_COLTEXTSTATUS\r
1370         if (entry->isNested)\r
1371         {\r
1372                 CString sTemp(MAKEINTRESOURCE(IDS_STATUSLIST_NESTED));\r
1373                 SetItemText(index, nCol++, sTemp);\r
1374         }\r
1375         else\r
1376         {\r
1377 #if 0\r
1378                 SVNStatus::GetStatusString(hResourceHandle, entry->textstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1379                 if ((entry->copied)&&(_tcslen(buf)>1))\r
1380                         _tcscat_s(buf, 100, _T(" (+)"));\r
1381                 if ((entry->switched)&&(_tcslen(buf)>1))\r
1382                         _tcscat_s(buf, 100, _T(" (s)"));\r
1383 #endif\r
1384                 SetItemText(index, nCol++, buf);\r
1385         }\r
1386         // SVNSLC_COLPROPSTATUS\r
1387         if (entry->isNested)\r
1388         {\r
1389                 SetItemText(index, nCol++, _T(""));\r
1390         }\r
1391         else\r
1392         {\r
1393 #if 0\r
1394                 SVNStatus::GetStatusString(hResourceHandle, entry->propstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1395                 if ((entry->copied)&&(_tcslen(buf)>1))\r
1396                         _tcscat_s(buf, 100, _T(" (+)"));\r
1397                 if ((entry->switched)&&(_tcslen(buf)>1))\r
1398                         _tcscat_s(buf, 100, _T(" (s)"));\r
1399 #endif\r
1400                 SetItemText(index, nCol++, buf);\r
1401         }\r
1402         // SVNSLC_COLREMOTETEXT\r
1403         if (entry->isNested)\r
1404         {\r
1405                 SetItemText(index, nCol++, _T(""));\r
1406         }\r
1407         else\r
1408         {\r
1409 #if 0\r
1410                 SVNStatus::GetStatusString(hResourceHandle, entry->remotetextstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1411                 SetItemText(index, nCol++, buf);\r
1412 #endif\r
1413         }\r
1414         // SVNSLC_COLREMOTEPROP\r
1415         if (entry->isNested)\r
1416         {\r
1417                 SetItemText(index, nCol++, _T(""));\r
1418         }\r
1419         else\r
1420         {\r
1421 //              SVNStatus::GetStatusString(hResourceHandle, entry->remotepropstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
1422                 SetItemText(index, nCol++, buf);\r
1423         }\r
1424         // SVNSLC_COLURL\r
1425 //      SetItemText(index, nCol++, entry->url);\r
1426         // SVNSLC_COLLOCK\r
1427 #if 0\r
1428         if (!m_HeadRev.IsHead())\r
1429         {\r
1430                 // we have contacted the repository\r
1431 \r
1432                 // decision-matrix\r
1433                 // wc           repository              text\r
1434                 // ""           ""                              ""\r
1435                 // ""           UID1                    owner\r
1436                 // UID1         UID1                    owner\r
1437                 // UID1         ""                              lock has been broken\r
1438                 // UID1         UID2                    lock has been stolen\r
1439                 if (entry->lock_token.IsEmpty() || (entry->lock_token.Compare(entry->lock_remotetoken)==0))\r
1440                 {\r
1441                         if (entry->lock_owner.IsEmpty())\r
1442                                 SetItemText(index, nCol++, entry->lock_remoteowner);\r
1443                         else\r
1444                                 SetItemText(index, nCol++, entry->lock_owner);\r
1445                 }\r
1446                 else if (entry->lock_remotetoken.IsEmpty())\r
1447                 {\r
1448                         // broken lock\r
1449                         CString temp(MAKEINTRESOURCE(IDS_STATUSLIST_LOCKBROKEN));\r
1450                         SetItemText(index, nCol++, temp);\r
1451                 }\r
1452                 else\r
1453                 {\r
1454                         // stolen lock\r
1455                         CString temp;\r
1456                         temp.Format(IDS_STATUSLIST_LOCKSTOLEN, (LPCTSTR)entry->lock_remoteowner);\r
1457                         SetItemText(index, nCol++, temp);\r
1458                 }\r
1459         }\r
1460         else\r
1461                 SetItemText(index, nCol++, entry->lock_owner);\r
1462         // SVNSLC_COLLOCKCOMMENT\r
1463         SetItemText(index, nCol++, entry->lock_comment);\r
1464         // SVNSLC_COLAUTHOR\r
1465         SetItemText(index, nCol++, entry->last_commit_author);\r
1466         // SVNSLC_COLREVISION\r
1467         CString temp;\r
1468         temp.Format(_T("%ld"), entry->last_commit_rev);\r
1469         if (entry->last_commit_rev > 0)\r
1470                 SetItemText(index, nCol++, temp);\r
1471         else\r
1472                 SetItemText(index, nCol++, _T(""));\r
1473         // SVNSLC_COLREMOTEREVISION\r
1474         temp.Format(_T("%ld"), entry->remoterev);\r
1475         if (entry->remoterev > 0)\r
1476                 SetItemText(index, nCol++, temp);\r
1477         else\r
1478                 SetItemText(index, nCol++, _T(""));\r
1479         // SVNSLC_COLDATE\r
1480         TCHAR datebuf[SVN_DATE_BUFFER];\r
1481         apr_time_t date = entry->last_commit_date;\r
1482         SVN::formatDate(datebuf, date, true);\r
1483         if (date)\r
1484                 SetItemText(index, nCol++, datebuf);\r
1485         else\r
1486                 SetItemText(index, nCol++, _T(""));\r
1487         // SVNSLC_COLSVNNEEDSLOCK\r
1488     BOOL bFoundSVNNeedsLock = entry->present_props.IsNeedsLockSet();\r
1489         CString strSVNNeedsLock = (bFoundSVNNeedsLock) ? _T("*") : _T("");\r
1490         SetItemText(index, nCol++, strSVNNeedsLock);\r
1491         // SVNSLC_COLCOPYFROM\r
1492         if (m_sURL.Compare(entry->copyfrom_url.Left(m_sURL.GetLength()))==0)\r
1493                 temp = entry->copyfrom_url.Mid(m_sURL.GetLength());\r
1494         else\r
1495                 temp = entry->copyfrom_url;\r
1496         SetItemText(index, nCol++, temp);\r
1497         // SVNSLC_COLMODIFICATIONDATE\r
1498         __int64 filetime = entry->GetPath().GetLastWriteTime();\r
1499         if ( (filetime) && (entry->status!=git_wc_status_deleted) )\r
1500         {\r
1501                 FILETIME* f = (FILETIME*)(__int64*)&filetime;\r
1502                 TCHAR datebuf[SVN_DATE_BUFFER];\r
1503                 SVN::formatDate(datebuf,*f,true);\r
1504                 SetItemText(index, nCol++, datebuf);\r
1505         }\r
1506         else\r
1507         {\r
1508                 SetItemText(index, nCol++, _T(""));\r
1509         }\r
1510 \r
1511     // user-defined properties\r
1512     for ( int i = SVNSLC_NUMCOLUMNS, count = m_ColumnManager.GetColumnCount()\r
1513         ; i < count\r
1514         ; ++i)\r
1515     {\r
1516         assert (i == nCol++);\r
1517         assert (m_ColumnManager.IsUserProp (i));\r
1518 \r
1519         CString name = m_ColumnManager.GetName(i);\r
1520         if (entry->present_props.HasProperty (name))\r
1521                 {\r
1522                         const CString& propVal = entry->present_props [name];\r
1523                         if (propVal.IsEmpty())\r
1524                                 SetItemText(index, i, m_sNoPropValueText);\r
1525                         else\r
1526                                 SetItemText(index, i, propVal);\r
1527                 }\r
1528                 else\r
1529             SetItemText(index, i, _T(""));\r
1530     }\r
1531 \r
1532         SetCheck(index, entry->checked);\r
1533         if (entry->checked)\r
1534                 m_nSelected++;\r
1535         if (m_changelists.find(entry->changelist) != m_changelists.end())\r
1536                 SetItemGroup(index, m_changelists[entry->changelist]);\r
1537         else\r
1538                 SetItemGroup(index, 0);\r
1539         m_bBlock = FALSE;\r
1540 #endif\r
1541 }\r
1542 \r
1543 bool CGitStatusListCtrl::SetItemGroup(int item, int groupindex)\r
1544 {\r
1545         if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS) == NULL)\r
1546                 return false;\r
1547         if (groupindex < 0)\r
1548                 return false;\r
1549         LVITEM i = {0};\r
1550         i.mask = LVIF_GROUPID;\r
1551         i.iItem = item;\r
1552         i.iSubItem = 0;\r
1553         i.iGroupId = groupindex;\r
1554 \r
1555         return !!SetItem(&i);\r
1556 }\r
1557 \r
1558 void CGitStatusListCtrl::Sort()\r
1559 {\r
1560         Locker lock(m_critSec);\r
1561 \r
1562     CSorter predicate (&m_ColumnManager, m_nSortedColumn, m_bAscending);\r
1563 \r
1564         std::sort(m_arStatusArray.begin(), m_arStatusArray.end(), predicate);\r
1565         SaveColumnWidths();\r
1566         Show(m_dwShow, 0, m_bShowFolders);\r
1567 }\r
1568 \r
1569 void CGitStatusListCtrl::OnHdnItemclick(NMHDR *pNMHDR, LRESULT *pResult)\r
1570 {\r
1571         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
1572         *pResult = 0;\r
1573         if (m_bBlock)\r
1574                 return;\r
1575         m_bBlock = TRUE;\r
1576         if (m_nSortedColumn == phdr->iItem)\r
1577                 m_bAscending = !m_bAscending;\r
1578         else\r
1579                 m_bAscending = TRUE;\r
1580         m_nSortedColumn = phdr->iItem;\r
1581         m_mapFilenameToChecked.clear();\r
1582         Sort();\r
1583 \r
1584         CHeaderCtrl * pHeader = GetHeaderCtrl();\r
1585         HDITEM HeaderItem = {0};\r
1586         HeaderItem.mask = HDI_FORMAT;\r
1587         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
1588         {\r
1589                 pHeader->GetItem(i, &HeaderItem);\r
1590                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
1591                 pHeader->SetItem(i, &HeaderItem);\r
1592         }\r
1593         pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
1594         HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
1595         pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
1596 \r
1597         // the checked state of the list control items must be restored\r
1598         for (int i=0; i<GetItemCount(); ++i)\r
1599         {\r
1600                 FileEntry * entry = GetListEntry(i);\r
1601                 SetCheck(i, entry->IsChecked());\r
1602         }\r
1603 \r
1604         m_bBlock = FALSE;\r
1605 }\r
1606 \r
1607 void CGitStatusListCtrl::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)\r
1608 {\r
1609         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
1610         *pResult = 0;\r
1611 \r
1612 #define ISCHECKED(x) ((x) ? ((((x)&LVIS_STATEIMAGEMASK)>>12)-1) : FALSE)\r
1613         if ((m_bBlock)&&(m_bBlockUI))\r
1614         {\r
1615                 // if we're blocked, prevent changing of the check state\r
1616                 if ((!ISCHECKED(pNMLV->uOldState) && ISCHECKED(pNMLV->uNewState))||\r
1617                         (ISCHECKED(pNMLV->uOldState) && !ISCHECKED(pNMLV->uNewState)))\r
1618                         *pResult = TRUE;\r
1619         }\r
1620 }\r
1621 \r
1622 BOOL CGitStatusListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)\r
1623 {\r
1624         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
1625         *pResult = 0;\r
1626         if ((pNMLV->uNewState==0)||(pNMLV->uNewState & LVIS_SELECTED)||(pNMLV->uNewState & LVIS_FOCUSED))\r
1627                 return FALSE;\r
1628 \r
1629         if (m_bBlock)\r
1630                 return FALSE;\r
1631 \r
1632         bool bSelected = !!(ListView_GetItemState(m_hWnd, pNMLV->iItem, LVIS_SELECTED) & LVIS_SELECTED);\r
1633         int nListItems = GetItemCount();\r
1634 \r
1635         m_bBlock = TRUE;\r
1636         // was the item checked?\r
1637         if (GetCheck(pNMLV->iItem))\r
1638         {\r
1639                 CheckEntry(pNMLV->iItem, nListItems);\r
1640                 if (bSelected)\r
1641                 {\r
1642                         POSITION pos = GetFirstSelectedItemPosition();\r
1643                         int index;\r
1644                         while ((index = GetNextSelectedItem(pos)) >= 0)\r
1645                         {\r
1646                                 if (index != pNMLV->iItem)\r
1647                                         CheckEntry(index, nListItems);\r
1648                         }\r
1649                 }\r
1650         }\r
1651         else\r
1652         {\r
1653                 UncheckEntry(pNMLV->iItem, nListItems);\r
1654                 if (bSelected)\r
1655                 {\r
1656                         POSITION pos = GetFirstSelectedItemPosition();\r
1657                         int index;\r
1658                         while ((index = GetNextSelectedItem(pos)) >= 0)\r
1659                         {\r
1660                                 if (index != pNMLV->iItem)\r
1661                                         UncheckEntry(index, nListItems);\r
1662                         }\r
1663                 }\r
1664         }\r
1665         GetStatisticsString();\r
1666         m_bBlock = FALSE;\r
1667         NotifyCheck();\r
1668 \r
1669         return FALSE;\r
1670 }\r
1671 \r
1672 void CGitStatusListCtrl::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)\r
1673 {\r
1674         LPNMHEADER header = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
1675     if (   (header != NULL) \r
1676         && (header->iItem >= 0) \r
1677         && (header->iItem < m_ColumnManager.GetColumnCount()))\r
1678     {\r
1679         m_ColumnManager.ColumnResized (header->iItem);\r
1680     }\r
1681 \r
1682     *pResult = FALSE;\r
1683 }\r
1684 \r
1685 void CGitStatusListCtrl::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)\r
1686 {\r
1687         LPNMHEADER header = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
1688         *pResult = TRUE;\r
1689     if (   (header != NULL) \r
1690         && (header->iItem >= 0) \r
1691         && (header->iItem < m_ColumnManager.GetColumnCount())\r
1692                 // only allow the reordering if the column was not moved left of the first\r
1693                 // visible item - otherwise the 'invisible' columns are not at the far left\r
1694                 // anymore and we get all kinds of redrawing problems.\r
1695                 && (header->pitem)\r
1696                 && (header->pitem->iOrder > m_ColumnManager.GetInvisibleCount()))\r
1697     {\r
1698         m_ColumnManager.ColumnMoved (header->iItem, header->pitem->iOrder);\r
1699                 *pResult = FALSE;\r
1700     }\r
1701 \r
1702     Invalidate(FALSE);\r
1703 }\r
1704 \r
1705 void CGitStatusListCtrl::CheckEntry(int index, int nListItems)\r
1706 {\r
1707         Locker lock(m_critSec);\r
1708         FileEntry * entry = GetListEntry(index);\r
1709         ASSERT(entry != NULL);\r
1710         if (entry == NULL)\r
1711                 return;\r
1712         SetCheck(index, TRUE);\r
1713         entry = GetListEntry(index);\r
1714         // if an unversioned item was checked, then we need to check if\r
1715         // the parent folders are unversioned too. If the parent folders actually\r
1716         // are unversioned, then check those too.\r
1717         if (entry->status == git_wc_status_unversioned)\r
1718         {\r
1719                 // we need to check the parent folder too\r
1720                 const CTGitPath& folderpath = entry->path;\r
1721                 for (int i=0; i< nListItems; ++i)\r
1722                 {\r
1723                         FileEntry * testEntry = GetListEntry(i);\r
1724                         ASSERT(testEntry != NULL);\r
1725                         if (testEntry == NULL)\r
1726                                 continue;\r
1727                         if (!testEntry->checked)\r
1728                         {\r
1729                                 if (testEntry->path.IsAncestorOf(folderpath) && (!testEntry->path.IsEquivalentTo(folderpath)))\r
1730                                 {\r
1731                                         SetEntryCheck(testEntry,i,true);\r
1732                                         m_nSelected++;\r
1733                                 }\r
1734                         }\r
1735                 }\r
1736         }\r
1737         bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
1738         if ( (entry->status == git_wc_status_deleted) || (m_bCheckChildrenWithParent) || (bShift) )\r
1739         {\r
1740                 // if a deleted folder gets checked, we have to check all\r
1741                 // children of that folder too.\r
1742                 if (entry->path.IsDirectory())\r
1743                 {\r
1744                         SetCheckOnAllDescendentsOf(entry, true);\r
1745                 }\r
1746 \r
1747                 // if a deleted file or folder gets checked, we have to\r
1748                 // check all parents of this item too.\r
1749                 for (int i=0; i<nListItems; ++i)\r
1750                 {\r
1751                         FileEntry * testEntry = GetListEntry(i);\r
1752                         ASSERT(testEntry != NULL);\r
1753                         if (testEntry == NULL)\r
1754                                 continue;\r
1755                         if (!testEntry->checked)\r
1756                         {\r
1757                                 if (testEntry->path.IsAncestorOf(entry->path) && (!testEntry->path.IsEquivalentTo(entry->path)))\r
1758                                 {\r
1759                                         if ((testEntry->status == git_wc_status_deleted)||(m_bCheckChildrenWithParent))\r
1760                                         {\r
1761                                                 SetEntryCheck(testEntry,i,true);\r
1762                                                 m_nSelected++;\r
1763                                                 // now we need to check all children of this parent folder\r
1764                                                 SetCheckOnAllDescendentsOf(testEntry, true);\r
1765                                         }\r
1766                                 }\r
1767                         }\r
1768                 }\r
1769         }\r
1770 \r
1771         if ( !entry->checked )\r
1772         {\r
1773                 entry->checked = TRUE;\r
1774                 m_nSelected++;\r
1775         }\r
1776 }\r
1777 \r
1778 void CGitStatusListCtrl::UncheckEntry(int index, int nListItems)\r
1779 {\r
1780         Locker lock(m_critSec);\r
1781         FileEntry * entry = GetListEntry(index);\r
1782         ASSERT(entry != NULL);\r
1783         if (entry == NULL)\r
1784                 return;\r
1785         SetCheck(index, FALSE);\r
1786         entry = GetListEntry(index);\r
1787         // item was unchecked\r
1788         if (entry->path.IsDirectory())\r
1789         {\r
1790                 // disable all files within an unselected folder, except when unchecking a folder with property changes\r
1791                 bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
1792                 if ( (entry->status != git_wc_status_modified) || (bShift) )\r
1793                 {\r
1794                         SetCheckOnAllDescendentsOf(entry, false);\r
1795                 }\r
1796         }\r
1797         else if (entry->status == git_wc_status_deleted)\r
1798         {\r
1799                 // a "deleted" file was unchecked, so uncheck all parent folders\r
1800                 // and all children of those parents\r
1801                 for (int i=0; i<nListItems; i++)\r
1802                 {\r
1803                         FileEntry * testEntry = GetListEntry(i);\r
1804                         ASSERT(testEntry != NULL);\r
1805                         if (testEntry == NULL)\r
1806                                 continue;\r
1807                         if (testEntry->checked)\r
1808                         {\r
1809                                 if (testEntry->path.IsAncestorOf(entry->path))\r
1810                                 {\r
1811                                         if (testEntry->status == git_wc_status_deleted)\r
1812                                         {\r
1813                                                 SetEntryCheck(testEntry,i,false);\r
1814                                                 m_nSelected--;\r
1815 \r
1816                                                 SetCheckOnAllDescendentsOf(testEntry, false);\r
1817                                         }\r
1818                                 }\r
1819                         }\r
1820                 }\r
1821         }\r
1822 \r
1823         if ( entry->checked )\r
1824         {\r
1825                 entry->checked = FALSE;\r
1826                 m_nSelected--;\r
1827         }\r
1828 }\r
1829 \r
1830 bool CGitStatusListCtrl::EntryPathCompareNoCase(const FileEntry* pEntry1, const FileEntry* pEntry2)\r
1831 {\r
1832         return pEntry1->path < pEntry2->path;\r
1833 }\r
1834 \r
1835 bool CGitStatusListCtrl::IsEntryVersioned(const FileEntry* pEntry1)\r
1836 {\r
1837         return pEntry1->status != git_wc_status_unversioned;\r
1838 }\r
1839 \r
1840 bool CGitStatusListCtrl::BuildStatistics()\r
1841 {\r
1842         bool bRefetchStatus = false;\r
1843         FileEntryVector::iterator itFirstUnversionedEntry;\r
1844         itFirstUnversionedEntry = std::partition(m_arStatusArray.begin(), m_arStatusArray.end(), IsEntryVersioned);\r
1845         if (m_bUnversionedLast)\r
1846         {\r
1847                 // We partition the list of items so that it's arrange with all the versioned items first\r
1848                 // then all the unversioned items afterwards.\r
1849                 // Then we sort the versioned part of this, so that we can do quick look-ups in it\r
1850                 std::sort(m_arStatusArray.begin(), itFirstUnversionedEntry, EntryPathCompareNoCase);\r
1851                 // Also sort the unversioned section, to make the list look nice...\r
1852                 std::sort(itFirstUnversionedEntry, m_arStatusArray.end(), EntryPathCompareNoCase);\r
1853         }\r
1854 \r
1855         // now gather some statistics\r
1856         m_nUnversioned = 0;\r
1857         m_nNormal = 0;\r
1858         m_nModified = 0;\r
1859         m_nAdded = 0;\r
1860         m_nDeleted = 0;\r
1861         m_nConflicted = 0;\r
1862         m_nTotal = 0;\r
1863         m_nSelected = 0;\r
1864         for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
1865         {\r
1866                 const FileEntry * entry = m_arStatusArray[i];\r
1867                 if (entry)\r
1868                 {\r
1869                         switch (entry->status)\r
1870                         {\r
1871                         case git_wc_status_normal:\r
1872                                 m_nNormal++;\r
1873                                 break;\r
1874                         case git_wc_status_added:\r
1875                                 m_nAdded++;\r
1876                                 break;\r
1877                         case git_wc_status_missing:\r
1878                         case git_wc_status_deleted:\r
1879                                 m_nDeleted++;\r
1880                                 break;\r
1881                         case git_wc_status_replaced:\r
1882                         case git_wc_status_modified:\r
1883                         case git_wc_status_merged:\r
1884                                 m_nModified++;\r
1885                                 break;\r
1886                         case git_wc_status_conflicted:\r
1887                         case git_wc_status_obstructed:\r
1888                                 m_nConflicted++;\r
1889                                 break;\r
1890                         case git_wc_status_ignored:\r
1891                                 m_nUnversioned++;\r
1892                                 break;\r
1893                         default:\r
1894 #if 0\r
1895                                 {\r
1896                                         if (GitStatus::IsImportant(entry->remotestatus))\r
1897                                                 break;\r
1898                                         m_nUnversioned++;\r
1899                                         // If an entry is in an unversioned folder, we don't have to do an expensive array search\r
1900                                         // to find out if it got case-renamed: an unversioned folder can't have versioned files\r
1901                                         // But nested folders are also considered to be in unversioned folders, we have to do the\r
1902                                         // check in that case too, otherwise we would miss case-renamed folders - they show up\r
1903                                         // as nested folders.\r
1904                                         if (((!entry->inunversionedfolder)||(entry->isNested))&&(m_bUnversionedLast))\r
1905                                         {\r
1906                                                 // check if the unversioned item is just\r
1907                                                 // a file differing in case but still versioned\r
1908                                                 FileEntryVector::iterator itMatchingItem;\r
1909                                                 if(std::binary_search(m_arStatusArray.begin(), itFirstUnversionedEntry, entry, EntryPathCompareNoCase))\r
1910                                                 {\r
1911                                                         // We've confirmed that there *is* a matching file\r
1912                                                         // Find its exact location\r
1913                                                         FileEntryVector::iterator itMatchingItem;\r
1914                                                         itMatchingItem = std::lower_bound(m_arStatusArray.begin(), itFirstUnversionedEntry, entry, EntryPathCompareNoCase);\r
1915 \r
1916                                                         // adjust the case of the filename\r
1917                                                         if (MoveFileEx(entry->path.GetWinPath(), (*itMatchingItem)->path.GetWinPath(), MOVEFILE_REPLACE_EXISTING))\r
1918                                                         {\r
1919                                                                 // We successfully adjusted the case in the filename. But there is now a file with status 'missing'\r
1920                                                                 // in the array, because that's the status of the file before we adjusted the case.\r
1921                                                                 // We have to refetch the status of that file.\r
1922                                                                 // Since fetching the status of single files/directories is very expensive and there can be\r
1923                                                                 // multiple case-renames here, we just set a flag and refetch the status at the end from scratch.\r
1924                                                                 bRefetchStatus = true;\r
1925                                                                 DeleteItem(i);\r
1926                                                                 m_arStatusArray.erase(m_arStatusArray.begin()+i);\r
1927                                                                 delete entry;\r
1928                                                                 i--;\r
1929                                                                 m_nUnversioned--;\r
1930                                                                 // now that we removed an unversioned item from the array, find the first unversioned item in the 'new'\r
1931                                                                 // list again.\r
1932                                                                 itFirstUnversionedEntry = std::partition(m_arStatusArray.begin(), m_arStatusArray.end(), IsEntryVersioned);\r
1933                                                         }\r
1934                                                         break;\r
1935                                                 }\r
1936                                         }\r
1937                                 }\r
1938 #endif\r
1939                                 break;\r
1940                         } // switch (entry->status)\r
1941                 } // if (entry)\r
1942         } // for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
1943         return !bRefetchStatus;\r
1944 }\r
1945 \r
1946 void CGitStatusListCtrl::GetMinMaxRevisions(git_revnum_t& rMin, git_revnum_t& rMax, bool bShownOnly, bool bCheckedOnly)\r
1947 {\r
1948 #if 0\r
1949         Locker lock(m_critSec);\r
1950         rMin = LONG_MAX;\r
1951         rMax = 0;\r
1952 \r
1953         if ((bShownOnly)||(bCheckedOnly))\r
1954         {\r
1955                 for (int i=0; i<GetItemCount(); ++i)\r
1956                 {\r
1957                         const FileEntry * entry = GetListEntry(i);\r
1958 \r
1959                         if ((entry)&&(entry->last_commit_rev))\r
1960                         {\r
1961                                 if ((!bCheckedOnly)||(entry->IsChecked()))\r
1962                                 {\r
1963                                         if (entry->last_commit_rev >= 0)\r
1964                                         {\r
1965                                                 rMin = min(rMin, entry->last_commit_rev);\r
1966                                                 rMax = max(rMax, entry->last_commit_rev);\r
1967                                         }\r
1968                                 }\r
1969                         }\r
1970                 }\r
1971         }\r
1972         else\r
1973         {\r
1974                 for (int i=0; i < (int)m_arStatusArray.size(); ++i)\r
1975                 {\r
1976                         const FileEntry * entry = m_arStatusArray[i];\r
1977                         if ((entry)&&(entry->last_commit_rev))\r
1978                         {\r
1979                                 if (entry->last_commit_rev >= 0)\r
1980                                 {\r
1981                                         rMin = min(rMin, entry->last_commit_rev);\r
1982                                         rMax = max(rMax, entry->last_commit_rev);\r
1983                                 }\r
1984                         }\r
1985                 }\r
1986         }\r
1987         if (rMin == LONG_MAX)\r
1988                 rMin = 0;\r
1989 #endif\r
1990 }\r
1991 \r
1992 int CGitStatusListCtrl::GetGroupFromPoint(POINT * ppt)\r
1993 {\r
1994         // the point must be relative to the upper left corner of the control\r
1995 \r
1996         if (ppt == NULL)\r
1997                 return -1;\r
1998         if (!IsGroupViewEnabled())\r
1999                 return -1;\r
2000 \r
2001         POINT pt = *ppt;\r
2002         pt.x = 10;\r
2003         UINT flags = 0;\r
2004         int nItem = -1;\r
2005         RECT rc;\r
2006         GetWindowRect(&rc);\r
2007         while (((flags & LVHT_BELOW) == 0)&&(pt.y < rc.bottom))\r
2008         {\r
2009                 nItem = HitTest(pt, &flags);\r
2010                 if ((flags & LVHT_ONITEM)||(flags & LVHT_EX_GROUP_HEADER))\r
2011                 {\r
2012                         // the first item below the point\r
2013 \r
2014                         // check if the point is too much right (i.e. if the point\r
2015                         // is farther to the right than the width of the item)\r
2016                         RECT r;\r
2017                         GetItemRect(nItem, &r, LVIR_LABEL);\r
2018                         if (ppt->x > r.right)\r
2019                                 return -1;\r
2020 \r
2021                         LVITEM lv = {0};\r
2022                         lv.mask = LVIF_GROUPID;\r
2023                         lv.iItem = nItem;\r
2024                         GetItem(&lv);\r
2025                         int groupID = lv.iGroupId;\r
2026                         // now we search upwards and check if the item above this one\r
2027                         // belongs to another group. If it belongs to the same group,\r
2028                         // we're not over a group header\r
2029                         while (pt.y >= 0)\r
2030                         {\r
2031                                 pt.y -= 2;\r
2032                                 nItem = HitTest(pt, &flags);\r
2033                                 if ((flags & LVHT_ONITEM)&&(nItem >= 0))\r
2034                                 {\r
2035                                         // the first item below the point\r
2036                                         LVITEM lv = {0};\r
2037                                         lv.mask = LVIF_GROUPID;\r
2038                                         lv.iItem = nItem;\r
2039                                         GetItem(&lv);\r
2040                                         if (lv.iGroupId != groupID)\r
2041                                                 return groupID;\r
2042                                         else\r
2043                                                 return -1;\r
2044                                 }\r
2045                         }\r
2046                         if (pt.y < 0)\r
2047                                 return groupID;\r
2048                         return -1;\r
2049                 }\r
2050                 pt.y += 2;\r
2051         };\r
2052         return -1;\r
2053 }\r
2054 \r
2055 void CGitStatusListCtrl::OnContextMenuGroup(CWnd * /*pWnd*/, CPoint point)\r
2056 {\r
2057         POINT clientpoint = point;\r
2058         ScreenToClient(&clientpoint);\r
2059         if ((IsGroupViewEnabled())&&(GetGroupFromPoint(&clientpoint) >= 0))\r
2060         {\r
2061                 CMenu popup;\r
2062                 if (popup.CreatePopupMenu())\r
2063                 {\r
2064                         CString temp;\r
2065                         temp.LoadString(IDS_STATUSLIST_CHECKGROUP);\r
2066                         popup.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CHECKGROUP, temp);\r
2067                         temp.LoadString(IDS_STATUSLIST_UNCHECKGROUP);\r
2068                         popup.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_UNCHECKGROUP, temp);\r
2069                         int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
2070                         bool bCheck = false;\r
2071                         switch (cmd)\r
2072                         {\r
2073                         case IDSVNLC_CHECKGROUP:\r
2074                                 bCheck = true;\r
2075                                 // fall through here\r
2076                         case IDSVNLC_UNCHECKGROUP:\r
2077                                 {\r
2078                                         int group = GetGroupFromPoint(&clientpoint);\r
2079                                         // go through all items and check/uncheck those assigned to the group\r
2080                                         // but block the OnLvnItemChanged handler\r
2081                                         m_bBlock = true;\r
2082                                         LVITEM lv;\r
2083                                         for (int i=0; i<GetItemCount(); ++i)\r
2084                                         {\r
2085                                                 SecureZeroMemory(&lv, sizeof(LVITEM));\r
2086                                                 lv.mask = LVIF_GROUPID;\r
2087                                                 lv.iItem = i;\r
2088                                                 GetItem(&lv);\r
2089                                                 if (lv.iGroupId == group)\r
2090                                                 {\r
2091                                                         FileEntry * entry = GetListEntry(i);\r
2092                                                         if (entry)\r
2093                                                         {\r
2094                                                                 bool bOldCheck = !!GetCheck(i);\r
2095                                                                 SetEntryCheck(entry, i, bCheck);\r
2096                                                                 if (bCheck != bOldCheck)\r
2097                                                                 {\r
2098                                                                         if (bCheck)\r
2099                                                                                 m_nSelected++;\r
2100                                                                         else\r
2101                                                                                 m_nSelected--;\r
2102                                                                 }\r
2103                                                         }\r
2104                                                 }\r
2105                                         }\r
2106                                         GetStatisticsString();\r
2107                                         NotifyCheck();\r
2108                                         m_bBlock = false;\r
2109                                 }\r
2110                                 break;\r
2111                         }\r
2112                 }\r
2113         }\r
2114 }\r
2115 \r
2116 void CGitStatusListCtrl::OnContextMenuList(CWnd * pWnd, CPoint point)\r
2117 {\r
2118 #if 0\r
2119         WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
2120 \r
2121         bool XPorLater = false;\r
2122         OSVERSIONINFOEX inf;\r
2123         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
2124         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
2125         GetVersionEx((OSVERSIONINFO *)&inf);\r
2126         WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
2127         if (fullver >= 0x0501)\r
2128                 XPorLater = true;\r
2129         bool bShift = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);\r
2130 \r
2131         int selIndex = GetSelectionMark();\r
2132         if ((point.x == -1) && (point.y == -1))\r
2133         {\r
2134                 CRect rect;\r
2135                 GetItemRect(selIndex, &rect, LVIR_LABEL);\r
2136                 ClientToScreen(&rect);\r
2137                 point = rect.CenterPoint();\r
2138         }\r
2139         if ((GetSelectedCount() == 0)&&(XPorLater)&&(m_bHasCheckboxes))\r
2140         {\r
2141                 // nothing selected could mean the context menu is requested for\r
2142                 // a group header\r
2143                 OnContextMenuGroup(pWnd, point);\r
2144         }\r
2145         else if (selIndex >= 0)\r
2146         {\r
2147                 FileEntry * entry = GetListEntry(selIndex);\r
2148                 ASSERT(entry != NULL);\r
2149                 if (entry == NULL)\r
2150                         return;\r
2151                 const CTGitPath& filepath = entry->path;\r
2152                 git_wc_status_kind wcStatus = entry->status;\r
2153                 // entry is selected, now show the popup menu\r
2154                 Locker lock(m_critSec);\r
2155                 CIconMenu popup;\r
2156                 CMenu changelistSubMenu;\r
2157                 CMenu ignoreSubMenu;\r
2158                 if (popup.CreatePopupMenu())\r
2159                 {\r
2160                         if (wcStatus >= git_wc_status_normal)\r
2161                         {\r
2162                                 if (m_dwContextMenus & SVNSLC_POPCOMPAREWITHBASE)\r
2163                                 {\r
2164                                         popup.AppendMenuIcon(IDSVNLC_COMPARE, IDS_LOG_COMPAREWITHBASE, IDI_DIFF);\r
2165                                         popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
2166                                 }\r
2167 \r
2168                                 if (GetSelectedCount() == 1)\r
2169                                 {\r
2170                                         bool bEntryAdded = false;\r
2171                                         if (entry->remotestatus <= git_wc_status_normal)\r
2172                                         {\r
2173                                                 if (wcStatus > git_wc_status_normal)\r
2174                                                 {\r
2175                                                         if ((m_dwContextMenus & SVNSLC_POPGNUDIFF)&&(wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing))\r
2176                                                         {\r
2177                                                                 popup.AppendMenuIcon(IDSVNLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
2178                                                                 bEntryAdded = true;\r
2179                                                         }\r
2180                                                 }\r
2181                                                 if (wcStatus > git_wc_status_normal)\r
2182                                                 {\r
2183                                                         if (m_dwContextMenus & SVNSLC_POPCOMMIT)\r
2184                                                         {\r
2185                                                                 popup.AppendMenuIcon(IDSVNLC_COMMIT, IDS_STATUSLIST_CONTEXT_COMMIT, IDI_COMMIT);\r
2186                                                                 popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
2187                                                                 bEntryAdded = true;\r
2188                                                         }\r
2189                                                 }\r
2190                                         }\r
2191                                         else if (wcStatus != git_wc_status_deleted)\r
2192                                         {\r
2193                                                 if (m_dwContextMenus & SVNSLC_POPCOMPARE)\r
2194                                                 {\r
2195                                                         popup.AppendMenuIcon(IDSVNLC_COMPAREWC, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
2196                                                         popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
2197                                                         bEntryAdded = true;\r
2198                                                 }\r
2199                                                 if (m_dwContextMenus & SVNSLC_POPGNUDIFF)\r
2200                                                 {\r
2201                                                         popup.AppendMenuIcon(IDSVNLC_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
2202                                                         bEntryAdded = true;\r
2203                                                 }\r
2204                                         }\r
2205                                         if (bEntryAdded)\r
2206                                                 popup.AppendMenu(MF_SEPARATOR);\r
2207                                 }\r
2208                                 else if (GetSelectedCount() > 1)\r
2209                                 {\r
2210                                         if (m_dwContextMenus & SVNSLC_POPCOMMIT)\r
2211                                         {\r
2212                                                 popup.AppendMenuIcon(IDSVNLC_COMMIT, IDS_STATUSLIST_CONTEXT_COMMIT, IDI_COMMIT);\r
2213                                                 popup.SetDefaultItem(IDSVNLC_COMPARE, FALSE);\r
2214                                         }\r
2215                                 }\r
2216                         }\r
2217                         if (GetSelectedCount() > 0)\r
2218                         {\r
2219                                 if ((GetSelectedCount() == 2)&&(m_dwContextMenus & SVNSLC_POPREPAIRMOVE))\r
2220                                 {\r
2221                                         POSITION pos = GetFirstSelectedItemPosition();\r
2222                                         int index = GetNextSelectedItem(pos);\r
2223                                         if (index >= 0)\r
2224                                         {\r
2225                                                 FileEntry * entry = GetListEntry(index);\r
2226                                                 git_wc_status_kind status1 = git_wc_status_none;\r
2227                                                 git_wc_status_kind status2 = git_wc_status_none;\r
2228                                                 if (entry)\r
2229                                                         status1 = entry->status;\r
2230                                                 index = GetNextSelectedItem(pos);\r
2231                                                 if (index >= 0)\r
2232                                                 {\r
2233                                                         entry = GetListEntry(index);\r
2234                                                         if (entry)\r
2235                                                                 status2 = entry->status;\r
2236                                                         if ((status1 == git_wc_status_missing && status2 == git_wc_status_unversioned) ||\r
2237                                                                 (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned))\r
2238                                                         {\r
2239                                                                 popup.AppendMenuIcon(IDSVNLC_REPAIRMOVE, IDS_STATUSLIST_CONTEXT_REPAIRMOVE);\r
2240                                                         }\r
2241                                                 }\r
2242                                         }\r
2243                                 }\r
2244                                 if (wcStatus > git_wc_status_normal)\r
2245                                 {\r
2246                                         if (m_dwContextMenus & SVNSLC_POPREVERT)\r
2247                                         {\r
2248                                                 // reverting missing folders is not possible\r
2249                                                 if (!entry->IsFolder() || (wcStatus != git_wc_status_missing))\r
2250                                                 {\r
2251                                                         popup.AppendMenuIcon(IDSVNLC_REVERT, IDS_MENUREVERT, IDI_REVERT);\r
2252                                                 }\r
2253                                         }\r
2254                                 }\r
2255                                 if (entry->remotestatus > git_wc_status_normal)\r
2256                                 {\r
2257                                         if (m_dwContextMenus & SVNSLC_POPUPDATE)\r
2258                                         {\r
2259                                                 popup.AppendMenuIcon(IDSVNLC_UPDATE, IDS_MENUUPDATE, IDI_UPDATE);\r
2260                                         }\r
2261                                 }\r
2262                         }\r
2263                         if ((GetSelectedCount() == 1)&&(wcStatus >= git_wc_status_normal)\r
2264                                 &&(wcStatus != git_wc_status_ignored))\r
2265                         {\r
2266                                 if (m_dwContextMenus & SVNSLC_POPSHOWLOG)\r
2267                                 {\r
2268                                         popup.AppendMenuIcon(IDSVNLC_LOG, IDS_REPOBROWSE_SHOWLOG, IDI_LOG);\r
2269                                 }\r
2270                                 if (m_dwContextMenus & SVNSLC_POPBLAME)\r
2271                                 {\r
2272                                         popup.AppendMenuIcon(IDSVNLC_BLAME, IDS_MENUBLAME, IDI_BLAME);\r
2273                                 }\r
2274                         }\r
2275                         if ((wcStatus != git_wc_status_deleted)&&(wcStatus != git_wc_status_missing) && (GetSelectedCount() == 1))\r
2276                         {\r
2277                                 if (m_dwContextMenus & SVNSLC_POPOPEN)\r
2278                                 {\r
2279                                         popup.AppendMenuIcon(IDSVNLC_OPEN, IDS_REPOBROWSE_OPEN, IDI_OPEN);\r
2280                                         popup.AppendMenuIcon(IDSVNLC_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);\r
2281                                 }\r
2282                                 if (m_dwContextMenus & SVNSLC_POPEXPLORE)\r
2283                                 {\r
2284                                         popup.AppendMenuIcon(IDSVNLC_EXPLORE, IDS_STATUSLIST_CONTEXT_EXPLORE, IDI_EXPLORER);\r
2285                                 }\r
2286                         }\r
2287                         if (GetSelectedCount() > 0)\r
2288                         {\r
2289                                 if (((wcStatus == git_wc_status_unversioned)||(wcStatus == git_wc_status_ignored))&&(m_dwContextMenus & SVNSLC_POPDELETE))\r
2290                                 {\r
2291                                         popup.AppendMenuIcon(IDGitLC_DELETE, IDS_MENUREMOVE, IDI_DELETE);\r
2292                                 }\r
2293                                 if ((wcStatus != Git_wc_status_unversioned)&&(wcStatus != git_wc_status_ignored)&&(wcStatus != Git_wc_status_deleted)&&(wcStatus != Git_wc_status_added)&&(m_dwContextMenus & GitSLC_POPDELETE))\r
2294                                 {\r
2295                                         if (bShift)\r
2296                                                 popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVEKEEP, IDI_DELETE);\r
2297                                         else\r
2298                                                 popup.AppendMenuIcon(IDGitLC_REMOVE, IDS_MENUREMOVE, IDI_DELETE);\r
2299                                 }\r
2300                                 if ((wcStatus == git_wc_status_unversioned)||(wcStatus == git_wc_status_deleted))\r
2301                                 {\r
2302                                         if (m_dwContextMenus & SVNSLC_POPADD)\r
2303                                         {\r
2304                                                 if ( entry->IsFolder() )\r
2305                                                 {\r
2306                                                         popup.AppendMenuIcon(IDSVNLC_ADD_RECURSIVE, IDS_STATUSLIST_CONTEXT_ADD_RECURSIVE, IDI_ADD);\r
2307                                                 }\r
2308                                                 else\r
2309                                                 {\r
2310                                                         popup.AppendMenuIcon(IDSVNLC_ADD, IDS_STATUSLIST_CONTEXT_ADD, IDI_ADD);\r
2311                                                 }\r
2312                                         }\r
2313                                 }\r
2314                                 if ( (wcStatus == git_wc_status_unversioned) || (wcStatus == git_wc_status_deleted) )\r
2315                                 {\r
2316                                         if (m_dwContextMenus & SVNSLC_POPIGNORE)\r
2317                                         {\r
2318                                                 CTSVNPathList ignorelist;\r
2319                                                 FillListOfSelectedItemPaths(ignorelist);\r
2320                                                 // check if all selected entries have the same extension\r
2321                                                 bool bSameExt = true;\r
2322                                                 CString sExt;\r
2323                                                 for (int i=0; i<ignorelist.GetCount(); ++i)\r
2324                                                 {\r
2325                                                         if (sExt.IsEmpty() && (i==0))\r
2326                                                                 sExt = ignorelist[i].GetFileExtension();\r
2327                                                         else if (sExt.CompareNoCase(ignorelist[i].GetFileExtension())!=0)\r
2328                                                                 bSameExt = false;\r
2329                                                 }\r
2330                                                 if (bSameExt)\r
2331                                                 {\r
2332                                                         if (ignoreSubMenu.CreateMenu())\r
2333                                                         {\r
2334                                                                 CString ignorepath;\r
2335                                                                 if (ignorelist.GetCount()==1)\r
2336                                                                         ignorepath = ignorelist[0].GetFileOrDirectoryName();\r
2337                                                                 else\r
2338                                                                         ignorepath.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());\r
2339                                                                 ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_IGNORE, ignorepath);\r
2340                                                                 ignorepath = _T("*")+sExt;\r
2341                                                                 ignoreSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_IGNOREMASK, ignorepath);\r
2342                                                                 CString temp;\r
2343                                                                 temp.LoadString(IDS_MENUIGNORE);\r
2344                                                                 popup.InsertMenu((UINT)-1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)ignoreSubMenu.m_hMenu, temp);\r
2345                                                         }\r
2346                                                 }\r
2347                                                 else\r
2348                                                 {\r
2349                                                         CString temp;\r
2350                                                         if (ignorelist.GetCount()==1)\r
2351                                                         {\r
2352                                                                 temp.LoadString(IDS_MENUIGNORE);\r
2353                                                         }\r
2354                                                         else\r
2355                                                         {\r
2356                                                                 temp.Format(IDS_MENUIGNOREMULTIPLE, ignorelist.GetCount());\r
2357                                                         }\r
2358                                                         popup.AppendMenuIcon(IDSVNLC_IGNORE, temp, IDI_IGNORE);\r
2359                                                 }\r
2360                                         }\r
2361                                 }\r
2362                         }\r
2363                         if (((wcStatus == git_wc_status_conflicted)||(entry->isConflicted)))\r
2364                         {\r
2365                                 if ((m_dwContextMenus & SVNSLC_POPCONFLICT)||(m_dwContextMenus & SVNSLC_POPRESOLVE))\r
2366                                         popup.AppendMenu(MF_SEPARATOR);\r
2367 \r
2368                                 if ((m_dwContextMenus & SVNSLC_POPCONFLICT)&&(entry->textstatus == git_wc_status_conflicted))\r
2369                                 {\r
2370                                         popup.AppendMenuIcon(IDSVNLC_EDITCONFLICT, IDS_MENUCONFLICT, IDI_CONFLICT);\r
2371                                 }\r
2372                                 if (m_dwContextMenus & SVNSLC_POPRESOLVE)\r
2373                                 {\r
2374                                         popup.AppendMenuIcon(IDSVNLC_RESOLVECONFLICT, IDS_STATUSLIST_CONTEXT_RESOLVED, IDI_RESOLVE);\r
2375                                 }\r
2376                                 if ((m_dwContextMenus & SVNSLC_POPRESOLVE)&&(entry->textstatus == git_wc_status_conflicted))\r
2377                                 {\r
2378                                         popup.AppendMenuIcon(IDSVNLC_RESOLVETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS, IDI_RESOLVE);\r
2379                                         popup.AppendMenuIcon(IDSVNLC_RESOLVEMINE, IDS_SVNPROGRESS_MENUUSEMINE, IDI_RESOLVE);\r
2380                                 }\r
2381                         }\r
2382                         if (GetSelectedCount() > 0)\r
2383                         {\r
2384                                 if ((!entry->IsFolder())&&(wcStatus >= git_wc_status_normal)\r
2385                                         &&(wcStatus!=git_wc_status_missing)&&(wcStatus!=git_wc_status_deleted)\r
2386                                         &&(wcStatus!=git_wc_status_added))\r
2387                                 {\r
2388                                         popup.AppendMenu(MF_SEPARATOR);\r
2389                                         if ((entry->lock_token.IsEmpty())&&(!entry->IsFolder()))\r
2390                                         {\r
2391                                                 if (m_dwContextMenus & SVNSLC_POPLOCK)\r
2392                                                 {\r
2393                                                         popup.AppendMenuIcon(IDSVNLC_LOCK, IDS_MENU_LOCK, IDI_LOCK);\r
2394                                                 }\r
2395                                         }\r
2396                                         if ((!entry->lock_token.IsEmpty())&&(!entry->IsFolder()))\r
2397                                         {\r
2398                                                 if (m_dwContextMenus & SVNSLC_POPUNLOCK)\r
2399                                                 {\r
2400                                                         popup.AppendMenuIcon(IDSVNLC_UNLOCK, IDS_MENU_UNLOCK, IDI_UNLOCK);\r
2401                                                 }\r
2402                                         }\r
2403                                 }\r
2404                                 if ((!entry->IsFolder())&&((!entry->lock_token.IsEmpty())||(!entry->lock_remotetoken.IsEmpty())))\r
2405                                 {\r
2406                                         if (m_dwContextMenus & SVNSLC_POPUNLOCKFORCE)\r
2407                                         {\r
2408                                                 popup.AppendMenuIcon(IDSVNLC_UNLOCKFORCE, IDS_MENU_UNLOCKFORCE, IDI_UNLOCK);\r
2409                                         }\r
2410                                 }\r
2411 \r
2412                                 if (wcStatus != git_wc_status_missing && wcStatus != git_wc_status_deleted &&wcStatus!=git_wc_status_unversioned)\r
2413                                 {\r
2414                                         popup.AppendMenu(MF_SEPARATOR);\r
2415                                         popup.AppendMenuIcon(IDSVNLC_PROPERTIES, IDS_STATUSLIST_CONTEXT_PROPERTIES, IDI_PROPERTIES);\r
2416                                 }\r
2417                                 popup.AppendMenu(MF_SEPARATOR);\r
2418                                 popup.AppendMenuIcon(IDSVNLC_COPY, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);\r
2419                                 popup.AppendMenuIcon(IDSVNLC_COPYEXT, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);\r
2420                                 if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS)&&(XPorLater)\r
2421                                         &&(wcStatus != git_wc_status_unversioned)&&(wcStatus != git_wc_status_none))\r
2422                                 {\r
2423                                         popup.AppendMenu(MF_SEPARATOR);\r
2424                                         // changelist commands\r
2425                                         size_t numChangelists = GetNumberOfChangelistsInSelection();\r
2426                                         if (numChangelists > 0)\r
2427                                         {\r
2428                                                 popup.AppendMenuIcon(IDSVNLC_REMOVEFROMCS, IDS_STATUSLIST_CONTEXT_REMOVEFROMCS);\r
2429                                         }\r
2430                                         if ((!entry->IsFolder())&&(changelistSubMenu.CreateMenu()))\r
2431                                         {\r
2432                                                 CString temp;\r
2433                                                 temp.LoadString(IDS_STATUSLIST_CONTEXT_CREATECS);\r
2434                                                 changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATECS, temp);\r
2435 \r
2436                                                 if (entry->changelist.Compare(SVNSLC_IGNORECHANGELIST))\r
2437                                                 {\r
2438                                                         changelistSubMenu.AppendMenu(MF_SEPARATOR);\r
2439                                                         changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, IDSVNLC_CREATEIGNORECS, SVNSLC_IGNORECHANGELIST);\r
2440                                                 }\r
2441 \r
2442                                                 if (m_changelists.size() > 0)\r
2443                                                 {\r
2444                                                         // find the changelist names\r
2445                                                         bool bNeedSeparator = true;\r
2446                                                         int cmdID = IDSVNLC_MOVETOCS;\r
2447                                                         for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)\r
2448                                                         {\r
2449                                                                 if ((entry->changelist.Compare(it->first))&&(it->first.Compare(SVNSLC_IGNORECHANGELIST)))\r
2450                                                                 {\r
2451                                                                         if (bNeedSeparator)\r
2452                                                                         {\r
2453                                                                                 changelistSubMenu.AppendMenu(MF_SEPARATOR);\r
2454                                                                                 bNeedSeparator = false;\r
2455                                                                         }\r
2456                                                                         changelistSubMenu.AppendMenu(MF_STRING | MF_ENABLED, cmdID, it->first);\r
2457                                                                         cmdID++;\r
2458                                                                 }\r
2459                                                         }\r
2460                                                 }\r
2461                                                 temp.LoadString(IDS_STATUSLIST_CONTEXT_MOVETOCS);\r
2462                                                 popup.AppendMenu(MF_POPUP|MF_STRING, (UINT_PTR)changelistSubMenu.GetSafeHmenu(), temp);\r
2463                                         }\r
2464                                 }\r
2465                         }\r
2466 \r
2467                         int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
2468                         m_bBlock = TRUE;\r
2469                         AfxGetApp()->DoWaitCursor(1);\r
2470                         int iItemCountBeforeMenuCmd = GetItemCount();\r
2471                         bool bForce = false;\r
2472                         switch (cmd)\r
2473                         {\r
2474                         case IDSVNLC_COPY:\r
2475                                 CopySelectedEntriesToClipboard(0);\r
2476                                 break;\r
2477                         case IDSVNLC_COPYEXT:\r
2478                                 CopySelectedEntriesToClipboard((DWORD)-1);\r
2479                                 break;\r
2480                         case IDSVNLC_PROPERTIES:\r
2481                                 {\r
2482                                         CTSVNPathList targetList;\r
2483                                         FillListOfSelectedItemPaths(targetList);\r
2484                                         CEditPropertiesDlg dlg;\r
2485                                         dlg.SetPathList(targetList);\r
2486                                         dlg.DoModal();\r
2487                                         if (dlg.HasChanged())\r
2488                                         {\r
2489                                                 // since the user might have changed/removed/added\r
2490                                                 // properties recursively, we don't really know\r
2491                                                 // which items have changed their status.\r
2492                                                 // So tell the parent to do a refresh.\r
2493                                                 CWnd* pParent = GetParent();\r
2494                                                 if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
2495                                                 {\r
2496                                                         pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
2497                                                 }\r
2498                                         }\r
2499                                 }\r
2500                                 break;\r
2501                         case IDSVNLC_COMMIT:\r
2502                                 {\r
2503                                         CTSVNPathList targetList;\r
2504                                         FillListOfSelectedItemPaths(targetList);\r
2505                                         CTSVNPath tempFile = CTempFiles::Instance().GetTempFilePath(false);\r
2506                                         VERIFY(targetList.WriteToFile(tempFile.GetWinPathString()));\r
2507                                         CString commandline = CPathUtils::GetAppDirectory();\r
2508                                         commandline += _T("TortoiseProc.exe /command:commit /pathfile:\"");\r
2509                                         commandline += tempFile.GetWinPathString();\r
2510                                         commandline += _T("\"");\r
2511                                         commandline += _T(" /deletepathfile");\r
2512                                         CAppUtils::LaunchApplication(commandline, NULL, false);\r
2513                                 }\r
2514                                 break;\r
2515                         case IDSVNLC_REVERT:\r
2516                                 {\r
2517                                         // If at least one item is not in the status "added"\r
2518                                         // we ask for a confirmation\r
2519                                         BOOL bConfirm = FALSE;\r
2520                                         POSITION pos = GetFirstSelectedItemPosition();\r
2521                                         int index;\r
2522                                         while ((index = GetNextSelectedItem(pos)) >= 0)\r
2523                                         {\r
2524                                                 FileEntry * fentry = GetListEntry(index);\r
2525                                                 if (fentry->textstatus != git_wc_status_added)\r
2526                                                 {\r
2527                                                         bConfirm = TRUE;\r
2528                                                         break;\r
2529                                                 }\r
2530                                         }       \r
2531 \r
2532                                         CString str;\r
2533                                         str.Format(IDS_PROC_WARNREVERT,GetSelectedCount());\r
2534 \r
2535                                         if (!bConfirm || CMessageBox::Show(this->m_hWnd, str, _T("TortoiseSVN"), MB_YESNO | MB_ICONQUESTION)==IDYES)\r
2536                                         {\r
2537                                                 CTSVNPathList targetList;\r
2538                                                 FillListOfSelectedItemPaths(targetList);\r
2539 \r
2540                                                 // make sure that the list is reverse sorted, so that\r
2541                                                 // children are removed before any parents\r
2542                                                 targetList.SortByPathname(true);\r
2543 \r
2544                                                 SVN git;\r
2545 \r
2546                                                 // put all reverted files in the trashbin, except the ones with 'added'\r
2547                                                 // status because they are not restored by the revert.\r
2548                                                 CTSVNPathList delList;\r
2549                                                 POSITION pos = GetFirstSelectedItemPosition();\r
2550                                                 int index;\r
2551                                                 while ((index = GetNextSelectedItem(pos)) >= 0)\r
2552                                                 {\r
2553                                                         FileEntry * entry = GetListEntry(index);\r
2554                                                         if (entry->status != git_wc_status_added)\r
2555                                                                 delList.AddPath(entry->GetPath());\r
2556                                                 }\r
2557                                                 if (DWORD(CRegDWORD(_T("Software\\TortoiseSVN\\RevertWithRecycleBin"), TRUE)))\r
2558                                                         delList.DeleteAllFiles(true);\r
2559 \r
2560                                                 if (!git.Revert(targetList, CStringArray(), FALSE))\r
2561                                                 {\r
2562                                                         CMessageBox::Show(this->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2563                                                 }\r
2564                                                 else\r
2565                                                 {\r
2566                                                         // since the entries got reverted we need to remove\r
2567                                                         // them from the list too, if no remote changes are shown,\r
2568                                                         // if the unmodified files are not shown\r
2569                                                         // and if the item is not part of a changelist\r
2570                                                         POSITION pos;\r
2571                                                         SetRedraw(FALSE);\r
2572                                                         while ((pos = GetFirstSelectedItemPosition())!=0)\r
2573                                                         {\r
2574                                                                 int index;\r
2575                                                                 index = GetNextSelectedItem(pos);\r
2576                                                                 FileEntry * fentry = m_arStatusArray[m_arListArray[index]];\r
2577                                                                 if ( fentry->IsFolder() )\r
2578                                                                 {\r
2579                                                                         // refresh!\r
2580                                                                         CWnd* pParent = GetParent();\r
2581                                                                         if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
2582                                                                         {\r
2583                                                                                 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
2584                                                                         }\r
2585                                                                         break;\r
2586                                                                 }\r
2587 \r
2588                                                                 BOOL bAdded = (fentry->textstatus == git_wc_status_added);\r
2589                                                                 fentry->status = git_wc_status_normal;\r
2590                                                                 fentry->propstatus = git_wc_status_normal;\r
2591                                                                 fentry->textstatus = git_wc_status_normal;\r
2592                                                                 fentry->copied = false;\r
2593                                                                 fentry->isConflicted = false;\r
2594                                                                 if ((fentry->GetChangeList().IsEmpty()&&(fentry->remotestatus <= git_wc_status_normal))||(m_dwShow & SVNSLC_SHOWNORMAL))\r
2595                                                                 {\r
2596                                                                         if ( bAdded )\r
2597                                                                         {\r
2598                                                                                 // reverting added items makes them unversioned, not 'normal'\r
2599                                                                                 if (fentry->IsFolder())\r
2600                                                                                         fentry->propstatus = git_wc_status_none;\r
2601                                                                                 else\r
2602                                                                                         fentry->propstatus = git_wc_status_unversioned;\r
2603                                                                                 fentry->status = git_wc_status_unversioned;\r
2604                                                                                 fentry->textstatus = git_wc_status_unversioned;\r
2605                                                                                 SetItemState(index, 0, LVIS_SELECTED);\r
2606                                                                                 SetEntryCheck(fentry, index, false);\r
2607                                                                         }\r
2608                                                                         else if ((fentry->switched)||(m_dwShow & SVNSLC_SHOWNORMAL))\r
2609                                                                         {\r
2610                                                                                 SetItemState(index, 0, LVIS_SELECTED);\r
2611                                                                         }\r
2612                                                                         else\r
2613                                                                         {\r
2614                                                                                 m_nTotal--;\r
2615                                                                                 if (GetCheck(index))\r
2616                                                                                         m_nSelected--;\r
2617                                                                                 RemoveListEntry(index);\r
2618                                                                                 Invalidate();\r
2619                                                                         }\r
2620                                                                 }\r
2621                                                                 else\r
2622                                                                 {\r
2623                                                                         SetItemState(index, 0, LVIS_SELECTED);\r
2624                                                                 }\r
2625                                                         }\r
2626                                                         SetRedraw(TRUE);\r
2627                                                         SaveColumnWidths();\r
2628                                                         Show(m_dwShow, 0, m_bShowFolders);\r
2629                                                         NotifyCheck();\r
2630                                                 }\r
2631                                         }\r
2632                                 }\r
2633                                 break;\r
2634                         case IDSVNLC_COMPARE:\r
2635                                 {\r
2636                                         POSITION pos = GetFirstSelectedItemPosition();\r
2637                                         while ( pos )\r
2638                                         {\r
2639                                                 int index = GetNextSelectedItem(pos);\r
2640                                                 StartDiff(index);\r
2641                                         }\r
2642                                 }\r
2643                                 break;\r
2644                         case IDSVNLC_COMPAREWC:\r
2645                                 {\r
2646                                         POSITION pos = GetFirstSelectedItemPosition();\r
2647                                         while ( pos )\r
2648                                         {\r
2649                                                 int index = GetNextSelectedItem(pos);\r
2650                                                 FileEntry * entry = GetListEntry(index);\r
2651                                                 ASSERT(entry != NULL);\r
2652                                                 if (entry == NULL)\r
2653                                                         continue;\r
2654                                                 SVNDiff diff(NULL, m_hWnd, true);\r
2655                                                 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
2656                                                 git_revnum_t baseRev = entry->Revision;\r
2657                                                 diff.DiffFileAgainstBase(\r
2658                                                         entry->path, baseRev, entry->textstatus, entry->propstatus);\r
2659                                         }\r
2660                                 }\r
2661                                 break;\r
2662                         case IDSVNLC_GNUDIFF1:\r
2663                                 {\r
2664                                         SVNDiff diff(NULL, this->m_hWnd, true);\r
2665 \r
2666                                         if (entry->remotestatus <= git_wc_status_normal)\r
2667                                                 CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_BASE, entry->path, SVNRev::REV_WC);\r
2668                                         else\r
2669                                                 CAppUtils::StartShowUnifiedDiff(m_hWnd, entry->path, SVNRev::REV_WC, entry->path, SVNRev::REV_HEAD);\r
2670                                 }\r
2671                                 break;\r
2672                         case IDSVNLC_UPDATE:\r
2673                                 {\r
2674                                         CTSVNPathList targetList;\r
2675                                         FillListOfSelectedItemPaths(targetList);\r
2676                                         bool bAllExist = true;\r
2677                                         for (int i=0; i<targetList.GetCount(); ++i)\r
2678                                         {\r
2679                                                 if (!targetList[i].Exists())\r
2680                                                 {\r
2681                                                         bAllExist = false;\r
2682                                                         break;\r
2683                                                 }\r
2684                                         }\r
2685                                         if (bAllExist)\r
2686                                         {\r
2687                                                 CSVNProgressDlg dlg;\r
2688                                                 dlg.SetCommand(CSVNProgressDlg::SVNProgress_Update);\r
2689                                                 dlg.SetPathList(targetList);\r
2690                                                 dlg.SetRevision(SVNRev::REV_HEAD);\r
2691                                                 dlg.DoModal();\r
2692                                         }\r
2693                                         else\r
2694                                         {\r
2695                                                 CString sTempFile = CTempFiles::Instance().GetTempFilePath(false).GetWinPathString();\r
2696                                                 targetList.WriteToFile(sTempFile, false);\r
2697                                                 CString sCmd;\r
2698                                                 sCmd.Format(_T("\"%s\" /command:update /rev /pathfile:\"%s\" /deletepathfile"),\r
2699                                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)sTempFile);\r
2700 \r
2701                                                 CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2702                                         }\r
2703                                 }\r
2704                                 break;\r
2705                         case IDSVNLC_LOG:\r
2706                                 {\r
2707                                         CString sCmd;\r
2708                                         sCmd.Format(_T("\"%s\" /command:log /path:\"%s\""),\r
2709                                                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), filepath.GetWinPath());\r
2710 \r
2711                                         if (!filepath.IsUrl())\r
2712                                         {\r
2713                                                 sCmd += _T(" /propspath:\"");\r
2714                                                 sCmd += filepath.GetWinPathString();\r
2715                                                 sCmd += _T("\"");\r
2716                                         }       \r
2717 \r
2718                                         CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2719                                 }\r
2720                                 break;\r
2721                         case IDSVNLC_BLAME:\r
2722                                 {\r
2723                                         CString sCmd;\r
2724                                         sCmd.Format(_T("\"%s\" /command:blame /path:\"%s\""),\r
2725                                                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), filepath.GetWinPath());\r
2726 \r
2727                                         if (!filepath.IsUrl())\r
2728                                         {\r
2729                                                 sCmd += _T(" /propspath:\"");\r
2730                                                 sCmd += filepath.GetWinPathString();\r
2731                                                 sCmd += _T("\"");\r
2732                                         }       \r
2733 \r
2734                                         CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2735                                 }\r
2736                                 break;\r
2737                         case IDSVNLC_OPEN:\r
2738                                 {\r
2739                                         int ret = (int)ShellExecute(this->m_hWnd, NULL, filepath.GetWinPath(), NULL, NULL, SW_SHOW);\r
2740                                         if (ret <= HINSTANCE_ERROR)\r
2741                                         {\r
2742                                                 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
2743                                                 cmd += filepath.GetWinPathString();\r
2744                                                 CAppUtils::LaunchApplication(cmd, NULL, false);\r
2745                                         }\r
2746                                 }\r
2747                                 break;\r
2748                         case IDSVNLC_OPENWITH:\r
2749                                 {\r
2750                                         CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
2751                                         cmd += filepath.GetWinPathString() + _T(" ");\r
2752                                         CAppUtils::LaunchApplication(cmd, NULL, false);\r
2753                                 }\r
2754                                 break;\r
2755                         case IDSVNLC_EXPLORE:\r
2756                                 {\r
2757                                         ShellExecute(this->m_hWnd, _T("explore"), filepath.GetDirectory().GetWinPath(), NULL, NULL, SW_SHOW);\r
2758                                 }\r
2759                                 break;\r
2760                         case IDSVNLC_REMOVE:\r
2761                                 {\r
2762                                         SVN git;\r
2763                                         CTSVNPathList itemsToRemove;\r
2764                                         FillListOfSelectedItemPaths(itemsToRemove);\r
2765 \r
2766                                         // We must sort items before removing, so that files are always removed\r
2767                                         // *before* their parents\r
2768                                         itemsToRemove.SortByPathname(true);\r
2769 \r
2770                                         bool bSuccess = false;\r
2771                                         if (git.Remove(itemsToRemove, FALSE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))\r
2772                                         {\r
2773                                                 bSuccess = true;\r
2774                                         }\r
2775                                         else\r
2776                                         {\r
2777                                                 if ((git.Err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE) ||\r
2778                                                         (git.Err->apr_err == SVN_ERR_CLIENT_MODIFIED))\r
2779                                                 {\r
2780                                                         CString msg, yes, no, yestoall;\r
2781                                                         msg.Format(IDS_PROC_REMOVEFORCE, (LPCTSTR)git.GetLastErrorMessage());\r
2782                                                         yes.LoadString(IDS_MSGBOX_YES);\r
2783                                                         no.LoadString(IDS_MSGBOX_NO);\r
2784                                                         yestoall.LoadString(IDS_PROC_YESTOALL);\r
2785                                                         UINT ret = CMessageBox::Show(m_hWnd, msg, _T("TortoiseSVN"), 2, IDI_ERROR, yes, no, yestoall);\r
2786                                                         if ((ret == 1)||(ret==3))\r
2787                                                         {\r
2788                                                                 if (!git.Remove(itemsToRemove, TRUE, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)))\r
2789                                                                 {\r
2790                                                                         CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2791                                                                 }\r
2792                                                                 else\r
2793                                                                         bSuccess = true;\r
2794                                                         }\r
2795                                                 }\r
2796                                                 else\r
2797                                                         CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2798                                         }\r
2799                                         if (bSuccess)\r
2800                                         {\r
2801                                                 // The remove went ok, but we now need to run through the selected items again\r
2802                                                 // and update their status\r
2803                                                 POSITION pos = GetFirstSelectedItemPosition();\r
2804                                                 int index;\r
2805                                                 std::vector<int> entriesToRemove;\r
2806                                                 while ((index = GetNextSelectedItem(pos)) >= 0)\r
2807                                                 {\r
2808                                                         FileEntry * e = GetListEntry(index);\r
2809                                                         if (!bShift &&\r
2810                                                                 ((e->textstatus == git_wc_status_unversioned)||\r
2811                                                                 (e->textstatus == git_wc_status_none)||\r
2812                                                                 (e->textstatus == git_wc_status_ignored)))\r
2813                                                         {\r
2814                                                                 if (GetCheck(index))\r
2815                                                                         m_nSelected--;\r
2816                                                                 m_nTotal--;\r
2817                                                                 entriesToRemove.push_back(index);\r
2818                                                         }\r
2819                                                         else\r
2820                                                         {\r
2821                                                                 e->textstatus = git_wc_status_deleted;\r
2822                                                                 e->status = git_wc_status_deleted;\r
2823                                                                 SetEntryCheck(e,index,true);\r
2824                                                         }\r
2825                                                 }\r
2826                                                 for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)\r
2827                                                 {\r
2828                                                         RemoveListEntry(*it);\r
2829                                                 }\r
2830                                         }\r
2831                                         SaveColumnWidths();\r
2832                                         Show(m_dwShow, 0, m_bShowFolders);\r
2833                                         NotifyCheck();\r
2834                                 }\r
2835                                 break;\r
2836                         case IDSVNLC_DELETE:\r
2837                                 {\r
2838                                         CTSVNPathList pathlist;\r
2839                                         FillListOfSelectedItemPaths(pathlist);\r
2840                                         pathlist.RemoveChildren();\r
2841                                         CString filelist;\r
2842                                         for (INT_PTR i=0; i<pathlist.GetCount(); ++i)\r
2843                                         {\r
2844                                                 filelist += pathlist[i].GetWinPathString();\r
2845                                                 filelist += _T("|");\r
2846                                         }\r
2847                                         filelist += _T("|");\r
2848                                         int len = filelist.GetLength();\r
2849                                         TCHAR * buf = new TCHAR[len+2];\r
2850                                         _tcscpy_s(buf, len+2, filelist);\r
2851                                         for (int i=0; i<len; ++i)\r
2852                                                 if (buf[i] == '|')\r
2853                                                         buf[i] = 0;\r
2854                                         SHFILEOPSTRUCT fileop;\r
2855                                         fileop.hwnd = this->m_hWnd;\r
2856                                         fileop.wFunc = FO_DELETE;\r
2857                                         fileop.pFrom = buf;\r
2858                                         fileop.pTo = NULL;\r
2859                                         fileop.fFlags = FOF_NO_CONNECTED_ELEMENTS | ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : FOF_ALLOWUNDO);\r
2860                                         fileop.lpszProgressTitle = _T("deleting file");\r
2861                                         int result = SHFileOperation(&fileop);\r
2862                                         delete [] buf;\r
2863 \r
2864                                         if ( (result==0) && (!fileop.fAnyOperationsAborted) )\r
2865                                         {\r
2866                                                 SetRedraw(FALSE);\r
2867                                                 POSITION pos = NULL;\r
2868                                                 CTSVNPathList deletedlist;      // to store list of deleted folders\r
2869                                                 while ((pos = GetFirstSelectedItemPosition()) != 0)\r
2870                                                 {\r
2871                                                         int index = GetNextSelectedItem(pos);\r
2872                                                         if (GetCheck(index))\r
2873                                                                 m_nSelected--;\r
2874                                                         m_nTotal--;\r
2875                                                         FileEntry * fentry = GetListEntry(index);\r
2876                                                         if ((fentry)&&(fentry->isfolder))\r
2877                                                                 deletedlist.AddPath(fentry->path);\r
2878                                                         RemoveListEntry(index);\r
2879                                                 }\r
2880                                                 // now go through the list of deleted folders\r
2881                                                 // and remove all their children from the list too!\r
2882                                                 int nListboxEntries = GetItemCount();\r
2883                                                 for (int folderindex = 0; folderindex < deletedlist.GetCount(); ++folderindex)\r
2884                                                 {\r
2885                                                         CTSVNPath folderpath = deletedlist[folderindex];\r
2886                                                         for (int i=0; i<nListboxEntries; ++i)\r
2887                                                         {\r
2888                                                                 FileEntry * entry = GetListEntry(i);\r
2889                                                                 if (folderpath.IsAncestorOf(entry->path))\r
2890                                                                 {\r
2891                                                                         RemoveListEntry(i--);\r
2892                                                                         nListboxEntries--;\r
2893                                                                 }\r
2894                                                         }\r
2895                                                 }\r
2896                                                 SetRedraw(TRUE);\r
2897                                         }\r
2898                                 }\r
2899                                 break;\r
2900                         case IDSVNLC_IGNOREMASK:\r
2901                                 {\r
2902                                         CString name = _T("*")+filepath.GetFileExtension();\r
2903                                         CTSVNPathList ignorelist;\r
2904                                         FillListOfSelectedItemPaths(ignorelist, true);\r
2905                                         std::set<CTSVNPath> parentlist;\r
2906                                         for (int i=0; i<ignorelist.GetCount(); ++i)\r
2907                                         {\r
2908                                                 parentlist.insert(ignorelist[i].GetContainingDirectory());\r
2909                                         }\r
2910                                         std::set<CTSVNPath>::iterator it;\r
2911                                         std::vector<CString> toremove;\r
2912                                         SetRedraw(FALSE);\r
2913                                         for (it = parentlist.begin(); it != parentlist.end(); ++it)\r
2914                                         {\r
2915                                                 CTSVNPath parentFolder = (*it).GetDirectory();\r
2916                                                 SVNProperties props(parentFolder, SVNRev::REV_WC, false);\r
2917                                                 CStringA value;\r
2918                                                 for (int i=0; i<props.GetCount(); i++)\r
2919                                                 {\r
2920                                                         CString propname(props.GetItemName(i).c_str());\r
2921                                                         if (propname.CompareNoCase(_T("git:ignore"))==0)\r
2922                                                         {\r
2923                                                                 stdstring stemp;\r
2924                                                                 // treat values as normal text even if they're not\r
2925                                                                 value = (char *)props.GetItemValue(i).c_str();\r
2926                                                         }\r
2927                                                 }\r
2928                                                 if (value.IsEmpty())\r
2929                                                         value = name;\r
2930                                                 else\r
2931                                                 {\r
2932                                                         value = value.Trim("\n\r");\r
2933                                                         value += "\n";\r
2934                                                         value += name;\r
2935                                                         value.Remove('\r');\r
2936                                                 }\r
2937                                                 if (!props.Add(_T("git:ignore"), (LPCSTR)value))\r
2938                                                 {\r
2939                                                         CString temp;\r
2940                                                         temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);\r
2941                                                         CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseSVN"), MB_ICONERROR);\r
2942                                                 }\r
2943                                                 else\r
2944                                                 {\r
2945                                                         CTSVNPath basepath;\r
2946                                                         int nListboxEntries = GetItemCount();\r
2947                                                         for (int i=0; i<nListboxEntries; ++i)\r
2948                                                         {\r
2949                                                                 FileEntry * entry = GetListEntry(i);\r
2950                                                                 ASSERT(entry != NULL);\r
2951                                                                 if (entry == NULL)\r
2952                                                                         continue;\r
2953                                                                 if (basepath.IsEmpty())\r
2954                                                                         basepath = entry->basepath;\r
2955                                                                 // since we ignored files with a mask (e.g. *.exe)\r
2956                                                                 // we have to find find all files in the same\r
2957                                                                 // folder (IsAncestorOf() returns TRUE for _all_ children,\r
2958                                                                 // not just the immediate ones) which match the\r
2959                                                                 // mask and remove them from the list too.\r
2960                                                                 if ((entry->status == git_wc_status_unversioned)&&(parentFolder.IsAncestorOf(entry->path)))\r
2961                                                                 {\r
2962                                                                         CString f = entry->path.GetSVNPathString();\r
2963                                                                         if (f.Mid(parentFolder.GetSVNPathString().GetLength()).Find('/')<=0)\r
2964                                                                         {\r
2965                                                                                 if (CStringUtils::WildCardMatch(name, f))\r
2966                                                                                 {\r
2967                                                                                         if (GetCheck(i))\r
2968                                                                                                 m_nSelected--;\r
2969                                                                                         m_nTotal--;\r
2970                                                                                         toremove.push_back(f);\r
2971                                                                                 }\r
2972                                                                         }\r
2973                                                                 }\r
2974                                                         }\r
2975                                                         if (!m_bIgnoreRemoveOnly)\r
2976                                                         {\r
2977                                                                 SVNStatus status;\r
2978                                                                 git_wc_status2_t * s;\r
2979                                                                 CTSVNPath gitPath;\r
2980                                                                 s = status.GetFirstFileStatus(parentFolder, gitPath, false, git_depth_empty);\r
2981                                                                 if (s!=0)\r
2982                                                                 {\r
2983                                                                         // first check if the folder isn't already present in the list\r
2984                                                                         bool bFound = false;\r
2985                                                                         for (int i=0; i<nListboxEntries; ++i)\r
2986                                                                         {\r
2987                                                                                 FileEntry * entry = GetListEntry(i);\r
2988                                                                                 if (entry->path.IsEquivalentTo(gitPath))\r
2989                                                                                 {\r
2990                                                                                         bFound = true;\r
2991                                                                                         break;\r
2992                                                                                 }\r
2993                                                                         }\r
2994                                                                         if (!bFound)\r
2995                                                                         {\r
2996                                                                                 FileEntry * entry = new FileEntry();\r
2997                                                                                 entry->path = gitPath;\r
2998                                                                                 entry->basepath = basepath;\r
2999                                                                                 entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);\r
3000                                                                                 entry->textstatus = s->text_status;\r
3001                                                                                 entry->propstatus = s->prop_status;\r
3002                                                                                 entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);\r
3003                                                                                 entry->remotetextstatus = s->repos_text_status;\r
3004                                                                                 entry->remotepropstatus = s->repos_prop_status;\r
3005                                                                                 entry->inunversionedfolder = false;\r
3006                                                                                 entry->checked = true;\r
3007                                                                                 entry->inexternal = false;\r
3008                                                                                 entry->direct = false;\r
3009                                                                                 entry->isfolder = true;\r
3010                                                                                 entry->last_commit_date = 0;\r
3011                                                                                 entry->last_commit_rev = 0;\r
3012                                                                                 entry->remoterev = 0;\r
3013                                                                                 if (s->entry)\r
3014                                                                                 {\r
3015                                                                                         if (s->entry->url)\r
3016                                                                                         {\r
3017                                                                                                 entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));\r
3018                                                                                         }\r
3019                                                                                 }\r
3020                                                                                 if (s->entry && s->entry->present_props)\r
3021                                                                                 {\r
3022                                                                                         entry->present_props = s->entry->present_props;\r
3023                                                                                 }\r
3024                                                                                 m_arStatusArray.push_back(entry);\r
3025                                                                                 m_arListArray.push_back(m_arStatusArray.size()-1);\r
3026                                                                                 AddEntry(entry, langID, GetItemCount());\r
3027                                                                         }\r
3028                                                                 }\r
3029                                                         }\r
3030                                                 }\r
3031                                         }\r
3032                                         for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)\r
3033                                         {\r
3034                                                 int nListboxEntries = GetItemCount();\r
3035                                                 for (int i=0; i<nListboxEntries; ++i)\r
3036                                                 {\r
3037                                                         if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)\r
3038                                                         {\r
3039                                                                 RemoveListEntry(i);\r
3040                                                                 break;\r
3041                                                         }\r
3042                                                 }\r
3043                                         }\r
3044                                         SetRedraw(TRUE);\r
3045                                 }\r
3046                                 break;\r
3047                         case IDSVNLC_IGNORE:\r
3048                                 {\r
3049                                         CTSVNPathList ignorelist;\r
3050                                         std::vector<CString> toremove;\r
3051                                         FillListOfSelectedItemPaths(ignorelist, true);\r
3052                                         SetRedraw(FALSE);\r
3053                                         for (int j=0; j<ignorelist.GetCount(); ++j)\r
3054                                         {\r
3055                                                 int nListboxEntries = GetItemCount();\r
3056                                                 for (int i=0; i<nListboxEntries; ++i)\r
3057                                                 {\r
3058                                                         if (GetListEntry(i)->GetPath().IsEquivalentTo(ignorelist[j]))\r
3059                                                         {\r
3060                                                                 selIndex = i;\r
3061                                                                 break;\r
3062                                                         }\r
3063                                                 }\r
3064                                                 CString name = CPathUtils::PathPatternEscape(ignorelist[j].GetFileOrDirectoryName());\r
3065                                                 CTSVNPath parentfolder = ignorelist[j].GetContainingDirectory();\r
3066                                                 SVNProperties props(parentfolder, SVNRev::REV_WC, false);\r
3067                                                 CStringA value;\r
3068                                                 for (int i=0; i<props.GetCount(); i++)\r
3069                                                 {\r
3070                                                         CString propname(props.GetItemName(i).c_str());\r
3071                                                         if (propname.CompareNoCase(_T("git:ignore"))==0)\r
3072                                                         {\r
3073                                                                 stdstring stemp;\r
3074                                                                 // treat values as normal text even if they're not\r
3075                                                                 value = (char *)props.GetItemValue(i).c_str();\r
3076                                                         }\r
3077                                                 }\r
3078                                                 if (value.IsEmpty())\r
3079                                                         value = name;\r
3080                                                 else\r
3081                                                 {\r
3082                                                         value = value.Trim("\n\r");\r
3083                                                         value += "\n";\r
3084                                                         value += name;\r
3085                                                         value.Remove('\r');\r
3086                                                 }\r
3087                                                 if (!props.Add(_T("git:ignore"), (LPCSTR)value))\r
3088                                                 {\r
3089                                                         CString temp;\r
3090                                                         temp.Format(IDS_ERR_FAILEDIGNOREPROPERTY, (LPCTSTR)name);\r
3091                                                         CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseSVN"), MB_ICONERROR);\r
3092                                                         break;\r
3093                                                 }\r
3094                                                 if (GetCheck(selIndex))\r
3095                                                         m_nSelected--;\r
3096                                                 m_nTotal--;\r
3097 \r
3098                                                 // now, if we ignored a folder, remove all its children\r
3099                                                 if (ignorelist[j].IsDirectory())\r
3100                                                 {\r
3101                                                         for (int i=0; i<(int)m_arListArray.size(); ++i)\r
3102                                                         {\r
3103                                                                 FileEntry * entry = GetListEntry(i);\r
3104                                                                 if (entry->status == git_wc_status_unversioned)\r
3105                                                                 {\r
3106                                                                         if (!ignorelist[j].IsEquivalentTo(entry->GetPath())&&(ignorelist[j].IsAncestorOf(entry->GetPath())))\r
3107                                                                         {\r
3108                                                                                 entry->status = git_wc_status_ignored;\r
3109                                                                                 entry->textstatus = git_wc_status_ignored;\r
3110                                                                                 if (GetCheck(i))\r
3111                                                                                         m_nSelected--;\r
3112                                                                                 toremove.push_back(entry->GetPath().GetSVNPathString());\r
3113                                                                         }\r
3114                                                                 }\r
3115                                                         }\r
3116                                                 }\r
3117 \r
3118                                                 CTSVNPath basepath = m_arStatusArray[m_arListArray[selIndex]]->basepath;\r
3119 \r
3120                                                 FileEntry * entry = m_arStatusArray[m_arListArray[selIndex]];\r
3121                                                 if ( entry->status == git_wc_status_unversioned ) // keep "deleted" items\r
3122                                                         toremove.push_back(entry->GetPath().GetSVNPathString());\r
3123 \r
3124                                                 if (!m_bIgnoreRemoveOnly)\r
3125                                                 {\r
3126                                                         SVNStatus status;\r
3127                                                         git_wc_status2_t * s;\r
3128                                                         CTSVNPath gitPath;\r
3129                                                         s = status.GetFirstFileStatus(parentfolder, gitPath, false, git_depth_empty);\r
3130                                                         // first check if the folder isn't already present in the list\r
3131                                                         bool bFound = false;\r
3132                                                         nListboxEntries = GetItemCount();\r
3133                                                         for (int i=0; i<nListboxEntries; ++i)\r
3134                                                         {\r
3135                                                                 FileEntry * entry = GetListEntry(i);\r
3136                                                                 if (entry->path.IsEquivalentTo(gitPath))\r
3137                                                                 {\r
3138                                                                         bFound = true;\r
3139                                                                         break;\r
3140                                                                 }\r
3141                                                         }\r
3142                                                         if (!bFound)\r
3143                                                         {\r
3144                                                                 if (s!=0)\r
3145                                                                 {\r
3146                                                                         FileEntry * entry = new FileEntry();\r
3147                                                                         entry->path = gitPath;\r
3148                                                                         entry->basepath = basepath;\r
3149                                                                         entry->status = SVNStatus::GetMoreImportant(s->text_status, s->prop_status);\r
3150                                                                         entry->textstatus = s->text_status;\r
3151                                                                         entry->propstatus = s->prop_status;\r
3152                                                                         entry->remotestatus = SVNStatus::GetMoreImportant(s->repos_text_status, s->repos_prop_status);\r
3153                                                                         entry->remotetextstatus = s->repos_text_status;\r
3154                                                                         entry->remotepropstatus = s->repos_prop_status;\r
3155                                                                         entry->inunversionedfolder = FALSE;\r
3156                                                                         entry->checked = true;\r
3157                                                                         entry->inexternal = false;\r
3158                                                                         entry->direct = false;\r
3159                                                                         entry->isfolder = true;\r
3160                                                                         entry->last_commit_date = 0;\r
3161                                                                         entry->last_commit_rev = 0;\r
3162                                                                         entry->remoterev = 0;\r
3163                                                                         if (s->entry)\r
3164                                                                         {\r
3165                                                                                 if (s->entry->url)\r
3166                                                                                 {\r
3167                                                                                         entry->url = CUnicodeUtils::GetUnicode(CPathUtils::PathUnescape(s->entry->url));\r
3168                                                                                 }\r
3169                                                                         }\r
3170                                                                         if (s->entry && s->entry->present_props)\r
3171                                                                         {\r
3172                                                                                 entry->present_props = s->entry->present_props;\r
3173                                                                         }\r
3174                                                                         m_arStatusArray.push_back(entry);\r
3175                                                                         m_arListArray.push_back(m_arStatusArray.size()-1);\r
3176                                                                         AddEntry(entry, langID, GetItemCount());\r
3177                                                                 }\r
3178                                                         }\r
3179                                                 }\r
3180                                         }\r
3181                                         for (std::vector<CString>::iterator it = toremove.begin(); it != toremove.end(); ++it)\r
3182                                         {\r
3183                                                 int nListboxEntries = GetItemCount();\r
3184                                                 for (int i=0; i<nListboxEntries; ++i)\r
3185                                                 {\r
3186                                                         if (GetListEntry(i)->path.GetSVNPathString().Compare(*it)==0)\r
3187                                                         {\r
3188                                                                 RemoveListEntry(i);\r
3189                                                                 break;\r
3190                                                         }\r
3191                                                 }\r
3192                                         }\r
3193                                         SetRedraw(TRUE);\r
3194                                 }\r
3195                                 break;\r
3196                         case IDSVNLC_EDITCONFLICT:\r
3197                                 SVNDiff::StartConflictEditor(filepath);\r
3198                                 break;\r
3199                         case IDSVNLC_RESOLVECONFLICT:\r
3200                         case IDSVNLC_RESOLVEMINE:\r
3201                         case IDSVNLC_RESOLVETHEIRS:\r
3202                                 {\r
3203                                         git_wc_conflict_choice_t result = git_wc_conflict_choose_merged;\r
3204                                         switch (cmd)\r
3205                                         {\r
3206                                         case IDSVNLC_RESOLVETHEIRS:\r
3207                                                 result = git_wc_conflict_choose_theirs_full;\r
3208                                                 break;\r
3209                                         case IDSVNLC_RESOLVEMINE:\r
3210                                                 result = git_wc_conflict_choose_mine_full;\r
3211                                                 break;\r
3212                                         case IDSVNLC_RESOLVECONFLICT:\r
3213                                                 result = git_wc_conflict_choose_merged;\r
3214                                                 break;\r
3215                                         }\r
3216                                         if (CMessageBox::Show(m_hWnd, IDS_PROC_RESOLVE, IDS_APPNAME, MB_ICONQUESTION | MB_YESNO)==IDYES)\r
3217                                         {\r
3218                                                 SVN git;\r
3219                                                 POSITION pos = GetFirstSelectedItemPosition();\r
3220                                                 while (pos != 0)\r
3221                                                 {\r
3222                                                         int index;\r
3223                                                         index = GetNextSelectedItem(pos);\r
3224                                                         FileEntry * fentry = m_arStatusArray[m_arListArray[index]];\r
3225                                                         if (!git.Resolve(fentry->GetPath(), result, FALSE))\r
3226                                                         {\r
3227                                                                 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
3228                                                         }\r
3229                                                         else\r
3230                                                         {\r
3231                                                                 fentry->status = git_wc_status_modified;\r
3232                                                                 fentry->textstatus = git_wc_status_modified;\r
3233                                                                 fentry->isConflicted = false;\r
3234                                                         }\r
3235                                                 }\r
3236                                                 Show(m_dwShow, 0, m_bShowFolders);\r
3237                                         }\r
3238                                 }\r
3239                                 break;\r
3240                         case IDSVNLC_ADD:\r
3241                                 {\r
3242                                         SVN git;\r
3243                                         CTSVNPathList itemsToAdd;\r
3244                                         FillListOfSelectedItemPaths(itemsToAdd);\r
3245 \r
3246                                         // We must sort items before adding, so that folders are always added\r
3247                                         // *before* any of their children\r
3248                                         itemsToAdd.SortByPathname();\r
3249 \r
3250                                         ProjectProperties props;\r
3251                                         props.ReadPropsPathList(itemsToAdd);\r
3252                                         if (git.Add(itemsToAdd, &props, git_depth_empty, TRUE, TRUE, TRUE))\r
3253                                         {\r
3254                                                 // The add went ok, but we now need to run through the selected items again\r
3255                                                 // and update their status\r
3256                                                 POSITION pos = GetFirstSelectedItemPosition();\r
3257                                                 int index;\r
3258                                                 while ((index = GetNextSelectedItem(pos)) >= 0)\r
3259                                                 {\r
3260                                                         FileEntry * e = GetListEntry(index);\r
3261                                                         e->textstatus = git_wc_status_added;\r
3262                                                         e->propstatus = git_wc_status_none;\r
3263                                                         e->status = git_wc_status_added;\r
3264                                                         SetEntryCheck(e,index,true);\r
3265                                                 }\r
3266                                         }\r
3267                                         else\r
3268                                         {\r
3269                                                 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
3270                                         }\r
3271                                         SaveColumnWidths();\r
3272                                         Show(m_dwShow, 0, m_bShowFolders);\r
3273                                         NotifyCheck();\r
3274                                 }\r
3275                                 break;\r
3276                         case IDSVNLC_ADD_RECURSIVE:\r
3277                                 {\r
3278                                         CTSVNPathList itemsToAdd;\r
3279                                         FillListOfSelectedItemPaths(itemsToAdd);\r
3280 \r
3281                                         CAddDlg dlg;\r
3282                                         dlg.m_pathList = itemsToAdd;\r
3283                                         if (dlg.DoModal() == IDOK)\r
3284                                         {\r
3285                                                 if (dlg.m_pathList.GetCount() == 0)\r
3286                                                         break;\r
3287                                                 CSVNProgressDlg progDlg;\r
3288                                                 progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Add);\r
3289                                                 progDlg.SetPathList(dlg.m_pathList);\r
3290                                                 ProjectProperties props;\r
3291                                                 props.ReadPropsPathList(dlg.m_pathList);\r
3292                                                 progDlg.SetProjectProperties(props);\r
3293                                                 progDlg.SetItemCount(dlg.m_pathList.GetCount());\r
3294                                                 progDlg.DoModal();\r
3295 \r
3296                                                 // refresh!\r
3297                                                 CWnd* pParent = GetParent();\r
3298                                                 if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
3299                                                 {\r
3300                                                         pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
3301                                                 }\r
3302                                         }\r
3303                                 }\r
3304                                 break;\r
3305                         case IDSVNLC_LOCK:\r
3306                                 {\r
3307                                         CTSVNPathList itemsToLock;\r
3308                                         FillListOfSelectedItemPaths(itemsToLock);\r
3309                                         CInputDlg inpDlg;\r
3310                                         inpDlg.m_sTitle.LoadString(IDS_MENU_LOCK);\r
3311                                         CStringUtils::RemoveAccelerators(inpDlg.m_sTitle);\r
3312                                         inpDlg.m_sHintText.LoadString(IDS_LOCK_MESSAGEHINT);\r
3313                                         inpDlg.m_sCheckText.LoadString(IDS_LOCK_STEALCHECK);\r
3314                                         ProjectProperties props;\r
3315                                         props.ReadPropsPathList(itemsToLock);\r
3316                                         props.nMinLogSize = 0;          // the lock message is optional, so no minimum!\r
3317                                         inpDlg.m_pProjectProperties = &props;\r
3318                                         if (inpDlg.DoModal()==IDOK)\r
3319                                         {\r
3320                                                 CSVNProgressDlg progDlg;\r
3321                                                 progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Lock);\r
3322                                                 progDlg.SetOptions(inpDlg.m_iCheck ? ProgOptLockForce : ProgOptNone);\r
3323                                                 progDlg.SetPathList(itemsToLock);\r
3324                                                 progDlg.SetCommitMessage(inpDlg.m_sInputText);\r
3325                                                 progDlg.DoModal();\r
3326                                                 // refresh!\r
3327                                                 CWnd* pParent = GetParent();\r
3328                                                 if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
3329                                                 {\r
3330                                                         pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
3331                                                 }\r
3332                                         }\r
3333                                 }\r
3334                                 break;\r
3335                         case IDSVNLC_UNLOCKFORCE:\r
3336                                 bForce = true;\r
3337                         case IDSVNLC_UNLOCK:\r
3338                                 {\r
3339                                         CTSVNPathList itemsToUnlock;\r
3340                                         FillListOfSelectedItemPaths(itemsToUnlock);\r
3341                                         CSVNProgressDlg progDlg;\r
3342                                         progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Unlock);\r
3343                                         progDlg.SetOptions(bForce ? ProgOptLockForce : ProgOptNone);\r
3344                                         progDlg.SetPathList(itemsToUnlock);\r
3345                                         progDlg.DoModal();\r
3346                                         // refresh!\r
3347                                         CWnd* pParent = GetParent();\r
3348                                         if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
3349                                         {\r
3350                                                 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
3351                                         }\r
3352                                 }\r
3353                                 break;\r
3354                         case IDSVNLC_REPAIRMOVE:\r
3355                                 {\r
3356                                         POSITION pos = GetFirstSelectedItemPosition();\r
3357                                         int index = GetNextSelectedItem(pos);\r
3358                                         FileEntry * entry1 = NULL;\r
3359                                         FileEntry * entry2 = NULL;\r
3360                                         if (index >= 0)\r
3361                                         {\r
3362                                                 entry1 = GetListEntry(index);\r
3363                                                 git_wc_status_kind status1 = git_wc_status_none;\r
3364                                                 git_wc_status_kind status2 = git_wc_status_none;\r
3365                                                 if (entry1)\r
3366                                                 {\r
3367                                                         status1 = entry1->status;\r
3368                                                         index = GetNextSelectedItem(pos);\r
3369                                                         if (index >= 0)\r
3370                                                         {\r
3371                                                                 entry2 = GetListEntry(index);\r
3372                                                                 if (entry2)\r
3373                                                                 {\r
3374                                                                         status2 = entry2->status;\r
3375                                                                         if (status2 == git_wc_status_missing && status1 == git_wc_status_unversioned)\r
3376                                                                         {\r
3377                                                                                 FileEntry * tempentry = entry1;\r
3378                                                                                 entry1 = entry2;\r
3379                                                                                 entry2 = tempentry;\r
3380                                                                         }\r
3381                                                                         // entry1 was renamed to entry2 but outside of Subversion\r
3382                                                                         // fix this by moving entry2 back to entry1 first,\r
3383                                                                         // then do an git-move from entry1 to entry2\r
3384                                                                         if (MoveFile(entry2->GetPath().GetWinPath(), entry1->GetPath().GetWinPath()))\r
3385                                                                         {\r
3386                                                                                 SVN git;\r
3387                                                                                 if (!git.Move(CTSVNPathList(entry1->GetPath()), entry2->GetPath(), TRUE))\r
3388                                                                                 {\r
3389                                                                                         CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
3390                                                                                 }\r
3391                                                                                 else\r
3392                                                                                 {\r
3393                                                                                         // check the previously unversioned item\r
3394                                                                                         entry1->checked = true;\r
3395                                                                                         // fixing the move was successful. We have to adjust the new status of the\r
3396                                                                                         // files.\r
3397                                                                                         // Since we don't know if the moved/renamed file had local modifications or not,\r
3398                                                                                         // we can't guess the new status. That means we have to refresh...\r
3399                                                                                         CWnd* pParent = GetParent();\r
3400                                                                                         if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
3401                                                                                         {\r
3402                                                                                                 pParent->SendMessage(SVNSLNM_NEEDSREFRESH);\r
3403                                                                                         }\r
3404                                                                                 }\r
3405                                                                         }\r
3406                                                                         else\r
3407                                                                         {\r
3408                                                                                 LPVOID lpMsgBuf;\r
3409                                                                                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
3410                                                                                         FORMAT_MESSAGE_FROM_SYSTEM | \r
3411                                                                                         FORMAT_MESSAGE_IGNORE_INSERTS,\r
3412                                                                                         NULL,\r
3413                                                                                         GetLastError(),\r
3414                                                                                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
3415                                                                                         (LPTSTR) &lpMsgBuf,\r
3416                                                                                         0,\r
3417                                                                                         NULL \r
3418                                                                                         );\r
3419                                                                                 MessageBox((LPCTSTR)lpMsgBuf, _T("Error"), MB_OK | MB_ICONINFORMATION );\r
3420                                                                                 LocalFree( lpMsgBuf );\r
3421                                                                         }\r
3422                                                                 }\r
3423                                                         }\r
3424                                                 }\r
3425                                         }\r
3426                                 }\r
3427                                 break;\r
3428                         case IDSVNLC_REMOVEFROMCS:\r
3429                                 {\r
3430                                         CTSVNPathList changelistItems;\r
3431                                         FillListOfSelectedItemPaths(changelistItems);\r
3432                                         SVN git;\r
3433                                         SetRedraw(FALSE);\r
3434                                         if (git.RemoveFromChangeList(changelistItems, CStringArray(), git_depth_empty))\r
3435                                         {\r
3436                                                 // The changelists were removed, but we now need to run through the selected items again\r
3437                                                 // and update their changelist\r
3438                                                 POSITION pos = GetFirstSelectedItemPosition();\r
3439                                                 int index;\r
3440                                                 std::vector<int> entriesToRemove;\r
3441                                                 while ((index = GetNextSelectedItem(pos)) >= 0)\r
3442                                                 {\r
3443                                                         FileEntry * e = GetListEntry(index);\r
3444                                                         if (e)\r
3445                                                         {\r
3446                                                                 e->changelist.Empty();\r
3447                                                                 if (e->status == git_wc_status_normal)\r
3448                                                                 {\r
3449                                                                         // remove the entry completely\r
3450                                                                         entriesToRemove.push_back(index);\r
3451                                                                 }\r
3452                                                                 else\r
3453                                                                         SetItemGroup(index, 0);\r
3454                                                         }\r
3455                                                 }\r
3456                                                 for (std::vector<int>::reverse_iterator it = entriesToRemove.rbegin(); it != entriesToRemove.rend(); ++it)\r
3457                                                 {\r
3458                                                         RemoveListEntry(*it);\r
3459                                                 }\r
3460                                                 // TODO: Should we go through all entries here and check if we also could\r
3461                                                 // remove the changelist from m_changelists ?\r
3462                                         }\r
3463                                         else\r
3464                                         {\r
3465                                                 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
3466                                         }\r
3467                                         SetRedraw(TRUE);\r
3468                                 }\r
3469                                 break;\r
3470                         case IDSVNLC_CREATEIGNORECS:\r
3471                                 CreateChangeList(SVNSLC_IGNORECHANGELIST);\r
3472                                 break;\r
3473                         case IDSVNLC_CREATECS:\r
3474                                 {\r
3475                                         CCreateChangelistDlg dlg;\r
3476                                         if (dlg.DoModal() == IDOK)\r
3477                                         {\r
3478                                                 CreateChangeList(dlg.m_sName);\r
3479                                         }\r
3480                                 }\r
3481                                 break;\r
3482                         default:\r
3483                                 {\r
3484                                         if (cmd < IDSVNLC_MOVETOCS)\r
3485                                                 break;\r
3486                                         CTSVNPathList changelistItems;\r
3487                                         FillListOfSelectedItemPaths(changelistItems);\r
3488 \r
3489                                         // find the changelist name\r
3490                                         CString sChangelist;\r
3491                                         int cmdID = IDSVNLC_MOVETOCS;\r
3492                                         SetRedraw(FALSE);\r
3493                                         for (std::map<CString, int>::const_iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)\r
3494                                         {\r
3495                                                 if ((it->first.Compare(SVNSLC_IGNORECHANGELIST))&&(entry->changelist.Compare(it->first)))\r
3496                                                 {\r
3497                                                         if (cmd == cmdID)\r
3498                                                         {\r
3499                                                                 sChangelist = it->first;\r
3500                                                         }\r
3501                                                         cmdID++;\r
3502                                                 }\r
3503                                         }\r
3504                                         if (!sChangelist.IsEmpty())\r
3505                                         {\r
3506                                                 SVN git;\r
3507                                                 if (git.AddToChangeList(changelistItems, sChangelist, git_depth_empty))\r
3508                                                 {\r
3509                                                         // The changelists were moved, but we now need to run through the selected items again\r
3510                                                         // and update their changelist\r
3511                                                         POSITION pos = GetFirstSelectedItemPosition();\r
3512                                                         int index;\r
3513                                                         while ((index = GetNextSelectedItem(pos)) >= 0)\r
3514                                                         {\r
3515                                                                 FileEntry * e = GetListEntry(index);\r
3516                                                                 e->changelist = sChangelist;\r
3517                                                                 if (!e->IsFolder())\r
3518                                                                 {\r
3519                                                                         if (m_changelists.find(e->changelist)!=m_changelists.end())\r
3520                                                                                 SetItemGroup(index, m_changelists[e->changelist]);\r
3521                                                                         else\r
3522                                                                                 SetItemGroup(index, 0);\r
3523                                                                 }\r
3524                                                         }\r
3525                                                 }\r
3526                                                 else\r
3527                                                 {\r
3528                                                         CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
3529                                                 }\r
3530                                         }\r
3531                                         SetRedraw(TRUE);\r
3532                                 }\r
3533                                 break;\r
3534                         } // switch (cmd)\r
3535                         m_bBlock = FALSE;\r
3536                         AfxGetApp()->DoWaitCursor(-1);\r
3537                         GetStatisticsString();\r
3538                         int iItemCountAfterMenuCmd = GetItemCount();\r
3539                         if (iItemCountAfterMenuCmd != iItemCountBeforeMenuCmd)\r
3540                         {\r
3541                                 CWnd* pParent = GetParent();\r
3542                                 if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
3543                                 {\r
3544                                         pParent->SendMessage(SVNSLNM_ITEMCOUNTCHANGED);\r
3545                                 }\r
3546                         }\r
3547                 } // if (popup.CreatePopupMenu())\r
3548         } // if (selIndex >= 0)\r
3549 #endif\r
3550 }\r
3551 \r
3552 void CGitStatusListCtrl::OnContextMenuHeader(CWnd * pWnd, CPoint point)\r
3553 {\r
3554         bool XPorLater = false;\r
3555         OSVERSIONINFOEX inf;\r
3556         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
3557         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
3558         GetVersionEx((OSVERSIONINFO *)&inf);\r
3559         WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
3560         if (fullver >= 0x0501)\r
3561                 XPorLater = true;\r
3562 \r
3563         CHeaderCtrl * pHeaderCtrl = (CHeaderCtrl *)pWnd;\r
3564         if ((point.x == -1) && (point.y == -1))\r
3565         {\r
3566                 CRect rect;\r
3567                 pHeaderCtrl->GetItemRect(0, &rect);\r
3568                 ClientToScreen(&rect);\r
3569                 point = rect.CenterPoint();\r
3570         }\r
3571         Locker lock(m_critSec);\r
3572         CMenu popup;\r
3573         if (popup.CreatePopupMenu())\r
3574         {\r
3575                 int columnCount = m_ColumnManager.GetColumnCount();\r
3576 \r
3577                 CString temp;\r
3578                 UINT uCheckedFlags = MF_STRING | MF_ENABLED | MF_CHECKED;\r
3579                 UINT uUnCheckedFlags = MF_STRING | MF_ENABLED;\r
3580 \r
3581                 // build control menu\r
3582 \r
3583                 if (XPorLater)\r
3584                 {\r
3585                         temp.LoadString(IDS_STATUSLIST_SHOWGROUPS);\r
3586                         popup.AppendMenu(IsGroupViewEnabled() ? uCheckedFlags : uUnCheckedFlags, columnCount, temp);\r
3587                 }\r
3588 \r
3589                 if (m_ColumnManager.AnyUnusedProperties())\r
3590                 {\r
3591                         temp.LoadString(IDS_STATUSLIST_REMOVEUNUSEDPROPS);\r
3592                         popup.AppendMenu(uUnCheckedFlags, columnCount+1, temp);\r
3593                 }\r
3594 \r
3595                 temp.LoadString(IDS_STATUSLIST_RESETCOLUMNORDER);\r
3596                 popup.AppendMenu(uUnCheckedFlags, columnCount+2, temp);\r
3597                 popup.AppendMenu(MF_SEPARATOR);\r
3598 \r
3599                 // standard columns\r
3600 \r
3601                 for (int i = 1; i < SVNSLC_NUMCOLUMNS; ++i)\r
3602                 {\r
3603                         popup.AppendMenu ( m_ColumnManager.IsVisible(i) \r
3604                                 ? uCheckedFlags \r
3605                                 : uUnCheckedFlags\r
3606                                 , i\r
3607                                 , m_ColumnManager.GetName(i));\r
3608                 }\r
3609 \r
3610                 // user-prop columns:\r
3611                 // find relevant ones and sort 'em\r
3612 \r
3613                 std::map<CString, int> sortedProps;\r
3614                 for (int i = SVNSLC_NUMCOLUMNS; i < columnCount; ++i)\r
3615                         if (m_ColumnManager.IsRelevant(i))\r
3616                                 sortedProps[m_ColumnManager.GetName(i)] = i;\r
3617 \r
3618                 if (!sortedProps.empty())\r
3619                 {\r
3620                         // add 'em to the menu\r
3621 \r
3622                         popup.AppendMenu(MF_SEPARATOR);\r
3623 \r
3624                         typedef std::map<CString, int>::const_iterator CIT;\r
3625                         for ( CIT iter = sortedProps.begin(), end = sortedProps.end()\r
3626                                 ; iter != end\r
3627                                 ; ++iter)\r
3628                         {\r
3629                                 popup.AppendMenu ( m_ColumnManager.IsVisible(iter->second) \r
3630                                         ? uCheckedFlags \r
3631                                         : uUnCheckedFlags\r
3632                                         , iter->second\r
3633                                         , iter->first);\r
3634                         }\r
3635                 }\r
3636 \r
3637                 // show menu & let user pick an entry\r
3638 \r
3639                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
3640                 if ((cmd >= 1)&&(cmd < columnCount))\r
3641                 {\r
3642                         m_ColumnManager.SetVisible (cmd, !m_ColumnManager.IsVisible(cmd));\r
3643                 } \r
3644                 else if (cmd == columnCount)\r
3645                 {\r
3646                         EnableGroupView(!IsGroupViewEnabled());\r
3647                 } \r
3648                 else if (cmd == columnCount+1)\r
3649                 {\r
3650                         m_ColumnManager.RemoveUnusedProps();\r
3651                 } \r
3652                 else if (cmd == columnCount+2)\r
3653                 {\r
3654                         m_ColumnManager.ResetColumns (m_dwDefaultColumns);\r
3655                 }\r
3656         }\r
3657 }\r
3658 \r
3659 void CGitStatusListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)\r
3660 {\r
3661 \r
3662         if (pWnd == this)\r
3663         {\r
3664                 OnContextMenuList(pWnd, point);\r
3665         } // if (pWnd == this)\r
3666         else if (pWnd == GetHeaderCtrl())\r
3667         {\r
3668                 OnContextMenuHeader(pWnd, point);\r
3669         }\r
3670 }\r
3671 \r
3672 void CGitStatusListCtrl::CreateChangeList(const CString& name)\r
3673 {\r
3674 #if 0\r
3675         CTGitPathList changelistItems;\r
3676         FillListOfSelectedItemPaths(changelistItems);\r
3677         Git git;\r
3678         if (git.AddToChangeList(changelistItems, name, git_depth_empty))\r
3679         {\r
3680                 TCHAR groupname[1024];\r
3681                 LVGROUP grp = {0};\r
3682                 grp.cbSize = sizeof(LVGROUP);\r
3683                 grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;\r
3684                 _tcsncpy_s(groupname, 1024, name, 1023);\r
3685                 grp.pszHeader = groupname;\r
3686                 grp.iGroupId = (int)m_changelists.size();\r
3687                 grp.uAlign = LVGA_HEADER_LEFT;\r
3688                 m_changelists[name] = InsertGroup(-1, &grp);\r
3689 \r
3690                 PrepareGroups(true);\r
3691 \r
3692                 POSITION pos = GetFirstSelectedItemPosition();\r
3693                 int index;\r
3694                 while ((index = GetNextSelectedItem(pos)) >= 0)\r
3695                 {\r
3696                         FileEntry * e = GetListEntry(index);\r
3697                         e->changelist = name;\r
3698                         SetEntryCheck(e, index, FALSE);\r
3699                 }\r
3700 \r
3701                 for (index = 0; index < GetItemCount(); ++index)\r
3702                 {\r
3703                         FileEntry * e = GetListEntry(index);\r
3704                         if (m_changelists.find(e->changelist)!=m_changelists.end())\r
3705                                 SetItemGroup(index, m_changelists[e->changelist]);\r
3706                         else\r
3707                                 SetItemGroup(index, 0);\r
3708                 }\r
3709         }\r
3710         else\r
3711         {\r
3712                 CMessageBox::Show(m_hWnd, git.GetLastErrorMessage(), _T("Tortoisegit"), MB_ICONERROR);\r
3713         }\r
3714 #endif\r
3715 }\r
3716 \r
3717 void CGitStatusListCtrl::OnNMDblclk(NMHDR *pNMHDR, LRESULT *pResult)\r
3718 {\r
3719 #if 0\r
3720         Locker lock(m_critSec);\r
3721         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
3722         *pResult = 0;\r
3723         if (m_bBlock)\r
3724                 return;\r
3725         if (pNMLV->iItem < 0)\r
3726         {\r
3727                 if (!IsGroupViewEnabled())\r
3728                         return;\r
3729                 POINT pt;\r
3730                 DWORD ptW = GetMessagePos();\r
3731                 pt.x = GET_X_LPARAM(ptW);\r
3732                 pt.y = GET_Y_LPARAM(ptW);\r
3733                 ScreenToClient(&pt);\r
3734                 int group = GetGroupFromPoint(&pt);\r
3735                 if (group < 0)\r
3736                         return;\r
3737                 // check/uncheck the whole group depending on the check-state \r
3738                 // of the first item in the group\r
3739                 m_bBlock = true;\r
3740                 bool bCheck = false;\r
3741                 bool bFirst = false;\r
3742                 LVITEM lv;\r
3743                 for (int i=0; i<GetItemCount(); ++i)\r
3744                 {\r
3745                         SecureZeroMemory(&lv, sizeof(LVITEM));\r
3746                         lv.mask = LVIF_GROUPID;\r
3747                         lv.iItem = i;\r
3748                         GetItem(&lv);\r
3749                         if (lv.iGroupId == group)\r
3750                         {\r
3751                                 FileEntry * entry = GetListEntry(i);\r
3752                                 if (!bFirst)\r
3753                                 {\r
3754                                         bCheck = !GetCheck(i);\r
3755                                         bFirst = true;\r
3756                                 }\r
3757                                 if (entry)\r
3758                                 {\r
3759                                         bool bOldCheck = !!GetCheck(i);\r
3760                                         SetEntryCheck(entry, i, bCheck);\r
3761                                         if (bCheck != bOldCheck)\r
3762                                         {\r
3763                                                 if (bCheck)\r
3764                                                         m_nSelected++;\r
3765                                                 else\r
3766                                                         m_nSelected--;\r
3767                                         }\r
3768                                 }\r
3769                         }\r
3770                 }\r
3771                 GetStatisticsString();\r
3772                 m_bBlock = false;\r
3773                 NotifyCheck();\r
3774                 return;\r
3775         }\r
3776         FileEntry * entry = GetListEntry(pNMLV->iItem);\r
3777         if (entry)\r
3778         {\r
3779                 if (entry->isConflicted)\r
3780                 {\r
3781                         gitDiff::StartConflictEditor(entry->GetPath());\r
3782                 }\r
3783                 else\r
3784                 {\r
3785                         StartDiff(pNMLV->iItem);\r
3786                 }\r
3787         }\r
3788 #endif\r
3789 }\r
3790 \r
3791 void CGitStatusListCtrl::StartDiff(int fileindex)\r
3792 {\r
3793 #if 0\r
3794         if (fileindex < 0)\r
3795                 return;\r
3796         FileEntry * entry = GetListEntry(fileindex);\r
3797         ASSERT(entry != NULL);\r
3798         if (entry == NULL)\r
3799                 return;\r
3800         if (((entry->status == git_wc_status_normal)&&(entry->remotestatus <= git_wc_status_normal))||\r
3801                 (entry->status == git_wc_status_unversioned)||(entry->status == git_wc_status_none))\r
3802         {\r
3803                 int ret = (int)ShellExecute(this->m_hWnd, NULL, entry->path.GetWinPath(), NULL, NULL, SW_SHOW);\r
3804                 if (ret <= HINSTANCE_ERROR)\r
3805                 {\r
3806                         CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
3807                         cmd += entry->path.GetWinPathString();\r
3808                         CAppUtils::LaunchApplication(cmd, NULL, false);\r
3809                 }\r
3810                 return;\r
3811         }\r
3812 \r
3813         GitDiff diff(NULL, m_hWnd, true);\r
3814         diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
3815         diff.DiffWCFile(\r
3816                 entry->path, entry->textstatus, entry->propstatus,\r
3817                 entry->remotetextstatus, entry->remotepropstatus);\r
3818 #endif\r
3819 }\r
3820 \r
3821 CString CGitStatusListCtrl::GetStatisticsString()\r
3822 {\r
3823 #if 0\r
3824         CString sNormal, sAdded, sDeleted, sModified, sConflicted, sUnversioned;\r
3825         WORD langID = (WORD)(DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
3826         TCHAR buf[MAX_STATUS_STRING_LENGTH];\r
3827         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_normal, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3828         sNormal = buf;\r
3829         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_added, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3830         sAdded = buf;\r
3831         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_deleted, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3832         sDeleted = buf;\r
3833         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_modified, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3834         sModified = buf;\r
3835         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_conflicted, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3836         sConflicted = buf;\r
3837         GitStatus::GetStatusString(AfxGetResourceHandle(), git_wc_status_unversioned, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
3838         sUnversioned = buf;\r
3839         CString sToolTip;\r
3840         sToolTip.Format(_T("%s = %d\n%s = %d\n%s = %d\n%s = %d\n%s = %d\n%s = %d"),\r
3841                 (LPCTSTR)sNormal, m_nNormal,\r
3842                 (LPCTSTR)sUnversioned, m_nUnversioned,\r
3843                 (LPCTSTR)sModified, m_nModified,\r
3844                 (LPCTSTR)sAdded, m_nAdded,\r
3845                 (LPCTSTR)sDeleted, m_nDeleted,\r
3846                 (LPCTSTR)sConflicted, m_nConflicted\r
3847                 );\r
3848         CString sStats;\r
3849         sStats.Format(IDS_COMMITDLG_STATISTICSFORMAT, m_nSelected, GetItemCount());\r
3850         if (m_pStatLabel)\r
3851         {\r
3852                 m_pStatLabel->SetWindowText(sStats);\r
3853         }\r
3854 \r
3855         if (m_pSelectButton)\r
3856         {\r
3857                 if (m_nSelected == 0)\r
3858                         m_pSelectButton->SetCheck(BST_UNCHECKED);\r
3859                 else if (m_nSelected != GetItemCount())\r
3860                         m_pSelectButton->SetCheck(BST_INDETERMINATE);\r
3861                 else\r
3862                         m_pSelectButton->SetCheck(BST_CHECKED);\r
3863         }\r
3864 \r
3865         if (m_pConfirmButton)\r
3866         {\r
3867                 m_pConfirmButton->EnableWindow(m_nSelected>0);\r
3868         }\r
3869 \r
3870         return sToolTip;\r
3871 #endif\r
3872         return _T("");\r
3873 }\r
3874 \r
3875 CTGitPath CGitStatusListCtrl::GetCommonDirectory(bool bStrict)\r
3876 {\r
3877         if (!bStrict)\r
3878         {\r
3879                 // not strict means that the selected folder has priority\r
3880                 if (!m_StatusFileList.GetCommonDirectory().IsEmpty())\r
3881                         return m_StatusFileList.GetCommonDirectory();\r
3882         }\r
3883 \r
3884         CTGitPath commonBaseDirectory;\r
3885         int nListItems = GetItemCount();\r
3886         for (int i=0; i<nListItems; ++i)\r
3887         {\r
3888                 const CTGitPath& baseDirectory = GetListEntry(i)->GetPath().GetDirectory();\r
3889                 if(commonBaseDirectory.IsEmpty())\r
3890                 {\r
3891                         commonBaseDirectory = baseDirectory;\r
3892                 }\r
3893                 else\r
3894                 {\r
3895                         if (commonBaseDirectory.GetWinPathString().GetLength() > baseDirectory.GetWinPathString().GetLength())\r
3896                         {\r
3897                                 if (baseDirectory.IsAncestorOf(commonBaseDirectory))\r
3898                                         commonBaseDirectory = baseDirectory;\r
3899                         }\r
3900                 }\r
3901         }\r
3902         return commonBaseDirectory;\r
3903 }\r
3904 \r
3905 CTGitPath CGitStatusListCtrl::GetCommonURL(bool bStrict)\r
3906 {\r
3907         CTGitPath commonBaseURL;\r
3908 #if 0\r
3909         \r
3910         if (!bStrict)\r
3911         {\r
3912                 // not strict means that the selected folder has priority\r
3913                 if (!m_StatusUrlList.GetCommonDirectory().IsEmpty())\r
3914                         return m_StatusUrlList.GetCommonDirectory();\r
3915         }\r
3916 \r
3917         int nListItems = GetItemCount();\r
3918         for (int i=0; i<nListItems; ++i)\r
3919         {\r
3920                 const CTGitPath& baseURL = CTGitPath(GetListEntry(i)->GetURL());\r
3921                 if (baseURL.IsEmpty())\r
3922                         continue;                       // item has no url\r
3923                 if(commonBaseURL.IsEmpty())\r
3924                 {\r
3925                         commonBaseURL = baseURL;\r
3926                 }\r
3927                 else\r
3928                 {\r
3929                         if (commonBaseURL.GetGitPathString().GetLength() > baseURL.GetGitPathString().GetLength())\r
3930                         {\r
3931                                 if (baseURL.IsAncestorOf(commonBaseURL))\r
3932                                         commonBaseURL = baseURL;\r
3933                         }\r
3934                 }\r
3935         }\r
3936         \r
3937 #endif\r
3938         return commonBaseURL;\r
3939 }\r
3940 \r
3941 void CGitStatusListCtrl::SelectAll(bool bSelect, bool bIncludeNoCommits)\r
3942 {\r
3943         CWaitCursor waitCursor;\r
3944         // block here so the LVN_ITEMCHANGED messages\r
3945         // get ignored\r
3946         m_bBlock = TRUE;\r
3947         SetRedraw(FALSE);\r
3948 \r
3949         int nListItems = GetItemCount();\r
3950         if (bSelect)\r
3951                 m_nSelected = nListItems;\r
3952         else\r
3953                 m_nSelected = 0;\r
3954         for (int i=0; i<nListItems; ++i)\r
3955         {\r
3956                 FileEntry * entry = GetListEntry(i);\r
3957                 ASSERT(entry != NULL);\r
3958                 if (entry == NULL)\r
3959                         continue;\r
3960                 if ((bIncludeNoCommits)||(entry->GetChangeList().Compare(SVNSLC_IGNORECHANGELIST)))\r
3961                         SetEntryCheck(entry,i,bSelect);\r
3962         }\r
3963         // unblock before redrawing\r
3964         m_bBlock = FALSE;\r
3965         SetRedraw(TRUE);\r
3966         GetStatisticsString();\r
3967         NotifyCheck();\r
3968 }\r
3969 \r
3970 void CGitStatusListCtrl::OnLvnGetInfoTip(NMHDR *pNMHDR, LRESULT *pResult)\r
3971 {\r
3972         LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);\r
3973         *pResult = 0;\r
3974         if (m_bBlock)\r
3975                 return;\r
3976         if (GetListEntry(pGetInfoTip->iItem >= 0))\r
3977                 if (pGetInfoTip->cchTextMax > GetListEntry(pGetInfoTip->iItem)->path.GetGitPathString().GetLength())\r
3978                 {\r
3979                         if (GetListEntry(pGetInfoTip->iItem)->GetRelativeGitPath().Compare(GetListEntry(pGetInfoTip->iItem)->path.GetGitPathString())!= 0)\r
3980                                 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, GetListEntry(pGetInfoTip->iItem)->path.GetGitPathString(), pGetInfoTip->cchTextMax);\r
3981                         else if (GetStringWidth(GetListEntry(pGetInfoTip->iItem)->path.GetGitPathString()) > GetColumnWidth(pGetInfoTip->iItem))\r
3982                                 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, GetListEntry(pGetInfoTip->iItem)->path.GetGitPathString(), pGetInfoTip->cchTextMax);\r
3983                 }\r
3984 }\r
3985 \r
3986 void CGitStatusListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)\r
3987 {\r
3988         NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );\r
3989 \r
3990         // Take the default processing unless we set this to something else below.\r
3991         *pResult = CDRF_DODEFAULT;\r
3992 \r
3993         // First thing - check the draw stage. If it's the control's prepaint\r
3994         // stage, then tell Windows we want messages for every item.\r
3995 \r
3996         switch (pLVCD->nmcd.dwDrawStage)\r
3997         {\r
3998         case CDDS_PREPAINT:\r
3999                 *pResult = CDRF_NOTIFYITEMDRAW;\r
4000                 break;\r
4001         case CDDS_ITEMPREPAINT:\r
4002                 {\r
4003                         // This is the prepaint stage for an item. Here's where we set the\r
4004                         // item's text color. Our return value will tell Windows to draw the\r
4005                         // item itself, but it will use the new color we set here.\r
4006 \r
4007                         // Tell Windows to paint the control itself.\r
4008                         *pResult = CDRF_DODEFAULT;\r
4009                         if (m_bBlock)\r
4010                                 return;\r
4011 \r
4012                         COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);\r
4013 \r
4014                         if (m_arListArray.size() > (DWORD_PTR)pLVCD->nmcd.dwItemSpec)\r
4015                         {\r
4016                                 FileEntry * entry = GetListEntry((int)pLVCD->nmcd.dwItemSpec);\r
4017                                 if (entry == NULL)\r
4018                                         return;\r
4019 \r
4020                                 // coloring\r
4021                                 // ========\r
4022                                 // black  : unversioned, normal\r
4023                                 // purple : added\r
4024                                 // blue   : modified\r
4025                                 // brown  : missing, deleted, replaced\r
4026                                 // green  : merged (or potential merges)\r
4027                                 // red    : conflicts or sure conflicts\r
4028                                 switch (entry->status)\r
4029                                 {\r
4030                                 case git_wc_status_added:\r
4031 //                                      if (entry->remotestatus > git_wc_status_unversioned)\r
4032                                                 // locally added file, but file already exists in repository!\r
4033 //                                              crText = m_Colors.GetColor(CColors::Conflict);\r
4034 //                                      else\r
4035                                                 crText = m_Colors.GetColor(CColors::Added);\r
4036                                         break;\r
4037                                 case git_wc_status_missing:\r
4038                                 case git_wc_status_deleted:\r
4039                                 case git_wc_status_replaced:\r
4040                                         crText = m_Colors.GetColor(CColors::Deleted);\r
4041                                         break;\r
4042                                 case git_wc_status_modified:\r
4043 //                                      if (entry->remotestatus == git_wc_status_modified)\r
4044                                                 // indicate a merge (both local and remote changes will require a merge)\r
4045 //                                              crText = m_Colors.GetColor(CColors::Merged);\r
4046 //                                      else if (entry->remotestatus == git_wc_status_deleted)\r
4047 //                                              // locally modified, but already deleted in the repository\r
4048 //                                              crText = m_Colors.GetColor(CColors::Conflict);\r
4049 //                                      else\r
4050                                                 crText = m_Colors.GetColor(CColors::Modified);\r
4051                                         break;\r
4052                                 case git_wc_status_merged:\r
4053                                         crText = m_Colors.GetColor(CColors::Merged);\r
4054                                         break;\r
4055                                 case git_wc_status_conflicted:\r
4056                                 case git_wc_status_obstructed:\r
4057                                         crText = m_Colors.GetColor(CColors::Conflict);\r
4058                                         break;\r
4059                                 case git_wc_status_none:\r
4060                                 case git_wc_status_unversioned:\r
4061                                 case git_wc_status_ignored:\r
4062                                 case git_wc_status_incomplete:\r
4063                                 case git_wc_status_normal:\r
4064                                 case git_wc_status_external:\r
4065                                 default:\r
4066                                         crText = GetSysColor(COLOR_WINDOWTEXT);\r
4067                                         break;\r
4068                                 }\r
4069 \r
4070                                 if (entry->isConflicted)\r
4071                                         crText = m_Colors.GetColor(CColors::Conflict);\r
4072 \r
4073                                 // Store the color back in the NMLVCUSTOMDRAW struct.\r
4074                                 pLVCD->clrText = crText;\r
4075                         }\r
4076                 }\r
4077                 break;\r
4078         }\r
4079 }\r
4080 \r
4081 BOOL CGitStatusListCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)\r
4082 {\r
4083         if (pWnd != this)\r
4084                 return CListCtrl::OnSetCursor(pWnd, nHitTest, message);\r
4085         if (!m_bBlock)\r
4086         {\r
4087                 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
4088                 SetCursor(hCur);\r
4089                 return CListCtrl::OnSetCursor(pWnd, nHitTest, message);\r
4090         }\r
4091         HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));\r
4092         SetCursor(hCur);\r
4093         return TRUE;\r
4094 }\r
4095 \r
4096 void CGitStatusListCtrl::RemoveListEntry(int index)\r
4097 {\r
4098         Locker lock(m_critSec);\r
4099         DeleteItem(index);\r
4100         delete m_arStatusArray[m_arListArray[index]];\r
4101         m_arStatusArray.erase(m_arStatusArray.begin()+m_arListArray[index]);\r
4102         m_arListArray.erase(m_arListArray.begin()+index);\r
4103         for (int i=index; i< (int)m_arListArray.size(); ++i)\r
4104         {\r
4105                 m_arListArray[i]--;\r
4106         }\r
4107 }\r
4108 \r
4109 ///< Set a checkbox on an entry in the listbox\r
4110 // NEVER, EVER call SetCheck directly, because you'll end-up with the checkboxes and the 'checked' flag getting out of sync\r
4111 void CGitStatusListCtrl::SetEntryCheck(FileEntry* pEntry, int listboxIndex, bool bCheck)\r
4112 {\r
4113         pEntry->checked = bCheck;\r
4114         SetCheck(listboxIndex, bCheck);\r
4115 }\r
4116 \r
4117 \r
4118 void CGitStatusListCtrl::SetCheckOnAllDescendentsOf(const FileEntry* parentEntry, bool bCheck)\r
4119 {\r
4120         int nListItems = GetItemCount();\r
4121         for (int j=0; j< nListItems ; ++j)\r
4122         {\r
4123                 FileEntry * childEntry = GetListEntry(j);\r
4124                 ASSERT(childEntry != NULL);\r
4125                 if (childEntry == NULL || childEntry == parentEntry)\r
4126                         continue;\r
4127                 if (childEntry->checked != bCheck)\r
4128                 {\r
4129                         if (parentEntry->path.IsAncestorOf(childEntry->path))\r
4130                         {\r
4131                                 SetEntryCheck(childEntry,j,bCheck);\r
4132                                 if(bCheck)\r
4133                                 {\r
4134                                         m_nSelected++;\r
4135                                 }\r
4136                                 else\r
4137                                 {\r
4138                                         m_nSelected--;\r
4139                                 }\r
4140                         }\r
4141                 }\r
4142         }\r
4143 }\r
4144 \r
4145 void CGitStatusListCtrl::WriteCheckedNamesToPathList(CTGitPathList& pathList)\r
4146 {\r
4147         pathList.Clear();\r
4148         int nListItems = GetItemCount();\r
4149         for (int i=0; i< nListItems; i++)\r
4150         {\r
4151                 const FileEntry* entry = GetListEntry(i);\r
4152                 ASSERT(entry != NULL);\r
4153                 if (entry->IsChecked())\r
4154                 {\r
4155                         pathList.AddPath(entry->path);\r
4156                 }\r
4157         }\r
4158         pathList.SortByPathname();\r
4159 }\r
4160 \r
4161 \r
4162 /// Build a path list of all the selected items in the list (NOTE - SELECTED, not CHECKED)\r
4163 void CGitStatusListCtrl::FillListOfSelectedItemPaths(CTGitPathList& pathList, bool bNoIgnored)\r
4164 {\r
4165         pathList.Clear();\r
4166 \r
4167         POSITION pos = GetFirstSelectedItemPosition();\r
4168         int index;\r
4169         while ((index = GetNextSelectedItem(pos)) >= 0)\r
4170         {\r
4171                 FileEntry * entry = GetListEntry(index);\r
4172                 if ((bNoIgnored)&&(entry->status == git_wc_status_ignored))\r
4173                         continue;\r
4174                 pathList.AddPath(GetListEntry(index)->path);\r
4175         }\r
4176 }\r
4177 \r
4178 UINT CGitStatusListCtrl::OnGetDlgCode()\r
4179 {\r
4180         // we want to process the return key and not have that one\r
4181         // routed to the default pushbutton\r
4182         return CListCtrl::OnGetDlgCode() | DLGC_WANTALLKEYS;\r
4183 }\r
4184 \r
4185 void CGitStatusListCtrl::OnNMReturn(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
4186 {\r
4187         *pResult = 0;\r
4188         if (m_bBlock)\r
4189                 return;\r
4190         POSITION pos = GetFirstSelectedItemPosition();\r
4191         while ( pos )\r
4192         {\r
4193                 int index = GetNextSelectedItem(pos);\r
4194                 StartDiff(index);\r
4195         }\r
4196 }\r
4197 \r
4198 void CGitStatusListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\r
4199 {\r
4200         // Since we catch all keystrokes (to have the enter key processed here instead\r
4201         // of routed to the default pushbutton) we have to make sure that other\r
4202         // keys like Tab and Esc still do what they're supposed to do\r
4203         // Tab = change focus to next/previous control\r
4204         // Esc = quit the dialog\r
4205         switch (nChar)\r
4206         {\r
4207         case (VK_TAB):\r
4208                 {\r
4209                         ::PostMessage(GetParent()->GetSafeHwnd(), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT)&0x8000, 0);\r
4210                         return;\r
4211                 }\r
4212                 break;\r
4213         case (VK_ESCAPE):\r
4214                 {\r
4215                         ::SendMessage(GetParent()->GetSafeHwnd(), WM_CLOSE, 0, 0);\r
4216                 }\r
4217                 break;\r
4218         }\r
4219 \r
4220         CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);\r
4221 }\r
4222 \r
4223 void CGitStatusListCtrl::PreSubclassWindow()\r
4224 {\r
4225         CListCtrl::PreSubclassWindow();\r
4226         EnableToolTips(TRUE);\r
4227 }\r
4228 \r
4229 INT_PTR CGitStatusListCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const\r
4230 {\r
4231         int row, col;\r
4232         RECT cellrect;\r
4233         row = CellRectFromPoint(point, &cellrect, &col );\r
4234 \r
4235         if (row == -1)\r
4236                 return -1;\r
4237 \r
4238 \r
4239         pTI->hwnd = m_hWnd;\r
4240         pTI->uId = (UINT)((row<<10)+(col&0x3ff)+1);\r
4241         pTI->lpszText = LPSTR_TEXTCALLBACK;\r
4242 \r
4243         pTI->rect = cellrect;\r
4244 \r
4245         return pTI->uId;\r
4246 }\r
4247 \r
4248 int CGitStatusListCtrl::CellRectFromPoint(CPoint& point, RECT *cellrect, int *col) const\r
4249 {\r
4250         int colnum;\r
4251 \r
4252         // Make sure that the ListView is in LVS_REPORT\r
4253         if ((GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT)\r
4254                 return -1;\r
4255 \r
4256         // Get the top and bottom row visible\r
4257         int row = GetTopIndex();\r
4258         int bottom = row + GetCountPerPage();\r
4259         if (bottom > GetItemCount())\r
4260                 bottom = GetItemCount();\r
4261 \r
4262         // Get the number of columns\r
4263         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);\r
4264         int nColumnCount = pHeader->GetItemCount();\r
4265 \r
4266         // Loop through the visible rows\r
4267         for ( ;row <=bottom;row++)\r
4268         {\r
4269                 // Get bounding rect of item and check whether point falls in it.\r
4270                 CRect rect;\r
4271                 GetItemRect(row, &rect, LVIR_BOUNDS);\r
4272                 if (rect.PtInRect(point))\r
4273                 {\r
4274                         // Now find the column\r
4275                         for (colnum = 0; colnum < nColumnCount; colnum++)\r
4276                         {\r
4277                                 int colwidth = GetColumnWidth(colnum);\r
4278                                 if (point.x >= rect.left && point.x <= (rect.left + colwidth))\r
4279                                 {\r
4280                                         RECT rectClient;\r
4281                                         GetClientRect(&rectClient);\r
4282                                         if (col)\r
4283                                                 *col = colnum;\r
4284                                         rect.right = rect.left + colwidth;\r
4285 \r
4286                                         // Make sure that the right extent does not exceed\r
4287                                         // the client area\r
4288                                         if (rect.right > rectClient.right)\r
4289                                                 rect.right = rectClient.right;\r
4290                                         *cellrect = rect;\r
4291                                         return row;\r
4292                                 }\r
4293                                 rect.left += colwidth;\r
4294                         }\r
4295                 }\r
4296         }\r
4297         return -1;\r
4298 }\r
4299 \r
4300 BOOL CGitStatusListCtrl::OnToolTipText(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)\r
4301 {\r
4302 #if 0\r
4303         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;\r
4304         CString strTipText;\r
4305         UINT_PTR nID = pNMHDR->idFrom;\r
4306 \r
4307         if (nID == 0)\r
4308                 return FALSE;\r
4309 \r
4310         UINT_PTR row = ((nID-1) >> 10) & 0x3fffff;\r
4311         UINT_PTR col = (nID-1) & 0x3ff;\r
4312 \r
4313         if (col == 0)\r
4314                 return FALSE;   // no custom tooltip for the path, we use the infotip there!\r
4315 \r
4316         // get the internal column from the visible columns\r
4317         int internalcol = 0;\r
4318         UINT_PTR currentcol = 0;\r
4319     for (;    (currentcol != col) \r
4320            && (internalcol < m_ColumnManager.GetColumnCount()-1)\r
4321          ; ++internalcol)\r
4322         {\r
4323         if (m_ColumnManager.IsVisible (internalcol))\r
4324                         currentcol++;\r
4325         }\r
4326 \r
4327         AFX_MODULE_THREAD_STATE* pModuleThreadState = AfxGetModuleThreadState();\r
4328         CToolTipCtrl* pToolTip = pModuleThreadState->m_pToolTip;\r
4329         pToolTip->SendMessage(TTM_SETMAXTIPWIDTH, 0, 300);\r
4330 \r
4331         *pResult = 0;\r
4332         if ((internalcol == 2)||(internalcol == 4))\r
4333         {\r
4334                 FileEntry *fentry = GetListEntry(row);\r
4335                 if (fentry)\r
4336                 {\r
4337                         if (fentry->copied)\r
4338                         {\r
4339                                 CString url;\r
4340                                 url.Format(IDS_STATUSLIST_COPYFROM, (LPCTSTR)CPathUtils::PathUnescape(fentry->copyfrom_url), (LONG)fentry->copyfrom_rev);\r
4341                                 lstrcpyn(pTTTW->szText, (LPCTSTR)url, 80);\r
4342                                 return TRUE;\r
4343                         }\r
4344                         if (fentry->switched)\r
4345                         {\r
4346                                 CString url;\r
4347                                 url.Format(IDS_STATUSLIST_SWITCHEDTO, (LPCTSTR)CPathUtils::PathUnescape(fentry->url));\r
4348                                 lstrcpyn(pTTTW->szText, (LPCTSTR)url, 80);\r
4349                                 return TRUE;\r
4350                         }\r
4351                         if (fentry->keeplocal)\r
4352                         {\r
4353                                 lstrcpyn(pTTTW->szText, (LPCTSTR)CString(MAKEINTRESOURCE(IDS_STATUSLIST_KEEPLOCAL)), 80);\r
4354                                 return TRUE;\r
4355                         }\r
4356                 }\r
4357         }\r
4358 #endif\r
4359         return FALSE;\r
4360 }\r
4361 \r
4362 void CGitStatusListCtrl::OnPaint()\r
4363 {\r
4364         Default();\r
4365         if ((m_bBusy)||(m_bEmpty))\r
4366         {\r
4367                 CString str;\r
4368                 if (m_bBusy)\r
4369                 {\r
4370                         if (m_sBusy.IsEmpty())\r
4371                                 str.LoadString(IDS_STATUSLIST_BUSYMSG);\r
4372                         else\r
4373                                 str = m_sBusy;\r
4374                 }\r
4375                 else\r
4376                 {\r
4377                         if (m_sEmpty.IsEmpty())\r
4378                                 str.LoadString(IDS_STATUSLIST_EMPTYMSG);\r
4379                         else\r
4380                                 str = m_sEmpty;\r
4381                 }\r
4382                 COLORREF clrText = ::GetSysColor(COLOR_WINDOWTEXT);\r
4383                 COLORREF clrTextBk;\r
4384                 if (IsWindowEnabled())\r
4385                         clrTextBk = ::GetSysColor(COLOR_WINDOW);\r
4386                 else\r
4387                         clrTextBk = ::GetSysColor(COLOR_3DFACE);\r
4388 \r
4389                 CRect rc;\r
4390                 GetClientRect(&rc);\r
4391                 CHeaderCtrl* pHC;\r
4392                 pHC = GetHeaderCtrl();\r
4393                 if (pHC != NULL)\r
4394                 {\r
4395                         CRect rcH;\r
4396                         pHC->GetItemRect(0, &rcH);\r
4397                         rc.top += rcH.bottom;\r
4398                 }\r
4399                 CDC* pDC = GetDC();\r
4400                 {\r
4401                         CMyMemDC memDC(pDC, &rc);\r
4402 \r
4403                         memDC.SetTextColor(clrText);\r
4404                         memDC.SetBkColor(clrTextBk);\r
4405                         memDC.FillSolidRect(rc, clrTextBk);\r
4406                         rc.top += 10;\r
4407                         CGdiObject * oldfont = memDC.SelectStockObject(DEFAULT_GUI_FONT);\r
4408                         memDC.DrawText(str, rc, DT_CENTER | DT_VCENTER |\r
4409                                 DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);\r
4410                         memDC.SelectObject(oldfont);\r
4411                 }\r
4412                 ReleaseDC(pDC);\r
4413         }\r
4414 }\r
4415 \r
4416 // prevent users from extending our hidden (size 0) columns\r
4417 void CGitStatusListCtrl::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)\r
4418 {\r
4419         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
4420         *pResult = 0;\r
4421         if ((phdr->iItem < 0)||(phdr->iItem >= SVNSLC_NUMCOLUMNS))\r
4422                 return;\r
4423 \r
4424     if (m_ColumnManager.IsVisible (phdr->iItem))\r
4425         {\r
4426                 return;\r
4427         }\r
4428         *pResult = 1;\r
4429 }\r
4430 \r
4431 // prevent any function from extending our hidden (size 0) columns\r
4432 void CGitStatusListCtrl::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)\r
4433 {\r
4434         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
4435         *pResult = 0;\r
4436     if ((phdr->iItem < 0)||(phdr->iItem >= m_ColumnManager.GetColumnCount()))\r
4437         {\r
4438                 Default();\r
4439                 return;\r
4440         }\r
4441 \r
4442     // visible columns may be modified \r
4443 \r
4444     if (m_ColumnManager.IsVisible (phdr->iItem))\r
4445         {\r
4446                 Default();\r
4447                 return;\r
4448         }\r
4449 \r
4450     // columns already marked as "invisible" internally may be (re-)sized to 0\r
4451 \r
4452     if (   (phdr->pitem != NULL) \r
4453         && (phdr->pitem->mask == HDI_WIDTH)\r
4454         && (phdr->pitem->cxy == 0))\r
4455         {\r
4456                 Default();\r
4457                 return;\r
4458         }\r
4459 \r
4460         if (   (phdr->pitem != NULL) \r
4461                 && (phdr->pitem->mask != HDI_WIDTH))\r
4462         {\r
4463                 Default();\r
4464                 return;\r
4465         }\r
4466 \r
4467     *pResult = 1;\r
4468 }\r
4469 \r
4470 void CGitStatusListCtrl::OnDestroy()\r
4471 {\r
4472         SaveColumnWidths(true);\r
4473         CListCtrl::OnDestroy();\r
4474 }\r
4475 \r
4476 void CGitStatusListCtrl::OnBeginDrag(NMHDR* /*pNMHDR*/, LRESULT* pResult)\r
4477 {\r
4478 #if 0\r
4479         Locker lock(m_critSec);\r
4480         CDropFiles dropFiles; // class for creating DROPFILES struct\r
4481 \r
4482         int index;\r
4483         POSITION pos = GetFirstSelectedItemPosition();\r
4484         while ( (index = GetNextSelectedItem(pos)) >= 0 )\r
4485         {\r
4486                 FileEntry * fentry = m_arStatusArray[m_arListArray[index]];\r
4487                 CTGitPath path = fentry->GetPath();\r
4488                 dropFiles.AddFile( path.GetWinPathString() );\r
4489         }\r
4490 \r
4491         if ( dropFiles.GetCount()>0 )\r
4492         {\r
4493                 m_bOwnDrag = true;\r
4494                 dropFiles.CreateStructure();\r
4495                 m_bOwnDrag = false;\r
4496         }\r
4497 #endif\r
4498         *pResult = 0;\r
4499 }\r
4500 \r
4501 void CGitStatusListCtrl::SaveColumnWidths(bool bSaveToRegistry /* = false */)\r
4502 {\r
4503         int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
4504         for (int col = 0; col <= maxcol; col++)\r
4505         if (m_ColumnManager.IsVisible (col))\r
4506             m_ColumnManager.ColumnResized (col);\r
4507 \r
4508         if (bSaveToRegistry)\r
4509         m_ColumnManager.WriteSettings();\r
4510 }\r
4511 \r
4512 bool CGitStatusListCtrl::EnableFileDrop()\r
4513 {\r
4514         m_bFileDropsEnabled = true;\r
4515         return true;\r
4516 }\r
4517 \r
4518 bool CGitStatusListCtrl::HasPath(const CTGitPath& path)\r
4519 {\r
4520         for (size_t i=0; i < m_arStatusArray.size(); i++)\r
4521         {\r
4522                 FileEntry * entry = m_arStatusArray[i];\r
4523                 if (entry->GetPath().IsEquivalentTo(path))\r
4524                         return true;\r
4525         }\r
4526         return false;\r
4527 }\r
4528 \r
4529 bool CGitStatusListCtrl::IsPathShown(const CTGitPath& path)\r
4530 {\r
4531         int itemCount = GetItemCount();\r
4532         for (int i=0; i < itemCount; i++)\r
4533         {\r
4534                 FileEntry * entry = GetListEntry(i);\r
4535                 if (entry->GetPath().IsEquivalentTo(path))\r
4536                         return true;\r
4537         }\r
4538         return false;\r
4539 }\r
4540 \r
4541 BOOL CGitStatusListCtrl::PreTranslateMessage(MSG* pMsg)\r
4542 {\r
4543         if (pMsg->message == WM_KEYDOWN)\r
4544         {\r
4545                 switch (pMsg->wParam)\r
4546                 {\r
4547                 case 'A':\r
4548                         {\r
4549                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
4550                                 {\r
4551                                         // select all entries\r
4552                                         for (int i=0; i<GetItemCount(); ++i)\r
4553                                         {\r
4554                                                 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);\r
4555                                         }\r
4556                                         return TRUE;\r
4557                                 }\r
4558                         }\r
4559                         break;\r
4560                 case 'C':\r
4561                 case VK_INSERT:\r
4562                         {\r
4563                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
4564                                 {\r
4565                                         // copy all selected paths to the clipboard\r
4566                                         if (GetAsyncKeyState(VK_SHIFT)&0x8000)\r
4567                                                 CopySelectedEntriesToClipboard(SVNSLC_COLSTATUS|SVNSLC_COLURL);\r
4568                                         else\r
4569                                                 CopySelectedEntriesToClipboard(0);\r
4570                                         return TRUE;\r
4571                                 }\r
4572                         }\r
4573                         break;\r
4574                 }\r
4575         }\r
4576 \r
4577         return CListCtrl::PreTranslateMessage(pMsg);\r
4578 }\r
4579 \r
4580 bool CGitStatusListCtrl::CopySelectedEntriesToClipboard(DWORD dwCols)\r
4581 {\r
4582 #if 0\r
4583         static CString ponly(MAKEINTRESOURCE(IDS_STATUSLIST_PROPONLY));\r
4584         static HINSTANCE hResourceHandle(AfxGetResourceHandle());\r
4585         WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
4586 \r
4587         CString sClipboard;\r
4588         CString temp;\r
4589         TCHAR buf[100];\r
4590         if (GetSelectedCount() == 0)\r
4591                 return false;\r
4592         // first add the column titles as the first line\r
4593         temp.LoadString(IDS_STATUSLIST_COLFILE);\r
4594         sClipboard = temp;\r
4595 \r
4596     DWORD selection = 0;\r
4597     for (int i = 0, count = m_ColumnManager.GetColumnCount(); i < count; ++i)\r
4598         if (   ((dwCols == -1) && m_ColumnManager.IsVisible (i))\r
4599             || ((dwCols != 1) && (i < 32) && ((dwCols & (1 << i)) != 0)))\r
4600         {\r
4601             sClipboard += _T("\t") + m_ColumnManager.GetName(i);\r
4602 \r
4603             if (i < 32)\r
4604                 selection += 1 << i;\r
4605         }\r
4606 \r
4607         sClipboard += _T("\r\n");\r
4608 \r
4609         POSITION pos = GetFirstSelectedItemPosition();\r
4610         int index;\r
4611         while ((index = GetNextSelectedItem(pos)) >= 0)\r
4612         {\r
4613                 FileEntry * entry = GetListEntry(index);\r
4614                 sClipboard += entry->GetDisplayName();\r
4615                 if (selection & SVNSLC_COLFILENAME)\r
4616                 {\r
4617                         sClipboard += _T("\t")+entry->path.GetFileOrDirectoryName();\r
4618                 }\r
4619                 if (selection & SVNSLC_COLEXT)\r
4620                 {\r
4621                         sClipboard += _T("\t")+entry->path.GetFileExtension();\r
4622                 }\r
4623                 if (selection & SVNSLC_COLSTATUS)\r
4624                 {\r
4625                         if (entry->isNested)\r
4626                         {\r
4627                                 temp.LoadString(IDS_STATUSLIST_NESTED);\r
4628                         }\r
4629                         else\r
4630                         {\r
4631                                 GitStatus::GetStatusString(hResourceHandle, entry->status, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4632                                 if ((entry->copied)&&(_tcslen(buf)>1))\r
4633                                         _tcscat_s(buf, 100, _T(" (+)"));\r
4634                                 if ((entry->switched)&&(_tcslen(buf)>1))\r
4635                                         _tcscat_s(buf, 100, _T(" (s)"));\r
4636                                 if ((entry->status == entry->propstatus)&&\r
4637                                         (entry->status != git_wc_status_normal)&&\r
4638                                         (entry->status != git_wc_status_unversioned)&&\r
4639                                         (!GitStatus::IsImportant(entry->textstatus)))\r
4640                                         _tcscat_s(buf, 100, ponly);\r
4641                                 temp = buf;\r
4642                         }\r
4643                         sClipboard += _T("\t")+temp;\r
4644                 }\r
4645                 if (selection & SVNSLC_COLTEXTSTATUS)\r
4646                 {\r
4647                         if (entry->isNested)\r
4648                         {\r
4649                                 temp.LoadString(IDS_STATUSLIST_NESTED);\r
4650                         }\r
4651                         else\r
4652                         {\r
4653                                 GitStatus::GetStatusString(hResourceHandle, entry->textstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4654                                 if ((entry->copied)&&(_tcslen(buf)>1))\r
4655                                         _tcscat_s(buf, 100, _T(" (+)"));\r
4656                                 if ((entry->switched)&&(_tcslen(buf)>1))\r
4657                                         _tcscat_s(buf, 100, _T(" (s)"));\r
4658                                 temp = buf;\r
4659                         }\r
4660                         sClipboard += _T("\t")+temp;\r
4661                 }\r
4662                 if (selection & SVNSLC_COLREMOTESTATUS)\r
4663                 {\r
4664                         if (entry->isNested)\r
4665                         {\r
4666                                 temp.LoadString(IDS_STATUSLIST_NESTED);\r
4667                         }\r
4668                         else\r
4669                         {\r
4670                                 GitStatus::GetStatusString(hResourceHandle, entry->remotestatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4671                                 if ((entry->copied)&&(_tcslen(buf)>1))\r
4672                                         _tcscat_s(buf, 100, _T(" (+)"));\r
4673                                 if ((entry->switched)&&(_tcslen(buf)>1))\r
4674                                         _tcscat_s(buf, 100, _T(" (s)"));\r
4675                                 if ((entry->remotestatus == entry->remotepropstatus)&&\r
4676                                         (entry->remotestatus != git_wc_status_none)&&\r
4677                                         (entry->remotestatus != git_wc_status_normal)&&\r
4678                                         (entry->remotestatus != git_wc_status_unversioned)&&\r
4679                                         (!SVNStatus::IsImportant(entry->remotetextstatus)))\r
4680                                         _tcscat_s(buf, 100, ponly);\r
4681                                 temp = buf;\r
4682                         }\r
4683                         sClipboard += _T("\t")+temp;\r
4684                 }\r
4685                 if (selection & GitSLC_COLPROPSTATUS)\r
4686                 {\r
4687                         if (entry->isNested)\r
4688                         {\r
4689                                 temp.Empty();\r
4690                         }\r
4691                         else\r
4692                         {\r
4693                                 GitStatus::GetStatusString(hResourceHandle, entry->propstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4694                                 if ((entry->copied)&&(_tcslen(buf)>1))\r
4695                                         _tcscat_s(buf, 100, _T(" (+)"));\r
4696                                 if ((entry->switched)&&(_tcslen(buf)>1))\r
4697                                         _tcscat_s(buf, 100, _T(" (s)"));\r
4698                                 temp = buf;\r
4699                         }\r
4700                         sClipboard += _T("\t")+temp;\r
4701                 }\r
4702                 if (selection & SVNSLC_COLREMOTETEXT)\r
4703                 {\r
4704                         if (entry->isNested)\r
4705                         {\r
4706                                 temp.Empty();\r
4707                         }\r
4708                         else\r
4709                         {\r
4710                                 GitStatus::GetStatusString(hResourceHandle, entry->remotetextstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4711                                 temp = buf;\r
4712                         }\r
4713                         sClipboard += _T("\t")+temp;\r
4714                 }\r
4715                 if (selection & SVNSLC_COLREMOTEPROP)\r
4716                 {\r
4717                         // SVNSLC_COLREMOTEPROP\r
4718                         if (entry->isNested)\r
4719                         {\r
4720                                 temp.Empty();\r
4721                         }\r
4722                         else\r
4723                         {\r
4724                                 GitStatus::GetStatusString(hResourceHandle, entry->remotepropstatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)langID);\r
4725                                 temp = buf;\r
4726                         }\r
4727                         sClipboard += _T("\t")+temp;\r
4728                 }\r
4729                 if (selection & SVNSLC_COLURL)\r
4730                         sClipboard += _T("\t")+entry->url;\r
4731                 if (selection & SVNSLC_COLLOCK)\r
4732                 {\r
4733                         if (!m_HeadRev.IsHead())\r
4734                         {\r
4735                                 // we have contacted the repository\r
4736 \r
4737                                 // decision-matrix\r
4738                                 // wc           repository              text\r
4739                                 // ""           ""                              ""\r
4740                                 // ""           UID1                    owner\r
4741                                 // UID1         UID1                    owner\r
4742                                 // UID1         ""                              lock has been broken\r
4743                                 // UID1         UID2                    lock has been stolen\r
4744                                 if (entry->lock_token.IsEmpty() || (entry->lock_token.Compare(entry->lock_remotetoken)==0))\r
4745                                 {\r
4746                                         if (entry->lock_owner.IsEmpty())\r
4747                                                 temp = entry->lock_remoteowner;\r
4748                                         else\r
4749                                                 temp = entry->lock_owner;\r
4750                                 }\r
4751                                 else if (entry->lock_remotetoken.IsEmpty())\r
4752                                 {\r
4753                                         // broken lock\r
4754                                         temp.LoadString(IDS_STATUSLIST_LOCKBROKEN);\r
4755                                 }\r
4756                                 else\r
4757                                 {\r
4758                                         // stolen lock\r
4759                                         temp.Format(IDS_STATUSLIST_LOCKSTOLEN, (LPCTSTR)entry->lock_remoteowner);\r
4760                                 }\r
4761                         }\r
4762                         else\r
4763                                 temp = entry->lock_owner;\r
4764                         sClipboard += _T("\t")+temp;\r
4765                 }\r
4766                 if (selection & SVNSLC_COLLOCKCOMMENT)\r
4767                         sClipboard += _T("\t")+entry->lock_comment;\r
4768                 if (selection & SVNSLC_COLAUTHOR)\r
4769                         sClipboard += _T("\t")+entry->last_commit_author;\r
4770                 if (selection & SVNSLC_COLREVISION)\r
4771                 {\r
4772                         temp.Format(_T("%ld"), entry->last_commit_rev);\r
4773                         if (entry->last_commit_rev == 0)\r
4774                                 temp.Empty();\r
4775                         sClipboard += _T("\t")+temp;\r
4776                 }\r
4777                 if (selection & SVNSLC_COLREMOTEREVISION)\r
4778                 {\r
4779                         temp.Format(_T("%ld"), entry->remoterev);\r
4780                         if (entry->remoterev == 0)\r
4781                                 temp.Empty();\r
4782                         sClipboard += _T("\t")+temp;\r
4783                 }\r
4784                 if (selection & SVNSLC_COLDATE)\r
4785                 {\r
4786                         TCHAR datebuf[SVN_DATE_BUFFER];\r
4787                         apr_time_t date = entry->last_commit_date;\r
4788                         SVN::formatDate(datebuf, date, true);\r
4789                         if (date)\r
4790                                 temp = datebuf;\r
4791                         else\r
4792                                 temp.Empty();\r
4793                         sClipboard += _T("\t")+temp;\r
4794                 }\r
4795                 if (selection & SVNSLC_COLCOPYFROM)\r
4796                 {\r
4797                         if (m_sURL.Compare(entry->copyfrom_url.Left(m_sURL.GetLength()))==0)\r
4798                                 temp = entry->copyfrom_url.Mid(m_sURL.GetLength());\r
4799                         else\r
4800                                 temp = entry->copyfrom_url;\r
4801                         sClipboard += _T("\t")+temp;\r
4802                 }\r
4803 \r
4804         for ( int i = SVNSLC_NUMCOLUMNS, count = m_ColumnManager.GetColumnCount()\r
4805             ; i < count\r
4806             ; ++i)\r
4807         {\r
4808             if ((dwCols == -1) && m_ColumnManager.IsVisible (i))\r
4809             {\r
4810                 CString value \r
4811                     = entry->present_props[m_ColumnManager.GetName(i)];\r
4812                 sClipboard += _T("\t") + value;\r
4813             }\r
4814         }\r
4815 \r
4816                 sClipboard += _T("\r\n");\r
4817         }\r
4818 \r
4819         return CStringUtils::WriteAsciiStringToClipboard(sClipboard);\r
4820 #endif\r
4821         return TRUE;\r
4822 \r
4823 }\r
4824 \r
4825 size_t CGitStatusListCtrl::GetNumberOfChangelistsInSelection()\r
4826 {\r
4827         std::set<CString> changelists;\r
4828         POSITION pos = GetFirstSelectedItemPosition();\r
4829         int index;\r
4830         while ((index = GetNextSelectedItem(pos)) >= 0)\r
4831         {\r
4832                 FileEntry * entry = GetListEntry(index);\r
4833                 if (!entry->changelist.IsEmpty())\r
4834                         changelists.insert(entry->changelist);\r
4835         }\r
4836         return changelists.size();\r
4837 }\r
4838 \r
4839 bool CGitStatusListCtrl::PrepareGroups(bool bForce /* = false */)\r
4840 {\r
4841         if ((m_dwContextMenus & SVNSLC_POPCHANGELISTS) == NULL)\r
4842                 return false;   // don't show groups\r
4843 \r
4844         bool bHasGroups = (m_changelists.size() > 0)||(bForce);\r
4845         RemoveAllGroups();\r
4846         EnableGroupView(bHasGroups);\r
4847 \r
4848         TCHAR groupname[1024];\r
4849 \r
4850         m_bHasIgnoreGroup = false;\r
4851 \r
4852         // add a new group for each changelist\r
4853         int groupindex = 0;\r
4854 \r
4855         // now add the items which don't belong to a group\r
4856         LVGROUP grp = {0};\r
4857         grp.cbSize = sizeof(LVGROUP);\r
4858         grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;\r
4859         CString sUnassignedName(MAKEINTRESOURCE(IDS_STATUSLIST_UNASSIGNED_CHANGESET));\r
4860         _tcsncpy_s(groupname, 1024, (LPCTSTR)sUnassignedName, 1023);\r
4861         grp.pszHeader = groupname;\r
4862         grp.iGroupId = groupindex;\r
4863         grp.uAlign = LVGA_HEADER_LEFT;\r
4864         InsertGroup(groupindex++, &grp);\r
4865 \r
4866         for (std::map<CString,int>::iterator it = m_changelists.begin(); it != m_changelists.end(); ++it)\r
4867         {\r
4868                 if (it->first.Compare(SVNSLC_IGNORECHANGELIST)!=0)\r
4869                 {\r
4870                         LVGROUP grp = {0};\r
4871                         grp.cbSize = sizeof(LVGROUP);\r
4872                         grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;\r
4873                         _tcsncpy_s(groupname, 1024, it->first, 1023);\r
4874                         grp.pszHeader = groupname;\r
4875                         grp.iGroupId = groupindex;\r
4876                         grp.uAlign = LVGA_HEADER_LEFT;\r
4877                         it->second = InsertGroup(groupindex++, &grp);\r
4878                 }\r
4879                 else\r
4880                         m_bHasIgnoreGroup = true;\r
4881         }\r
4882 \r
4883         if (m_bHasIgnoreGroup)\r
4884         {\r
4885                 // and now add the group 'ignore-on-commit'\r
4886                 std::map<CString,int>::iterator it = m_changelists.find(SVNSLC_IGNORECHANGELIST);\r
4887                 if (it != m_changelists.end())\r
4888                 {\r
4889                         grp.cbSize = sizeof(LVGROUP);\r
4890                         grp.mask = LVGF_ALIGN | LVGF_GROUPID | LVGF_HEADER;\r
4891                         _tcsncpy_s(groupname, 1024, SVNSLC_IGNORECHANGELIST, 1023);\r
4892                         grp.pszHeader = groupname;\r
4893                         grp.iGroupId = groupindex;\r
4894                         grp.uAlign = LVGA_HEADER_LEFT;\r
4895                         it->second = InsertGroup(groupindex, &grp);\r
4896                 }\r
4897         }\r
4898 \r
4899         return bHasGroups;\r
4900 }\r
4901 \r
4902 void CGitStatusListCtrl::NotifyCheck()\r
4903 {\r
4904         CWnd* pParent = GetParent();\r
4905         if (NULL != pParent && NULL != pParent->GetSafeHwnd())\r
4906         {\r
4907                 pParent->SendMessage(SVNSLNM_CHECKCHANGED, m_nSelected);\r
4908         }\r
4909 }\r
4910 \r
4911 \r
4912 //////////////////////////////////////////////////////////////////////////\r
4913 #if 0\r
4914 bool CGitStatusListCtrlDropTarget::OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium, DWORD * /*pdwEffect*/, POINTL pt)\r
4915 {\r
4916         if(pFmtEtc->cfFormat == CF_HDROP && medium.tymed == TYMED_HGLOBAL)\r
4917         {\r
4918                 HDROP hDrop = (HDROP)GlobalLock(medium.hGlobal);\r
4919                 if(hDrop != NULL)\r
4920                 {\r
4921                         TCHAR szFileName[MAX_PATH];\r
4922 \r
4923                         UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);\r
4924 \r
4925                         POINT clientpoint;\r
4926                         clientpoint.x = pt.x;\r
4927                         clientpoint.y = pt.y;\r
4928                         ScreenToClient(m_hTargetWnd, &clientpoint);\r
4929                         if ((m_pSVNStatusListCtrl->IsGroupViewEnabled())&&(m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint) >= 0))\r
4930                         {\r
4931                                 CTSVNPathList changelistItems;\r
4932                                 for(UINT i = 0; i < cFiles; ++i)\r
4933                                 {\r
4934                                         DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));\r
4935                                         changelistItems.AddPath(CTSVNPath(szFileName));\r
4936                                 }\r
4937                                 // find the changelist name\r
4938                                 CString sChangelist;\r
4939                                 LONG_PTR nGroup = m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint);\r
4940                                 for (std::map<CString, int>::iterator it = m_pSVNStatusListCtrl->m_changelists.begin(); it != m_pSVNStatusListCtrl->m_changelists.end(); ++it)\r
4941                                         if (it->second == nGroup)\r
4942                                                 sChangelist = it->first;\r
4943                                 if (!sChangelist.IsEmpty())\r
4944                                 {\r
4945                                         SVN git;\r
4946                                         if (git.AddToChangeList(changelistItems, sChangelist, git_depth_empty))\r
4947                                         {\r
4948                                                 for (int l=0; l<changelistItems.GetCount(); ++l)\r
4949                                                 {\r
4950                                                         int index = m_pSVNStatusListCtrl->GetIndex(changelistItems[l]);\r
4951                                                         if (index >= 0)\r
4952                                                         {\r
4953                                                                 CSVNStatusListCtrl::FileEntry * e = m_pSVNStatusListCtrl->GetListEntry(index);\r
4954                                                                 if (e)\r
4955                                                                 {\r
4956                                                                         e->changelist = sChangelist;\r
4957                                                                         if (!e->IsFolder())\r
4958                                                                         {\r
4959                                                                                 if (m_pSVNStatusListCtrl->m_changelists.find(e->changelist)!=m_pSVNStatusListCtrl->m_changelists.end())\r
4960                                                                                         m_pSVNStatusListCtrl->SetItemGroup(index, m_pSVNStatusListCtrl->m_changelists[e->changelist]);\r
4961                                                                                 else\r
4962                                                                                         m_pSVNStatusListCtrl->SetItemGroup(index, 0);\r
4963                                                                         }\r
4964                                                                 }\r
4965                                                         }\r
4966                                                         else\r
4967                                                         {\r
4968                                                                 HWND hParentWnd = GetParent(m_hTargetWnd);\r
4969                                                                 if (hParentWnd != NULL)\r
4970                                                                         ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)changelistItems[l].GetWinPath());\r
4971                                                         }\r
4972                                                 }\r
4973                                         }\r
4974                                         else\r
4975                                         {\r
4976                                                 CMessageBox::Show(m_pSVNStatusListCtrl->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
4977                                         }\r
4978                                 }\r
4979                                 else\r
4980                                 {\r
4981                                         SVN git;\r
4982                                         if (git.RemoveFromChangeList(changelistItems, CStringArray(), git_depth_empty))\r
4983                                         {\r
4984                                                 for (int l=0; l<changelistItems.GetCount(); ++l)\r
4985                                                 {\r
4986                                                         int index = m_pSVNStatusListCtrl->GetIndex(changelistItems[l]);\r
4987                                                         if (index >= 0)\r
4988                                                         {\r
4989                                                                 CSVNStatusListCtrl::FileEntry * e = m_pSVNStatusListCtrl->GetListEntry(index);\r
4990                                                                 if (e)\r
4991                                                                 {\r
4992                                                                         e->changelist = sChangelist;\r
4993                                                                         m_pSVNStatusListCtrl->SetItemGroup(index, 0);\r
4994                                                                 }\r
4995                                                         }\r
4996                                                         else\r
4997                                                         {\r
4998                                                                 HWND hParentWnd = GetParent(m_hTargetWnd);\r
4999                                                                 if (hParentWnd != NULL)\r
5000                                                                         ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)changelistItems[l].GetWinPath());\r
5001                                                         }\r
5002                                                 }\r
5003                                         }\r
5004                                         else\r
5005                                         {\r
5006                                                 CMessageBox::Show(m_pSVNStatusListCtrl->m_hWnd, git.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
5007                                         }\r
5008                                 }\r
5009                         }\r
5010                         else\r
5011                         {\r
5012                                 for(UINT i = 0; i < cFiles; ++i)\r
5013                                 {\r
5014                                         DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));\r
5015                                         HWND hParentWnd = GetParent(m_hTargetWnd);\r
5016                                         if (hParentWnd != NULL)\r
5017                                                 ::SendMessage(hParentWnd, CSVNStatusListCtrl::SVNSLNM_ADDFILE, 0, (LPARAM)szFileName);\r
5018                                 }\r
5019                         }\r
5020                 }\r
5021                 GlobalUnlock(medium.hGlobal);\r
5022         }\r
5023         return true; //let base free the medium\r
5024 }\r
5025 HRESULT STDMETHODCALLTYPE CSVNStatusListCtrlDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect)\r
5026 {\r
5027         CIDropTarget::DragOver(grfKeyState, pt, pdwEffect);\r
5028         *pdwEffect = DROPEFFECT_COPY;\r
5029         if (m_pSVNStatusListCtrl)\r
5030         {\r
5031                 POINT clientpoint;\r
5032                 clientpoint.x = pt.x;\r
5033                 clientpoint.y = pt.y;\r
5034                 ScreenToClient(m_hTargetWnd, &clientpoint);\r
5035                 if ((m_pSVNStatusListCtrl->IsGroupViewEnabled())&&(m_pSVNStatusListCtrl->GetGroupFromPoint(&clientpoint) >= 0))\r
5036                 {\r
5037                         *pdwEffect = DROPEFFECT_MOVE;\r
5038                 }\r
5039                 else if ((!m_pSVNStatusListCtrl->m_bFileDropsEnabled)||(m_pSVNStatusListCtrl->m_bOwnDrag))\r
5040                 {\r
5041                         *pdwEffect = DROPEFFECT_NONE;\r
5042                 }\r
5043         }\r
5044         return S_OK;\r
5045 }\r
5046 \r
5047 #endif