1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
10 * @brief Implementation file for CDirDoc
16 #include <Poco/StringTokenizer.h>
17 #include <boost/range/mfc.hpp>
19 #include "IMergeDoc.h"
20 #include "CompareOptions.h"
21 #include "UnicodeString.h"
22 #include "CompareStats.h"
23 #include "FilterList.h"
29 #include "OptionsDef.h"
30 #include "OptionsMgr.h"
31 #include "OptionsDiffOptions.h"
32 #include "LineFiltersList.h"
33 #include "FileFilterHelper.h"
35 #include "DirActions.h"
37 #include "MessageBoxDialog.h"
38 #include "DirCmpReport.h"
39 #include "DiffWrapper.h"
40 #include "FolderCmp.h"
41 #include "DirViewColItems.h"
42 #include <Poco/Semaphore.h>
48 using Poco::StringTokenizer;
52 int CDirDoc::m_nDirsTemp = 2;
54 /////////////////////////////////////////////////////////////////////////////
57 IMPLEMENT_DYNCREATE(CDirDoc, CDocument)
65 , m_pCompareStats(nullptr)
66 , m_bMarkedRescan(false)
67 , m_pTempPathContext(nullptr)
68 , m_bGeneratingReport(false)
71 m_nDirs = m_nDirsTemp;
81 * Clears document list and deleted possible archive-temp files.
85 // Inform all of our merge docs that we're closing
86 for (auto pMergeDoc : m_MergeDocs)
87 pMergeDoc->DirDocClosing(this);
88 // Delete all temporary folders belonging to this document
89 while (m_pTempPathContext != nullptr)
91 m_pTempPathContext = m_pTempPathContext->DeleteHead();
96 * @brief Called when new dirdoc is created.
98 BOOL CDirDoc::OnNewDocument()
100 if (!CDocument::OnNewDocument())
107 BEGIN_MESSAGE_MAP(CDirDoc, CDocument)
108 //{{AFX_MSG_MAP(CDirDoc)
109 // NOTE - the ClassWizard will add and remove mapping macros here.
114 /////////////////////////////////////////////////////////////////////////////
115 // CDirDoc serialization
117 void CDirDoc::Serialize(CArchive& ar)
121 // TODO: add storing code here
125 // TODO: add loading code here
129 /////////////////////////////////////////////////////////////////////////////
133 * @brief Initialise directory compare for given paths.
135 * Initialises directory compare with paths given and recursive choice.
136 * Previous compare context is first free'd.
137 * @param [in] paths Paths to compare
138 * @param [in] bRecursive If `true` subdirectories are included to compare.
140 void CDirDoc::InitCompare(const PathContext & paths, bool bRecursive, CTempPathContext *pTempPathContext)
142 // Abort previous comparing
143 while (m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING)
145 m_diffThread.Abort();
149 m_pDirView->DeleteAllDisplayItems();
150 // Anything that can go wrong here will yield an exception.
151 // Default implementation of operator new() never returns `nullptr`.
153 if (m_pCompareStats == nullptr)
154 m_pCompareStats.reset(new CompareStats(m_nDirs));
156 m_pCtxt.reset(new CDiffContext(paths,
157 GetOptionsMgr()->GetInt(OPT_CMP_METHOD)));
158 m_pCtxt->m_bRecursive = bRecursive;
160 if (pTempPathContext != nullptr)
163 for (nIndex = 0; nIndex < m_nDirs; nIndex++)
164 ApplyDisplayRoot(nIndex, pTempPathContext->m_strDisplayRoot[nIndex]);
165 pTempPathContext->m_pParent = m_pTempPathContext;
166 m_pTempPathContext = pTempPathContext;
167 for (nIndex = 0; nIndex < m_nDirs; nIndex++)
168 m_pTempPathContext->m_strRoot[nIndex] = m_pCtxt->GetNormalizedPath(nIndex);
174 * @brief Load line filters to the compare context.
175 * Loads linefilters, converts them to UTF-8 and sets them for compare context.
177 void CDirDoc::LoadLineFilterList(CDiffContext *pCtxt)
179 ASSERT(pCtxt != nullptr);
181 bool bFilters = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
182 String filters = theApp.m_pLineFilters->GetAsString();
183 if (!bFilters || filters.empty())
185 pCtxt->m_pFilterList.reset();
189 if (pCtxt->m_pFilterList)
190 pCtxt->m_pFilterList->RemoveAllFilters();
192 pCtxt->m_pFilterList.reset(new FilterList());
194 std::string regexp_str = ucr::toUTF8(filters);
196 // Add every "line" of regexps to regexp list
197 StringTokenizer tokens(regexp_str, "\r\n");
198 for (StringTokenizer::Iterator it = tokens.begin(); it != tokens.end(); ++it)
199 pCtxt->m_pFilterList->AddRegExp(*it);
202 void CDirDoc::DiffThreadCallback(int& state)
204 PostMessage(m_pDirView->GetSafeHwnd(), MSG_UI_UPDATE, state, false);
207 void CDirDoc::InitDiffContext(CDiffContext *pCtxt)
209 LoadLineFilterList(pCtxt);
211 DIFFOPTIONS options = {0};
212 Options::DiffOptions::Load(GetOptionsMgr(), options);
214 pCtxt->CreateCompareOptions(GetOptionsMgr()->GetInt(OPT_CMP_METHOD), options);
216 pCtxt->m_iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
217 if ((pCtxt->m_iGuessEncodingType >> 16) == 0)
218 pCtxt->m_iGuessEncodingType |= 50001 << 16;
219 pCtxt->m_bIgnoreSmallTimeDiff = GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME);
220 pCtxt->m_bStopAfterFirstDiff = GetOptionsMgr()->GetBool(OPT_CMP_STOP_AFTER_FIRST);
221 pCtxt->m_nQuickCompareLimit = GetOptionsMgr()->GetInt(OPT_CMP_QUICK_LIMIT);
222 pCtxt->m_nBinaryCompareLimit = GetOptionsMgr()->GetInt(OPT_CMP_BINARY_LIMIT);
223 pCtxt->m_bPluginsEnabled = GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED);
224 pCtxt->m_bWalkUniques = GetOptionsMgr()->GetBool(OPT_CMP_WALK_UNIQUE_DIRS);
225 pCtxt->m_bIgnoreReparsePoints = GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_REPARSE_POINTS);
226 pCtxt->m_bIgnoreCodepage = GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CODEPAGE);
227 pCtxt->m_bEnableImageCompare = GetOptionsMgr()->GetBool(OPT_CMP_ENABLE_IMGCMP_IN_DIRCMP);
228 pCtxt->m_dColorDistanceThreshold = GetOptionsMgr()->GetInt(OPT_CMP_IMG_THRESHOLD) / 1000.0;
230 m_imgfileFilter.UseMask(true);
231 m_imgfileFilter.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
232 pCtxt->m_pImgfileFilter = &m_imgfileFilter;
234 pCtxt->m_pCompareStats = m_pCompareStats.get();
236 // Make sure filters are up-to-date
237 theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
238 pCtxt->m_piFilterGlobal = theApp.m_pGlobalFileFilter.get();
240 // All plugin management is done by our plugin manager
241 pCtxt->m_piPluginInfos = GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED) ? &m_pluginman : nullptr;
245 * @brief Perform directory comparison again from scratch
247 void CDirDoc::Rescan()
249 if (m_pCtxt == nullptr)
252 CDirFrame *pf = m_pDirView->GetParentFrame();
254 // If we're already doing a rescan, bail out
255 UINT threadState = m_diffThread.GetThreadState();
256 if (threadState == CDiffThread::THREAD_COMPARING)
259 if (!m_bGeneratingReport)
260 m_pCompareStats->Reset();
261 m_pDirView->StartCompare(m_pCompareStats.get());
263 if (!m_bGeneratingReport)
264 m_pDirView->DeleteAllDisplayItems();
265 // Don't clear if only scanning selected items
266 if (!m_bMarkedRescan && !m_bGeneratingReport)
268 m_pCtxt->RemoveAll();
269 m_pCtxt->InitDiffItemList();
272 InitDiffContext(m_pCtxt.get());
274 pf->GetHeaderInterface()->SetPaneCount(m_nDirs);
275 pf->GetHeaderInterface()->SetOnSetFocusCallback([&](int pane) {
276 m_pDirView->SetActivePane(pane);
277 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, pane);
279 for (int nIndex = 0; nIndex < m_nDirs; nIndex++)
281 UpdateHeaderPath(nIndex);
282 // draw the headers as inactive ones
283 pf->GetHeaderInterface()->SetActive(nIndex, false);
285 pf->GetHeaderInterface()->Resize();
286 int nPane = GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE);
287 m_pDirView->SetActivePane((nPane >= 0 && nPane < m_nDirs) ? nPane : 0);
289 // Show current compare method name and active filter name in statusbar
290 pf->SetFilterStatusDisplay(theApp.m_pGlobalFileFilter->GetFilterNameOrMask().c_str());
291 pf->SetCompareMethodStatusDisplay(m_pCtxt->GetCompareMethod());
293 // Folder names to compare are in the compare context
294 m_diffThread.SetContext(m_pCtxt.get());
295 m_diffThread.RemoveListener(this, &CDirDoc::DiffThreadCallback);
296 m_diffThread.AddListener(this, &CDirDoc::DiffThreadCallback);
297 if (m_bGeneratingReport)
299 m_diffThread.SetCollectFunction([&](DiffFuncStruct* myStruct) {
301 if (m_pReport->GetCopyToClipboard())
304 if (m_pReport->GetReportType() == REPORT_TYPE_SIMPLEHTML)
307 if (!m_pReport->GetReportFile().empty())
309 myStruct->context->m_pCompareStats->IncreaseTotalItems(
310 (m_pDirView->GetListCtrl().GetItemCount() - (myStruct->context->m_bRecursive ? 0 : 1)) * m);
312 m_diffThread.SetCompareFunction([&](DiffFuncStruct* myStruct) {
313 m_pReport->SetDiffFuncStruct(myStruct);
314 myStruct->pSemaphore->wait();
316 if (m_pReport->GenerateReport(errStr))
320 if (GetReportFile().empty())
321 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
325 String msg = strutils::format_string1(
326 _("Error creating the report:\n%1"),
328 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
331 SetGeneratingReport(false);
335 else if (m_bMarkedRescan)
337 m_diffThread.SetCollectFunction(nullptr);
338 m_diffThread.SetCompareFunction([](DiffFuncStruct* myStruct) {
339 int nItems = DirScan_UpdateMarkedItems(myStruct, nullptr);
340 myStruct->context->m_pCompareStats->IncreaseTotalItems(nItems);
341 DirScan_CompareRequestedItems(myStruct, nullptr);
346 m_diffThread.SetCollectFunction([](DiffFuncStruct* myStruct) {
347 bool casesensitive = false;
348 int depth = myStruct->context->m_bRecursive ? -1 : 0;
349 PathContext paths = myStruct->context->GetNormalizedPaths();
350 String subdir[3] = {_T(""), _T(""), _T("")}; // blank to start at roots specified in diff context
351 // Build results list (except delaying file comparisons until below)
352 DirScan_GetItems(paths, subdir, myStruct,
353 casesensitive, depth, nullptr, myStruct->context->m_bWalkUniques);
355 m_diffThread.SetCompareFunction([](DiffFuncStruct* myStruct) {
356 DirScan_CompareItems(myStruct, nullptr);
359 m_diffThread.CompareDirectories();
360 m_bMarkedRescan = false;
364 * @brief Empty & reload listview (of files & columns) with comparison results
365 * @todo Better solution for special items ("..")?
367 void CDirDoc::Redisplay()
369 if (m_pDirView == nullptr)
372 // Do not redisplay an empty CDirView
373 // Not only does it not have results, but AddSpecialItems will crash
374 // trying to dereference null context pointer to get to paths
378 m_pDirView->Redisplay();
381 CDirView * CDirDoc::GetMainView() const
383 CDirView *pView = nullptr;
384 if (POSITION pos = GetFirstViewPosition())
386 pView = static_cast<CDirView*>(GetNextView(pos));
387 ASSERT_KINDOF(CDirView, pView);
393 * @brief Update in-memory diffitem status from disk and update view.
394 * @param [in] diffPos POSITION of item in UI list.
395 * @param [in] idx index to reload.
396 * @note Do not call this function from DirView code! This function
397 * calls slow DirView functions to get item position and to update GUI.
398 * Use UpdateStatusFromDisk() function instead.
400 void CDirDoc::ReloadItemStatus(DIFFITEM *diffPos, int idx)
402 // in case just copied (into existence) or modified
403 m_pCtxt->UpdateStatusFromDisk(diffPos, idx);
405 int nIdx = m_pDirView->GetItemIndex(diffPos);
409 m_pDirView->UpdateDiffItemStatus(nIdx);
413 void CDirDoc::InitStatusStrings()
419 * @brief Update any resources necessary after a GUI language change
421 void CDirDoc::UpdateResources()
423 if (m_pDirView != nullptr)
424 m_pDirView->UpdateResources();
432 * @brief Stash away our view pointer.
434 void CDirDoc::SetDirView(CDirView * newView)
436 m_pDirView = newView;
437 // MFC has a view list for us, so lets check against it
438 POSITION pos = GetFirstViewPosition();
439 CDirView * temp = static_cast<CDirView *>(GetNextView(pos));
440 ASSERT(temp == m_pDirView); // verify that our stashed pointer is the same as MFC's
444 * @brief A new MergeDoc has been opened.
446 void CDirDoc::AddMergeDoc(IMergeDoc * pMergeDoc)
448 ASSERT(pMergeDoc != nullptr);
449 m_MergeDocs.AddTail(pMergeDoc);
453 * @brief MergeDoc informs us it is closing.
455 void CDirDoc::MergeDocClosing(IMergeDoc * pMergeDoc)
457 ASSERT(pMergeDoc != nullptr);
458 if (POSITION pos = m_MergeDocs.CPtrList::Find(pMergeDoc))
459 m_MergeDocs.RemoveAt(pos);
463 // If dir compare is empty (no compare results) and we are not closing
464 // because of reuse close also dir compare
465 if (m_pDirView != nullptr)
467 if (m_pCtxt == nullptr)
468 m_pDirView->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
470 else if (m_MergeDocs.GetCount() == 0)
477 * @brief Close MergeDocs opened from DirDoc.
479 * Asks confirmation for docs containing unsaved data and then
481 * @return `true` if success, `false` if user canceled or closing failed
483 bool CDirDoc::CloseMergeDocs()
485 while (!m_MergeDocs.IsEmpty())
486 if (!m_MergeDocs.GetTail()->CloseNow())
492 * @brief Update changed item's compare status
493 * @param [in] paths Paths for files we update
494 * @param [in] nDiffs Total amount of differences
495 * @param [in] nTrivialDiffs Amount of ignored differences
496 * @param [in] bIdentical `true` if files became identical, `false` otherwise.
498 void CDirDoc::UpdateChangedItem(const PathContext &paths,
499 UINT nDiffs, UINT nTrivialDiffs, bool bIdentical)
501 DIFFITEM *pos = FindItemFromPaths(*m_pCtxt, paths);
502 // If we failed files could have been swapped so lets try again
505 PathContext pathsSwapped(paths);
506 std::swap(pathsSwapped[0], pathsSwapped[pathsSwapped.GetSize() - 1]);
507 pos = FindItemFromPaths(*m_pCtxt, pathsSwapped);
508 if (!pos && paths.GetSize() > 2)
510 pathsSwapped = paths;
511 std::swap(pathsSwapped[0], pathsSwapped[1]);
512 pos = FindItemFromPaths(*m_pCtxt, pathsSwapped);
513 if (!pos && paths.GetSize() > 2)
515 pathsSwapped = paths;
516 std::swap(pathsSwapped[1], pathsSwapped[2]);
517 pos = FindItemFromPaths(*m_pCtxt, pathsSwapped);
522 // Update status if paths were found for items.
523 // Fail means we had unique items compared as 'renamed' items
524 // so there really is not status to update.
527 // Figure out new status code
528 UINT diffcode = (bIdentical ? DIFFCODE::SAME : DIFFCODE::DIFF);
530 // Update both views and diff context memory
531 m_pCtxt->SetDiffStatusCode(pos, diffcode, DIFFCODE::COMPAREFLAGS);
533 if (nDiffs != -1 && nTrivialDiffs != -1)
534 m_pCtxt->SetDiffCounts(pos, nDiffs, nTrivialDiffs);
535 for (int i = 0; i < m_pCtxt->GetCompareDirs(); ++i)
536 ReloadItemStatus(pos, i);
541 * @brief Cleans up after directory compare
543 void CDirDoc::CompareReady()
548 * @brief Refresh cached options.
550 * For compare speed, we have to cache some frequently needed options,
551 * instead of getting option value every time from OptionsMgr. This
552 * function must be called every time options are changed to OptionsMgr.
554 void CDirDoc::RefreshOptions()
556 if (m_pCtxt != nullptr)
557 m_pCtxt->m_bRecursive = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
558 if (m_pDirView != nullptr)
559 m_pDirView->RefreshOptions();
563 * @brief Write path and filename to headerbar
564 * @note SetText() does not repaint unchanged text
566 void CDirDoc::UpdateHeaderPath(int nIndex)
568 CDirFrame *pf = m_pDirView->GetParentFrame();
569 ASSERT(pf != nullptr);
572 if (!m_strDesc[nIndex].empty())
573 sText = m_strDesc[nIndex];
576 sText = m_pCtxt->GetPath(nIndex);
577 ApplyDisplayRoot(nIndex, sText);
580 pf->GetHeaderInterface()->SetText(nIndex, sText);
584 * @brief virtual override called just before document is saved and closed
586 BOOL CDirDoc::SaveModified()
588 // Do not allow closing if there is a thread running
589 if (m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING)
591 int ans = LangMessageBox(IDS_CONFIRM_CLOSE_WINDOW, MB_YESNO | MB_ICONWARNING);
594 m_diffThread.Abort();
595 while (m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING)
599 return CDocument::SaveModified();
603 * @brief Send signal to thread to stop current scan
605 * @sa CDirCompStateBar::OnStop()
607 void CDirDoc::AbortCurrentScan()
609 m_diffThread.Abort();
613 * @brief Send signal to thread to pause current scan
615 void CDirDoc::PauseCurrentScan()
617 m_diffThread.Pause();
621 * @brief Send signal to thread to continue current scan
623 void CDirDoc::ContinueCurrentScan()
625 m_diffThread.Continue();
629 * @brief Returns true if there is an active scan that hasn't been aborted.
631 bool CDirDoc::IsCurrentScanAbortable() const
633 return (m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING
634 && !m_diffThread.IsAborting());
638 * @brief Set directory description texts shown in headerbar
640 void CDirDoc::SetDescriptions(const String strDesc[])
642 if (strDesc != nullptr)
643 std::copy_n(strDesc, m_nDirs, m_strDesc);
647 * @brief Replace internal root by display root
648 * When we have a archive file open, this function converts physical folder
649 * (that is in the temp folder where archive was extracted) to the virtual
650 * path for showing. The virtual path is path to the archive file, archive
651 * file name and folder inside the archive.
652 * @param [in, out] sText Path to convert.
654 void CDirDoc::ApplyDisplayRoot(int nIndex, String &sText)
656 if (m_pTempPathContext != nullptr)
658 sText.erase(0, m_pTempPathContext->m_strRoot[nIndex].length());
659 sText.insert(0, m_pTempPathContext->m_strDisplayRoot[nIndex]);
664 * @brief Set document title to given string or items compared.
666 * Formats and sets caption for directory compare window. Caption
667 * has left- and right-side paths separated with '-'.
669 * @param [in] lpszTitle New title for window. If this parameter
670 * is not `nullptr` we use this string, otherwise format caption from
673 void CDirDoc::SetTitle(LPCTSTR lpszTitle)
675 if (m_pDirView == nullptr)
678 if (lpszTitle != nullptr)
679 CDocument::SetTitle(lpszTitle);
680 else if (m_pCtxt == nullptr || m_pCtxt->GetLeftPath().empty() ||
681 m_pCtxt->GetRightPath().empty() ||
682 (m_nDirs > 2 && m_pCtxt->GetMiddlePath().empty()))
684 String title = _("Folder Comparison Results");
685 CDocument::SetTitle(title.c_str());
691 for (int index = 0; index < m_nDirs; index++)
693 String strPath = m_pCtxt->GetPath(index);
694 ApplyDisplayRoot(index, strPath);
695 sDirName[index] = paths::FindFileName(strPath);
697 if (std::count(&sDirName[0], &sDirName[0] + m_nDirs, sDirName[0]) == m_nDirs)
698 sTitle = sDirName[0] + strutils::format(_T(" x %d"), m_nDirs);
700 sTitle = strutils::join(&sDirName[0], &sDirName[0] + m_nDirs, _T(" - "));
701 CDocument::SetTitle(sTitle.c_str());
707 * @brief Checks if current folders are opened from archive file.
708 * @return true if we are inside archive, false otherwise.
710 bool CDirDoc::IsArchiveFolders() const
712 if (m_pTempPathContext != nullptr)
718 void CDirDoc::Swap(int idx1, int idx2)
720 std::swap(m_bRO[idx1], m_bRO[idx2]);
721 std::swap(m_strDesc[idx1], m_strDesc[idx2]);
722 if (m_pTempPathContext != nullptr)
723 m_pTempPathContext->Swap(idx1, idx2);
724 m_pCtxt->Swap(idx1, idx2);
725 m_pCompareStats->Swap(idx1, idx2);
726 for (int nIndex = 0; nIndex < m_nDirs; nIndex++)
727 UpdateHeaderPath(nIndex);
731 bool CDirDoc::MoveableToNextDiff()
733 if (m_pDirView == nullptr)
735 CMessageBoxDialog dlg(nullptr, _("Do you want to move to the next file?").c_str());
736 const int nFormerResult = dlg.GetFormerResult();
737 if (nFormerResult != -1 && nFormerResult == IDNO)
739 return m_pDirView->HasNextDiff();
742 bool CDirDoc::MoveableToPrevDiff()
744 if (m_pDirView == nullptr)
746 CMessageBoxDialog dlg(nullptr, _("Do you want to move to the previous file?").c_str());
747 const int nFormerResult = dlg.GetFormerResult();
748 if (nFormerResult != -1 && nFormerResult == IDNO)
750 return m_pDirView->HasPrevDiff();
753 void CDirDoc::MoveToNextDiff(IMergeDoc *pMergeDoc)
755 if (m_pDirView == nullptr)
757 if (AfxMessageBox(_("Do you want to move to the next file?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
759 pMergeDoc->CloseNow();
760 m_pDirView->OpenNextDiff();
761 GetMainFrame()->OnUpdateFrameTitle(FALSE);
765 void CDirDoc::MoveToPrevDiff(IMergeDoc *pMergeDoc)
767 if (m_pDirView == nullptr)
769 if (AfxMessageBox(_("Do you want to move to the previous file?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
771 pMergeDoc->CloseNow();
772 m_pDirView->OpenPrevDiff();
773 GetMainFrame()->OnUpdateFrameTitle(FALSE);
777 bool CDirDoc::CompareFilesIfFilesAreLarge(int nFiles, const FileLocation ifileloc[])
780 bool bLargeFile = false;
781 for (int i = 0; i < nFiles; ++i)
783 di.diffFileInfo[i].SetFile(paths::FindFileName(ifileloc[i].filepath));
784 if (di.diffFileInfo[i].Update(ifileloc[i].filepath))
785 di.diffcode.setSideFlag(i);
788 di.diffcode.diffcode |= DIFFCODE::THREEWAY;
790 size_t fileSizeThreshold = GetOptionsMgr()->GetInt(OPT_FILE_SIZE_THRESHOLD);
791 for (int i = 0; i < nFiles; ++i)
793 if (di.diffFileInfo[i].size != DirItem::FILE_SIZE_NONE && di.diffFileInfo[i].size > fileSizeThreshold)
800 for (int i = 0; i < nFiles; ++i)
801 paths.SetPath(i, ifileloc[i].filepath.empty() ? _T("NUL") : paths::GetParentPath(ifileloc[i].filepath));
802 CDiffContext ctxt(paths, CMP_QUICK_CONTENT);
803 DirViewColItems ci(nFiles);
804 String msg = LoadResString(IDS_COMPARE_LARGE_FILES);
807 String sidestr[] = { _("Left:"), _("Right:") };
808 for (int i = 0; i < nFiles; ++i)
810 if (ifileloc[i].filepath.empty())
812 msg += strutils::format(_T("%s %s\n\n"), sidestr[i], _("None"));
816 msg += strutils::format(_T("%s %s\n %s: %s\n %s: %s (%s)\n\n"),
817 sidestr[i].c_str(), ifileloc[i].filepath.c_str(),
818 ci.GetColDisplayName(3 + i).c_str(), ci.ColGetTextToDisplay(&ctxt, 3 + i, di).c_str(),
819 ci.GetColDisplayName(8 + i).c_str(), ci.ColGetTextToDisplay(&ctxt, 8 + i, di).c_str(), ci.ColGetTextToDisplay(&ctxt, 10 + i, di).c_str());
825 String sidestr[] = { _("Left:"), _("Middle:"), _("Right:") };
826 for (int i = 0; i < nFiles; ++i)
828 if (ifileloc[i].filepath.empty())
830 msg += strutils::format(_T("%s %s\n\n"), sidestr[i], _("None"));
834 msg += strutils::format(_T("%s %s\n %s: %s\n %s: %s (%s)\n\n"),
835 sidestr[i].c_str(), ifileloc[i].filepath.c_str(),
836 ci.GetColDisplayName(3 + i).c_str(), ci.ColGetTextToDisplay(&ctxt, 3 + i, di).c_str(),
837 ci.GetColDisplayName(10 + i).c_str(), ci.ColGetTextToDisplay(&ctxt, 10 + i, di).c_str(), ci.ColGetTextToDisplay(&ctxt, 13 + i, di).c_str());
841 int ans = AfxMessageBox(msg.c_str(), MB_YESNOCANCEL | MB_ICONQUESTION | MB_DONT_ASK_AGAIN);
844 else if (ans == IDNO)
847 InitDiffContext(&ctxt);
848 FolderCmp cmp(&ctxt);
849 CWaitCursor waitstatus;
850 di.diffcode.diffcode |= cmp.prepAndCompareFiles(di);
851 AfxMessageBox(ci.ColGetTextToDisplay(&ctxt, 2, di).c_str(), MB_OK | MB_ICONINFORMATION);
852 theApp.SetLastCompareResult(di.diffcode.isResultDiff() ? 1 : 0);