1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
24 * @brief Main implementation file for CDirView
29 #include "Constants.h"
31 #include "ClipBoard.h"
32 #include "DirActions.h"
33 #include "SourceControl.h"
34 #include "DirViewColItems.h"
35 #include "DirFrame.h" // StatePane
37 #include "IMergeDoc.h"
38 #include "FileLocation.h"
41 #include "FileTransform.h"
42 #include "SelectUnpackerDlg.h"
45 #include "OptionsDef.h"
46 #include "OptionsMgr.h"
48 #include "DirCmpReport.h"
49 #include "DirCompProgressBar.h"
50 #include "CompareStatisticsDlg.h"
51 #include "LoadSaveCodepageDlg.h"
52 #include "ConfirmFolderCopyDlg.h"
53 #include "DirColsDlg.h"
55 #include "ShellContextMenu.h"
57 #include "IListCtrlImpl.h"
58 #include "Merge7zFormatMergePluginImpl.h"
59 #include "FileOrFolderSelect.h"
60 #include "IntToIntMap.h"
61 #include "PatchTool.h"
70 using namespace std::placeholders;
73 * @brief Location for folder compare specific help to open.
75 static TCHAR DirViewHelpLocation[] = _T("::/htmlhelp/Compare_dirs.html");
78 * @brief Limit (in seconds) to signal compare is ready for user.
79 * If compare takes longer than this value (in seconds) we inform
80 * user about it. Current implementation uses MessageBeep(IDOK).
82 const int TimeToSignalCompare = 3;
84 // The resource ID constants/limits for the Shell context menu
85 const UINT LeftCmdFirst = 0x9000; // this should be greater than any of already defined command IDs
86 const UINT RightCmdLast = 0xffff; // maximum available value
87 const UINT LeftCmdLast = LeftCmdFirst + (RightCmdLast - LeftCmdFirst) / 3; // divide available range equally between two context menus
88 const UINT MiddleCmdFirst = LeftCmdLast + 1;
89 const UINT MiddleCmdLast = MiddleCmdFirst + (RightCmdLast - LeftCmdFirst) / 3;
90 const UINT RightCmdFirst = MiddleCmdLast + 1;
92 /////////////////////////////////////////////////////////////////////////////
97 STATUSBAR_UPDATE = 100
100 IMPLEMENT_DYNCREATE(CDirView, CListView)
105 , m_bNeedSearchFirstDiffItem(true)
106 , m_bNeedSearchLastDiffItem(true)
107 , m_firstDiffItem(-1)
109 , m_pCmpProgressBar(nullptr)
112 , m_dirfilter(std::bind(&COptionsMgr::GetBool, GetOptionsMgr(), _1))
113 , m_pShellContextMenuLeft(nullptr)
114 , m_pShellContextMenuMiddle(nullptr)
115 , m_pShellContextMenuRight(nullptr)
116 , m_hCurrentMenu(nullptr)
117 , m_pSavedTreeState(nullptr)
118 , m_pColItems(nullptr)
121 m_dwDefaultStyle &= ~LVS_TYPEMASK;
122 // Show selection all the time, so user can see current item even when
123 // focus is elsewhere (ie, on file edit window)
124 m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS;
126 m_bTreeMode = GetOptionsMgr()->GetBool(OPT_TREE_MODE);
127 m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
128 m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
129 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
132 CDirView::~CDirView()
136 BEGIN_MESSAGE_MAP(CDirView, CListView)
138 //{{AFX_MSG_MAP(CDirView)
139 ON_WM_LBUTTONDBLCLK()
140 ON_COMMAND_RANGE(ID_L2R, ID_R2L, OnDirCopy)
141 ON_UPDATE_COMMAND_UI_RANGE(ID_L2R, ID_R2L, OnUpdateDirCopy)
142 ON_COMMAND(ID_DIR_COPY_LEFT_TO_RIGHT, (OnCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
143 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_RIGHT, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
144 ON_COMMAND(ID_DIR_COPY_LEFT_TO_MIDDLE, (OnCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>))
145 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>))
146 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_LEFT, (OnCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>))
147 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>))
148 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_MIDDLE, (OnCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>))
149 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>))
150 ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_LEFT, (OnCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>))
151 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>))
152 ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_RIGHT, (OnCtxtDirCopy<SIDE_MIDDLE, SIDE_RIGHT>))
153 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_RIGHT, (OnUpdateCtxtDirCopy<SIDE_MIDDLE, SIDE_RIGHT>))
154 ON_COMMAND(ID_DIR_DEL_LEFT, OnCtxtDirDel<SIDE_LEFT>)
155 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_LEFT, OnUpdateCtxtDirDel<SIDE_LEFT>)
156 ON_COMMAND(ID_DIR_DEL_RIGHT, OnCtxtDirDel<SIDE_RIGHT>)
157 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_MIDDLE, OnUpdateCtxtDirDel<SIDE_MIDDLE>)
158 ON_COMMAND(ID_DIR_DEL_MIDDLE, OnCtxtDirDel<SIDE_MIDDLE>)
159 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_RIGHT, OnUpdateCtxtDirDel<SIDE_RIGHT>)
160 ON_COMMAND(ID_DIR_DEL_BOTH, OnCtxtDirDelBoth)
161 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_BOTH, OnUpdateCtxtDirDelBoth)
162 ON_COMMAND(ID_DIR_DEL_ALL, OnCtxtDirDelBoth)
163 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_ALL, OnUpdateCtxtDirDelBoth)
164 ON_COMMAND(ID_DIR_OPEN_LEFT, OnCtxtDirOpen<SIDE_LEFT>)
165 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT, OnUpdateCtxtDirOpen<SIDE_LEFT>)
166 ON_COMMAND(ID_DIR_OPEN_LEFT_WITH, OnCtxtDirOpenWith<SIDE_LEFT>)
167 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITH, OnUpdateCtxtDirOpenWith<SIDE_LEFT>)
168 ON_COMMAND(ID_DIR_OPEN_LEFT_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_LEFT>)
169 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_LEFT>)
170 ON_COMMAND(ID_DIR_OPEN_MIDDLE, OnCtxtDirOpen<SIDE_MIDDLE>)
171 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE, OnUpdateCtxtDirOpen<SIDE_MIDDLE>)
172 ON_COMMAND(ID_DIR_OPEN_MIDDLE_WITH, OnCtxtDirOpenWith<SIDE_MIDDLE>)
173 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_WITH, OnUpdateCtxtDirOpenWith<SIDE_MIDDLE>)
174 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_MIDDLE>)
175 ON_COMMAND(ID_DIR_OPEN_MIDDLE_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_MIDDLE>)
176 ON_COMMAND(ID_DIR_OPEN_RIGHT, OnCtxtDirOpen<SIDE_RIGHT>)
177 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT, OnUpdateCtxtDirOpen<SIDE_RIGHT>)
178 ON_COMMAND(ID_DIR_OPEN_RIGHT_WITH, OnCtxtDirOpenWith<SIDE_RIGHT>)
179 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITH, OnUpdateCtxtDirOpenWith<SIDE_RIGHT>)
180 ON_COMMAND(ID_DIR_OPEN_RIGHT_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_RIGHT>)
181 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_RIGHT>)
182 ON_COMMAND(ID_POPUP_OPEN_WITH_UNPACKER, OnCtxtOpenWithUnpacker)
183 ON_UPDATE_COMMAND_UI(ID_POPUP_OPEN_WITH_UNPACKER, OnUpdateCtxtOpenWithUnpacker)
184 ON_COMMAND(ID_DIR_OPEN_LEFT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_LEFT>)
185 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_LEFT>)
186 ON_COMMAND(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_MIDDLE>)
187 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_MIDDLE>)
188 ON_COMMAND(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_RIGHT>)
189 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_RIGHT>)
190 ON_COMMAND(ID_DIR_COPY_LEFT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_LEFT>)
191 ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnCtxtDirCopyTo<SIDE_MIDDLE>)
192 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_RIGHT>)
193 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
194 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
195 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
199 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
200 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
201 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
202 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
203 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
204 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
205 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
206 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
207 ON_COMMAND(ID_CURDIFF, OnCurdiff)
208 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
209 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateSave)
210 ON_MESSAGE(MSG_UI_UPDATE, OnUpdateUIMessage)
211 ON_COMMAND(ID_REFRESH, OnRefresh)
212 ON_UPDATE_COMMAND_UI(ID_REFRESH, OnUpdateRefresh)
214 ON_UPDATE_COMMAND_UI(ID_STATUS_RIGHTDIR_RO, OnUpdateStatusRightRO)
215 ON_UPDATE_COMMAND_UI(ID_STATUS_MIDDLEDIR_RO, OnUpdateStatusMiddleRO)
216 ON_UPDATE_COMMAND_UI(ID_STATUS_LEFTDIR_RO, OnUpdateStatusLeftRO)
217 ON_COMMAND(ID_FILE_LEFT_READONLY, OnReadOnly<SIDE_LEFT>)
218 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateReadOnly<SIDE_LEFT>)
219 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnReadOnly<SIDE_MIDDLE>)
220 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateReadOnly<SIDE_MIDDLE>)
221 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnReadOnly<SIDE_RIGHT>)
222 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateReadOnly<SIDE_RIGHT>)
223 ON_COMMAND(ID_TOOLS_CUSTOMIZECOLUMNS, OnCustomizeColumns)
224 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
225 ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
226 ON_COMMAND(ID_DIR_ZIP_LEFT, OnCtxtDirZip<DirItemEnumerator::Left>)
227 ON_COMMAND(ID_DIR_ZIP_MIDDLE, OnCtxtDirZip<DirItemEnumerator::Middle>)
228 ON_COMMAND(ID_DIR_ZIP_RIGHT, OnCtxtDirZip<DirItemEnumerator::Right>)
229 ON_COMMAND(ID_DIR_ZIP_BOTH, OnCtxtDirZip<DirItemEnumerator::Original | DirItemEnumerator::Altered | DirItemEnumerator::BalanceFolders>)
230 ON_COMMAND(ID_DIR_ZIP_ALL, OnCtxtDirZip<DirItemEnumerator::Original | DirItemEnumerator::Altered | DirItemEnumerator::BalanceFolders>)
231 ON_COMMAND(ID_DIR_ZIP_BOTH_DIFFS_ONLY, OnCtxtDirZip<DirItemEnumerator::Original | DirItemEnumerator::Altered | DirItemEnumerator::BalanceFolders | DirItemEnumerator::DiffsOnly>)
232 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_LEFT, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
233 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_MIDDLE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
234 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_RIGHT, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
235 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_BOTH, OnUpdateCtxtDirCopyBothTo)
236 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_ALL, OnUpdateCtxtDirCopyBothTo)
237 ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_BOTH_DIFFS_ONLY, OnUpdateCtxtDirCopyBothDiffsOnlyTo)
238 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_LEFT, OnCtxtDirShellContextMenu<SIDE_LEFT>)
239 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, OnCtxtDirShellContextMenu<SIDE_MIDDLE>)
240 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_RIGHT, OnCtxtDirShellContextMenu<SIDE_RIGHT>)
241 ON_COMMAND(ID_EDIT_SELECT_ALL, OnSelectAll)
242 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateSelectAll)
243 ON_COMMAND_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnPluginPredifferMode)
244 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnUpdatePluginPredifferMode)
245 ON_COMMAND(ID_DIR_COPY_PATHNAMES_LEFT, OnCopyPathnames<SIDE_LEFT>)
246 ON_COMMAND(ID_DIR_COPY_PATHNAMES_MIDDLE, OnCopyPathnames<SIDE_MIDDLE>)
247 ON_COMMAND(ID_DIR_COPY_PATHNAMES_RIGHT, OnCopyPathnames<SIDE_RIGHT>)
248 ON_COMMAND(ID_DIR_COPY_PATHNAMES_BOTH, OnCopyBothPathnames)
249 ON_COMMAND(ID_DIR_COPY_PATHNAMES_ALL, OnCopyBothPathnames)
250 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_LEFT, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
251 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_MIDDLE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
252 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_RIGHT, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
253 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_BOTH, OnUpdateCtxtDirCopyBothTo)
254 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_ALL, OnUpdateCtxtDirCopyBothTo)
255 ON_COMMAND(ID_DIR_COPY_FILENAMES, OnCopyFilenames)
256 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_FILENAMES, OnUpdateCopyFilenames)
257 ON_COMMAND(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnCopyToClipboard<SIDE_LEFT>)
258 ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, OnCopyToClipboard<SIDE_MIDDLE>)
259 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnCopyToClipboard<SIDE_RIGHT>)
260 ON_COMMAND(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnCopyBothToClipboard)
261 ON_COMMAND(ID_DIR_COPY_ALL_TO_CLIPBOARD, OnCopyBothToClipboard)
262 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
263 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
264 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
265 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnUpdateCtxtDirCopyBothTo)
266 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_ALL_TO_CLIPBOARD, OnUpdateCtxtDirCopyBothTo)
267 ON_COMMAND(ID_DIR_ITEM_RENAME, OnItemRename)
268 ON_UPDATE_COMMAND_UI(ID_DIR_ITEM_RENAME, OnUpdateItemRename)
269 ON_COMMAND(ID_DIR_HIDE_FILENAMES, OnHideFilenames)
270 ON_COMMAND(ID_DIR_MOVE_LEFT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_LEFT>)
271 ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_LEFT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_LEFT>)
272 ON_COMMAND(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnCtxtDirMoveTo<SIDE_MIDDLE>)
273 ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_MIDDLE>)
274 ON_COMMAND(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_RIGHT>)
275 ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_RIGHT>)
276 ON_UPDATE_COMMAND_UI(ID_DIR_HIDE_FILENAMES, OnUpdateHideFilenames)
278 ON_COMMAND(ID_MERGE_DELETE, OnDelete)
279 ON_UPDATE_COMMAND_UI(ID_MERGE_DELETE, OnUpdateDelete)
280 ON_COMMAND(ID_RESCAN, OnMarkedRescan)
281 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
282 ON_COMMAND(ID_VIEW_SHOWHIDDENITEMS, OnViewShowHiddenItems)
283 ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWHIDDENITEMS, OnUpdateViewShowHiddenItems)
284 ON_COMMAND(ID_MERGE_COMPARE, OnMergeCompare)
285 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE, OnUpdateMergeCompare)
286 ON_COMMAND(ID_MERGE_COMPARE_LEFT1_LEFT2, OnMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
287 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_LEFT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
288 ON_COMMAND(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
289 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
290 ON_COMMAND(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
291 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
292 ON_COMMAND(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
293 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
294 ON_COMMAND(ID_MERGE_COMPARE_XML, OnMergeCompareXML)
295 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_XML, OnUpdateMergeCompare)
296 ON_COMMAND(ID_MERGE_COMPARE_HEX, OnMergeCompareHex)
297 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_HEX, OnUpdateMergeCompare)
298 ON_COMMAND(ID_VIEW_TREEMODE, OnViewTreeMode)
299 ON_UPDATE_COMMAND_UI(ID_VIEW_TREEMODE, OnUpdateViewTreeMode)
300 ON_COMMAND(ID_VIEW_EXPAND_ALLSUBDIRS, OnViewExpandAllSubdirs)
301 ON_UPDATE_COMMAND_UI(ID_VIEW_EXPAND_ALLSUBDIRS, OnUpdateViewExpandAllSubdirs)
302 ON_COMMAND(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnViewCollapseAllSubdirs)
303 ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnUpdateViewCollapseAllSubdirs)
304 ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
305 ON_COMMAND(ID_VIEW_DIR_STATISTICS, OnViewCompareStatistics)
306 ON_COMMAND(ID_OPTIONS_SHOWDIFFERENT, OnOptionsShowDifferent)
307 ON_COMMAND(ID_OPTIONS_SHOWIDENTICAL, OnOptionsShowIdentical)
308 ON_COMMAND(ID_OPTIONS_SHOWUNIQUELEFT, OnOptionsShowUniqueLeft)
309 ON_COMMAND(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnOptionsShowUniqueMiddle)
310 ON_COMMAND(ID_OPTIONS_SHOWUNIQUERIGHT, OnOptionsShowUniqueRight)
311 ON_COMMAND(ID_OPTIONS_SHOWBINARIES, OnOptionsShowBinaries)
312 ON_COMMAND(ID_OPTIONS_SHOWSKIPPED, OnOptionsShowSkipped)
313 ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnOptionsShowDifferentLeftOnly)
314 ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnOptionsShowDifferentMiddleOnly)
315 ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnOptionsShowDifferentRightOnly)
316 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENT, OnUpdateOptionsShowdifferent)
317 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWIDENTICAL, OnUpdateOptionsShowidentical)
318 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUELEFT, OnUpdateOptionsShowuniqueleft)
319 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnUpdateOptionsShowuniquemiddle)
320 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUERIGHT, OnUpdateOptionsShowuniqueright)
321 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWBINARIES, OnUpdateOptionsShowBinaries)
322 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWSKIPPED, OnUpdateOptionsShowSkipped)
323 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnUpdateOptionsShowDifferentLeftOnly)
324 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnUpdateOptionsShowDifferentMiddleOnly)
325 ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnUpdateOptionsShowDifferentRightOnly)
326 ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
327 ON_UPDATE_COMMAND_UI(ID_FILE_ENCODING, OnUpdateFileEncoding)
328 ON_COMMAND(ID_HELP, OnHelp)
329 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
330 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
331 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
332 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
333 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
335 ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
336 ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged)
337 ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
338 ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)
339 ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
340 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
341 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
342 ON_BN_CLICKED(IDC_COMPARISON_STOP, OnBnClickedComparisonStop)
343 ON_BN_CLICKED(IDC_COMPARISON_PAUSE, OnBnClickedComparisonPause)
344 ON_BN_CLICKED(IDC_COMPARISON_CONTINUE, OnBnClickedComparisonContinue)
347 /////////////////////////////////////////////////////////////////////////////
348 // CDirView diagnostics
352 CDirDoc* CDirView::GetDocument() // non-debug version is inline
354 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirDoc)));
355 return (CDirDoc*)m_pDocument;
359 /////////////////////////////////////////////////////////////////////////////
360 // CDirView message handlers
362 void CDirView::OnInitialUpdate()
364 const int iconCX = []() {
365 const int cx = GetSystemMetrics(SM_CXSMICON);
374 const int iconCY = iconCX;
375 CListView::OnInitialUpdate();
376 m_pList = &GetListCtrl();
377 m_pIList.reset(new IListCtrlImpl(m_pList->m_hWnd));
378 GetDocument()->SetDirView(this);
379 m_pColItems.reset(new DirViewColItems(GetDocument()->m_nDirs));
382 m_pList->SendMessage(CCM_SETUNICODEFORMAT, TRUE, 0);
384 m_pList->SendMessage(CCM_SETUNICODEFORMAT, FALSE, 0);
387 // Load user-selected font
388 if (GetOptionsMgr()->GetBool(OPT_FONT_DIRCMP + OPT_FONT_USECUSTOM))
390 m_font.CreateFontIndirect(&GetMainFrame()->m_lfDir);
391 CWnd::SetFont(&m_font, TRUE);
394 // Replace standard header with sort header
395 if (HWND hWnd = ListView_GetHeader(m_pList->m_hWnd))
396 m_ctlSortHeader.SubclassWindow(hWnd);
398 // Load the icons used for the list view (to reflect diff status)
399 // NOTE: these must be in the exactly the same order than in enum
400 // definition in begin of this file!
401 VERIFY(m_imageList.Create(iconCX, iconCY, ILC_COLOR32 | ILC_MASK, 15, 1));
403 IDI_LFILE, IDI_MFILE, IDI_RFILE,
404 IDI_MRFILE, IDI_LRFILE, IDI_LMFILE,
405 IDI_NOTEQUALFILE, IDI_EQUALFILE,
406 IDI_EQUALBINARY, IDI_BINARYDIFF,
407 IDI_LFOLDER, IDI_MFOLDER, IDI_RFOLDER,
408 IDI_MRFOLDER, IDI_LRFOLDER, IDI_LMFOLDER,
409 IDI_FILESKIP, IDI_FOLDERSKIP,
410 IDI_NOTEQUALFOLDER, IDI_EQUALFOLDER, IDI_FOLDER,
412 IDI_FOLDERUP, IDI_FOLDERUP_DISABLE,
414 IDI_NOTEQUALTEXTFILE, IDI_EQUALTEXTFILE
416 for (auto id : icon_ids)
417 VERIFY(-1 != m_imageList.Add((HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(id), IMAGE_ICON, iconCX, iconCY, 0)));
418 m_pList->SetImageList(&m_imageList, LVSIL_SMALL);
420 // Load the icons used for the list view (expanded/collapsed state icons)
421 VERIFY(m_imageState.Create(iconCX, iconCY, ILC_COLOR32 | ILC_MASK, 15, 1));
422 for (auto id : { IDI_TREE_STATE_COLLAPSED, IDI_TREE_STATE_EXPANDED })
423 VERIFY(-1 != m_imageState.Add((HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(id), IMAGE_ICON, iconCX, iconCY, 0)));
425 // Restore column orders as they had them last time they ran
426 m_pColItems->LoadColumnOrders(
427 (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnOrders")));
429 // Display column headers (in appropriate order)
432 // Show selection across entire row.u
433 // Also allow user to rearrange columns via drag&drop of headers.
434 // Also enable infotips.
435 DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP;
436 m_pList->SetExtendedStyle(exstyle);
440 * @brief Called before compare is started.
441 * CDirDoc calls this function before new compare is started, so this
442 * is good place to setup GUI for compare.
443 * @param [in] pCompareStats Pointer to class having current compare stats.
445 void CDirView::StartCompare(CompareStats *pCompareStats)
447 if (m_pCmpProgressBar == NULL)
448 m_pCmpProgressBar.reset(new DirCompProgressBar());
450 if (!::IsWindow(m_pCmpProgressBar->GetSafeHwnd()))
451 m_pCmpProgressBar->Create(GetParentFrame());
453 m_pCmpProgressBar->SetCompareStat(pCompareStats);
454 m_pCmpProgressBar->StartUpdating();
456 GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), TRUE, FALSE);
458 m_compareStart = clock();
462 * @brief Called when folder compare row is double-clicked with mouse.
463 * Selected item is opened to folder or file compare.
465 void CDirView::OnLButtonDblClk(UINT nFlags, CPoint point)
469 m_pList->SubItemHitTest(&lvhti);
470 if (lvhti.iItem >= 0)
472 const DIFFITEM& di = GetDiffItem(lvhti.iItem);
473 if (m_bTreeMode && GetDiffContext().m_bRecursive && di.diffcode.isDirectory())
475 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
476 CollapseSubdir(lvhti.iItem);
478 ExpandSubdir(lvhti.iItem);
482 CWaitCursor waitstatus;
486 CListView::OnLButtonDblClk(nFlags, point);
490 * @brief Load or reload the columns (headers) of the list view
492 void CDirView::ReloadColumns()
494 LoadColumnHeaderItems();
497 m_pColItems->LoadColumnWidths(
498 (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnWidths")),
499 std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), DefColumnWidth);
504 * @brief Redisplay items in subfolder
505 * @param [in] diffpos First item position in subfolder.
506 * @param [in] level Indent level
507 * @param [in,out] index Index of the item to be inserted.
508 * @param [in,out] alldiffs Number of different items
510 void CDirView::RedisplayChildren(uintptr_t diffpos, int level, UINT &index, int &alldiffs)
512 CDirDoc *pDoc = GetDocument();
513 const CDiffContext &ctxt = GetDiffContext();
516 uintptr_t curdiffpos = diffpos;
517 const DIFFITEM &di = ctxt.GetNextSiblingDiffPosition(diffpos);
519 if (di.diffcode.isResultDiff() || (!di.diffcode.existAll() && !di.diffcode.isResultFiltered()))
522 bool bShowable = IsShowable(ctxt, di, m_dirfilter);
527 AddNewItem(index, curdiffpos, I_IMAGECALLBACK, level);
529 if (di.HasChildren())
531 m_pList->SetItemState(index - 1, INDEXTOSTATEIMAGEMASK((di.customFlags1 & ViewCustomFlags::EXPANDED) ? 2 : 1), LVIS_STATEIMAGEMASK);
532 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
533 RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
538 if (!ctxt.m_bRecursive || !di.diffcode.isDirectory() || !di.diffcode.existAll())
540 AddNewItem(index, curdiffpos, I_IMAGECALLBACK, 0);
543 if (di.HasChildren())
545 RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
553 * @brief Redisplay folder compare view.
554 * This function clears folder compare view and then adds
555 * items from current compare to it.
557 void CDirView::Redisplay()
559 const CDirDoc *pDoc = GetDocument();
560 const CDiffContext &ctxt = GetDiffContext();
561 PathContext pathsParent;
562 CImageList emptyImageList;
565 // Disable redrawing while adding new items
568 DeleteAllDisplayItems();
570 m_pList->SetImageList((m_bTreeMode && ctxt.m_bRecursive) ? &m_imageState : &emptyImageList, LVSIL_STATE);
572 // If non-recursive compare, add special item(s)
573 if (!ctxt.m_bRecursive ||
574 CheckAllowUpwardDirectory(ctxt, pDoc->m_pTempPathContext, pathsParent) == AllowUpwardDirectory::ParentIsTempPath)
576 cnt += AddSpecialItems();
580 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
581 RedisplayChildren(diffpos, 0, cnt, alldiffs);
582 if (pDoc->m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPLETED)
583 GetParentFrame()->SetLastCompareResult(alldiffs);
584 SortColumnsAppropriately();
589 * @brief User right-clicked somewhere in this view
591 void CDirView::OnContextMenu(CWnd*, CPoint point)
593 if (GetListCtrl().GetItemCount() == 0)
595 // Make sure window is active
596 GetParentFrame()->ActivateFrame();
599 if (point.x == -1 && point.y == -1)
601 //keystroke invocation
604 ClientToScreen(rect);
606 point = rect.TopLeft();
611 // Check if user right-clicked on header
612 // convert screen coordinates to client coordinates of listview
613 CPoint insidePt = point;
614 GetListCtrl().ScreenToClient(&insidePt);
615 // TODO: correct for hscroll ?
616 // Ask header control if click was on one of its header items
617 HDHITTESTINFO hhti = { 0 };
619 int col = static_cast<int>(GetListCtrl().GetHeaderCtrl()->SendMessage(HDM_HITTEST, 0, (LPARAM) & hhti));
622 // Presumably hhti.flags & HHT_ONHEADER is true
623 HeaderContextMenu(point, m_pColItems->ColPhysToLog(col));
626 // bail out if point is not in any row
627 LVHITTESTINFO lhti = { 0 };
629 ScreenToClient(&insidePt);
631 i = GetListCtrl().HitTest(insidePt);
632 TRACE(_T("i=%d\n"), i);
637 ListContextMenu(point, i);
641 * @brief Format context menu string and disable item if it cannot be applied.
643 static void NTAPI FormatContextMenu(BCMenu *pPopup, UINT uIDItem, int n1, int n2 = 0, int n3 = 0)
646 pPopup->GetMenuText(uIDItem, s1, MF_BYCOMMAND);
647 s2.FormatMessage(s1, NumToStr(n1).c_str(), NumToStr(n2).c_str(), NumToStr(n3).c_str());
648 pPopup->SetMenuText(uIDItem, s2, MF_BYCOMMAND);
651 pPopup->EnableMenuItem(uIDItem, MF_GRAYED);
656 * @brief Toggle context menu item
658 static void NTAPI CheckContextMenu(BCMenu *pPopup, UINT uIDItem, BOOL bCheck)
661 pPopup->CheckMenuItem(uIDItem, MF_CHECKED);
663 pPopup->CheckMenuItem(uIDItem, MF_UNCHECKED);
667 * @brief User right-clicked in listview rows
669 void CDirView::ListContextMenu(CPoint point, int /*i*/)
671 CDirDoc *pDoc = GetDocument();
673 VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
674 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
675 theApp.TranslateMenu(menu.m_hMenu);
677 // 1st submenu of IDR_POPUP_DIRVIEW is for item popup
678 BCMenu *pPopup = static_cast<BCMenu*>(menu.GetSubMenu(0));
679 ASSERT(pPopup != NULL);
681 if (pDoc->m_nDirs < 3)
683 pPopup->RemoveMenu(ID_DIR_COPY_LEFT_TO_MIDDLE, MF_BYCOMMAND);
684 pPopup->RemoveMenu(ID_DIR_COPY_MIDDLE_TO_LEFT, MF_BYCOMMAND);
685 pPopup->RemoveMenu(ID_DIR_COPY_MIDDLE_TO_RIGHT, MF_BYCOMMAND);
686 pPopup->RemoveMenu(ID_DIR_COPY_MIDDLE_TO_BROWSE, MF_BYCOMMAND);
687 pPopup->RemoveMenu(ID_DIR_COPY_RIGHT_TO_MIDDLE, MF_BYCOMMAND);
688 pPopup->RemoveMenu(ID_DIR_MOVE_MIDDLE_TO_BROWSE, MF_BYCOMMAND);
689 pPopup->RemoveMenu(ID_DIR_DEL_MIDDLE, MF_BYCOMMAND);
690 pPopup->RemoveMenu(ID_DIR_DEL_ALL, MF_BYCOMMAND);
691 pPopup->RemoveMenu(ID_DIR_OPEN_MIDDLE, MF_BYCOMMAND);
693 for (int i = 0; i < pPopup->GetMenuItemCount(); ++i)
695 if (pPopup->GetMenuItemID(i) == ID_DIR_HIDE_FILENAMES)
696 pPopup->RemoveMenu(i + 3, MF_BYPOSITION);
699 pPopup->RemoveMenu(ID_DIR_OPEN_MIDDLE_WITHEDITOR, MF_BYCOMMAND);
700 pPopup->RemoveMenu(ID_DIR_OPEN_MIDDLE_WITH, MF_BYCOMMAND);
701 pPopup->RemoveMenu(ID_DIR_COPY_PATHNAMES_MIDDLE, MF_BYCOMMAND);
702 pPopup->RemoveMenu(ID_DIR_COPY_PATHNAMES_ALL, MF_BYCOMMAND);
703 pPopup->RemoveMenu(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, MF_BYCOMMAND);
704 pPopup->RemoveMenu(ID_DIR_COPY_ALL_TO_CLIPBOARD, MF_BYCOMMAND);
705 pPopup->RemoveMenu(ID_DIR_ZIP_MIDDLE, MF_BYCOMMAND);
706 pPopup->RemoveMenu(ID_DIR_ZIP_ALL, MF_BYCOMMAND);
707 pPopup->RemoveMenu(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, MF_BYCOMMAND);
712 pPopup->RemoveMenu(ID_DIR_COPY_PATHNAMES_BOTH, MF_BYCOMMAND);
713 pPopup->RemoveMenu(ID_DIR_COPY_BOTH_TO_CLIPBOARD, MF_BYCOMMAND);
714 pPopup->RemoveMenu(ID_DIR_ZIP_BOTH, MF_BYCOMMAND);
715 pPopup->RemoveMenu(ID_DIR_DEL_BOTH, MF_BYCOMMAND);
718 CMenu menuPluginsHolder;
719 menuPluginsHolder.LoadMenu(IDR_POPUP_PLUGINS_SETTINGS);
720 theApp.TranslateMenu(menuPluginsHolder.m_hMenu);
721 String s = _("Plugin Settings");
722 pPopup->AppendMenu(MF_SEPARATOR);
723 pPopup->AppendMenu(MF_POPUP, static_cast<int>(reinterpret_cast<uintptr_t>(menuPluginsHolder.m_hMenu)), s.c_str());
725 CFrameWnd *pFrame = GetTopLevelFrame();
726 ASSERT(pFrame != NULL);
727 pFrame->m_bAutoMenuEnable = FALSE;
728 // invoke context menu
729 // this will invoke all the OnUpdate methods to enable/disable the individual items
730 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
733 pFrame->m_bAutoMenuEnable = TRUE;
737 * @brief User right-clicked on specified logical column
739 void CDirView::HeaderContextMenu(CPoint point, int /*i*/)
742 VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
743 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
744 theApp.TranslateMenu(menu.m_hMenu);
745 // 2nd submenu of IDR_POPUP_DIRVIEW is for header popup
746 BCMenu* pPopup = static_cast<BCMenu *>(menu.GetSubMenu(1));
747 ASSERT(pPopup != NULL);
749 // invoke context menu
750 // this will invoke all the OnUpdate methods to enable/disable the individual items
751 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
756 * @brief Gets Explorer's context menu for a group of selected files.
758 * @param [in] Side whether to get context menu for the files from the left or
760 * @retval true menu successfully retrieved.
761 * @retval falsea an error occurred while retrieving the menu.
763 bool CDirView::ListShellContextMenu(SIDE_TYPE stype)
765 CShellContextMenu* shellContextMenu;
768 shellContextMenu = m_pShellContextMenuMiddle.get(); break;
770 shellContextMenu = m_pShellContextMenuRight.get(); break;
772 shellContextMenu = m_pShellContextMenuLeft.get(); break;
774 shellContextMenu->Initialize();
775 ApplyFolderNameAndFileName(SelBegin(), SelEnd(), stype, GetDiffContext(),
776 [&](const String& path, const String& filename) { shellContextMenu->AddItem(path, filename); });
777 return shellContextMenu->RequeryShellContextMenu();
781 * @brief User chose (main menu) Copy from right to left
783 void CDirView::OnDirCopy(UINT id)
785 bool to_right = (id == ID_L2R) ? true : false;
786 if (GetDocument()->m_nDirs < 3)
789 DoDirAction(&DirActions::Copy<SIDE_LEFT, SIDE_RIGHT>, _("Copying files..."));
791 DoDirAction(&DirActions::Copy<SIDE_RIGHT, SIDE_LEFT>, _("Copying files..."));
797 switch (m_nActivePane)
800 DoDirAction(&DirActions::Copy<SIDE_LEFT, SIDE_MIDDLE>, _("Copying files..."));
804 DoDirAction(&DirActions::Copy<SIDE_MIDDLE, SIDE_RIGHT>, _("Copying files..."));
810 switch (m_nActivePane)
814 DoDirAction(&DirActions::Copy<SIDE_MIDDLE, SIDE_LEFT>, _("Copying files..."));
817 DoDirAction(&DirActions::Copy<SIDE_RIGHT, SIDE_MIDDLE>, _("Copying files..."));
824 /// User chose (context men) Copy from right to left
825 template<SIDE_TYPE srctype, SIDE_TYPE dsttype>
826 void CDirView::OnCtxtDirCopy()
828 DoDirAction(&DirActions::Copy<srctype, dsttype>, _("Copying files..."));
831 /// User chose (context menu) Copy left to...
832 template<SIDE_TYPE stype>
833 void CDirView::OnCtxtDirCopyTo()
835 DoDirActionTo(stype, &DirActions::CopyTo<stype>, _("Copying files..."));
838 /// Update context menu Copy Right to Left item
839 template<SIDE_TYPE srctype, SIDE_TYPE dsttype>
840 void CDirView::OnUpdateCtxtDirCopy(CCmdUI* pCmdUI)
842 DoUpdateDirCopy<srctype, dsttype>(pCmdUI, eContext);
845 /// Update main menu Copy Right to Left item
846 void CDirView::OnUpdateDirCopy(CCmdUI* pCmdUI)
848 bool to_right = pCmdUI->m_nID == ID_L2R ? true : false;
849 if (GetDocument()->m_nDirs < 3)
852 DoUpdateDirCopy<SIDE_LEFT, SIDE_RIGHT>(pCmdUI, eContext);
854 DoUpdateDirCopy<SIDE_RIGHT, SIDE_LEFT>(pCmdUI, eContext);
860 switch (m_nActivePane)
863 DoUpdateDirCopy<SIDE_LEFT, SIDE_MIDDLE>(pCmdUI, eContext);
867 DoUpdateDirCopy<SIDE_MIDDLE, SIDE_RIGHT>(pCmdUI, eContext);
873 switch (m_nActivePane)
877 DoUpdateDirCopy<SIDE_MIDDLE, SIDE_LEFT>(pCmdUI, eContext);
880 DoUpdateDirCopy<SIDE_RIGHT, SIDE_MIDDLE>(pCmdUI, eContext);
887 void CDirView::DoDirAction(DirActions::method_type func, const String& status_message)
889 CWaitCursor waitstatus;
892 // First we build a list of desired actions
893 FileActionScript actionScript;
894 DirItemWithIndexIterator begin(m_pIList.get(), -1, true);
895 DirItemWithIndexIterator end;
896 FileActionScript *rsltScript;
897 rsltScript = std::accumulate(begin, end, &actionScript, MakeDirActions(func));
898 ASSERT(rsltScript == &actionScript);
899 // Now we prompt, and execute actions
900 ConfirmAndPerformActions(actionScript);
901 } catch (ContentsChangedException& e) {
902 AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
906 void CDirView::DoDirActionTo(SIDE_TYPE stype, DirActions::method_type func, const String& status_message)
909 String startPath(m_lastCopyFolder);
910 String selectfolder_title;
912 if (stype == SIDE_LEFT)
913 selectfolder_title = _("Left side - select destination folder:");
914 else if (stype == SIDE_MIDDLE)
915 selectfolder_title = _("Middle side - select destination folder:");
916 else if (stype == SIDE_RIGHT)
917 selectfolder_title = _("Right side - select destination folder:");
919 if (!SelectFolder(destPath, startPath.c_str(), selectfolder_title))
922 m_lastCopyFolder = destPath;
923 CWaitCursor waitstatus;
926 // First we build a list of desired actions
927 FileActionScript actionScript;
928 actionScript.m_destBase = destPath;
929 DirItemWithIndexIterator begin(m_pIList.get(), -1, true);
930 DirItemWithIndexIterator end;
931 FileActionScript *rsltScript;
932 rsltScript = std::accumulate(begin, end, &actionScript, MakeDirActions(func));
933 ASSERT(rsltScript == &actionScript);
934 // Now we prompt, and execute actions
935 ConfirmAndPerformActions(actionScript);
936 } catch (ContentsChangedException& e) {
937 AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
941 // Confirm with user, then perform the action list
942 void CDirView::ConfirmAndPerformActions(FileActionScript & actionList)
944 if (actionList.GetActionItemCount() == 0) // Not sure it is possible to get right-click menu without
945 return; // any selected items, but may as well be safe
947 ASSERT(actionList.GetActionItemCount()>0); // Or else the update handler got it wrong
949 // Set parent window so modality is correct and correct window gets focus
951 actionList.SetParentWindow(this->GetSafeHwnd());
954 ConfirmActionList(GetDiffContext(), actionList);
955 } catch (ConfirmationNeededException& e) {
956 ConfirmFolderCopyDlg dlg;
957 dlg.m_caption = e.m_caption;
958 dlg.m_question = e.m_question;
959 dlg.m_fromText = e.m_fromText;
960 dlg.m_toText = e.m_toText;
961 dlg.m_fromPath = e.m_fromPath;
962 dlg.m_toPath = e.m_toPath;
963 INT_PTR ans = dlg.DoModal();
964 if (ans != IDOK && ans != IDYES)
967 PerformActionList(actionList);
971 * @brief Perform an array of actions
972 * @note There can be only COPY or DELETE actions, not both!
973 * @sa SourceControl::SaveToVersionControl()
974 * @sa SourceControl::SyncFilesToVCS()
976 void CDirView::PerformActionList(FileActionScript & actionScript)
978 // Reset suppressing VSS dialog for multiple files.
979 // Set in SourceControl::SaveToVersionControl().
980 theApp.m_pSourceControl->m_CheckOutMulti = false;
981 theApp.m_pSourceControl->m_bVssSuppressPathCheck = false;
983 // Check option and enable putting deleted items to Recycle Bin
984 if (GetOptionsMgr()->GetBool(OPT_USE_RECYCLE_BIN))
985 actionScript.UseRecycleBin(true);
987 actionScript.UseRecycleBin(false);
989 actionScript.SetParentWindow(GetMainFrame()->GetSafeHwnd());
991 theApp.AddOperation();
992 if (actionScript.Run())
993 UpdateAfterFileScript(actionScript);
994 theApp.RemoveOperation();
998 * @brief Update results after running FileActionScript.
999 * This functions is called after script is finished to update
1000 * results (including UI).
1001 * @param [in] actionlist Script that was run.
1003 void CDirView::UpdateAfterFileScript(FileActionScript & actionList)
1005 bool bItemsRemoved = false;
1006 int curSel = GetFirstSelectedInd();
1007 CDiffContext& ctxt = GetDiffContext();
1008 while (actionList.GetActionItemCount()>0)
1010 // Start handling from tail of list, so removing items
1011 // doesn't invalidate our item indexes.
1012 FileActionItem act = actionList.RemoveTailActionItem();
1014 // Synchronized items may need VCS operations
1015 if (act.UIResult == FileActionItem::UI_SYNC)
1017 if (theApp.m_pSourceControl->m_bCheckinVCS)
1018 theApp.m_pSourceControl->CheckinToClearCase(act.dest);
1021 // Update doc (difflist)
1022 UPDATEITEM_TYPE updatetype = UpdateDiffAfterOperation(act, ctxt, GetDiffItem(act.context));
1023 if (updatetype == UPDATEITEM_REMOVE)
1025 DeleteItem(act.context);
1026 bItemsRemoved = true;
1028 else if (updatetype == UPDATEITEM_UPDATE)
1029 UpdateDiffItemStatus(act.context);
1032 // Make sure selection is at sensible place if all selected items
1034 if (bItemsRemoved == true)
1036 UINT selected = GetSelectedCount();
1041 MoveFocus(0, curSel - 1, selected);
1046 Counts CDirView::Count(DirActions::method_type2 func) const
1048 return ::Count(SelBegin(), SelEnd(), MakeDirActions(func));
1051 /// Should Copy to Left be enabled or disabled ? (both main menu & context menu use this)
1052 template<SIDE_TYPE srctype, SIDE_TYPE dsttype>
1053 void CDirView::DoUpdateDirCopy(CCmdUI* pCmdUI, eMenuType menuType)
1055 Counts counts = Count(&DirActions::IsItemCopyableOnTo<srctype, dsttype>);
1056 pCmdUI->Enable(counts.count > 0);
1057 if (menuType == eContext)
1058 pCmdUI->SetText(FormatMenuItemString(srctype, dsttype, counts.count, counts.total).c_str());
1062 * @brief Update any resources necessary after a GUI language change
1064 void CDirView::UpdateResources()
1066 UpdateColumnNames();
1067 GetParentFrame()->UpdateResources();
1071 * @brief User just clicked a column, so perform sort
1073 void CDirView::OnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
1075 // set sort parameters and handle ascending/descending
1076 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*) pNMHDR;
1077 int oldSortColumn = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
1078 int sortcol = m_pColItems->ColPhysToLog(pNMListView->iSubItem);
1079 if (sortcol == oldSortColumn)
1082 bool bSortAscending = GetOptionsMgr()->GetBool(OPT_DIRVIEW_SORT_ASCENDING);
1083 GetOptionsMgr()->SaveOption(OPT_DIRVIEW_SORT_ASCENDING, !bSortAscending);
1087 GetOptionsMgr()->SaveOption((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3, sortcol);
1088 // most columns start off ascending, but not dates
1089 bool bSortAscending = m_pColItems->IsDefaultSortAscending(sortcol);
1090 GetOptionsMgr()->SaveOption(OPT_DIRVIEW_SORT_ASCENDING, bSortAscending);
1093 SortColumnsAppropriately();
1097 void CDirView::SortColumnsAppropriately()
1099 int sortCol = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
1100 if (sortCol == -1 || sortCol >= m_pColItems->GetColCount())
1103 bool bSortAscending = GetOptionsMgr()->GetBool(OPT_DIRVIEW_SORT_ASCENDING);
1104 m_ctlSortHeader.SetSortImage(m_pColItems->ColLogToPhys(sortCol), bSortAscending);
1105 //sort using static CompareFunc comparison function
1106 CompareState cs(&GetDiffContext(), m_pColItems.get(), sortCol, bSortAscending, m_bTreeMode);
1107 GetListCtrl().SortItems(cs.CompareFunc, reinterpret_cast<DWORD_PTR>(&cs));
1109 m_bNeedSearchLastDiffItem = true;
1110 m_bNeedSearchFirstDiffItem = true;
1113 /// Do any last minute work as view closes
1114 void CDirView::OnDestroy()
1116 DeleteAllDisplayItems();
1118 String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
1119 theApp.WriteProfileString(secname.c_str(), _T("ColumnOrders"), m_pColItems->SaveColumnOrders().c_str());
1120 theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
1121 m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)).c_str());
1123 CListView::OnDestroy();
1125 GetMainFrame()->ClearStatusbarItemCount();
1129 * @brief Open selected item when user presses ENTER key.
1131 void CDirView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
1133 if (nChar == VK_RETURN)
1135 int sel = GetFocusedItem();
1138 const DIFFITEM& di = GetDiffItem(sel);
1139 if (m_bTreeMode && GetDiffContext().m_bRecursive && di.diffcode.isDirectory())
1141 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
1142 CollapseSubdir(sel);
1148 CWaitCursor waitstatus;
1153 CListView::OnChar(nChar, nRepCnt, nFlags);
1157 * @brief Expand/collapse subfolder when "+/-" icon is clicked.
1159 void CDirView::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
1161 LPNMITEMACTIVATE pNM = (LPNMITEMACTIVATE)pNMHDR;
1162 LVHITTESTINFO lvhti;
1163 lvhti.pt = pNM->ptAction;
1164 m_pList->SubItemHitTest(&lvhti);
1165 if (lvhti.flags == LVHT_ONITEMSTATEICON)
1167 const DIFFITEM &di = GetDiffItem(pNM->iItem);
1168 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
1169 CollapseSubdir(pNM->iItem);
1171 ExpandSubdir(pNM->iItem);
1178 * @brief Collapse subfolder
1179 * @param [in] sel Folder item index in listview.
1181 void CDirView::CollapseSubdir(int sel)
1183 DIFFITEM& dip = this->GetDiffItem(sel);
1184 if (!m_bTreeMode || !(dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren())
1187 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
1189 dip.customFlags1 &= ~ViewCustomFlags::EXPANDED;
1190 m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
1192 int count = m_pList->GetItemCount();
1193 for (int i = sel + 1; i < count; i++)
1195 const DIFFITEM& di = GetDiffItem(i);
1196 if (!di.IsAncestor(&dip))
1198 m_pList->DeleteItem(i--);
1202 m_pList->SetRedraw(TRUE); // Turn updating back on
1206 * @brief Expand subfolder
1207 * @param [in] sel Folder item index in listview.
1209 void CDirView::ExpandSubdir(int sel, bool bRecursive)
1211 DIFFITEM& dip = GetDiffItem(sel);
1212 if (!m_bTreeMode || (dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren())
1215 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
1216 m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
1218 CDiffContext &ctxt = GetDiffContext();
1219 dip.customFlags1 |= ViewCustomFlags::EXPANDED;
1221 ExpandSubdirs(ctxt, dip);
1223 uintptr_t diffpos = ctxt.GetFirstChildDiffPosition(GetItemKey(sel));
1224 UINT indext = sel + 1;
1226 RedisplayChildren(diffpos, dip.GetDepth() + 1, indext, alldiffs);
1228 SortColumnsAppropriately();
1230 m_pList->SetRedraw(TRUE); // Turn updating back on
1234 * @brief Open parent folder if possible.
1236 void CDirView::OpenParentDirectory()
1238 CDirDoc *pDoc = GetDocument();
1239 PathContext pathsParent;
1240 switch (CheckAllowUpwardDirectory(GetDiffContext(), pDoc->m_pTempPathContext, pathsParent))
1242 case AllowUpwardDirectory::ParentIsTempPath:
1243 pDoc->m_pTempPathContext = pDoc->m_pTempPathContext->DeleteHead();
1244 // fall through (no break!)
1245 case AllowUpwardDirectory::ParentIsRegularPath:
1248 for (int nIndex = 0; nIndex < pathsParent.GetSize(); ++nIndex)
1249 dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nIndex) ? FFILEOPEN_READONLY : 0);
1250 GetMainFrame()->DoFileOpen(&pathsParent, dwFlags, NULL, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
1252 // fall through (no break!)
1253 case AllowUpwardDirectory::No:
1256 LangMessageBox(IDS_INVALID_DIRECTORY, MB_ICONSTOP);
1262 * @brief Get one or two selected items
1264 * Returns false if 0 or more than 3 items selecte
1266 bool CDirView::GetSelectedItems(int * sel1, int * sel2, int * sel3)
1270 *sel1 = m_pList->GetNextItem(-1, LVNI_SELECTED);
1273 *sel2 = m_pList->GetNextItem(*sel1, LVNI_SELECTED);
1276 *sel3 = m_pList->GetNextItem(*sel2, LVNI_SELECTED);
1279 int extra = m_pList->GetNextItem(*sel3, LVNI_SELECTED);
1280 return (extra == -1);
1284 * @brief Open special items (parent folders etc).
1285 * @param [in] pos1 First item position.
1286 * @param [in] pos2 Second item position.
1288 void CDirView::OpenSpecialItems(uintptr_t pos1, uintptr_t pos2, uintptr_t pos3)
1292 // Browse to parent folder(s) selected
1293 // SPECIAL_ITEM_POS is position for
1294 // special items, but there is currenly
1295 // only one (parent folder)
1296 OpenParentDirectory();
1300 // Parent directory & something else selected
1306 * @brief Creates a pairing folder for unique folder item.
1307 * This function creates a pairing folder for unique folder item in
1308 * folder compare. This way user can browse into unique folder's
1309 * contents and don't necessarily need to copy whole folder structure.
1310 * @return true if user agreed and folder was created.
1312 static bool CreateFoldersPair(const PathContext& paths)
1314 bool created = false;
1315 for (const auto& path : paths)
1317 if (!paths::DoesPathExist(path))
1320 strutils::format_string1(
1321 _("The folder exists only in other side and cannot be opened.\n\nDo you want to create a matching folder:\n%1\nto the other side and open these folders?"),
1323 int res = AfxMessageBox(message.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN);
1325 created = paths::CreateIfNeeded(path);
1332 * @brief Open selected files or directories.
1334 * Opens selected files to file compare. If comparing
1335 * directories non-recursively, then subfolders and parent
1336 * folder are opened too.
1338 * This handles the case that one item is selected
1339 * and the case that two items are selected (one on each side)
1341 void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMAL*/, PackingInfo * infoUnpacker /*= NULL*/)
1343 Merge7zFormatMergePluginScope scope(infoUnpacker);
1344 CDirDoc * pDoc = GetDocument();
1345 const CDiffContext& ctxt = GetDiffContext();
1347 // First, figure out what was selected (store into pos1 & pos2)
1348 uintptr_t pos1 = NULL, pos2 = NULL, pos3 = NULL;
1349 int sel1 = -1, sel2 = -1, sel3 = -1;
1350 if (!GetSelectedItems(&sel1, &sel2, &sel3))
1352 // Must have 1 or 2 or 3 items selected
1357 pos1 = GetItemKey(sel1);
1361 pos2 = GetItemKey(sel2);
1364 pos3 = GetItemKey(sel3);
1367 // Now handle the various cases of what was selected
1369 if (pos1 == SPECIAL_ITEM_POS)
1371 OpenSpecialItems(pos1, pos2, pos3);
1375 // Common variables which both code paths below are responsible for setting
1377 const DIFFITEM *pdi[3] = {0}; // left & right items (di1==di2 if single selection)
1378 bool isdir = false; // set if we're comparing directories
1383 success = GetOpenTwoItems(ctxt, selectionType, pos1, pos2, pdi,
1384 paths, sel1, sel2, isdir, nPane, errmsg);
1385 else if (pos2 && pos3)
1386 success = GetOpenThreeItems(ctxt, pos1, pos2, pos3, pdi,
1387 paths, sel1, sel2, sel3, isdir, nPane, errmsg);
1390 // Only one item selected, so perform diff on its sides
1391 success = GetOpenOneItem(ctxt, pos1, pdi,
1392 paths, sel1, isdir, nPane, errmsg);
1394 CreateFoldersPair(paths);
1398 if (!errmsg.empty())
1399 AfxMessageBox(errmsg.c_str(), MB_ICONSTOP);
1403 // Now pathLeft, pathRight, di1, di2, and isdir are all set
1404 // We have two items to compare, no matter whether same or different underlying DirView item
1407 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
1408 dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nPane[nIndex]) ? FFILEOPEN_READONLY : 0);
1412 // Don't add folders to MRU
1413 GetMainFrame()->DoFileOpen(&paths, dwFlags, NULL, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
1415 else if (HasZipSupport() && std::count_if(paths.begin(), paths.end(), ArchiveGuessFormat) == paths.GetSize())
1417 // Open archives, not adding paths to MRU
1418 GetMainFrame()->DoFileOpen(&paths, dwFlags, NULL, _T(""), GetDiffContext().m_bRecursive, NULL, _T(""), infoUnpacker);
1422 // Regular file case
1424 // Binary attributes are set after files are unpacked
1425 // so after plugins such as the MS-Office plugins have had a chance to make them textual
1426 // We haven't done unpacking yet in this diff, but if a binary flag is already set,
1427 // then it was set in a previous diff after unpacking, so we trust it
1429 // Open identical and different files
1430 FileLocation fileloc[3];
1432 if (paths.GetSize() < 3)
1434 if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(0))
1437 strDesc[0] = _("Untitled left");
1439 if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(1))
1442 strDesc[1] = _("Untitled right");
1447 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(0))
1450 strDesc[0] = _("Untitled left");
1452 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(1))
1455 strDesc[1] = _("Untitled middle");
1457 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(2))
1460 strDesc[2] = _("Untitled right");
1464 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
1465 fileloc[nIndex].setPath(paths[nIndex]);
1466 GetMainFrame()->ShowAutoMergeDoc(pDoc, paths.GetSize(), fileloc,
1467 dwFlags, strDesc, _T(""), infoUnpacker);
1471 void CDirView::OpenSelectionHex()
1473 CDirDoc * pDoc = GetDocument();
1474 const CDiffContext& ctxt = GetDiffContext();
1476 // First, figure out what was selected (store into pos1 & pos2)
1477 uintptr_t pos1 = NULL, pos2 = NULL;
1478 int sel1 = -1, sel2 = -1, sel3 = -1;
1479 if (!GetSelectedItems(&sel1, &sel2, &sel3))
1481 // Must have 1 or 2 items selected
1486 pos1 = GetItemKey(sel1);
1489 pos2 = GetItemKey(sel2);
1491 // Now handle the various cases of what was selected
1493 if (pos1 == SPECIAL_ITEM_POS)
1499 // Common variables which both code paths below are responsible for setting
1501 const DIFFITEM *pdi[3]; // left & right items (di1==di2 if single selection)
1502 bool isdir = false; // set if we're comparing directories
1507 success = GetOpenTwoItems(ctxt, SELECTIONTYPE_NORMAL, pos1, pos2, pdi,
1508 paths, sel1, sel2, isdir, nPane, errmsg);
1511 // Only one item selected, so perform diff on its sides
1512 success = GetOpenOneItem(ctxt, pos1, pdi,
1513 paths, sel1, isdir, nPane, errmsg);
1517 if (!errmsg.empty())
1518 AfxMessageBox(errmsg.c_str(), MB_ICONSTOP);
1522 // Open identical and different files
1523 DWORD dwFlags[3] = { 0 };
1524 FileLocation fileloc[3];
1525 for (int pane = 0; pane < paths.GetSize(); pane++)
1527 fileloc[pane].setPath(paths[pane]);
1528 dwFlags[pane] |= FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nPane[pane]) ? FFILEOPEN_READONLY : 0);
1530 GetMainFrame()->ShowHexMergeDoc(pDoc, paths.GetSize(), fileloc, dwFlags, NULL);
1533 /// User chose (context menu) delete left
1534 template<SIDE_TYPE stype>
1535 void CDirView::OnCtxtDirDel()
1537 DoDirAction(&DirActions::DeleteOn<stype>, _("Deleting files..."));
1540 /// User chose (context menu) delete both
1541 void CDirView::OnCtxtDirDelBoth()
1543 DoDirAction(&DirActions::DeleteOnBoth, _("Deleting files..."));
1546 /// Enable/disable Delete Left menu choice on context menu
1547 template<SIDE_TYPE stype>
1548 void CDirView::OnUpdateCtxtDirDel(CCmdUI* pCmdUI)
1550 Counts counts = Count(&DirActions::IsItemDeletableOn<stype>);
1551 pCmdUI->Enable(counts.count > 0);
1552 pCmdUI->SetText(FormatMenuItemString(stype, counts.count, counts.total).c_str());
1555 /// Enable/disable Delete Both menu choice on context menu
1556 void CDirView::OnUpdateCtxtDirDelBoth(CCmdUI* pCmdUI)
1558 Counts counts = Count(&DirActions::IsItemDeletableOnBoth);
1559 pCmdUI->Enable(counts.count > 0);
1560 pCmdUI->SetText(FormatMenuItemStringAll(GetDocument()->m_nDirs, counts.count, counts.total).c_str());
1564 * @brief Update "Copy | Right to..." item
1566 template<SIDE_TYPE stype>
1567 void CDirView::OnUpdateCtxtDirCopyTo(CCmdUI* pCmdUI)
1569 Counts counts = Count(&DirActions::IsItemCopyableToOn<stype>);
1570 pCmdUI->Enable(counts.count > 0);
1571 pCmdUI->SetText(FormatMenuItemStringTo(stype, counts.count, counts.total).c_str());
1574 void CDirView::OnUpdateCtxtDirCopyBothTo(CCmdUI* pCmdUI)
1576 Counts counts = Count(&DirActions::IsItemCopyableBothToOn);
1577 pCmdUI->Enable(counts.count > 0);
1578 pCmdUI->SetText(FormatMenuItemStringAllTo(GetDocument()->m_nDirs, counts.count, counts.total).c_str());
1581 void CDirView::OnUpdateCtxtDirCopyBothDiffsOnlyTo(CCmdUI* pCmdUI)
1583 Counts counts = Count(&DirActions::IsItemNavigableDiff);
1584 pCmdUI->Enable(counts.count > 0);
1585 pCmdUI->SetText(FormatMenuItemStringDifferencesTo(counts.count, counts.total).c_str());
1589 * @brief Get keydata associated with item in given index.
1590 * @param [in] idx Item's index to list in UI.
1591 * @return Key for item in given index.
1593 uintptr_t CDirView::GetItemKey(int idx) const
1595 return (uintptr_t) m_pList->GetItemData(idx);
1598 // SetItemKey & GetItemKey encapsulate how the display list items
1599 // are mapped to DiffItems, which in turn are DiffContext keys to the actual DIFFITEM data
1602 * @brief Get DIFFITEM data for item.
1603 * This function returns DIFFITEM data for item in given index in GUI.
1604 * @param [in] sel Item's index in folder compare GUI list.
1605 * @return DIFFITEM for item.
1607 const DIFFITEM &CDirView::GetDiffItem(int sel) const
1609 CDirView * pDirView = const_cast<CDirView *>(this);
1610 return pDirView->GetDiffItem(sel);
1614 * Given index in list control, get modifiable reference to its DIFFITEM data
1616 DIFFITEM & CDirView::GetDiffItem(int sel)
1618 uintptr_t diffpos = GetItemKey(sel);
1620 // If it is special item, return empty DIFFITEM
1621 if (diffpos == SPECIAL_ITEM_POS)
1623 // TODO: It would be better if there were individual items
1624 // for whatever these special items are
1625 // because here we have to hope client does not modify this
1626 // static (shared) item
1627 return DIFFITEM::emptyitem;
1629 return GetDiffContext().GetDiffRefAt(diffpos);
1632 void CDirView::DeleteItem(int sel)
1635 CollapseSubdir(sel);
1636 m_pList->DeleteItem(sel);
1639 void CDirView::DeleteAllDisplayItems()
1641 // item data are just positions (diffposes)
1642 // that is, they contain no memory needing to be freed
1643 m_pList->DeleteAllItems();
1647 * @brief Given key, get index of item which has it stored.
1648 * This function searches from list in UI.
1650 int CDirView::GetItemIndex(uintptr_t key)
1652 LVFINDINFO findInfo;
1654 findInfo.flags = LVFI_PARAM; // Search for itemdata
1655 findInfo.lParam = (LPARAM)key;
1656 return m_pList->FindItem(&findInfo);
1660 * @brief Get the file names on both sides for specified item.
1661 * @note Return empty strings if item is special item.
1663 void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const
1665 UINT_PTR diffpos = GetItemKey(sel);
1666 if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS)
1673 const CDiffContext& ctxt = GetDiffContext();
1674 ::GetItemFileNames(ctxt, ctxt.GetDiffAt(diffpos), strLeft, strRight);
1679 * @brief Get the file names on both sides for specified item.
1680 * @note Return empty strings if item is special item.
1682 void CDirView::GetItemFileNames(int sel, PathContext * paths) const
1684 UINT_PTR diffpos = GetItemKey(sel);
1685 if (diffpos == SPECIAL_ITEM_POS)
1687 for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++)
1688 paths->SetPath(nIndex, _T(""));
1692 const CDiffContext& ctxt = GetDiffContext();
1693 *paths = ::GetItemFileNames(ctxt, ctxt.GetDiffAt(diffpos));
1698 * @brief Open selected file with registered application.
1699 * Uses shell file associations to open file with registered
1700 * application. We first try to use "Edit" action which should
1701 * open file to editor, since we are more interested editing
1702 * files than running them (scripts).
1703 * @param [in] stype Side of file to open.
1705 void CDirView::DoOpen(SIDE_TYPE stype)
1707 int sel = GetSingleSelectedItem();
1708 if (sel == -1) return;
1709 DirItemIterator dirBegin = SelBegin();
1710 String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
1711 if (file.empty()) return;
1712 HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), file.c_str(), 0, 0, SW_SHOWNORMAL);
1713 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
1714 rtn = ShellExecute(::GetDesktopWindow(), _T("open"), file.c_str(), 0, 0, SW_SHOWNORMAL);
1715 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
1719 /// Open with dialog for file on selected side
1720 void CDirView::DoOpenWith(SIDE_TYPE stype)
1722 int sel = GetSingleSelectedItem();
1723 if (sel == -1) return;
1724 DirItemIterator dirBegin = SelBegin();
1725 String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
1726 if (file.empty()) return;
1728 if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH)) return;
1729 sysdir.ReleaseBuffer();
1730 CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + file.c_str();
1731 ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg, sysdir, SW_SHOWNORMAL);
1734 /// Open selected file on specified side to external editor
1735 void CDirView::DoOpenWithEditor(SIDE_TYPE stype)
1737 int sel = GetSingleSelectedItem();
1738 if (sel == -1) return;
1739 DirItemIterator dirBegin = SelBegin();
1740 String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
1741 if (file.empty()) return;
1743 theApp.OpenFileToExternalEditor(file);
1746 void CDirView::DoOpenParentFolder(SIDE_TYPE stype)
1748 int sel = GetSingleSelectedItem();
1749 if (sel == -1) return;
1750 DirItemIterator dirBegin = SelBegin();
1751 String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
1752 if (file.empty()) return;
1753 String parentFolder = paths::GetParentPath(file);
1754 ShellExecute(::GetDesktopWindow(), _T("open"), parentFolder.c_str(), 0, 0, SW_SHOWNORMAL);
1757 /// User chose (context menu) open left
1758 template<SIDE_TYPE stype>
1759 void CDirView::OnCtxtDirOpen()
1764 /// User chose (context menu) open left with
1765 template<SIDE_TYPE stype>
1766 void CDirView::OnCtxtDirOpenWith()
1771 /// User chose (context menu) open left with editor
1772 template<SIDE_TYPE stype>
1773 void CDirView::OnCtxtDirOpenWithEditor()
1775 DoOpenWithEditor(stype);
1778 /// User chose (context menu) open left parent folder
1779 template<SIDE_TYPE stype>
1780 void CDirView::OnCtxtDirOpenParentFolder()
1782 DoOpenParentFolder(stype);
1785 /// Update context menuitem "Open left | with editor"
1786 template<SIDE_TYPE stype>
1787 void CDirView::OnUpdateCtxtDirOpenWithEditor(CCmdUI* pCmdUI)
1789 Counts counts = Count(&DirActions::IsItemOpenableOnWith<stype>);
1790 pCmdUI->Enable(counts.count > 0 && counts.total == 1);
1793 // return selected item index, or -1 if none or multiple
1794 int CDirView::GetSingleSelectedItem() const
1796 int sel = -1, sel2 = -1;
1797 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
1798 if (sel == -1) return -1;
1799 sel2 = m_pList->GetNextItem(sel, LVNI_SELECTED);
1800 if (sel2 != -1) return -1;
1803 // Enable/disable Open Left menu choice on context menu
1804 template<SIDE_TYPE stype>
1805 void CDirView::OnUpdateCtxtDirOpen(CCmdUI* pCmdUI)
1807 Counts counts = Count(&DirActions::IsItemOpenableOn<stype>);
1808 pCmdUI->Enable(counts.count > 0 && counts.total == 1);
1811 // Enable/disable Open Left With menu choice on context menu
1812 template<SIDE_TYPE stype>
1813 void CDirView::OnUpdateCtxtDirOpenWith(CCmdUI* pCmdUI)
1815 Counts counts = Count(&DirActions::IsItemOpenableOnWith<stype>);
1816 pCmdUI->Enable(counts.count > 0 && counts.total == 1);
1819 // Enable/disable Open Parent Folder menu choice on context menu
1820 template<SIDE_TYPE stype>
1821 void CDirView::OnUpdateCtxtDirOpenParentFolder(CCmdUI* pCmdUI)
1823 Counts counts = Count(&DirActions::IsParentFolderOpenable<stype>);
1824 pCmdUI->Enable(counts.count > 0 && counts.total == 1);
1828 void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI)
1830 int sel1 = -1, sel2 = -1, sel3 = -1;
1831 if (!GetSelectedItems(&sel1, &sel2, &sel3))
1833 // 0 items or more than 2 items seleted
1834 pCmdUI->Enable(FALSE);
1839 // One item selected
1840 if (selectionType != SELECTIONTYPE_NORMAL)
1842 pCmdUI->Enable(FALSE);
1846 else if (sel3 == -1)
1848 // Two items selected
1849 const DIFFITEM& di1 = GetDiffItem(sel1);
1850 const DIFFITEM& di2 = GetDiffItem(sel2);
1851 if (!AreItemsOpenable(GetDiffContext(), selectionType, di1, di2))
1853 pCmdUI->Enable(FALSE);
1859 // Three items selected
1860 const DIFFITEM& di1 = GetDiffItem(sel1);
1861 const DIFFITEM& di2 = GetDiffItem(sel2);
1862 const DIFFITEM& di3 = GetDiffItem(sel3);
1863 if (selectionType != SELECTIONTYPE_NORMAL || !::AreItemsOpenable(GetDiffContext(), di1, di2, di3))
1865 pCmdUI->Enable(FALSE);
1869 pCmdUI->Enable(TRUE);
1873 * @brief Return count of selected items in folder compare.
1875 UINT CDirView::GetSelectedCount() const
1877 return m_pList->GetSelectedCount();
1881 * @brief Return index of first selected item in folder compare.
1883 int CDirView::GetFirstSelectedInd()
1885 return m_pList->GetNextItem(-1, LVNI_SELECTED);
1889 // If none or one item selected select found item
1890 // This is used for scrolling to first diff too
1891 void CDirView::OnFirstdiff()
1893 DirItemIterator it =
1894 std::find_if(Begin(), End(), MakeDirActions(&DirActions::IsItemNavigableDiff));
1896 MoveFocus(GetFirstSelectedInd(), it.m_sel, GetSelectedCount());
1899 void CDirView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1901 pCmdUI->Enable(GetFirstDifferentItem() > -1);
1905 // If none or one item selected select found item
1906 void CDirView::OnLastdiff()
1908 DirItemIterator it =
1909 std::find_if(RevBegin(), RevEnd(), MakeDirActions(&DirActions::IsItemNavigableDiff));
1911 MoveFocus(GetFirstSelectedInd(), it.m_sel, GetSelectedCount());
1914 void CDirView::OnUpdateLastdiff(CCmdUI* pCmdUI)
1916 pCmdUI->Enable(GetFirstDifferentItem() > -1);
1919 bool CDirView::HasNextDiff()
1921 int lastDiff = GetLastDifferentItem();
1923 // Check if different files were found and
1924 // there is different item after focused item
1925 return (lastDiff > -1) && (GetFocusedItem() < lastDiff);
1928 bool CDirView::HasPrevDiff()
1930 int firstDiff = GetFirstDifferentItem();
1932 // Check if different files were found and
1933 // there is different item before focused item
1934 return (firstDiff > -1) && (firstDiff < GetFocusedItem());
1937 void CDirView::MoveToNextDiff()
1939 int currentInd = GetFocusedItem();
1940 DirItemIterator begin(m_pIList.get(), currentInd + 1);
1941 DirItemIterator it =
1942 std::find_if(begin, End(), MakeDirActions(&DirActions::IsItemNavigableDiff));
1944 MoveFocus(currentInd, it.m_sel, GetSelectedCount());
1947 void CDirView::MoveToPrevDiff()
1949 int currentInd = GetFocusedItem();
1950 if (currentInd <= 0)
1952 DirItemIterator begin(m_pIList.get(), currentInd - 1, false, true);
1953 DirItemIterator it =
1954 std::find_if(begin, RevEnd(), MakeDirActions(&DirActions::IsItemNavigableDiff));
1956 MoveFocus(currentInd, it.m_sel, GetSelectedCount());
1959 void CDirView::OpenNextDiff()
1962 int currentInd = GetFocusedItem();
1963 const DIFFITEM& dip = GetDiffItem(currentInd);
1964 if (!dip.diffcode.isDirectory())
1970 GetParentFrame()->ActivateFrame();
1974 void CDirView::OpenPrevDiff()
1977 int currentInd = GetFocusedItem();
1978 const DIFFITEM& dip = GetDiffItem(currentInd);
1979 if (!dip.diffcode.isDirectory())
1985 GetParentFrame()->ActivateFrame();
1989 void CDirView::SetActivePane(int pane)
1991 if (m_nActivePane >= 0)
1992 GetParentFrame()->GetHeaderInterface()->SetActive(m_nActivePane, false);
1993 GetParentFrame()->GetHeaderInterface()->SetActive(pane, true);
1994 m_nActivePane = pane;
1998 // If none or one item selected select found item
1999 void CDirView::OnNextdiff()
2005 void CDirView::OnUpdateNextdiff(CCmdUI* pCmdUI)
2007 pCmdUI->Enable(HasNextDiff());
2011 // If none or one item selected select found item
2012 void CDirView::OnPrevdiff()
2018 void CDirView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
2020 pCmdUI->Enable(HasPrevDiff());
2023 void CDirView::OnCurdiff()
2025 const int count = m_pList->GetItemCount();
2027 int i = GetFirstSelectedInd();
2029 // No selection - no diff to go
2033 while (i < count && found == false)
2035 UINT selected = m_pList->GetItemState(i, LVIS_SELECTED);
2036 UINT focused = m_pList->GetItemState(i, LVIS_FOCUSED);
2038 if (selected == LVIS_SELECTED && focused == LVIS_FOCUSED)
2040 m_pList->EnsureVisible(i, FALSE);
2047 void CDirView::OnUpdateCurdiff(CCmdUI* pCmdUI)
2049 pCmdUI->Enable(GetFirstSelectedInd() > -1);
2052 int CDirView::GetFocusedItem()
2054 return m_pList->GetNextItem(-1, LVNI_FOCUSED);
2057 int CDirView::GetFirstDifferentItem()
2059 if (!m_bNeedSearchFirstDiffItem)
2060 return m_firstDiffItem;
2062 DirItemIterator it =
2063 std::find_if(Begin(), End(), MakeDirActions(&DirActions::IsItemNavigableDiff));
2064 m_firstDiffItem = it.m_sel;
2065 m_bNeedSearchFirstDiffItem = false;
2067 return m_firstDiffItem;
2070 int CDirView::GetLastDifferentItem()
2072 if (!m_bNeedSearchLastDiffItem)
2073 return m_lastDiffItem;
2075 DirItemIterator it =
2076 std::find_if(RevBegin(), RevEnd(), MakeDirActions(&DirActions::IsItemNavigableDiff));
2077 m_lastDiffItem = it.m_sel;
2078 m_bNeedSearchLastDiffItem = false;
2080 return m_lastDiffItem;
2084 * @brief Move focus to specified item (and selection if multiple items not selected)
2086 * Moves the focus from item [currentInd] to item [i]
2087 * Additionally, if there are not multiple items selected,
2088 * deselects item [currentInd] and selects item [i]
2090 void CDirView::MoveFocus(int currentInd, int i, int selCount)
2094 // Not multiple items selected, so bring selection with us
2095 m_pList->SetItemState(currentInd, 0, LVIS_SELECTED);
2096 m_pList->SetItemState(currentInd, 0, LVIS_FOCUSED);
2097 m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2100 // Move focus to specified item
2101 // (this automatically defocuses old item)
2102 m_pList->SetItemState(i, LVIS_FOCUSED, LVIS_FOCUSED);
2103 m_pList->EnsureVisible(i, FALSE);
2106 void CDirView::OnUpdateSave(CCmdUI* pCmdUI)
2108 pCmdUI->Enable(FALSE);
2111 CDirFrame * CDirView::GetParentFrame()
2113 // can't verify cast without introducing more coupling
2114 // (CDirView doesn't include DirFrame.h)
2115 return static_cast<CDirFrame *>(CListView::GetParentFrame());
2118 void CDirView::OnRefresh()
2120 m_pSavedTreeState.reset(SaveTreeState(GetDiffContext()));
2121 GetDocument()->Rescan();
2124 BOOL CDirView::PreTranslateMessage(MSG* pMsg)
2126 // Handle special shortcuts here
2127 if (pMsg->message == WM_KEYDOWN)
2129 if (false == IsLabelEdit())
2131 // Check if we got 'ESC pressed' -message
2132 if (pMsg->wParam == VK_ESCAPE)
2134 if (m_pCmpProgressBar)
2136 OnBnClickedComparisonStop();
2142 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
2146 // Check if we got 'DEL pressed' -message
2147 if (pMsg->wParam == VK_DELETE)
2149 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_MERGE_DELETE);
2152 int sel = GetFocusedItem();
2153 // Check if we got 'Backspace pressed' -message
2154 if (pMsg->wParam == VK_BACK)
2156 if (!GetDiffContext().m_bRecursive)
2158 OpenParentDirectory();
2161 else if (m_bTreeMode && sel >= 0)
2163 const DIFFITEM& di = GetDiffItem(sel);
2164 if (di.parent != NULL)
2166 int i = GetItemIndex((uintptr_t)di.parent);
2168 MoveFocus(sel, i, GetSelectedCount());
2174 DIFFITEM& dip = this->GetDiffItem(sel);
2175 if (pMsg->wParam == VK_LEFT)
2177 if (m_bTreeMode && GetDiffContext().m_bRecursive && (!(dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren()))
2178 PostMessage(WM_KEYDOWN, VK_BACK);
2180 CollapseSubdir(sel);
2183 if (pMsg->wParam == VK_SUBTRACT)
2185 CollapseSubdir(sel);
2188 if (pMsg->wParam == VK_RIGHT)
2190 if (m_bTreeMode && GetDiffContext().m_bRecursive && dip.customFlags1 & ViewCustomFlags::EXPANDED && dip.HasChildren())
2191 PostMessage(WM_KEYDOWN, VK_DOWN);
2196 if (pMsg->wParam == VK_ADD)
2201 if (pMsg->wParam == VK_MULTIPLY)
2203 ExpandSubdir(sel, true);
2210 // ESC doesn't close window when user is renaming an item.
2211 if (pMsg->wParam == VK_ESCAPE)
2213 m_bUserCancelEdit = TRUE;
2215 // The edit control send LVN_ENDLABELEDIT when it loses focus,
2216 // so we use it to cancel the rename action.
2217 m_pList->SetFocus();
2219 // Stop the ESC before it reach the main frame which might
2220 // cause a program termination.
2225 return CListView::PreTranslateMessage(pMsg);
2228 void CDirView::OnUpdateRefresh(CCmdUI* pCmdUI)
2230 UINT threadState = GetDocument()->m_diffThread.GetThreadState();
2231 pCmdUI->Enable(threadState != CDiffThread::THREAD_COMPARING);
2235 * @brief Called when compare thread asks UI update.
2236 * @note Currently thread asks update after compare is ready
2239 LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
2241 UNREFERENCED_PARAMETER(lParam);
2243 CDirDoc * pDoc = GetDocument();
2246 if (wParam == CDiffThread::EVENT_COMPARE_COMPLETED)
2248 // Close and destroy the dialog after compare
2249 if (m_pCmpProgressBar)
2250 GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), FALSE, FALSE);
2251 m_pCmpProgressBar.reset();
2253 pDoc->CompareReady();
2257 if (!pDoc->GetReportFile().empty())
2259 OnToolsGenerateReport();
2260 pDoc->SetReportFile(_T(""));
2263 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
2268 // If compare took more than TimeToSignalCompare seconds, notify user
2269 clock_t elapsed = clock() - m_compareStart;
2270 GetParentFrame()->SetStatus(
2271 strutils::format(_("Elapsed time: %ld ms").c_str(), elapsed).c_str()
2273 if (elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
2275 GetMainFrame()->StartFlashing();
2277 else if (wParam == CDiffThread::EVENT_COMPARE_PROGRESSED)
2279 InvalidateRect(NULL, FALSE);
2281 else if (wParam == CDiffThread::EVENT_COLLECT_COMPLETED)
2283 if (m_pSavedTreeState)
2285 RestoreTreeState(GetDiffContext(), m_pSavedTreeState.get());
2286 m_pSavedTreeState.reset();
2291 if (m_bExpandSubdirs)
2292 OnViewExpandAllSubdirs();
2298 return 0; // return value unused
2302 BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
2304 NMHDR * hdr = reinterpret_cast<NMHDR *>(lParam);
2305 if (hdr->code == HDN_ENDDRAG)
2306 return OnHeaderEndDrag((LPNMHEADER)hdr, pResult);
2307 if (hdr->code == HDN_BEGINDRAG)
2308 return OnHeaderBeginDrag((LPNMHEADER)hdr, pResult);
2310 return CListView::OnNotify(wParam, lParam, pResult);
2313 BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
2315 if (uMsg == WM_NOTIFY)
2317 NMHDR *pNMHDR = (NMHDR *)lParam;
2318 switch (pNMHDR->code)
2320 case LVN_GETDISPINFO:
2321 ReflectGetdispinfo((NMLVDISPINFO *)lParam);
2323 case LVN_GETINFOTIPW:
2324 case LVN_GETINFOTIPA:
2328 return CListView::OnChildNotify(uMsg, wParam, lParam, pResult);
2332 * @brief User is starting to drag a column header
2334 BOOL CDirView::OnHeaderBeginDrag(LPNMHEADER hdr, LRESULT* pResult)
2336 // save column widths before user reorders them
2337 // so we can reload them on the end drag
2338 String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
2339 theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
2340 m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)).c_str());
2345 * @brief User just finished dragging a column header
2347 BOOL CDirView::OnHeaderEndDrag(LPNMHEADER hdr, LRESULT* pResult)
2349 int src = hdr->iItem;
2350 int dest = hdr->pitem->iOrder;
2351 bool allowDrop = TRUE;
2352 *pResult = !allowDrop;
2353 if (allowDrop && src != dest && dest != -1)
2355 m_pColItems->MoveColumn(src, dest);
2362 * @brief Remove any windows reordering of columns
2364 void CDirView::FixReordering()
2367 lvcol.mask = LVCF_ORDER;
2372 for (int i = 0; i < m_pColItems->GetColCount(); ++i)
2375 GetListCtrl().SetColumn(i, &lvcol);
2379 /** @brief Add columns to display, loading width & order from registry. */
2380 void CDirView::LoadColumnHeaderItems()
2382 bool dummyflag = false;
2384 CHeaderCtrl * h = m_pList->GetHeaderCtrl();
2385 if (h->GetItemCount())
2388 while (m_pList->GetHeaderCtrl()->GetItemCount() > 1)
2389 m_pList->DeleteColumn(1);
2392 for (int i = 0; i < m_pColItems->GetDispColCount(); ++i)
2395 lvc.mask = LVCF_FMT + LVCF_SUBITEM + LVCF_TEXT;
2396 lvc.fmt = LVCFMT_LEFT;
2398 lvc.pszText = _T("text");
2400 m_pList->InsertColumn(i, &lvc);
2403 m_pList->DeleteColumn(1);
2407 void CDirView::SetFont(const LOGFONT & lf)
2409 m_font.DeleteObject();
2410 m_font.CreateFontIndirect(&lf);
2411 CWnd::SetFont(&m_font);
2414 /** @brief Fire off a resort of the data, to take place when things stabilize. */
2415 void CDirView::InitiateSort()
2417 PostMessage(WM_TIMER, COLUMN_REORDER);
2420 void CDirView::OnTimer(UINT_PTR nIDEvent)
2422 if (nIDEvent == COLUMN_REORDER)
2424 // Remove the windows reordering, as we're doing it ourselves
2426 // Now redraw screen
2427 UpdateColumnNames();
2428 m_pColItems->LoadColumnWidths(
2429 (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnWidths")),
2430 std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), DefColumnWidth);
2433 else if (nIDEvent == STATUSBAR_UPDATE)
2435 int items = GetSelectedCount();
2436 String msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
2437 GetParentFrame()->SetStatus(msg.c_str());
2440 CListView::OnTimer(nIDEvent);
2444 * @brief Change left-side readonly-status
2446 template<SIDE_TYPE stype>
2447 void CDirView::OnReadOnly()
2449 const int index = SideToIndex(GetDiffContext(), stype);
2450 bool bReadOnly = GetDocument()->GetReadOnly(index);
2451 GetDocument()->SetReadOnly(index, !bReadOnly);
2455 * @brief Update left-readonly menu item
2457 template<SIDE_TYPE stype>
2458 void CDirView::OnUpdateReadOnly(CCmdUI* pCmdUI)
2460 const int index = SideToIndex(GetDiffContext(), stype);
2461 bool bReadOnly = GetDocument()->GetReadOnly(index);
2462 if (stype != SIDE_MIDDLE)
2464 pCmdUI->Enable(TRUE);
2465 pCmdUI->SetCheck(bReadOnly);
2469 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
2470 pCmdUI->SetCheck(bReadOnly && GetDocument()->m_nDirs > 2);
2475 * @brief Update left-side readonly statusbar item
2477 void CDirView::OnUpdateStatusLeftRO(CCmdUI* pCmdUI)
2479 bool bROLeft = GetDocument()->GetReadOnly(0);
2480 pCmdUI->Enable(bROLeft);
2484 * @brief Update middle readonly statusbar item
2486 void CDirView::OnUpdateStatusMiddleRO(CCmdUI* pCmdUI)
2488 bool bROMiddle = GetDocument()->GetReadOnly(1);
2489 pCmdUI->Enable(bROMiddle && GetDocument()->m_nDirs > 2);
2493 * @brief Update right-side readonly statusbar item
2495 void CDirView::OnUpdateStatusRightRO(CCmdUI* pCmdUI)
2497 bool bRORight = GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1);
2498 pCmdUI->Enable(bRORight);
2502 * @brief Open dialog to customize dirview columns
2504 void CDirView::OnCustomizeColumns()
2506 // Located in DirViewColHandler.cpp
2508 String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
2509 theApp.WriteProfileString(secname.c_str(), _T("ColumnOrders"), m_pColItems->SaveColumnOrders().c_str());
2512 void CDirView::OnCtxtOpenWithUnpacker()
2515 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
2518 // let the user choose a handler
2519 CSelectUnpackerDlg dlg(GetDiffItem(sel).diffFileInfo[0].filename, this);
2520 // create now a new infoUnpacker to initialize the manual/automatic flag
2521 PackingInfo infoUnpacker(PLUGIN_AUTO);
2522 dlg.SetInitialInfoHandler(&infoUnpacker);
2524 if (dlg.DoModal() == IDOK)
2526 infoUnpacker = dlg.GetInfoHandler();
2527 OpenSelection(SELECTIONTYPE_NORMAL, &infoUnpacker);
2533 void CDirView::OnUpdateCtxtOpenWithUnpacker(CCmdUI* pCmdUI)
2535 if (!GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
2537 pCmdUI->Enable(FALSE);
2541 // we need one selected file, existing on both side
2542 if (m_pList->GetSelectedCount() != 1)
2543 pCmdUI->Enable(FALSE);
2547 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
2548 const DIFFITEM& di = GetDiffItem(sel);
2549 pCmdUI->Enable(IsItemDeletableOnBoth(GetDiffContext(), di));
2554 * @brief Fill string list with current dirview column registry key names
2556 void CDirView::GetCurrentColRegKeys(std::vector<String>& colKeys)
2558 int nphyscols = GetListCtrl().GetHeaderCtrl()->GetItemCount();
2559 for (int col = 0; col < nphyscols; ++col)
2561 int logcol = m_pColItems->ColPhysToLog(col);
2562 colKeys.push_back(m_pColItems->GetColRegValueNameBase(logcol));
2566 struct FileCmpReport: public IFileCmpReport
2568 explicit FileCmpReport(CDirView *pDirView) : m_pDirView(pDirView) {}
2569 bool operator()(REPORT_TYPE nReportType, IListCtrl *pList, int nIndex, const String &sDestDir, String &sLinkPath)
2571 const CDiffContext& ctxt = m_pDirView->GetDiffContext();
2572 const DIFFITEM &di = m_pDirView->GetDiffItem(nIndex);
2574 String sLinkFullPath = paths::ConcatPath(ctxt.GetLeftPath(), di.diffFileInfo[0].GetFile());
2576 if (di.diffcode.isDirectory() || !IsItemNavigableDiff(ctxt, di) || IsArchiveFile(sLinkFullPath))
2582 sLinkPath = di.diffFileInfo[0].GetFile();
2584 strutils::replace(sLinkPath, _T("\\"), _T("_"));
2585 sLinkPath += _T(".html");
2587 m_pDirView->MoveFocus(m_pDirView->GetFirstSelectedInd(), nIndex, m_pDirView->GetSelectedCount());
2589 m_pDirView->OpenSelection();
2590 CFrameWnd * pFrame = GetMainFrame()->GetActiveFrame();
2591 IMergeDoc * pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
2593 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
2597 pMergeDoc->GenerateReport(paths::ConcatPath(sDestDir, sLinkPath));
2598 pMergeDoc->CloseNow();
2602 while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
2603 if (!AfxGetApp()->PumpMessage())
2605 GetMainFrame()->OnUpdateFrameTitle(FALSE);
2611 CDirView *m_pDirView;
2615 * @brief Generate report from dir compare results.
2617 void CDirView::OnToolsGenerateReport()
2619 CDirDoc *pDoc = GetDocument();
2620 pDoc->SetGeneratingReport(true);
2621 const CDiffContext& ctxt = GetDiffContext();
2623 // Make list of registry keys for columns
2624 // (needed for XML reports)
2625 std::vector<String> colKeys;
2626 GetCurrentColRegKeys(colKeys);
2628 DirCmpReport report(colKeys);
2629 FileCmpReport freport(this);
2630 IListCtrlImpl list(m_pList->m_hWnd);
2631 report.SetList(&list);
2632 PathContext paths = ctxt.GetNormalizedPaths();
2634 // If inside archive, convert paths
2635 if (pDoc->IsArchiveFolders())
2637 for (int i = 0; i < paths.GetSize(); i++)
2638 pDoc->ApplyDisplayRoot(i, paths[i]);
2641 report.SetRootPaths(paths);
2642 report.SetColumns(m_pColItems->GetDispColCount());
2643 report.SetFileCmpReport(&freport);
2644 report.SetReportFile(pDoc->GetReportFile());
2646 if (report.GenerateReport(errStr))
2650 if (pDoc->GetReportFile().empty())
2651 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
2655 String msg = strutils::format_string1(
2656 _("Error creating the report:\n%1"),
2658 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
2661 pDoc->SetGeneratingReport(false);
2665 * @brief Generate patch from files selected.
2667 * Creates a patch from selected files in active directory compare, or
2668 * active file compare. Files in file compare must be saved before
2671 void CDirView::OnToolsGeneratePatch()
2674 const CDiffContext& ctxt = GetDiffContext();
2676 // Get selected items from folder compare
2677 BOOL bValidFiles = TRUE;
2678 for (DirItemIterator it = SelBegin(); bValidFiles && it != SelEnd(); ++it)
2680 const DIFFITEM &item = *it;
2681 if (item.diffcode.isBin())
2683 LangMessageBox(IDS_CANNOT_CREATE_BINARYPATCH, MB_ICONWARNING |
2684 MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_BINARYPATCH);
2685 bValidFiles = FALSE;
2687 else if (item.diffcode.isDirectory())
2689 LangMessageBox(IDS_CANNOT_CREATE_DIRPATCH, MB_ICONWARNING |
2690 MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_DIRPATCH);
2691 bValidFiles = FALSE;
2696 // Format full paths to files (leftFile/rightFile)
2697 String leftFile = item.getFilepath(0, ctxt.GetNormalizedPath(0));
2698 if (!leftFile.empty())
2699 leftFile = paths::ConcatPath(leftFile, item.diffFileInfo[0].filename);
2700 String rightFile = item.getFilepath(1, ctxt.GetNormalizedPath(1));
2701 if (!rightFile.empty())
2702 rightFile = paths::ConcatPath(rightFile, item.diffFileInfo[1].filename);
2704 // Format relative paths to files in folder compare
2705 String leftpatch = item.diffFileInfo[0].path;
2706 if (!leftpatch.empty())
2707 leftpatch += _T("/");
2708 leftpatch += item.diffFileInfo[0].filename;
2709 String rightpatch = item.diffFileInfo[1].path;
2710 if (!rightpatch.empty())
2711 rightpatch += _T("/");
2712 rightpatch += item.diffFileInfo[1].filename;
2713 patcher.AddFiles(leftFile, leftpatch, rightFile, rightpatch);
2717 patcher.CreatePatch();
2721 * @brief Add special items for non-recursive compare
2722 * to directory view.
2724 * Currently only special item is ".." for browsing to
2726 * @return number of items added to view
2728 int CDirView::AddSpecialItems()
2730 CDirDoc *pDoc = GetDocument();
2732 bool bEnable = true;
2733 PathContext pathsParent;
2734 switch (CheckAllowUpwardDirectory(GetDiffContext(), pDoc->m_pTempPathContext, pathsParent))
2736 case AllowUpwardDirectory::No:
2740 AddParentFolderItem(bEnable);
2743 case AllowUpwardDirectory::Never:
2750 * @brief Add "Parent folder" ("..") item to directory view
2752 void CDirView::AddParentFolderItem(bool bEnable)
2754 AddNewItem(0, SPECIAL_ITEM_POS, bEnable ? DIFFIMG_DIRUP : DIFFIMG_DIRUP_DISABLE, 0);
2758 void CDirView::OnCtxtDirZip()
2760 if (!HasZipSupport())
2762 LangMessageBox(IDS_NO_ZIP_SUPPORT, MB_ICONINFORMATION);
2768 this, LVNI_SELECTED | flag
2769 ).CompressArchive();
2772 void CDirView::ShowShellContextMenu(SIDE_TYPE stype)
2774 CShellContextMenu *pContextMenu = NULL;
2778 if (!m_pShellContextMenuLeft)
2779 m_pShellContextMenuLeft.reset(new CShellContextMenu(LeftCmdFirst, LeftCmdLast));
2780 pContextMenu = m_pShellContextMenuLeft.get();
2783 if (!m_pShellContextMenuMiddle)
2784 m_pShellContextMenuMiddle.reset(new CShellContextMenu(MiddleCmdFirst, MiddleCmdLast));
2785 pContextMenu = m_pShellContextMenuMiddle.get();
2788 if (!m_pShellContextMenuRight)
2789 m_pShellContextMenuRight.reset(new CShellContextMenu(RightCmdFirst, RightCmdLast));
2790 pContextMenu = m_pShellContextMenuRight.get();
2793 if (pContextMenu && ListShellContextMenu(stype))
2796 GetCursorPos(&point);
2797 HWND hWnd = GetSafeHwnd();
2798 CFrameWnd *pFrame = GetTopLevelFrame();
2799 ASSERT(pFrame != NULL);
2800 BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
2801 pFrame->m_bAutoMenuEnable = FALSE;
2802 BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, NULL);
2804 pContextMenu->InvokeCommand(nCmd, hWnd);
2805 pContextMenu->ReleaseShellContextMenu();
2806 pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
2810 template <SIDE_TYPE stype>
2811 void CDirView::OnCtxtDirShellContextMenu()
2813 ShowShellContextMenu(stype);
2817 * @brief Select all visible items in dir compare
2819 void CDirView::OnSelectAll()
2821 // While the user is renaming an item, select all the edited text.
2822 CEdit *pEdit = m_pList->GetEditControl();
2825 pEdit->SetSel(pEdit->GetWindowTextLength());
2829 int selCount = m_pList->GetItemCount();
2831 for (int i = 0; i < selCount; i++)
2833 // Don't select special items (SPECIAL_ITEM_POS)
2834 uintptr_t diffpos = GetItemKey(i);
2835 if (diffpos != SPECIAL_ITEM_POS)
2836 m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2842 * @brief Update "Select All" item
2844 void CDirView::OnUpdateSelectAll(CCmdUI* pCmdUI)
2846 bool bEnable = (!IsLabelEdit()) || (m_pList->GetItemCount() > 0);
2847 pCmdUI->Enable(bEnable);
2851 * @brief Handle clicks in plugin context view in list
2853 void CDirView::OnPluginPredifferMode(UINT nID)
2855 ApplyPluginPrediffSetting(SelBegin(), SelEnd(), GetDiffContext(),
2856 (nID == ID_PREDIFF_AUTO) ? PLUGIN_AUTO : PLUGIN_MANUAL);
2860 * @brief Updates just before displaying plugin context view in list
2862 void CDirView::OnUpdatePluginPredifferMode(CCmdUI* pCmdUI)
2864 // 2004-04-03, Perry
2865 // CMainFrame::OnUpdatePluginUnpackMode handles this for global unpacking
2866 // and is the template to copy, but here, this is a bit tricky
2867 // as a group of files may be selected
2868 // and they may not all have the same setting
2869 // so I'm not trying this right now
2871 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
2873 BCMenu *pPopup = static_cast<BCMenu*>(pCmdUI->m_pSubMenu);
2877 std::pair<int, int> counts = CountPredifferYesNo(SelBegin(), SelEnd(), GetDiffContext());
2879 CheckContextMenu(pPopup, ID_PREDIFF_AUTO, (counts.first > 0));
2880 CheckContextMenu(pPopup, ID_PREDIFF_MANUAL, (counts.second > 0));
2884 * @brief Refresh cached options.
2886 void CDirView::RefreshOptions()
2888 m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
2889 m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
2890 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
2894 * @brief Copy selected item left side paths (containing filenames) to clipboard.
2896 template<SIDE_TYPE stype>
2897 void CDirView::OnCopyPathnames()
2899 std::list<String> list;
2900 CopyPathnames(SelBegin(), SelEnd(), std::back_inserter(list), stype, GetDiffContext());
2901 PutToClipboard(strutils::join(list.begin(), list.end(), _T("\r\n")), GetMainFrame()->GetSafeHwnd());
2904 void CDirView::OnCopyBothPathnames()
2906 std::list<String> list;
2907 CopyBothPathnames(SelBegin(), SelEnd(), std::back_inserter(list), GetDiffContext());
2908 PutToClipboard(strutils::join(list.begin(), list.end(), _T("\r\n")), GetMainFrame()->GetSafeHwnd());
2912 * @brief Copy selected item filenames to clipboard.
2914 void CDirView::OnCopyFilenames()
2916 std::list<String> list;
2917 CopyFilenames(SelBegin(), SelEnd(), std::back_inserter(list));
2918 PutToClipboard(strutils::join(list.begin(), list.end(), _T("\r\n")), GetMainFrame()->GetSafeHwnd());
2922 * @brief Enable/Disable dirview Copy Filenames context menu item.
2924 void CDirView::OnUpdateCopyFilenames(CCmdUI* pCmdUI)
2926 pCmdUI->Enable(Count(&DirActions::IsItemFile).count > 0);
2930 * @brief Copy selected item left side to clipboard.
2932 template<SIDE_TYPE stype>
2933 void CDirView::OnCopyToClipboard()
2935 std::list<String> list;
2936 CopyPathnames(SelBegin(), SelEnd(), std::back_inserter(list), stype, GetDiffContext());
2937 PutFilesToClipboard(list, GetMainFrame()->GetSafeHwnd());
2941 * @brief Copy selected item both side to clipboard.
2943 void CDirView::OnCopyBothToClipboard()
2945 std::list<String> list;
2946 CopyBothPathnames(SelBegin(), SelEnd(), std::back_inserter(list), GetDiffContext());
2947 PutFilesToClipboard(list, GetMainFrame()->GetSafeHwnd());
2951 * @brief Rename a selected item on both sides.
2954 void CDirView::OnItemRename()
2956 ASSERT(1 == m_pList->GetSelectedCount());
2957 int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
2958 ASSERT(-1 != nSelItem);
2959 m_pList->EditLabel(nSelItem);
2963 * @brief Enable/Disable dirview Rename context menu item.
2966 void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
2968 BOOL bEnabled = (1 == m_pList->GetSelectedCount());
2969 pCmdUI->Enable(bEnabled && SelBegin() != SelEnd());
2973 * @brief hide selected item filenames (removes them from the ListView)
2975 void CDirView::OnHideFilenames()
2977 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
2979 while ((it = SelRevBegin()) != SelRevEnd())
2982 SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
2983 DeleteItem(it.m_sel);
2986 m_pList->SetRedraw(TRUE); // Turn updating back on
2990 * @brief update menu item
2992 void CDirView::OnUpdateHideFilenames(CCmdUI* pCmdUI)
2994 pCmdUI->Enable(m_pList->GetSelectedCount() != 0);
2997 /// User chose (context menu) Move left to...
2998 template<SIDE_TYPE stype>
2999 void CDirView::OnCtxtDirMoveTo()
3001 DoDirActionTo(stype, &DirActions::MoveTo<stype>, _("Moving files..."));
3005 * @brief Update "Move | Left to..." item
3007 template<SIDE_TYPE stype>
3008 void CDirView::OnUpdateCtxtDirMoveTo(CCmdUI* pCmdUI)
3010 Counts counts = Count(&DirActions::IsItemMovableToOn<stype>);
3011 pCmdUI->Enable(counts.count > 0);
3012 pCmdUI->SetText(FormatMenuItemStringTo(stype, counts.count, counts.total).c_str());
3016 * @brief Update title after window is resized.
3018 void CDirView::OnSize(UINT nType, int cx, int cy)
3020 CListView::OnSize(nType, cx, cy);
3021 GetDocument()->SetTitle(NULL);
3025 * @brief Called when user selects 'Delete' from 'Merge' menu.
3027 void CDirView::OnDelete()
3029 DoDirAction(&DirActions::DeleteOnEitherOrBoth, _("Deleting files..."));
3033 * @brief Enables/disables 'Delete' item in 'Merge' menu.
3035 void CDirView::OnUpdateDelete(CCmdUI* pCmdUI)
3037 pCmdUI->Enable(Count(&DirActions::IsItemDeletableOnEitherOrBoth).count > 0);
3041 * @brief Called when item state is changed.
3043 * Show count of selected items in statusbar.
3045 void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
3047 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
3049 // If item's selected state changed
3050 if ((pNMListView->uOldState & LVIS_SELECTED) !=
3051 (pNMListView->uNewState & LVIS_SELECTED))
3053 int items = GetSelectedCount();
3054 String msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
3055 GetParentFrame()->SetStatus(msg.c_str());
3061 * @brief Called before user start to item label edit.
3063 * Disable label edit if initiated from a user double-click.
3065 afx_msg void CDirView::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
3067 *pResult = (SelBegin() == SelEnd());
3069 // If label edit is allowed.
3070 if (FALSE == *pResult)
3072 const NMLVDISPINFO *pdi = (NMLVDISPINFO*)pNMHDR;
3073 ASSERT(pdi != NULL);
3075 // Locate the edit box on the right column in case the user changed the
3077 const int nColPos = m_pColItems->ColLogToPhys(0);
3079 // Get text from the "File Name" column.
3080 CString sText = m_pList->GetItemText(pdi->item.iItem, nColPos);
3081 ASSERT(!sText.IsEmpty());
3083 // Keep only left file name (separated by '|'). This form occurs
3084 // when two files exists with same name but not in same case.
3085 int nPos = sText.Find('|');
3088 sText = sText.Left(nPos);
3091 // Set the edit control with the updated text.
3092 CEdit *pEdit = m_pList->GetEditControl();
3093 ASSERT(NULL != pEdit);
3094 pEdit->SetWindowText(sText);
3096 m_bUserCancelEdit = FALSE;
3101 * @brief Called when user done with item label edit.
3104 afx_msg void CDirView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
3108 // We can't use the normal condition of pszText==NULL to know if the
3109 // user cancels editing when file names had different case (e.g.
3110 // "file.txt|FILE.txt"). The edit text was changed to "file.txt" and
3111 // if the user accept it as the new file name, pszText is NULL.
3113 if (TRUE != m_bUserCancelEdit)
3115 CEdit *pEdit = m_pList->GetEditControl();
3116 ASSERT(NULL != pEdit);
3119 pEdit->GetWindowText(sText);
3121 if (!sText.IsEmpty())
3124 DirItemIterator dirBegin = SelBegin();
3125 *pResult = DoItemRename(dirBegin, GetDiffContext(), String(sText));
3126 } catch (ContentsChangedException& e) {
3127 AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
3134 * @brief Called when item is marked for rescan.
3135 * This function marks selected items for rescan and rescans them.
3137 void CDirView::OnMarkedRescan()
3139 std::for_each(SelBegin(), SelEnd(), MarkForRescan);
3140 if (std::distance(SelBegin(), SelEnd()) > 0)
3142 m_pSavedTreeState.reset(SaveTreeState(GetDiffContext()));
3143 GetDocument()->SetMarkedRescan();
3144 GetDocument()->Rescan();
3149 * @brief Called to update the item count in the status bar
3151 void CDirView::OnUpdateStatusNum(CCmdUI* pCmdUI)
3153 String s; // text to display
3155 int count = m_pList->GetItemCount();
3156 int focusItem = GetFocusedItem();
3158 if (focusItem == -1)
3160 // No item has focus
3162 s = strutils::format_string1(_("Items: %1"), strutils::to_str(count));
3166 // Don't show number to special items
3167 uintptr_t pos = GetItemKey(focusItem);
3168 if (pos != SPECIAL_ITEM_POS)
3170 // If compare is non-recursive reduce special items count
3171 bool bRecursive = GetDiffContext().m_bRecursive;
3178 s = strutils::format_string2(_("Item %1 of %2"),
3179 strutils::to_str(focusItem + 1), strutils::to_str(count));
3182 pCmdUI->SetText(s.c_str());
3186 * @brief Show all hidden items.
3188 void CDirView::OnViewShowHiddenItems()
3190 SetItemViewFlag(GetDiffContext(), ViewCustomFlags::VISIBLE, ViewCustomFlags::VISIBILITY);
3196 * @brief Enable/Disable 'Show hidden items' menuitem.
3198 void CDirView::OnUpdateViewShowHiddenItems(CCmdUI* pCmdUI)
3200 pCmdUI->Enable(m_nHiddenItems > 0);
3204 * @brief Toggle Tree Mode
3206 void CDirView::OnViewTreeMode()
3208 m_bTreeMode = !m_bTreeMode;
3209 m_dirfilter.tree_mode = m_bTreeMode;
3210 GetOptionsMgr()->SaveOption(OPT_TREE_MODE, m_bTreeMode); // reverse
3215 * @brief Check/Uncheck 'Tree Mode' menuitem.
3217 void CDirView::OnUpdateViewTreeMode(CCmdUI* pCmdUI)
3219 // Don't show Tree Mode as 'checked' if the
3220 // menu item is greyed out (disabled). Its very confusing.
3221 if( GetDocument()->GetDiffContext().m_bRecursive ) {
3222 pCmdUI->SetCheck(m_bTreeMode);
3223 pCmdUI->Enable(TRUE);
3225 pCmdUI->SetCheck(FALSE);
3226 pCmdUI->Enable(FALSE);
3231 * @brief Expand all subfolders
3233 void CDirView::OnViewExpandAllSubdirs()
3235 ExpandAllSubdirs(GetDiffContext());
3240 * @brief Update "Expand All Subfolders" item
3242 void CDirView::OnUpdateViewExpandAllSubdirs(CCmdUI* pCmdUI)
3244 pCmdUI->Enable(m_bTreeMode && GetDiffContext().m_bRecursive);
3248 * @brief Collapse all subfolders
3250 void CDirView::OnViewCollapseAllSubdirs()
3252 CollapseAllSubdirs(GetDiffContext());
3257 * @brief Update "Collapse All Subfolders" item
3259 void CDirView::OnUpdateViewCollapseAllSubdirs(CCmdUI* pCmdUI)
3261 pCmdUI->Enable(m_bTreeMode && GetDiffContext().m_bRecursive);
3264 void CDirView::OnViewSwapPanes()
3266 GetDocument()->Swap(0, GetDocument()->m_nDirs - 1);
3271 * @brief Show/Hide different files/directories
3273 void CDirView::OnOptionsShowDifferent()
3275 m_dirfilter.show_different = !m_dirfilter.show_different;
3276 GetOptionsMgr()->SaveOption(OPT_SHOW_DIFFERENT, m_dirfilter.show_different);
3281 * @brief Show/Hide identical files/directories
3283 void CDirView::OnOptionsShowIdentical()
3285 m_dirfilter.show_identical = !m_dirfilter.show_identical;
3286 GetOptionsMgr()->SaveOption(OPT_SHOW_IDENTICAL, m_dirfilter.show_identical);
3291 * @brief Show/Hide left-only files/directories
3293 void CDirView::OnOptionsShowUniqueLeft()
3295 m_dirfilter.show_unique_left = !m_dirfilter.show_unique_left;
3296 GetOptionsMgr()->SaveOption(OPT_SHOW_UNIQUE_LEFT, m_dirfilter.show_unique_left);
3301 * @brief Show/Hide middle-only files/directories
3303 void CDirView::OnOptionsShowUniqueMiddle()
3305 m_dirfilter.show_unique_middle = !m_dirfilter.show_unique_middle;
3306 GetOptionsMgr()->SaveOption(OPT_SHOW_UNIQUE_MIDDLE, m_dirfilter.show_unique_middle);
3311 * @brief Show/Hide right-only files/directories
3313 void CDirView::OnOptionsShowUniqueRight()
3315 m_dirfilter.show_unique_right = !m_dirfilter.show_unique_right;
3316 GetOptionsMgr()->SaveOption(OPT_SHOW_UNIQUE_RIGHT, m_dirfilter.show_unique_right);
3321 * @brief Show/Hide binary files
3323 void CDirView::OnOptionsShowBinaries()
3325 m_dirfilter.show_binaries = !m_dirfilter.show_binaries;
3326 GetOptionsMgr()->SaveOption(OPT_SHOW_BINARIES, m_dirfilter.show_binaries);
3331 * @brief Show/Hide skipped files/directories
3333 void CDirView::OnOptionsShowSkipped()
3335 m_dirfilter.show_skipped = !m_dirfilter.show_skipped;
3336 GetOptionsMgr()->SaveOption(OPT_SHOW_SKIPPED, m_dirfilter.show_skipped);
3341 * @brief Show/Hide different files/folders (Middle and right are identical)
3343 void CDirView::OnOptionsShowDifferentLeftOnly()
3345 m_dirfilter.show_different_left_only = !m_dirfilter.show_different_left_only;
3346 GetOptionsMgr()->SaveOption(OPT_SHOW_DIFFERENT_LEFT_ONLY, m_dirfilter.show_different_left_only);
3351 * @brief Show/Hide different files/folders (Left and right are identical)
3353 void CDirView::OnOptionsShowDifferentMiddleOnly()
3355 m_dirfilter.show_different_middle_only = !m_dirfilter.show_different_middle_only;
3356 GetOptionsMgr()->SaveOption(OPT_SHOW_DIFFERENT_MIDDLE_ONLY, m_dirfilter.show_different_middle_only);
3361 * @brief Show/Hide different files/folders (Left and middle are identical)
3363 void CDirView::OnOptionsShowDifferentRightOnly()
3365 m_dirfilter.show_different_right_only = !m_dirfilter.show_different_right_only;
3366 GetOptionsMgr()->SaveOption(OPT_SHOW_DIFFERENT_RIGHT_ONLY, m_dirfilter.show_different_right_only);
3370 void CDirView::OnUpdateOptionsShowdifferent(CCmdUI* pCmdUI)
3372 pCmdUI->SetCheck(m_dirfilter.show_different);
3375 void CDirView::OnUpdateOptionsShowidentical(CCmdUI* pCmdUI)
3377 pCmdUI->SetCheck(m_dirfilter.show_identical);
3380 void CDirView::OnUpdateOptionsShowuniqueleft(CCmdUI* pCmdUI)
3382 pCmdUI->SetCheck(m_dirfilter.show_unique_left);
3385 void CDirView::OnUpdateOptionsShowuniquemiddle(CCmdUI* pCmdUI)
3387 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
3388 pCmdUI->SetCheck(m_dirfilter.show_unique_middle);
3391 void CDirView::OnUpdateOptionsShowuniqueright(CCmdUI* pCmdUI)
3393 pCmdUI->SetCheck(m_dirfilter.show_unique_right);
3396 void CDirView::OnUpdateOptionsShowBinaries(CCmdUI* pCmdUI)
3398 pCmdUI->SetCheck(m_dirfilter.show_binaries);
3401 void CDirView::OnUpdateOptionsShowSkipped(CCmdUI* pCmdUI)
3403 pCmdUI->SetCheck(m_dirfilter.show_skipped);
3406 void CDirView::OnUpdateOptionsShowDifferentLeftOnly(CCmdUI* pCmdUI)
3408 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
3409 pCmdUI->SetCheck(m_dirfilter.show_different_left_only);
3412 void CDirView::OnUpdateOptionsShowDifferentMiddleOnly(CCmdUI* pCmdUI)
3414 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
3415 pCmdUI->SetCheck(m_dirfilter.show_different_middle_only);
3418 void CDirView::OnUpdateOptionsShowDifferentRightOnly(CCmdUI* pCmdUI)
3420 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
3421 pCmdUI->SetCheck(m_dirfilter.show_different_right_only);
3424 void CDirView::OnMergeCompare()
3426 CWaitCursor waitstatus;
3430 template<SELECTIONTYPE seltype>
3431 void CDirView::OnMergeCompare2()
3433 CWaitCursor waitstatus;
3434 OpenSelection(seltype);
3437 void CDirView::OnMergeCompareXML()
3439 CWaitCursor waitstatus;
3440 PackingInfo packingInfo(PLUGIN_BUILTIN_XML);
3441 OpenSelection(SELECTIONTYPE_NORMAL, &packingInfo);
3444 void CDirView::OnMergeCompareHex()
3446 CWaitCursor waitstatus;
3450 void CDirView::OnUpdateMergeCompare(CCmdUI *pCmdUI)
3452 DoUpdateOpen(SELECTIONTYPE_NORMAL, pCmdUI);
3455 template<SELECTIONTYPE seltype>
3456 void CDirView::OnUpdateMergeCompare2(CCmdUI *pCmdUI)
3458 DoUpdateOpen(seltype, pCmdUI);
3461 void CDirView::OnViewCompareStatistics()
3463 CompareStatisticsDlg dlg(GetDocument()->GetCompareStats());
3468 * @brief Count left & right files, and number with editable text encoding
3469 * @param nLeft [out] #files on left side selected
3470 * @param nLeftAffected [out] #files on left side selected which can have text encoding changed
3471 * @param nRight [out] #files on right side selected
3472 * @param nRightAffected [out] #files on right side selected which can have text encoding changed
3474 * Affected files include all except unicode files
3476 void CDirView::FormatEncodingDialogDisplays(CLoadSaveCodepageDlg * dlg)
3478 IntToIntMap currentCodepages = CountCodepages(SelBegin(), SelEnd(), GetDiffContext());
3480 Counts left, middle, right;
3481 left = Count(&DirActions::IsItemEditableEncoding<SIDE_LEFT>);
3482 if (GetDocument()->m_nDirs > 2)
3483 middle = Count(&DirActions::IsItemEditableEncoding<SIDE_MIDDLE>);
3484 right = Count(&DirActions::IsItemEditableEncoding<SIDE_RIGHT>);
3486 // Format strings such as "25 of 30 Files Affected"
3487 String sLeftAffected = FormatFilesAffectedString(left.count, left.total);
3488 String sMiddleAffected = (GetDocument()->m_nDirs < 3) ? _T("") : FormatFilesAffectedString(middle.count, middle.total);
3489 String sRightAffected = FormatFilesAffectedString(right.count, right.total);
3490 dlg->SetLeftRightAffectStrings(sLeftAffected, sMiddleAffected, sRightAffected);
3491 int codepage = currentCodepages.FindMaxKey();
3492 dlg->SetCodepages(codepage);
3496 * @brief Display file encoding dialog to user & handle user's choices
3498 * This handles DirView invocation, so multiple files may be affected
3500 void CDirView::DoFileEncodingDialog()
3502 CLoadSaveCodepageDlg dlg(GetDocument()->m_nDirs);
3503 // set up labels about what will be affected
3504 FormatEncodingDialogDisplays(&dlg);
3505 dlg.EnableSaveCodepage(false); // disallow setting a separate codepage for saving
3508 if (dlg.DoModal() != IDOK)
3512 affected[0] = dlg.DoesAffectLeft();
3513 affected[1] = dlg.DoesAffectMiddle();
3514 affected[SideToIndex(GetDiffContext(), SIDE_RIGHT)] = dlg.DoesAffectRight();
3516 ApplyCodepage(SelBegin(), SelEnd(), GetDiffContext(), affected, dlg.GetLoadCodepage());
3518 m_pList->InvalidateRect(NULL);
3519 m_pList->UpdateWindow();
3521 // TODO: We could loop through any active merge windows belonging to us
3522 // and see if any of their files are affected
3523 // but, if they've been edited, we cannot throw away the user's work?
3527 * @brief Display file encoding dialog & handle user's actions
3529 void CDirView::OnFileEncoding()
3531 DoFileEncodingDialog();
3535 * @brief Update "File Encoding" item
3537 void CDirView::OnUpdateFileEncoding(CCmdUI* pCmdUI)
3539 pCmdUI->Enable(TRUE);
3542 /** @brief Open help from mainframe when user presses F1*/
3543 void CDirView::OnHelp()
3545 theApp.ShowHelp(DirViewHelpLocation);
3549 * @brief true while user is editing a file name.
3551 bool CDirView::IsLabelEdit() const
3553 return (NULL != m_pList->GetEditControl());
3557 * @brief Allow edit "Paste" when renaming an item.
3559 void CDirView::OnEditCopy()
3561 CEdit *pEdit = m_pList->GetEditControl();
3569 * @brief Allow edit "Cut" when renaming an item.
3571 void CDirView::OnEditCut()
3573 CEdit *pEdit = m_pList->GetEditControl();
3581 * @brief Allow edit "Paste" when renaming an item.
3583 void CDirView::OnEditPaste()
3585 CEdit *pEdit = m_pList->GetEditControl();
3593 * @brief Allow edit "Undo" when renaming an item.
3595 void CDirView::OnEditUndo()
3597 CEdit *pEdit = m_pList->GetEditControl();
3605 * @brief Update the tool bar's "Undo" icon. It should be enabled when
3606 * renaming an item and undo is possible.
3608 void CDirView::OnUpdateEditUndo(CCmdUI* pCmdUI)
3610 CEdit *pEdit = m_pList->GetEditControl();
3611 pCmdUI->Enable(pEdit && pEdit->CanUndo());
3615 * @brief Returns CShellContextMenu object that owns given HMENU.
3617 * @param [in] hMenu Handle to the menu to check ownership of.
3618 * @return Either m_pShellContextMenuLeft, m_pShellContextMenuRight
3619 * or NULL if hMenu is not owned by these two.
3621 CShellContextMenu* CDirView::GetCorrespondingShellContextMenu(HMENU hMenu) const
3623 CShellContextMenu* pMenu = NULL;
3624 if (m_pShellContextMenuLeft && hMenu == m_pShellContextMenuLeft->GetHMENU())
3625 pMenu = m_pShellContextMenuLeft.get();
3626 else if (m_pShellContextMenuMiddle && hMenu == m_pShellContextMenuMiddle->GetHMENU())
3627 pMenu = m_pShellContextMenuMiddle.get();
3628 else if (m_pShellContextMenuRight && hMenu == m_pShellContextMenuRight->GetHMENU())
3629 pMenu = m_pShellContextMenuRight.get();
3635 * @brief Handle messages related to correct menu working.
3637 * We need to requery shell context menu each time we switch from context menu
3638 * for one side to context menu for other side. Here we check whether we need to
3639 * requery and call ShellContextMenuHandleMenuMessage.
3641 LRESULT CDirView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
3643 while (message == WM_INITMENUPOPUP)
3645 HMENU hMenu = (HMENU)wParam;
3646 if (CShellContextMenu* pMenu = GetCorrespondingShellContextMenu(hMenu))
3648 if (m_hCurrentMenu != hMenu)
3650 // re-query context menu once more, because if context menu was queried for right
3651 // group of files and we are showing menu for left group (or vice versa) menu will
3652 // be shown incorrectly
3653 // also, if context menu was last queried for right group of files and we are
3654 // invoking command for left command will be executed for right group (the last
3655 // group that menu was requested for)
3656 // may be a "feature" of Shell
3658 pMenu->RequeryShellContextMenu();
3659 m_hCurrentMenu = hMenu;
3665 CShellContextMenu* pMenu = GetCorrespondingShellContextMenu(m_hCurrentMenu);
3670 pMenu->HandleMenuMessage(message, wParam, lParam, res);
3673 return CListView::WindowProc(message, wParam, lParam);
3677 * @brief Implement background item coloring
3679 void CDirView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
3681 LPNMLISTVIEW pNM = (LPNMLISTVIEW)pNMHDR;
3682 *pResult = CDRF_DODEFAULT;
3684 if (pNM->hdr.code == NM_CUSTOMDRAW)
3686 LPNMLVCUSTOMDRAW lpC = (LPNMLVCUSTOMDRAW)pNMHDR;
3688 if (lpC->nmcd.dwDrawStage == CDDS_PREPAINT)
3690 *pResult = CDRF_NOTIFYITEMDRAW;
3694 if (lpC->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
3696 *pResult = CDRF_NOTIFYITEMDRAW;
3700 if (lpC->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM ))
3702 GetColors (static_cast<int>(lpC->nmcd.dwItemSpec), lpC->iSubItem, lpC->clrTextBk, lpC->clrText);
3707 void CDirView::OnBnClickedComparisonStop()
3709 if (m_pCmpProgressBar)
3710 m_pCmpProgressBar->EndUpdating();
3711 GetDocument()->AbortCurrentScan();
3714 void CDirView::OnBnClickedComparisonPause()
3716 if (m_pCmpProgressBar)
3717 m_pCmpProgressBar->SetPaused(true);
3718 GetDocument()->PauseCurrentScan();
3721 void CDirView::OnBnClickedComparisonContinue()
3723 if (m_pCmpProgressBar)
3724 m_pCmpProgressBar->SetPaused(false);
3725 GetDocument()->ContinueCurrentScan();
3729 * @brief Populate colors for items in view, depending on difference status
3731 void CDirView::GetColors (int nRow, int nCol, COLORREF& clrBk, COLORREF& clrText) const
3733 const DIFFITEM& di = GetDiffItem (nRow);
3737 clrText = ::GetSysColor (COLOR_WINDOWTEXT);
3738 clrBk = ::GetSysColor (COLOR_WINDOW);
3740 else if (di.diffcode.isResultFiltered())
3742 clrText = m_cachedColors.clrTrivialText;
3743 clrBk = m_cachedColors.clrTrivial;
3745 else if (!IsItemExistAll(GetDiffContext(), di))
3747 clrText = m_cachedColors.clrDiffText;
3748 clrBk = m_cachedColors.clrDiffDeleted;
3750 else if (di.diffcode.isResultDiff())
3752 clrText = m_cachedColors.clrDiffText;
3753 clrBk = m_cachedColors.clrDiff;
3757 clrText = ::GetSysColor (COLOR_WINDOWTEXT);
3758 clrBk = ::GetSysColor (COLOR_WINDOW);
3762 void CDirView::OnSearch()
3764 CDirDoc *pDoc = GetDocument();
3765 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
3766 int nRows = m_pList->GetItemCount();
3767 for (int currRow = nRows - 1; currRow >= 0; currRow--)
3769 uintptr_t pos = GetItemKey(currRow);
3770 if (pos == SPECIAL_ITEM_POS)
3773 bool bFound = false;
3774 DIFFITEM & di = GetDiffItem(currRow);
3776 for (int i = 0; i < pDoc->m_nDirs; i++)
3778 if (di.diffcode.exists(i) && !di.diffcode.isDirectory())
3780 GetItemFileNames(currRow, &paths);
3782 if (!ufile.OpenReadOnly(paths[i]))
3785 ufile.SetUnicoding(di.diffFileInfo[i].encoding.m_unicoding);
3786 ufile.SetBom(di.diffFileInfo[i].encoding.m_bom);
3787 ufile.SetCodepage(di.diffFileInfo[i].encoding.m_codepage);
3795 if (!ufile.ReadString(line, &lossy))
3798 if (_tcsstr(line.c_str(), _T("DirView")))
3812 SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
3813 DeleteItem(currRow);
3817 m_pList->SetRedraw(TRUE); // Turn updating back on
3821 * @brief Drag files/directories from folder compare listing view.
3823 void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
3825 COleDataSource *DropData = new COleDataSource();
3827 std::list<String> list;
3828 CopyPathnamesForDragAndDrop(SelBegin(), SelEnd(), std::back_inserter(list), GetDiffContext());
3829 String filesForDroping = strutils::join(list.begin(), list.end(), _T("\n")) + _T("\n");
3831 CSharedFile file(GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT);
3832 file.Write(filesForDroping.data(), static_cast<unsigned>((filesForDroping.length() + 1) * sizeof(TCHAR)));
3834 HGLOBAL hMem = GlobalReAlloc(file.Detach(), (filesForDroping.length() + 1) * sizeof(TCHAR), 0);
3838 DropData->CacheGlobalData(CF_UNICODETEXT, hMem);
3840 DropData->CacheGlobalData(CF_TEXT, hMem);
3842 DROPEFFECT de = DropData->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE, NULL);
3848 /// Assign column name, using string resource & current column ordering
3849 void CDirView::NameColumn(const char *idname, int subitem)
3851 int phys = m_pColItems->ColLogToPhys(subitem);
3854 String s = tr(idname);
3856 lvc.mask = LVCF_TEXT;
3857 lvc.pszText = const_cast<LPTSTR>(s.c_str());
3858 m_pList->SetColumn(phys, &lvc);
3862 /// Load column names from string table
3863 void CDirView::UpdateColumnNames()
3865 int ncols = m_pColItems->GetColCount();
3866 for (int i=0; i<ncols; ++i)
3868 const DirColInfo * col = m_pColItems->GetDirColInfo(i);
3869 NameColumn(col->idName, i);
3874 * @brief Set alignment of columns.
3876 void CDirView::SetColAlignments()
3878 int ncols = m_pColItems->GetColCount();
3879 for (int i=0; i<ncols; ++i)
3881 const DirColInfo * col = m_pColItems->GetDirColInfo(i);
3883 lvc.mask = LVCF_FMT;
3884 lvc.fmt = col->alignment;
3885 m_pList->SetColumn(m_pColItems->ColLogToPhys(i), &lvc);
3889 CDirView::CompareState::CompareState(const CDiffContext *pCtxt, const DirViewColItems *pColItems, int sortCol, bool bSortAscending, bool bTreeMode)
3891 , pColItems(pColItems)
3893 , bSortAscending(bSortAscending)
3894 , bTreeMode(bTreeMode)
3898 /// Compare two specified rows during a sort operation (windows callback)
3899 int CALLBACK CDirView::CompareState::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3901 CompareState *pThis = reinterpret_cast<CompareState*>(lParamSort);
3902 // Sort special items always first in dir view
3908 uintptr_t diffposl = (uintptr_t)lParam1;
3909 uintptr_t diffposr = (uintptr_t)lParam2;
3910 const DIFFITEM &ldi = pThis->pCtxt->GetDiffAt(diffposl);
3911 const DIFFITEM &rdi = pThis->pCtxt->GetDiffAt(diffposr);
3912 // compare 'left' and 'right' parameters as appropriate
3913 int retVal = pThis->pColItems->ColSort(pThis->pCtxt, pThis->sortCol, ldi, rdi, pThis->bTreeMode);
3914 // return compare result, considering sort direction
3915 return pThis->bSortAscending ? retVal : -retVal;
3918 /// Add new item to list view
3919 int CDirView::AddNewItem(int i, uintptr_t diffpos, int iImage, int iIndent)
3922 lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE | LVIF_INDENT;
3924 lvItem.iIndent = iIndent;
3925 lvItem.iSubItem = 0;
3926 lvItem.pszText = LPSTR_TEXTCALLBACK;
3927 lvItem.lParam = (LPARAM)diffpos;
3928 lvItem.iImage = iImage;
3929 return GetListCtrl().InsertItem(&lvItem);
3933 * @brief Update listview display of details for specified row
3934 * @note Customising shownd data should be done here
3936 void CDirView::UpdateDiffItemStatus(UINT nIdx)
3938 GetListCtrl().RedrawItems(nIdx, nIdx);
3941 static String rgDispinfoText[2]; // used in function below
3944 * @brief Allocate a text buffer to assign to NMLVDISPINFO::item::pszText
3945 * Quoting from SDK Docs:
3946 * If the LVITEM structure is receiving item text, the pszText and cchTextMax
3947 * members specify the address and size of a buffer. You can either copy text to
3948 * the buffer or assign the address of a string to the pszText member. In the
3949 * latter case, you must not change or delete the string until the corresponding
3950 * item text is deleted or two additional LVN_GETDISPINFO messages have been sent.
3952 static LPTSTR NTAPI AllocDispinfoText(const String &s)
3955 LPCTSTR pszText = (rgDispinfoText[i] = s).c_str();
3957 return (LPTSTR)pszText;
3961 * @brief Respond to LVN_GETDISPINFO message
3963 void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
3965 int nIdx = pParam->item.iItem;
3966 int i = m_pColItems->ColPhysToLog(pParam->item.iSubItem);
3967 uintptr_t key = GetItemKey(nIdx);
3968 if (key == SPECIAL_ITEM_POS)
3970 if (m_pColItems->IsColName(i))
3972 pParam->item.pszText = _T("..");
3976 if (!GetDocument()->HasDiffs())
3978 const CDiffContext &ctxt = GetDiffContext();
3979 const DIFFITEM &di = ctxt.GetDiffAt(key);
3980 if (pParam->item.mask & LVIF_TEXT)
3982 String s = m_pColItems->ColGetTextToDisplay(&ctxt, i, di);
3983 pParam->item.pszText = AllocDispinfoText(s);
3985 if (pParam->item.mask & LVIF_IMAGE)
3987 pParam->item.iImage = GetColImage(di);
3990 m_bNeedSearchLastDiffItem = true;
3991 m_bNeedSearchFirstDiffItem = true;
3995 * @brief User examines & edits which columns are displayed in dirview, and in which order
3997 void CDirView::OnEditColumns()
4000 // List all the currently displayed columns
4001 for (int col=0; col<GetListCtrl().GetHeaderCtrl()->GetItemCount(); ++col)
4003 int l = m_pColItems->ColPhysToLog(col);
4004 dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l, col);
4006 // Now add all the columns not currently displayed
4008 for (l=0; l<m_pColItems->GetColCount(); ++l)
4010 if (m_pColItems->ColLogToPhys(l)==-1)
4012 dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l);
4016 // Add default order of columns for resetting to defaults
4017 for (l = 0; l < m_pColItems->GetColCount(); ++l)
4019 int phy = m_pColItems->GetColDefaultOrder(l);
4020 dlg.AddDefColumn(m_pColItems->GetColDisplayName(l), l, phy);
4023 if (dlg.DoModal() != IDOK)
4026 String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
4027 theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
4028 (dlg.m_bReset ? m_pColItems->ResetColumnWidths(DefColumnWidth) :
4029 m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1))).c_str());
4031 // Reset our data to reflect the new data from the dialog
4032 const CDirColsDlg::ColumnArray & cols = dlg.GetColumns();
4033 m_pColItems->ClearColumnOrders();
4034 const int sortColumn = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
4035 std::vector<int> colorder(m_pColItems->GetColCount(), -1);
4036 for (CDirColsDlg::ColumnArray::const_iterator iter = cols.begin();
4037 iter != cols.end(); ++iter)
4039 int log = iter->log_col;
4040 int phy = iter->phy_col;
4041 colorder[log] = phy;
4043 // If sorted column was hidden, reset sorting
4044 if (log == sortColumn && phy < 0)
4046 GetOptionsMgr()->Reset((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
4047 GetOptionsMgr()->Reset(OPT_DIRVIEW_SORT_ASCENDING);
4051 m_pColItems->SetColumnOrdering(&colorder[0]);
4053 if (m_pColItems->GetDispColCount() < 1)
4055 // Ignore them if they didn't leave a column showing
4056 m_pColItems->ResetColumnOrdering();
4065 DirActions CDirView::MakeDirActions(DirActions::method_type func) const
4067 const CDirDoc *pDoc = GetDocument();
4068 return DirActions(pDoc->GetDiffContext(), pDoc->GetReadOnly(), func);
4071 DirActions CDirView::MakeDirActions(DirActions::method_type2 func) const
4073 const CDirDoc *pDoc = GetDocument();
4074 return DirActions(pDoc->GetDiffContext(), pDoc->GetReadOnly(), NULL, func);
4077 const CDiffContext& CDirView::GetDiffContext() const
4079 return GetDocument()->GetDiffContext();
4082 CDiffContext& CDirView::GetDiffContext()
4084 return GetDocument()->GetDiffContext();