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())
207 m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
210 m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
216 LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
217 ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
219 if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
222 int nCmdShow = SW_SHOW;
223 if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
224 nCmdShow = SW_SHOWMAXIMIZED;
225 ShowWindow(nCmdShow);
226 BringToTop(nCmdShow);
228 GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
230 GetMainFrame()->WatchDocuments(this);
235 void CWebPageDiffFrame::MoveOnLoad(int nPane, int)
239 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
240 if (nPane < 0 || nPane >= m_pWebDiffWindow->GetPaneCount())
244 m_pWebDiffWindow->SetActivePane(nPane);
246 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST) && m_pWebDiffWindow->GetDiffCount() > 0)
247 m_pWebDiffWindow->SelectDiff(0);
251 * @brief DirDoc gives us its identity just after it creates us
253 void CWebPageDiffFrame::SetDirDoc(CDirDoc * pDirDoc)
255 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
259 IMergeDoc::FileChange CWebPageDiffFrame::IsFileChangedOnDisk(int pane) const
262 if (!dfi.Update(m_filePaths[pane]))
263 return FileChange::Removed;
265 if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
266 tolerance = SmallTimeDiff; // From MainFrm.h
267 int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
268 if (timeDiff < 0) timeDiff = -timeDiff;
269 if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
270 return FileChange::Changed;
271 return FileChange::NoChange;
274 void CWebPageDiffFrame::CheckFileChanged(void)
276 if (!m_pWebDiffWindow)
278 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
280 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
282 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]);
283 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
293 * @brief Create a status bar to be associated with a heksedit control
295 void CWebPageDiffFrame::CreateWebWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
297 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
298 wndStatusBar.SetIndicators(0, 1);
299 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
300 wndStatusBar.SetParent(this);
301 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
304 static bool isTempFile(const String& path)
306 String tmpDir = env::GetTemporaryPath();
307 strutils::replace(tmpDir, _T("\\"), _T("/"));
308 tmpDir = _T("file:///") + tmpDir;
309 return (path.find(tmpDir) == 0);
312 void CWebPageDiffFrame::OnWebDiffEvent(const WebDiffEvent& event)
316 case WebDiffEvent::SourceChanged:
317 case WebDiffEvent::TabChanged:
319 if (m_nBufferType[event.pane] == BUFFERTYPE::UNNAMED)
321 m_nBufferType[event.pane] = BUFFERTYPE::NORMAL;
322 m_strDesc[event.pane].clear();
324 String curUrl = m_pWebDiffWindow->GetCurrentUrl(event.pane);
325 if (!isTempFile(curUrl) && curUrl != _T("about:blank"))
327 m_filePaths[event.pane] = paths::isFileURL(curUrl) ? paths::FromURL(curUrl) : curUrl;
328 UpdateHeaderPath(event.pane);
332 case WebDiffEvent::ZoomFactorChanged:
333 UpdateWebPageDiffBar();
339 * @brief returns true if WinWebDiffLib.dll is loadable
341 bool CWebPageDiffFrame::IsLoadable()
343 static HMODULE hModule;
344 if (hModule == nullptr)
346 hModule = LoadLibraryW(L"WinWebDiff\\WinWebDiffLib.dll");
347 if (hModule == nullptr)
355 * @brief returns true if URL matches configured pattern
357 bool CWebPageDiffFrame::MatchURLPattern(const String& url)
359 const String& includePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_INCLUDE);
360 if (includePattern.empty())
362 std::string textu8 = ucr::toUTF8(url);
365 Poco::RegularExpression reInclude(ucr::toUTF8(includePattern));
366 if (!reInclude.match(textu8))
368 const String& excludePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_EXCLUDE);
369 if (excludePattern.empty())
371 Poco::RegularExpression reExclude(ucr::toUTF8(excludePattern));
372 return !reExclude.match(textu8);
381 * @brief Create the splitter, the filename bar, the status bar, and the two views
383 BOOL CWebPageDiffFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
384 CCreateContext* pContext)
389 HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
390 if (hModule == nullptr)
393 IWebDiffWindow * (*pfnWinWebDiff_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
394 (IWebDiffWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinWebDiff_CreateWindow");
395 if (pfnWinWebDiff_CreateWindow == nullptr ||
396 (m_pWebDiffWindow = pfnWinWebDiff_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
398 FreeLibrary(hModule);
402 if (!m_pWebDiffWindow->IsWebView2Installed())
404 if (IDYES == AfxMessageBox(_("WebView2 runtime is not installed. Do you want to download it?").c_str(), MB_ICONWARNING | MB_YESNO))
406 m_pWebDiffWindow->DownloadWebView2();
411 m_pWebDiffWindow->AddEventListener(
412 Callback<IWebDiffEventHandler>([this](const WebDiffEvent& event) -> HRESULT
414 OnWebDiffEvent(event);
421 m_pWebDiffWindow->SetUserDataFolderType(
422 static_cast<IWebDiffWindow::UserdataFolderType>(GetOptionsMgr()->GetInt(OPT_CMP_WEB_USERDATAFOLDER_TYPE)),
423 GetOptionsMgr()->GetBool(OPT_CMP_WEB_USERDATAFOLDER_PERPANE));
425 auto callback = Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
427 int nNormalBuffer = 0;
428 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
430 if (m_nBufferType[pane] != BUFFERTYPE::UNNAMED)
433 if (nNormalBuffer == 0)
434 UpdateDiffItem(m_pDirDoc);
436 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
437 m_pWebDiffWindow->FirstDiff();
439 if (m_callbackOnOpenCompleted)
441 m_callbackOnOpenCompleted();
442 m_callbackOnOpenCompleted = nullptr;
444 m_bCompareCompleted = true;
448 if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
450 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
451 m_filePaths[pane] = _T("about:blank");
453 bResult = OpenUrls(callback);
455 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
457 m_fileInfo[pane].Update(m_filePaths[pane]);
460 // Merge frame has also a dockable bar at the very left
461 // This is not the client area, but we create it now because we want
462 // to use the CCreateContext
463 // String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
464 // if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
466 // TRACE0("Failed to create LocationBar\n");
470 // IWebToolWindow * (*pfnWinWebDiff_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *) =
471 // (IWebToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *pWebPageDiffWindow))GetProcAddress(hModule, "WinWebDiff_CreateToolWindow");
472 // if (pfnWinWebDiff_CreateToolWindow == nullptr ||
473 // (m_pWebToolWindow = pfnWinWebDiff_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pWebDiffWindow)) == nullptr)
478 // m_pWebToolWindow->Translate(TranslateLocationPane);
480 // m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
485 //void CWebPageDiffFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
487 // swprintf_s(dst, dstbufsize, L"%s", tr("WebPageDiffFrame|LocationPane", ucr::toUTF8(org)).c_str());
490 /////////////////////////////////////////////////////////////////////////////
491 // CWebPageDiffFrame message handlers
493 int CWebPageDiffFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
495 if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
498 EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
500 CMergeFrameCommon::RemoveBarBorder();
502 // Merge frame has a header bar at top
503 if (!m_wndWebPageDiffBar.Create(this) || !m_wndFilePathBar.Create(this))
505 TRACE0("Failed to create dialog bar\n");
506 return -1; // fail to create
509 UpdateWebPageDiffBar();
511 m_wndFilePathBar.SetPaneCount(m_pWebDiffWindow->GetPaneCount());
512 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
513 if (m_nActivePane != pane)
514 m_pWebDiffWindow->SetActivePane(pane);
516 m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
517 m_strDesc[pane] = sText;
518 UpdateHeaderPath(pane);
521 // Merge frame also has a dockable bar at the very left
522 // created in OnCreateClient
523 //m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
524 // CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
525 //m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
526 //DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
528 for (int nPane = 0; nPane < m_pWebDiffWindow->GetPaneCount(); nPane++)
530 m_wndFilePathBar.SetActive(nPane, FALSE);
531 // CreateWebWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pWebDiffWindow->GetPaneHWND(nPane)));
532 UpdateHeaderPath(nPane);
535 // CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
536 // m_rectBorder.bottom = size.cy;
538 CDockState pDockState;
539 pDockState.LoadState(_T("Settings-WebPageDiffFrame"));
540 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
541 SetDockState(pDockState);
542 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
543 //m_wndLocationBar.LoadState(_T("Settings-WebPageDiffFrame"));
549 * @brief We must use this function before a call to SetDockState
551 * @note Without this, SetDockState will assert or crash if a bar from the
552 * CDockState is missing in the current CMergeEditFrame.
553 * The bars are identified with their ID. This means the missing bar bug is triggered
554 * when we run WinMerge after changing the ID of a bar.
556 bool CWebPageDiffFrame::EnsureValidDockState(CDockState& state)
558 for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
560 bool barIsCorrect = true;
561 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
562 if (pInfo == nullptr)
563 barIsCorrect = false;
566 if (!pInfo->m_bFloating)
568 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
569 if (pInfo->m_pBar == nullptr)
570 barIsCorrect = false; //toolbar id's probably changed
575 state.m_arrBarInfo.RemoveAt(i);
581 * @brief Save the window's position, free related resources, and destroy the window
583 BOOL CWebPageDiffFrame::DestroyWindow()
589 CFrameWnd* pParentFrame = GetParentFrame();
590 BOOL result = CMergeFrameCommon::DestroyWindow();
592 pParentFrame->OnUpdateFrameTitle(FALSE);
596 void CWebPageDiffFrame::LoadOptions()
598 m_pWebDiffWindow->SetHorizontalSplit(GetOptionsMgr()->GetBool(OPT_SPLIT_HORIZONTALLY));
599 m_pWebDiffWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_WEB_ZOOM) / 1000.0);
600 SIZE size{ GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_WIDTH), GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_HEIGHT) };
601 m_pWebDiffWindow->SetSize(size);
602 m_pWebDiffWindow->SetFitToWindow(GetOptionsMgr()->GetBool(OPT_CMP_WEB_FIT_TO_WINDOW));
603 m_pWebDiffWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_WEB_SHOWDIFFERENCES));
604 m_pWebDiffWindow->SetShowWordDifferences(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
605 m_pWebDiffWindow->SetUserAgent(GetOptionsMgr()->GetString(OPT_CMP_WEB_USER_AGENT).c_str());
606 COLORSETTINGS colors;
607 IWebDiffWindow::ColorSettings colorSettings;
608 Options::DiffColors::Load(GetOptionsMgr(), colors);
609 colorSettings.clrDiff = colors.clrDiff;
610 colorSettings.clrDiffText = colors.clrDiffText;
611 colorSettings.clrSelDiff = colors.clrSelDiff;
612 colorSettings.clrSelDiffText = colors.clrSelDiffText;
613 colorSettings.clrSNP = colors.clrSNP;
614 colorSettings.clrSNPText = colors.clrSNPText;
615 colorSettings.clrSelSNP = colors.clrSelSNP;
616 colorSettings.clrSelSNPText = colors.clrSelSNPText;
617 colorSettings.clrWordDiff = colors.clrWordDiff;
618 colorSettings.clrWordDiffDeleted = colors.clrWordDiffDeleted;
619 colorSettings.clrWordDiffText = colors.clrWordDiffText;
620 colorSettings.clrSelWordDiff = colors.clrSelWordDiff;
621 colorSettings.clrSelWordDiffDeleted = colors.clrSelWordDiffDeleted;
622 colorSettings.clrSelWordDiffText = colors.clrSelWordDiffText;
623 m_pWebDiffWindow->SetDiffColorSettings(colorSettings);
625 IWebDiffWindow::DiffOptions diffOptions;
626 Options::DiffOptions::Load(GetOptionsMgr(), options);
627 diffOptions.bFilterCommentsLines = options.bFilterCommentsLines;
628 diffOptions.completelyBlankOutIgnoredChanges = options.bCompletelyBlankOutIgnoredChanges;
629 diffOptions.diffAlgorithm = options.nDiffAlgorithm;
630 diffOptions.ignoreBlankLines = options.bIgnoreBlankLines;
631 diffOptions.ignoreCase = options.bIgnoreCase;
632 diffOptions.ignoreEol = options.bIgnoreEol;
633 diffOptions.ignoreNumbers = options.bIgnoreNumbers;
634 diffOptions.ignoreWhitespace = options.nIgnoreWhitespace;
635 m_pWebDiffWindow->SetDiffOptions(diffOptions);
638 void CWebPageDiffFrame::SaveOptions()
640 // GetOptionsMgr()->SaveOption(OPT_CMP_WEB_DIFFCOLORALPHA, static_cast<int>(m_pWebDiffWindow->GetDiffColorAlpha() * 100.0));
641 SIZE size = m_pWebDiffWindow->GetSize();
642 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_WIDTH, size.cx);
643 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_HEIGHT, size.cy);
644 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_FIT_TO_WINDOW, m_pWebDiffWindow->GetFitToWindow());
645 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
646 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_ZOOM, static_cast<int>(m_pWebDiffWindow->GetZoom() * 1000));
647 GetOptionsMgr()->SaveOption(OPT_CMP_WEB_USER_AGENT, m_pWebDiffWindow->GetUserAgent());
651 * @brief Save coordinates of the frame, splitters, and bars
653 * @note Do not save the maximized/restored state here. We are interested
654 * in the state of the active frame, and maybe this frame is not active
656 void CWebPageDiffFrame::SavePosition()
661 // save the bars layout
662 // save docking positions and sizes
663 CDockState m_pDockState;
664 GetDockState(m_pDockState);
665 m_pDockState.SaveState(_T("Settings-WebPageDiffFrame"));
666 // for the dimensions of the diff pane, use the CSizingControlBar save
667 //m_wndLocationBar.SaveState(_T("Settings-WebPageDiffFrame"));
670 void CWebPageDiffFrame::SaveActivePane()
672 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pWebDiffWindow->GetActivePane());
675 void CWebPageDiffFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
677 CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
681 GetMainFrame()->PostMessage(WM_USER + 1);
685 void CWebPageDiffFrame::OnClose()
687 // clean up pointers.
688 CMergeFrameCommon::OnClose();
691 void CWebPageDiffFrame::OnDestroy()
693 if (!m_pWebDiffWindow)
698 * @brief Reloads the opened files
700 void CWebPageDiffFrame::OnFileReload()
702 int nActivePane = m_pWebDiffWindow->GetActivePane();
704 Callback<IWebDiffCallback>([nActivePane, this](const WebDiffCallbackResult& result) -> HRESULT
706 MoveOnLoad(nActivePane);
707 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
708 m_fileInfo[pane].Update(m_filePaths[pane]);
713 void CWebPageDiffFrame::OnFileClose()
718 void CWebPageDiffFrame::OnFileRecompareAs(UINT nID)
720 PathContext paths = m_filePaths;
721 fileopenflags_t dwFlags[3];
723 int nBuffers = m_filePaths.GetSize();
724 PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
726 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
728 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
729 strDesc[nBuffer] = m_strDesc[nBuffer];
731 if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
733 infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
734 nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE : -ID_MERGE_COMPARE_WEBPAGE;
738 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
739 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
742 void CWebPageDiffFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
744 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_WEBPAGE);
747 void CWebPageDiffFrame::OnOpenWithUnpacker()
749 CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
750 strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
751 CSelectPluginDlg::PluginType::Unpacker, false);
752 if (dlg.DoModal() == IDOK)
754 PackingInfo infoUnpacker(dlg.GetPluginPipeline());
755 PathContext paths = m_filePaths;
756 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
757 String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
759 GetMainFrame()->DoFileOpen(
760 GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE: -1,
761 &paths, dwFlags, strDesc, _T(""), &infoUnpacker);
765 void CWebPageDiffFrame::OnWindowChangePane(UINT nID)
767 int npanes = m_pWebDiffWindow->GetPaneCount();
768 int pane = m_pWebDiffWindow->GetActivePane();
769 pane = (nID == ID_NEXT_PANE) ? ((pane + 1) % npanes) : ((pane + npanes - 1) % npanes);
770 m_pWebDiffWindow->SetActivePane(pane);
774 * @brief Write path and filename to headerbar
775 * @note SetText() does not repaint unchanged text
777 void CWebPageDiffFrame::UpdateHeaderPath(int pane)
781 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
782 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
784 sText = m_strDesc[pane];
788 sText = m_filePaths.GetPath(pane);
789 if (m_pDirDoc != nullptr)
790 m_pDirDoc->ApplyDisplayRoot(pane, sText);
793 m_wndFilePathBar.SetText(pane, sText.c_str());
798 /// update splitting position for panels 1/2 and headerbar and statusbar
799 void CWebPageDiffFrame::UpdateHeaderSizes()
801 if (m_pWebDiffWindow != nullptr)
804 CRect rc, rcMergeWindow;
805 int nPaneCount = m_pWebDiffWindow->GetPaneCount();
807 ::GetWindowRect(m_pWebDiffWindow->GetHWND(), &rcMergeWindow);
808 ScreenToClient(rcMergeWindow);
809 if (!m_pWebDiffWindow->GetHorizontalSplit())
811 for (int pane = 0; pane < nPaneCount; pane++)
813 RECT rc1 = m_pWebDiffWindow->GetPaneWindowRect(pane);
814 w[pane] = rc1.right - rc1.left - 4;
815 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
820 for (int pane = 0; pane < nPaneCount; pane++)
821 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
824 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
826 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
828 // resize controls in header dialog bar
829 m_wndFilePathBar.Resize(w);
831 rc.left = rcMergeWindow.left;
832 rc.top = rc.bottom - m_rectBorder.bottom;
834 for (int pane = 0; pane < nPaneCount; pane++)
836 rc.right += w[pane] + 4 + 2;
837 // m_wndStatusBar[pane].MoveWindow(&rc);
845 * @brief Update document filenames to title
847 void CWebPageDiffFrame::SetTitle(LPCTSTR lpszTitle)
849 String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr);
850 CMergeFrameCommon::SetTitle(sTitle.c_str());
851 if (m_hWnd != nullptr)
853 SetWindowText(sTitle.c_str());
854 GetMainFrame()->OnUpdateFrameTitle(TRUE);
858 void CWebPageDiffFrame::UpdateLastCompareResult()
860 if (m_bCompareCompleted)
861 SetLastCompareResult(m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0);
864 void CWebPageDiffFrame::UpdateAutoPaneResize()
868 void CWebPageDiffFrame::UpdateSplitter()
872 bool CWebPageDiffFrame::OpenUrls(IWebDiffCallback* callback)
875 String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
876 String strTempFileName[3];
877 m_infoUnpacker.EnableWebBrowserMode();
878 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
880 strTempFileName[pane] = m_filePaths[pane];
881 if (!m_infoUnpacker.Unpacking(&m_unpackerSubcodes[pane], strTempFileName[pane], filteredFilenames, {strTempFileName[pane]}))
886 if (m_filePaths.GetSize() == 2)
887 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), callback));
889 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str(), callback));
894 * @brief Update associated diff item
896 int CWebPageDiffFrame::UpdateDiffItem(CDirDoc *pDirDoc)
898 // If directory compare has results
899 if (pDirDoc && pDirDoc->HasDiffs())
902 // const String &pathLeft = m_filePaths.GetLeft();
903 // const String &pathRight = m_filePaths.GetRight();
904 // CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
905 // if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
907 // DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
908 // ::UpdateDiffItem(m_nBuffers, di, &ctxt);
911 int result = m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0;
912 SetLastCompareResult(result != 0);
916 /// Document commanding us to close
917 bool CWebPageDiffFrame::CloseNow()
924 * @brief A string to display as a tooltip for MDITabbar
926 CString CWebPageDiffFrame::GetTooltipString() const
928 return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
932 * @brief Update any resources necessary after a GUI language change
934 void CWebPageDiffFrame::UpdateResources()
936 //m_pWebToolWindow->Translate(TranslateLocationPane);
939 void CWebPageDiffFrame::RefreshOptions()
945 * @brief Handle some keys when in merging mode
947 bool CWebPageDiffFrame::MergeModeKeyDown(MSG* pMsg)
949 bool bHandled = false;
951 // Allow default text selection when SHIFT pressed
952 if (::GetAsyncKeyState(VK_SHIFT))
955 // Allow default editor functions when CTRL pressed
956 if (::GetAsyncKeyState(VK_CONTROL))
959 // If we are in merging mode (merge with cursor keys)
960 // handle some keys here
961 switch (pMsg->wParam)
976 * @brief Check for keyboard commands
978 BOOL CWebPageDiffFrame::PreTranslateMessage(MSG* pMsg)
980 if (pMsg->message == WM_KEYDOWN)
982 // If we are in merging mode (merge with cursor keys)
983 // handle some keys here
984 if (theApp.GetMergingMode())
986 bool bHandled = MergeModeKeyDown(pMsg);
991 // Close window in response to VK_ESCAPE if user has allowed it from options
992 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) != 0)
994 PostMessage(WM_CLOSE, 0, 0);
998 return CMergeFrameCommon::PreTranslateMessage(pMsg);
1001 void CWebPageDiffFrame::OnSize(UINT nType, int cx, int cy)
1003 CMergeFrameCommon::OnSize(nType, cx, cy);
1004 UpdateHeaderSizes();
1008 * @brief Synchronize control and status bar placements with splitter position,
1009 * update mod indicators, synchronize scrollbars
1011 void CWebPageDiffFrame::OnIdleUpdateCmdUI()
1013 if (IsWindowVisible())
1015 int nActivePane = m_pWebDiffWindow->GetActivePane();
1016 if (nActivePane != -1)
1017 m_nActivePane = nActivePane;
1019 UpdateHeaderSizes();
1020 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1022 // Update mod indicators
1023 m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1025 UpdateLastCompareResult();
1027 CMergeFrameCommon::OnIdleUpdateCmdUI();
1031 * @brief Save pane sizes and positions when one of panes requests it.
1033 LRESULT CWebPageDiffFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1039 void CWebPageDiffFrame::UpdateWebPageDiffBar()
1041 m_bInUpdateWebPageDiffBar = true;
1042 bool fitToWindow = m_pWebDiffWindow->GetFitToWindow();
1043 m_wndWebPageDiffBar.CheckDlgButton(IDC_FITTOWINDOW, fitToWindow);
1044 m_wndWebPageDiffBar.CheckDlgButton(IDC_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
1045 m_wndWebPageDiffBar.EnableDlgItem(IDC_WIDTH, !fitToWindow);
1046 m_wndWebPageDiffBar.EnableDlgItem(IDC_HEIGHT, !fitToWindow);
1047 SIZE size = m_pWebDiffWindow->GetSize();
1048 m_wndWebPageDiffBar.SetDlgItemText(IDC_WIDTH, strutils::format(_T("%d"), size.cx));
1049 m_wndWebPageDiffBar.SetDlgItemText(IDC_HEIGHT, strutils::format(_T("%d"), size.cy));
1050 m_wndWebPageDiffBar.SetDlgItemText(IDC_ZOOM, strutils::format(_T("%.1f%%"),
1051 m_pWebDiffWindow->GetZoom() * 100.0));
1052 m_wndWebPageDiffBar.SetDlgItemText(IDC_USERAGENT, m_pWebDiffWindow->GetUserAgent());
1053 m_bInUpdateWebPageDiffBar = false;
1056 void CWebPageDiffFrame::OnBnClickedFitToWindow()
1058 m_pWebDiffWindow->SetFitToWindow(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_FITTOWINDOW));
1059 UpdateWebPageDiffBar();
1062 void CWebPageDiffFrame::OnBnClickedShowDifferences()
1064 m_pWebDiffWindow->SetShowDifferences(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_SHOWDIFFERENCES));
1065 UpdateWebPageDiffBar();
1068 void CWebPageDiffFrame::OnBnClickedCompare()
1073 void CWebPageDiffFrame::OnDropDownCompare(NMHDR *pNMHDR, LRESULT *pResult)
1076 m_wndWebPageDiffBar.GetDlgItem(IDC_COMPARE)->GetWindowRect(&rc);
1077 CPoint point { rc.left, rc.bottom };
1079 menuPopup.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_WEBPAGE_COMPARE));
1080 theApp.TranslateMenu(menuPopup.m_hMenu);
1081 BCMenu* pPopup = (BCMenu*)menuPopup.GetSubMenu(0);
1082 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
1083 point.x, point.y, AfxGetMainWnd());
1086 void CWebPageDiffFrame::OnEnChangeWidth()
1088 if (m_bInUpdateWebPageDiffBar)
1091 m_wndWebPageDiffBar.GetDlgItemText(IDC_WIDTH, text);
1092 int v = tc::ttoi(text.c_str());
1093 if (v > 0 && v < 32678)
1095 SIZE size = m_pWebDiffWindow->GetSize();
1097 m_pWebDiffWindow->SetSize(size);
1101 void CWebPageDiffFrame::OnEnChangeHeight()
1103 if (m_bInUpdateWebPageDiffBar)
1106 m_wndWebPageDiffBar.GetDlgItemText(IDC_HEIGHT, text);
1107 int v = tc::ttoi(text.c_str());
1108 if (v > 0 && v < 32678)
1110 SIZE size = m_pWebDiffWindow->GetSize();
1112 m_pWebDiffWindow->SetSize(size);
1116 void CWebPageDiffFrame::OnEnChangeZoom()
1118 if (m_bInUpdateWebPageDiffBar)
1121 m_wndWebPageDiffBar.GetDlgItemText(IDC_ZOOM, text);
1122 tchar_t* endptr = nullptr;
1123 double v = tc::tcstod(text.c_str(), &endptr);
1124 if ((* endptr == '\0' || *endptr == '%') && v >= 25.0 && v <= 500.0)
1125 m_pWebDiffWindow->SetZoom(v / 100.0);
1128 void CWebPageDiffFrame::OnEnChangeUserAgent()
1130 if (m_bInUpdateWebPageDiffBar)
1133 m_wndWebPageDiffBar.GetDlgItemText(IDC_USERAGENT, text);
1134 m_pWebDiffWindow->SetUserAgent(text.c_str());
1137 void CWebPageDiffFrame::OnKillFocusBarControls()
1139 UpdateWebPageDiffBar();
1142 m_wndFilePathBar.GetDlgItemTextW(IDC_USERAGENT, text);
1145 void CWebPageDiffFrame::OnUpdateStatusNum(CCmdUI* pCmdUI)
1147 tchar_t sIdx[32] = { 0 };
1148 tchar_t sCnt[32] = { 0 };
1150 const int nDiffs = m_pWebDiffWindow->GetDiffCount();
1152 // Files are identical - show text "Identical"
1154 s = theApp.LoadString(IDS_IDENTICAL);
1156 // There are differences, but no selected diff
1157 // - show amount of diffs
1158 else if (m_pWebDiffWindow->GetCurrentDiffIndex() < 0)
1160 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1161 _itot_s(nDiffs, sCnt, 10);
1162 strutils::replace(s, _T("%1"), sCnt);
1165 // There are differences and diff selected
1166 // - show diff number and amount of diffs
1169 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1170 const int signInd = m_pWebDiffWindow->GetCurrentDiffIndex();
1171 _itot_s(signInd + 1, sIdx, 10);
1172 strutils::replace(s, _T("%1"), sIdx);
1173 _itot_s(nDiffs, sCnt, 10);
1174 strutils::replace(s, _T("%2"), sCnt);
1176 pCmdUI->SetText(s.c_str());
1180 * @brief Cut current selection to clipboard
1182 void CWebPageDiffFrame::OnEditCut()
1184 m_pWebDiffWindow->Cut();
1188 * @brief Copy current selection to clipboard
1190 void CWebPageDiffFrame::OnEditCopy()
1192 m_pWebDiffWindow->Copy();
1196 * @brief Paste clipboard content over selected content
1198 void CWebPageDiffFrame::OnEditPaste()
1200 m_pWebDiffWindow->Paste();
1204 * @brief Undo last action
1206 void CWebPageDiffFrame::OnEditUndo()
1208 m_pWebDiffWindow->Undo();
1212 * @brief Redo last action
1214 void CWebPageDiffFrame::OnEditRedo()
1216 m_pWebDiffWindow->Redo();
1220 * @brief Update the tool bar's "Undo" icon. It should be enabled when
1221 * renaming an item and undo is possible.
1223 void CWebPageDiffFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1225 pCmdUI->Enable(m_pWebDiffWindow->CanUndo());
1229 * @brief Update the tool bar's "Redo" icon. It should be enabled when
1230 * renaming an item and undo is possible.
1232 void CWebPageDiffFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1234 pCmdUI->Enable(m_pWebDiffWindow->CanRedo());
1238 * @brief Select entire image
1240 void CWebPageDiffFrame::OnEditSelectAll()
1242 m_pWebDiffWindow->SelectAll();
1246 * @brief Called when user selects View/Zoom In from menu.
1248 void CWebPageDiffFrame::OnViewZoomIn()
1250 m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() + 0.1);
1254 * @brief Called when user selects View/Zoom Out from menu.
1256 void CWebPageDiffFrame::OnViewZoomOut()
1258 m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() - 0.1);
1262 * @brief Called when user selects View/Zoom Normal from menu.
1264 void CWebPageDiffFrame::OnViewZoomNormal()
1266 m_pWebDiffWindow->SetZoom(1.0);
1270 * @brief Enables/disables linediff (different color for diffs)
1272 void CWebPageDiffFrame::OnViewLineDiffs()
1274 bool bWordDiffHighlight = !GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
1275 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, bWordDiffHighlight);
1276 m_pWebDiffWindow->SetShowWordDifferences(bWordDiffHighlight);
1279 void CWebPageDiffFrame::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
1281 pCmdUI->Enable(true);
1282 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
1286 * @brief Split panes vertically
1288 void CWebPageDiffFrame::OnViewSplitVertically()
1290 bool bSplitVertically = !m_pWebDiffWindow->GetHorizontalSplit();
1291 bSplitVertically = !bSplitVertically; // toggle
1292 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1293 m_pWebDiffWindow->SetHorizontalSplit(!bSplitVertically);
1297 * @brief Update "Split Vertically" UI items
1299 void CWebPageDiffFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
1301 pCmdUI->Enable(TRUE);
1302 pCmdUI->SetCheck(!m_pWebDiffWindow->GetHorizontalSplit());
1306 * @brief Go to first diff
1308 * Called when user selects "First Difference"
1309 * @sa CWebPageDiffFrame::SelectDiff()
1311 void CWebPageDiffFrame::OnFirstdiff()
1313 m_pWebDiffWindow->FirstDiff();
1317 * @brief Update "First diff" UI items
1319 void CWebPageDiffFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1321 OnUpdatePrevdiff(pCmdUI);
1325 * @brief Go to last diff
1327 void CWebPageDiffFrame::OnLastdiff()
1329 m_pWebDiffWindow->LastDiff();
1333 * @brief Update "Last diff" UI items
1335 void CWebPageDiffFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1337 OnUpdateNextdiff(pCmdUI);
1341 * @brief Go to next diff and select it.
1343 void CWebPageDiffFrame::OnNextdiff()
1345 if (m_pWebDiffWindow->GetCurrentDiffIndex() != m_pWebDiffWindow->GetDiffCount() - 1)
1346 m_pWebDiffWindow->NextDiff();
1347 else if (m_pDirDoc != nullptr)
1348 m_pDirDoc->MoveToNextDiff(this);
1352 * @brief Update "Next diff" UI items
1354 void CWebPageDiffFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1356 bool enabled = m_bCompareCompleted && (
1357 m_pWebDiffWindow->GetNextDiffIndex() >= 0 ||
1358 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1360 if (!enabled && m_pDirDoc != nullptr)
1361 enabled = m_pDirDoc->MoveableToNextDiff();
1362 pCmdUI->Enable(enabled);
1366 * @brief Go to previous diff and select it.
1368 void CWebPageDiffFrame::OnPrevdiff()
1370 if (m_pWebDiffWindow->GetCurrentDiffIndex() > 0)
1372 m_pWebDiffWindow->PrevDiff();
1374 else if (m_pDirDoc != nullptr)
1375 m_pDirDoc->MoveToPrevDiff(this);
1379 * @brief Update "Previous diff" UI items
1381 void CWebPageDiffFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1383 bool enabled = m_bCompareCompleted && (
1384 m_pWebDiffWindow->GetPrevDiffIndex() >= 0 ||
1385 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1387 if (!enabled && m_pDirDoc != nullptr)
1388 enabled = m_pDirDoc->MoveableToPrevDiff();
1389 pCmdUI->Enable(enabled);
1393 * @brief Go to next conflict and select it.
1395 void CWebPageDiffFrame::OnNextConflict()
1397 m_pWebDiffWindow->NextConflict();
1401 * @brief Update "Next Conflict" UI items
1403 void CWebPageDiffFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1405 pCmdUI->Enable(m_bCompareCompleted &&
1406 m_pWebDiffWindow->GetPaneCount() > 2 && (
1407 m_pWebDiffWindow->GetNextConflictIndex() >= 0 ||
1408 (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1414 * @brief Go to previous diff and select it.
1416 void CWebPageDiffFrame::OnPrevConflict()
1418 m_pWebDiffWindow->PrevConflict();
1422 * @brief Update "Previous diff" UI items
1424 void CWebPageDiffFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1426 pCmdUI->Enable(m_bCompareCompleted &&
1427 m_pWebDiffWindow->GetPaneCount() > 2 && (
1428 m_pWebDiffWindow->GetPrevConflictIndex() >= 0 ||
1429 (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1434 void CWebPageDiffFrame::OnWebFitToWindow()
1436 m_pWebDiffWindow->SetFitToWindow(true);
1438 UpdateWebPageDiffBar();
1441 void CWebPageDiffFrame::OnUpdateWebFitToWindow(CCmdUI* pCmdUI)
1443 pCmdUI->SetCheck(m_pWebDiffWindow->GetFitToWindow());
1446 void CWebPageDiffFrame::OnWebSize(UINT nID)
1450 case ID_WEB_SIZE_320x512:
1451 m_pWebDiffWindow->SetFitToWindow(false);
1452 m_pWebDiffWindow->SetSize({ 320, 512 });
1453 UpdateWebPageDiffBar();
1455 case ID_WEB_SIZE_375x600:
1456 m_pWebDiffWindow->SetFitToWindow(false);
1457 m_pWebDiffWindow->SetSize({ 375, 600 });
1458 UpdateWebPageDiffBar();
1460 case ID_WEB_SIZE_1024x640:
1461 m_pWebDiffWindow->SetFitToWindow(false);
1462 m_pWebDiffWindow->SetSize({ 1024, 640 });
1464 UpdateWebPageDiffBar();
1466 case ID_WEB_SIZE_1280x800:
1467 m_pWebDiffWindow->SetFitToWindow(false);
1468 m_pWebDiffWindow->SetSize({ 1280, 800 });
1470 UpdateWebPageDiffBar();
1472 case ID_WEB_SIZE_1440x900:
1473 m_pWebDiffWindow->SetFitToWindow(false);
1474 m_pWebDiffWindow->SetSize({ 1440, 900 });
1476 UpdateWebPageDiffBar();
1481 void CWebPageDiffFrame::OnWebCompareScreenshots(UINT nID)
1483 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1485 const wchar_t *spaths[3];
1486 std::vector<String> descs;
1487 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1488 for (int pane = 0; pane < nPanes; ++pane)
1490 std::shared_ptr<TempFile> pTempFile(new TempFile());
1491 pTempFile->Create(_T("SCR"), _T(".png"));
1492 paths.SetPath(pane, pTempFile->GetPath());
1493 spaths[pane] = paths[pane].c_str();
1494 descs.push_back(m_filePaths[pane]);
1495 m_tempFiles.push_back(pTempFile);
1497 m_pWebDiffWindow->SaveFiles(
1498 (nID == ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS) ? IWebDiffWindow::FULLSIZE_SCREENSHOT : IWebDiffWindow::SCREENSHOT,
1500 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1502 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1503 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data());
1508 void CWebPageDiffFrame::OnWebCompareHTMLs()
1510 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1512 const wchar_t *spaths[3];
1513 std::vector<String> descs;
1514 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1515 for (int pane = 0; pane < nPanes; ++pane)
1517 std::shared_ptr<TempFile> pTempFile(new TempFile());
1518 pTempFile->Create(_T("HTM"), _T(".html"));
1519 paths.SetPath(pane, pTempFile->GetPath());
1520 spaths[pane] = paths[pane].c_str();
1521 descs.push_back(m_filePaths[pane]);
1522 m_tempFiles.push_back(pTempFile);
1524 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::HTML, spaths,
1525 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1527 PackingInfo infoUnpacker(String(_T("PrettifyHTML")));
1528 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1529 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""), &infoUnpacker);
1534 void CWebPageDiffFrame::OnWebCompareTexts()
1536 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1538 const wchar_t *spaths[3];
1539 std::vector<String> descs;
1540 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1541 for (int pane = 0; pane < nPanes; ++pane)
1543 std::shared_ptr<TempFile> pTempFile(new TempFile());
1544 pTempFile->Create(_T("TXT"), _T(".txt"));
1545 paths.SetPath(pane, pTempFile->GetPath());
1546 spaths[pane] = paths[pane].c_str();
1547 descs.push_back(m_filePaths[pane]);
1548 m_tempFiles.push_back(pTempFile);
1550 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::TEXT, spaths,
1551 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1553 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1554 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""));
1559 void CWebPageDiffFrame::OnWebCompareResourceTrees()
1561 std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1563 const wchar_t *spaths[3];
1564 std::vector<String> descs;
1565 const int nPanes = m_pWebDiffWindow->GetPaneCount();
1566 for (int pane = 0; pane < nPanes; ++pane)
1568 std::shared_ptr<TempFolder> pTempFolder(new TempFolder());
1569 pTempFolder->Create();
1570 paths.SetPath(pane, pTempFolder->GetPath());
1571 spaths[pane] = paths[pane].c_str();
1572 descs.push_back(m_filePaths[pane]);
1573 m_tempFolders.push_back(pTempFolder);
1575 m_pWebDiffWindow->SaveFiles(IWebDiffWindow::RESOURCETREE, spaths,
1576 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1578 fileopenflags_t dwFlags[3]{};
1579 for (int pane = 0; pane < paths.GetSize(); ++pane)
1580 dwFlags[pane] = FFILEOPEN_NOMRU;
1581 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, descs.data(), _T(""), true);
1586 void CWebPageDiffFrame::OnWebClear(UINT nID)
1588 IWebDiffWindow::BrowsingDataType dataKinds;
1591 case ID_WEB_CLEAR_DISK_CACHE: dataKinds = IWebDiffWindow::BrowsingDataType::DISK_CACHE; break;
1592 case ID_WEB_CLEAR_COOKIES: dataKinds = IWebDiffWindow::BrowsingDataType::COOKIES; break;
1593 case ID_WEB_CLEAR_BROWSING_HISTORY: dataKinds = IWebDiffWindow::BrowsingDataType::BROWSING_HISTORY; break;
1594 case ID_WEB_CLEAR_ALL_PROFILE: dataKinds = IWebDiffWindow::BrowsingDataType::ALL_PROFILE; break;
1598 m_pWebDiffWindow->ClearBrowsingData(-1, dataKinds);
1601 bool CWebPageDiffFrame::GenerateReport(const String& sFileName) const
1603 bool result = false;
1604 bool completed = false;
1605 if (!GenerateReport(sFileName, [&completed, &result](bool res) { result = res; completed = true; }))
1607 CMainFrame::WaitAndDoMessageLoop(completed, 0);
1611 bool CWebPageDiffFrame::GenerateReport(const String& sFileName, std::function<void(bool)> callback) const
1613 String rptdir_full, rptdir, path, name, ext;
1615 String diffrpt_filename[3];
1616 String diffrpt_filename_full[3];
1617 const wchar_t* pfilenames[3]{};
1618 paths::SplitFilename(sFileName, &path, &name, &ext);
1619 rptdir_full = paths::ConcatPath(path, name) + _T(".files");
1620 rptdir = paths::FindFileName(rptdir_full);
1621 paths::CreateIfNeeded(rptdir_full);
1623 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1625 url[pane] = ucr::toTString(m_pWebDiffWindow->GetCurrentUrl(pane));
1626 diffrpt_filename[pane] = strutils::format(_T("%s/%d.pdf"), rptdir, pane + 1);
1627 diffrpt_filename_full[pane] = strutils::format(_T("%s/%d.pdf"), rptdir_full, pane + 1);
1628 pfilenames[pane] = diffrpt_filename_full[pane].c_str();
1632 if (!file.Open(sFileName, _T("wt")))
1634 String errMsg = GetSysError(GetLastError());
1635 String msg = strutils::format_string1(
1636 _("Error creating the report:\n%1"), errMsg);
1637 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1641 file.SetCodepage(ucr::CP_UTF_8);
1644 _T("<!DOCTYPE html>\n")
1647 _T("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n")
1648 _T("<title>WinMerge Webpage Compare Report</title>\n")
1649 _T("<style type=\"text/css\">\n")
1650 _T("table { table-layout: fixed; width: 100%; border-collapse: collapse; }\n")
1651 _T("th {position: sticky; top: 0;}\n")
1652 _T("td,th { border: solid 1px black; }\n")
1653 _T("embed { width: 100%; height: calc(100vh - 56px) }\n")
1654 _T(".title { color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
1660 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1661 file.WriteString(strutils::format(_T("<th class=\"title\">%s</th>\n"), url[pane]));
1662 file.WriteString(_T("</tr>\n"));
1665 for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1667 strutils::format(_T("<td><embed type=\"application/pdf\" src=\"%s\" title=\"%s\"></td>\n"),
1668 diffrpt_filename[pane], diffrpt_filename[pane]));
1676 return SUCCEEDED(m_pWebDiffWindow->SaveDiffFiles(IWebDiffWindow::PDF, pfilenames,
1677 Callback<IWebDiffCallback>([this, callback](const WebDiffCallbackResult& result) -> HRESULT
1679 callback(SUCCEEDED(result.errorCode));
1685 * @brief Generate report from file compare results.
1687 void CWebPageDiffFrame::OnToolsGenerateReport()
1691 if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
1694 CWaitCursor waitstatus;
1695 if (GenerateReport(s))
1696 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION | MB_MODELESS);
1699 void CWebPageDiffFrame::OnRefresh()
1701 if (!m_bCompareCompleted)
1703 m_bCompareCompleted = false;
1704 m_pWebDiffWindow->Recompare(
1705 Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
1707 m_bCompareCompleted = true;
1708 if (UpdateDiffItem(m_pDirDoc) == 0 &&
1709 std::count(m_filePaths.begin(), m_filePaths.end(), L"about:blank") != m_filePaths.GetSize())
1711 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
1712 [](const tchar_t* msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
1719 void CWebPageDiffFrame::OnSetFocus(CWnd* pNewWnd)
1721 if (m_nActivePane != -1)
1722 m_pWebDiffWindow->SetActivePane(m_nActivePane);
1726 * @brief Open help from mainframe when user presses F1
1728 void CWebPageDiffFrame::OnHelp()
1730 theApp.ShowHelp(WebPageDiffFrameHelpLocation);