OSDN Git Service

Use __super
[winmerge-jp/winmerge-jp.git] / Src / DirView.cpp
index de2f5ec..e21a88b 100644 (file)
@@ -32,7 +32,6 @@
 #include "BCMenu.h"
 #include "DirCmpReportDlg.h"
 #include "DirCmpReport.h"
-#include "DirCompProgressBar.h"
 #include "CompareStatisticsDlg.h"
 #include "LoadSaveCodepageDlg.h"
 #include "ConfirmFolderCopyDlg.h"
@@ -49,6 +48,7 @@
 #include "PatchTool.h"
 #include "SyntaxColors.h"
 #include "Shell.h"
+#include "DirTravel.h"
 #include <numeric>
 #include <functional>
 
@@ -91,9 +91,8 @@ IMPLEMENT_DYNCREATE(CDirView, CListView)
 
 CDirView::CDirView()
                : m_pList(nullptr)
-               , m_nHiddenItems(0)
-               , m_pCmpProgressBar(nullptr)
                , m_compareStart(0)
+               , m_elapsed(0)
                , m_bTreeMode(false)
                , m_dirfilter(std::bind(&COptionsMgr::GetBool, GetOptionsMgr(), _1))
                , m_pShellContextMenuLeft(nullptr)
@@ -107,7 +106,7 @@ CDirView::CDirView()
        m_dwDefaultStyle &= ~LVS_TYPEMASK;
        // Show selection all the time, so user can see current item even when
        // focus is elsewhere (ie, on file edit window)
-       m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS;
+       m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS | LVS_OWNERDATA;
 
        m_bTreeMode =  GetOptionsMgr()->GetBool(OPT_TREE_MODE);
        m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
@@ -140,6 +139,7 @@ BEGIN_MESSAGE_MAP(CDirView, CListView)
        ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged)
        ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
        ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)
+       ON_NOTIFY_REFLECT(LVN_ODFINDITEM, OnODFindItem)
        ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
        ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
        ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
@@ -238,9 +238,9 @@ BEGIN_MESSAGE_MAP(CDirView, CListView)
        ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
        ON_COMMAND(ID_MERGE_COMPARE_NONHORIZONTALLY, OnMergeCompareNonHorizontally)
        // Context menu -> Compare As
-       ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnMergeCompareAs)
+       ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnMergeCompareAs)
        ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnMergeCompareAs)
-       ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnUpdateMergeCompare)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateMergeCompare)
        ON_UPDATE_COMMAND_UI(ID_NO_UNPACKER, OnUpdateNoUnpacker)
        // Context menu -> Copy
        ON_COMMAND(ID_DIR_COPY_LEFT_TO_RIGHT, (OnCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
@@ -363,10 +363,6 @@ BEGIN_MESSAGE_MAP(CDirView, CListView)
        ON_UPDATE_COMMAND_UI(ID_STATUS_RIGHTDIR_RO, OnUpdateStatusRightRO)
        ON_UPDATE_COMMAND_UI(ID_STATUS_MIDDLEDIR_RO, OnUpdateStatusMiddleRO)
        ON_UPDATE_COMMAND_UI(ID_STATUS_LEFTDIR_RO, OnUpdateStatusLeftRO)
-       // Progress dialog
-       ON_BN_CLICKED(IDC_COMPARISON_STOP, OnBnClickedComparisonStop)
-       ON_BN_CLICKED(IDC_COMPARISON_PAUSE, OnBnClickedComparisonPause)
-       ON_BN_CLICKED(IDC_COMPARISON_CONTINUE, OnBnClickedComparisonContinue)
        //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
@@ -398,9 +394,9 @@ void CDirView::OnInitialUpdate()
                return 48;
        }();
        const int iconCY = iconCX;
-       CListView::OnInitialUpdate();
+       __super::OnInitialUpdate();
        m_pList = &GetListCtrl();
-       m_pIList.reset(new IListCtrlImpl(m_pList->m_hWnd));
+       m_pIList.reset(new IListCtrlImpl(m_pList->m_hWnd, m_listViewItems));
        CDirDoc* pDoc = GetDocument();
        pDoc->SetDirView(this);
 
@@ -462,13 +458,13 @@ void CDirView::OnInitialUpdate()
        // Show selection across entire row.u
        // Also allow user to rearrange columns via drag&drop of headers.
        // Also enable infotips.
-       DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP;
+       DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP | LVS_EX_DOUBLEBUFFER;
        m_pList->SetExtendedStyle(exstyle);
 }
 
 BOOL CDirView::PreCreateWindow(CREATESTRUCT& cs)
 {
-       CListView::PreCreateWindow(cs);
+       __super::PreCreateWindow(cs);
        cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
        return TRUE;
 }
