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 "DirFrame.h" // StatePane
34 #include "IMergeDoc.h"
35 #include "FileLocation.h"
38 #include "coretools.h"
40 #include "FileTransform.h"
41 #include "SelectUnpackerDlg.h"
44 #include "OptionsDef.h"
45 #include "OptionsMgr.h"
47 #include "DirCmpReport.h"
48 #include "DirCompProgressBar.h"
49 #include "CompareStatisticsDlg.h"
51 #include "ShellContextMenu.h"
53 #include "IListCtrlImpl.h"
54 #include "Merge7zFormatMergePluginImpl.h"
59 static char THIS_FILE[] = __FILE__;
65 * @brief Location for folder compare specific help to open.
67 static TCHAR DirViewHelpLocation[] = _T("::/htmlhelp/Compare_dirs.html");
70 * @brief Limit (in seconds) to signal compare is ready for user.
71 * If compare takes longer than this value (in seconds) we inform
72 * user about it. Current implementation uses MessageBeep(IDOK).
74 const int TimeToSignalCompare = 3;
77 * @brief Folder compare icon indexes.
78 * This enum defines indexes for imagelist used for folder compare icons.
79 * Note that this enum must be in synch with code in OnInitialUpdate() and
80 * GetColImage(). Also remember that icons are in resource file...
107 DIFFIMG_DIRUP_DISABLE,
113 // The resource ID constants/limits for the Shell context menu
114 const UINT LeftCmdFirst = 0x9000; // this should be greater than any of already defined command IDs
115 const UINT RightCmdLast = 0xffff; // maximum available value
116 const UINT LeftCmdLast = LeftCmdFirst + (RightCmdLast - LeftCmdFirst) / 3; // divide available range equally between two context menus
117 const UINT MiddleCmdFirst = LeftCmdLast + 1;
118 const UINT MiddleCmdLast = MiddleCmdFirst + (RightCmdLast - LeftCmdFirst) / 3;
119 const UINT RightCmdFirst = MiddleCmdLast + 1;
121 /////////////////////////////////////////////////////////////////////////////
126 STATUSBAR_UPDATE = 100
129 IMPLEMENT_DYNCREATE(CDirView, CListView)
136 , m_bNeedSearchFirstDiffItem(true)
137 , m_bNeedSearchLastDiffItem(true)
138 , m_firstDiffItem(-1)
140 , m_pCmpProgressBar(nullptr)
143 , m_pShellContextMenuLeft(nullptr)
144 , m_pShellContextMenuMiddle(nullptr)
145 , m_pShellContextMenuRight(nullptr)
146 , m_hCurrentMenu(nullptr)
147 , m_pSavedTreeState(nullptr)
149 m_dwDefaultStyle &= ~LVS_TYPEMASK;
150 // Show selection all the time, so user can see current item even when
151 // focus is elsewhere (ie, on file edit window)
152 m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS;
154 m_bTreeMode = GetOptionsMgr()->GetBool(OPT_TREE_MODE);
155 m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
156 m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
157 Options::DiffColors::Load(m_cachedColors);
160 CDirView::~CDirView()
164 BEGIN_MESSAGE_MAP(CDirView, CListView)
166 //{{AFX_MSG_MAP(CDirView)
167 ON_WM_LBUTTONDBLCLK()
168 ON_COMMAND(ID_R2L, OnDirCopyRightToLeft)
169 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateDirCopyRightToLeft)
170 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_LEFT, OnCtxtDirCopyRightToLeft)
171 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_LEFT, OnUpdateCtxtDirCopyRightToLeft)
172 ON_COMMAND(ID_L2R, OnDirCopyLeftToRight)
173 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateDirCopyLeftToRight)
174 ON_COMMAND(ID_DIR_COPY_LEFT_TO_RIGHT, OnCtxtDirCopyLeftToRight)
175 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_RIGHT, OnUpdateCtxtDirCopyLeftToRight)
176 ON_COMMAND(ID_DIR_DEL_LEFT, OnCtxtDirDelLeft)
177 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_LEFT, OnUpdateCtxtDirDelLeft)
178 ON_COMMAND(ID_DIR_DEL_RIGHT, OnCtxtDirDelRight)
179 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_RIGHT, OnUpdateCtxtDirDelRight)
180 ON_COMMAND(ID_DIR_DEL_BOTH, OnCtxtDirDelBoth)
181 ON_UPDATE_COMMAND_UI(ID_DIR_DEL_BOTH, OnUpdateCtxtDirDelBoth)
182 ON_COMMAND(ID_DIR_OPEN_LEFT, OnCtxtDirOpenLeft)
183 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT, OnUpdateCtxtDirOpenLeft)
184 ON_COMMAND(ID_DIR_OPEN_LEFT_WITH, OnCtxtDirOpenLeftWith)
185 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITH, OnUpdateCtxtDirOpenLeftWith)
186 ON_COMMAND(ID_DIR_OPEN_RIGHT, OnCtxtDirOpenRight)
187 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT, OnUpdateCtxtDirOpenRight)
188 ON_COMMAND(ID_DIR_OPEN_RIGHT_WITH, OnCtxtDirOpenRightWith)
189 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITH, OnUpdateCtxtDirOpenRightWith)
190 ON_COMMAND(ID_POPUP_OPEN_WITH_UNPACKER, OnCtxtOpenWithUnpacker)
191 ON_UPDATE_COMMAND_UI(ID_POPUP_OPEN_WITH_UNPACKER, OnUpdateCtxtOpenWithUnpacker)
192 ON_COMMAND(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnCtxtDirOpenRightWithEditor)
193 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnUpdateCtxtDirOpenRightWithEditor)
194 ON_COMMAND(ID_DIR_OPEN_LEFT_WITHEDITOR, OnCtxtDirOpenLeftWithEditor)
195 ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITHEDITOR, OnUpdateCtxtDirOpenLeftWithEditor)
196 ON_COMMAND(ID_DIR_COPY_LEFT_TO_BROWSE, OnCtxtDirCopyLeftTo)
197 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_BROWSE, OnCtxtDirCopyRightTo)
198 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_BROWSE, OnUpdateCtxtDirCopyLeftTo)
199 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_BROWSE, OnUpdateCtxtDirCopyRightTo)
203 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
204 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
205 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
206 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
207 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
208 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
209 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
210 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
211 ON_COMMAND(ID_CURDIFF, OnCurdiff)
212 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
213 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateSave)
214 ON_MESSAGE(MSG_UI_UPDATE, OnUpdateUIMessage)
215 ON_COMMAND(ID_REFRESH, OnRefresh)
216 ON_UPDATE_COMMAND_UI(ID_REFRESH, OnUpdateRefresh)
218 ON_UPDATE_COMMAND_UI(ID_STATUS_RIGHTDIR_RO, OnUpdateStatusRightRO)
219 ON_UPDATE_COMMAND_UI(ID_STATUS_MIDDLEDIR_RO, OnUpdateStatusMiddleRO)
220 ON_UPDATE_COMMAND_UI(ID_STATUS_LEFTDIR_RO, OnUpdateStatusLeftRO)
221 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
222 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
223 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
224 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
225 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
226 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
227 ON_COMMAND(ID_TOOLS_CUSTOMIZECOLUMNS, OnCustomizeColumns)
228 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
229 ON_COMMAND(ID_DIR_ZIP_LEFT, OnCtxtDirZipLeft)
230 ON_COMMAND(ID_DIR_ZIP_RIGHT, OnCtxtDirZipRight)
231 ON_COMMAND(ID_DIR_ZIP_BOTH, OnCtxtDirZipBoth)
232 ON_COMMAND(ID_DIR_ZIP_BOTH_DIFFS_ONLY, OnCtxtDirZipBothDiffsOnly)
233 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_LEFT, OnCtxtDirShellContextMenu<SIDE_LEFT>)
234 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, OnCtxtDirShellContextMenu<SIDE_MIDDLE>)
235 ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_RIGHT, OnCtxtDirShellContextMenu<SIDE_RIGHT>)
236 ON_COMMAND(ID_EDIT_SELECT_ALL, OnSelectAll)
237 ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateSelectAll)
238 ON_COMMAND_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnPluginPredifferMode)
239 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnUpdatePluginPredifferMode)
240 ON_COMMAND(ID_DIR_COPY_PATHNAMES_LEFT, OnCopyLeftPathnames)
241 ON_COMMAND(ID_DIR_COPY_PATHNAMES_RIGHT, OnCopyRightPathnames)
242 ON_COMMAND(ID_DIR_COPY_PATHNAMES_BOTH, OnCopyBothPathnames)
243 ON_COMMAND(ID_DIR_COPY_FILENAMES, OnCopyFilenames)
244 ON_UPDATE_COMMAND_UI(ID_DIR_COPY_FILENAMES, OnUpdateCopyFilenames)
245 ON_COMMAND(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnCopyLeftToClipboard)
246 ON_COMMAND(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnCopyRightToClipboard)
247 ON_COMMAND(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnCopyBothToClipboard)
248 ON_COMMAND(ID_DIR_ITEM_RENAME, OnItemRename)
249 ON_UPDATE_COMMAND_UI(ID_DIR_ITEM_RENAME, OnUpdateItemRename)
250 ON_COMMAND(ID_DIR_HIDE_FILENAMES, OnHideFilenames)
251 ON_COMMAND(ID_DIR_MOVE_LEFT_TO_BROWSE, OnCtxtDirMoveLeftTo)
252 ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_LEFT_TO_BROWSE, OnUpdateCtxtDirMoveLeftTo)
253 ON_COMMAND(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnCtxtDirMoveRightTo)
254 ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnUpdateCtxtDirMoveRightTo)
255 ON_UPDATE_COMMAND_UI(ID_DIR_HIDE_FILENAMES, OnUpdateHideFilenames)
257 ON_COMMAND(ID_MERGE_DELETE, OnDelete)
258 ON_UPDATE_COMMAND_UI(ID_MERGE_DELETE, OnUpdateDelete)
259 ON_COMMAND(ID_RESCAN, OnMarkedRescan)
260 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
261 ON_COMMAND(ID_VIEW_SHOWHIDDENITEMS, OnViewShowHiddenItems)
262 ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWHIDDENITEMS, OnUpdateViewShowHiddenItems)
263 ON_COMMAND(ID_MERGE_COMPARE, OnMergeCompare)
264 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE, OnUpdateMergeCompare)
265 ON_COMMAND(ID_MERGE_COMPARE_LEFT1_LEFT2, OnMergeCompareLeft1Left2)
266 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_LEFT2, OnUpdateMergeCompareLeft1Left2)
267 ON_COMMAND(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnMergeCompareRight1Right2)
268 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnUpdateMergeCompareRight1Right2)
269 ON_COMMAND(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnMergeCompareLeft1Right2)
270 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnUpdateMergeCompareLeft1Right2)
271 ON_COMMAND(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnMergeCompareLeft2Right1)
272 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnUpdateMergeCompareLeft2Right1)
273 ON_COMMAND(ID_MERGE_COMPARE_XML, OnMergeCompareXML)
274 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_XML, OnUpdateMergeCompare)
275 ON_COMMAND(ID_MERGE_COMPARE_HEX, OnMergeCompareHex)
276 ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_HEX, OnUpdateMergeCompare)
277 ON_COMMAND(ID_VIEW_TREEMODE, OnViewTreeMode)
278 ON_UPDATE_COMMAND_UI(ID_VIEW_TREEMODE, OnUpdateViewTreeMode)
279 ON_COMMAND(ID_VIEW_EXPAND_ALLSUBDIRS, OnViewExpandAllSubdirs)
280 ON_UPDATE_COMMAND_UI(ID_VIEW_EXPAND_ALLSUBDIRS, OnUpdateViewExpandAllSubdirs)
281 ON_COMMAND(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnViewCollapseAllSubdirs)
282 ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnUpdateViewCollapseAllSubdirs)
283 ON_COMMAND(ID_VIEW_DIR_STATISTICS, OnViewCompareStatistics)
284 ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
285 ON_UPDATE_COMMAND_UI(ID_FILE_ENCODING, OnUpdateFileEncoding)
286 ON_COMMAND(ID_HELP, OnHelp)
287 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
288 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
289 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
290 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
291 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
292 ON_COMMAND(ID_VIEW_EXPAND, OnExpandFolder)
293 ON_COMMAND(ID_VIEW_COLLAPSE, OnCollapseFolder)
295 ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
296 ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged)
297 ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
298 ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)
299 ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
300 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
301 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
302 ON_BN_CLICKED(IDC_COMPARISON_STOP, OnBnClickedComparisonStop)
305 /////////////////////////////////////////////////////////////////////////////
306 // CDirView diagnostics
310 CDirDoc* CDirView::GetDocument() // non-debug version is inline
312 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirDoc)));
313 return (CDirDoc*)m_pDocument;
317 /////////////////////////////////////////////////////////////////////////////
318 // CDirView message handlers
320 void CDirView::OnInitialUpdate()
322 const int iconCX = 16;
323 const int iconCY = 16;
324 CListView::OnInitialUpdate();
325 m_pList = &GetListCtrl();
326 GetDocument()->SetDirView(this);
329 m_pList->SendMessage(CCM_SETUNICODEFORMAT, TRUE, 0);
331 m_pList->SendMessage(CCM_SETUNICODEFORMAT, FALSE, 0);
334 // Load user-selected font
335 if (GetOptionsMgr()->GetBool(String(OPT_FONT_DIRCMP) + OPT_FONT_USECUSTOM))
337 m_font.CreateFontIndirect(&GetMainFrame()->m_lfDir);
338 CWnd::SetFont(&m_font, TRUE);
341 // Replace standard header with sort header
342 if (HWND hWnd = ListView_GetHeader(m_pList->m_hWnd))
343 m_ctlSortHeader.SubclassWindow(hWnd);
345 // Load the icons used for the list view (to reflect diff status)
346 // NOTE: these must be in the exactly the same order than in enum
347 // definition in begin of this file!
348 VERIFY(m_imageList.Create(iconCX, iconCY, ILC_COLOR32 | ILC_MASK, 15, 1));
349 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LFILE)));
350 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_MFILE)));
351 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_RFILE)));
352 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_MRFILE)));
353 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LRFILE)));
354 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LMFILE)));
355 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_NOTEQUALFILE)));
356 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_EQUALFILE)));
357 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_EQUALBINARY)));
358 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_BINARYDIFF)));
359 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LFOLDER)));
360 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_MFOLDER)));
361 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_RFOLDER)));
362 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_MRFOLDER)));
363 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LRFOLDER)));
364 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_LMFOLDER)));
365 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_FILESKIP)));
366 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_FOLDERSKIP)));
367 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_NOTEQUALFOLDER)));
368 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_EQUALFOLDER)));
369 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_FOLDER)));
370 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_COMPARE_ERROR)));
371 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_FOLDERUP)));
372 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_FOLDERUP_DISABLE)));
373 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_COMPARE_ABORTED)));
374 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_NOTEQUALTEXTFILE)));
375 VERIFY(-1 != m_imageList.Add(AfxGetApp()->LoadIcon(IDI_EQUALTEXTFILE)));
376 m_pList->SetImageList(&m_imageList, LVSIL_SMALL);
378 // Load the icons used for the list view (expanded/collapsed state icons)
379 VERIFY(m_imageState.Create(IDB_TREE_STATE, 16, 1, RGB(255, 0, 255)));
381 // Restore column orders as they had them last time they ran
384 // Display column headers (in appropriate order)
387 // Show selection across entire row.
388 // Also allow user to rearrange columns via drag&drop of headers.
389 // Also enable infotips.
390 DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP;
391 m_pList->SetExtendedStyle(exstyle);
395 * @brief Return image index appropriate for this row
397 int CDirView::GetColImage(const DIFFITEM & di) const
399 // Must return an image index into image list created above in OnInitDialog
400 if (di.diffcode.isResultError())
401 return DIFFIMG_ERROR;
402 if (di.diffcode.isResultAbort())
403 return DIFFIMG_ABORT;
404 if (di.diffcode.isResultFiltered())
405 return (di.diffcode.isDirectory() ? DIFFIMG_DIRSKIP : DIFFIMG_SKIP);
406 if (di.diffcode.isSideFirstOnly())
407 return (di.diffcode.isDirectory() ? DIFFIMG_LDIRUNIQUE : DIFFIMG_LUNIQUE);
408 if (di.diffcode.isSideSecondOnly())
409 return (GetDocument()->m_nDirs < 3 ?
410 (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE) :
411 (di.diffcode.isDirectory() ? DIFFIMG_MDIRUNIQUE : DIFFIMG_MUNIQUE));
412 if (di.diffcode.isSideThirdOnly())
413 return (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE);
414 if (GetDocument()->m_nDirs == 3)
416 if (!di.diffcode.exists(0))
417 return (di.diffcode.isDirectory() ? DIFFIMG_LDIRMISSING : DIFFIMG_LMISSING);
418 if (!di.diffcode.exists(1))
419 return (di.diffcode.isDirectory() ? DIFFIMG_MDIRMISSING : DIFFIMG_MMISSING);
420 if (!di.diffcode.exists(2))
421 return (di.diffcode.isDirectory() ? DIFFIMG_RDIRMISSING : DIFFIMG_RMISSING);
423 if (di.diffcode.isResultSame())
425 if (di.diffcode.isDirectory())
426 return DIFFIMG_DIRSAME;
429 if (di.diffcode.isText())
430 return DIFFIMG_TEXTSAME;
431 else if (di.diffcode.isBin())
432 return DIFFIMG_BINSAME;
438 if (di.diffcode.isResultDiff())
440 if (di.diffcode.isDirectory())
441 return DIFFIMG_DIRDIFF;
444 if (di.diffcode.isText())
445 return DIFFIMG_TEXTDIFF;
446 else if (di.diffcode.isBin())
447 return DIFFIMG_BINDIFF;
452 return (di.diffcode.isDirectory() ? DIFFIMG_DIR : DIFFIMG_ABORT);
456 * @brief Get default folder compare status image.
458 int CDirView::GetDefaultColImage() const
460 return DIFFIMG_ERROR;
464 * @brief Called before compare is started.
465 * CDirDoc calls this function before new compare is started, so this
466 * is good place to setup GUI for compare.
467 * @param [in] pCompareStats Pointer to class having current compare stats.
469 void CDirView::StartCompare(CompareStats *pCompareStats)
471 if (m_pCmpProgressBar == NULL)
472 m_pCmpProgressBar.reset(new DirCompProgressBar());
474 if (!::IsWindow(m_pCmpProgressBar->GetSafeHwnd()))
475 m_pCmpProgressBar->Create(GetParentFrame());
477 m_pCmpProgressBar->SetCompareStat(pCompareStats);
478 m_pCmpProgressBar->StartUpdating();
480 GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), TRUE, FALSE);
482 m_compareStart = clock();
486 * @brief Called when folder compare row is double-clicked with mouse.
487 * Selected item is opened to folder or file compare.
489 void CDirView::OnLButtonDblClk(UINT nFlags, CPoint point)
493 m_pList->SubItemHitTest(&lvhti);
494 if (lvhti.iItem >= 0)
496 const DIFFITEM& di = GetDiffItem(lvhti.iItem);
497 if (m_bTreeMode && GetDocument()->GetRecursive() && di.diffcode.isDirectory())
499 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
500 CollapseSubdir(lvhti.iItem);
502 ExpandSubdir(lvhti.iItem);
506 CWaitCursor waitstatus;
510 CListView::OnLButtonDblClk(nFlags, point);
514 * @brief Load or reload the columns (headers) of the list view
516 void CDirView::ReloadColumns()
518 LoadColumnHeaderItems();
526 * @brief Redisplay items in subfolder
527 * @param [in] diffpos First item position in subfolder.
528 * @param [in] level Indent level
529 * @param [in,out] index Index of the item to be inserted.
530 * @param [in,out] alldiffs Number of different items
532 void CDirView::RedisplayChildren(uintptr_t diffpos, int level, UINT &index, int &alldiffs)
534 CDirDoc *pDoc = GetDocument();
535 const CDiffContext &ctxt = pDoc->GetDiffContext();
538 uintptr_t curdiffpos = diffpos;
539 const DIFFITEM &di = ctxt.GetNextSiblingDiffPosition(diffpos);
541 if (di.diffcode.isResultDiff() || (!di.diffcode.existAll(pDoc->m_nDirs) && !di.diffcode.isResultFiltered()))
544 bool bShowable = pDoc->IsShowable(di);
549 AddNewItem(index, curdiffpos, I_IMAGECALLBACK, level);
551 if (di.HasChildren())
553 m_pList->SetItemState(index - 1, INDEXTOSTATEIMAGEMASK((di.customFlags1 & ViewCustomFlags::EXPANDED) ? 2 : 1), LVIS_STATEIMAGEMASK);
554 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
555 RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
560 if (!pDoc->GetRecursive() || !di.diffcode.isDirectory() || !di.diffcode.existAll(pDoc->m_nDirs))
562 AddNewItem(index, curdiffpos, I_IMAGECALLBACK, 0);
565 if (di.HasChildren())
567 RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
575 * @brief Redisplay folder compare view.
576 * This function clears folder compare view and then adds
577 * items from current compare to it.
579 void CDirView::Redisplay()
581 CDirDoc *pDoc = GetDocument();
582 const CDiffContext &ctxt = pDoc->GetDiffContext();
583 PathContext pathsParent;
586 // Disable redrawing while adding new items
589 DeleteAllDisplayItems();
591 m_pList->SetImageList((m_bTreeMode && pDoc->GetRecursive()) ? &m_imageState : NULL, LVSIL_STATE);
593 // If non-recursive compare, add special item(s)
594 if (!pDoc->GetRecursive() ||
595 pDoc->AllowUpwardDirectory(pathsParent) == CDirDoc::AllowUpwardDirectory::ParentIsTempPath)
597 cnt += AddSpecialItems();
601 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
602 RedisplayChildren(diffpos, 0, cnt, alldiffs);
603 if (pDoc->m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPLETED)
604 GetParentFrame()->SetLastCompareResult(alldiffs);
605 SortColumnsAppropriately();
610 * @brief User right-clicked somewhere in this view
612 void CDirView::OnContextMenu(CWnd*, CPoint point)
614 if (GetListCtrl().GetItemCount() == 0)
616 // Make sure window is active
617 GetParentFrame()->ActivateFrame();
620 if (point.x == -1 && point.y == -1)
622 //keystroke invocation
625 ClientToScreen(rect);
627 point = rect.TopLeft();
632 // Check if user right-clicked on header
633 // convert screen coordinates to client coordinates of listview
634 CPoint insidePt = point;
635 GetListCtrl().ScreenToClient(&insidePt);
636 // TODO: correct for hscroll ?
637 // Ask header control if click was on one of its header items
639 memset(&hhti, 0, sizeof(hhti));
641 int col = GetListCtrl().GetHeaderCtrl()->SendMessage(HDM_HITTEST, 0, (LPARAM) & hhti);
644 // Presumably hhti.flags & HHT_ONHEADER is true
645 HeaderContextMenu(point, ColPhysToLog(col));
648 // bail out if point is not in any row
650 memset(&lhti, 0, sizeof(lhti));
652 ScreenToClient(&insidePt);
654 i = GetListCtrl().HitTest(insidePt);
655 TRACE(_T("i=%d\n"), i);
660 ListContextMenu(point, i);
664 * @brief Format context menu string and disable item if it cannot be applied.
666 static void NTAPI FormatContextMenu(BCMenu *pPopup, UINT uIDItem, int n1, int n2 = 0, int n3 = 0)
669 pPopup->GetMenuText(uIDItem, s1, MF_BYCOMMAND);
670 s2.FormatMessage(s1, NumToStr(n1).c_str(), NumToStr(n2).c_str(), NumToStr(n3).c_str());
671 pPopup->SetMenuText(uIDItem, s2, MF_BYCOMMAND);
674 pPopup->EnableMenuItem(uIDItem, MF_GRAYED);
679 * @brief Toggle context menu item
681 static void NTAPI CheckContextMenu(BCMenu *pPopup, UINT uIDItem, BOOL bCheck)
684 pPopup->CheckMenuItem(uIDItem, MF_CHECKED);
686 pPopup->CheckMenuItem(uIDItem, MF_UNCHECKED);
690 * @brief User right-clicked in listview rows
692 void CDirView::ListContextMenu(CPoint point, int /*i*/)
694 CDirDoc *pDoc = GetDocument();
696 VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
697 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
698 theApp.TranslateMenu(menu.m_hMenu);
700 // 1st submenu of IDR_POPUP_DIRVIEW is for item popup
701 BCMenu *pPopup = (BCMenu*) menu.GetSubMenu(0);
702 ASSERT(pPopup != NULL);
704 if (pDoc->m_nDirs < 3)
705 pPopup->RemoveMenu(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, MF_BYCOMMAND);
707 CMenu menuPluginsHolder;
708 menuPluginsHolder.LoadMenu(IDR_POPUP_PLUGINS_SETTINGS);
709 theApp.TranslateMenu(menuPluginsHolder.m_hMenu);
710 String s = theApp.LoadString(IDS_TITLE_PLUGINS_SETTINGS);
711 pPopup->AppendMenu(MF_SEPARATOR);
712 pPopup->AppendMenu(MF_POPUP, (int)menuPluginsHolder.m_hMenu, s.c_str());
714 // set the menu items with the proper directory names
716 GetSelectedDirNames(sl, sr);
718 // TODO: It would be more efficient to set
719 // all the popup items now with one traverse over selected items
720 // instead of using updates, in which we make a traverse for every item
724 //- Archive related menu items follow the above suggestion.
725 //- For disabling to work properly, the tracking frame's m_bAutoMenuEnable
726 // member has to temporarily be turned off.
727 int nTotal = 0; // total #items (includes files & directories, either side)
728 int nCopyableToLeft = 0;
729 int nCopyableToRight = 0;
730 int nDeletableOnLeft = 0;
731 int nDeletableOnRight = 0;
732 int nDeletableOnBoth = 0;
733 int nOpenableOnLeft = 0;
734 int nOpenableOnRight = 0;
735 int nOpenableOnBoth = 0;
736 int nOpenableOnLeftWith = 0;
737 int nOpenableOnRightWith = 0;
740 while ((i = m_pList->GetNextItem(i, LVNI_SELECTED)) != -1)
742 const DIFFITEM& di = GetDiffItem(i);
743 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
745 if (IsItemCopyableToLeft(di))
747 if (IsItemCopyableToRight(di))
750 if (IsItemDeletableOnLeft(di))
752 if (IsItemDeletableOnRight(di))
754 if (IsItemDeletableOnBoth(di))
757 if (IsItemOpenableOnLeft(di))
759 if (IsItemOpenableOnRight(di))
762 if (IsItemOpenableOnLeftWith(di))
763 ++nOpenableOnLeftWith;
764 if (IsItemOpenableOnRightWith(di))
765 ++nOpenableOnRightWith;
767 if (IsItemNavigableDiff(di))
769 if (IsItemOpenableOnLeft(di) || IsItemOpenableOnRight(di))
775 FormatContextMenu(pPopup, ID_DIR_COPY_PATHNAMES_LEFT, nOpenableOnLeft, nTotal);
776 FormatContextMenu(pPopup, ID_DIR_COPY_PATHNAMES_RIGHT, nOpenableOnRight, nTotal);
777 FormatContextMenu(pPopup, ID_DIR_COPY_PATHNAMES_BOTH, nOpenableOnBoth, nTotal);
779 FormatContextMenu(pPopup, ID_DIR_COPY_LEFT_TO_CLIPBOARD, nOpenableOnLeft, nTotal);
780 FormatContextMenu(pPopup, ID_DIR_COPY_RIGHT_TO_CLIPBOARD, nOpenableOnRight, nTotal);
781 FormatContextMenu(pPopup, ID_DIR_COPY_BOTH_TO_CLIPBOARD, nOpenableOnBoth, nTotal);
783 FormatContextMenu(pPopup, ID_DIR_ZIP_LEFT, nOpenableOnLeft, nTotal);
784 FormatContextMenu(pPopup, ID_DIR_ZIP_RIGHT, nOpenableOnRight, nTotal);
785 FormatContextMenu(pPopup, ID_DIR_ZIP_BOTH, nOpenableOnBoth, nTotal);
786 FormatContextMenu(pPopup, ID_DIR_ZIP_BOTH_DIFFS_ONLY, nDiffItems, nTotal);
788 CFrameWnd *pFrame = GetTopLevelFrame();
789 ASSERT(pFrame != NULL);
790 pFrame->m_bAutoMenuEnable = FALSE;
791 // invoke context menu
792 // this will invoke all the OnUpdate methods to enable/disable the individual items
793 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
796 pFrame->m_bAutoMenuEnable = TRUE;
800 * @brief User right-clicked on specified logical column
802 void CDirView::HeaderContextMenu(CPoint point, int /*i*/)
805 VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
806 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
807 theApp.TranslateMenu(menu.m_hMenu);
808 // 2nd submenu of IDR_POPUP_DIRVIEW is for header popup
809 BCMenu* pPopup = (BCMenu *)menu.GetSubMenu(1);
810 ASSERT(pPopup != NULL);
812 // invoke context menu
813 // this will invoke all the OnUpdate methods to enable/disable the individual items
814 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
819 * @brief Gets Explorer's context menu for a group of selected files.
821 * @param [in] Side whether to get context menu for the files from the left or
823 * @retval true menu successfully retrieved.
824 * @retval falsea an error occurred while retrieving the menu.
826 bool CDirView::ListShellContextMenu(SIDE_TYPE side)
828 CShellContextMenu* shellContextMenu;
831 shellContextMenu = m_pShellContextMenuLeft.get(); break;
833 shellContextMenu = (GetDocument()->m_nDirs < 3) ? m_pShellContextMenuRight.get() : m_pShellContextMenuMiddle.get(); break;
835 shellContextMenu = m_pShellContextMenuRight.get(); break;
837 shellContextMenu->Initialize();
839 CDirDoc *pDoc = GetDocument();
842 while ((i = m_pList->GetNextItem(i, LVNI_SELECTED)) != -1)
844 const DIFFITEM& di = GetDiffItem(i);
845 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
848 String filename, currentDir;
851 filename = di.diffFileInfo[0].filename;
852 currentDir = di.getFilepath(0, pDoc->GetBasePath(0));
855 filename = di.diffFileInfo[1].filename;
856 currentDir = di.getFilepath(1, pDoc->GetBasePath(1));
859 filename = di.diffFileInfo[pDoc->m_nDirs - 1].filename;
860 currentDir = di.getFilepath(pDoc->m_nDirs - 1, pDoc->GetBasePath(pDoc->m_nDirs - 1));
864 shellContextMenu->AddItem(currentDir, filename);
866 return shellContextMenu->RequeryShellContextMenu();
870 * @brief Convert number to string.
871 * Converts number to string, with commas between digits in
872 * locale-appropriate manner.
874 String NumToStr(int n)
876 return locality::NumToLocaleStr(n);
879 /// Change menu item by using string resource
880 // (Question: Why don't we just remove it from the menu resource entirely & do an Add here ?)
881 void CDirView::ModifyPopup(CMenu * pPopup, int nStringResource, int nMenuId, LPCTSTR szPath)
883 String s = LangFormatString1(nStringResource, szPath);
884 pPopup->ModifyMenu(nMenuId, MF_BYCOMMAND | MF_STRING, nMenuId, s.c_str());
889 * @brief User chose (main menu) Copy from right to left
891 void CDirView::OnDirCopyRightToLeft()
896 * @brief User chose (main menu) Copy from left to right
898 void CDirView::OnDirCopyLeftToRight()
903 /// User chose (context men) Copy from right to left
904 void CDirView::OnCtxtDirCopyRightToLeft()
908 /// User chose (context menu) Copy from left to right
909 void CDirView::OnCtxtDirCopyLeftToRight()
914 /// User chose (context menu) Copy left to...
915 void CDirView::OnCtxtDirCopyLeftTo()
920 /// User chose (context menu) Copy from right to...
921 void CDirView::OnCtxtDirCopyRightTo()
926 /// Update context menu Copy Right to Left item
927 void CDirView::OnUpdateCtxtDirCopyRightToLeft(CCmdUI* pCmdUI)
929 DoUpdateDirCopyRightToLeft(pCmdUI, eContext);
931 /// Update context menu Copy Left to Right item
932 void CDirView::OnUpdateCtxtDirCopyLeftToRight(CCmdUI* pCmdUI)
934 DoUpdateDirCopyLeftToRight(pCmdUI, eContext);
937 /// Update main menu Copy Right to Left item
938 void CDirView::OnUpdateDirCopyRightToLeft(CCmdUI* pCmdUI)
940 DoUpdateDirCopyRightToLeft(pCmdUI, eMain);
942 /// Update main menu Copy Left to Right item
943 void CDirView::OnUpdateDirCopyLeftToRight(CCmdUI* pCmdUI)
945 DoUpdateDirCopyLeftToRight(pCmdUI, eMain);
948 /// Should Copy to Left be enabled or disabled ? (both main menu & context menu use this)
949 void CDirView::DoUpdateDirCopyRightToLeft(CCmdUI* pCmdUI, eMenuType menuType)
951 if (GetDocument()->GetReadOnly(0))
952 pCmdUI->Enable(FALSE);
956 int legalcount = 0, selcount = 0;
957 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
959 const DIFFITEM& di = GetDiffItem(sel);
960 if (di.diffcode.diffcode != 0 && IsItemCopyableToLeft(di))
964 pCmdUI->Enable(legalcount > 0);
965 if (menuType == eContext)
968 if (legalcount == selcount)
969 s = LangFormatString1(IDS_COPY_TO_LEFT, NumToStr(selcount).c_str());
971 s = LangFormatString2(IDS_COPY_TO_LEFT2, NumToStr(legalcount).c_str(), NumToStr(selcount).c_str());
972 pCmdUI->SetText(s.c_str());
977 /// Should Copy to Right be enabled or disabled ? (both main menu & context menu use this)
978 void CDirView::DoUpdateDirCopyLeftToRight(CCmdUI* pCmdUI, eMenuType menuType)
980 if (GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1))
981 pCmdUI->Enable(FALSE);
985 int legalcount = 0, selcount = 0;
986 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
988 const DIFFITEM& di = GetDiffItem(sel);
989 if (di.diffcode.diffcode != 0 && IsItemCopyableToRight(di))
993 pCmdUI->Enable(legalcount > 0);
994 if (menuType == eContext)
997 if (legalcount == selcount)
998 s = LangFormatString1(IDS_COPY_TO_RIGHT, NumToStr(selcount).c_str());
1000 s = LangFormatString2(IDS_COPY_TO_RIGHT2, NumToStr(legalcount).c_str(), NumToStr(selcount).c_str());
1001 pCmdUI->SetText(s.c_str());
1007 * @brief Update any resources necessary after a GUI language change
1009 void CDirView::UpdateResources()
1011 UpdateColumnNames();
1012 GetParentFrame()->UpdateResources();
1016 * @brief User just clicked a column, so perform sort
1018 void CDirView::OnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
1020 // set sort parameters and handle ascending/descending
1021 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*) pNMHDR;
1022 int oldSortColumn = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
1023 int sortcol = m_invcolorder[pNMListView->iSubItem];
1024 if (sortcol == oldSortColumn)
1027 bool bSortAscending = GetOptionsMgr()->GetBool(OPT_DIRVIEW_SORT_ASCENDING);
1028 GetOptionsMgr()->SaveOption(OPT_DIRVIEW_SORT_ASCENDING, !bSortAscending);
1032 GetOptionsMgr()->SaveOption((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3, sortcol);
1033 // most columns start off ascending, but not dates
1034 bool bSortAscending = IsDefaultSortAscending(sortcol);
1035 GetOptionsMgr()->SaveOption(OPT_DIRVIEW_SORT_ASCENDING, bSortAscending);
1038 SortColumnsAppropriately();
1042 void CDirView::SortColumnsAppropriately()
1044 int sortCol = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
1045 if (sortCol == -1 || sortCol >= GetColLogCount())
1048 bool bSortAscending = GetOptionsMgr()->GetBool(OPT_DIRVIEW_SORT_ASCENDING);
1049 m_ctlSortHeader.SetSortImage(ColLogToPhys(sortCol), bSortAscending);
1050 //sort using static CompareFunc comparison function
1051 CompareState cs(this, sortCol, bSortAscending);
1052 GetListCtrl().SortItems(cs.CompareFunc, reinterpret_cast<DWORD_PTR>(&cs));
1054 m_bNeedSearchLastDiffItem = true;
1055 m_bNeedSearchFirstDiffItem = true;
1058 /// Do any last minute work as view closes
1059 void CDirView::OnDestroy()
1061 DeleteAllDisplayItems();
1066 CListView::OnDestroy();
1068 GetMainFrame()->ClearStatusbarItemCount();
1072 * @brief Open selected item when user presses ENTER key.
1074 void CDirView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
1076 if (nChar == VK_RETURN)
1078 int sel = GetFocusedItem();
1081 const DIFFITEM& di = GetDiffItem(sel);
1082 if (m_bTreeMode && GetDocument()->GetRecursive() && di.diffcode.isDirectory())
1084 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
1085 CollapseSubdir(sel);
1091 CWaitCursor waitstatus;
1096 CListView::OnChar(nChar, nRepCnt, nFlags);
1100 * @brief Expand/collapse subfolder when "+/-" icon is clicked.
1102 void CDirView::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
1104 LPNMITEMACTIVATE pNM = (LPNMITEMACTIVATE)pNMHDR;
1105 LVHITTESTINFO lvhti;
1106 lvhti.pt = pNM->ptAction;
1107 m_pList->SubItemHitTest(&lvhti);
1108 if (lvhti.flags == LVHT_ONITEMSTATEICON)
1110 const DIFFITEM &di = GetItemAt(pNM->iItem);
1111 if (di.customFlags1 & ViewCustomFlags::EXPANDED)
1112 CollapseSubdir(pNM->iItem);
1114 ExpandSubdir(pNM->iItem);
1121 * @brief Expand collapsed folder in tree-view mode.
1123 void CDirView::OnExpandFolder()
1125 const int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
1128 const DIFFITEM &di = GetItemAt(nSelItem);
1129 if (di.diffcode.isDirectory() && (di.customFlags1 &
1130 ViewCustomFlags::EXPANDED) == 0)
1132 ExpandSubdir(nSelItem);
1137 * @brief Collapse expanded folder in tree-view mode.
1139 void CDirView::OnCollapseFolder()
1141 const int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
1144 const DIFFITEM &di = GetItemAt(nSelItem);
1145 if (di.diffcode.isDirectory() && (di.customFlags1 &
1146 ViewCustomFlags::EXPANDED))
1148 CollapseSubdir(nSelItem);
1153 * @brief Collapse subfolder
1154 * @param [in] sel Folder item index in listview.
1156 void CDirView::CollapseSubdir(int sel)
1158 DIFFITEM& dip = this->GetDiffItemRef(sel);
1159 if (!m_bTreeMode || !(dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren())
1162 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
1164 dip.customFlags1 &= ~ViewCustomFlags::EXPANDED;
1165 m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
1167 int count = m_pList->GetItemCount();
1168 for (int i = sel + 1; i < count; i++)
1170 const DIFFITEM& di = GetDiffItem(i);
1171 if (!di.IsAncestor(&dip))
1173 m_pList->DeleteItem(i--);
1177 m_pList->SetRedraw(TRUE); // Turn updating back on
1181 * @brief Expand subfolder
1182 * @param [in] sel Folder item index in listview.
1184 void CDirView::ExpandSubdir(int sel, bool bRecursive)
1186 DIFFITEM& dip = GetDiffItemRef(sel);
1187 if (!m_bTreeMode || (dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren())
1190 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
1192 dip.customFlags1 |= ViewCustomFlags::EXPANDED;
1193 m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
1195 CDirDoc *pDoc = GetDocument();
1196 CDiffContext &ctxt = const_cast<CDiffContext &>(pDoc->GetDiffContext());
1199 uintptr_t diffpos = ctxt.GetFirstChildDiffPosition(GetItemKey(sel));
1202 DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
1203 if (!di.IsAncestor(&dip))
1205 if (di.HasChildren())
1206 di.customFlags1 |= ViewCustomFlags::EXPANDED;
1210 uintptr_t diffpos = ctxt.GetFirstChildDiffPosition(GetItemKey(sel));
1211 UINT indext = sel + 1;
1213 RedisplayChildren(diffpos, dip.GetDepth() + 1, indext, alldiffs);
1215 SortColumnsAppropriately();
1217 m_pList->SetRedraw(TRUE); // Turn updating back on
1221 * @brief Open parent folder if possible.
1223 void CDirView::OpenParentDirectory()
1225 CDirDoc *pDoc = GetDocument();
1226 PathContext pathsParent;
1227 switch (pDoc->AllowUpwardDirectory(pathsParent))
1229 case CDirDoc::AllowUpwardDirectory::ParentIsTempPath:
1230 pDoc->m_pTempPathContext = pDoc->m_pTempPathContext->DeleteHead();
1231 // fall through (no break!)
1232 case CDirDoc::AllowUpwardDirectory::ParentIsRegularPath:
1235 for (int nIndex = 0; nIndex < pathsParent.GetSize(); ++nIndex)
1236 dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nIndex) ? FFILEOPEN_READONLY : 0);
1237 GetMainFrame()->DoFileOpen(&pathsParent, dwFlags, pDoc->GetRecursive(), (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
1239 // fall through (no break!)
1240 case CDirDoc::AllowUpwardDirectory::No:
1243 LangMessageBox(IDS_INVALID_DIRECTORY, MB_ICONSTOP);
1249 * @brief Get one or two selected items
1251 * Returns false if 0 or more than 3 items selecte
1253 bool CDirView::GetSelectedItems(int * sel1, int * sel2, int * sel3)
1257 *sel1 = m_pList->GetNextItem(-1, LVNI_SELECTED);
1260 *sel2 = m_pList->GetNextItem(*sel1, LVNI_SELECTED);
1263 *sel3 = m_pList->GetNextItem(*sel2, LVNI_SELECTED);
1266 int extra = m_pList->GetNextItem(*sel3, LVNI_SELECTED);
1267 return (extra == -1);
1271 * @brief Open special items (parent folders etc).
1272 * @param [in] pos1 First item position.
1273 * @param [in] pos2 Second item position.
1275 void CDirView::OpenSpecialItems(uintptr_t pos1, uintptr_t pos2, uintptr_t pos3)
1279 // Browse to parent folder(s) selected
1280 // SPECIAL_ITEM_POS is position for
1281 // special items, but there is currenly
1282 // only one (parent folder)
1283 OpenParentDirectory();
1287 // Parent directory & something else selected
1293 * @brief Creates a pairing folder for unique folder item.
1294 * This function creates a pairing folder for unique folder item in
1295 * folder compare. This way user can browse into unique folder's
1296 * contents and don't necessarily need to copy whole folder structure.
1297 * @param [in] di DIFFITEM for folder compare item.
1298 * @param [in] side1 true if our unique folder item is side1 item.
1299 * @param [out] newFolder New created folder (full folder path).
1300 * @return true if user agreed and folder was created.
1302 bool CDirView::CreateFoldersPair(DIFFITEM & di, bool side1, String &newFolder)
1308 // Get left side (side1) folder name (existing) and
1309 // right side base path (where to create)
1310 subdir = di.diffFileInfo[0].filename;
1311 basedir = GetDocument()->GetBasePath(1);
1312 basedir = di.getFilepath(0, basedir);
1316 // Get right side (side2) folder name (existing) and
1317 // left side base path (where to create)
1318 subdir = di.diffFileInfo[1].filename;
1319 basedir = GetDocument()->GetBasePath(0);
1320 basedir = di.getFilepath(1, basedir);
1322 String createpath = paths_ConcatPath(basedir, subdir);
1323 newFolder = createpath;
1325 String message = LangFormatString1(IDS_CREATE_PAIR_FOLDER, createpath.c_str());
1326 int res = AfxMessageBox(message.c_str(), MB_YESNO | MB_ICONWARNING);
1329 bool ret = paths_CreateIfNeeded(createpath);
1336 * @brief Open one selected item.
1337 * @param [in] pos1 Item position.
1338 * @param [in,out] di1 Pointer to first diffitem.
1339 * @param [in,out] di2 Pointer to second diffitem.
1340 * @param [in,out] di3 Pointer to third diffitem.
1341 * @param [out] paths First/Second/Third paths.
1342 * @param [out] sel1 Item's selection index in listview.
1343 * @param [in,out] isDir Is item folder?
1344 * return false if there was error or item was completely processed.
1346 bool CDirView::OpenOneItem(uintptr_t pos1, DIFFITEM *pdi[3],
1347 PathContext & paths, int & sel1, bool & isdir, int nPane[3])
1349 CDirDoc * pDoc = GetDocument();
1351 pdi[0] = &pDoc->GetDiffRefByKey(pos1);
1355 GetItemFileNames(sel1, &paths);
1357 for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
1358 nPane[nIndex] = nIndex;
1360 if (pdi[0]->diffcode.isDirectory())
1363 if (isdir && (pdi[0]->diffcode.existsFirst() && pdi[1]->diffcode.existsSecond() && pdi[2]->diffcode.existsThird()))
1365 // Check both folders exist. If either folder is missing that means
1366 // folder has been changed behind our back, so we just tell user to
1367 // refresh the compare.
1368 PATH_EXISTENCE path1Exists = paths_DoesPathExist(paths[0]);
1369 PATH_EXISTENCE path2Exists = paths_DoesPathExist(paths[1]);
1370 if (path1Exists != IS_EXISTING_DIR || path2Exists != IS_EXISTING_DIR)
1372 String invalid = path1Exists == IS_EXISTING_DIR ? paths[0] : paths[1];
1373 ResMsgBox1(IDS_DIRCMP_NOTSYNC, invalid.c_str(), MB_ICONSTOP);
1377 // else if ((*di1)->diffcode.isSideLeftOnly())
1379 // // Open left-only item to editor if its not a folder or binary
1382 // if (CreateFoldersPair(**di1, true, path2))
1387 // else if ((*di1)->diffcode.isBin())
1388 // LangMessageBox(IDS_CANNOT_OPEN_BINARYFILE, MB_ICONSTOP);
1390 // DoOpenWithEditor(SIDE_LEFT);
1393 // else if ((*di1)->diffcode.isSideRightOnly())
1395 // // Open right-only item to editor if its not a folder or binary
1398 // if (CreateFoldersPair(**di1, false, path1))
1403 // else if ((*di1)->diffcode.isBin())
1404 // LangMessageBox(IDS_CANNOT_OPEN_BINARYFILE, MB_ICONSTOP);
1406 // DoOpenWithEditor(SIDE_RIGHT);
1409 // Fall through and compare files (which may be archives)
1415 * @brief Open two selected items.
1416 * @param [in] pos1 First item position.
1417 * @param [in] pos2 Second item position.
1418 * @param [in,out] di1 Pointer to first diffitem.
1419 * @param [in,out] di2 Pointer to second diffitem.
1420 * @param [out] paths First/Second/Third paths.
1421 * @param [out] sel1 First item's selection index in listview.
1422 * @param [out] sel2 Second item's selection index in listview.
1423 * @param [in,out] isDir Is item folder?
1424 * return false if there was error or item was completely processed.
1426 bool CDirView::OpenTwoItems(SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, DIFFITEM *pdi[3],
1427 PathContext & paths, int & sel1, int & sel2, bool & isDir, int nPane[3])
1429 String pathLeft, pathRight;
1430 CDirDoc * pDoc = GetDocument();
1432 // Two items selected, get their info
1433 pdi[0] = &pDoc->GetDiffRefByKey(pos1);
1434 pdi[1] = &pDoc->GetDiffRefByKey(pos2);
1438 // Check for binary & side compatibility & file/dir compatibility
1439 if (!AreItemsOpenable(selectionType, *pdi[0], *pdi[1]))
1445 switch (selectionType)
1447 case SELECTIONTYPE_NORMAL:
1448 // Ensure that di1 is on left (swap if needed)
1449 if (pdi[0]->diffcode.isSideSecondOnly() || (pdi[0]->diffcode.isSideBoth() &&
1450 pdi[1]->diffcode.isSideFirstOnly()))
1452 swap(pdi[0], pdi[1]);
1456 case SELECTIONTYPE_LEFT1LEFT2:
1457 nPane[0] = nPane[1] = 0;
1459 case SELECTIONTYPE_RIGHT1RIGHT2:
1460 nPane[0] = nPane[1] = 1;
1462 case SELECTIONTYPE_LEFT1RIGHT2:
1464 case SELECTIONTYPE_LEFT2RIGHT1:
1465 swap(pdi[0], pdi[1]);
1470 PathContext files1, files2;
1471 GetItemFileNames(sel1, &files1);
1472 GetItemFileNames(sel2, &files2);
1473 paths.SetLeft(files1[nPane[0]]);
1474 paths.SetRight(files2[nPane[1]]);
1476 if (pdi[0]->diffcode.isDirectory())
1479 if (GetPairComparability(paths) != IS_EXISTING_DIR)
1481 LangMessageBox(IDS_INVALID_DIRECTORY, MB_ICONSTOP);
1490 * @brief Open three selected items.
1491 * @param [in] pos1 First item position.
1492 * @param [in] pos2 Second item position.
1493 * @param [in] pos3 Third item position.
1494 * @param [in,out] di1 Pointer to first diffitem.
1495 * @param [in,out] di2 Pointer to second diffitem.
1496 * @param [in,out] di3 Pointer to third diffitem.
1497 * @param [out] paths First/Second/Third paths.
1498 * @param [out] sel1 First item's selection index in listview.
1499 * @param [out] sel2 Second item's selection index in listview.
1500 * @param [out] sel3 Third item's selection index in listview.
1501 * @param [in,out] isDir Is item folder?
1502 * return false if there was error or item was completely processed.
1504 bool CDirView::OpenThreeItems(uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, DIFFITEM *pdi[3],
1505 PathContext & paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3])
1507 String pathLeft, pathMiddle, pathRight;
1508 CDirDoc * pDoc = GetDocument();
1511 for (int nIndex = 0; nIndex < 3; ++nIndex)
1512 nPane[nIndex] = nIndex;
1515 // Two items selected, get their info
1516 pdi[0] = &pDoc->GetDiffRefByKey(pos1);
1517 pdi[1] = &pDoc->GetDiffRefByKey(pos2);
1519 // Check for binary & side compatibility & file/dir compatibility
1520 if (!AreItemsOpenable(*pdi[0], *pdi[1], *pdi[1]) && !AreItemsOpenable(*pdi[0], *pdi[0], *pdi[1]))
1524 // Ensure that pdi[0] is on left (swap if needed)
1525 if (pdi[0]->diffcode.exists(0) && pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(2))
1532 else if (pdi[0]->diffcode.exists(0) && pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(1))
1537 else if (pdi[0]->diffcode.exists(1) && pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(0))
1539 swap(pdi[0], pdi[1]);
1544 else if (pdi[1]->diffcode.exists(0) && pdi[1]->diffcode.exists(1) && pdi[0]->diffcode.exists(2))
1546 swap(pdi[0], pdi[1]);
1553 else if (pdi[1]->diffcode.exists(0) && pdi[1]->diffcode.exists(2) && pdi[0]->diffcode.exists(1))
1555 swap(pdi[0], pdi[1]);
1560 else if (pdi[1]->diffcode.exists(1) && pdi[1]->diffcode.exists(2) && pdi[0]->diffcode.exists(0))
1568 // Three items selected, get their info
1569 pdi[0] = &pDoc->GetDiffRefByKey(pos1);
1570 pdi[1] = &pDoc->GetDiffRefByKey(pos2);
1571 pdi[2] = &pDoc->GetDiffRefByKey(pos3);
1573 // Check for binary & side compatibility & file/dir compatibility
1574 if (!AreItemsOpenable(*pdi[0], *pdi[1], *pdi[2]))
1578 // Ensure that pdi[0] is on left (swap if needed)
1579 if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(1) && pdi[2]->diffcode.exists(2))
1582 else if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(2) && pdi[2]->diffcode.exists(1))
1584 swap(pdi[1], pdi[2]);
1587 else if (pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(0) && pdi[2]->diffcode.exists(2))
1589 swap(pdi[0], pdi[1]);
1592 else if (pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(2) && pdi[2]->diffcode.exists(0))
1594 swap(pdi[0], pdi[2]);
1596 swap(pdi[1], pdi[2]);
1599 else if (pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(0) && pdi[2]->diffcode.exists(1))
1601 swap(pdi[0], pdi[1]);
1603 swap(pdi[1], pdi[2]);
1606 else if (pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(1) && pdi[2]->diffcode.exists(0))
1608 swap(pdi[0], pdi[2]);
1613 // Fill in pathLeft & & pathMiddle & pathRight
1614 PathContext pathsTemp;
1615 GetItemFileNames(sel1, &pathsTemp);
1616 pathLeft = pathsTemp[0];
1617 GetItemFileNames(sel2, &pathsTemp);
1618 pathMiddle = pathsTemp[1];
1619 GetItemFileNames(sel3, &pathsTemp);
1620 pathRight = pathsTemp[2];
1622 paths.SetLeft(pathLeft.c_str());
1623 paths.SetMiddle(pathMiddle.c_str());
1624 paths.SetRight(pathRight.c_str());
1626 if (pdi[0]->diffcode.isDirectory())
1629 if (GetPairComparability(paths) != IS_EXISTING_DIR)
1631 AfxMessageBox(IDS_INVALID_DIRECTORY, MB_ICONSTOP);
1640 * @brief Open selected files or directories.
1642 * Opens selected files to file compare. If comparing
1643 * directories non-recursively, then subfolders and parent
1644 * folder are opened too.
1646 * This handles the case that one item is selected
1647 * and the case that two items are selected (one on each side)
1649 void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMAL*/, PackingInfo * infoUnpacker /*= NULL*/)
1651 Merge7zFormatMergePluginScope scope(infoUnpacker);
1652 CDirDoc * pDoc = GetDocument();
1654 // First, figure out what was selected (store into pos1 & pos2)
1655 uintptr_t pos1 = NULL, pos2 = NULL, pos3 = NULL;
1656 int sel1 = -1, sel2 = -1, sel3 = -1;
1657 if (!GetSelectedItems(&sel1, &sel2, &sel3))
1659 // Must have 1 or 2 or 3 items selected
1664 pos1 = GetItemKey(sel1);
1668 pos2 = GetItemKey(sel2);
1671 pos3 = GetItemKey(sel3);
1674 // Now handle the various cases of what was selected
1676 if (pos1 == SPECIAL_ITEM_POS)
1678 OpenSpecialItems(pos1, pos2, pos3);
1682 // Common variables which both code paths below are responsible for setting
1684 DIFFITEM *pdi[3] = {0}; // left & right items (di1==di2 if single selection)
1685 bool isdir = false; // set if we're comparing directories
1686 bool bRO[3] = { false, false, false };
1691 bool success = OpenTwoItems(selectionType, pos1, pos2, pdi,
1692 paths, sel1, sel2, isdir, nPane);
1696 else if (pos2 && pos3)
1698 bool success = OpenThreeItems(pos1, pos2, pos3, pdi,
1699 paths, sel1, sel2, sel3, isdir, nPane);
1705 // Only one item selected, so perform diff on its sides
1706 bool success = OpenOneItem(pos1, pdi, paths, sel1, isdir, nPane);
1711 // Now pathLeft, pathRight, di1, di2, and isdir are all set
1712 // We have two items to compare, no matter whether same or different underlying DirView item
1715 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
1716 dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nPane[nIndex]) ? FFILEOPEN_READONLY : 0);
1720 // Don't add folders to MRU
1721 GetMainFrame()->DoFileOpen(&paths, dwFlags, pDoc->GetRecursive(), (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
1723 else if (HasZipSupport() && std::count_if(paths.begin(), paths.end(), ArchiveGuessFormat) == paths.GetSize())
1725 // Open archives, not adding paths to MRU
1726 GetMainFrame()->DoFileOpen(&paths, dwFlags, pDoc->GetRecursive(), NULL, _T(""), infoUnpacker);
1730 // Regular file case
1732 // Binary attributes are set after files are unpacked
1733 // so after plugins such as the MS-Office plugins have had a chance to make them textual
1734 // We haven't done unpacking yet in this diff, but if a binary flag is already set,
1735 // then it was set in a previous diff after unpacking, so we trust it
1737 // Open identical and different files
1738 FileLocation fileloc[3];
1739 if (paths.GetSize() < 3)
1741 theApp.m_strDescriptions[0].erase();
1742 theApp.m_strDescriptions[1].erase();
1743 if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(0))
1746 theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
1748 if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(1))
1751 theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
1756 theApp.m_strDescriptions[0].erase();
1757 theApp.m_strDescriptions[1].erase();
1758 theApp.m_strDescriptions[2].erase();
1759 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(0))
1762 theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
1764 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(1))
1767 theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_MIDDLE_FILE);
1769 if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(2))
1772 theApp.m_strDescriptions[2] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
1776 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
1778 fileloc[nIndex].setPath(paths[nIndex]);
1779 fileloc[nIndex].encoding = pdi[nIndex]->diffFileInfo[nPane[nIndex]].encoding;
1781 GetMainFrame()->ShowAutoMergeDoc(pDoc, paths.GetSize(), fileloc,
1782 dwFlags, infoUnpacker);
1786 void CDirView::OpenSelectionHex()
1788 CDirDoc * pDoc = GetDocument();
1790 // First, figure out what was selected (store into pos1 & pos2)
1791 uintptr_t pos1 = NULL, pos2 = NULL;
1792 int sel1 = -1, sel2 = -1, sel3 = -1;
1793 if (!GetSelectedItems(&sel1, &sel2, &sel3))
1795 // Must have 1 or 2 items selected
1800 pos1 = GetItemKey(sel1);
1803 pos2 = GetItemKey(sel2);
1805 // Now handle the various cases of what was selected
1807 if (pos1 == SPECIAL_ITEM_POS)
1813 // Common variables which both code paths below are responsible for setting
1815 DIFFITEM *pdi[3] = { 0 }; // left & right items (di1==di2 if single selection)
1816 bool isdir = false; // set if we're comparing directories
1820 bool success = OpenTwoItems(SELECTIONTYPE_NORMAL, pos1, pos2, pdi,
1821 paths, sel1, sel2, isdir, nPane);
1827 // Only one item selected, so perform diff on its sides
1828 bool success = OpenOneItem(pos1, pdi, paths, sel1, isdir, nPane);
1833 // Open identical and different files
1835 for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
1836 bRO[nIndex] = !!pDoc->GetReadOnly(nPane[nIndex]);
1838 GetMainFrame()->ShowHexMergeDoc(pDoc, paths, bRO);
1841 /// User chose (context menu) delete left
1842 void CDirView::OnCtxtDirDelLeft()
1847 /// User chose (context menu) delete right
1848 void CDirView::OnCtxtDirDelRight()
1853 /// User chose (context menu) delete both
1854 void CDirView::OnCtxtDirDelBoth()
1859 /// Enable/disable Delete Left menu choice on context menu
1860 void CDirView::OnUpdateCtxtDirDelLeft(CCmdUI* pCmdUI)
1862 DoUpdateCtxtDirDelLeft(pCmdUI);
1865 /// Enable/disable Delete Right menu choice on context menu
1866 void CDirView::OnUpdateCtxtDirDelRight(CCmdUI* pCmdUI)
1868 DoUpdateCtxtDirDelRight(pCmdUI);
1870 /// Enable/disable Delete Both menu choice on context menu
1871 void CDirView::OnUpdateCtxtDirDelBoth(CCmdUI* pCmdUI)
1873 DoUpdateCtxtDirDelBoth(pCmdUI);
1876 /// Should Delete left be enabled or disabled ?
1877 void CDirView::DoUpdateCtxtDirDelLeft(CCmdUI* pCmdUI)
1879 if (GetDocument()->GetReadOnly(0))
1880 pCmdUI->Enable(FALSE);
1884 int count = 0, total = 0;
1885 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
1887 const DIFFITEM& di = GetDiffItem(sel);
1888 if (di.diffcode.diffcode != 0 && IsItemDeletableOnLeft(di))
1892 pCmdUI->Enable(count > 0);
1896 s = LangFormatString1(IDS_DEL_LEFT_FMT, NumToStr(total).c_str());
1898 s = LangFormatString2(IDS_DEL_LEFT_FMT2, NumToStr(count).c_str(), NumToStr(total).c_str());
1899 pCmdUI->SetText(s.c_str());
1903 /// Should Delete right be enabled or disabled ?
1904 void CDirView::DoUpdateCtxtDirDelRight(CCmdUI* pCmdUI)
1906 if (GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1))
1907 pCmdUI->Enable(FALSE);
1911 int count = 0, total = 0;
1912 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
1914 const DIFFITEM& di = GetDiffItem(sel);
1915 if (di.diffcode.diffcode != 0 && IsItemDeletableOnRight(di))
1919 pCmdUI->Enable(count > 0);
1923 s = LangFormatString1(IDS_DEL_RIGHT_FMT, NumToStr(total).c_str());
1925 s = LangFormatString2(IDS_DEL_RIGHT_FMT2, NumToStr(count).c_str(), NumToStr(total).c_str());
1926 pCmdUI->SetText(s.c_str());
1931 * @brief Should Delete both be enabled or disabled ?
1933 void CDirView::DoUpdateCtxtDirDelBoth(CCmdUI* pCmdUI)
1935 if (GetDocument()->GetReadOnly(0) || GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1))
1936 pCmdUI->Enable(FALSE);
1940 int count = 0, total = 0;
1941 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
1943 const DIFFITEM& di = GetDiffItem(sel);
1944 if (di.diffcode.diffcode != 0 && IsItemDeletableOnBoth(di))
1948 pCmdUI->Enable(count > 0);
1952 s = LangFormatString1(IDS_DEL_BOTH_FMT, NumToStr(total).c_str());
1954 s = LangFormatString2(IDS_DEL_BOTH_FMT2, NumToStr(count).c_str(), NumToStr(total).c_str());
1955 pCmdUI->SetText(s.c_str());
1960 * @brief Enable/disable "Copy | Left to..." and update item text
1962 void CDirView::DoUpdateCtxtDirCopyLeftTo(CCmdUI* pCmdUI)
1965 int count = 0, total = 0;
1966 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
1968 const DIFFITEM& di = GetDiffItem(sel);
1969 if (di.diffcode.diffcode != 0 && IsItemCopyableToOnLeft(di))
1973 pCmdUI->Enable(count > 0);
1977 s = LangFormatString1(IDS_COPY_LEFT_TO, NumToStr(total).c_str());
1979 s = LangFormatString2(IDS_COPY_LEFT_TO2, NumToStr(count).c_str(), NumToStr(total).c_str());
1980 pCmdUI->SetText(s.c_str());
1984 * @brief Enable/disable "Copy | Right to..." and update item text
1986 void CDirView::DoUpdateCtxtDirCopyRightTo(CCmdUI* pCmdUI)
1989 int count = 0, total = 0;
1990 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
1992 const DIFFITEM& di = GetDiffItem(sel);
1993 if (di.diffcode.diffcode != 0 && IsItemCopyableToOnRight(di))
1997 pCmdUI->Enable(count > 0);
2001 s = LangFormatString1(IDS_COPY_RIGHT_TO, NumToStr(total).c_str());
2003 s = LangFormatString2(IDS_COPY_RIGHT_TO2, NumToStr(count).c_str(), NumToStr(total).c_str());
2004 pCmdUI->SetText(s.c_str());
2008 * @brief Enable/disable "Move | Left to..." and update item text
2010 void CDirView::DoUpdateCtxtDirMoveLeftTo(CCmdUI* pCmdUI)
2013 int count = 0, total = 0;
2014 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
2016 const DIFFITEM& di = GetDiffItem(sel);
2017 if (di.diffcode.diffcode != 0 && IsItemCopyableToOnLeft(di) && IsItemDeletableOnLeft(di))
2021 pCmdUI->Enable(count > 0);
2025 s = LangFormatString1(IDS_MOVE_LEFT_TO, NumToStr(total).c_str());
2027 s = LangFormatString2(IDS_MOVE_LEFT_TO2, NumToStr(count).c_str(), NumToStr(total).c_str());
2028 pCmdUI->SetText(s.c_str());
2032 * @brief Enable/disable "Move | Right to..." and update item text
2034 void CDirView::DoUpdateCtxtDirMoveRightTo(CCmdUI* pCmdUI)
2037 int count = 0, total = 0;
2038 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
2040 const DIFFITEM& di = GetDiffItem(sel);
2041 if (di.diffcode.diffcode != 0 && IsItemCopyableToOnRight(di) && IsItemDeletableOnRight(di))
2045 pCmdUI->Enable(count > 0);
2049 s = LangFormatString1(IDS_MOVE_RIGHT_TO, NumToStr(total).c_str());
2051 s = LangFormatString2(IDS_MOVE_RIGHT_TO2, NumToStr(count).c_str(), NumToStr(total).c_str());
2052 pCmdUI->SetText(s.c_str());
2056 * @brief Update "Copy | Right to..." item
2058 void CDirView::OnUpdateCtxtDirCopyLeftTo(CCmdUI* pCmdUI)
2060 DoUpdateCtxtDirCopyLeftTo(pCmdUI);
2064 * @brief Update "Copy | Right to..." item
2066 void CDirView::OnUpdateCtxtDirCopyRightTo(CCmdUI* pCmdUI)
2068 DoUpdateCtxtDirCopyRightTo(pCmdUI);
2072 * @brief Get keydata associated with item in given index.
2073 * @param [in] idx Item's index to list in UI.
2074 * @return Key for item in given index.
2076 uintptr_t CDirView::GetItemKey(int idx) const
2078 return (uintptr_t) m_pList->GetItemData(idx);
2081 // SetItemKey & GetItemKey encapsulate how the display list items
2082 // are mapped to DiffItems, which in turn are DiffContext keys to the actual DIFFITEM data
2085 * @brief Get DIFFITEM data for item.
2086 * This function returns DIFFITEM data for item in given index in GUI.
2087 * @param [in] sel Item's index in folder compare GUI list.
2088 * @return DIFFITEM for item.
2090 const DIFFITEM &CDirView::GetDiffItem(int sel) const
2092 CDirView * pDirView = const_cast<CDirView *>(this);
2093 return pDirView->GetDiffItemRef(sel);
2097 * Given index in list control, get modifiable reference to its DIFFITEM data
2099 DIFFITEM & CDirView::GetDiffItemRef(int sel)
2101 uintptr_t diffpos = GetItemKey(sel);
2103 // If it is special item, return empty DIFFITEM
2104 if (diffpos == SPECIAL_ITEM_POS)
2106 // TODO: It would be better if there were individual items
2107 // for whatever these special items are
2108 // because here we have to hope client does not modify this
2109 // static (shared) item
2110 return DIFFITEM::emptyitem;
2112 return GetDocument()->GetDiffRefByKey(diffpos);
2115 void CDirView::DeleteAllDisplayItems()
2117 // item data are just positions (diffposes)
2118 // that is, they contain no memory needing to be freed
2119 m_pList->DeleteAllItems();
2123 * @brief Given key, get index of item which has it stored.
2124 * This function searches from list in UI.
2126 int CDirView::GetItemIndex(uintptr_t key)
2128 LVFINDINFO findInfo;
2130 findInfo.flags = LVFI_PARAM; // Search for itemdata
2131 findInfo.lParam = (LPARAM)key;
2132 return m_pList->FindItem(&findInfo);
2135 /// User chose (context menu) open left
2136 void CDirView::OnCtxtDirOpenLeft()
2140 /// User chose (context menu) open right
2141 void CDirView::OnCtxtDirOpenRight()
2146 /// User chose (context menu) open left with
2147 void CDirView::OnCtxtDirOpenLeftWith()
2149 DoOpenWith(SIDE_LEFT);
2152 /// User chose (context menu) open right with
2153 void CDirView::OnCtxtDirOpenRightWith()
2155 DoOpenWith(SIDE_RIGHT);
2158 /// User chose (context menu) open right with editor
2159 void CDirView::OnCtxtDirOpenRightWithEditor()
2161 DoOpenWithEditor(SIDE_RIGHT);
2164 /// Update context menuitem "Open right | with editor"
2165 void CDirView::OnUpdateCtxtDirOpenRightWithEditor(CCmdUI* pCmdUI)
2167 DoUpdateOpenRightWith(pCmdUI);
2170 /// User chose (context menu) open left with editor
2171 void CDirView::OnCtxtDirOpenLeftWithEditor()
2173 DoOpenWithEditor(SIDE_LEFT);
2176 void CDirView::OnUpdateCtxtDirOpenLeftWithEditor(CCmdUI* pCmdUI)
2178 DoUpdateOpenLeftWith(pCmdUI);
2181 // return selected item index, or -1 if none or multiple
2182 int CDirView::GetSingleSelectedItem() const
2184 int sel = -1, sel2 = -1;
2185 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
2186 if (sel == -1) return -1;
2187 sel2 = m_pList->GetNextItem(sel, LVNI_SELECTED);
2188 if (sel2 != -1) return -1;
2191 // Enable/disable Open Left menu choice on context menu
2192 void CDirView::OnUpdateCtxtDirOpenLeft(CCmdUI* pCmdUI)
2194 DoUpdateOpenLeft(pCmdUI);
2196 // Enable/disable Open Right menu choice on context menu
2197 void CDirView::OnUpdateCtxtDirOpenRight(CCmdUI* pCmdUI)
2199 DoUpdateOpenRight(pCmdUI);
2202 // Enable/disable Open Left With menu choice on context menu
2203 void CDirView::OnUpdateCtxtDirOpenLeftWith(CCmdUI* pCmdUI)
2205 DoUpdateOpenLeftWith(pCmdUI);
2207 // Enable/disable Open Right With menu choice on context menu
2208 void CDirView::OnUpdateCtxtDirOpenRightWith(CCmdUI* pCmdUI)
2210 DoUpdateOpenRightWith(pCmdUI);
2214 void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI)
2216 int sel1 = -1, sel2 = -1, sel3 = -1;
2217 if (!GetSelectedItems(&sel1, &sel2, &sel3))
2219 // 0 items or more than 2 items seleted
2220 pCmdUI->Enable(FALSE);
2225 // One item selected
2226 const DIFFITEM& di = GetDiffItem(sel1);
2227 if (selectionType != SELECTIONTYPE_NORMAL || !IsItemOpenable(di))
2229 pCmdUI->Enable(FALSE);
2233 else if (sel3 == -1)
2235 // Two items selected
2236 const DIFFITEM& di1 = GetDiffItem(sel1);
2237 const DIFFITEM& di2 = GetDiffItem(sel2);
2238 if (!AreItemsOpenable(selectionType, di1, di2))
2240 pCmdUI->Enable(FALSE);
2246 // Three items selected
2247 const DIFFITEM& di1 = GetDiffItem(sel1);
2248 const DIFFITEM& di2 = GetDiffItem(sel2);
2249 const DIFFITEM& di3 = GetDiffItem(sel3);
2250 if (selectionType != SELECTIONTYPE_NORMAL || !AreItemsOpenable(di1, di2, di3))
2252 pCmdUI->Enable(FALSE);
2256 pCmdUI->Enable(TRUE);
2259 // used for OpenLeft
2260 void CDirView::DoUpdateOpenLeft(CCmdUI* pCmdUI)
2262 int sel = GetSingleSelectedItem();
2265 const DIFFITEM& di = GetDiffItem(sel);
2266 if (di.diffcode.diffcode == 0 || !IsItemOpenableOnLeft(di))
2270 pCmdUI->Enable(sel >= 0);
2273 // used for OpenRight
2274 void CDirView::DoUpdateOpenRight(CCmdUI* pCmdUI)
2276 int sel = GetSingleSelectedItem();
2279 const DIFFITEM& di = GetDiffItem(sel);
2280 if (di.diffcode.diffcode == 0 || !IsItemOpenableOnRight(di))
2284 pCmdUI->Enable(sel >= 0);
2287 // used for OpenLeftWith
2288 void CDirView::DoUpdateOpenLeftWith(CCmdUI* pCmdUI)
2290 int sel = GetSingleSelectedItem();
2293 const DIFFITEM& di = GetDiffItem(sel);
2294 if (di.diffcode.diffcode == 0 || !IsItemOpenableOnLeftWith(di))
2298 pCmdUI->Enable(sel >= 0);
2301 // used for OpenRightWith
2302 void CDirView::DoUpdateOpenRightWith(CCmdUI* pCmdUI)
2304 int sel = GetSingleSelectedItem();
2307 const DIFFITEM& di = GetDiffItem(sel);
2308 if (di.diffcode.diffcode == 0 || !IsItemOpenableOnRightWith(di))
2312 pCmdUI->Enable(sel >= 0);
2316 * @brief Update main menu "Merge | Delete" item.
2317 * @param [in] pCmdUI Item to modify.
2319 void CDirView::DoUpdateDelete(CCmdUI* pCmdUI)
2321 // If both sides are read-only, then there is nothing to delete
2322 if (GetDocument()->GetReadOnly(TRUE) && GetDocument()->GetReadOnly(FALSE))
2324 pCmdUI->Enable(FALSE);
2328 // If no selected items, disable
2329 int count = GetSelectedCount();
2332 pCmdUI->Enable(FALSE);
2336 // Enable if one deletable item is found
2339 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1 && count == 0)
2341 const DIFFITEM& di = GetDiffItem(sel);
2342 if (di.diffcode.diffcode != 0 &&
2343 (IsItemDeletableOnLeft(di) || IsItemDeletableOnRight(di)))
2348 pCmdUI->Enable(count > 0);
2352 * @brief Update dirview context menu "Copy Filenames" item
2354 void CDirView::DoUpdateCopyFilenames(CCmdUI* pCmdUI)
2358 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
2360 const DIFFITEM& di = GetDiffItem(sel);
2361 if (di.diffcode.diffcode != 0 && !di.diffcode.isDirectory())
2364 pCmdUI->Enable(count > 0);
2368 * @brief Return count of selected items in folder compare.
2370 UINT CDirView::GetSelectedCount() const
2372 return m_pList->GetSelectedCount();
2376 * @brief Return index of first selected item in folder compare.
2378 int CDirView::GetFirstSelectedInd()
2380 return m_pList->GetNextItem(-1, LVNI_SELECTED);
2384 * @brief Get index of next selected item in folder compare.
2385 * @param [in,out] ind
2386 * - IN current index, for which next index is searched
2387 * - OUT new index of found item
2388 * @return DIFFITEM in found index.
2390 DIFFITEM &CDirView::GetNextSelectedInd(int &ind)
2392 int sel = m_pList->GetNextItem(ind, LVNI_SELECTED);
2393 DIFFITEM &di = GetDiffItemRef(ind);
2400 * @brief Return DIFFITEM from given index.
2401 * @param [in] ind Index from where DIFFITEM is wanted.
2402 * @return DIFFITEM in given index.
2404 DIFFITEM &CDirView::GetItemAt(int ind)
2406 ASSERT(ind != -1); // Trap programmer errors in debug
2407 return GetDiffItemRef(ind);
2411 // If none or one item selected select found item
2412 // This is used for scrolling to first diff too
2413 void CDirView::OnFirstdiff()
2416 const int count = m_pList->GetItemCount();
2419 int currentInd = GetFirstSelectedInd();
2420 int selCount = GetSelectedCount();
2422 while (i < count && found == false)
2424 const DIFFITEM &di = GetItemAt(i);
2425 if (IsItemNavigableDiff(di))
2427 MoveFocus(currentInd, i, selCount);
2434 void CDirView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
2436 int firstDiff = GetFirstDifferentItem();
2438 pCmdUI->Enable(TRUE);
2440 pCmdUI->Enable(FALSE);
2444 // If none or one item selected select found item
2445 void CDirView::OnLastdiff()
2448 const int count = m_pList->GetItemCount();
2450 int currentInd = GetFirstSelectedInd();
2451 int selCount = GetSelectedCount();
2453 while (i > -1 && found == false)
2455 const DIFFITEM &di = GetItemAt(i);
2456 if (IsItemNavigableDiff(di))
2458 MoveFocus(currentInd, i, selCount);
2465 void CDirView::OnUpdateLastdiff(CCmdUI* pCmdUI)
2467 int firstDiff = GetFirstDifferentItem();
2469 pCmdUI->Enable(TRUE);
2471 pCmdUI->Enable(FALSE);
2475 // If none or one item selected select found item
2476 void CDirView::OnNextdiff()
2478 const int count = m_pList->GetItemCount();
2480 int i = GetFocusedItem();
2482 int selCount = GetSelectedCount();
2487 while (i < count && found == false)
2489 const DIFFITEM &di = GetItemAt(i);
2490 if (IsItemNavigableDiff(di))
2492 MoveFocus(currentInd, i, selCount);
2500 void CDirView::OnUpdateNextdiff(CCmdUI* pCmdUI)
2502 int focused = GetFocusedItem();
2503 int lastDiff = GetLastDifferentItem();
2505 // Check if different files were found and
2506 // there is different item after focused item
2507 if ((lastDiff > -1) && (focused < lastDiff))
2508 pCmdUI->Enable(TRUE);
2510 pCmdUI->Enable(FALSE);
2514 // If none or one item selected select found item
2515 void CDirView::OnPrevdiff()
2518 int i = GetFocusedItem();
2520 int selCount = GetSelectedCount();
2526 while (i > -1 && found == false)
2528 const DIFFITEM &di = GetItemAt(i);
2529 if (IsItemNavigableDiff(di))
2531 MoveFocus(currentInd, i, selCount);
2539 void CDirView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
2541 int focused = GetFocusedItem();
2542 int firstDiff = GetFirstDifferentItem();
2544 // Check if different files were found and
2545 // there is different item before focused item
2546 if ((firstDiff > -1) && (firstDiff < focused))
2547 pCmdUI->Enable(TRUE);
2549 pCmdUI->Enable(FALSE);
2552 void CDirView::OnCurdiff()
2554 const int count = m_pList->GetItemCount();
2556 int i = GetFirstSelectedInd();
2560 // No selection - no diff to go
2564 while (i < count && found == false)
2566 selected = m_pList->GetItemState(i, LVIS_SELECTED);
2567 focused = m_pList->GetItemState(i, LVIS_FOCUSED);
2569 if (selected == LVIS_SELECTED && focused == LVIS_FOCUSED)
2571 m_pList->EnsureVisible(i, FALSE);
2578 void CDirView::OnUpdateCurdiff(CCmdUI* pCmdUI)
2580 int selection = GetFirstSelectedInd();
2582 pCmdUI->Enable(TRUE);
2584 pCmdUI->Enable(FALSE);
2587 int CDirView::GetFocusedItem()
2589 return m_pList->GetNextItem(-1, LVNI_FOCUSED);
2592 int CDirView::GetFirstDifferentItem()
2594 if (!m_bNeedSearchFirstDiffItem)
2595 return m_firstDiffItem;
2597 const int count = m_pList->GetItemCount();
2602 while (i < count && found == false)
2604 const DIFFITEM &di = GetItemAt(i);
2605 if (IsItemNavigableDiff(di))
2613 m_firstDiffItem = foundInd;
2614 m_bNeedSearchFirstDiffItem = false;
2616 return m_firstDiffItem;
2619 int CDirView::GetLastDifferentItem()
2621 if (!m_bNeedSearchLastDiffItem)
2622 return m_lastDiffItem;
2624 const int count = m_pList->GetItemCount();
2629 while (i > 0 && found == false)
2631 const DIFFITEM &di = GetItemAt(i);
2632 if (IsItemNavigableDiff(di))
2640 m_lastDiffItem = foundInd;
2641 m_bNeedSearchLastDiffItem = false;
2643 return m_lastDiffItem;
2646 // When navigating differences, do we stop at this one ?
2647 bool CDirView::IsItemNavigableDiff(const DIFFITEM & di) const
2649 // Not a valid diffitem, one of special items (e.g "..")
2650 if (di.diffcode.diffcode == 0)
2652 if (di.diffcode.isResultFiltered() || di.diffcode.isResultError())
2654 if (!di.diffcode.isResultDiff() && !di.diffcode.isSideFirstOnly() &&
2655 !di.diffcode.isSideSecondOnly()) /* FIXME: 3-pane */
2661 * @brief Move focus to specified item (and selection if multiple items not selected)
2663 * Moves the focus from item [currentInd] to item [i]
2664 * Additionally, if there are not multiple items selected,
2665 * deselects item [currentInd] and selects item [i]
2667 void CDirView::MoveFocus(int currentInd, int i, int selCount)
2671 // Not multiple items selected, so bring selection with us
2672 m_pList->SetItemState(currentInd, 0, LVIS_SELECTED);
2673 m_pList->SetItemState(currentInd, 0, LVIS_FOCUSED);
2674 m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2677 // Move focus to specified item
2678 // (this automatically defocuses old item)
2679 m_pList->SetItemState(i, LVIS_FOCUSED, LVIS_FOCUSED);
2680 m_pList->EnsureVisible(i, FALSE);
2683 void CDirView::OnUpdateSave(CCmdUI* pCmdUI)
2685 pCmdUI->Enable(FALSE);
2688 CDirFrame * CDirView::GetParentFrame()
2690 // can't verify cast without introducing more coupling
2691 // (CDirView doesn't include DirFrame.h)
2692 return (CDirFrame *)CListView::GetParentFrame();
2695 void CDirView::OnRefresh()
2697 m_pSavedTreeState.reset(SaveTreeState());
2698 GetDocument()->Rescan();
2701 BOOL CDirView::PreTranslateMessage(MSG* pMsg)
2703 // Handle special shortcuts here
2704 if (pMsg->message == WM_KEYDOWN)
2706 if (false == IsLabelEdit())
2708 // Check if we got 'ESC pressed' -message
2709 if (pMsg->wParam == VK_ESCAPE)
2711 if (m_pCmpProgressBar)
2713 OnBnClickedComparisonStop();
2719 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
2723 // Check if we got 'DEL pressed' -message
2724 if (pMsg->wParam == VK_DELETE)
2726 AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_MERGE_DELETE);
2729 int sel = GetFocusedItem();
2730 // Check if we got 'Backspace pressed' -message
2731 if (pMsg->wParam == VK_BACK)
2733 if (!GetDocument()->GetRecursive())
2735 OpenParentDirectory();
2738 else if (m_bTreeMode && sel >= 0)
2740 const DIFFITEM& di = GetDiffItem(sel);
2741 if (di.parent != NULL)
2743 int i = GetItemIndex((uintptr_t)di.parent);
2745 MoveFocus(sel, i, GetSelectedCount());
2751 DIFFITEM& dip = this->GetDiffItemRef(sel);
2752 if (pMsg->wParam == VK_LEFT)
2754 if (m_bTreeMode && GetDocument()->GetRecursive() && (!(dip.customFlags1 & ViewCustomFlags::EXPANDED) || !dip.HasChildren()))
2755 PostMessage(WM_KEYDOWN, VK_BACK);
2757 CollapseSubdir(sel);
2760 if (pMsg->wParam == VK_SUBTRACT)
2762 CollapseSubdir(sel);
2765 if (pMsg->wParam == VK_RIGHT)
2767 if (m_bTreeMode && GetDocument()->GetRecursive() && dip.customFlags1 & ViewCustomFlags::EXPANDED && dip.HasChildren())
2768 PostMessage(WM_KEYDOWN, VK_DOWN);
2773 if (pMsg->wParam == VK_ADD)
2778 if (pMsg->wParam == VK_MULTIPLY)
2780 ExpandSubdir(sel, true);
2787 // ESC doesn't close window when user is renaming an item.
2788 if (pMsg->wParam == VK_ESCAPE)
2790 m_bUserCancelEdit = TRUE;
2792 // The edit control send LVN_ENDLABELEDIT when it loses focus,
2793 // so we use it to cancel the rename action.
2794 m_pList->SetFocus();
2796 // Stop the ESC before it reach the main frame which might
2797 // cause a program termination.
2802 return CListView::PreTranslateMessage(pMsg);
2805 void CDirView::OnUpdateRefresh(CCmdUI* pCmdUI)
2807 UINT threadState = GetDocument()->m_diffThread.GetThreadState();
2808 pCmdUI->Enable(threadState != CDiffThread::THREAD_COMPARING);
2812 * @brief Called when compare thread asks UI update.
2813 * @note Currently thread asks update after compare is ready
2816 LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
2818 UNREFERENCED_PARAMETER(lParam);
2820 CDirDoc * pDoc = GetDocument();
2823 if (wParam == CDiffThread::EVENT_COMPARE_COMPLETED)
2825 // Close and destroy the dialog after compare
2826 if (m_pCmpProgressBar)
2827 GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), FALSE, FALSE);
2828 m_pCmpProgressBar.reset();
2830 pDoc->CompareReady();
2834 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
2839 // If compare took more than TimeToSignalCompare seconds, notify user
2840 clock_t elapsed = clock() - m_compareStart;
2841 GetParentFrame()->SetMessageText(
2842 string_format(theApp.LoadString(IDS_ELAPSED_TIME).c_str(), elapsed).c_str()
2844 if (elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
2846 GetMainFrame()->StartFlashing();
2848 else if (wParam == CDiffThread::EVENT_COMPARE_PROGRESSED)
2850 InvalidateRect(NULL, FALSE);
2852 else if (wParam == CDiffThread::EVENT_COLLECT_COMPLETED)
2854 if (m_pSavedTreeState)
2856 RestoreTreeState(m_pSavedTreeState.get());
2857 m_pSavedTreeState.reset();
2862 if (m_bExpandSubdirs)
2863 OnViewExpandAllSubdirs();
2869 return 0; // return value unused
2873 BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
2875 NMHDR * hdr = reinterpret_cast<NMHDR *>(lParam);
2876 if (hdr->code == HDN_ENDDRAG)
2877 return OnHeaderEndDrag((LPNMHEADER)hdr, pResult);
2878 if (hdr->code == HDN_BEGINDRAG)
2879 return OnHeaderBeginDrag((LPNMHEADER)hdr, pResult);
2881 return CListView::OnNotify(wParam, lParam, pResult);
2884 BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
2886 if (uMsg == WM_NOTIFY)
2888 NMHDR *pNMHDR = (NMHDR *)lParam;
2889 switch (pNMHDR->code)
2891 case LVN_GETDISPINFO:
2892 ReflectGetdispinfo((NMLVDISPINFO *)lParam);
2894 case LVN_GETINFOTIPW:
2895 case LVN_GETINFOTIPA:
2899 return CListView::OnChildNotify(uMsg, wParam, lParam, pResult);
2903 * @brief User is starting to drag a column header
2905 BOOL CDirView::OnHeaderBeginDrag(LPNMHEADER hdr, LRESULT* pResult)
2907 // save column widths before user reorders them
2908 // so we can reload them on the end drag
2914 * @brief User just finished dragging a column header
2916 BOOL CDirView::OnHeaderEndDrag(LPNMHEADER hdr, LRESULT* pResult)
2918 int src = hdr->iItem;
2919 int dest = hdr->pitem->iOrder;
2920 bool allowDrop = TRUE;
2921 *pResult = !allowDrop;
2922 if (allowDrop && src != dest && dest != -1)
2924 MoveColumn(src, dest);
2930 * @brief Remove any windows reordering of columns
2932 void CDirView::FixReordering()
2935 lvcol.mask = LVCF_ORDER;
2940 for (int i = 0; i < m_numcols; ++i)
2943 GetListCtrl().SetColumn(i, &lvcol);
2947 /** @brief Add columns to display, loading width & order from registry. */
2948 void CDirView::LoadColumnHeaderItems()
2950 bool dummyflag = false;
2952 CHeaderCtrl * h = m_pList->GetHeaderCtrl();
2953 if (h->GetItemCount())
2956 while (m_pList->GetHeaderCtrl()->GetItemCount() > 1)
2957 m_pList->DeleteColumn(1);
2960 for (int i = 0; i < m_dispcols; ++i)
2963 lvc.mask = LVCF_FMT + LVCF_SUBITEM + LVCF_TEXT;
2964 lvc.fmt = LVCFMT_LEFT;
2966 lvc.pszText = _T("text");
2968 m_pList->InsertColumn(i, &lvc);
2971 m_pList->DeleteColumn(1);
2975 /// Update all column widths (from registry to screen)
2976 // Necessary when user reorders columns
2977 void CDirView::SetColumnWidths()
2979 for (int i = 0; i < m_numcols; ++i)
2981 int phy = ColLogToPhys(i);
2984 String sWidthKey = GetColRegValueNameBase(i) + _T("_Width");
2985 int w = max(10, theApp.GetProfileInt(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), sWidthKey.c_str(), DefColumnWidth));
2986 GetListCtrl().SetColumnWidth(m_colorder[i], w);
2991 void CDirView::SetFont(const LOGFONT & lf)
2993 m_font.DeleteObject();
2994 m_font.CreateFontIndirect(&lf);
2995 CWnd::SetFont(&m_font);
2998 /** @brief store current column widths into registry */
2999 void CDirView::SaveColumnWidths()
3001 for (int i = 0; i < m_numcols; i++)
3003 int phy = ColLogToPhys(i);
3006 String sWidthKey = GetColRegValueNameBase(i) + _T("_Width");
3007 int w = GetListCtrl().GetColumnWidth(phy);
3008 theApp.WriteProfileInt(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), sWidthKey.c_str(), w);
3013 /** @brief Fire off a resort of the data, to take place when things stabilize. */
3014 void CDirView::InitiateSort()
3016 PostMessage(WM_TIMER, COLUMN_REORDER);
3019 void CDirView::OnTimer(UINT_PTR nIDEvent)
3021 if (nIDEvent == COLUMN_REORDER)
3023 // Remove the windows reordering, as we're doing it ourselves
3025 // Now redraw screen
3026 UpdateColumnNames();
3030 else if (nIDEvent == STATUSBAR_UPDATE)
3033 int items = GetSelectedCount();
3036 msg = theApp.LoadString(IDS_STATUS_SELITEM1);
3039 String num = string_format(_T("%d"), items);
3040 msg = LangFormatString1(IDS_STATUS_SELITEMS, num.c_str());
3042 GetParentFrame()->SetStatus(msg.c_str());
3045 CListView::OnTimer(nIDEvent);
3049 * @brief Change left-side readonly-status
3051 void CDirView::OnLeftReadOnly()
3053 bool bReadOnly = GetDocument()->GetReadOnly(0);
3054 GetDocument()->SetReadOnly(0, !bReadOnly);
3058 * @brief Update left-readonly menu item
3060 void CDirView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
3062 bool bReadOnly = GetDocument()->GetReadOnly(0);
3063 pCmdUI->Enable(TRUE);
3064 pCmdUI->SetCheck(bReadOnly);
3068 * @brief Change middle-side readonly-status
3070 void CDirView::OnMiddleReadOnly()
3072 bool bReadOnly = GetDocument()->GetReadOnly(1);
3073 GetDocument()->SetReadOnly(1, !bReadOnly);
3077 * @brief Update middle-readonly menu item
3079 void CDirView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
3081 bool bReadOnly = GetDocument()->GetReadOnly(1);
3082 pCmdUI->Enable(GetDocument()->m_nDirs > 2);
3083 pCmdUI->SetCheck(bReadOnly && GetDocument()->m_nDirs > 2);
3087 * @brief Change right-side readonly-status
3089 void CDirView::OnRightReadOnly()
3091 bool bReadOnly = GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1);
3092 GetDocument()->SetReadOnly(GetDocument()->m_nDirs - 1, !bReadOnly);
3096 * @brief Update right-side readonly menuitem
3098 void CDirView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
3100 bool bReadOnly = GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1);
3101 pCmdUI->Enable(TRUE);
3102 pCmdUI->SetCheck(bReadOnly);
3106 * @brief Update left-side readonly statusbar item
3108 void CDirView::OnUpdateStatusLeftRO(CCmdUI* pCmdUI)
3110 bool bROLeft = GetDocument()->GetReadOnly(0);
3111 pCmdUI->Enable(bROLeft);
3115 * @brief Update middle readonly statusbar item
3117 void CDirView::OnUpdateStatusMiddleRO(CCmdUI* pCmdUI)
3119 bool bROMiddle = GetDocument()->GetReadOnly(1);
3120 pCmdUI->Enable(bROMiddle && GetDocument()->m_nDirs > 2);
3124 * @brief Update right-side readonly statusbar item
3126 void CDirView::OnUpdateStatusRightRO(CCmdUI* pCmdUI)
3128 bool bRORight = GetDocument()->GetReadOnly(GetDocument()->m_nDirs - 1);
3129 pCmdUI->Enable(bRORight);
3133 * @brief Open dialog to customize dirview columns
3135 void CDirView::OnCustomizeColumns()
3137 // Located in DirViewColHandler.cpp
3142 void CDirView::OnCtxtOpenWithUnpacker()
3145 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
3148 // let the user choose a handler
3149 CSelectUnpackerDlg dlg(GetDiffItem(sel).diffFileInfo[0].filename.get().c_str(), this);
3150 // create now a new infoUnpacker to initialize the manual/automatic flag
3151 PackingInfo infoUnpacker(PLUGIN_AUTO);
3152 dlg.SetInitialInfoHandler(&infoUnpacker);
3154 if (dlg.DoModal() == IDOK)
3156 infoUnpacker = dlg.GetInfoHandler();
3157 OpenSelection(SELECTIONTYPE_NORMAL, &infoUnpacker);
3163 void CDirView::OnUpdateCtxtOpenWithUnpacker(CCmdUI* pCmdUI)
3165 if (!GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
3167 pCmdUI->Enable(FALSE);
3171 // we need one selected file, existing on both side
3172 if (m_pList->GetSelectedCount() != 1)
3173 pCmdUI->Enable(FALSE);
3177 sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
3178 const DIFFITEM& di = GetDiffItem(sel);
3179 if (IsItemDeletableOnBoth(di))
3180 pCmdUI->Enable(TRUE);
3182 pCmdUI->Enable(FALSE);
3187 * @brief Fill string list with current dirview column registry key names
3189 void CDirView::GetCurrentColRegKeys(std::vector<String>& colKeys)
3191 int nphyscols = GetListCtrl().GetHeaderCtrl()->GetItemCount();
3192 for (int col = 0; col < nphyscols; ++col)
3194 int logcol = ColPhysToLog(col);
3195 colKeys.push_back(GetColRegValueNameBase(logcol));
3199 struct FileCmpReport: public IFileCmpReport
3201 FileCmpReport(CDirView *pDirView) : m_pDirView(pDirView) {}
3202 bool operator()(REPORT_TYPE nReportType, IListCtrl *pList, int nIndex, const String &sDestDir, String &sLinkPath)
3204 const DIFFITEM &di = m_pDirView->GetDiffItem(nIndex);
3206 String sLinkFullPath = paths_ConcatPath(m_pDirView->GetDocument()->GetLeftBasePath(), di.diffFileInfo[0].GetFile());
3208 if (di.diffcode.isDirectory() || !m_pDirView->IsItemNavigableDiff(di) || IsArchiveFile(sLinkFullPath))
3214 sLinkPath = di.diffFileInfo[0].GetFile();
3216 string_replace(sLinkPath, _T("\\"), _T("_"));
3217 sLinkPath += _T(".html");
3219 m_pDirView->MoveFocus(m_pDirView->GetFirstSelectedInd(), nIndex, m_pDirView->GetSelectedCount());
3221 m_pDirView->OpenSelection();
3222 CFrameWnd * pFrame = GetMainFrame()->GetActiveFrame();
3223 IMergeDoc * pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
3225 pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
3229 pMergeDoc->GenerateReport(paths_ConcatPath(sDestDir, sLinkPath).c_str());
3230 pMergeDoc->CloseNow();
3234 while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
3235 if (!AfxGetApp()->PumpMessage())
3237 GetMainFrame()->OnUpdateFrameTitle(FALSE);
3243 CDirView *m_pDirView;
3247 * @brief Generate report from dir compare results.
3249 void CDirView::OnToolsGenerateReport()
3251 CDirDoc *pDoc = GetDocument();
3252 if (!pDoc->HasDiffs())
3254 // No items, no report
3258 // Make list of registry keys for columns
3259 // (needed for XML reports)
3260 std::vector<String> colKeys;
3261 GetCurrentColRegKeys(colKeys);
3263 DirCmpReport report(colKeys);
3264 FileCmpReport freport(this);
3265 IListCtrlImpl list(m_pList->m_hWnd);
3266 report.SetList(&list);
3268 for (int i = 0; i < pDoc->m_nDirs; i++)
3269 paths.SetPath(i, pDoc->GetBasePath(i));
3271 // If inside archive, convert paths
3272 if (pDoc->IsArchiveFolders())
3274 for (int i = 0; i < paths.GetSize(); i++)
3275 pDoc->ApplyDisplayRoot(i, paths[i]);
3278 report.SetRootPaths(paths);
3279 report.SetColumns(m_dispcols);
3280 report.SetFileCmpReport(&freport);
3282 if (report.GenerateReport(errStr))
3285 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
3287 ResMsgBox1(IDS_REPORT_ERROR, errStr.c_str(), MB_OK | MB_ICONSTOP);
3292 * @brief Add special items for non-recursive compare
3293 * to directory view.
3295 * Currently only special item is ".." for browsing to
3297 * @return number of items added to view
3299 int CDirView::AddSpecialItems()
3301 CDirDoc *pDoc = GetDocument();
3303 bool bEnable = true;
3304 PathContext pathsParent;
3305 switch (pDoc->AllowUpwardDirectory(pathsParent))
3307 case CDirDoc::AllowUpwardDirectory::No:
3311 AddParentFolderItem(bEnable);
3314 case CDirDoc::AllowUpwardDirectory::Never:
3321 * @brief Add "Parent folder" ("..") item to directory view
3323 void CDirView::AddParentFolderItem(bool bEnable)
3325 AddNewItem(0, SPECIAL_ITEM_POS, bEnable ? DIFFIMG_DIRUP : DIFFIMG_DIRUP_DISABLE, 0);
3329 * @brief Zip selected files from left side.
3331 void CDirView::OnCtxtDirZipLeft()
3333 if (!HasZipSupport())
3335 LangMessageBox(IDS_NO_ZIP_SUPPORT, MB_ICONINFORMATION);
3342 | DirItemEnumerator::Left
3343 ).CompressArchive();
3347 * @brief Zip selected files from right side.
3349 void CDirView::OnCtxtDirZipRight()
3351 if (!HasZipSupport())
3353 LangMessageBox(IDS_NO_ZIP_SUPPORT, MB_ICONINFORMATION);
3360 | DirItemEnumerator::Right
3361 ).CompressArchive();
3365 * @brief Zip selected files from both sides, using original/altered format.
3367 void CDirView::OnCtxtDirZipBoth()
3369 if (!HasZipSupport())
3371 LangMessageBox(IDS_NO_ZIP_SUPPORT, MB_ICONINFORMATION);
3378 | DirItemEnumerator::Original
3379 | DirItemEnumerator::Altered
3380 | DirItemEnumerator::BalanceFolders
3381 ).CompressArchive();
3385 * @brief Zip selected diffs from both sides, using original/altered format.
3387 void CDirView::OnCtxtDirZipBothDiffsOnly()
3389 if (!HasZipSupport())
3391 LangMessageBox(IDS_NO_ZIP_SUPPORT, MB_ICONINFORMATION);
3398 | DirItemEnumerator::Original
3399 | DirItemEnumerator::Altered
3400 | DirItemEnumerator::BalanceFolders
3401 | DirItemEnumerator::DiffsOnly
3402 ).CompressArchive();
3405 void CDirView::ShowShellContextMenu(SIDE_TYPE stype)
3407 CShellContextMenu *pContextMenu = NULL;
3411 if (!m_pShellContextMenuLeft)
3412 m_pShellContextMenuLeft.reset(new CShellContextMenu(LeftCmdFirst, LeftCmdLast));
3413 pContextMenu = m_pShellContextMenuLeft.get();
3416 if (!m_pShellContextMenuMiddle)
3417 m_pShellContextMenuMiddle.reset(new CShellContextMenu(MiddleCmdFirst, MiddleCmdLast));
3418 pContextMenu = m_pShellContextMenuMiddle.get();
3421 if (!m_pShellContextMenuRight)
3422 m_pShellContextMenuRight.reset(new CShellContextMenu(RightCmdFirst, RightCmdLast));
3423 pContextMenu = m_pShellContextMenuRight.get();
3426 if (pContextMenu && ListShellContextMenu(stype))
3429 GetCursorPos(&point);
3430 HWND hWnd = GetSafeHwnd();
3431 CFrameWnd *pFrame = GetTopLevelFrame();
3432 ASSERT(pFrame != NULL);
3433 BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
3434 pFrame->m_bAutoMenuEnable = FALSE;
3435 BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, NULL);
3437 pContextMenu->InvokeCommand(nCmd, hWnd);
3438 pContextMenu->ReleaseShellContextMenu();
3439 pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
3443 template <CDirView::SIDE_TYPE stype>
3444 void CDirView::OnCtxtDirShellContextMenu()
3446 ShowShellContextMenu(stype);
3450 * @brief Select all visible items in dir compare
3452 void CDirView::OnSelectAll()
3454 // While the user is renaming an item, select all the edited text.
3455 CEdit *pEdit = m_pList->GetEditControl();
3458 pEdit->SetSel(pEdit->GetWindowTextLength());
3462 int selCount = m_pList->GetItemCount();
3464 for (int i = 0; i < selCount; i++)
3466 // Don't select special items (SPECIAL_ITEM_POS)
3467 uintptr_t diffpos = GetItemKey(i);
3468 if (diffpos != SPECIAL_ITEM_POS)
3469 m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
3475 * @brief Update "Select All" item
3477 void CDirView::OnUpdateSelectAll(CCmdUI* pCmdUI)
3479 bool bEnable = (!IsLabelEdit()) || (m_pList->GetItemCount() > 0);
3480 pCmdUI->Enable(bEnable);
3484 * @brief Handle clicks in plugin context view in list
3486 void CDirView::OnPluginPredifferMode(UINT nID)
3491 case ID_PREDIFF_MANUAL:
3492 newsetting = PLUGIN_MANUAL;
3494 case ID_PREDIFF_AUTO:
3495 newsetting = PLUGIN_AUTO;
3498 ApplyPluginPrediffSetting(newsetting);
3502 * @brief Updates just before displaying plugin context view in list
3504 void CDirView::OnUpdatePluginPredifferMode(CCmdUI* pCmdUI)
3506 // 2004-04-03, Perry
3507 // CMainFrame::OnUpdatePluginUnpackMode handles this for global unpacking
3508 // and is the template to copy, but here, this is a bit tricky
3509 // as a group of files may be selected
3510 // and they may not all have the same setting
3511 // so I'm not trying this right now
3513 if (GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
3514 pCmdUI->Enable(TRUE);
3516 pCmdUI->Enable(FALSE);
3518 BCMenu *pPopup = (BCMenu*) pCmdUI->m_pSubMenu;
3522 CDirDoc *pDoc = GetDocument();
3524 int nPredifferYes = 0;
3525 int nPredifferNo = 0;
3527 while ((i = m_pList->GetNextItem(i, LVNI_SELECTED)) != -1)
3529 const DIFFITEM& di = GetDiffItem(i);
3530 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
3533 // note the prediffer flag for 'files present on both sides and not skipped'
3534 if (!di.diffcode.isDirectory() && !di.diffcode.isBin() &&
3535 !di.diffcode.isSideFirstOnly() && !di.diffcode.isSideSecondOnly() &&
3536 !di.diffcode.isResultFiltered())
3538 String leftPath = paths_ConcatPath(di.getFilepath(0, pDoc->GetBasePath(0)),
3539 di.diffFileInfo[0].filename);
3540 String rightPath = paths_ConcatPath(di.getFilepath(1, pDoc->GetBasePath(1)),
3541 di.diffFileInfo[1].filename);
3542 String filteredFilenames = string_format(_T("%s|%s"), leftPath.c_str(), rightPath.c_str());
3543 PackingInfo * unpacker;
3544 PrediffingInfo * prediffer;
3545 GetDocument()->FetchPluginInfos(filteredFilenames, &unpacker, &prediffer);
3546 if (prediffer->bToBeScanned == 1 || prediffer->pluginName.empty() == false)
3553 CheckContextMenu(pPopup, ID_PREDIFF_AUTO, (nPredifferYes > 0));
3554 CheckContextMenu(pPopup, ID_PREDIFF_MANUAL, (nPredifferNo > 0));
3558 * @brief Resets column widths to defaults.
3560 void CDirView::ResetColumnWidths()
3562 for (int i = 0; i < m_numcols; i++)
3564 int phy = ColLogToPhys(i);
3567 String sWidthKey = GetColRegValueNameBase(i) + _T("_Width");
3568 theApp.WriteProfileInt(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), sWidthKey.c_str(), DefColumnWidth);
3574 * @brief Refresh cached options.
3576 void CDirView::RefreshOptions()
3578 m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
3579 m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
3580 Options::DiffColors::Load(m_cachedColors);
3584 * @brief Copy selected item left side paths (containing filenames) to clipboard.
3586 void CDirView::OnCopyLeftPathnames()
3591 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
3593 const DIFFITEM& di = GetDiffItem(sel);
3594 if (!di.diffcode.isSideSecondOnly())
3596 strPaths += di.getFilepath(0, GetDocument()->GetBasePath(0));
3597 // If item is a folder then subfolder (relative to base folder)
3598 // is in filename member.
3599 strPaths = paths_ConcatPath(strPaths, di.diffFileInfo[0].filename);
3600 strPaths += _T("\r\n");
3603 PutToClipboard(strPaths, AfxGetMainWnd()->GetSafeHwnd());
3607 * @brief Copy selected item right side paths (containing filenames) to clipboard.
3609 void CDirView::OnCopyRightPathnames()
3611 CDirDoc *pDoc = GetDocument();
3615 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
3617 const DIFFITEM& di = GetDiffItem(sel);
3618 if (!di.diffcode.isSideFirstOnly())
3620 strPaths += di.getFilepath(1, pDoc->GetRightBasePath());
3621 // If item is a folder then subfolder (relative to base folder)
3622 // is in filename member.
3623 strPaths = paths_ConcatPath(strPaths, di.diffFileInfo[1].filename);
3624 strPaths += _T("\r\n");
3627 PutToClipboard(strPaths, AfxGetMainWnd()->GetSafeHwnd());
3631 * @brief Copy selected item both side paths (containing filenames) to clipboard.
3633 void CDirView::OnCopyBothPathnames()
3635 CDirDoc * pDoc = GetDocument();
3639 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
3641 const DIFFITEM& di = GetDiffItem(sel);
3642 if (!di.diffcode.isSideSecondOnly())
3644 strPaths += di.getFilepath(0, pDoc->GetBasePath(0));
3645 // If item is a folder then subfolder (relative to base folder)
3646 // is in filename member.
3647 strPaths = paths_ConcatPath(strPaths, di.diffFileInfo[0].filename);
3648 strPaths += _T("\r\n");
3651 if (!di.diffcode.isSideFirstOnly())
3653 strPaths += di.getFilepath(1, pDoc->GetRightBasePath());
3654 // If item is a folder then subfolder (relative to base folder)
3655 // is in filename member.
3656 strPaths = paths_ConcatPath(strPaths, di.diffFileInfo[1].filename);
3657 strPaths += _T("\r\n");
3660 PutToClipboard(strPaths, AfxGetMainWnd()->GetSafeHwnd());
3664 * @brief Copy selected item filenames to clipboard.
3666 void CDirView::OnCopyFilenames()
3671 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
3673 const DIFFITEM& di = GetDiffItem(sel);
3674 if (!di.diffcode.isDirectory())
3676 strPaths += di.diffFileInfo[0].filename;
3677 strPaths += _T("\r\n");
3680 PutToClipboard(strPaths, AfxGetMainWnd()->GetSafeHwnd());
3684 * @brief Enable/Disable dirview Copy Filenames context menu item.
3686 void CDirView::OnUpdateCopyFilenames(CCmdUI* pCmdUI)
3688 DoUpdateCopyFilenames(pCmdUI);
3692 * @brief Copy selected item left side to clipboard.
3694 void CDirView::OnCopyLeftToClipboard()
3696 DoCopyItemsToClipboard(1);
3700 * @brief Copy selected item right side to clipboard.
3702 void CDirView::OnCopyRightToClipboard()
3704 DoCopyItemsToClipboard(2);
3708 * @brief Copy selected item both side to clipboard.
3710 void CDirView::OnCopyBothToClipboard()
3712 DoCopyItemsToClipboard(3);
3716 * @brief Rename a selected item on both sides.
3719 void CDirView::OnItemRename()
3721 ASSERT(1 == m_pList->GetSelectedCount());
3722 int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
3723 ASSERT(-1 != nSelItem);
3724 m_pList->EditLabel(nSelItem);
3728 * @brief Enable/Disable dirview Rename context menu item.
3731 void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
3733 BOOL bEnabled = (1 == m_pList->GetSelectedCount());
3734 pCmdUI->Enable(bEnabled && !IsItemSelectedSpecial());
3738 * @brief hide selected item filenames (removes them from the ListView)
3740 void CDirView::OnHideFilenames()
3742 CDirDoc *pDoc = GetDocument();
3744 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
3745 while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1)
3747 uintptr_t pos = GetItemKey(sel);
3748 if (pos == (uintptr_t) SPECIAL_ITEM_POS)
3750 pDoc->SetItemViewFlag(pos, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
3751 const DIFFITEM &di = GetDiffItem(sel);
3752 if (m_bTreeMode && di.diffcode.isDirectory())
3754 int count = m_pList->GetItemCount();
3755 for (int i = sel + 1; i < count; i++)
3757 const DIFFITEM &dic = GetDiffItem(i);
3758 if (!dic.IsAncestor(&di))
3760 m_pList->DeleteItem(i--);
3764 m_pList->DeleteItem(sel--);
3767 m_pList->SetRedraw(TRUE); // Turn updating back on
3771 * @brief update menu item
3773 void CDirView::OnUpdateHideFilenames(CCmdUI* pCmdUI)
3775 pCmdUI->Enable(m_pList->GetSelectedCount() != 0);
3778 /// User chose (context menu) Move left to...
3779 void CDirView::OnCtxtDirMoveLeftTo()
3784 /// User chose (context menu) Move right to...
3785 void CDirView::OnCtxtDirMoveRightTo()
3791 * @brief Update "Move | Right to..." item
3793 void CDirView::OnUpdateCtxtDirMoveRightTo(CCmdUI* pCmdUI)
3795 // Because move deletes original item, origin side
3796 // cannot be read-only
3797 if (GetDocument()->GetReadOnly(FALSE))
3798 pCmdUI->Enable(FALSE);
3801 DoUpdateCtxtDirMoveRightTo(pCmdUI);
3806 * @brief Update "Move | Left to..." item
3808 void CDirView::OnUpdateCtxtDirMoveLeftTo(CCmdUI* pCmdUI)
3810 // Because move deletes original item, origin side
3811 // cannot be read-only
3812 if (GetDocument()->GetReadOnly(TRUE))
3813 pCmdUI->Enable(FALSE);
3816 DoUpdateCtxtDirMoveLeftTo(pCmdUI);
3821 * @brief Update title after window is resized.
3823 void CDirView::OnSize(UINT nType, int cx, int cy)
3825 CListView::OnSize(nType, cx, cy);
3826 GetDocument()->SetTitle(NULL);
3830 * @brief Called when user selects 'Delete' from 'Merge' menu.
3832 void CDirView::OnDelete()
3838 * @brief Enables/disables 'Delete' item in 'Merge' menu.
3840 void CDirView::OnUpdateDelete(CCmdUI* pCmdUI)
3842 DoUpdateDelete(pCmdUI);
3846 * @brief Called when item state is changed.
3848 * Show count of selected items in statusbar.
3850 void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
3852 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
3854 // If item's selected state changed
3855 if ((pNMListView->uOldState & LVIS_SELECTED) !=
3856 (pNMListView->uNewState & LVIS_SELECTED))
3858 int items = GetSelectedCount();
3859 String num = string_format(_T("%d"), items);
3860 String msg = LangFormatString1(items == 1 ? IDS_STATUS_SELITEM1 : IDS_STATUS_SELITEMS, num.c_str());
3861 GetParentFrame()->SetStatus(msg.c_str());
3867 * @brief Called before user start to item label edit.
3869 * Disable label edit if initiated from a user double-click.
3871 afx_msg void CDirView::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
3873 *pResult = IsItemSelectedSpecial();
3875 // If label edit is allowed.
3876 if (FALSE == *pResult)
3878 const NMLVDISPINFO *pdi = (NMLVDISPINFO*)pNMHDR;
3879 ASSERT(pdi != NULL);
3881 // Locate the edit box on the right column in case the user changed the
3883 const int nColPos = ColLogToPhys(0);
3885 // Get text from the "File Name" column.
3886 CString sText = m_pList->GetItemText(pdi->item.iItem, nColPos);
3887 ASSERT(!sText.IsEmpty());
3889 // Keep only left file name (separated by '|'). This form occurs
3890 // when two files exists with same name but not in same case.
3891 int nPos = sText.Find('|');
3894 sText = sText.Left(nPos);
3897 // Set the edit control with the updated text.
3898 CEdit *pEdit = m_pList->GetEditControl();
3899 ASSERT(NULL != pEdit);
3900 pEdit->SetWindowText(sText);
3902 m_bUserCancelEdit = FALSE;
3907 * @brief Called when user done with item label edit.
3910 afx_msg void CDirView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
3914 // We can't use the normal condition of pszText==NULL to know if the
3915 // user cancels editing when file names had different case (e.g.
3916 // "file.txt|FILE.txt"). The edit text was changed to "file.txt" and
3917 // if the user accept it as the new file name, pszText is NULL.
3919 if (TRUE != m_bUserCancelEdit)
3921 CEdit *pEdit = m_pList->GetEditControl();
3922 ASSERT(NULL != pEdit);
3925 pEdit->GetWindowText(sText);
3927 if (!sText.IsEmpty())
3929 *pResult = DoItemRename(String(sText));
3935 * @brief Called when item is marked for rescan.
3936 * This function marks selected items for rescan and rescans them.
3938 void CDirView::OnMarkedRescan()
3940 UINT items = MarkSelectedForRescan();
3943 m_pSavedTreeState.reset(SaveTreeState());
3944 GetDocument()->Rescan();
3949 * @brief Called to update the item count in the status bar
3951 void CDirView::OnUpdateStatusNum(CCmdUI* pCmdUI)
3953 String s; // text to display
3955 int count = m_pList->GetItemCount();
3956 int focusItem = GetFocusedItem();
3958 if (focusItem == -1)
3960 // No item has focus
3961 String sCnt = string_format(_T("%ld"), count);
3963 s = LangFormatString1(IDS_DIRVIEW_STATUS_FMT_NOFOCUS, sCnt.c_str());
3967 // Don't show number to special items
3968 uintptr_t pos = GetItemKey(focusItem);
3969 if (pos != SPECIAL_ITEM_POS)
3971 // If compare is non-recursive reduce special items count
3972 bool bRecursive = GetDocument()->GetRecursive();
3978 String sIdx = string_format(_T("%ld"), focusItem + 1);
3979 String sCnt = string_format(_T("%ld"), count);
3981 s = LangFormatString2(IDS_DIRVIEW_STATUS_FMT_FOCUS, sIdx.c_str(), sCnt.c_str());
3984 pCmdUI->SetText(s.c_str());
3988 * @brief Show all hidden items.
3990 void CDirView::OnViewShowHiddenItems()
3992 GetDocument()->SetItemViewFlag(ViewCustomFlags::VISIBLE, ViewCustomFlags::VISIBILITY);
3998 * @brief Enable/Disable 'Show hidden items' menuitem.
4000 void CDirView::OnUpdateViewShowHiddenItems(CCmdUI* pCmdUI)
4002 pCmdUI->Enable(m_nHiddenItems > 0);
4006 * @brief Toggle Tree Mode
4008 void CDirView::OnViewTreeMode()
4010 m_bTreeMode = !m_bTreeMode;
4011 GetOptionsMgr()->SaveOption(OPT_TREE_MODE, m_bTreeMode); // reverse
4016 * @brief Check/Uncheck 'Tree Mode' menuitem.
4018 void CDirView::OnUpdateViewTreeMode(CCmdUI* pCmdUI)
4020 pCmdUI->SetCheck(m_bTreeMode);
4021 pCmdUI->Enable(GetDocument()->GetRecursive());
4025 * @brief Expand all subfolders
4027 void CDirView::OnViewExpandAllSubdirs()
4029 CDirDoc *pDoc = GetDocument();
4030 CDiffContext &ctxt = (CDiffContext &)pDoc->GetDiffContext();
4031 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
4034 DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
4035 di.customFlags1 |= ViewCustomFlags::EXPANDED;
4041 * @brief Update "Expand All Subfolders" item
4043 void CDirView::OnUpdateViewExpandAllSubdirs(CCmdUI* pCmdUI)
4045 pCmdUI->Enable(m_bTreeMode && GetDocument()->GetRecursive());
4049 * @brief Collapse all subfolders
4051 void CDirView::OnViewCollapseAllSubdirs()
4053 CDirDoc *pDoc = GetDocument();
4054 CDiffContext &ctxt = (CDiffContext &)pDoc->GetDiffContext();
4055 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
4058 DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
4059 di.customFlags1 &= ~ViewCustomFlags::EXPANDED;
4065 * @brief Update "Collapse All Subfolders" item
4067 void CDirView::OnUpdateViewCollapseAllSubdirs(CCmdUI* pCmdUI)
4069 pCmdUI->Enable(m_bTreeMode && GetDocument()->GetRecursive());
4072 void CDirView::OnMergeCompare()
4074 CWaitCursor waitstatus;
4078 void CDirView::OnMergeCompareLeft1Left2()
4080 CWaitCursor waitstatus;
4081 OpenSelection(SELECTIONTYPE_LEFT1LEFT2);
4084 void CDirView::OnMergeCompareRight1Right2()
4086 CWaitCursor waitstatus;
4087 OpenSelection(SELECTIONTYPE_RIGHT1RIGHT2);
4090 void CDirView::OnMergeCompareLeft1Right2()
4092 CWaitCursor waitstatus;
4093 OpenSelection(SELECTIONTYPE_LEFT1RIGHT2);
4096 void CDirView::OnMergeCompareLeft2Right1()
4098 CWaitCursor waitstatus;
4099 OpenSelection(SELECTIONTYPE_LEFT2RIGHT1);
4102 void CDirView::OnMergeCompareXML()
4104 CWaitCursor waitstatus;
4105 PackingInfo packingInfo = PLUGIN_BUILTIN_XML;
4106 OpenSelection(SELECTIONTYPE_NORMAL, &packingInfo);
4109 void CDirView::OnMergeCompareHex()
4111 CWaitCursor waitstatus;
4115 void CDirView::OnUpdateMergeCompare(CCmdUI *pCmdUI)
4117 DoUpdateOpen(SELECTIONTYPE_NORMAL, pCmdUI);
4120 void CDirView::OnUpdateMergeCompareLeft1Left2(CCmdUI *pCmdUI)
4122 DoUpdateOpen(SELECTIONTYPE_LEFT1LEFT2, pCmdUI);
4125 void CDirView::OnUpdateMergeCompareRight1Right2(CCmdUI *pCmdUI)
4127 DoUpdateOpen(SELECTIONTYPE_RIGHT1RIGHT2, pCmdUI);
4130 void CDirView::OnUpdateMergeCompareLeft1Right2(CCmdUI *pCmdUI)
4132 DoUpdateOpen(SELECTIONTYPE_LEFT1RIGHT2, pCmdUI);
4135 void CDirView::OnUpdateMergeCompareLeft2Right1(CCmdUI *pCmdUI)
4137 DoUpdateOpen(SELECTIONTYPE_LEFT2RIGHT1, pCmdUI);
4140 void CDirView::OnViewCompareStatistics()
4142 CompareStatisticsDlg dlg;
4143 dlg.SetCompareStats(GetDocument()->GetCompareStats());
4148 * @brief Display file encoding dialog & handle user's actions
4150 void CDirView::OnFileEncoding()
4152 DoFileEncodingDialog();
4156 * @brief Update "File Encoding" item
4158 void CDirView::OnUpdateFileEncoding(CCmdUI* pCmdUI)
4160 pCmdUI->Enable(TRUE);
4163 /** @brief Open help from mainframe when user presses F1*/
4164 void CDirView::OnHelp()
4166 theApp.ShowHelp(DirViewHelpLocation);
4170 * @brief true while user is editing a file name.
4172 bool CDirView::IsLabelEdit() const
4174 return (NULL != m_pList->GetEditControl());
4178 * @brief true if selected item is a "special item".
4180 bool CDirView::IsItemSelectedSpecial() const
4182 int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
4183 ASSERT(-1 != nSelItem);
4184 return (SPECIAL_ITEM_POS == GetItemKey(nSelItem));
4188 * @brief Allow edit "Paste" when renaming an item.
4190 void CDirView::OnEditCopy()
4192 CEdit *pEdit = m_pList->GetEditControl();
4200 * @brief Allow edit "Cut" when renaming an item.
4202 void CDirView::OnEditCut()
4204 CEdit *pEdit = m_pList->GetEditControl();
4212 * @brief Allow edit "Paste" when renaming an item.
4214 void CDirView::OnEditPaste()
4216 CEdit *pEdit = m_pList->GetEditControl();
4224 * @brief Allow edit "Undo" when renaming an item.
4226 void CDirView::OnEditUndo()
4228 CEdit *pEdit = m_pList->GetEditControl();
4236 * @brief Update the tool bar's "Undo" icon. It should be enabled when
4237 * renaming an item and undo is possible.
4239 void CDirView::OnUpdateEditUndo(CCmdUI* pCmdUI)
4241 CEdit *pEdit = m_pList->GetEditControl();
4244 pCmdUI->Enable(pEdit->CanUndo());
4248 pCmdUI->Enable(FALSE);
4253 * @brief Returns CShellContextMenu object that owns given HMENU.
4255 * @param [in] hMenu Handle to the menu to check ownership of.
4256 * @return Either m_pShellContextMenuLeft, m_pShellContextMenuRight
4257 * or NULL if hMenu is not owned by these two.
4259 CShellContextMenu* CDirView::GetCorrespondingShellContextMenu(HMENU hMenu) const
4261 CShellContextMenu* pMenu = NULL;
4262 if (m_pShellContextMenuLeft && hMenu == m_pShellContextMenuLeft->GetHMENU())
4263 pMenu = m_pShellContextMenuLeft.get();
4264 else if (m_pShellContextMenuMiddle && hMenu == m_pShellContextMenuMiddle->GetHMENU())
4265 pMenu = m_pShellContextMenuMiddle.get();
4266 else if (m_pShellContextMenuRight && hMenu == m_pShellContextMenuRight->GetHMENU())
4267 pMenu = m_pShellContextMenuRight.get();
4273 * @brief Handle messages related to correct menu working.
4275 * We need to requery shell context menu each time we switch from context menu
4276 * for one side to context menu for other side. Here we check whether we need to
4277 * requery and call ShellContextMenuHandleMenuMessage.
4279 LRESULT CDirView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
4281 while (message == WM_INITMENUPOPUP)
4283 HMENU hMenu = (HMENU)wParam;
4284 if (CShellContextMenu* pMenu = GetCorrespondingShellContextMenu(hMenu))
4286 if (m_hCurrentMenu != hMenu)
4288 // re-query context menu once more, because if context menu was queried for right
4289 // group of files and we are showing menu for left group (or vice versa) menu will
4290 // be shown incorrectly
4291 // also, if context menu was last queried for right group of files and we are
4292 // invoking command for left command will be executed for right group (the last
4293 // group that menu was requested for)
4294 // may be a "feature" of Shell
4296 pMenu->RequeryShellContextMenu();
4297 m_hCurrentMenu = hMenu;
4303 CShellContextMenu* pMenu = GetCorrespondingShellContextMenu(m_hCurrentMenu);
4308 pMenu->HandleMenuMessage(message, wParam, lParam, res);
4311 return CListView::WindowProc(message, wParam, lParam);
4315 * @brief Implement background item coloring
4317 void CDirView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
4319 LPNMLISTVIEW pNM = (LPNMLISTVIEW)pNMHDR;
4320 *pResult = CDRF_DODEFAULT;
4322 if (pNM->hdr.code == NM_CUSTOMDRAW)
4324 LPNMLVCUSTOMDRAW lpC = (LPNMLVCUSTOMDRAW)pNMHDR;
4326 if (lpC->nmcd.dwDrawStage == CDDS_PREPAINT)
4328 *pResult = CDRF_NOTIFYITEMDRAW;
4332 if (lpC->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
4334 *pResult = CDRF_NOTIFYITEMDRAW;
4338 if (lpC->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM ))
4340 GetColors (lpC->nmcd.dwItemSpec, lpC->iSubItem, lpC->clrTextBk, lpC->clrText);
4345 void CDirView::OnBnClickedComparisonStop()
4347 if (m_pCmpProgressBar)
4348 m_pCmpProgressBar->EndUpdating();
4349 GetDocument()->AbortCurrentScan();
4353 * @brief Populate colors for items in view, depending on difference status
4355 void CDirView::GetColors (int nRow, int nCol, COLORREF& clrBk, COLORREF& clrText) const
4357 const DIFFITEM& di = GetDiffItem (nRow);
4361 clrText = ::GetSysColor (COLOR_WINDOWTEXT);
4362 clrBk = ::GetSysColor (COLOR_WINDOW);
4364 else if (di.diffcode.isResultFiltered())
4366 clrText = m_cachedColors.clrTrivialText;
4367 clrBk = m_cachedColors.clrTrivial;
4369 else if (!di.diffcode.exists(0) || !di.diffcode.existAll(GetDocument()->m_nDirs))
4371 clrText = m_cachedColors.clrDiffText;
4372 clrBk = m_cachedColors.clrDiffDeleted;
4374 else if (di.diffcode.isResultDiff())
4376 clrText = m_cachedColors.clrDiffText;
4377 clrBk = m_cachedColors.clrDiff;
4381 clrText = ::GetSysColor (COLOR_WINDOWTEXT);
4382 clrBk = ::GetSysColor (COLOR_WINDOW);
4386 DirViewTreeState *CDirView::SaveTreeState()
4388 DirViewTreeState *pTreeState = new DirViewTreeState();
4389 CDiffContext &ctxt = const_cast<CDiffContext &>(GetDocument()->GetDiffContext());
4390 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
4393 DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
4394 if (di.HasChildren())
4396 const String& relpath = di.diffFileInfo[0].GetFile();
4397 pTreeState->insert(std::pair<String, bool>(relpath, !!(di.customFlags1 & ViewCustomFlags::EXPANDED)));
4403 void CDirView::RestoreTreeState(DirViewTreeState *pTreeState)
4405 CDiffContext &ctxt = const_cast<CDiffContext &>(GetDocument()->GetDiffContext());
4406 uintptr_t diffpos = ctxt.GetFirstDiffPosition();
4409 DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
4410 if (di.HasChildren())
4412 const String& relpath = di.diffFileInfo[0].GetFile();
4413 std::map<String, bool>::iterator p = pTreeState->find(relpath);
4414 if (p != pTreeState->end())
4416 di.customFlags1 &= ~ViewCustomFlags::EXPANDED;
4417 di.customFlags1 |= (p->second ? ViewCustomFlags::EXPANDED : 0);
4423 void CDirView::OnSearch()
4425 CDirDoc *pDoc = GetDocument();
4426 m_pList->SetRedraw(FALSE); // Turn off updating (better performance)
4427 int nRows = m_pList->GetItemCount();
4428 for (int currRow = nRows - 1; currRow >= 0; currRow--)
4430 uintptr_t pos = GetItemKey(currRow);
4431 if (pos == SPECIAL_ITEM_POS)
4434 bool bFound = false;
4435 DIFFITEM & di = GetDiffItemRef(currRow);
4437 for (int i = 0; i < pDoc->m_nDirs; i++)
4439 if (di.diffcode.exists(i) && !di.diffcode.isDirectory())
4441 GetItemFileNames(currRow, &paths);
4443 if (!ufile.OpenReadOnly(paths[i]))
4446 ufile.SetUnicoding(di.diffFileInfo[i].encoding.m_unicoding);
4447 ufile.SetBom(di.diffFileInfo[i].encoding.m_bom);
4448 ufile.SetCodepage(di.diffFileInfo[i].encoding.m_codepage);
4456 if (!ufile.ReadString(line, &lossy))
4459 if (_tcsstr(line.c_str(), _T("DirView")))
4473 pDoc->SetItemViewFlag(pos, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
4474 m_pList->DeleteItem(currRow);
4478 m_pList->SetRedraw(TRUE); // Turn updating back on
4482 * @brief Retrives file list of all selected files, and store them like
4483 * file_path1\nfile_path2\n...file_pathN
4485 * @param [out] filesForDroping Reference to buffer where file list will be stored
4487 void CDirView::PrepareDragData(String& filesForDroping)
4489 int pos = GetFirstSelectedInd();
4490 const int count = GetSelectedCount();
4493 // No selection - no diff to go
4501 const DIFFITEM &diffitem = GetNextSelectedInd(pos);
4503 // check for special items (e.g not "..")
4504 if (diffitem.diffcode.diffcode == 0)
4509 if (diffitem.diffcode.isSideFirstOnly())
4511 String spath = diffitem.getFilepath(0, GetDocument()->GetDiffContext().GetNormalizedLeft());
4512 spath = paths_ConcatPath(spath, diffitem.diffFileInfo[0].filename);
4513 filesForDroping += spath;
4515 else if (diffitem.diffcode.isSideSecondOnly())
4517 String spath = diffitem.getFilepath(1, GetDocument()->GetDiffContext().GetNormalizedRight());
4518 spath = paths_ConcatPath(spath, diffitem.diffFileInfo[1].filename);
4519 filesForDroping += spath;
4521 else if (diffitem.diffcode.isSideBoth())
4523 // when both files equal, there is no difference between what file to drag
4524 // so we put file from the left panel
4525 String spath = diffitem.getFilepath(0, GetDocument()->GetDiffContext().GetNormalizedLeft());
4526 spath = paths_ConcatPath(spath, diffitem.diffFileInfo[0].filename);
4527 filesForDroping += spath;
4529 // if both files are different then we also put file from the right panel
4530 if (diffitem.diffcode.isResultDiff())
4532 filesForDroping += _T('\n'); // end of left file path
4533 String spath = diffitem.getFilepath(1, GetDocument()->GetDiffContext().GetNormalizedRight());
4534 spath = paths_ConcatPath(spath, diffitem.diffFileInfo[1].filename);
4535 filesForDroping += spath;
4538 filesForDroping += _T('\n'); // end of file path
4541 if (!filesForDroping.empty())
4543 filesForDroping.erase(filesForDroping.length() - 1); // omit final \n
4548 * @brief Drag files/directories from folder compare listing view.
4550 void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
4552 COleDataSource *DropData = new COleDataSource();
4553 String filesForDroping;
4555 PrepareDragData(filesForDroping);
4557 CSharedFile file(GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT);
4558 file.Write(filesForDroping.data(), filesForDroping.length() * sizeof(TCHAR));
4559 file.Write(_T("\0"), sizeof(TCHAR)); // include terminating zero
4561 HGLOBAL hMem = file.Detach();
4565 DropData->CacheGlobalData(CF_UNICODETEXT, hMem);
4567 DropData->CacheGlobalData(CF_TEXT, hMem);
4569 DROPEFFECT de = DropData->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE, NULL);