2 * @file WebPageDiffFrm.cpp
4 * @brief Implementation file for CWebPageDiffFrame
9 #include "WebPageDiffFrm.h"
14 #include "OptionsDef.h"
15 #include "OptionsMgr.h"
16 #include "OptionsDiffColors.h"
17 #include "OptionsDiffOptions.h"
18 #include "CompareOptions.h"
20 #include "PathContext.h"
22 #include "FileOrFolderSelect.h"
23 #include "SelectPluginDlg.h"
24 #include "FileLocation.h"
25 #include "Constants.h"
26 #include "Environment.h"
28 #include <Poco/RegularExpression.h>
35 #define BCN_DROPDOWN (BCN_FIRST + 0x0002)
38 template <typename T, typename Func>
39 struct CallbackImpl : public T
41 CallbackImpl(Func&& callback) : m_callback(std::move(callback)) {}
42 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
43 ULONG STDMETHODCALLTYPE AddRef(void) override { return ++m_nRef; }
44 ULONG STDMETHODCALLTYPE Release(void) override { if (--m_nRef == 0) { delete this; return 0; } return m_nRef; }
45 HRESULT STDMETHODCALLTYPE Invoke(const WebDiffCallbackResult& result) { return m_callback(result); }
46 HRESULT STDMETHODCALLTYPE Invoke(const WebDiffEvent& event) { return m_callback(event); }
51 template<typename T, typename Func>
52 CComPtr<T> Callback(Func&& callback)
54 return CComPtr<T>(new CallbackImpl<T, Func>(std::move(callback)));
57 /////////////////////////////////////////////////////////////////////////////
60 IMPLEMENT_DYNCREATE(CWebPageDiffFrame, CMergeFrameCommon)
62 BEGIN_MESSAGE_MAP(CWebPageDiffFrame, CMergeFrameCommon)
63 //{{AFX_MSG_MAP(CWebPageDiffFrame)
70 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
71 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
73 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
74 ON_COMMAND(ID_RESCAN, OnFileReload)
75 ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnFileRecompareAs)
76 ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateFileRecompareAs)
78 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
79 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
80 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
81 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
82 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
83 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
84 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
85 ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
87 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
88 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
89 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
90 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
91 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
92 ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
93 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
94 ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
95 ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
96 ON_COMMAND(ID_REFRESH, OnRefresh)
98 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
99 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
100 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
101 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
102 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
103 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
104 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
105 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
106 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
107 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
108 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
109 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
111 ON_COMMAND(ID_WEB_SIZE_FIT_TO_WINDOW, OnWebFitToWindow)
112 ON_UPDATE_COMMAND_UI(ID_WEB_SIZE_FIT_TO_WINDOW, OnUpdateWebFitToWindow)
113 ON_COMMAND_RANGE(ID_WEB_SIZE_320x512, ID_WEB_SIZE_1440x900, OnWebSize)
114 ON_COMMAND_RANGE(ID_WEB_COMPARE_SCREENSHOTS, ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS, OnWebCompareScreenshots)
115 ON_COMMAND(ID_WEB_COMPARE_HTMLS, OnWebCompareHTMLs)
116 ON_COMMAND(ID_WEB_COMPARE_TEXTS, OnWebCompareTexts)
117 ON_COMMAND(ID_WEB_COMPARE_RESOURCETREES, OnWebCompareResourceTrees)
118 ON_COMMAND_RANGE(ID_WEB_CLEAR_DISK_CACHE, ID_WEB_CLEAR_ALL_PROFILE, OnWebClear)
120 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
122 ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
123 ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
125 ON_COMMAND_RANGE(ID_NEXT_PANE, ID_PREV_PANE, OnWindowChangePane)
127 ON_COMMAND(ID_HELP, OnHelp)
129 ON_BN_CLICKED(IDC_FITTOWINDOW, OnBnClickedFitToWindow)
130 ON_BN_CLICKED(IDC_SHOWDIFFERENCES, OnBnClickedShowDifferences)
131 ON_BN_CLICKED(IDC_COMPARE, OnBnClickedCompare)
132 ON_EN_CHANGE(IDC_WIDTH, OnEnChangeWidth)
133 ON_EN_CHANGE(IDC_HEIGHT, OnEnChangeHeight)
134 ON_EN_CHANGE(IDC_ZOOM, OnEnChangeZoom)
135 ON_EN_CHANGE(IDC_USERAGENT, OnEnChangeUserAgent)
136 ON_EN_KILLFOCUS(IDC_WIDTH, OnKillFocusBarControls)
137 ON_EN_KILLFOCUS(IDC_HEIGHT, OnKillFocusBarControls)
138 ON_EN_KILLFOCUS(IDC_ZOOM, OnKillFocusBarControls)
139 ON_EN_KILLFOCUS(IDC_USERAGENT, OnKillFocusBarControls)
140 ON_NOTIFY(BCN_DROPDOWN, IDC_COMPARE, OnDropDownCompare)
142 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
146 CMenu CWebPageDiffFrame::menu;
148 /////////////////////////////////////////////////////////////////////////////
149 // CWebPageDiffFrame construction/destruction
151 CWebPageDiffFrame::CWebPageDiffFrame()
152 : CMergeFrameCommon(IDI_EQUALWEBPAGE, IDI_NOTEQUALWEBPAGE)
154 , m_bAutoMerged(false)
155 , m_pWebDiffWindow(nullptr)
156 //, m_pWebToolWindow(nullptr)
157 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
160 , m_bInUpdateWebPageDiffBar(false)
161 , m_bCompareCompleted(false)
165 CWebPageDiffFrame::~CWebPageDiffFrame()
167 GetMainFrame()->UnwatchDocuments(this);
169 if (m_pDirDoc != nullptr)
171 m_pDirDoc->MergeDocClosing(this);
175 HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
176 if (hModule != nullptr)
178 bool (*pfnWinWebDiff_DestroyWindow)(IWebDiffWindow *) =
179 (bool (*)(IWebDiffWindow *))GetProcAddress(hModule, "WinWebDiff_DestroyWindow");
180 // bool (*pfnWinWebDiff_DestroyToolWindow)(IWebToolWindow *) =
181 // (bool (*)(IWebToolWindow *))GetProcAddress(hModule, "WinWebDiff_DestroyToolWindow");
182 if (pfnWinWebDiff_DestroyWindow != nullptr/* && pfnWinWebDiff_DestroyToolWindow != nullptr*/)
184 if (m_pWebDiffWindow != nullptr)
185 pfnWinWebDiff_DestroyWindow(m_pWebDiffWindow);
186 // if (m_pWebToolWindow != nullptr)
187 // pfnWinWebDiff_DestroyToolWindow(m_pWebToolWindow);
188 m_pWebDiffWindow = nullptr;
189 // m_pWebToolWindow = nullptr;
194 bool CWebPageDiffFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent, std::function<void ()> callback)
196 m_callbackOnOpenCompleted = callback;
197 m_bCompareCompleted = false;
199 CWaitCursor waitstatus;
200 int nNormalBuffer = 0;
201 for (int pane = 0; pane < nFiles; ++pane)
203 m_filePaths.SetPath(pane, fileloc[pane].filepath, false);
204 m_bRO[pane] = bRO[pane];
205 m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
206 if (fileloc[pane].filepath.empty() || paths::IsNullDeviceName(fileloc[pane].filepath))
208 m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
209 if (m_strDesc[pane].empty())
210 m_strDesc[pane] = (pane == 0) ? _("Untitled left") : ((nFiles < 3 || pane == 2) ? _("Untitled right") : _("Untitled middle"));
211 if (paths::IsNullDeviceName(fileloc[pane].filepath))
212 m_filePaths.SetPath(pane, _T("about:blank"), false);
216 m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
222 LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
223 ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
225 if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
228 int nCmdShow = SW_SHOW;
229 if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
230 nCmdShow = SW_SHOWMAXIMIZED;
231 ShowWindow(nCmdShow);
232 BringToTop(nCmdShow);
234 GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
236 GetMainFrame()->WatchDocuments(this);
241 void CWebPageDiffFrame::MoveOnLoad(int nPane, int)
245 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
246 if (nPane < 0 || nPane >= m_pWebDiffWindow->GetPaneCount())
250 m_pWebDiffWindow->SetActivePane(nPane);
252 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST) && m_pWebDiffWindow->GetDiffCount() > 0)
253 m_pWebDiffWindow->SelectDiff(0);
257 * @brief DirDoc gives us its identity just after it creates us
259 void CWebPageDiffFrame::SetDirDoc(IDirDoc * pDirDoc)
261 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
265 IMergeDoc::FileChange CWebPageDiffFrame::IsFileChangedOnDisk(int pane) const
268 if (!dfi.Update(m_filePaths[pane]))
269 return FileChange::Removed;
271 if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
272 tolerance = SmallTimeDiff; // From MainFrm.h
273 int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
274 if (timeDiff < 0) timeDiff = -timeDiff;
275 if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
276 return FileChange::Changed;
277 return FileChange::NoChange;
280 void CWebPageDiffFrame::CheckFileChanged(void)
282 if (!m_pWebDiffWindow)
284 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
286 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
288 String msg = strutils::format_string1(_("Another application has updated file\n%1\nsince WinMerge scanned it last time.\n\nDo you want to reload the file?"), m_filePaths[pane]);
289 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
299 * @brief Create a status bar to be associated with a heksedit control
301 void CWebPageDiffFrame::CreateWebWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
303 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
304 wndStatusBar.SetIndicators(0, 1);
305 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
306 wndStatusBar.SetParent(this);
307 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
310 static bool isTempFile(const String& path)
312 String tmpDir = env::GetTemporaryPath();
313 strutils::replace(tmpDir, _T("\\"), _T("/"));
314 tmpDir = _T("file:///") + tmpDir;
315 return (path.find(tmpDir) == 0);
318 void CWebPageDiffFrame::OnWebDiffEvent(const WebDiffEvent& event)
322 case WebDiffEvent::SourceChanged:
323 case WebDiffEvent::TabChanged:
325 if (m_nBufferType[event.pane] == BUFFERTYPE::UNNAMED)
327 m_nBufferType[event.pane] = BUFFERTYPE::NORMAL;
328 m_strDesc[event.pane].clear();
330 String curUrl = m_pWebDiffWindow->GetCurrentUrl(event.pane);
331 if (!isTempFile(curUrl) && curUrl != _T("about:blank"))
333 m_filePaths[event.pane] = paths::isFileURL(curUrl) ? paths::FromURL(curUrl) : curUrl;
334 UpdateHeaderPath(event.pane);
338 case WebDiffEvent::ZoomFactorChanged:
339 UpdateWebPageDiffBar();
345 * @brief returns true if WinWebDiffLib.dll is loadable
347 bool CWebPageDiffFrame::IsLoadable()
349 static HMODULE hModule;
350 if (hModule == nullptr)
352 hModule = LoadLibraryW(L"WinWebDiff\\WinWebDiffLib.dll");
353 if (hModule == nullptr)
361 * @brief returns true if URL matches configured pattern
363 bool CWebPageDiffFrame::MatchURLPattern(const String& url)
365 const String& includePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_INCLUDE);
366 if (includePattern.empty())
368 std::string textu8 = ucr::toUTF8(url);
371 Poco::RegularExpression reInclude(ucr::toUTF8(includePattern));
372 if (!reInclude.match(textu8))
374 const String& excludePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_EXCLUDE);
375 if (excludePattern.empty())
377 Poco::RegularExpression reExclude(ucr::toUTF8(excludePattern));
378 return !reExclude.match(textu8);
387 * @brief Create the splitter, the filename bar, the status bar, and the two views
389 BOOL CWebPageDiffFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
390 CCreateContext* pContext)
395 HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
396 if (hModule == nullptr)
399 IWebDiffWindow * (*pfnWinWebDiff_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
400 (IWebDiffWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinWebDiff_CreateWindow");
401 if (pfnWinWebDiff_CreateWindow == nullptr ||
402 (m_pWebDiffWindow = pfnWinWebDiff_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
404 FreeLibrary(hModule);
408 if (!m_pWebDiffWindow->IsWebView2Installed())
410 if (IDYES == AfxMessageBox(_("WebView2 runtime is not installed. Do you want to download it?").c_str(), MB_ICONWARNING | MB_YESNO))
412 m_pWebDiffWindow->DownloadWebView2();
417 m_pWebDiffWindow->AddEventListener(
418 Callback<IWebDiffEventHandler>([this](const WebDiffEvent& event) -> HRESULT
420 OnWebDiffEvent(event);
427 m_pWebDiffWindow->SetUserDataFolderType(
428 static_cast<IWebDiffWindow::UserdataFolderType>(GetOptionsMgr()->GetInt(OPT_CMP_WEB_USERDATAFOLDER_TYPE)),
429 GetOptionsMgr()->GetBool(OPT_CMP_WEB_USERDATAFOLDER_PERPANE));
431 auto callback = Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
433 int nNormalBuffer = 0;
434 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
436 if (m_nBufferType[pane] != BUFFERTYPE::UNNAMED)
439 if (nNormalBuffer == 0)
440 UpdateLastCompareResult();
442 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
443 m_pWebDiffWindow->FirstDiff();
445 if (m_callbackOnOpenCompleted)
447 m_callbackOnOpenCompleted();
448 m_callbackOnOpenCompleted = nullptr;
450 m_bCompareCompleted = true;
454 if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
456 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
457 m_filePaths[pane] = _T("about:blank");
459 bResult = OpenUrls(callback);
461 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
463 m_fileInfo[pane].Update(m_filePaths[pane]);
466 // Merge frame has also a dockable bar at the very left
467 // This is not the client area, but we create it now because we want
468 // to use the CCreateContext
469 // String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
470 // if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
472 // TRACE0("Failed to create LocationBar\n");
476 // IWebToolWindow * (*pfnWinWebDiff_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *) =
477 // (IWebToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *pWebPageDiffWindow))GetProcAddress(hModule, "WinWebDiff_CreateToolWindow");
478 // if (pfnWinWebDiff_CreateToolWindow == nullptr ||
479 // (m_pWebToolWindow = pfnWinWebDiff_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pWebDiffWindow)) == nullptr)
484 // m_pWebToolWindow->Translate(TranslateLocationPane);
486 // m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
491 //void CWebPageDiffFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
493 // swprintf_s(dst, dstbufsize, L"%s", tr("WebPageDiffFrame|LocationPane", ucr::toUTF8(org)).c_str());
496 /////////////////////////////////////////////////////////////////////////////
497 // CWebPageDiffFrame message handlers
499 int CWebPageDiffFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
501 if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
504 EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
506 CMergeFrameCommon::RemoveBarBorder();
508 // Merge frame has a header bar at top
509 if (!m_wndWebPageDiffBar.Create(this) || !m_wndFilePathBar.Create(this))
511 TRACE0("Failed to create dialog bar\n");
512 return -1; // fail to create
515 UpdateWebPageDiffBar();
517 m_wndFilePathBar.SetPaneCount(m_pWebDiffWindow->GetPaneCount());
518 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
519 if (m_nActivePane != pane)
520 m_pWebDiffWindow->SetActivePane(pane);
522 m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
523 m_strDesc[pane] = sText;
524 UpdateHeaderPath(pane);
527 // Merge frame also has a dockable bar at the very left
528 // created in OnCreateClient
529 //m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
530 // CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
531 //m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
532 //DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
534 for (int nPane = 0; nPane < m_pWebDiffWindow->GetPaneCount(); nPane++)
536 m_wndFilePathBar.SetActive(nPane, FALSE);
537 // CreateWebWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pWebDiffWindow->GetPaneHWND(nPane)));
538 UpdateHeaderPath(nPane);
541 // CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
542 // m_rectBorder.bottom = size.cy;
544 CDockState pDockState;
545 pDockState.LoadState(_T("Settings-WebPageDiffFrame"));
546 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
547 SetDockState(pDockState);
548 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
549 //m_wndLocationBar.LoadState(_T("Settings-WebPageDiffFrame"));
555 * @brief We must use this function before a call to SetDockState
557 * @note Without this, SetDockState will assert or crash if a bar from the
558 * CDockState is missing in the current CMergeEditFrame.
559 * The bars are identified with their ID. This means the missing bar bug is triggered
560 * when we run WinMerge after changing the ID of a bar.
562 bool CWebPageDiffFrame::EnsureValidDockState(CDockState& state)
564 for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
566 bool barIsCorrect = true;
567 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
568 if (pInfo == nullptr)
569 barIsCorrect = false;
572 if (!pInfo->m_bFloating)
574 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
575 if (pInfo->m_pBar == nullptr)
576 barIsCorrect = false; //toolbar id's probably changed
581 state.m_arrBarInfo.RemoveAt(i);
587 * @brief Save the window's position, free related resources, and destroy the window
589 BOOL CWebPageDiffFrame::DestroyWindow()
595 CFrameWnd* pParentFrame = GetParentFrame();
596 BOOL result = CMergeFrameCommon::DestroyWindow();
598 pParentFrame->OnUpdateFrameTitle(FALSE);
602 void CWebPageDiffFrame::LoadOptions()
604 m_pWebDiffWindow->SetHorizontalSplit(GetOptionsMgr()->GetBool(OPT_SPLIT_HORIZONTALLY));
605 m_pWebDiffWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_WEB_ZOOM) / 1000.0);
606 SIZE size{ GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_WIDTH), GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_HEIGHT) };
607 m_pWebDiffWindow->SetSize(size);
608 m_pWebDiffWindow->SetFitToWindow(GetOptionsMgr()->GetBool(OPT_CMP_WEB_FIT_TO_WINDOW));
609 m_pWebDiffWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_WEB_SHOWDIFFERENCES));
610 m_pWebDiffWindow->SetShowWordDifferences(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
611 m_pWebDiffWindow->SetUserAgent(GetOptionsMgr()->GetString(OPT_CMP_WEB_USER_AGENT).c_str());
612 COLORSETTINGS colors;
613 IWebDiffWindow::ColorSettings colorSettings;
614 Options::DiffColors::Load(GetOptionsMgr(), colors);
615 colorSettings.clrDiff = colors.clrDiff;
616 colorSettings.clrDiffText = colors.clrDiffText;
617 colorSettings.clrSelDiff = colors.clrSelDiff;
618 colorSettings.clrSelDiffText = colors.clrSelDiffText;
619 colorSettings.clrSNP = colors.clrSNP;
620 colorSettings.clrSNPText = colors.clrSNPText;
621 colorSettings.clrSelSNP = colors.clrSelSNP;
622 colorSettings.clrSelSNPText = colors.clrSelSNPText;
623 colorSettings.clrWordDiff = colors.clrWordDiff;
624 colorSettings.clrWordDiffDeleted = colors.clrWordDiffDeleted;
625 colorSettings.clrWordDiffText = colors.clrWordDiffText;
626 colorSettings.clrSelWordDiff = colors.clrSelWordDiff;
627 colorSettings.clrSelWordDiffDeleted = colors.clrSelWordDiffDeleted;
628 colorSettings.clrSelWordDiffText = colors.clrSelWordDiffText;
629 m_pWebDiffWindow->SetDiffColorSettings(colorSettings);
631 IWebDiffWindow::DiffOptions diffOptions;
632 Options::DiffOptions::Load(GetOptionsMgr(), options);
633 diffOptions.bFilterCommentsLines = options.bFilterCommentsLines;
634 diffOptions.completelyBlankOutIgnoredChanges = options.bCompletelyBlankOutIgnoredChanges;
635 diffOptions.diffAlgorithm = options.nDiffAlgorithm;
636 diffOptions.ignoreBlankLines = options.bIgnoreBlankLines;
637 diffOptions.ignoreCase = options.bIgnoreCase;
638 diffOptions.ignoreEol = options.bIgnoreEol;
639 diffOptions.ignoreNumbers = options.bIgnoreNumbers;
640 diffOptions.ignoreWhitespace = options.nIgnoreWhitespace;
641 m_pWebDiffWindow->SetDiffOptions(diffOptions);
644 void CWebPageDiffFrame::SaveOptions()
646 // GetOptionsMgr()->SaveOption(OPT_CMP_WEB_DIFFCOLORALPHA, static_cast<int>(m_pWebDiffWindow->GetDiffColorAlpha() * 100.0));
647 SIZE size = m_pWebDiffWindow->GetSize();
648 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_WIDTH, size.cx);
649 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_HEIGHT, size.cy);
650 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_FIT_TO_WINDOW, m_pWebDiffWindow->GetFitToWindow());
651 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
652 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_ZOOM, static_cast<int>(m_pWebDiffWindow->GetZoom() * 1000));
653 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_USER_AGENT, m_pWebDiffWindow->GetUserAgent());
657 * @brief Save coordinates of the frame, splitters, and bars
659 * @note Do not save the maximized/restored state here. We are interested
660 * in the state of the active frame, and maybe this frame is not active
662 void CWebPageDiffFrame::SavePosition()
667 // save the bars layout
668 // save docking positions and sizes
669 CDockState m_pDockState;
670 GetDockState(m_pDockState);
671 m_pDockState.SaveState(_T("Settings-WebPageDiffFrame"));
672 // for the dimensions of the diff pane, use the CSizingControlBar save
673 //m_wndLocationBar.SaveState(_T("Settings-WebPageDiffFrame"));
676 void CWebPageDiffFrame::SaveActivePane()
678 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pWebDiffWindow->GetActivePane());
681 void CWebPageDiffFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
683 CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
687 GetMainFrame()->PostMessage(WM_USER + 1);
691 void CWebPageDiffFrame::OnClose()
693 // clean up pointers.
694 CMergeFrameCommon::OnClose();
697 void CWebPageDiffFrame::OnDestroy()
699 if (!m_pWebDiffWindow)
704 * @brief Reloads the opened files
706 void CWebPageDiffFrame::OnFileReload()
708 int nActivePane = m_pWebDiffWindow->GetActivePane();
710 Callback<IWebDiffCallback>([nActivePane, this](const WebDiffCallbackResult& result) -> HRESULT
712 MoveOnLoad(nActivePane);
713 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
714 m_fileInfo[pane].Update(m_filePaths[pane]);
719 void CWebPageDiffFrame::OnFileClose()
724 void CWebPageDiffFrame::OnFileRecompareAs(UINT nID)
726 PathContext paths = m_filePaths;
727 fileopenflags_t dwFlags[3];
729 int nBuffers = m_filePaths.GetSize();
730 PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
732 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
734 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
735 strDesc[nBuffer] = m_strDesc[nBuffer];
737 if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
739 infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
740 nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE : -ID_MERGE_COMPARE_WEBPAGE;
744 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
745 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
748 void CWebPageDiffFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
750 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_WEBPAGE);
753 void CWebPageDiffFrame::OnOpenWithUnpacker()
755 CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
756 strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
757 CSelectPluginDlg::PluginType::Unpacker, false);
758 if (dlg.DoModal() == IDOK)
760 PackingInfo infoUnpacker(dlg.GetPluginPipeline());
761 PathContext paths = m_filePaths;
762 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
763 String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
765 GetMainFrame()->DoFileOpen(
766 GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE: -1,
767 &paths, dwFlags, strDesc, _T(""), &infoUnpacker);
771 void CWebPageDiffFrame::OnWindowChangePane(UINT nID)
773 int npanes = m_pWebDiffWindow->GetPaneCount();
774 int pane = m_pWebDiffWindow->GetActivePane();
775 pane = (nID == ID_NEXT_PANE) ? ((pane + 1) % npanes) : ((pane + npanes - 1) % npanes);
776 m_pWebDiffWindow->SetActivePane(pane);
780 * @brief Write path and filename to headerbar
781 * @note SetText() does not repaint unchanged text
783 void CWebPageDiffFrame::UpdateHeaderPath(int pane)
787 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
788 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
790 sText = m_strDesc[pane];
794 sText = m_filePaths.GetPath(pane);
795 if (m_pDirDoc != nullptr)
796 m_pDirDoc->ApplyDisplayRoot(pane, sText);
799 m_wndFilePathBar.SetText(pane, sText.c_str());
804 /// update splitting position for panels 1/2 and headerbar and statusbar
805 void CWebPageDiffFrame::UpdateHeaderSizes()
807 if (m_pWebDiffWindow != nullptr)
810 CRect rc, rcMergeWindow;
811 int nPaneCount = m_pWebDiffWindow->GetPaneCount();
813 ::GetWindowRect(m_pWebDiffWindow->GetHWND(), &rcMergeWindow);
814 ScreenToClient(rcMergeWindow);
815 if (!m_pWebDiffWindow->GetHorizontalSplit())
817 for (int pane = 0; pane < nPaneCount; pane++)
819 RECT rc1 = m_pWebDiffWindow->GetPaneWindowRect(pane);
820 w[pane] = rc1.right - rc1.left - 4;
821 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
826 for (int pane = 0; pane < nPaneCount; pane++)
827 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
830 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
832 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
834 // resize controls in header dialog bar
835 m_wndFilePathBar.Resize(w);
837 rc.left = rcMergeWindow.left;
838 rc.top = rc.bottom - m_rectBorder.bottom;
840 for (int pane = 0; pane < nPaneCount; pane++)
842 rc.right += w[pane] + 4 + 2;
843 // m_wndStatusBar[pane].MoveWindow(&rc);
851 * @brief Update document filenames to title
853 void CWebPageDiffFrame::SetTitle(LPCTSTR lpszTitle)
855 String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr);
856 CMergeFrameCommon::SetTitle(sTitle.c_str());
857 if (m_hWnd != nullptr)
859 SetWindowText(sTitle.c_str());
860 GetMainFrame()->OnUpdateFrameTitle(TRUE);
864 int CWebPageDiffFrame::UpdateLastCompareResult()
867 if (m_bCompareCompleted)
869 result = m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0;
870 SetLastCompareResult(result);
875 void CWebPageDiffFrame::UpdateAutoPaneResize()
879 void CWebPageDiffFrame::UpdateSplitter()
883 bool CWebPageDiffFrame::OpenUrls(IWebDiffCallback* callback)
886 String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
887 String strTempFileName[3];
888 m_infoUnpacker.EnableWebBrowserMode();
889 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
891 strTempFileName[pane] = m_filePaths[pane];
892 if (!m_infoUnpacker.Unpacking(&m_unpackerSubcodes[pane], strTempFileName[pane], filteredFilenames, {strTempFileName[pane]}))
897 if (m_filePaths.GetSize() == 2)
898 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), callback));
900 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str(), callback));
904 /// Document commanding us to close
905 bool CWebPageDiffFrame::CloseNow()
912 * @brief A string to display as a tooltip for MDITabbar
914 CString CWebPageDiffFrame::GetTooltipString() const
916 return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
920 * @brief Update any resources necessary after a GUI language change
922 void CWebPageDiffFrame::UpdateResources()
924 //m_pWebToolWindow->Translate(TranslateLocationPane);
927 void CWebPageDiffFrame::RefreshOptions()
933 * @brief Handle some keys when in merging mode
935 bool CWebPageDiffFrame::MergeModeKeyDown(MSG* pMsg)
937 bool bHandled = false;
939 // Allow default text selection when SHIFT pressed
940 if (::GetAsyncKeyState(VK_SHIFT))
943 // Allow default editor functions when CTRL pressed
944 if (::GetAsyncKeyState(VK_CONTROL))
947 // If we are in merging mode (merge with cursor keys)
948 // handle some keys here
949 switch (pMsg->wParam)
964 * @brief Check for keyboard commands
966 BOOL CWebPageDiffFrame::PreTranslateMessage(MSG* pMsg)
968 if (pMsg->message == WM_KEYDOWN)
970 // If we are in merging mode (merge with cursor keys)
971 // handle some keys here
972 if (theApp.GetMergingMode())
974 bool bHandled = MergeModeKeyDown(pMsg);
979 // Close window in response to VK_ESCAPE if user has allowed it from options
980 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) != 0)
982 PostMessage(WM_CLOSE, 0, 0);
986 return CMergeFrameCommon::PreTranslateMessage(pMsg);
989 void CWebPageDiffFrame::OnSize(UINT nType, int cx, int cy)
991 CMergeFrameCommon::OnSize(nType, cx, cy);
996 * @brief Synchronize control and status bar placements with splitter position,
997 * update mod indicators, synchronize scrollbars
999 void CWebPageDiffFrame::OnIdleUpdateCmdUI()
1001 if (IsWindowVisible())
1003 int nActivePane = m_pWebDiffWindow->GetActivePane();
1004 if (nActivePane != -1)
1005 m_nActivePane = nActivePane;
1007 UpdateHeaderSizes();
1008 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1010 // Update mod indicators
1011 m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1013 UpdateLastCompareResult();
1015 CMergeFrameCommon::OnIdleUpdateCmdUI();
1019 * @brief Save pane sizes and positions when one of panes requests it.
1021 LRESULT CWebPageDiffFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1027 void CWebPageDiffFrame::UpdateWebPageDiffBar()
1029 m_bInUpdateWebPageDiffBar = true;
1030 bool fitToWindow = m_pWebDiffWindow->GetFitToWindow();
1031 m_wndWebPageDiffBar.CheckDlgButton(IDC_FITTOWINDOW, fitToWindow);
1032 m_wndWebPageDiffBar.CheckDlgButton(IDC_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
1033 m_wndWebPageDiffBar.EnableDlgItem(IDC_WIDTH, !fitToWindow);
1034 m_wndWebPageDiffBar.EnableDlgItem(IDC_HEIGHT, !fitToWindow);
1035 SIZE size = m_pWebDiffWindow->GetSize();
1036 m_wndWebPageDiffBar.SetDlgItemText(IDC_WIDTH, strutils::format(_T("%d"), size.cx));
1037 m_wndWebPageDiffBar.SetDlgItemText(IDC_HEIGHT, strutils::format(_T("%d"), size.cy));
1038 m_wndWebPageDiffBar.SetDlgItemText(IDC_ZOOM, strutils::format(_T("%.1f%%"),
1039 m_pWebDiffWindow->GetZoom() * 100.0));
1040 m_wndWebPageDiffBar.SetDlgItemText(IDC_USERAGENT, m_pWebDiffWindow->GetUserAgent());
1041 m_bInUpdateWebPageDiffBar = false;
1044 void CWebPageDiffFrame::OnBnClickedFitToWindow()
1046 m_pWebDiffWindow->SetFitToWindow(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_FITTOWINDOW));
1047 UpdateWebPageDiffBar();
1050 void CWebPageDiffFrame::OnBnClickedShowDifferences()
1052 m_pWebDiffWindow->SetShowDifferences(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_SHOWDIFFERENCES));
1053 UpdateWebPageDiffBar();
1056 void CWebPageDiffFrame::OnBnClickedCompare()
1061 void CWebPageDiffFrame::OnDropDownCompare(NMHDR *pNMHDR, LRESULT *pResult)
1064 m_wndWebPageDiffBar.GetDlgItem(IDC_COMPARE)->GetWindowRect(&rc);
1065 CPoint point { rc.left, rc.bottom };
1067 menuPopup.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_WEBPAGE_COMPARE));
1068 theApp.TranslateMenu(menuPopup.m_hMenu);
1069 BCMenu* pPopup = (BCMenu*)menuPopup.GetSubMenu(0);
1070 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
1071 point.x, point.y, AfxGetMainWnd());
1074 void CWebPageDiffFrame::OnEnChangeWidth()
1076 if (m_bInUpdateWebPageDiffBar)
1079 m_wndWebPageDiffBar.GetDlgItemText(IDC_WIDTH, text);
1080 int v = tc::ttoi(text.c_str());
1081 if (v > 0 && v < 32678)
1083 SIZE size = m_pWebDiffWindow->GetSize();
1085 m_pWebDiffWindow->SetSize(size);
1089 void CWebPageDiffFrame::OnEnChangeHeight()
1091 if (m_bInUpdateWebPageDiffBar)
1094 m_wndWebPageDiffBar.GetDlgItemText(IDC_HEIGHT, text);
1095 int v = tc::ttoi(text.c_str());
1096 if (v > 0 && v < 32678)
1098 SIZE size = m_pWebDiffWindow->GetSize();
1100 m_pWebDiffWindow->SetSize(size);
1104 void CWebPageDiffFrame::OnEnChangeZoom()
1106 if (m_bInUpdateWebPageDiffBar)
1109 m_wndWebPageDiffBar.GetDlgItemText(IDC_ZOOM, text);
1110 tchar_t* endptr = nullptr;
1111 double v = tc::tcstod(text.c_str(), &endptr);
1112 if ((* endptr == '\0' || *endptr == '%') && v >= 25.0 && v <= 500.0)
1113 m_pWebDiffWindow->SetZoom(v / 100.0);
1116 void CWebPageDiffFrame::OnEnChangeUserAgent()
1118 if (m_bInUpdateWebPageDiffBar)
1121 m_wndWebPageDiffBar.GetDlgItemText(IDC_USERAGENT, text);
1122 m_pWebDiffWindow->SetUserAgent(text.c_str());
1125 void CWebPageDiffFrame::OnKillFocusBarControls()
1127 UpdateWebPageDiffBar();
1130 m_wndFilePathBar.GetDlgItemTextW(IDC_USERAGENT, text);
1133 void CWebPageDiffFrame::OnUpdateStatusNum(CCmdUI* pCmdUI)
1135 tchar_t sIdx[32] = { 0 };
1136 tchar_t sCnt[32] = { 0 };
1138 const int nDiffs = m_pWebDiffWindow->GetDiffCount();
1140 // Files are identical - show text "Identical"
1142 s = theApp.LoadString(IDS_IDENTICAL);
1144 // There are differences, but no selected diff
1145 // - show amount of diffs
1146 else if (m_pWebDiffWindow->GetCurrentDiffIndex() < 0)
1148 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1149 _itot_s(nDiffs, sCnt, 10);
1150 strutils::replace(s, _T("%1"), sCnt);
1153 // There are differences and diff selected
1154 // - show diff number and amount of diffs
1157 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1158 const int signInd = m_pWebDiffWindow->GetCurrentDiffIndex();
1159 _itot_s(signInd + 1, sIdx, 10);
1160 strutils::replace(s, _T("%1"), sIdx);
1161 _itot_s(nDiffs, sCnt, 10);
1162 strutils::replace(s, _T("%2"), sCnt);
1164 pCmdUI->SetText(s.c_str());
1168 * @brief Cut current selection to clipboard
1170 void CWebPageDiffFrame::OnEditCut()
1172 m_pWebDiffWindow->Cut();
1176 * @brief Copy current selection to clipboard
1178 void CWebPageDiffFrame::OnEditCopy()
1180 m_pWebDiffWindow->Copy();
1184 * @brief Paste clipboard content over selected content
1186 void CWebPageDiffFrame::OnEditPaste()
1188 m_pWebDiffWindow->Paste();
1192 * @brief Undo last action
1194 void CWebPageDiffFrame::OnEditUndo()
1196 m_pWebDiffWindow->Undo();
1200 * @brief Redo last action
1202 void CWebPageDiffFrame::OnEditRedo()
1204 m_pWebDiffWindow->Redo();
1208 * @brief Update the tool bar's "Undo" icon. It should be enabled when
1209 * renaming an item and undo is possible.
1211 void CWebPageDiffFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1213 pCmdUI->Enable(m_pWebDiffWindow->CanUndo());
1217 * @brief Update the tool bar's "Redo" icon. It should be enabled when
1218 * renaming an item and undo is possible.
1220 void CWebPageDiffFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1222 pCmdUI->Enable(m_pWebDiffWindow->CanRedo());
1226 * @brief Select entire image
1228 void CWebPageDiffFrame::OnEditSelectAll()
1230 m_pWebDiffWindow->SelectAll();
1234 * @brief Called when user selects View/Zoom In from menu.
1236 void CWebPageDiffFrame::OnViewZoomIn()
1238 m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() + 0.1);
1242 * @brief Called when user selects View/Zoom Out from menu.
1244 void CWebPageDiffFrame::OnViewZoomOut()
1246 m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() - 0.1);
1250 * @brief Called when user selects View/Zoom Normal from menu.
1252 void CWebPageDiffFrame::OnViewZoomNormal()
1254 m_pWebDiffWindow->SetZoom(1.0);
1258 * @brief Enables/disables linediff (different color for diffs)
1260 void CWebPageDiffFrame::OnViewLineDiffs()
1262 bool bWordDiffHighlight = !GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
1263 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, bWordDiffHighlight);
1264 m_pWebDiffWindow->SetShowWordDifferences(bWordDiffHighlight);
1267 void CWebPageDiffFrame::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
1269 pCmdUI->Enable(true);
1270 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
1274 * @brief Split panes vertically
1276 void CWebPageDiffFrame::OnViewSplitVertically()
1278 bool bSplitVertically = !m_pWebDiffWindow->GetHorizontalSplit();
1279 bSplitVertically = !bSplitVertically; // toggle
1280 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1281 m_pWebDiffWindow->SetHorizontalSplit(!bSplitVertically);
1285 * @brief Update "Split Vertically" UI items
1287 void CWebPageDiffFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
1289 pCmdUI->Enable(TRUE);
1290 pCmdUI->SetCheck(!m_pWebDiffWindow->GetHorizontalSplit());
1294 * @brief Go to first diff
1296 * Called when user selects "First Difference"
1297 * @sa CWebPageDiffFrame::SelectDiff()
1299 void CWebPageDiffFrame::OnFirstdiff()
1301 m_pWebDiffWindow->FirstDiff();
1305 * @brief Update "First diff" UI items
1307 void CWebPageDiffFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1309 OnUpdatePrevdiff(pCmdUI);
1313 * @brief Go to last diff
1315 void CWebPageDiffFrame::OnLastdiff()
1317 m_pWebDiffWindow->LastDiff();
1321 * @brief Update "Last diff" UI items
1323 void CWebPageDiffFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1325 OnUpdateNextdiff(pCmdUI);
1329 * @brief Go to next diff and select it.
1331 void CWebPageDiffFrame::OnNextdiff()
1333 if (m_pWebDiffWindow->GetCurrentDiffIndex() != m_pWebDiffWindow->GetDiffCount() - 1)
1334 m_pWebDiffWindow->NextDiff();
1335 else if (m_pDirDoc != nullptr)
1336 m_pDirDoc->MoveToNextDiff(this);
1340 * @brief Update "Next diff" UI items
1342 void CWebPageDiffFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1344 bool enabled = m_bCompareCompleted && (
1345 m_pWebDiffWindow->GetNextDiffIndex() >= 0 ||
1346 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1348 if (!enabled && m_pDirDoc != nullptr)
1349 enabled = m_pDirDoc->MoveableToNextDiff();
1350 pCmdUI->Enable(enabled);
1354 * @brief Go to previous diff and select it.
1356 void CWebPageDiffFrame::OnPrevdiff()
1358 if (m_pWebDiffWindow->GetCurrentDiffIndex() > 0)
1360 m_pWebDiffWindow->PrevDiff();
1362 else if (m_pDirDoc != nullptr)
1363 m_pDirDoc->MoveToPrevDiff(this);
1367 * @brief Update "Previous diff" UI items
1369 void CWebPageDiffFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1371 bool enabled = m_bCompareCompleted && (
1372 m_pWebDiffWindow->GetPrevDiffIndex() >= 0 ||
1373 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1375 if (!enabled && m_pDirDoc != nullptr)
1376 enabled = m_pDirDoc->MoveableToPrevDiff();
1377 pCmdUI->Enable(enabled);
1381 * @brief Go to next conflict and select it.
1383 void CWebPageDiffFrame::OnNextConflict()
1385 m_pWebDiffWindow->NextConflict();
1389 * @brief Update "Next Conflict" UI items
1391 void CWebPageDiffFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1393 pCmdUI->Enable(m_bCompareCompleted &&
1394 m_pWebDiffWindow->GetPaneCount() > 2 && (
1395 m_pWebDiffWindow->GetNextConflictIndex() >= 0 ||
1396 (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1402 * @brief Go to previous diff and select it.
1404 void CWebPageDiffFrame::OnPrevConflict()
1406 m_pWebDiffWindow->PrevConflict();
1410 * @brief Update "Previous diff" UI items
1412 void CWebPageDiffFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1414 pCmdUI->Enable(m_bCompareCompleted &&
1415 m_pWebDiffWindow->GetPaneCount() > 2 && (
1416 m_pWebDiffWindow->GetPrevConflictIndex() >= 0 ||
1417 (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1422 void CWebPageDiffFrame::OnWebFitToWindow()
1424 m_pWebDiffWindow->SetFitToWindow(true);
1426 UpdateWebPageDiffBar();
1429 void CWebPageDiffFrame::OnUpdateWebFitToWindow(CCmdUI* pCmdUI)
1431 pCmdUI->SetCheck(m_pWebDiffWindow->GetFitToWindow());
1434 void CWebPageDiffFrame::OnWebSize(UINT nID)
1438 case ID_WEB_SIZE_320x512:
1439 m_pWebDiffWindow->SetFitToWindow(false);
1440 m_pWebDiffWindow->SetSize({ 320, 512 });
1441 UpdateWebPageDiffBar();
1443 case ID_WEB_SIZE_375x600:
1444 m_pWebDiffWindow->SetFitToWindow(false);
1445 m_pWebDiffWindow->SetSize({ 375, 600 });
1446 UpdateWebPageDiffBar();
1448 case ID_WEB_SIZE_1024x640:
1449 m_pWebDiffWindow->SetFitToWindow(false);
1450 m_pWebDiffWindow->SetSize({ 1024, 640 });
1452 UpdateWebPageDiffBar();
1454 case ID_WEB_SIZE_1280x800:
1455 m_pWebDiffWindow->SetFitToWindow(false);
1456 m_pWebDiffWindow->SetSize({ 1280, 800 });
1458 UpdateWebPageDiffBar();
1460 case ID_WEB_SIZE_1440x900:
1461 m_pWebDiffWindow->SetFitToWindow(false);
1462 m_pWebDiffWindow->SetSize({ 1440, 900 });
1464 UpdateWebPageDiffBar();
1469 void CWebPageDiffFrame::OnWebCompareScreenshots(UINT nID)
1471 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1473 const wchar_t *spaths[3];
1474 std::vector<String> descs;
1475 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1476 for (int pane = 0; pane < nPanes; ++pane)
1478 std::shared_ptr<TempFile> pTempFile(new TempFile());
1479 pTempFile->Create(_T("SCR"), _T(".png"));
1480 paths.SetPath(pane, pTempFile->GetPath());
1481 spaths[pane] = paths[pane].c_str();
1482 descs.push_back(m_filePaths[pane]);
1483 m_tempFiles.push_back(pTempFile);
1485 m_pWebDiffWindow->SaveFiles(
1486 (nID == ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS) ? IWebDiffWindow::FULLSIZE_SCREENSHOT : IWebDiffWindow::SCREENSHOT,
1488 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1490 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1491 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data());
1496 void CWebPageDiffFrame::OnWebCompareHTMLs()
1498 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1500 const wchar_t *spaths[3];
1501 std::vector<String> descs;
1502 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1503 for (int pane = 0; pane < nPanes; ++pane)
1505 std::shared_ptr<TempFile> pTempFile(new TempFile());
1506 pTempFile->Create(_T("HTM"), _T(".html"));
1507 paths.SetPath(pane, pTempFile->GetPath());
1508 spaths[pane] = paths[pane].c_str();
1509 descs.push_back(m_filePaths[pane]);
1510 m_tempFiles.push_back(pTempFile);
1512 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::HTML, spaths,
1513 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1515 PackingInfo infoUnpacker(String(_T("PrettifyHTML")));
1516 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1517 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""), &infoUnpacker);
1522 void CWebPageDiffFrame::OnWebCompareTexts()
1524 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1526 const wchar_t *spaths[3];
1527 std::vector<String> descs;
1528 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1529 for (int pane = 0; pane < nPanes; ++pane)
1531 std::shared_ptr<TempFile> pTempFile(new TempFile());
1532 pTempFile->Create(_T("TXT"), _T(".txt"));
1533 paths.SetPath(pane, pTempFile->GetPath());
1534 spaths[pane] = paths[pane].c_str();
1535 descs.push_back(m_filePaths[pane]);
1536 m_tempFiles.push_back(pTempFile);
1538 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::TEXT, spaths,
1539 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1541 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1542 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""));
1547 void CWebPageDiffFrame::OnWebCompareResourceTrees()
1549 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1551 const wchar_t *spaths[3];
1552 std::vector<String> descs;
1553 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1554 for (int pane = 0; pane < nPanes; ++pane)
1556 std::shared_ptr<TempFolder> pTempFolder(new TempFolder());
1557 pTempFolder->Create();
1558 paths.SetPath(pane, pTempFolder->GetPath());
1559 spaths[pane] = paths[pane].c_str();
1560 descs.push_back(m_filePaths[pane]);
1561 m_tempFolders.push_back(pTempFolder);
1563 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::RESOURCETREE, spaths,
1564 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1566 fileopenflags_t dwFlags[3]{};
1567 for (int pane = 0; pane < paths.GetSize(); ++pane)
1568 dwFlags[pane] = FFILEOPEN_NOMRU;
1569 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, descs.data(), _T(""), true);
1574 void CWebPageDiffFrame::OnWebClear(UINT nID)
1576 IWebDiffWindow::BrowsingDataType dataKinds;
1579 case ID_WEB_CLEAR_DISK_CACHE: dataKinds = IWebDiffWindow::BrowsingDataType::DISK_CACHE; break;
1580 case ID_WEB_CLEAR_COOKIES: dataKinds = IWebDiffWindow::BrowsingDataType::COOKIES; break;
1581 case ID_WEB_CLEAR_BROWSING_HISTORY: dataKinds = IWebDiffWindow::BrowsingDataType::BROWSING_HISTORY; break;
1582 case ID_WEB_CLEAR_ALL_PROFILE: dataKinds = IWebDiffWindow::BrowsingDataType::ALL_PROFILE; break;
1586 m_pWebDiffWindow->ClearBrowsingData(-1, dataKinds);
1589 bool CWebPageDiffFrame::GenerateReport(const String& sFileName) const
1591 bool result = false;
1592 bool completed = false;
1593 if (!GenerateReport(sFileName, [&completed, &result](bool res) { result = res; completed = true; }))
1595 CMainFrame::WaitAndDoMessageLoop(completed, 0);
1599 bool CWebPageDiffFrame::GenerateReport(const String& sFileName, std::function<void(bool)> callback) const
1601 String rptdir_full, rptdir, path, name, ext;
1603 String diffrpt_filename[3];
1604 String diffrpt_filename_full[3];
1605 const wchar_t* pfilenames[3]{};
1606 paths::SplitFilename(sFileName, &path, &name, &ext);
1607 rptdir_full = paths::ConcatPath(path, name) + _T(".files");
1608 rptdir = paths::FindFileName(rptdir_full);
1609 paths::CreateIfNeeded(rptdir_full);
1611 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1613 url[pane] = ucr::toTString(m_pWebDiffWindow->GetCurrentUrl(pane));
1614 diffrpt_filename[pane] = strutils::format(_T("%s/%d.pdf"), rptdir, pane + 1);
1615 diffrpt_filename_full[pane] = strutils::format(_T("%s/%d.pdf"), rptdir_full, pane + 1);
1616 pfilenames[pane] = diffrpt_filename_full[pane].c_str();
1620 if (!file.Open(sFileName, _T("wt")))
1622 String errMsg = GetSysError(GetLastError());
1623 String msg = strutils::format_string1(
1624 _("Error creating the report:\n%1"), errMsg);
1625 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1629 file.SetCodepage(ucr::CP_UTF_8);
1632 _T("<!DOCTYPE html>\n")
1635 _T("<meta charset=\"UTF-8\">\n")
1636 _T("<title>WinMerge Webpage Compare Report</title>\n")
1638 _T("table { table-layout: fixed; width: 100%; border-collapse: collapse; }\n")
1639 _T("th {position: sticky; top: 0;}\n")
1640 _T("td,th { border: solid 1px black; }\n")
1641 _T("embed { width: 100%; height: calc(100vh - 56px) }\n")
1642 _T(".title { color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
1648 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1649 file.WriteString(strutils::format(_T("<th class=\"title\">%s</th>\n"), url[pane]));
1650 file.WriteString(_T("</tr>\n"));
1653 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1655 strutils::format(_T("<td><embed type=\"application/pdf\" src=\"%s\" title=\"%s\"></td>\n"),
1656 diffrpt_filename[pane], diffrpt_filename[pane]));
1664 return SUCCEEDED(m_pWebDiffWindow->SaveDiffFiles(IWebDiffWindow::PDF, pfilenames,
1665 Callback<IWebDiffCallback>([this, callback](const WebDiffCallbackResult& result) -> HRESULT
1667 callback(SUCCEEDED(result.errorCode));
1673 * @brief Generate report from file compare results.
1675 void CWebPageDiffFrame::OnToolsGenerateReport()
1679 if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
1682 CWaitCursor waitstatus;
1683 if (GenerateReport(s))
1684 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION | MB_MODELESS);
1687 void CWebPageDiffFrame::OnRefresh()
1689 if (!m_bCompareCompleted)
1691 m_bCompareCompleted = false;
1692 m_pWebDiffWindow->Recompare(
1693 Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
1695 m_bCompareCompleted = true;
1696 if (UpdateLastCompareResult() == 0 &&
1697 std::count(m_filePaths.begin(), m_filePaths.end(), L"about:blank") != m_filePaths.GetSize())
1699 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
1700 [](const tchar_t* msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
1707 void CWebPageDiffFrame::OnSetFocus(CWnd* pNewWnd)
1709 if (m_nActivePane != -1)
1710 m_pWebDiffWindow->SetActivePane(m_nActivePane);
1714 * @brief Open help from mainframe when user presses F1
1716 void CWebPageDiffFrame::OnHelp()
1718 theApp.ShowHelp(WebPageDiffFrameHelpLocation);