@@ -481,17 +477,6 @@ BOOL CDirView::PreCreateWindow(CREATESTRUCT& cs)
  */
 void CDirView::StartCompare(CompareStats *pCompareStats)
 {
-       if (m_pCmpProgressBar == nullptr)
-               m_pCmpProgressBar.reset(new DirCompProgressBar());
-
-       if (!::IsWindow(m_pCmpProgressBar->GetSafeHwnd()))
-               m_pCmpProgressBar->Create(GetParentFrame());
-
-       m_pCmpProgressBar->SetCompareStat(pCompareStats);
-       m_pCmpProgressBar->StartUpdating();
-
-       GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), TRUE, FALSE);
-
        m_compareStart = clock();
 }
 
@@ -520,7 +505,7 @@ void CDirView::OnLButtonDblClk(UINT nFlags, CPoint point)
                }
        }
        if (GetFocus() == this)
-               CListView::OnLButtonDblClk(nFlags, point);
+               __super::OnLButtonDblClk(nFlags, point);
 }
 
 /**
@@ -564,14 +549,13 @@ void CDirView::RedisplayChildren(DIFFITEM *diffpos, int level, UINT &index, int
                                index++;
                                if (di.HasChildren())
                                {
-                                       m_pList->SetItemState(index - 1, INDEXTOSTATEIMAGEMASK((di.customFlags & ViewCustomFlags::EXPANDED) ? 2 : 1), LVIS_STATEIMAGEMASK);
                                        if (di.customFlags & ViewCustomFlags::EXPANDED)
                                                RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
                                }
                        }
                        else
                        {
-                               if (!ctxt.m_bRecursive || !di.diffcode.isDirectory() || !di.diffcode.existAll())
+                               if (!ctxt.m_bRecursive || !di.diffcode.isDirectory() || (!di.diffcode.existAll() && !di.HasChildren()))
                                {
                                        AddNewItem(index, curdiffpos, I_IMAGECALLBACK, 0);
                                        index++;
@@ -621,6 +605,8 @@ void CDirView::Redisplay()
                GetParentFrame()->SetLastCompareResult(alldiffs);
        SortColumnsAppropriately();
        SetRedraw(TRUE);
+       m_pList->SetItemCount(static_cast<int>(m_listViewItems.size()));
+       m_pList->Invalidate();
 }
 
 /**
@@ -717,6 +703,10 @@ void CDirView::ListContextMenu(CPoint point, int /*i*/)
        ASSERT(pPopup != nullptr);
 
        int sel = GetFocusedItem();
+       if (sel == -1)
+               sel = GetFirstSelectedInd();
+       if (sel == -1)
+               return;
        const DIFFITEM& di = GetDiffItem(sel);
        if (GetDiffContext().m_bRecursive && di.diffcode.isDirectory())
                pPopup->RemoveMenu(ID_MERGE_COMPARE, MF_BYCOMMAND);
@@ -972,7 +962,7 @@ void CDirView::DoDirActionTo(SIDE_TYPE stype, DirActions::method_type func, cons
        try {
                // First we build a list of desired actions
                FileActionScript actionScript;
-               actionScript.m_destBase = destPath;
+               actionScript.m_destBase = std::move(destPath);
                DirItemWithIndexIterator begin(m_pIList.get(), -1, true);
                DirItemWithIndexIterator end;
                FileActionScript *rsltScript;
@@ -1140,10 +1130,13 @@ void CDirView::SortColumnsAppropriately()
        m_ctlSortHeader.SetSortImage(m_pColItems->ColLogToPhys(sortCol), bSortAscending);
        //sort using static CompareFunc comparison function
        CompareState cs(&GetDiffContext(), m_pColItems.get(), sortCol, bSortAscending, m_bTreeMode);
-       GetListCtrl().SortItems(cs.CompareFunc, reinterpret_cast<DWORD_PTR>(&cs));
+       std::stable_sort(m_listViewItems.begin(), m_listViewItems.end(), [&cs](const ListViewOwnerDataItem& a, const ListViewOwnerDataItem& b)
+               { return CompareState::CompareFunc(a.lParam, b.lParam, reinterpret_cast<LPARAM>(&cs)) < 0; });
 
        m_firstDiffItem.reset();
        m_lastDiffItem.reset();
+       
+       m_pList->Invalidate();
 }
 
 /// Do any last minute work as view closes
@@ -1161,9 +1154,7 @@ void CDirView::OnDestroy()
                        m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)));
        }
 
