OSDN Git Service

Fix the issue where the Apache Tika plugin becomes enabled again when reopening the...
[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 "UniFile.h"
28 #include <Poco/RegularExpression.h>
29
30 #ifdef _DEBUG
31 #define new DEBUG_NEW
32 #endif
33
34 #ifndef BCN_DROPDOWN
35 #define BCN_DROPDOWN (BCN_FIRST + 0x0002)
36 #endif
37
38 template <typename T, typename Func>
39 struct CallbackImpl : public T
40 {
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); }
47         Func m_callback;
48         int m_nRef = 0;
49 };
50
51 template<typename T, typename Func>
52 CComPtr<T> Callback(Func&& callback)
53 {
54         return CComPtr<T>(new CallbackImpl<T, Func>(std::move(callback)));
55 }
56  
57 /////////////////////////////////////////////////////////////////////////////
58 // CWebPageDiffFrame
59
60 IMPLEMENT_DYNCREATE(CWebPageDiffFrame, CMergeFrameCommon)
61
62 BEGIN_MESSAGE_MAP(CWebPageDiffFrame, CMergeFrameCommon)
63         //{{AFX_MSG_MAP(CWebPageDiffFrame)
64         ON_WM_CREATE()
65         ON_WM_CLOSE()
66         ON_WM_DESTROY()
67         ON_WM_MDIACTIVATE()
68         ON_WM_SIZE()
69         ON_WM_SETFOCUS ()       
70         ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
71         ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
72         // [File] menu
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)
77         // [Edit] menu
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)
86         // [View] menu
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)
97         // [Merge] menu
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)
110         // [Web] menu
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)
119         // [Tools] menu
120         ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
121         // [Plugins] menu
122         ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
123         ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
124         // [Window] menu
125         ON_COMMAND_RANGE(ID_NEXT_PANE, ID_PREV_PANE, OnWindowChangePane)
126         // [Help] menu
127         ON_COMMAND(ID_HELP, OnHelp)
128         // Dialog bar
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)
141         // Status bar
142         ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
143         //}}AFX_MSG_MAP
144 END_MESSAGE_MAP()
145
146 CMenu CWebPageDiffFrame::menu;
147
148 /////////////////////////////////////////////////////////////////////////////
149 // CWebPageDiffFrame construction/destruction
150
151 CWebPageDiffFrame::CWebPageDiffFrame()
152 : CMergeFrameCommon(IDI_EQUALWEBPAGE, IDI_NOTEQUALWEBPAGE)
153 , m_pDirDoc(nullptr)
154 , m_bAutoMerged(false)
155 , m_pWebDiffWindow(nullptr)
156 //, m_pWebToolWindow(nullptr)
157 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
158 , m_bRO{}
159 , m_nActivePane(-1)
160 , m_bInUpdateWebPageDiffBar(false)
161 , m_bCompareCompleted(false)
162 {
163 }
164
165 CWebPageDiffFrame::~CWebPageDiffFrame()
166 {
167         GetMainFrame()->UnwatchDocuments(this);
168
169         if (m_pDirDoc != nullptr)
170         {
171                 m_pDirDoc->MergeDocClosing(this);
172                 m_pDirDoc = nullptr;
173         }
174
175         HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
176         if (hModule != nullptr)
177         {
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*/)
183                 {
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;
190                 }
191         }
192 }
193
194 bool CWebPageDiffFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent, std::function<void ()> callback)
195 {
196         m_callbackOnOpenCompleted = callback;
197         m_bCompareCompleted = false;
198         
199         CWaitCursor waitstatus;
200         int nNormalBuffer = 0;
201         for (int pane = 0; pane < nFiles; ++pane)
202         {
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;
208                 else
209                 {
210                         m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
211                         ++nNormalBuffer;
212                 }
213         }
214         SetTitle(nullptr);
215
216         LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
217                         ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
218
219         if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
220                 return false;
221
222         int nCmdShow = SW_SHOW;
223         if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
224                 nCmdShow = SW_SHOWMAXIMIZED;
225         ShowWindow(nCmdShow);
226         BringToTop(nCmdShow);
227
228         GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
229
230         GetMainFrame()->WatchDocuments(this);
231
232         return true;
233 }
234
235 void CWebPageDiffFrame::MoveOnLoad(int nPane, int)
236 {
237         if (nPane < 0)
238         {
239                 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
240                 if (nPane < 0 || nPane >= m_pWebDiffWindow->GetPaneCount())
241                         nPane = 0;
242         }
243
244         m_pWebDiffWindow->SetActivePane(nPane);
245
246         if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST) && m_pWebDiffWindow->GetDiffCount() > 0)
247                 m_pWebDiffWindow->SelectDiff(0);
248 }
249
250 /**
251  * @brief DirDoc gives us its identity just after it creates us
252  */
253 void CWebPageDiffFrame::SetDirDoc(CDirDoc * pDirDoc)
254 {
255         ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
256         m_pDirDoc = pDirDoc;
257 }
258
259 IMergeDoc::FileChange CWebPageDiffFrame::IsFileChangedOnDisk(int pane) const
260 {
261         DiffFileInfo dfi;
262         if (!dfi.Update(m_filePaths[pane]))
263                 return FileChange::Removed;
264         int tolerance = 0;
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;
272 }
273
274 void CWebPageDiffFrame::CheckFileChanged(void)
275 {
276         if (!m_pWebDiffWindow)
277                 return;
278         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
279         {
280                 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
281                 {
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)
284                         {
285                                 OnFileReload();
286                         }
287                         break;
288                 }
289         }
290 }
291
292 /**
293  * @brief Create a status bar to be associated with a heksedit control
294  */
295 void CWebPageDiffFrame::CreateWebWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
296 {
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);
302 }
303
304 static bool isTempFile(const String& path)
305 {
306         String tmpDir = env::GetTemporaryPath();
307         strutils::replace(tmpDir, _T("\\"), _T("/"));
308         tmpDir = _T("file:///") + tmpDir;
309         return (path.find(tmpDir) == 0);
310 }
311
312 void CWebPageDiffFrame::OnWebDiffEvent(const WebDiffEvent& event)
313 {
314         switch (event.type)
315         {
316         case WebDiffEvent::SourceChanged:
317         case WebDiffEvent::TabChanged:
318         {
319                 if (m_nBufferType[event.pane] == BUFFERTYPE::UNNAMED)
320                 {
321                         m_nBufferType[event.pane] = BUFFERTYPE::NORMAL;
322                         m_strDesc[event.pane].clear();
323                 }
324                 String curUrl = m_pWebDiffWindow->GetCurrentUrl(event.pane);
325                 if (!isTempFile(curUrl) && curUrl != _T("about:blank"))
326                 {
327                         m_filePaths[event.pane] = paths::isFileURL(curUrl) ? paths::FromURL(curUrl) : curUrl;
328                         UpdateHeaderPath(event.pane);
329                 }
330                 break;
331         }
332         case WebDiffEvent::ZoomFactorChanged:
333                 UpdateWebPageDiffBar();
334                 break;
335         }
336 }
337
338 /**
339  * @brief returns true if WinWebDiffLib.dll is loadable
340  */
341 bool CWebPageDiffFrame::IsLoadable()
342 {
343         static HMODULE hModule;
344         if (hModule == nullptr)
345         {
346                 hModule = LoadLibraryW(L"WinWebDiff\\WinWebDiffLib.dll");
347                 if (hModule == nullptr)
348                         return false;
349         }
350         return true;
351 }
352
353
354 /**
355  * @brief returns true if URL matches configured pattern
356  */
357 bool CWebPageDiffFrame::MatchURLPattern(const String& url)
358 {
359         const String& includePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_INCLUDE);
360         if (includePattern.empty())
361                 return false;
362         std::string textu8 = ucr::toUTF8(url);
363         try
364         {
365                 Poco::RegularExpression reInclude(ucr::toUTF8(includePattern));
366                 if (!reInclude.match(textu8))
367                         return false;
368                 const String& excludePattern = GetOptionsMgr()->GetString(OPT_CMP_WEB_URL_PATTERN_TO_EXCLUDE);
369                 if (excludePattern.empty())
370                         return true;
371                 Poco::RegularExpression reExclude(ucr::toUTF8(excludePattern));
372                 return !reExclude.match(textu8);
373         }
374         catch (...)
375         {
376                 return false;
377         }
378 }
379
380 /**
381  * @brief Create the splitter, the filename bar, the status bar, and the two views
382  */
383 BOOL CWebPageDiffFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
384         CCreateContext* pContext)
385 {
386         if (!IsLoadable())
387                 return FALSE;
388
389         HMODULE hModule = GetModuleHandleW(L"WinWebDiffLib.dll");
390         if (hModule == nullptr)
391                 return FALSE;
392
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)
397         {
398                 FreeLibrary(hModule);
399                 return FALSE;
400         }
401
402         if (!m_pWebDiffWindow->IsWebView2Installed())
403         {
404                 if (IDYES == AfxMessageBox(_("WebView2 runtime is not installed. Do you want to download it?").c_str(), MB_ICONWARNING | MB_YESNO))
405                 {
406                         m_pWebDiffWindow->DownloadWebView2();
407                 }
408                 return FALSE;
409         }
410
411         m_pWebDiffWindow->AddEventListener(
412                 Callback<IWebDiffEventHandler>([this](const WebDiffEvent& event) -> HRESULT
413                 {
414                         OnWebDiffEvent(event);
415                         return S_OK;
416                 })
417         );
418
419         LoadOptions();
420
421         m_pWebDiffWindow->SetUserDataFolderType(
422                 static_cast<IWebDiffWindow::UserdataFolderType>(GetOptionsMgr()->GetInt(OPT_CMP_WEB_USERDATAFOLDER_TYPE)),
423                 GetOptionsMgr()->GetBool(OPT_CMP_WEB_USERDATAFOLDER_PERPANE));
424
425         auto callback = Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
426                 {
427                         int nNormalBuffer = 0;
428                         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
429                         {
430                                 if (m_nBufferType[pane] != BUFFERTYPE::UNNAMED)
431                                         ++nNormalBuffer;
432                         }
433                         if (nNormalBuffer == 0)
434                                 UpdateDiffItem(m_pDirDoc);
435
436                         if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
437                                 m_pWebDiffWindow->FirstDiff();
438
439                         if (m_callbackOnOpenCompleted)
440                         {
441                                 m_callbackOnOpenCompleted();
442                                 m_callbackOnOpenCompleted = nullptr;
443                         }
444                         m_bCompareCompleted = true;
445                         return S_OK;
446                 });
447         bool bResult;
448         if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
449         {
450                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
451                         m_filePaths[pane] = _T("about:blank");
452         }
453         bResult = OpenUrls(callback);
454
455         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
456         {
457                 m_fileInfo[pane].Update(m_filePaths[pane]);
458         }
459
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))
465 //      {
466 //              TRACE0("Failed to create LocationBar\n");
467 //              return FALSE;
468 //      }
469 //
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)
474 //      {
475 //              return FALSE;
476 //      }
477 //
478 //      m_pWebToolWindow->Translate(TranslateLocationPane);
479 //
480 //      m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
481
482         return TRUE;
483 }
484
485 //void CWebPageDiffFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
486 //{
487 //      swprintf_s(dst, dstbufsize, L"%s", tr("WebPageDiffFrame|LocationPane", ucr::toUTF8(org)).c_str());
488 //}
489
490 /////////////////////////////////////////////////////////////////////////////
491 // CWebPageDiffFrame message handlers
492
493 int CWebPageDiffFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
494 {
495         if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
496                 return -1;
497
498         EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
499
500         CMergeFrameCommon::RemoveBarBorder();
501
502         // Merge frame has a header bar at top
503         if (!m_wndWebPageDiffBar.Create(this) || !m_wndFilePathBar.Create(this))
504         {
505                 TRACE0("Failed to create dialog bar\n");
506                 return -1;      // fail to create
507         }
508
509         UpdateWebPageDiffBar();
510
511         m_wndFilePathBar.SetPaneCount(m_pWebDiffWindow->GetPaneCount());
512         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
513                 if (m_nActivePane != pane)
514                         m_pWebDiffWindow->SetActivePane(pane);
515         });
516         m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
517                 m_strDesc[pane] = sText;
518                 UpdateHeaderPath(pane);
519         });
520
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);
527
528         for (int nPane = 0; nPane < m_pWebDiffWindow->GetPaneCount(); nPane++)
529         {
530                 m_wndFilePathBar.SetActive(nPane, FALSE);
531 //              CreateWebWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pWebDiffWindow->GetPaneHWND(nPane)));
532                 UpdateHeaderPath(nPane);
533         }
534
535 //      CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
536 //      m_rectBorder.bottom = size.cy;
537
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"));
544
545         return 0;
546 }
547
548 /**
549 * @brief We must use this function before a call to SetDockState
550 *
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.
555 */
556 bool CWebPageDiffFrame::EnsureValidDockState(CDockState& state)
557 {
558         for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
559         {
560                 bool barIsCorrect = true;
561                 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
562                 if (pInfo == nullptr)
563                         barIsCorrect = false;
564                 else
565                 {
566                         if (!pInfo->m_bFloating)
567                         {
568                                 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
569                                 if (pInfo->m_pBar == nullptr)
570                                         barIsCorrect = false; //toolbar id's probably changed   
571                         }
572                 }
573
574                 if (!barIsCorrect)
575                         state.m_arrBarInfo.RemoveAt(i);
576         }
577         return true;
578 }
579
580 /**
581  * @brief Save the window's position, free related resources, and destroy the window
582  */
583 BOOL CWebPageDiffFrame::DestroyWindow() 
584 {
585         SavePosition();
586         SaveActivePane();
587         SaveOptions();
588         SaveWindowState();
589         CFrameWnd* pParentFrame = GetParentFrame();
590         BOOL result = CMergeFrameCommon::DestroyWindow();
591         if (pParentFrame)
592                 pParentFrame->OnUpdateFrameTitle(FALSE);
593         return result;
594 }
595
596 void CWebPageDiffFrame::LoadOptions()
597 {
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);
624         DIFFOPTIONS options;
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);
636 }
637
638 void CWebPageDiffFrame::SaveOptions()
639 {
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());
648 }
649
650 /**
651  * @brief Save coordinates of the frame, splitters, and bars
652  *
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
655  */
656 void CWebPageDiffFrame::SavePosition()
657 {
658         CRect rc;
659         GetWindowRect(&rc);
660
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"));
668 }
669
670 void CWebPageDiffFrame::SaveActivePane()
671 {
672         GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pWebDiffWindow->GetActivePane());
673 }
674
675 void CWebPageDiffFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
676 {
677         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
678
679         if (bActivate)
680         {
681                 GetMainFrame()->PostMessage(WM_USER + 1);
682         }
683 }
684
685 void CWebPageDiffFrame::OnClose() 
686 {
687         // clean up pointers.
688         CMergeFrameCommon::OnClose();
689 }
690
691 void CWebPageDiffFrame::OnDestroy()
692 {
693         if (!m_pWebDiffWindow)
694                 return;
695 }
696
697 /**
698  * @brief Reloads the opened files
699  */
700 void CWebPageDiffFrame::OnFileReload()
701 {
702         int nActivePane = m_pWebDiffWindow->GetActivePane();
703         OpenUrls(
704                 Callback<IWebDiffCallback>([nActivePane, this](const WebDiffCallbackResult& result) -> HRESULT
705                         {
706                                 MoveOnLoad(nActivePane);
707                                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
708                                         m_fileInfo[pane].Update(m_filePaths[pane]);
709                                 return S_OK;
710                         }));
711 }
712
713 void CWebPageDiffFrame::OnFileClose() 
714 {
715         OnClose();
716 }
717
718 void CWebPageDiffFrame::OnFileRecompareAs(UINT nID)
719 {
720         PathContext paths = m_filePaths;
721         fileopenflags_t dwFlags[3];
722         String strDesc[3];
723         int nBuffers = m_filePaths.GetSize();
724         PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
725
726         for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
727         {
728                 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
729                 strDesc[nBuffer] = m_strDesc[nBuffer];
730         }
731         if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
732         {
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;
735         }
736
737         CloseNow();
738         GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
739                 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
740 }
741
742 void CWebPageDiffFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
743 {
744         pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_WEBPAGE);
745 }
746
747 void CWebPageDiffFrame::OnOpenWithUnpacker()
748 {
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)
753         {
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] };
758                 CloseNow();
759                 GetMainFrame()->DoFileOpen(
760                         GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_WEBPAGE: -1,
761                         &paths, dwFlags, strDesc, _T(""), &infoUnpacker);
762         }
763 }
764
765 void  CWebPageDiffFrame::OnWindowChangePane(UINT nID) 
766 {
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);
771 }
772
773 /**
774  * @brief Write path and filename to headerbar
775  * @note SetText() does not repaint unchanged text
776  */
777 void CWebPageDiffFrame::UpdateHeaderPath(int pane)
778 {
779         String sText;
780
781         if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
782                 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
783         {
784                 sText = m_strDesc[pane];
785         }
786         else
787         {
788                 sText = m_filePaths.GetPath(pane);
789                 if (m_pDirDoc != nullptr)
790                         m_pDirDoc->ApplyDisplayRoot(pane, sText);
791         }
792
793         m_wndFilePathBar.SetText(pane, sText.c_str());
794
795         SetTitle(nullptr);
796 }
797
798 /// update splitting position for panels 1/2 and headerbar and statusbar 
799 void CWebPageDiffFrame::UpdateHeaderSizes()
800 {
801         if (m_pWebDiffWindow != nullptr)
802         {
803                 int w[3];
804                 CRect rc, rcMergeWindow;
805                 int nPaneCount = m_pWebDiffWindow->GetPaneCount();
806                 GetClientRect(&rc);
807                 ::GetWindowRect(m_pWebDiffWindow->GetHWND(), &rcMergeWindow);
808                 ScreenToClient(rcMergeWindow);
809                 if (!m_pWebDiffWindow->GetHorizontalSplit())
810                 {
811                         for (int pane = 0; pane < nPaneCount; pane++)
812                         {
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)
816                         }
817                 }
818                 else
819                 {
820                         for (int pane = 0; pane < nPaneCount; pane++)
821                                 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
822                 }
823
824                 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
825                 {
826                         std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
827
828                         // resize controls in header dialog bar
829                         m_wndFilePathBar.Resize(w);
830
831                         rc.left = rcMergeWindow.left;
832                         rc.top = rc.bottom - m_rectBorder.bottom;
833                         rc.right = rc.left;
834                         for (int pane = 0; pane < nPaneCount; pane++)
835                         {
836                                 rc.right += w[pane] + 4 + 2;
837 //                              m_wndStatusBar[pane].MoveWindow(&rc);
838                                 rc.left = rc.right;
839                         }
840                 }
841         }
842 }
843
844 /**
845  * @brief Update document filenames to title
846  */
847 void CWebPageDiffFrame::SetTitle(LPCTSTR lpszTitle)
848 {
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)
852         {
853                 SetWindowText(sTitle.c_str());
854                 GetMainFrame()->OnUpdateFrameTitle(TRUE);
855         }
856 }
857
858 void CWebPageDiffFrame::UpdateLastCompareResult()
859 {
860         if (m_bCompareCompleted)
861                 SetLastCompareResult(m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0);
862 }
863
864 void CWebPageDiffFrame::UpdateAutoPaneResize()
865 {
866 }
867
868 void CWebPageDiffFrame::UpdateSplitter()
869 {
870 }
871
872 bool CWebPageDiffFrame::OpenUrls(IWebDiffCallback* callback)
873 {
874         bool bResult;
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)
879         {
880                 strTempFileName[pane] = m_filePaths[pane];
881                 if (!m_infoUnpacker.Unpacking(&m_unpackerSubcodes[pane], strTempFileName[pane], filteredFilenames, {strTempFileName[pane]}))
882                 {
883 //                      return false;
884                 }
885         }
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));
888         else
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));
890         return bResult;
891 }
892
893 /**
894  * @brief Update associated diff item
895  */
896 int CWebPageDiffFrame::UpdateDiffItem(CDirDoc *pDirDoc)
897 {
898         // If directory compare has results
899         if (pDirDoc && pDirDoc->HasDiffs())
900         {
901 // FIXME:
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))
906 //              {
907 //                      DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
908 //                      ::UpdateDiffItem(m_nBuffers, di, &ctxt);
909 //              }
910         }
911         int result = m_pWebDiffWindow->GetDiffCount() > 0 ? 1 : 0;
912         SetLastCompareResult(result != 0);
913         return result;
914 }
915
916 /// Document commanding us to close
917 bool CWebPageDiffFrame::CloseNow()
918 {
919         DestroyWindow();
920         return true;
921 }
922
923 /**
924  * @brief A string to display as a tooltip for MDITabbar
925  */
926 CString CWebPageDiffFrame::GetTooltipString() const
927 {
928         return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
929 }
930
931 /**
932  * @brief Update any resources necessary after a GUI language change
933  */
934 void CWebPageDiffFrame::UpdateResources()
935 {
936         //m_pWebToolWindow->Translate(TranslateLocationPane);
937 }
938
939 void CWebPageDiffFrame::RefreshOptions()
940 {
941         LoadOptions();
942 }
943
944 /**
945  * @brief Handle some keys when in merging mode
946  */
947 bool CWebPageDiffFrame::MergeModeKeyDown(MSG* pMsg)
948 {
949         bool bHandled = false;
950
951         // Allow default text selection when SHIFT pressed
952         if (::GetAsyncKeyState(VK_SHIFT))
953                 return false;
954
955         // Allow default editor functions when CTRL pressed
956         if (::GetAsyncKeyState(VK_CONTROL))
957                 return false;
958
959         // If we are in merging mode (merge with cursor keys)
960         // handle some keys here
961         switch (pMsg->wParam)
962         {
963         case VK_UP:
964                 OnPrevdiff();
965                 bHandled = true;
966                 break;
967         case VK_DOWN:
968                 OnNextdiff();
969                 bHandled = true;
970                 break;
971         }
972
973         return bHandled;
974 }
975 /**
976  * @brief Check for keyboard commands
977  */
978 BOOL CWebPageDiffFrame::PreTranslateMessage(MSG* pMsg)
979 {
980         if (pMsg->message == WM_KEYDOWN)
981         {
982                 // If we are in merging mode (merge with cursor keys)
983                 // handle some keys here
984                 if (theApp.GetMergingMode())
985                 {
986                         bool bHandled = MergeModeKeyDown(pMsg);
987                         if (bHandled)
988                                 return true;
989                 }
990
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)
993                 {
994                         PostMessage(WM_CLOSE, 0, 0);
995                         return true;
996                 }
997         }
998         return CMergeFrameCommon::PreTranslateMessage(pMsg);
999 }
1000
1001 void CWebPageDiffFrame::OnSize(UINT nType, int cx, int cy) 
1002 {
1003         CMergeFrameCommon::OnSize(nType, cx, cy);
1004         UpdateHeaderSizes();
1005 }
1006
1007 /**
1008  * @brief Synchronize control and status bar placements with splitter position,
1009  * update mod indicators, synchronize scrollbars
1010  */
1011 void CWebPageDiffFrame::OnIdleUpdateCmdUI()
1012 {
1013         if (IsWindowVisible())
1014         {
1015                 int nActivePane = m_pWebDiffWindow->GetActivePane();
1016                 if (nActivePane != -1)
1017                         m_nActivePane = nActivePane;
1018
1019                 UpdateHeaderSizes();
1020                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1021                 {
1022                         // Update mod indicators
1023                         m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1024                 }
1025                 UpdateLastCompareResult();
1026         }
1027         CMergeFrameCommon::OnIdleUpdateCmdUI();
1028 }
1029
1030 /**
1031  * @brief Save pane sizes and positions when one of panes requests it.
1032  */
1033 LRESULT CWebPageDiffFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1034 {
1035         SavePosition();
1036         return 0;
1037 }
1038
1039 void CWebPageDiffFrame::UpdateWebPageDiffBar()
1040 {
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;
1054 }
1055
1056 void CWebPageDiffFrame::OnBnClickedFitToWindow()
1057 {
1058         m_pWebDiffWindow->SetFitToWindow(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_FITTOWINDOW));
1059         UpdateWebPageDiffBar();
1060 }
1061
1062 void CWebPageDiffFrame::OnBnClickedShowDifferences()
1063 {
1064         m_pWebDiffWindow->SetShowDifferences(m_wndWebPageDiffBar.IsDlgButtonChecked(IDC_SHOWDIFFERENCES));
1065         UpdateWebPageDiffBar();
1066 }
1067
1068 void CWebPageDiffFrame::OnBnClickedCompare()
1069 {
1070         OnRefresh();
1071 }
1072
1073 void CWebPageDiffFrame::OnDropDownCompare(NMHDR *pNMHDR, LRESULT *pResult)
1074 {
1075         CRect rc;
1076         m_wndWebPageDiffBar.GetDlgItem(IDC_COMPARE)->GetWindowRect(&rc);
1077         CPoint point { rc.left, rc.bottom };
1078         BCMenu menuPopup;
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());
1084 }
1085
1086 void CWebPageDiffFrame::OnEnChangeWidth()
1087 {
1088         if (m_bInUpdateWebPageDiffBar)
1089                 return;
1090         String text;
1091         m_wndWebPageDiffBar.GetDlgItemText(IDC_WIDTH, text);
1092         int v = tc::ttoi(text.c_str());
1093         if (v > 0 && v < 32678)
1094         {
1095                 SIZE size = m_pWebDiffWindow->GetSize();
1096                 size.cx = v;
1097                 m_pWebDiffWindow->SetSize(size);
1098         }
1099 }
1100
1101 void CWebPageDiffFrame::OnEnChangeHeight()
1102 {
1103         if (m_bInUpdateWebPageDiffBar)
1104                 return;
1105         String text;
1106         m_wndWebPageDiffBar.GetDlgItemText(IDC_HEIGHT, text);
1107         int v = tc::ttoi(text.c_str());
1108         if (v > 0 && v < 32678)
1109         {
1110                 SIZE size = m_pWebDiffWindow->GetSize();
1111                 size.cy = v;
1112                 m_pWebDiffWindow->SetSize(size);
1113         }
1114 }
1115
1116 void CWebPageDiffFrame::OnEnChangeZoom()
1117 {
1118         if (m_bInUpdateWebPageDiffBar)
1119                 return;
1120         String text;
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);
1126 }
1127
1128 void CWebPageDiffFrame::OnEnChangeUserAgent()
1129 {
1130         if (m_bInUpdateWebPageDiffBar)
1131                 return;
1132         String text;
1133         m_wndWebPageDiffBar.GetDlgItemText(IDC_USERAGENT, text);
1134         m_pWebDiffWindow->SetUserAgent(text.c_str());
1135 }
1136
1137 void CWebPageDiffFrame::OnKillFocusBarControls()
1138 {
1139         UpdateWebPageDiffBar();
1140         SaveOptions();
1141         CString text;
1142         m_wndFilePathBar.GetDlgItemTextW(IDC_USERAGENT, text);
1143 }
1144
1145 void CWebPageDiffFrame::OnUpdateStatusNum(CCmdUI* pCmdUI) 
1146 {
1147         tchar_t sIdx[32] = { 0 };
1148         tchar_t sCnt[32] = { 0 };
1149         String s;
1150         const int nDiffs = m_pWebDiffWindow->GetDiffCount();
1151         
1152         // Files are identical - show text "Identical"
1153         if (nDiffs <= 0)
1154                 s = theApp.LoadString(IDS_IDENTICAL);
1155         
1156         // There are differences, but no selected diff
1157         // - show amount of diffs
1158         else if (m_pWebDiffWindow->GetCurrentDiffIndex() < 0)
1159         {
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);
1163         }
1164         
1165         // There are differences and diff selected
1166         // - show diff number and amount of diffs
1167         else
1168         {
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);
1175         }
1176         pCmdUI->SetText(s.c_str());
1177 }
1178         
1179 /**
1180  * @brief Cut current selection to clipboard
1181  */
1182 void CWebPageDiffFrame::OnEditCut()
1183 {
1184         m_pWebDiffWindow->Cut();
1185 }
1186
1187 /**
1188  * @brief Copy current selection to clipboard
1189  */
1190 void CWebPageDiffFrame::OnEditCopy()
1191 {
1192         m_pWebDiffWindow->Copy();
1193 }
1194
1195 /**
1196  * @brief Paste clipboard content over selected content
1197  */
1198 void CWebPageDiffFrame::OnEditPaste()
1199 {
1200         m_pWebDiffWindow->Paste();
1201 }
1202
1203 /**
1204  * @brief Undo last action
1205  */
1206 void CWebPageDiffFrame::OnEditUndo()
1207 {
1208         m_pWebDiffWindow->Undo();
1209 }
1210
1211 /**
1212  * @brief Redo last action
1213  */
1214 void CWebPageDiffFrame::OnEditRedo()
1215 {
1216         m_pWebDiffWindow->Redo();
1217 }
1218
1219 /**
1220  * @brief Update the tool bar's "Undo" icon. It should be enabled when
1221  * renaming an item and undo is possible.
1222  */
1223 void CWebPageDiffFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1224 {
1225         pCmdUI->Enable(m_pWebDiffWindow->CanUndo());
1226 }
1227
1228 /**
1229  * @brief Update the tool bar's "Redo" icon. It should be enabled when
1230  * renaming an item and undo is possible.
1231  */
1232 void CWebPageDiffFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1233 {
1234         pCmdUI->Enable(m_pWebDiffWindow->CanRedo());
1235 }
1236
1237 /**
1238  * @brief Select entire image
1239  */
1240 void CWebPageDiffFrame::OnEditSelectAll()
1241 {
1242         m_pWebDiffWindow->SelectAll();
1243 }
1244
1245 /**
1246  * @brief Called when user selects View/Zoom In from menu.
1247  */
1248 void CWebPageDiffFrame::OnViewZoomIn()
1249 {
1250         m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() + 0.1);
1251 }
1252
1253 /**
1254  * @brief Called when user selects View/Zoom Out from menu.
1255  */
1256 void CWebPageDiffFrame::OnViewZoomOut()
1257 {
1258         m_pWebDiffWindow->SetZoom(m_pWebDiffWindow->GetZoom() - 0.1);
1259 }
1260
1261 /**
1262  * @brief Called when user selects View/Zoom Normal from menu.
1263  */
1264 void CWebPageDiffFrame::OnViewZoomNormal()
1265 {
1266         m_pWebDiffWindow->SetZoom(1.0);
1267 }
1268
1269 /**
1270  * @brief Enables/disables linediff (different color for diffs)
1271  */
1272 void CWebPageDiffFrame::OnViewLineDiffs() 
1273 {
1274         bool bWordDiffHighlight = !GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
1275         GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, bWordDiffHighlight);
1276         m_pWebDiffWindow->SetShowWordDifferences(bWordDiffHighlight);
1277 }
1278
1279 void CWebPageDiffFrame::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
1280 {
1281         pCmdUI->Enable(true);
1282         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
1283 }
1284
1285 /**
1286  * @brief Split panes vertically
1287  */
1288 void CWebPageDiffFrame::OnViewSplitVertically() 
1289 {
1290         bool bSplitVertically = !m_pWebDiffWindow->GetHorizontalSplit();
1291         bSplitVertically = !bSplitVertically; // toggle
1292         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1293         m_pWebDiffWindow->SetHorizontalSplit(!bSplitVertically);
1294 }
1295
1296 /**
1297  * @brief Update "Split Vertically" UI items
1298  */
1299 void CWebPageDiffFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
1300 {
1301         pCmdUI->Enable(TRUE);
1302         pCmdUI->SetCheck(!m_pWebDiffWindow->GetHorizontalSplit());
1303 }
1304
1305 /**
1306  * @brief Go to first diff
1307  *
1308  * Called when user selects "First Difference"
1309  * @sa CWebPageDiffFrame::SelectDiff()
1310  */
1311 void CWebPageDiffFrame::OnFirstdiff()
1312 {
1313         m_pWebDiffWindow->FirstDiff();
1314 }
1315
1316 /**
1317  * @brief Update "First diff" UI items
1318  */
1319 void CWebPageDiffFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1320 {
1321         OnUpdatePrevdiff(pCmdUI);
1322 }
1323
1324 /**
1325  * @brief Go to last diff
1326  */
1327 void CWebPageDiffFrame::OnLastdiff()
1328 {
1329         m_pWebDiffWindow->LastDiff();
1330 }
1331
1332 /**
1333  * @brief Update "Last diff" UI items
1334  */
1335 void CWebPageDiffFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1336 {
1337         OnUpdateNextdiff(pCmdUI);
1338 }
1339
1340 /**
1341  * @brief Go to next diff and select it.
1342  */
1343 void CWebPageDiffFrame::OnNextdiff()
1344 {
1345         if (m_pWebDiffWindow->GetCurrentDiffIndex() != m_pWebDiffWindow->GetDiffCount() - 1)
1346                 m_pWebDiffWindow->NextDiff();
1347         else if (m_pDirDoc != nullptr)
1348                 m_pDirDoc->MoveToNextDiff(this);
1349 }
1350
1351 /**
1352  * @brief Update "Next diff" UI items
1353  */
1354 void CWebPageDiffFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1355 {
1356         bool enabled = m_bCompareCompleted && (
1357                 m_pWebDiffWindow->GetNextDiffIndex() >= 0 ||
1358                 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1359
1360         if (!enabled && m_pDirDoc != nullptr)
1361                 enabled = m_pDirDoc->MoveableToNextDiff();
1362         pCmdUI->Enable(enabled);
1363 }
1364
1365 /**
1366  * @brief Go to previous diff and select it.
1367  */
1368 void CWebPageDiffFrame::OnPrevdiff()
1369 {
1370         if (m_pWebDiffWindow->GetCurrentDiffIndex() > 0)
1371         {
1372                 m_pWebDiffWindow->PrevDiff();
1373         }
1374         else if (m_pDirDoc != nullptr)
1375                 m_pDirDoc->MoveToPrevDiff(this);
1376 }
1377
1378 /**
1379  * @brief Update "Previous diff" UI items
1380  */
1381 void CWebPageDiffFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1382 {
1383         bool enabled = m_bCompareCompleted && (
1384                 m_pWebDiffWindow->GetPrevDiffIndex() >= 0 ||
1385                 (m_pWebDiffWindow->GetDiffCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1));
1386
1387         if (!enabled && m_pDirDoc != nullptr)
1388                 enabled = m_pDirDoc->MoveableToPrevDiff();
1389         pCmdUI->Enable(enabled);
1390 }
1391
1392 /**
1393  * @brief Go to next conflict and select it.
1394  */
1395 void CWebPageDiffFrame::OnNextConflict()
1396 {
1397         m_pWebDiffWindow->NextConflict();
1398 }
1399
1400 /**
1401  * @brief Update "Next Conflict" UI items
1402  */
1403 void CWebPageDiffFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1404 {
1405         pCmdUI->Enable(m_bCompareCompleted && 
1406                 m_pWebDiffWindow->GetPaneCount() > 2 && (
1407                         m_pWebDiffWindow->GetNextConflictIndex() >= 0 ||
1408                         (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1409                 )
1410         );
1411 }
1412
1413 /**
1414  * @brief Go to previous diff and select it.
1415  */
1416 void CWebPageDiffFrame::OnPrevConflict()
1417 {
1418         m_pWebDiffWindow->PrevConflict();
1419 }
1420
1421 /**
1422  * @brief Update "Previous diff" UI items
1423  */
1424 void CWebPageDiffFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1425 {
1426         pCmdUI->Enable(m_bCompareCompleted &&
1427                 m_pWebDiffWindow->GetPaneCount() > 2 && (
1428                         m_pWebDiffWindow->GetPrevConflictIndex() >= 0 ||
1429                         (m_pWebDiffWindow->GetConflictCount() > 0 && m_pWebDiffWindow->GetCurrentDiffIndex() == -1)
1430                 )
1431         );
1432 }
1433
1434 void CWebPageDiffFrame::OnWebFitToWindow()
1435 {
1436         m_pWebDiffWindow->SetFitToWindow(true);
1437         SaveOptions();
1438         UpdateWebPageDiffBar();
1439 }
1440
1441 void CWebPageDiffFrame::OnUpdateWebFitToWindow(CCmdUI* pCmdUI)
1442 {
1443         pCmdUI->SetCheck(m_pWebDiffWindow->GetFitToWindow());
1444 }
1445
1446 void CWebPageDiffFrame::OnWebSize(UINT nID)
1447 {
1448         switch (nID)
1449         {
1450         case ID_WEB_SIZE_320x512:
1451                 m_pWebDiffWindow->SetFitToWindow(false);
1452                 m_pWebDiffWindow->SetSize({ 320, 512 });
1453                 UpdateWebPageDiffBar();
1454                 break;
1455         case ID_WEB_SIZE_375x600:
1456                 m_pWebDiffWindow->SetFitToWindow(false);
1457                 m_pWebDiffWindow->SetSize({ 375, 600 });
1458                 UpdateWebPageDiffBar();
1459                 break;
1460         case ID_WEB_SIZE_1024x640:
1461                 m_pWebDiffWindow->SetFitToWindow(false);
1462                 m_pWebDiffWindow->SetSize({ 1024, 640 });
1463                 SaveOptions();
1464                 UpdateWebPageDiffBar();
1465                 break;
1466         case ID_WEB_SIZE_1280x800:
1467                 m_pWebDiffWindow->SetFitToWindow(false);
1468                 m_pWebDiffWindow->SetSize({ 1280, 800 });
1469                 SaveOptions();
1470                 UpdateWebPageDiffBar();
1471                 break;
1472         case ID_WEB_SIZE_1440x900:
1473                 m_pWebDiffWindow->SetFitToWindow(false);
1474                 m_pWebDiffWindow->SetSize({ 1440, 900 });
1475                 SaveOptions();
1476                 UpdateWebPageDiffBar();
1477                 break;
1478         }
1479 }
1480
1481 void CWebPageDiffFrame::OnWebCompareScreenshots(UINT nID)
1482 {
1483         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1484         PathContext paths;
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)
1489         {
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);
1496         }
1497         m_pWebDiffWindow->SaveFiles(
1498                 (nID == ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS) ? IWebDiffWindow::FULLSIZE_SCREENSHOT : IWebDiffWindow::SCREENSHOT,
1499                 spaths,
1500                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1501                         {
1502                                 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1503                                 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data());
1504                                 return S_OK;
1505                         }));
1506 }
1507
1508 void CWebPageDiffFrame::OnWebCompareHTMLs()
1509 {
1510         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1511         PathContext paths;
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)
1516         {
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);
1523         }
1524         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::HTML, spaths,
1525                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1526                         {
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);
1530                                 return S_OK;
1531                         }));
1532 }
1533
1534 void CWebPageDiffFrame::OnWebCompareTexts()
1535 {
1536         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1537         PathContext paths;
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)
1542         {
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);
1549         }
1550         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::TEXT, spaths,
1551                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1552                         {
1553                                 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1554                                 GetMainFrame()->DoFileOpen(0, &paths, dwFlags, descs.data(), _T(""));
1555                                 return S_OK;
1556                         }));
1557 }
1558
1559 void CWebPageDiffFrame::OnWebCompareResourceTrees()
1560 {
1561         std::shared_ptr<CWaitCursor> pWaitStatus{ new CWaitCursor() };
1562         PathContext paths;
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)
1567         {
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);
1574         }
1575         m_pWebDiffWindow->SaveFiles(IWebDiffWindow::RESOURCETREE, spaths,
1576                 Callback<IWebDiffCallback>([paths, descs, pWaitStatus](const WebDiffCallbackResult& result) -> HRESULT
1577                         {
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);
1582                                 return S_OK;
1583                         }));
1584 }
1585
1586 void CWebPageDiffFrame::OnWebClear(UINT nID)
1587 {
1588         IWebDiffWindow::BrowsingDataType dataKinds;
1589         switch (nID)
1590         {
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;
1595         default:
1596                 return;
1597         }
1598         m_pWebDiffWindow->ClearBrowsingData(-1, dataKinds);
1599 }
1600
1601 bool CWebPageDiffFrame::GenerateReport(const String& sFileName) const
1602 {
1603         bool result = false;
1604         bool completed = false;
1605         if (!GenerateReport(sFileName, [&completed, &result](bool res) { result = res; completed = true; }))
1606                 return false;
1607         CMainFrame::WaitAndDoMessageLoop(completed, 0);
1608         return result;
1609 }
1610
1611 bool CWebPageDiffFrame::GenerateReport(const String& sFileName, std::function<void(bool)> callback) const
1612 {
1613         String rptdir_full, rptdir, path, name, ext;
1614         String url[3];
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);
1622
1623         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1624         {
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();
1629         }
1630
1631         UniStdioFile file;
1632         if (!file.Open(sFileName, _T("wt")))
1633         {
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);
1638                 return false;
1639         }
1640
1641         file.SetCodepage(ucr::CP_UTF_8);
1642
1643         file.WriteString(
1644                 _T("<!DOCTYPE html>\n")
1645                 _T("<html>\n")
1646                 _T("<head>\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")
1655                 _T("</style>\n")
1656                 _T("</head>\n")
1657                 _T("<body>\n")
1658                 _T("<table>\n")
1659                 _T("<tr>\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"));
1663         file.WriteString(
1664                 _T("<tr>\n"));
1665         for (int pane = 0; pane < m_pWebDiffWindow->GetPaneCount(); ++pane)
1666                 file.WriteString(
1667                         strutils::format(_T("<td><embed type=\"application/pdf\" src=\"%s\" title=\"%s\"></td>\n"),
1668                                 diffrpt_filename[pane], diffrpt_filename[pane]));
1669         file.WriteString(
1670                 _T("</tr>\n"));
1671         file.WriteString(
1672                 _T("</table>\n")
1673                 _T("</body>\n")
1674                 _T("</html>\n"));
1675
1676         return SUCCEEDED(m_pWebDiffWindow->SaveDiffFiles(IWebDiffWindow::PDF, pfilenames, 
1677                 Callback<IWebDiffCallback>([this, callback](const WebDiffCallbackResult& result) -> HRESULT
1678                         {
1679                                 callback(SUCCEEDED(result.errorCode));
1680                                 return S_OK;
1681                         })));
1682 }
1683
1684 /**
1685  * @brief Generate report from file compare results.
1686  */
1687 void CWebPageDiffFrame::OnToolsGenerateReport()
1688 {
1689         String s;
1690         CString folder;
1691         if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
1692                 return;
1693
1694         CWaitCursor waitstatus;
1695         if (GenerateReport(s))
1696                 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION | MB_MODELESS);
1697 }
1698
1699 void CWebPageDiffFrame::OnRefresh()
1700 {
1701         if (!m_bCompareCompleted)
1702                 return;
1703         m_bCompareCompleted = false;
1704         m_pWebDiffWindow->Recompare(
1705                 Callback<IWebDiffCallback>([this](const WebDiffCallbackResult& result) -> HRESULT
1706                         {
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())
1710                                 {
1711                                         CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
1712                                                 [](const tchar_t* msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
1713                                 }
1714                                 return S_OK;
1715                         })
1716                 );
1717 }
1718
1719 void CWebPageDiffFrame::OnSetFocus(CWnd* pNewWnd)
1720 {
1721         if (m_nActivePane != -1)
1722                 m_pWebDiffWindow->SetActivePane(m_nActivePane);
1723 }
1724
1725 /**
1726  * @brief Open help from mainframe when user presses F1
1727  */
1728 void CWebPageDiffFrame::OnHelp()
1729 {
1730         theApp.ShowHelp(WebPageDiffFrameHelpLocation);
1731 }