OSDN Git Service

Update TranslationsStatus
[winmerge-jp/winmerge-jp.git] / Src / WebPageDiffFrm.cpp
1 /** 
2  * @file  WebPageDiffFrm.cpp
3  *
4  * @brief Implementation file for CWebPageDiffFrame
5  *
6  */
7
8 #include "stdafx.h"
9 #include "WebPageDiffFrm.h"
10 #include "Merge.h"
11 #include "MainFrm.h"
12 #include "BCMenu.h"
13 #include "DirDoc.h"
14 #include "OptionsDef.h"
15 #include "OptionsMgr.h"
16 #include "OptionsDiffColors.h"
17 #include "OptionsDiffOptions.h"
18 #include "CompareOptions.h"
19 #include "paths.h"
20 #include "PathContext.h"
21 #include "unicoder.h"
22 #include "FileOrFolderSelect.h"
23 #include "SelectPluginDlg.h"
24 #include "FileLocation.h"
25 #include "Constants.h"
26 #include "Environment.h"
27 #include <Poco/RegularExpression.h>
28
29 #ifdef _DEBUG
30 #define new DEBUG_NEW
31 #endif
32
33 #ifndef BCN_DROPDOWN
34 #define BCN_DROPDOWN (BCN_FIRST + 0x0002)
35 #endif
36
37 template <typename T, typename Func>
38 struct CallbackImpl : public T
39 {
40         CallbackImpl(Func&& callback) : m_callback(std::move(callback)) {}
41         HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
42         ULONG STDMETHODCALLTYPE AddRef(void) override { return ++m_nRef; }
43         ULONG STDMETHODCALLTYPE Release(void) override { if (--m_nRef == 0) { delete this; return 0; } return m_nRef; }
44         HRESULT STDMETHODCALLTYPE Invoke(const WebDiffCallbackResult& result) { return m_callback(result); }
45         HRESULT STDMETHODCALLTYPE Invoke(const WebDiffEvent& event) { return m_callback(event); }
46         Func m_callback;
47         int m_nRef = 0;
48 };
49
50 template<typename T, typename Func>
51 CComPtr<T> Callback(Func&& callback)
52 {
53         return CComPtr<T>(new CallbackImpl<T, Func>(std::move(callback)));
54 }
55  
56 /////////////////////////////////////////////////////////////////////////////
57 // CWebPageDiffFrame
58
59 IMPLEMENT_DYNCREATE(CWebPageDiffFrame, CMergeFrameCommon)
60
61 BEGIN_MESSAGE_MAP(CWebPageDiffFrame, CMergeFrameCommon)
62         //{{AFX_MSG_MAP(CWebPageDiffFrame)
63         ON_WM_CREATE()
64         ON_WM_CLOSE()
65         ON_WM_DESTROY()
66         ON_WM_MDIACTIVATE()
67         ON_WM_SIZE()
68         ON_WM_SETFOCUS ()       
69         ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
70         ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
71         // [File] menu
72         ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
73         ON_COMMAND(ID_RESCAN, OnFileReload)
74         ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnFileRecompareAs)
75         ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateFileRecompareAs)
76         // [Edit] menu
77         ON_COMMAND(ID_EDIT_CUT, OnEditCut)
78         ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
79         ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
80         ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
81         ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
82         ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
83         ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
84         ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
85         // [View] menu
86         ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
87         ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
88         ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
89         ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
90         ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
91         ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
92         ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
93         ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
94         ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
95         ON_COMMAND(ID_REFRESH, OnRefresh)
96         // [Merge] menu
97         ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
98         ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
99         ON_COMMAND(ID_LASTDIFF, OnLastdiff)
100         ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
101         ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
102         ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
103         ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
104         ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
105         ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
106         ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
107         ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
108         ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
109         // [Web] menu
110         ON_COMMAND(ID_WEB_SIZE_FIT_TO_WINDOW, OnWebFitToWindow)
111         ON_UPDATE_COMMAND_UI(ID_WEB_SIZE_FIT_TO_WINDOW, OnUpdateWebFitToWindow)
112         ON_COMMAND_RANGE(ID_WEB_SIZE_320x512, ID_WEB_SIZE_1440x900, OnWebSize)
113         ON_COMMAND_RANGE(ID_WEB_COMPARE_SCREENSHOTS, ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS, OnWebCompareScreenshots)
114         ON_COMMAND(ID_WEB_COMPARE_HTMLS, OnWebCompareHTMLs)
115         ON_COMMAND(ID_WEB_COMPARE_TEXTS, OnWebCompareTexts)
116         ON_COMMAND(ID_WEB_COMPARE_RESOURCETREES, OnWebCompareResourceTrees)
117         ON_COMMAND_RANGE(ID_WEB_CLEAR_DISK_CACHE, ID_WEB_CLEAR_ALL_PROFILE, OnWebClear)
118         // [Tools] menu
119 //      ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
120         // [Plugins] menu
121         ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
122         ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
123         // [Window] menu
124         ON_COMMAND_RANGE(ID_NEXT_PANE, ID_PREV_PANE, OnWindowChangePane)
125         // [Help] menu
126         ON_COMMAND(ID_HELP, OnHelp)
127         // Dialog bar
128         ON_BN_CLICKED(IDC_FITTOWINDOW, OnBnClickedFitToWindow)
129         ON_BN_CLICKED(IDC_SHOWDIFFERENCES, OnBnClickedShowDifferences)
130         ON_BN_CLICKED(IDC_COMPARE, OnBnClickedCompare)
131         ON_EN_CHANGE(IDC_WIDTH, OnEnChangeWidth)
132         ON_EN_CHANGE(IDC_HEIGHT, OnEnChangeHeight)
133         ON_EN_CHANGE(IDC_ZOOM, OnEnChangeZoom)
134         ON_EN_CHANGE(IDC_USERAGENT, OnEnChangeUserAgent)
135         ON_EN_KILLFOCUS(IDC_WIDTH, OnKillFocusBarControls)
136         ON_EN_KILLFOCUS(IDC_HEIGHT, OnKillFocusBarControls)
137         ON_EN_KILLFOCUS(IDC_ZOOM, OnKillFocusBarControls)
138         ON_EN_KILLFOCUS(IDC_USERAGENT, OnKillFocusBarControls)
139         ON_NOTIFY(BCN_DROPDOWN, IDC_COMPARE, OnDropDownCompare)
140         // Status bar
141         ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
142         //}}AFX_MSG_MAP
143 END_MESSAGE_MAP()
144
145 CMenu CWebPageDiffFrame::menu;
146
147 /////////////////////////////////////////////////////////////////////////////
148 // CWebPageDiffFrame construction/destruction
149
150 CWebPageDiffFrame::CWebPageDiffFrame()
151 : CMergeFrameCommon(IDI_EQUALWEBPAGE, IDI_NOTEQUALWEBPAGE)
152 , m_pDirDoc(nullptr)
153 , m_bAutoMerged(false)
154 , m_pWebDiffWindow(nullptr)
155 //, m_pWebToolWindow(nullptr)
156 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
157 , m_bRO{}
158 , m_nActivePane(-1)
159 , m_bInUpdateWebPageDiffBar(false)
160 , m_bCompareCompleted(false)
161 {
162 }
163
164 CWebPageDiffFrame::~CWebPageDiffFrame()
165 {
166         GetMainFrame()->UnwatchDocuments(this);
167
168         if (m_pDirDoc != nullptr)
169         {
170                 m_pDirDoc->MergeDocClosing(this);
171                 m_pDirDoc = nullptr;
172         }
173
174         HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
175         if (hModule != nullptr)
176         {
177                 bool (*pfnWinWebDiff_DestroyWindow)(IWebDiffWindow *) = 
178                         (bool (*)(IWebDiffWindow *))GetProcAddress(hModule, "WinWebDiff_DestroyWindow");
179 //              bool (*pfnWinWebDiff_DestroyToolWindow)(IWebToolWindow *) = 
180 //                      (bool (*)(IWebToolWindow *))GetProcAddress(hModule, "WinWebDiff_DestroyToolWindow");
181                 if (pfnWinWebDiff_DestroyWindow != nullptr/* && pfnWinWebDiff_DestroyToolWindow != nullptr*/)
182                 {
183                         if (m_pWebDiffWindow != nullptr)
184                                 pfnWinWebDiff_DestroyWindow(m_pWebDiffWindow);
185 //                      if (m_pWebToolWindow != nullptr)
186 //                              pfnWinWebDiff_DestroyToolWindow(m_pWebToolWindow);
187                         m_pWebDiffWindow = nullptr;
188 //                      m_pWebToolWindow = nullptr;
189                 }
190         }
191 }
192
193 bool CWebPageDiffFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent, std::function<void ()> callback)
194 {
195         m_callbackOnOpenCompleted = callback;
196         m_bCompareCompleted = false;
197         
198         CWaitCursor waitstatus;
199         int nNormalBuffer = 0;
200         for (int pane = 0; pane < nFiles; ++pane)
201         {
202                 m_filePaths.SetPath(pane, fileloc[pane].filepath, false);
203                 m_bRO[pane] = bRO[pane];
204                 m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
205                 if (fileloc[pane].filepath.empty())
206                         m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
207                 else
208                 {
209                         m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
210                         ++nNormalBuffer;
211                 }
212         }
213         SetTitle(nullptr);
214
215         LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
216                         ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
217
218         if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
219                 return false;
220
221         int nCmdShow = SW_SHOW;
222         if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
223                 nCmdShow = SW_SHOWMAXIMIZED;
224         ShowWindow(nCmdShow);
225         BringToTop(nCmdShow);
226
227         GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
228
229         GetMainFrame()->WatchDocuments(this);
230
231         return true;
232 }
233
234 void CWebPageDiffFrame::MoveOnLoad(int nPane, int)
235 {
236         if (nPane < 0)
237         {
238                 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
239                 if (nPane < 0 || nPane >= m_pWebDiffWindow->GetPaneCount())
240                         nPane = 0;
241         }
242
243         m_pWebDiffWindow->SetActivePane(nPane);
244
245         if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST) && m_pWebDiffWindow->GetDiffCount() > 0)
246                 m_pWebDiffWindow->SelectDiff(0);
247 }
248
249 /**
250  * @brief DirDoc gives us its identity just after it creates us
251  */
252 void CWebPageDiffFrame::SetDirDoc(CDirDoc * pDirDoc)
253 {
254         ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
255         m_pDirDoc = pDirDoc;
256 }
257
258 IMergeDoc::FileChange CWebPageDiffFrame::IsFileChangedOnDisk(int pane) const
259 {
260         DiffFileInfo dfi;
261         if (!dfi.Update(m_filePaths[pane]))
262                 return FileChange::Removed;
263         int tolerance = 0;
264         if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
265                 tolerance = SmallTimeDiff; // From MainFrm.h
266         int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
267         if (timeDiff < 0) timeDiff = -timeDiff;
268         if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
269                 return FileChange::Changed;
270         return FileChange::NoChange;
271 }
272
273 void CWebPageDiffFrame::CheckFileChanged(void)
274 {
275         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
276         {
277                 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
278                 {
279                         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]);
280                         if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
281                         {
282                                 OnFileReload();
283                         }
284                         break;
285                 }
286         }
287 }
288
289 /**
290  * @brief Create a status bar to be associated with a heksedit control
291  */
292 void CWebPageDiffFrame::CreateWebWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
293 {
294         wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
295         wndStatusBar.SetIndicators(0, 1);
296         wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
297         wndStatusBar.SetParent(this);
298         wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
299 }
300
301 static bool isTempFile(const String& path)
302 {
303         String tmpDir = env::GetTemporaryPath();
304         strutils::replace(tmpDir, _T("\\"), _T("/"));
305         tmpDir = _T("file:///") + tmpDir;
306         return (path.find(tmpDir) == 0);
307 }
308
309 void CWebPageDiffFrame::OnWebDiffEvent(const WebDiffEvent& event)
310 {
311         switch (event.type)
312         {
313         case WebDiffEvent::SourceChanged:
314         case WebDiffEvent::TabChanged:
315         {
316                 if (m_nBufferType[event.pane] == BUFFERTYPE::UNNAMED)
317                 {
318                         m_nBufferType[event.pane] = BUFFERTYPE::NORMAL;
319                         m_strDesc[event.pane].clear();
320                 }
321                 String curUrl = m_pWebDiffWindow->GetCurrentUrl(event.pane);
322                 if (!isTempFile(curUrl) && curUrl != _T("about:blank"))
323                 {
324                         m_filePaths[event.pane] = paths::isFileURL(curUrl) ? paths::FromURL(curUrl) : curUrl;
325                         UpdateHeaderPath(event.pane);
326                 }
327                 break;
328         }
329         case WebDiffEvent::ZoomFactorChanged:
330                 UpdateWebPageDiffBar();
331                 break;
332         }
333 }
334
335 /**
336  * @brief returns true if WinWebDiffLib.dll is loadable
337  */
338 bool CWebPageDiffFrame::IsLoadable()
339 {
340         static HMODULE hModule;
341         if (hModule == nullptr)
342         {
343                 hModule = LoadLibraryW(L"WinWebDiff\\WinWebDiffLib.dll");
344                 if (hModule == nullptr)
345                         return false;
346         }
347         return true;
348 }
349
350
351 /**
352  * @brief returns true if URL matches configured pattern
353  */
354 bool CWebPageDiffFrame::MatchURLPattern(const String& url)
355 {
356         const String& includePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_INCLUDE);
357         if (includePattern.empty())
358                 return false;
359         std::string textu8 = ucr::toUTF8(url);
360         try
361         {
362                 Poco::RegularExpression reInclude(ucr::toUTF8(includePattern));
363                 if (!reInclude.match(textu8))
364                         return false;
365                 const String& excludePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_EXCLUDE);
366                 if (excludePattern.empty())
367                         return true;
368                 Poco::RegularExpression reExclude(ucr::toUTF8(excludePattern));
369                 return !reExclude.match(textu8);
370         }
371         catch (...)
372         {
373                 return false;
374         }
375 }
376
377 /**
378  * @brief Create the splitter, the filename bar, the status bar, and the two views
379  */
380 BOOL CWebPageDiffFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
381         CCreateContext* pContext)
382 {
383         if (!IsLoadable())
384                 return FALSE;
385
386         HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
387         if (hModule == nullptr)
388                 return FALSE;
389
390         IWebDiffWindow * (*pfnWinWebDiff_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
391                 (IWebDiffWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinWebDiff_CreateWindow");
392         if (pfnWinWebDiff_CreateWindow == nullptr ||
393                 (m_pWebDiffWindow = pfnWinWebDiff_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
394         {
395                 FreeLibrary(hModule);
396                 return FALSE;
397         }
398
399         if (!m_pWebDiffWindow->IsWebView2Installed())
400         {
401                 if (IDYES == AfxMessageBox(_("WebView2 runtime is not installed. Do you want to download it?").c_str(), MB_ICONWARNING | MB_YESNO))
402                 {
403                         m_pWebDiffWindow->DownloadWebView2();
404                 }
405                 return FALSE;
406         }
407
408         m_pWebDiffWindow->AddEventListener(
409                 Callback<IWebDiffEventHandler>([this](const WebDiffEvent& event) -> HRESULT
410                 {
411                         OnWebDiffEvent(event);
412                         return S_OK;
413                 })
414         );
415
416         LoadOptions();
417
418         m_pWebDiffWindow->SetUserDataFolderType(
419                 static_cast<IWebDiffWindow::UserdataFolderType>(GetOptionsMgr()->GetInt(OPT_CMP_WEB_USERDATAFOLDER_TYPE)),
420                 GetOptionsMgr()->GetBool(OPT_CMP_WEB_USERDATAFOLDER_PERPANE));
421
422         auto callback = Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
423                 {
424                         int nNormalBuffer = 0;
425                         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
426                         {
427                                 if (m_nBufferType[pane] != BUFFERTYPE::UNNAMED)
428                                         ++nNormalBuffer;
429                         }
430                         if (nNormalBuffer == 0)
431                                 UpdateDiffItem(m_pDirDoc);
432
433                         if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
434                                 m_pWebDiffWindow->FirstDiff();
435
436                         if (m_callbackOnOpenCompleted)
437                         {
438                                 m_callbackOnOpenCompleted();
439                                 m_callbackOnOpenCompleted = nullptr;
440                         }
441                         m_bCompareCompleted = true;
442                         return S_OK;
443                 });
444         bool bResult;
445         if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
446         {
447                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
448                         m_filePaths[pane] = _T("about:blank");
449         }
450         bResult = OpenUrls(callback);
451
452         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
453         {
454                 m_fileInfo[pane].Update(m_filePaths[pane]);
455         }
456
457         // Merge frame has also a dockable bar at the very left
458         // This is not the client area, but we create it now because we want
459         // to use the CCreateContext
460 //      String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
461 //      if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
462 //      {
463 //              TRACE0("Failed to create LocationBar\n");
464 //              return FALSE;
465 //      }
466 //
467 //      IWebToolWindow * (*pfnWinWebDiff_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *) =
468 //              (IWebToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *pWebPageDiffWindow))GetProcAddress(hModule, "WinWebDiff_CreateToolWindow");
469 //      if (pfnWinWebDiff_CreateToolWindow == nullptr ||
470 //              (m_pWebToolWindow = pfnWinWebDiff_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pWebDiffWindow)) == nullptr)
471 //      {
472 //              return FALSE;
473 //      }
474 //
475 //      m_pWebToolWindow->Translate(TranslateLocationPane);
476 //
477 //      m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
478
479         return TRUE;
480 }
481
482 //void CWebPageDiffFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
483 //{
484 //      swprintf_s(dst, dstbufsize, L"%s", tr("WebPageDiffFrame|LocationPane", ucr::toUTF8(org)).c_str());
485 //}
486
487 /////////////////////////////////////////////////////////////////////////////
488 // CWebPageDiffFrame message handlers
489
490 int CWebPageDiffFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
491 {
492         if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
493                 return -1;
494
495         EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
496
497         CMergeFrameCommon::RemoveBarBorder();
498
499         // Merge frame has a header bar at top
500         if (!m_wndWebPageDiffBar.Create(this) || !m_wndFilePathBar.Create(this))
501         {
502                 TRACE0("Failed to create dialog bar\n");
503                 return -1;      // fail to create
504         }
505
506         UpdateWebPageDiffBar();
507
508         m_wndFilePathBar.SetPaneCount(m_pWebDiffWindow->GetPaneCount());
509         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
510                 if (m_nActivePane != pane)
511                         m_pWebDiffWindow->SetActivePane(pane);
512         });
513         m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
514                 m_strDesc[pane] = sText;
515                 UpdateHeaderPath(pane);
516         });
517
518         // Merge frame also has a dockable bar at the very left
519         // created in OnCreateClient 
520         //m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
521         //      CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
522         //m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
523         //DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
524
525         for (int nPane = 0; nPane < m_pWebDiffWindow->GetPaneCount(); nPane++)
526         {
527                 m_wndFilePathBar.SetActive(nPane, FALSE);
528 //              CreateWebWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pWebDiffWindow->GetPaneHWND(nPane)));
529                 UpdateHeaderPath(nPane);
530         }
531
532 //      CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
533 //      m_rectBorder.bottom = size.cy;
534
535         CDockState pDockState;
536         pDockState.LoadState(_T("Settings-WebPageDiffFrame"));
537         if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
538                 SetDockState(pDockState);
539         // for the dimensions of the diff and location pane, use the CSizingControlBar loader
540         //m_wndLocationBar.LoadState(_T("Settings-WebPageDiffFrame"));
541
542         return 0;
543 }
544
545 /**
546 * @brief We must use this function before a call to SetDockState
547 *
548 * @note Without this, SetDockState will assert or crash if a bar from the
549 * CDockState is missing in the current CMergeEditFrame.
550 * The bars are identified with their ID. This means the missing bar bug is triggered
551 * when we run WinMerge after changing the ID of a bar.
552 */
553 bool CWebPageDiffFrame::EnsureValidDockState(CDockState& state)
554 {
555         for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
556         {
557                 bool barIsCorrect = true;
558                 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
559                 if (pInfo == nullptr)
560                         barIsCorrect = false;
561                 else
562                 {
563                         if (!pInfo->m_bFloating)
564                         {
565                                 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
566                                 if (pInfo->m_pBar == nullptr)
567                                         barIsCorrect = false; //toolbar id's probably changed   
568                         }
569                 }
570
571                 if (!barIsCorrect)
572                         state.m_arrBarInfo.RemoveAt(i);
573         }
574         return true;
575 }
576
577 /**
578  * @brief Save the window's position, free related resources, and destroy the window
579  */
580 BOOL CWebPageDiffFrame::DestroyWindow() 
581 {
582         SavePosition();
583         SaveActivePane();
584         SaveOptions();
585         SaveWindowState();
586         CFrameWnd* pParentFrame = GetParentFrame();
587         BOOL result = CMergeFrameCommon::DestroyWindow();
588         if (pParentFrame)
589                 pParentFrame->OnUpdateFrameTitle(FALSE);
590         return result;
591 }
592
593 void CWebPageDiffFrame::LoadOptions()
594 {
595         m_pWebDiffWindow->SetHorizontalSplit(GetOptionsMgr()->GetBool(OPT_SPLIT_HORIZONTALLY));
596         m_pWebDiffWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_WEB_ZOOM) / 1000.0);
597         SIZE size{ GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_WIDTH), GetOptionsMgr()->GetInt(OPT_CMP_WEB_VIEW_HEIGHT) };
598         m_pWebDiffWindow->SetSize(size);
599         m_pWebDiffWindow->SetFitToWindow(GetOptionsMgr()->GetBool(OPT_CMP_WEB_FIT_TO_WINDOW));
600         m_pWebDiffWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_WEB_SHOWDIFFERENCES));
601         m_pWebDiffWindow->SetShowWordDifferences(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
602         m_pWebDiffWindow->SetUserAgent(GetOptionsMgr()->GetString(OPT_CMP_WEB_USER_AGENT).c_str());
603         COLORSETTINGS colors;
604         IWebDiffWindow::ColorSettings colorSettings;
605         Options::DiffColors::Load(GetOptionsMgr(), colors);
606         colorSettings.clrDiff = colors.clrDiff;
607         colorSettings.clrDiffText = colors.clrDiffText;
608         colorSettings.clrSelDiff = colors.clrSelDiff;
609         colorSettings.clrSelDiffText = colors.clrSelDiffText;
610         colorSettings.clrSNP = colors.clrSNP;
611         colorSettings.clrSNPText = colors.clrSNPText;
612         colorSettings.clrSelSNP = colors.clrSelSNP;
613         colorSettings.clrSelSNPText = colors.clrSelSNPText;
614         colorSettings.clrWordDiff = colors.clrWordDiff;
615         colorSettings.clrWordDiffDeleted = colors.clrWordDiffDeleted;
616         colorSettings.clrWordDiffText = colors.clrWordDiffText;
617         colorSettings.clrSelWordDiff = colors.clrSelWordDiff;
618         colorSettings.clrSelWordDiffDeleted = colors.clrSelWordDiffDeleted;
619         colorSettings.clrSelWordDiffText = colors.clrSelWordDiffText;
620         m_pWebDiffWindow->SetDiffColorSettings(colorSettings);
621         DIFFOPTIONS options;
622         IWebDiffWindow::DiffOptions diffOptions;
623         Options::DiffOptions::Load(GetOptionsMgr(), options);
624         diffOptions.bFilterCommentsLines = options.bFilterCommentsLines;
625         diffOptions.completelyBlankOutIgnoredChanges = options.bCompletelyBlankOutIgnoredChanges;
626         diffOptions.diffAlgorithm = options.nDiffAlgorithm;
627         diffOptions.ignoreBlankLines = options.bIgnoreBlankLines;
628         diffOptions.ignoreCase = options.bIgnoreCase;
629         diffOptions.ignoreEol = options.bIgnoreEol;
630         diffOptions.ignoreNumbers = options.bIgnoreNumbers;
631         diffOptions.ignoreWhitespace = options.nIgnoreWhitespace;
632         m_pWebDiffWindow->SetDiffOptions(diffOptions);
633 }
634
635 void CWebPageDiffFrame::SaveOptions()
636 {
637         //      GetOptionsMgr()->SaveOption(OPT_CMP_WEB_DIFFCOLORALPHA, static_cast<int>(m_pWebDiffWindow->GetDiffColorAlpha() * 100.0));
638         SIZE size = m_pWebDiffWindow->GetSize();
639         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_WIDTH, size.cx);
640         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_VIEW_HEIGHT, size.cy);
641         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_FIT_TO_WINDOW, m_pWebDiffWindow->GetFitToWindow());
642         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
643         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_ZOOM, static_cast<int>(m_pWebDiffWindow->GetZoom() * 1000));
644         GetOptionsMgr()->SaveOption(OPT_CMP_WEB_USER_AGENT, m_pWebDiffWindow->GetUserAgent());
645 }
646
647 /**
648  * @brief Save coordinates of the frame, splitters, and bars
649  *
650  * @note Do not save the maximized/restored state here. We are interested
651  * in the state of the active frame, and maybe this frame is not active
652  */
653 void CWebPageDiffFrame::SavePosition()
654 {
655         CRect rc;
656         GetWindowRect(&rc);
657
658         // save the bars layout
659         // save docking positions and sizes
660         CDockState m_pDockState;
661         GetDockState(m_pDockState);
662         m_pDockState.SaveState(_T("Settings-WebPageDiffFrame"));
663         // for the dimensions of the diff pane, use the CSizingControlBar save
664         //m_wndLocationBar.SaveState(_T("Settings-WebPageDiffFrame"));
665 }
666
667 void CWebPageDiffFrame::SaveActivePane()
668 {
669         GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pWebDiffWindow->GetActivePane());
670 }
671
672 void CWebPageDiffFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
673 {
674         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
675
676         if (bActivate)
677         {
678                 GetMainFrame()->PostMessage(WM_USER + 1);
679         }
680 }
681
682 void CWebPageDiffFrame::OnClose() 
683 {
684         // clean up pointers.
685         CMergeFrameCommon::OnClose();
686 }
687
688 void CWebPageDiffFrame::OnDestroy()
689 {
690         if (!m_pWebDiffWindow)
691                 return;
692 }
693
694 /**
695  * @brief Reloads the opened files
696  */
697 void CWebPageDiffFrame::OnFileReload()
698 {
699         int nActivePane = m_pWebDiffWindow->GetActivePane();
700         OpenUrls(
701                 Callback<IWebDiffCallback>([nActivePane, this](const WebDiffCallbackResult& result) -> HRESULT
702                         {
703                                 MoveOnLoad(nActivePane);
704                                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
705                                         m_fileInfo[pane].Update(m_filePaths[pane]);
706                                 return S_OK;
707                         }));
708 }
709
710 void CWebPageDiffFrame::OnFileClose() 
711 {
712         OnClose();
713 }
714
715 void CWebPageDiffFrame::OnFileRecompareAs(UINT nID)
716 {
717         PathContext paths = m_filePaths;
718         DWORD dwFlags[3];
719         String strDesc[3];
720         int nBuffers = m_filePaths.GetSize();
721         PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
722
723         for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
724         {
725                 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
726                 strDesc[nBuffer] = m_strDesc[nBuffer];
727         }
728         if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
729         {
730                 infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
731                 nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE : -ID_MERGE_COMPARE_WEBPAGE;
732         }
733
734         CloseNow();
735         GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
736                 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
737 }
738
739 void CWebPageDiffFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
740 {
741         pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_WEBPAGE);
742 }
743
744 void CWebPageDiffFrame::OnOpenWithUnpacker()
745 {
746         CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
747                 strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
748                 CSelectPluginDlg::PluginType::Unpacker, false);
749         if (dlg.DoModal() == IDOK)
750         {
751                 PackingInfo infoUnpacker(dlg.GetPluginPipeline());
752                 PathContext paths = m_filePaths;
753                 DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
754                 String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
755                 CloseNow();
756                 GetMainFrame()->DoFileOpen(
757                         GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE: -1,
758                         &paths, dwFlags, strDesc, _T(""), &infoUnpacker);
759         }
760 }
761
762 void  CWebPageDiffFrame::OnWindowChangePane(UINT nID) 
763 {
764         int npanes = m_pWebDiffWindow->GetPaneCount();
765         int pane = m_pWebDiffWindow->GetActivePane();
766         pane = (nID == ID_NEXT_PANE) ? ((pane + 1) % npanes) : ((pane + npanes - 1) % npanes);
767         m_pWebDiffWindow->SetActivePane(pane);
768 }
769
770 /**
771  * @brief Write path and filename to headerbar
772  * @note SetText() does not repaint unchanged text
773  */
774 void CWebPageDiffFrame::UpdateHeaderPath(int pane)
775 {
776         String sText;
777
778         if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
779                 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
780         {
781                 sText = m_strDesc[pane];
782         }
783         else
784         {
785                 sText = m_filePaths.GetPath(pane);
786                 if (m_pDirDoc != nullptr)
787                         m_pDirDoc->ApplyDisplayRoot(pane, sText);
788         }
789
790         m_wndFilePathBar.SetText(pane, sText.c_str());
791
792         SetTitle(nullptr);
793 }
794
795 /// update splitting position for panels 1/2 and headerbar and statusbar 
796 void CWebPageDiffFrame::UpdateHeaderSizes()
797 {
798         if (m_pWebDiffWindow != nullptr)
799         {
800                 int w[3];
801                 CRect rc, rcMergeWindow;
802                 int nPaneCount = m_pWebDiffWindow->GetPaneCount();
803                 GetClientRect(&rc);
804                 ::GetWindowRect(m_pWebDiffWindow->GetHWND(), &rcMergeWindow);
805                 ScreenToClient(rcMergeWindow);
806                 if (!m_pWebDiffWindow->GetHorizontalSplit())
807                 {
808                         for (int pane = 0; pane < nPaneCount; pane++)
809                         {
810                                 RECT rc1 = m_pWebDiffWindow->GetPaneWindowRect(pane);
811                                 w[pane] = rc1.right - rc1.left - 4;
812                                 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
813                         }
814                 }
815                 else
816                 {
817                         for (int pane = 0; pane < nPaneCount; pane++)
818                                 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
819                 }
820
821                 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
822                 {
823                         std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
824
825                         // resize controls in header dialog bar
826                         m_wndFilePathBar.Resize(w);
827
828                         rc.left = rcMergeWindow.left;
829                         rc.top = rc.bottom - m_rectBorder.bottom;
830                         rc.right = rc.left;
831                         for (int pane = 0; pane < nPaneCount; pane++)
832                         {
833                                 rc.right += w[pane] + 4 + 2;
834 //                              m_wndStatusBar[pane].MoveWindow(&rc);
835                                 rc.left = rc.right;
836                         }
837                 }
838         }
839 }
840
841 /**
842  * @brief Update document filenames to title
843  */
844 void CWebPageDiffFrame::SetTitle(LPCTSTR lpszTitle)
845 {
846         String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr);
847         CMergeFrameCommon::SetTitle(sTitle.c_str());
848         if (m_hWnd != nullptr)
849         {
850                 SetWindowText(sTitle.c_str());
851                 GetMainFrame()->OnUpdateFrameTitle(TRUE);
852         }
853 }
854
855 void CWebPageDiffFrame::UpdateLastCompareResult()
856 {
857         if (m_bCompareCompleted)
858                 SetLastCompareResult(m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0);
859 }
860
861 void CWebPageDiffFrame::UpdateAutoPaneResize()
862 {
863 }
864
865 void CWebPageDiffFrame::UpdateSplitter()
866 {
867 }
868
869 bool CWebPageDiffFrame::OpenUrls(IWebDiffCallback* callback)
870 {
871         bool bResult;
872         String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
873         String strTempFileName[3];
874         m_infoUnpacker.EnableWebBrowserMode();
875         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
876         {
877                 strTempFileName[pane] = m_filePaths[pane];
878                 if (!m_infoUnpacker.Unpacking(&m_unpackerSubcodes[pane], strTempFileName[pane], filteredFilenames, {strTempFileName[pane]}))
879                 {
880 //                      return false;
881                 }
882         }
883         if (m_filePaths.GetSize() == 2)
884                 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), callback));
885         else
886                 bResult = SUCCEEDED(m_pWebDiffWindow->Open(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str(), callback));
887         return bResult;
888 }
889
890 /**
891  * @brief Update associated diff item
892  */
893 int CWebPageDiffFrame::UpdateDiffItem(CDirDoc *pDirDoc)
894 {
895         // If directory compare has results
896         if (pDirDoc && pDirDoc->HasDiffs())
897         {
898 // FIXME:
899 //              const String &pathLeft = m_filePaths.GetLeft();
900 //              const String &pathRight = m_filePaths.GetRight();
901 //              CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
902 //              if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
903 //              {
904 //                      DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
905 //                      ::UpdateDiffItem(m_nBuffers, di, &ctxt);
906 //              }
907         }
908         int result = m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0;
909         SetLastCompareResult(result != 0);
910         return result;
911 }
912
913 /// Document commanding us to close
914 bool CWebPageDiffFrame::CloseNow()
915 {
916         DestroyWindow();
917         return true;
918 }
919
920 /**
921  * @brief A string to display as a tooltip for MDITabbar
922  */
923 CString CWebPageDiffFrame::GetTooltipString() const
924 {
925         return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
926 }
927
928 /**
929  * @brief Update any resources necessary after a GUI language change
930  */
931 void CWebPageDiffFrame::UpdateResources()
932 {
933         //m_pWebToolWindow->Translate(TranslateLocationPane);
934 }
935
936 void CWebPageDiffFrame::RefreshOptions()
937 {
938         LoadOptions();
939 }
940
941 /**
942  * @brief Handle some keys when in merging mode
943  */
944 bool CWebPageDiffFrame::MergeModeKeyDown(MSG* pMsg)
945 {
946         bool bHandled = false;
947
948         // Allow default text selection when SHIFT pressed
949         if (::GetAsyncKeyState(VK_SHIFT))
950                 return false;
951
952         // Allow default editor functions when CTRL pressed
953         if (::GetAsyncKeyState(VK_CONTROL))
954                 return false;
955
956         // If we are in merging mode (merge with cursor keys)
957         // handle some keys here
958         switch (pMsg->wParam)
959         {
960         case VK_UP:
961                 OnPrevdiff();
962                 bHandled = true;
963                 break;
964         case VK_DOWN:
965                 OnNextdiff();
966                 bHandled = true;
967                 break;
968         }
969
970         return bHandled;
971 }
972 /**
973  * @brief Check for keyboard commands
974  */
975 BOOL CWebPageDiffFrame::PreTranslateMessage(MSG* pMsg)
976 {
977         if (pMsg->message == WM_KEYDOWN)
978         {
979                 // If we are in merging mode (merge with cursor keys)
980                 // handle some keys here
981                 if (theApp.GetMergingMode())
982                 {
983                         bool bHandled = MergeModeKeyDown(pMsg);
984                         if (bHandled)
985                                 return true;
986                 }
987
988                 // Close window in response to VK_ESCAPE if user has allowed it from options
989                 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) != 0)
990                 {
991                         PostMessage(WM_CLOSE, 0, 0);
992                         return true;
993                 }
994         }
995         return CMergeFrameCommon::PreTranslateMessage(pMsg);
996 }
997
998 void CWebPageDiffFrame::OnSize(UINT nType, int cx, int cy) 
999 {
1000         CMergeFrameCommon::OnSize(nType, cx, cy);
1001         UpdateHeaderSizes();
1002 }
1003
1004 /**
1005  * @brief Synchronize control and status bar placements with splitter position,
1006  * update mod indicators, synchronize scrollbars
1007  */
1008 void CWebPageDiffFrame::OnIdleUpdateCmdUI()
1009 {
1010         if (IsWindowVisible())
1011         {
1012                 int nActivePane = m_pWebDiffWindow->GetActivePane();
1013                 if (nActivePane != -1)
1014                         m_nActivePane = nActivePane;
1015
1016                 UpdateHeaderSizes();
1017                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1018                 {
1019                         // Update mod indicators
1020                         m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1021                 }
1022                 UpdateLastCompareResult();
1023         }
1024         CMergeFrameCommon::OnIdleUpdateCmdUI();
1025 }
1026
1027 /**
1028  * @brief Save pane sizes and positions when one of panes requests it.
1029  */
1030 LRESULT CWebPageDiffFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1031 {
1032         SavePosition();
1033         return 0;
1034 }
1035
1036 void CWebPageDiffFrame::UpdateWebPageDiffBar()
1037 {
1038         m_bInUpdateWebPageDiffBar = true;
1039         bool fitToWindow = m_pWebDiffWindow->GetFitToWindow();
1040         m_wndWebPageDiffBar.CheckDlgButton(IDC_FITTOWINDOW, fitToWindow);
1041         m_wndWebPageDiffBar.CheckDlgButton(IDC_SHOWDIFFERENCES, m_pWebDiffWindow->GetShowDifferences());
1042         m_wndWebPageDiffBar.GetDlgItem(IDC_WIDTH)->EnableWindow(!fitToWindow);
1043         m_wndWebPageDiffBar.GetDlgItem(IDC_HEIGHT)->EnableWindow(!fitToWindow);
1044         SIZE size = m_pWebDiffWindow->GetSize();
1045         m_wndWebPageDiffBar.SetDlgItemText(IDC_WIDTH, strutils::format(_T("%d"), size.cx));
1046         m_wndWebPageDiffBar.SetDlgItemText(IDC_HEIGHT, strutils::format(_T("%d"), size.cy));
1047         m_wndWebPageDiffBar.SetDlgItemText(IDC_ZOOM, strutils::format(_T("%.1f%%"), 
1048                 m_pWebDiffWindow->GetZoom() * 100.0));
1049         m_wndWebPageDiffBar.SetDlgItemText(IDC_USERAGENT, m_pWebDiffWindow->GetUserAgent());
1050         m_bInUpdateWebPageDiffBar = false;
1051 }
1052
1053 void CWebPageDiffFrame::OnBnClickedFitToWindow()
1054 {
1055         m_pWebDiffWindow->SetFitToWindow(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_FITTOWINDOW));
1056         UpdateWebPageDiffBar();
1057 }
1058
1059 void CWebPageDiffFrame::OnBnClickedShowDifferences()
1060 {
1061         m_pWebDiffWindow->SetShowDifferences(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_SHOWDIFFERENCES));
1062         UpdateWebPageDiffBar();
1063 }
1064
1065 void CWebPageDiffFrame::OnBnClickedCompare()
1066 {
1067         OnRefresh();
1068 }
1069
1070 void CWebPageDiffFrame::OnDropDownCompare(NMHDR *pNMHDR, LRESULT *pResult)
1071 {
1072         CRect rc;
1073         m_wndWebPageDiffBar.GetDlgItem(IDC_COMPARE)->GetWindowRect(&rc);
1074         CPoint point { rc.left, rc.bottom };
1075         BCMenu menuPopup;
1076         menuPopup.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_WEBPAGE_COMPARE));
1077         theApp.TranslateMenu(menuPopup.m_hMenu);
1078         BCMenu* pPopup = (BCMenu*)menuPopup.GetSubMenu(0);
1079         pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
1080                 point.x, point.y, AfxGetMainWnd());
1081 }
1082
1083 void CWebPageDiffFrame::OnEnChangeWidth()
1084 {
1085         if (m_bInUpdateWebPageDiffBar)
1086                 return;
1087         String text;
1088         m_wndWebPageDiffBar.GetDlgItemText(IDC_WIDTH, text);
1089         int v = _ttoi(text.c_str());
1090         if (v > 0 && v < 32678)
1091         {
1092                 SIZE size = m_pWebDiffWindow->GetSize();
1093                 size.cx = v;
1094                 m_pWebDiffWindow->SetSize(size);
1095         }
1096 }
1097
1098 void CWebPageDiffFrame::OnEnChangeHeight()
1099 {
1100         if (m_bInUpdateWebPageDiffBar)
1101                 return;
1102         String text;
1103         m_wndWebPageDiffBar.GetDlgItemText(IDC_HEIGHT, text);
1104         int v = _ttoi(text.c_str());
1105         if (v > 0 && v < 32678)
1106         {
1107                 SIZE size = m_pWebDiffWindow->GetSize();
1108                 size.cy = v;
1109                 m_pWebDiffWindow->SetSize(size);
1110         }
1111 }
1112
1113 void CWebPageDiffFrame::OnEnChangeZoom()
1114 {
1115         if (m_bInUpdateWebPageDiffBar)
1116                 return;
1117         String text;
1118         m_wndWebPageDiffBar.GetDlgItemText(IDC_ZOOM, text);
1119         TCHAR* endptr = nullptr;
1120         double v = _tcstod(text.c_str(), &endptr);
1121         if ((* endptr == '\0' || *endptr == '%') && v >= 25.0 && v <= 500.0)
1122                 m_pWebDiffWindow->SetZoom(v / 100.0);
1123 }
1124
1125 void CWebPageDiffFrame::OnEnChangeUserAgent()
1126 {
1127         if (m_bInUpdateWebPageDiffBar)
1128                 return;
1129         String text;
1130         m_wndWebPageDiffBar.GetDlgItemText(IDC_USERAGENT, text);
1131         m_pWebDiffWindow->SetUserAgent(text.c_str());
1132 }
1133
1134 void CWebPageDiffFrame::OnKillFocusBarControls()
1135 {
1136         UpdateWebPageDiffBar();
1137         SaveOptions();
1138         CString text;
1139         m_wndFilePathBar.GetDlgItemTextW(IDC_USERAGENT, text);
1140 }
1141
1142 void CWebPageDiffFrame::OnUpdateStatusNum(CCmdUI* pCmdUI) 
1143 {
1144         TCHAR sIdx[32] = { 0 };
1145         TCHAR sCnt[32] = { 0 };
1146         String s;
1147         const int nDiffs = m_pWebDiffWindow->GetDiffCount();
1148         
1149         // Files are identical - show text "Identical"
1150         if (nDiffs <= 0)
1151                 s = theApp.LoadString(IDS_IDENTICAL);
1152         
1153         // There are differences, but no selected diff
1154         // - show amount of diffs
1155         else if (m_pWebDiffWindow->GetCurrentDiffIndex() < 0)
1156         {
1157                 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1158                 _itot_s(nDiffs, sCnt, 10);
1159                 strutils::replace(s, _T("%1"), sCnt);
1160         }
1161         
1162         // There are differences and diff selected
1163         // - show diff number and amount of diffs
1164         else
1165         {
1166                 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1167                 const int signInd = m_pWebDiffWindow->GetCurrentDiffIndex();
1168                 _itot_s(signInd + 1, sIdx, 10);
1169                 strutils::replace(s, _T("%1"), sIdx);
1170                 _itot_s(nDiffs, sCnt, 10);
1171                 strutils::replace(s, _T("%2"), sCnt);
1172         }
1173         pCmdUI->SetText(s.c_str());
1174 }
1175         
1176 /**
1177  * @brief Cut current selection to clipboard
1178  */
1179 void CWebPageDiffFrame::OnEditCut()
1180 {
1181         m_pWebDiffWindow->Cut();
1182 }
1183
1184 /**
1185  * @brief Copy current selection to clipboard
1186  */
1187 void CWebPageDiffFrame::OnEditCopy()
1188 {
1189         m_pWebDiffWindow->Copy();
1190 }
1191
1192 /**
1193  * @brief Paste clipboard content over selected content
1194  */
1195 void CWebPageDiffFrame::OnEditPaste()
1196 {
1197         m_pWebDiffWindow->Paste();
1198 }
1199
1200 /**
1201  * @brief Undo last action
1202  */
1203 void CWebPageDiffFrame::OnEditUndo()
1204 {
1205         m_pWebDiffWindow->Undo();
1206 }
1207
1208 /**
1209  * @brief Redo last action
1210  */
1211 void CWebPageDiffFrame::OnEditRedo()
1212 {
1213         m_pWebDiffWindow->Redo();
1214 }
1215
1216 /**
1217  * @brief Update the tool bar's "Undo" icon. It should be enabled when
1218  * renaming an item and undo is possible.
1219  */
1220 void CWebPageDiffFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1221 {
1222         pCmdUI->Enable(m_pWebDiffWindow->CanUndo());
1223 }
1224
1225 /**
1226  * @brief Update the tool bar's "Redo" icon. It should be enabled when
1227  * renaming an item and undo is possible.
1228  */
1229 void CWebPageDiffFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1230 {
1231         pCmdUI->Enable(m_pWebDiffWindow->CanRedo());
1232 }
1233
1234 /**
1235  * @brief Select entire image
1236  */
1237 void CWebPageDiffFrame::OnEditSelectAll()
1238 {
1239         m_pWebDiffWindow->SelectAll();
1240 }
1241
1242 /**
1243  * @brief Called when user selects View/Zoom In from menu.
1244  */
1245 void CWebPageDiffFrame::OnViewZoomIn()
1246 {
1247         m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() + 0.1);
1248 }
1249
1250 /**
1251  * @brief Called when user selects View/Zoom Out from menu.
1252  */
1253 void CWebPageDiffFrame::OnViewZoomOut()
1254 {
1255         m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() - 0.1);
1256 }
1257
1258 /**
1259  * @brief Called when user selects View/Zoom Normal from menu.
1260  */
1261 void CWebPageDiffFrame::OnViewZoomNormal()
1262 {
1263         m_pWebDiffWindow->SetZoom(1.0);
1264 }
1265
1266 /**
1267  * @brief Enables/disables linediff (different color for diffs)
1268  */
1269 void CWebPageDiffFrame::OnViewLineDiffs() 
1270 {
1271         bool bWordDiffHighlight = !GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
1272         GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, bWordDiffHighlight);
1273         m_pWebDiffWindow->SetShowWordDifferences(bWordDiffHighlight);
1274 }
1275
1276 void CWebPageDiffFrame::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
1277 {
1278         pCmdUI->Enable(true);
1279         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
1280 }
1281
1282 /**
1283  * @brief Split panes vertically
1284  */
1285 void CWebPageDiffFrame::OnViewSplitVertically() 
1286 {
1287         bool bSplitVertically = !m_pWebDiffWindow->GetHorizontalSplit();
1288         bSplitVertically = !bSplitVertically; // toggle
1289         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1290         m_pWebDiffWindow->SetHorizontalSplit(!bSplitVertically);
1291 }
1292
1293 /**
1294  * @brief Update "Split Vertically" UI items
1295  */
1296 void CWebPageDiffFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
1297 {
1298         pCmdUI->Enable(TRUE);
1299         pCmdUI->SetCheck(!m_pWebDiffWindow->GetHorizontalSplit());
1300 }
1301
1302 /**
1303  * @brief Go to first diff
1304  *
1305  * Called when user selects "First Difference"
1306  * @sa CWebPageDiffFrame::SelectDiff()
1307  */
1308 void CWebPageDiffFrame::OnFirstdiff()
1309 {
1310         m_pWebDiffWindow->FirstDiff();
1311 }
1312
1313 /**
1314  * @brief Update "First diff" UI items
1315  */
1316 void CWebPageDiffFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1317 {
1318         OnUpdatePrevdiff(pCmdUI);
1319 }
1320
1321 /**
1322  * @brief Go to last diff
1323  */
1324 void CWebPageDiffFrame::OnLastdiff()
1325 {
1326         m_pWebDiffWindow->LastDiff();
1327 }
1328
1329 /**
1330  * @brief Update "Last diff" UI items
1331  */
1332 void CWebPageDiffFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1333 {
1334         OnUpdateNextdiff(pCmdUI);
1335 }
1336
1337 /**
1338  * @brief Go to next diff and select it.
1339  */
1340 void CWebPageDiffFrame::OnNextdiff()
1341 {
1342         if (m_pWebDiffWindow->GetCurrentDiffIndex() != m_pWebDiffWindow->GetDiffCount() - 1)
1343                 m_pWebDiffWindow->NextDiff();
1344         else if (m_pDirDoc != nullptr)
1345                 m_pDirDoc->MoveToNextDiff(this);
1346 }
1347
1348 /**
1349  * @brief Update "Next diff" UI items
1350  */
1351 void CWebPageDiffFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1352 {
1353         bool enabled = m_bCompareCompleted && (
1354                 m_pWebDiffWindow->GetNextDiffIndex() >= 0 ||
1355                 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1356
1357         if (!enabled && m_pDirDoc != nullptr)
1358                 enabled = m_pDirDoc->MoveableToNextDiff();
1359         pCmdUI->Enable(enabled);
1360 }
1361
1362 /**
1363  * @brief Go to previous diff and select it.
1364  */
1365 void CWebPageDiffFrame::OnPrevdiff()
1366 {
1367         if (m_pWebDiffWindow->GetCurrentDiffIndex() > 0)
1368         {
1369                 m_pWebDiffWindow->PrevDiff();
1370         }
1371         else if (m_pDirDoc != nullptr)
1372                 m_pDirDoc->MoveToPrevDiff(this);
1373 }
1374
1375 /**
1376  * @brief Update "Previous diff" UI items
1377  */
1378 void CWebPageDiffFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1379 {
1380         bool enabled = m_bCompareCompleted && (
1381                 m_pWebDiffWindow->GetPrevDiffIndex() >= 0 ||
1382                 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1383
1384         if (!enabled && m_pDirDoc != nullptr)
1385                 enabled = m_pDirDoc->MoveableToPrevDiff();
1386         pCmdUI->Enable(enabled);
1387 }
1388
1389 /**
1390  * @brief Go to next conflict and select it.
1391  */
1392 void CWebPageDiffFrame::OnNextConflict()
1393 {
1394         m_pWebDiffWindow->NextConflict();
1395 }
1396
1397 /**
1398  * @brief Update "Next Conflict" UI items
1399  */
1400 void CWebPageDiffFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1401 {
1402         pCmdUI->Enable(m_bCompareCompleted && 
1403                 m_pWebDiffWindow->GetPaneCount() > 2 && (
1404                         m_pWebDiffWindow->GetNextConflictIndex() >= 0 ||
1405                         (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1406                 )
1407         );
1408 }
1409
1410 /**
1411  * @brief Go to previous diff and select it.
1412  */
1413 void CWebPageDiffFrame::OnPrevConflict()
1414 {
1415         m_pWebDiffWindow->PrevConflict();
1416 }
1417
1418 /**
1419  * @brief Update "Previous diff" UI items
1420  */
1421 void CWebPageDiffFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1422 {
1423         pCmdUI->Enable(m_bCompareCompleted &&
1424                 m_pWebDiffWindow->GetPaneCount() > 2 && (
1425                         m_pWebDiffWindow->GetPrevConflictIndex() >= 0 ||
1426                         (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1427                 )
1428         );
1429 }
1430
1431 void CWebPageDiffFrame::OnWebFitToWindow()
1432 {
1433         m_pWebDiffWindow->SetFitToWindow(true);
1434         SaveOptions();
1435         UpdateWebPageDiffBar();
1436 }
1437
1438 void CWebPageDiffFrame::OnUpdateWebFitToWindow(CCmdUI* pCmdUI)
1439 {
1440         pCmdUI->SetCheck(m_pWebDiffWindow->GetFitToWindow());
1441 }
1442
1443 void CWebPageDiffFrame::OnWebSize(UINT nID)
1444 {
1445         switch (nID)
1446         {
1447         case ID_WEB_SIZE_320x512:
1448                 m_pWebDiffWindow->SetFitToWindow(false);
1449                 m_pWebDiffWindow->SetSize({ 320, 512 });
1450                 UpdateWebPageDiffBar();
1451                 break;
1452         case ID_WEB_SIZE_375x600:
1453                 m_pWebDiffWindow->SetFitToWindow(false);
1454                 m_pWebDiffWindow->SetSize({ 375, 600 });
1455                 UpdateWebPageDiffBar();
1456                 break;
1457         case ID_WEB_SIZE_1024x640:
1458                 m_pWebDiffWindow->SetFitToWindow(false);
1459                 m_pWebDiffWindow->SetSize({ 1024, 640 });
1460                 SaveOptions();
1461                 UpdateWebPageDiffBar();
1462                 break;
1463         case ID_WEB_SIZE_1280x800:
1464                 m_pWebDiffWindow->SetFitToWindow(false);
1465                 m_pWebDiffWindow->SetSize({ 1280, 800 });
1466                 SaveOptions();
1467                 UpdateWebPageDiffBar();
1468                 break;
1469         case ID_WEB_SIZE_1440x900:
1470                 m_pWebDiffWindow->SetFitToWindow(false);
1471                 m_pWebDiffWindow->SetSize({ 1440, 900 });
1472                 SaveOptions();
1473                 UpdateWebPageDiffBar();
1474                 break;
1475         }
1476 }
1477
1478 void CWebPageDiffFrame::OnWebCompareScreenshots(UINT nID)
1479 {
1480         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1481         PathContext paths;
1482         const wchar_t *spaths[3];
1483         std::vector<String> descs;
1484         const int nPanes = m_pWebDiffWindow->GetPaneCount();
1485         for (int pane = 0; pane < nPanes; ++pane)
1486         {
1487                 std::shared_ptr<TempFile> pTempFile(new TempFile());
1488                 pTempFile->Create(_T("SCR"), _T(".png"));
1489                 paths.SetPath(pane, pTempFile->GetPath());
1490                 spaths[pane] = paths[pane].c_str();
1491                 descs.push_back(m_filePaths[pane]);
1492                 m_tempFiles.push_back(pTempFile);
1493         }
1494         m_pWebDiffWindow->SaveFiles(
1495                 (nID == ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS) ? IWebDiffWindow::FULLSIZE_SCREENSHOT : IWebDiffWindow::SCREENSHOT,
1496                 spaths,
1497                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1498                         {
1499                                 DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1500                                 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data());
1501                                 return S_OK;
1502                         }));
1503 }
1504
1505 void CWebPageDiffFrame::OnWebCompareHTMLs()
1506 {
1507         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1508         PathContext paths;
1509         const wchar_t *spaths[3];
1510         std::vector<String> descs;
1511         const int nPanes = m_pWebDiffWindow->GetPaneCount();
1512         for (int pane = 0; pane < nPanes; ++pane)
1513         {
1514                 std::shared_ptr<TempFile> pTempFile(new TempFile());
1515                 pTempFile->Create(_T("HTM"), _T(".html"));
1516                 paths.SetPath(pane, pTempFile->GetPath());
1517                 spaths[pane] = paths[pane].c_str();
1518                 descs.push_back(m_filePaths[pane]);
1519                 m_tempFiles.push_back(pTempFile);
1520         }
1521         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::HTML, spaths,
1522                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1523                         {
1524                                 PackingInfo infoUnpacker(String(_T("PrettifyHTML")));
1525                                 DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1526                                 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""), &infoUnpacker);
1527                                 return S_OK;
1528                         }));
1529 }
1530
1531 void CWebPageDiffFrame::OnWebCompareTexts()
1532 {
1533         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1534         PathContext paths;
1535         const wchar_t *spaths[3];
1536         std::vector<String> descs;
1537         const int nPanes = m_pWebDiffWindow->GetPaneCount();
1538         for (int pane = 0; pane < nPanes; ++pane)
1539         {
1540                 std::shared_ptr<TempFile> pTempFile(new TempFile());
1541                 pTempFile->Create(_T("TXT"), _T(".txt"));
1542                 paths.SetPath(pane, pTempFile->GetPath());
1543                 spaths[pane] = paths[pane].c_str();
1544                 descs.push_back(m_filePaths[pane]);
1545                 m_tempFiles.push_back(pTempFile);
1546         }
1547         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::TEXT, spaths,
1548                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1549                         {
1550                                 DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1551                                 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""));
1552                                 return S_OK;
1553                         }));
1554 }
1555
1556 void CWebPageDiffFrame::OnWebCompareResourceTrees()
1557 {
1558         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1559         PathContext paths;
1560         const wchar_t *spaths[3];
1561         std::vector<String> descs;
1562         const int nPanes = m_pWebDiffWindow->GetPaneCount();
1563         for (int pane = 0; pane < nPanes; ++pane)
1564         {
1565                 std::shared_ptr<TempFolder> pTempFolder(new TempFolder());
1566                 pTempFolder->Create();
1567                 paths.SetPath(pane, pTempFolder->GetPath());
1568                 spaths[pane] = paths[pane].c_str();
1569                 descs.push_back(m_filePaths[pane]);
1570                 m_tempFolders.push_back(pTempFolder);
1571         }
1572         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::RESOURCETREE, spaths,
1573                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1574                         {
1575                                 DWORD dwFlags[3]{};
1576                                 for (int pane = 0; pane < paths.GetSize(); ++pane)
1577                                         dwFlags[pane] = FFILEOPEN_NOMRU;
1578                                 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, descs.data(), _T(""), true);
1579                                 return S_OK;
1580                         }));
1581 }
1582
1583 void CWebPageDiffFrame::OnWebClear(UINT nID)
1584 {
1585         IWebDiffWindow::BrowsingDataType dataKinds;
1586         switch (nID)
1587         {
1588         case ID_WEB_CLEAR_DISK_CACHE:       dataKinds = IWebDiffWindow::BrowsingDataType::DISK_CACHE; break;
1589         case ID_WEB_CLEAR_COOKIES:          dataKinds = IWebDiffWindow::BrowsingDataType::COOKIES; break;
1590         case ID_WEB_CLEAR_BROWSING_HISTORY: dataKinds = IWebDiffWindow::BrowsingDataType::BROWSING_HISTORY; break;
1591         case ID_WEB_CLEAR_ALL_PROFILE:      dataKinds = IWebDiffWindow::BrowsingDataType::ALL_PROFILE; break;
1592         default:
1593                 return;
1594         }
1595         m_pWebDiffWindow->ClearBrowsingData(-1, dataKinds);
1596 }
1597
1598 bool CWebPageDiffFrame::GenerateReport(const String& sFileName) const
1599 {
1600         //return GenerateReport(sFileName, true);
1601         return false;
1602 }
1603 //
1604 ///**
1605 // * @brief Generate report from file compare results.
1606 // */
1607 //bool CWebPageDiffFrame::GenerateReport(const String& sFileName, bool allPages) const
1608 //{
1609 //      return false;
1610 //}
1611 //
1612 ///**
1613 // * @brief Generate report from file compare results.
1614 // */
1615 //void CWebPageDiffFrame::OnToolsGenerateReport()
1616 //{
1617 //}
1618
1619 void CWebPageDiffFrame::OnRefresh()
1620 {
1621         if (!m_bCompareCompleted)
1622                 return;
1623         m_bCompareCompleted = false;
1624         m_pWebDiffWindow->Recompare(
1625                 Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
1626                         {
1627                                 m_bCompareCompleted = true;
1628                                 if (UpdateDiffItem(m_pDirDoc) == 0 &&
1629                                     std::count(m_filePaths.begin(), m_filePaths.end(), L"about:blank") != m_filePaths.GetSize())
1630                                 {
1631                                         CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
1632                                                 [](LPCTSTR msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
1633                                 }
1634                                 return S_OK;
1635                         })
1636                 );
1637 }
1638
1639 void CWebPageDiffFrame::OnSetFocus(CWnd* pNewWnd)
1640 {
1641         if (m_nActivePane != -1)
1642                 m_pWebDiffWindow->SetActivePane(m_nActivePane);
1643 }
1644
1645 /**
1646  * @brief Open help from mainframe when user presses F1
1647  */
1648 void CWebPageDiffFrame::OnHelp()
1649 {
1650         theApp.ShowHelp(WebPageDiffFrameHelpLocation);
1651 }