-       CListView::OnDestroy();
-
-       GetMainFrame()->ClearStatusbarItemCount();
+       __super::OnDestroy();
 }
 
 /**
@@ -1190,7 +1181,7 @@ void CDirView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
                        }
                }
        }
-       CListView::OnChar(nChar, nRepCnt, nFlags);
+       __super::OnChar(nChar, nRepCnt, nFlags);
 }
 
 /**
@@ -1227,7 +1218,6 @@ void CDirView::CollapseSubdir(int sel)
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
 
        dip.customFlags &= ~ViewCustomFlags::EXPANDED;
-       m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
 
        int count = m_pList->GetItemCount();
        for (int i = sel + 1; i < count; i++)
@@ -1235,6 +1225,7 @@ void CDirView::CollapseSubdir(int sel)
                const DIFFITEM& di = GetDiffItem(i);
                if (!di.IsAncestor(&dip))
                        break;
+               m_listViewItems.erase(m_listViewItems.begin() + i);
                m_pList->DeleteItem(i--);
                count--;
        }
@@ -1253,7 +1244,9 @@ void CDirView::ExpandSubdir(int sel, bool bRecursive)
                return;
 
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
-       m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+
+       const int top = m_pList->GetTopIndex();
+       const size_t num = m_listViewItems.size();
 
        CDiffContext &ctxt = GetDiffContext();
        dip.customFlags |= ViewCustomFlags::EXPANDED;
@@ -1265,9 +1258,15 @@ void CDirView::ExpandSubdir(int sel, bool bRecursive)
        int alldiffs;
        RedisplayChildren(diffpos, dip.GetDepth() + 1, indext, alldiffs);
 
+       for (size_t i = 0; i < m_listViewItems.size() - num; ++i)
+               m_pList->InsertItem(sel + 1, nullptr);
+
        SortColumnsAppropriately();
 
        m_pList->SetRedraw(TRUE);       // Turn updating back on
+       m_pList->SetItemCount(static_cast<int>(m_listViewItems.size()));
+       m_pList->EnsureVisible(top, TRUE);
+       m_pList->Invalidate();
 }
 
 /**
@@ -1379,7 +1378,8 @@ void CDirView::Open(CDirDoc *pDoc, const PathContext& paths, DWORD dwFlags[3], F
        {
                // Open subfolders
                // Don't add folders to MRU
-               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? nullptr : pDoc);
+               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive,
+                       ((GetAsyncKeyState(VK_CONTROL) & 0x8000) || GetDiffContext().m_bRecursive) ? nullptr : pDoc);
        }
        else if (HasZipSupport() && std::count_if(paths.begin(), paths.end(), ArchiveGuessFormat) == paths.GetSize())
        {
@@ -1422,7 +1422,7 @@ void CDirView::Open(CDirDoc *pDoc, const PathContext& paths, DWORD dwFlags[3], F
                        GetDiffContext().FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
                }
 
-               GetMainFrame()->ShowAutoMergeDoc(GetDocument(), paths.GetSize(), fileloc,
+               GetMainFrame()->ShowAutoMergeDoc(0, GetDocument(), paths.GetSize(), fileloc,
                        dwFlags, strDesc, _T(""), infoUnpacker);
        }
 }
@@ -1599,7 +1599,8 @@ void CDirView::OpenSelectionAs(UINT id)
        {
                PackingInfo infoUnpacker(
                                CMainFrame::GetPluginPipelineByMenuId(id, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
-               GetMainFrame()->ShowAutoMergeDoc(pDoc, paths.GetSize(), fileloc, dwFlags, strDesc, _T(""), &infoUnpacker);
+               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
+                       ctxt.m_bRecursive, nullptr, &infoUnpacker, nullptr, 0);
        }
        else
        {
@@ -1690,7 +1691,8 @@ void CDirView::OnUpdateCtxtDirCopyBoth2(CCmdUI* pCmdUI)
  */
 DIFFITEM *CDirView::GetItemKey(int idx) const
 {
-       return (DIFFITEM *) m_pList->GetItemData(idx);
+       ASSERT(idx >= 0 && idx < static_cast<int>(m_listViewItems.size()));
+       return reinterpret_cast<DIFFITEM*>(m_listViewItems[idx].lParam);
 }
 
 // SetItemKey & GetItemKey encapsulate how the display list items
@@ -1731,6 +1733,7 @@ void CDirView::DeleteItem(int sel, bool removeDIFFITEM)
        if (m_bTreeMode)
        {
                CollapseSubdir(sel);
+               m_listViewItems.erase(m_listViewItems.begin() + sel);
                m_pList->DeleteItem(sel);
        }
        else if (GetDiffContext().m_bRecursive || diffpos->HasChildren())
@@ -1742,11 +1745,15 @@ void CDirView::DeleteItem(int sel, bool removeDIFFITEM)
                        int cursel = it.m_sel;
                        ++it;
                        if (di.IsAncestor(diffpos) || diffpos == &di)
+                       {
+                               m_listViewItems.erase(m_listViewItems.begin() + cursel);
                                m_pList->DeleteItem(cursel);
+                       }
                }
        }
        else
        {
+               m_listViewItems.erase(m_listViewItems.begin() + sel);
                m_pList->DeleteItem(sel);
        }
        if (removeDIFFITEM)
@@ -1766,6 +1773,7 @@ void CDirView::DeleteAllDisplayItems()
        // item data are just positions (diffposes)
        // that is, they contain no memory needing to be freed
        m_pList->DeleteAllItems();
+       m_listViewItems.clear();
 
        m_firstDiffItem.reset();
        m_lastDiffItem.reset();
@@ -1777,11 +1785,12 @@ void CDirView::DeleteAllDisplayItems()
  */
 int CDirView::GetItemIndex(DIFFITEM *key)
 {
-       LVFINDINFO findInfo;
-
-       findInfo.flags = LVFI_PARAM;  // Search for itemdata
-       findInfo.lParam = (LPARAM)key;
-       return m_pList->FindItem(&findInfo);
+       for (size_t i = 0; i < m_listViewItems.size(); ++i)
+       {
+               if (m_listViewItems[i].lParam == reinterpret_cast<LPARAM>(key))
+                       return static_cast<int>(i);
+       }
+       return 0;
 }
 
 /**
@@ -2336,6 +2345,7 @@ void CDirView::MoveFocus(int currentInd, int i, int selCount)
                m_pList->SetItemState(currentInd, 0, LVIS_SELECTED);
                m_pList->SetItemState(currentInd, 0, LVIS_FOCUSED);
                m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
+               m_pList->SetSelectionMark(i);
        }
 
        // Move focus to specified item
@@ -2353,7 +2363,7 @@ CDirFrame * CDirView::GetParentFrame()
 {
        // can't verify cast without introducing more coupling
        // (CDirView doesn't include DirFrame.h)
-       return static_cast<CDirFrame *>(CListView::GetParentFrame());
+       return static_cast<CDirFrame *>(__super::GetParentFrame());
 }
 
 void CDirView::OnRefresh()
@@ -2372,9 +2382,9 @@ BOOL CDirView::PreTranslateMessage(MSG* pMsg)
                        // Check if we got 'ESC pressed' -message
                        if (pMsg->wParam == VK_ESCAPE)
                        {
-                               if (m_pCmpProgressBar != nullptr)
+                               if (GetDocument()->m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING)
                                {
-                                       OnBnClickedComparisonStop();
+                                       GetDocument()->AbortCurrentScan();
                                        return TRUE;
                                }
 
@@ -2464,7 +2474,7 @@ BOOL CDirView::PreTranslateMessage(MSG* pMsg)
                        }
                }
        }
-       return CListView::PreTranslateMessage(pMsg);
+       return __super::PreTranslateMessage(pMsg);
 }
 
 void CDirView::OnUpdateRefresh(CCmdUI* pCmdUI)
@@ -2485,16 +2495,20 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
        CDirDoc * pDoc = GetDocument();
        ASSERT(pDoc != nullptr);
 
+       // Since the Collect thread deletes the DiffItems in the rescan by "Update selection",
+       // the UI update process should not be executed until the Collect thread process is completed 
+       // to avoid accessing the deleted DiffItem.
+       if (pDoc->m_diffThread.IsMarkedRescan() && pDoc->m_diffThread.GetCollectThreadState() != CDiffThread::THREAD_COMPLETED)
+       {
+               ASSERT(0);
+               return 0;       // return value unused
+       }
+
        if (wParam == CDiffThread::EVENT_COMPARE_COMPLETED)
        {
                if (pDoc->GetDiffContext().m_pPropertySystem && pDoc->GetDiffContext().m_pPropertySystem->HasHashProperties())
                        pDoc->GetDiffContext().CreateDuplicateValueMap();
 
-               // Close and destroy the dialog after compare
-               if (m_pCmpProgressBar != nullptr)
-                       GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), FALSE, FALSE);
-               m_pCmpProgressBar.reset();
-
                pDoc->CompareReady();
 
                if (!pDoc->GetGeneratingReport())
@@ -2512,11 +2526,9 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
                        MoveFocus(0, 0, 0);
 
                // If compare took more than TimeToSignalCompare seconds, notify user
-               clock_t elapsed = clock() - m_compareStart;
-               GetParentFrame()->SetStatus(
-                       strutils::format(_("Elapsed time: %ld ms"), elapsed).c_str()
-               );
-               if (elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
+               m_elapsed = clock() - m_compareStart;
+               SetTimer(STATUSBAR_UPDATE, 150, nullptr);
+               if (m_elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
                        MessageBeep(IDOK);
                GetMainFrame()->StartFlashing();
        }
@@ -2539,12 +2551,13 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
                        else
                                Redisplay();
                }
+
+               HideItems(GetDiffContext().m_vCurrentlyHiddenItems);
        }
 
        return 0; // return value unused
 }
 
-
 BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
 {
        NMHDR * hdr = reinterpret_cast<NMHDR *>(lParam);
@@ -2553,7 +2566,7 @@ BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
        if (hdr->code == HDN_BEGINDRAG)
                return OnHeaderBeginDrag((LPNMHEADER)hdr, pResult);
 
-       return CListView::OnNotify(wParam, lParam, pResult);
+       return __super::OnNotify(wParam, lParam, pResult);
 }
 
 BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
@@ -2571,7 +2584,7 @@ BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* p
                        return TRUE;
                }
        }
-       return CListView::OnChildNotify(uMsg, wParam, lParam, pResult);
+       return __super::OnChildNotify(uMsg, wParam, lParam, pResult);
 }
 
 /**
@@ -2680,11 +2693,20 @@ void CDirView::OnTimer(UINT_PTR nIDEvent)
        {
                KillTimer(STATUSBAR_UPDATE);
                int items = GetSelectedCount();
-               String msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
+               String msg;
+               if (m_elapsed != 0)
+               {
+                       msg = strutils::format(_("Elapsed time: %ld ms"), m_elapsed);
+                       m_elapsed = 0;
+               }
+               else
+               {
+                       msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
+               }
                GetParentFrame()->SetStatus(msg.c_str());
        }
        
-       CListView::OnTimer(nIDEvent);
+       __super::OnTimer(nIDEvent);
 }
 
 /**
@@ -2836,7 +2858,7 @@ struct FileCmpReport: public IFileCmpReport
                        return false;
                }
 
-               sLinkPath = di.diffFileInfo[0].GetFile();
+               sLinkPath = strutils::format(_T("%d_"), nIndex) + di.diffFileInfo[0].GetFile();
 
                strutils::replace(sLinkPath, _T("\\"), _T("_"));
                sLinkPath += _T(".html");
@@ -2914,7 +2936,7 @@ void CDirView::OnToolsGenerateReport()
        pReport->SetRootPaths(paths);
        pReport->SetColumns(m_pColItems->GetDispColCount());
        pReport->SetFileCmpReport(new FileCmpReport(this));
-       pReport->SetList(new IListCtrlImpl(m_pList->m_hWnd));
+       pReport->SetList(new IListCtrlImpl(m_pList->m_hWnd, m_listViewItems));
        pReport->SetReportType(dlg.m_nReportType);
        pReport->SetReportFile(dlg.m_sReportFile);
        pReport->SetCopyToClipboard(dlg.m_bCopyToClipboard);
@@ -3114,7 +3136,7 @@ void CDirView::OnPluginSettings(UINT nID)
        {
        case ID_PREDIFFER_SETTINGS_NONE:
        case ID_UNPACKER_SETTINGS_NONE:
-               pluginPipeline = _T("");
+               pluginPipeline.clear();
                break;
        case ID_PREDIFFER_SETTINGS_AUTO:
        case ID_UNPACKER_SETTINGS_AUTO:
@@ -3255,7 +3277,12 @@ void CDirView::OnItemRename()
 void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
 {
        bool bEnabled = (1 == m_pList->GetSelectedCount());
-       pCmdUI->Enable(bEnabled && SelBegin() != SelEnd());
+       if (bEnabled)
+       {
+               Counts counts = Count(&DirActions::IsItemRenamable);
+               bEnabled = (counts.count > 0 && counts.total == 1);
+       }
+       pCmdUI->Enable(bEnabled);
 }
 
 /**
@@ -3263,15 +3290,64 @@ void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
  */
 void CDirView::OnHideFilenames()
 {
+       CDiffContext& ctxt = GetDiffContext();
+       int selection_index;
+       String hiddden_item_path;
+       
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
        DirItemIterator it;
+
        while ((it = SelRevBegin()) != SelRevEnd())
        {
                DIFFITEM &di = *it;
+               selection_index = it.m_sel;
+               hiddden_item_path = di.getItemRelativePath();
                SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
-               DeleteItem(it.m_sel);
-               m_nHiddenItems++;
+               DeleteItem(selection_index);
+               ctxt.m_vCurrentlyHiddenItems.push_back(hiddden_item_path);
+       }
+       m_pList->SetRedraw(TRUE);       // Turn updating back on
+}
+
+/**
+ * @brief determine if an item-relative-path is contained in the list of items to hide
+ */
+bool CDirView::IsItemToHide(const String& currentItem, const std::vector<String>& ItemsToHide) const
+{
+       return std::find(ItemsToHide.begin(), ItemsToHide.end(), currentItem) != ItemsToHide.end();
+}
+
+/**
+ * @brief hides items specified in the .winmerge file
+ */
+
+void CDirView::HideItems(const std::vector<String>& ItemsToHide)
+{
+       CDiffContext& ctxt = GetDiffContext();
+       DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
+       while (diffpos != nullptr)
+       {
+               DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
+               if (IsItemToHide(di.getItemRelativePath(), ItemsToHide))
+                       SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
+       }
+
+       m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
+
+       int num_hidden = 0;
+       const size_t num_to_hide = ItemsToHide.size();
+       DirItemIterator it = RevBegin();
+       while((num_hidden < num_to_hide) && (it != RevEnd()))
+       {
+               DIFFITEM& di = *it;
+               if (di.customFlags & ViewCustomFlags::HIDDEN)
+               {
+                       DeleteItem(it.m_sel);
+                       num_hidden++;
+               }
+               ++it;
        }
+
        m_pList->SetRedraw(TRUE);       // Turn updating back on
 }
 
@@ -3306,7 +3382,7 @@ void CDirView::OnUpdateCtxtDirMoveTo(CCmdUI* pCmdUI)
  */
 void CDirView::OnSize(UINT nType, int cx, int cy)
 {
-       CListView::OnSize(nType, cx, cy);
+       __super::OnSize(nType, cx, cy);
        GetDocument()->SetTitle(nullptr);
 }
 
@@ -3339,10 +3415,7 @@ void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
        if ((pNMListView->uOldState & LVIS_SELECTED) !=
                        (pNMListView->uNewState & LVIS_SELECTED))
        {
-               if ((pNMListView->iItem % 5000) > 0)
-                       SetTimer(STATUSBAR_UPDATE, 100, nullptr);
-               else
-                       OnTimer(STATUSBAR_UPDATE);
+               SetTimer(STATUSBAR_UPDATE, 100, nullptr);
        }
        *pResult = 0;
 }
@@ -3354,7 +3427,8 @@ void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
  */
 afx_msg void CDirView::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
 {
-       *pResult = (SelBegin() == SelEnd());
+       Counts counts = Count(&DirActions::IsItemRenamable);
+       *pResult = !(counts.count > 0 && counts.total == 1);
 
        // If label edit is allowed.
        if (*pResult == FALSE)
@@ -3408,18 +3482,77 @@ afx_msg void CDirView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
                CString sText;
                pEdit->GetWindowText(sText);
 
-               if (!sText.IsEmpty())
+               if (!sText.IsEmpty() && paths::IsValidName(String(sText)))
                {
                        try {
                                DirItemIterator it(m_pIList.get(), reinterpret_cast<NMLVDISPINFO *>(pNMHDR)->item.iItem);
+                               DIFFITEM& di = *it;
+                               unsigned sideFlags = (di.diffcode.diffcode & DIFFCODE::SIDEFLAGS);
                                *pResult = DoItemRename(it, GetDiffContext(), String(sText));
+                               // Rescan the item if side flags change due to renaming.
+                               if (*pResult)
+                               {
+                                       if ((di.diffcode.diffcode & DIFFCODE::SIDEFLAGS) != sideFlags)
+                                       {
+                                               // Delete the item with the same file name as after renaming.
+                                               if (di.HasParent())
+                                               {
+                                                       for (DIFFITEM* pItem = di.GetParentLink()->GetFirstChild(); pItem != nullptr; pItem = pItem->GetFwdSiblingLink())
+                                                       {
+                                                               if ((pItem != &di) && (pItem->diffcode.isDirectory() == di.diffcode.isDirectory()) && (collstr(pItem->diffFileInfo[0].filename, di.diffFileInfo[0].filename, false) == 0))
+                                                               {
+                                                                       pItem->DelinkFromSiblings();
+                                                                       delete pItem;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               // Rescan the item.
+                                               MarkForRescan(di);
+                                               m_pSavedTreeState.reset(SaveTreeState(GetDiffContext()));
+                                               GetDocument()->SetMarkedRescan();
+                                               GetDocument()->Rescan();
+                                       }
+                                       else {
+                                               int nDirs = GetDiffContext().GetCompareDirs();
+                                               assert(nDirs == 2 || nDirs == 3);
+                                               UpdatePaths(nDirs, di);
+
+                                               int nIdx = reinterpret_cast<NMLVDISPINFO*>(pNMHDR)->item.iItem;
+                                               UpdateDiffItemStatus(nIdx);
+                                       }
+                               }
                        } catch (ContentsChangedException& e) {
                                AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
                        }
                }
+               else
+               {
+                       LangMessageBox(IDS_ERROR_INVALID_DIR_FILE_NAME, MB_ICONWARNING);
+               }
        }
 }
 
+void CDirView::OnODFindItem(NMHDR* pNMHDR, LRESULT* pResult)
+{
+       NMLVFINDITEM* pFindItem = reinterpret_cast<NMLVFINDITEM*>(pNMHDR);
+       if (pFindItem->lvfi.flags & LVFI_STRING)
+       {
+               String text = strutils::makelower(pFindItem->lvfi.psz);
+               for (size_t i = pFindItem->iStart; i < m_listViewItems.size(); ++i)
+               {
+                       DIFFITEM *di = GetItemKey(static_cast<int>(i));
+                       String filename = strutils::makelower(di->diffFileInfo[0].filename);
+                       if (di && _tcsncmp(text.c_str(), filename.c_str(), text.length()) == 0)
+                       {
+                               *pResult = i;
+                               return;
+                       }
+               }
+       }
+       *pResult = -1;
+}
+
 /**
  * @brief Called when item is marked for rescan.
  * This function marks selected items for rescan and rescans them.
@@ -3477,8 +3610,9 @@ void CDirView::OnUpdateStatusNum(CCmdUI* pCmdUI)
  */
 void CDirView::OnViewShowHiddenItems()
 {
+       CDiffContext& ctxt = GetDiffContext();
        SetItemViewFlag(GetDiffContext(), ViewCustomFlags::VISIBLE, ViewCustomFlags::VISIBILITY);
-       m_nHiddenItems = 0;
+       ctxt.m_vCurrentlyHiddenItems.clear();
        Redisplay();
 }
 
@@ -3487,7 +3621,8 @@ void CDirView::OnViewShowHiddenItems()
  */
 void CDirView::OnUpdateViewShowHiddenItems(CCmdUI* pCmdUI)
 {
-       pCmdUI->Enable(m_nHiddenItems > 0);
+       const CDiffContext& ctxt = GetDiffContext();
+       pCmdUI->Enable(ctxt.m_vCurrentlyHiddenItems.size() > 0);
 }
 
 /**
@@ -3819,7 +3954,7 @@ void CDirView::OnMergeCompareAs(UINT nID)
 
 void CDirView::OnUpdateMergeCompare(CCmdUI *pCmdUI)
 {
-       bool openableForDir = !((pCmdUI->m_nID >= ID_MERGE_COMPARE_TEXT && pCmdUI->m_nID <= ID_MERGE_COMPARE_IMAGE) ||
+       bool openableForDir = !((pCmdUI->m_nID >= ID_MERGE_COMPARE_TEXT && pCmdUI->m_nID <= ID_MERGE_COMPARE_WEBPAGE) ||
                (pCmdUI->m_nID >= ID_UNPACKERS_FIRST && pCmdUI->m_nID <= ID_UNPACKERS_LAST));
 
        DoUpdateOpen(SELECTIONTYPE_NORMAL, pCmdUI, openableForDir);
@@ -4048,7 +4183,7 @@ LRESULT CDirView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
                pMenu->HandleMenuMessage(message, wParam, lParam, res);
        }
 
-       return CListView::WindowProc(message, wParam, lParam);
+       return __super::WindowProc(message, wParam, lParam);
 }
 
 /**
@@ -4086,27 +4221,6 @@ void CDirView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
        }
 }
 
-void CDirView::OnBnClickedComparisonStop()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->EndUpdating();
-       GetDocument()->AbortCurrentScan();
-}
-
-void CDirView::OnBnClickedComparisonPause()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->SetPaused(true);
-       GetDocument()->PauseCurrentScan();
-}
-
-void CDirView::OnBnClickedComparisonContinue()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->SetPaused(false);
-       GetDocument()->ContinueCurrentScan();
-}
-
 /**
  * @brief Populate colors for items in view, depending on difference status
  */
@@ -4151,6 +4265,8 @@ void CDirView::OnSearch()
        CDirDoc *pDoc = GetDocument();
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
        int nRows = m_pList->GetItemCount();
+       CDiffContext& ctxt = GetDiffContext();
+
        for (int currRow = nRows - 1; currRow >= 0; currRow--)
        {
                DIFFITEM *pos = GetItemKey(currRow);
@@ -4160,6 +4276,7 @@ void CDirView::OnSearch()
                bool bFound = false;
                DIFFITEM &di = GetDiffItem(currRow);
                PathContext paths;
+
                for (int i = 0; i < pDoc->m_nDirs; i++)
                {
                        if (di.diffcode.exists(i) && !di.diffcode.isDirectory())
@@ -4196,9 +4313,10 @@ void CDirView::OnSearch()
                }
                if (!bFound)
                {
+                       String hiddden_item_path = di.getItemRelativePath();
                        SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
                        DeleteItem(currRow);
-                       m_nHiddenItems++;
+                       ctxt.m_vCurrentlyHiddenItems.push_back(hiddden_item_path);
                }
        }
        m_pList->SetRedraw(TRUE);       // Turn updating back on
@@ -4209,8 +4327,6 @@ void CDirView::OnSearch()
  */
 void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
 {
-       COleDataSource *DropData = new COleDataSource();
-
        std::list<String> list;
        CopyPathnamesForDragAndDrop(SelBegin(), SelEnd(), std::back_inserter(list), GetDiffContext());
        String filesForDroping = strutils::join(list.begin(), list.end(), _T("\n")) + _T("\n");
@@ -4221,6 +4337,7 @@ void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
        HGLOBAL hMem = GlobalReAlloc(file.Detach(), (filesForDroping.length() + 1) * sizeof(TCHAR), 0);
        if (hMem != nullptr) 
        {
+               COleDataSource* DropData = new COleDataSource();
                DropData->CacheGlobalData(CF_UNICODETEXT, hMem);
                DROPEFFECT de = DropData->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE, nullptr);
        }
@@ -4303,17 +4420,16 @@ int CALLBACK CDirView::CompareState::CompareFunc(LPARAM lParam1, LPARAM lParam2,
 }
 
 /// Add new item to list view
-int CDirView::AddNewItem(int i, DIFFITEM *diffpos, int iImage, int iIndent)
+void CDirView::AddNewItem(int i, DIFFITEM *diffpos, int iImage, int iIndent)
 {
-       LV_ITEM lvItem;
-       lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE | LVIF_INDENT;
-       lvItem.iItem = i;
+       ListViewOwnerDataItem lvItem;
        lvItem.iIndent = iIndent;
-       lvItem.iSubItem = 0;
-       lvItem.pszText = LPSTR_TEXTCALLBACK;
        lvItem.lParam = (LPARAM)diffpos;
        lvItem.iImage = iImage;
-       return GetListCtrl().InsertItem(&lvItem);
+       if (i == static_cast<int>(m_listViewItems.size()))
+               m_listViewItems.push_back(lvItem);
+       else
+               m_listViewItems.insert(m_listViewItems.begin() + i, lvItem);
 }
 
 /**
@@ -4368,10 +4484,13 @@ static LPTSTR NTAPI AllocDispinfoText(const String &s)
 void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
 {
        int nIdx = pParam->item.iItem;
+       if (nIdx >= static_cast<int>(m_listViewItems.size()))
+               return;
+       DIFFITEM *key = reinterpret_cast<DIFFITEM*>(m_listViewItems[nIdx].lParam);
        int i = m_pColItems->ColPhysToLog(pParam->item.iSubItem);
-       DIFFITEM *key = GetItemKey(nIdx);
        if (IsDiffItemSpecial(key))
        {
+               pParam->item.iImage = m_listViewItems[nIdx].iImage;
                if (m_pColItems->IsColName(i))
                {
                        pParam->item.pszText = _T("..");
@@ -4390,6 +4509,22 @@ void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
        if (pParam->item.mask & LVIF_IMAGE)
        {
                pParam->item.iImage = GetColImage(di);
+               if ((pParam->item.mask & LVIF_STATE) == 0)
+               {
+                       // for WinXP
+                       pParam->item.mask |= LVIF_STATE;
+                       pParam->item.state = m_pList->GetItemState(nIdx, static_cast<UINT>(~LVIS_STATEIMAGEMASK));
+               }
+       }
+       if (pParam->item.mask & LVIF_INDENT)
+       {
+               pParam->item.iIndent = m_listViewItems[nIdx].iIndent;
+       }
+       if (pParam->item.mask & LVIF_STATE)
+       {
+               pParam->item.stateMask |= LVIS_STATEIMAGEMASK;
+               if (di.HasChildren())
+                       pParam->item.state |= INDEXTOSTATEIMAGEMASK((di.customFlags & ViewCustomFlags::EXPANDED) ? 2 : 1);
        }
 }
 
@@ -4419,6 +4554,7 @@ void CDirView::OnEditColumns()
                                dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l);
                        }
                }
+               assert(m_pColItems->GetColCount() == dlg.GetColumns().size());
 
                // Add default order of columns for resetting to defaults
                for (l = 0; l < m_pColItems->GetColCount(); ++l)
@@ -4480,14 +4616,12 @@ void CDirView::OnEditColumns()
 
        if (m_pColItems->GetDispColCount() < 1)
        {
-               // Ignore them if they didn't leave a column showing
+               // Set them back to default if they didn't leave a column showing
+               // (However, if none of the items are checked, this process will not be executed because the "OK" button in the "Display Columns" dialog cannot be pressed.)
                m_pColItems->ResetColumnOrdering();
        }
-       else
-       {
-               ReloadColumns();
-               Redisplay();
-       }
+       ReloadColumns();
+       Redisplay();
 }
 
 DirActions CDirView::MakeDirActions(DirActions::method_type func) const