diff --git a/src/WinWebDiff/WinWebDiff.cpp b/src/WinWebDiff/WinWebDiff.cpp index 8abcf9d..589350e 100644 --- a/src/WinWebDiff/WinWebDiff.cpp +++ b/src/WinWebDiff/WinWebDiff.cpp @@ -124,14 +124,28 @@ struct CmdLineInfo int nUrls; }; +static HRESULT OnWebDiffEvent(const WebDiffEvent& e); + +struct CallbackImpl: public IWebDiffEventHandler +{ + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; } + ULONG STDMETHODCALLTYPE AddRef(void) override { return ++m_nRef; } + ULONG STDMETHODCALLTYPE Release(void) override { if (--m_nRef == 0) { delete this; return 0; } return m_nRef; } + HRESULT STDMETHODCALLTYPE Invoke(const WebDiffEvent& event) { return OnWebDiffEvent(event); } + int m_nRef = 0; +}; + HINSTANCE m_hInstance; HINSTANCE hInstDLL; HWND m_hWnd; +HWND m_hwndWebToolWindow; WCHAR m_szTitle[256] = L"WinWebDiff"; WCHAR m_szWindowClass[256] = L"WINWEBDIFF"; IWebDiffWindow* m_pWebDiffWindow = nullptr; +IWebToolWindow *m_pWebToolWindow = nullptr; std::list m_tempFiles; std::list m_tempFolders; +CallbackImpl m_callback; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); @@ -185,7 +199,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg) && m_hwndWebToolWindow == 0 || !IsDialogMessage(m_hwndWebToolWindow, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -372,6 +386,14 @@ void UpdateMenuState(HWND hWnd) CheckMenuItem(hMenu, IDM_SYNC_CLICK, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_CLICK) ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hMenu, IDM_SYNC_INPUT, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_INPUT) ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hMenu, IDM_SYNC_GOBACKFORWARD, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_GOBACKFORWARD) ? MF_CHECKED : MF_UNCHECKED); + m_pWebToolWindow->Sync(); +} + +HRESULT OnWebDiffEvent(const WebDiffEvent& e) +{ + if (WebDiffEvent::CompareScreenshotsSelected <= e.type && e.type <= WebDiffEvent::CompareResourceTreesSelected) + PostMessage(m_hWnd, WM_COMMAND, IDM_COMPARE_SCREENSHOTS + e.type - WebDiffEvent::CompareScreenshotsSelected, 0); + return S_OK; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -380,13 +402,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { case WM_CREATE: m_pWebDiffWindow = WinWebDiff_CreateWindow(hInstDLL, hWnd); + m_pWebToolWindow = WinWebDiff_CreateToolWindow(hInstDLL, hWnd, m_pWebDiffWindow); + m_hwndWebToolWindow = m_pWebToolWindow->GetHWND(); + m_callback.AddRef(); + m_pWebDiffWindow->AddEventListener(&m_callback); UpdateMenuState(hWnd); break; case WM_SIZE: { - RECT rc; + RECT rc, rcToolWindow; GetClientRect(hWnd, &rc); + GetClientRect(m_hwndWebToolWindow, &rcToolWindow); + rc.right -= rcToolWindow.right; m_pWebDiffWindow->SetWindowRect(rc); + MoveWindow(m_hwndWebToolWindow, rc.right, 0, rcToolWindow.right, rc.bottom, TRUE); break; } case WM_COMMAND: @@ -447,26 +476,32 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_VIEW_SIZE_FIT_TO_WINDOW: m_pWebDiffWindow->SetFitToWindow(true); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SIZE_320x512: m_pWebDiffWindow->SetFitToWindow(false); m_pWebDiffWindow->SetSize({ 320, 512 }); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SIZE_375x600: m_pWebDiffWindow->SetFitToWindow(false); m_pWebDiffWindow->SetSize({ 375, 600 }); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SIZE_1024x640: m_pWebDiffWindow->SetFitToWindow(false); m_pWebDiffWindow->SetSize({ 1024, 640 }); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SIZE_1280x800: m_pWebDiffWindow->SetFitToWindow(false); m_pWebDiffWindow->SetSize({ 1280, 800 }); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SIZE_1440x900: m_pWebDiffWindow->SetFitToWindow(false); m_pWebDiffWindow->SetSize({ 1440, 900}); + m_pWebToolWindow->Sync(); break; case IDM_VIEW_SPLITHORIZONTALLY: m_pWebDiffWindow->SetHorizontalSplit(!m_pWebDiffWindow->GetHorizontalSplit()); diff --git a/src/WinWebDiff/WinWebDiff.rc b/src/WinWebDiff/WinWebDiff.rc index 54bed9b..6f01e40 100644 Binary files a/src/WinWebDiff/WinWebDiff.rc and b/src/WinWebDiff/WinWebDiff.rc differ diff --git a/src/WinWebDiffLib/DiffLocation.hpp b/src/WinWebDiffLib/DiffLocation.hpp new file mode 100644 index 0000000..aee3368 --- /dev/null +++ b/src/WinWebDiffLib/DiffLocation.hpp @@ -0,0 +1,227 @@ +#include +#include +#include +#include + +using WDocument = rapidjson::GenericDocument>; +using WValue = rapidjson::GenericValue>; + +class DiffLocation +{ +public: + struct Rect + { + float left; + float top; + float width; + float height; + }; + struct ContainerRect : public Rect + { + int id; + int containerId; + float scrollLeft; + float scrollTop; + float scrollWidth; + float scrollHeight; + float clientWidth; + float clientHeight; + }; + struct DiffRect : public Rect + { + int id; + int containerId; + }; + + void read(const WDocument& doc) + { + std::wstring window = doc[L"window"].GetString(); + std::vector containerRects; + std::vector diffRects; + for (const auto& value : doc[L"diffRects"].GetArray()) + { + DiffRect rect; + rect.id = value[L"id"].GetInt(); + rect.containerId = value[L"containerId"].GetInt(); + rect.left = value[L"left"].GetFloat(); + rect.top = value[L"top"].GetFloat(); + rect.width = value[L"width"].GetFloat(); + rect.height = value[L"height"].GetFloat(); + diffRects.push_back(rect); + } + for (const auto& value : doc[L"containerRects"].GetArray()) + { + ContainerRect rect; + rect.id = value[L"id"].GetInt(); + rect.containerId = value[L"containerId"].GetInt(); + rect.left = value[L"left"].GetFloat(); + rect.top = value[L"top"].GetFloat(); + rect.width = value[L"width"].GetFloat(); + rect.height = value[L"height"].GetFloat(); + rect.scrollLeft = value[L"scrollLeft"].GetFloat(); + rect.scrollTop = value[L"scrollTop"].GetFloat(); + rect.scrollWidth = value[L"scrollWidth"].GetFloat(); + rect.scrollHeight = value[L"scrollHeight"].GetFloat(); + rect.clientWidth = value[L"clientWidth"].GetFloat(); + rect.clientHeight = value[L"clientHeight"].GetFloat(); + containerRects.push_back(rect); + } + for (auto it = doc[L"frameRects"].MemberBegin(); it != doc[L"frameRects"].MemberEnd(); ++it) + { + Rect rect; + rect.left = it->value[L"left"].GetFloat(); + rect.top = it->value[L"top"].GetFloat(); + rect.width = it->value[L"width"].GetFloat(); + rect.height = it->value[L"height"].GetFloat(); + m_frameRects.insert_or_assign(it->name.GetString(), rect); + } + m_diffRects.insert_or_assign(window, diffRects); + m_containerRects.insert_or_assign(window, containerRects); + if (window == L"") + { + m_scrollX = doc[L"scrollX"].GetFloat(); + m_scrollY = doc[L"scrollY"].GetFloat(); + m_clientWidth = doc[L"clientWidth"].GetFloat(); + m_clientHeight = doc[L"clientHeight"].GetFloat(); + } + } + + void clear() + { + m_diffRects.clear(); + m_containerRects.clear(); + m_scrollX = 0.0f; + m_scrollY = 0.0f; + m_clientWidth = 0.0f; + m_clientHeight = 0.0f; + } + + void calcGlobalPosition(Rect& rect, std::wstring window) + { + while (!window.empty()) + { + rect.left += m_frameRects[window].left; + rect.top += m_frameRects[window].top; + size_t lastOpeningBracketPos = window.find_last_of('['); + if (lastOpeningBracketPos != std::string::npos) { + window = window.substr(0, lastOpeningBracketPos); + } + } + } + + std::vector getDiffRectArray() + { + std::vector diffRectsSerialized; + for (const auto& pair: m_diffRects) + { + const auto& window = pair.first; + for (const auto& diffRect : pair.second) + { + DiffRect rect; + rect.id = diffRect.id; + rect.containerId = diffRect.containerId; + rect.left = diffRect.left; + rect.top = diffRect.top; + rect.width = diffRect.width; + rect.height = diffRect.height; + for (int containerId = diffRect.containerId; containerId != -1; ) + { + const ContainerRect& containerRect = m_containerRects[window][containerId]; + if (containerRect.id == 0 && (containerRect.width == 0 || containerRect.height == 0)) + break; + clip(rect, containerRect); + containerId = containerRect.containerId; + } + rect.left += m_scrollX; + rect.top += m_scrollY; + if (!window.empty()) + { + calcGlobalPosition(rect, window); + Rect rcFrame = m_frameRects[window]; + rcFrame.left += m_scrollX; + rcFrame.top += m_scrollY; + clip(rect, rcFrame); + } + diffRectsSerialized.push_back(rect); + } + } + return diffRectsSerialized; + } + + std::vector getContainerRectArray() + { + std::vector containerRectsSerialized; + for (const auto& pair: m_containerRects) + { + const auto& window = pair.first; + for (const auto& containerRect : pair.second) + { + ContainerRect rect; + rect.id = containerRect.id; + rect.containerId = containerRect.containerId; + rect.left = containerRect.left + m_scrollX; + rect.top = containerRect.top + m_scrollY; + rect.width = containerRect.width; + rect.height = containerRect.height; + rect.scrollLeft = containerRect.scrollLeft; + rect.scrollTop = containerRect.scrollTop; + rect.scrollWidth = containerRect.scrollWidth; + rect.scrollHeight = containerRect.scrollHeight; + rect.clientWidth = containerRect.clientWidth; + rect.clientHeight = containerRect.clientHeight; + if (!window.empty()) + { + calcGlobalPosition(rect, window); + Rect rcFrame = m_frameRects[window]; + rcFrame.left += m_scrollX; + rcFrame.top += m_scrollY; + clip(rect, rcFrame); + } + containerRectsSerialized.push_back(rect); + } + } + return containerRectsSerialized; + } + + Rect getVisibleAreaRect() + { + return { m_scrollX, m_scrollY, m_clientWidth, m_clientHeight }; + } + +private: + bool clip(Rect& rect, const Rect& containerRect) + { + if (rect.left + rect.width < containerRect.left || + rect.top + rect.height < containerRect.top || + rect.left > containerRect.left + containerRect.width || + rect.top > containerRect.top + containerRect.height) + { + rect.left = rect.top = -99999.9f; + rect.width = rect.height = 0.0f; + return true; + } + if (rect.left < containerRect.left) { + rect.width -= containerRect.left - rect.left; + rect.left = containerRect.left; + } + if (rect.top < containerRect.top) { + rect.height -= containerRect.top - rect.top; + rect.top = containerRect.top; + } + if (rect.left + rect.width > containerRect.left + containerRect.width) { + rect.width = containerRect.left + containerRect.width - rect.left; + } + if (rect.top + rect.height > containerRect.top + containerRect.height) { + rect.height = containerRect.top + containerRect.height - rect.top; + } + return false; + } + + std::map> m_diffRects; + std::map> m_containerRects; + std::map m_frameRects; + float m_scrollX = 0.0f; + float m_scrollY = 0.0f; + float m_clientWidth = 0.0f; + float m_clientHeight = 0.0f; +}; diff --git a/src/WinWebDiffLib/Resource.h b/src/WinWebDiffLib/Resource.h index b23a89a..ccbabda 100644 --- a/src/WinWebDiffLib/Resource.h +++ b/src/WinWebDiffLib/Resource.h @@ -9,10 +9,42 @@ #define IDD_DIALOGBAR 103 #define IDC_DIFFMAP 112 #define IDR_SCRIPT 130 -#define IDC_MENU 1021 -#define IDC_ZOOM 1022 -#define IDC_WIDTH 1023 -#define IDC_HEIGHT 1024 +#define IDR_POPUP_WEBPAGE_COMPARE 131 +#define IDR_POPUP_WEBPAGE_SYNC_EVENTS 132 +#define IDC_COMPARE 1021 +#define IDC_ZOOM_LABEL 1022 +#define IDC_ZOOM 1023 +#define IDC_WIDTH 1024 +#define IDC_BY 1025 +#define IDC_HEIGHT 1026 +#define IDC_FITTOWINDOW 1027 +#define IDC_USERAGENT 1028 +#define IDC_SHOWDIFFERENCES 1029 +#define IDC_SYNC_EVENTS 1030 +#define ID_WEB_COMPARE_SCREENSHOTS 1633 +#define ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS 1634 +#define ID_WEB_COMPARE_HTMLS 1635 +#define ID_WEB_COMPARE_TEXTS 1636 +#define ID_WEB_COMPARE_RESOURCETREES 1637 +#define ID_WEB_SYNC_ENABLED 1638 +#define ID_WEB_SYNC_SCROLL 1639 +#define ID_WEB_SYNC_CLICK 1640 +#define ID_WEB_SYNC_INPUT 1641 +#define ID_WEB_SYNC_GOBACKFORWARD 1642 +#define IDS_COMPARE 2000 +#define IDS_ZOOM 2001 +#define IDS_SYNC_EVENTS 2002 +#define IDS_SHOWDIFFERENCES 2003 +#define IDS_WEB_COMPARE_SCREENSHOTS 2004 +#define IDS_WEB_COMPARE_FULLSIZE_SCREENSHOTS 2005 +#define IDS_WEB_COMPARE_HTMLS 2006 +#define IDS_WEB_COMPARE_TEXTS 2007 +#define IDS_WEB_COMPARE_RESOURCETREES 2008 +#define IDS_WEB_SYNC_ENABLED 2009 +#define IDS_WEB_SYNC_SCROLL 2010 +#define IDS_WEB_SYNC_CLICK 2011 +#define IDS_WEB_SYNC_INPUT 2012 +#define IDS_WEB_SYNC_GOBACKFORWARD 2013 #define IDC_STATIC -1 // Next default values for new objects @@ -20,9 +52,9 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_RESOURCE_VALUE 133 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_CONTROL_VALUE 1031 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/src/WinWebDiffLib/WebDiffWindow.hpp b/src/WinWebDiffLib/WebDiffWindow.hpp index 7586133..beb604f 100644 --- a/src/WinWebDiffLib/WebDiffWindow.hpp +++ b/src/WinWebDiffLib/WebDiffWindow.hpp @@ -3,6 +3,7 @@ #include "WinWebDiffLib.h" #include "WebWindow.hpp" #include "DiffHighlighter.hpp" +#include "DiffLocation.hpp" #include #include @@ -178,6 +179,7 @@ class CWebDiffWindow : public IWebDiffWindow WDocument doc; doc.Parse(msg.c_str()); std::wstring event = doc.HasMember(L"event") ? doc[L"event"].GetString() : L""; + std::wstring window = doc.HasMember(L"window") ? doc[L"window"].GetString() : L""; if (event == L"dblclick") { const int diffIndex = doc[L"wwdid"].GetInt(); @@ -188,6 +190,8 @@ class CWebDiffWindow : public IWebDiffWindow { if (m_bSynchronizeEvents && GetSyncEventFlag(EVENT_SCROLL)) syncEvent(ev.pane, msg); + for (int pane = 0; pane < m_nPanes; ++pane) + m_webWindow[pane].PostWebMessageAsJsonInAllFrames(L"{\"event\": \"diffRects\"}"); } else if (event == L"click") { @@ -199,21 +203,12 @@ class CWebDiffWindow : public IWebDiffWindow if (m_bSynchronizeEvents && GetSyncEventFlag(EVENT_INPUT)) syncEvent(ev.pane, msg); } -/* - else if (event == L"submit") - { - if (m_bSynchronizeEvents && GetSyncEventFlag(EVENT_CLICK)) - syncEvent(ev.pane, msg); - } - else if (event == L"keydown") + else if (event == L"diffRects") { - if (m_bSynchronizeEvents && GetSyncEventFlag(EVENT_INPUT)) - syncEvent(ev.pane, msg); + m_diffLocation[ev.pane].read(doc); } -*/ } - for (const auto& listener : m_listeners) - listener->Invoke(ev); + RaiseEvent(ev); }); } std::vector rects = CalcChildWebWindowRect(m_hWnd, m_nPanes, m_bHorizontalSplit); @@ -571,8 +566,17 @@ class CWebDiffWindow : public IWebDiffWindow if (m_compareState != oldCompareState) { WebDiffEvent ev{ WebDiffEvent::CompareStateChanged, -1 }; - for (const auto& listener : m_listeners) - listener->Invoke(ev); + RaiseEvent(ev); + if (compareState == CompareState::COMPARED || compareState == CompareState::NOT_COMPARED) + { + if (ev.pane >= 0) + m_diffLocation[ev.pane].clear(); + } + if (compareState == CompareState::COMPARED) + { + for (int pane = 0; pane < m_nPanes; ++pane) + m_webWindow[pane].PostWebMessageAsJsonInAllFrames(L"{\"event\": \"diffRects\"}"); + } } } @@ -797,6 +801,33 @@ class CWebDiffWindow : public IWebDiffWindow return true; } + void RaiseEvent(const WebDiffEvent& e) override + { + for (const auto& listener : m_listeners) + listener->Invoke(e); + } + + std::vector GetDiffRectArray(int pane) + { + return m_diffLocation[pane].getDiffRectArray(); + } + + std::vector GetContainerRectArray(int pane) + { + return m_diffLocation[pane].getContainerRectArray(); + } + + DiffLocation::Rect GetVisibleAreaRect(int pane) + { + return m_diffLocation[pane].getVisibleAreaRect(); + } + + void ScrollTo(int pane, float scrollX, float scrollY) + { + std::wstring json = L"{\"event\": \"scrollTo\", \"window\": \"\", \"scrollX\": " + std::to_wstring(scrollX) + L", \"scrollY\": " + std::to_wstring(scrollY) + L"}"; + m_webWindow[pane].PostWebMessageAsJsonInAllFrames(json.c_str()); + } + private: HRESULT getDocumentsLoop(std::shared_ptr> jsons, IWebDiffCallback* callback, int pane = 0) @@ -1149,7 +1180,21 @@ class CWebDiffWindow : public IWebDiffWindow { HRESULT hr = result.errorCode; if (SUCCEEDED(hr)) - hr = scrollIntoViewIfNeededLoop(diffIndex, callback2.Get()); + { + hr = scrollIntoViewIfNeededLoop(diffIndex, + Callback([this, callback2](const WebDiffCallbackResult& result) -> HRESULT + { + HRESULT hr = result.errorCode; + if (SUCCEEDED(hr)) + { + WebDiffEvent ev{ WebDiffEvent::DiffSelected, -1 }; + RaiseEvent(ev); + } + if (FAILED(hr) && callback2) + return callback2->Invoke({ hr, nullptr }); + return S_OK; + }).Get()); + } if (FAILED(hr) && callback2) return callback2->Invoke({ hr, nullptr }); return S_OK; @@ -1478,6 +1523,7 @@ class CWebDiffWindow : public IWebDiffWindow std::vector> m_listeners; int m_currentDiffIndex = -1; std::vector m_diffInfos; + DiffLocation m_diffLocation[3]; DiffOptions m_diffOptions{}; bool m_bShowDifferences = true; bool m_bShowWordDifferences = true; diff --git a/src/WinWebDiffLib/WebToolWindow.hpp b/src/WinWebDiffLib/WebToolWindow.hpp index 236b6b2..531e0f4 100644 --- a/src/WinWebDiffLib/WebToolWindow.hpp +++ b/src/WinWebDiffLib/WebToolWindow.hpp @@ -6,15 +6,21 @@ #include "resource.h" #pragma once +#pragma comment(lib, "msimg32.lib") -class CWebToolWindow : public IWebToolWindow +class CWebToolWindow : public IWebToolWindow, IWebDiffEventHandler { public: + const int MARGIN = 2; + CWebToolWindow() : - m_hWnd(NULL) - , m_hInstance(NULL) - , m_pWebDiffWindow(NULL) + m_hWnd(nullptr) + , m_hInstance(nullptr) + , m_hComparePopup(nullptr) + , m_hEventSyncPopup(nullptr) + , m_pWebDiffWindow(nullptr) , m_bInSync(false) + , m_nRef(0) { } @@ -26,13 +32,19 @@ class CWebToolWindow : public IWebToolWindow { m_hInstance = hInstance; m_hWnd = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DIALOGBAR), hWndParent, DlgProc, reinterpret_cast(this)); + m_hComparePopup = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_POPUP_WEBPAGE_COMPARE)); + m_hEventSyncPopup = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_POPUP_WEBPAGE_SYNC_EVENTS)); return m_hWnd ? true : false; } bool Destroy() { BOOL bSucceeded = DestroyWindow(m_hWnd); - m_hWnd = NULL; + m_hWnd = nullptr; + DestroyMenu(m_hComparePopup); + m_hComparePopup = nullptr; + DestroyMenu(m_hEventSyncPopup); + m_hEventSyncPopup = nullptr; return !!bSucceeded; } @@ -48,64 +60,30 @@ class CWebToolWindow : public IWebToolWindow m_bInSync = true; - TCHAR buf[256]; - wsprintf(buf, _T("(%d)"), m_pWebDiffWindow->GetDiffBlockSize()); - SetDlgItemText(m_hWnd, IDC_DIFF_BLOCKSIZE_STATIC, buf); - wsprintf(buf, _T("(%d)"), static_cast(m_pWebDiffWindow->GetDiffColorAlpha() * 100)); - SetDlgItemText(m_hWnd, IDC_DIFF_BLOCKALPHA_STATIC, buf); - wsprintf(buf, _T("(%d)"), static_cast(m_pWebDiffWindow->GetColorDistanceThreshold())); - SetDlgItemText(m_hWnd, IDC_DIFF_CDTHRESHOLD_STATIC, buf); - wsprintf(buf, _T("(%d)"), static_cast(m_pWebDiffWindow->GetOverlayAlpha() * 100)); - SetDlgItemText(m_hWnd, IDC_OVERLAY_ALPHA_STATIC, buf); - wsprintf(buf, _T("(%d%%)"), static_cast(100 * m_pWebDiffWindow->GetZoom())); - SetDlgItemText(m_hWnd, IDC_ZOOM_STATIC, buf); - - SendDlgItemMessage(m_hWnd, IDC_DIFF_HIGHLIGHT, BM_SETCHECK, m_pWebDiffWindow->GetShowDifferences() ? BST_CHECKED : BST_UNCHECKED, 0); - SendDlgItemMessage(m_hWnd, IDC_DIFF_BLOCKSIZE_SLIDER, TBM_SETPOS, TRUE, m_pWebDiffWindow->GetDiffBlockSize()); - SendDlgItemMessage(m_hWnd, IDC_DIFF_BLOCKALPHA_SLIDER, TBM_SETPOS, TRUE, static_cast(m_pWebDiffWindow->GetDiffColorAlpha() * 100)); - SendDlgItemMessage(m_hWnd, IDC_DIFF_CDTHRESHOLD_SLIDER, TBM_SETPOS, TRUE, static_cast(m_pWebDiffWindow->GetColorDistanceThreshold())); - SendDlgItemMessage(m_hWnd, IDC_OVERLAY_ALPHA_SLIDER, TBM_SETPOS, TRUE, static_cast(m_pWebDiffWindow->GetOverlayAlpha() * 100)); - SendDlgItemMessage(m_hWnd, IDC_ZOOM_SLIDER, TBM_SETPOS, TRUE, static_cast(m_pWebDiffWindow->GetZoom() * 8 - 8)); - SendDlgItemMessage(m_hWnd, IDC_DIFF_INSERTION_DELETION_DETECTION_MODE, CB_SETCURSEL, m_pWebDiffWindow->GetInsertionDeletionDetectionMode(), 0); - SendDlgItemMessage(m_hWnd, IDC_OVERLAY_MODE, CB_SETCURSEL, m_pWebDiffWindow->GetOverlayMode(), 0); - SendDlgItemMessage(m_hWnd, IDC_PAGE_SPIN, UDM_SETRANGE, 0, MAKELONG(1, m_pWebDiffWindow->GetMaxPageCount())); - SendDlgItemMessage(m_hWnd, IDC_PAGE_SPIN, UDM_SETPOS, 0, MAKELONG(m_pWebDiffWindow->GetCurrentMaxPage() + 1, 0)); - - int w = static_cast(m_pWebDiffWindow)->GetDiffImageWidth(); - int h = static_cast(m_pWebDiffWindow)->GetDiffImageHeight(); + SIZE size = m_pWebDiffWindow->GetSize(); + SendDlgItemMessage(m_hWnd, IDC_SHOWDIFFERENCES, BM_SETCHECK, m_pWebDiffWindow->GetShowDifferences() ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(m_hWnd, IDC_FITTOWINDOW, BM_SETCHECK, m_pWebDiffWindow->GetFitToWindow() ? BST_CHECKED : BST_UNCHECKED, 0); + SetDlgItemText(m_hWnd, IDC_USERAGENT, m_pWebDiffWindow->GetUserAgent()); + SetDlgItemInt(m_hWnd, IDC_WIDTH, size.cx, false); + SetDlgItemInt(m_hWnd, IDC_HEIGHT, size.cy, false); + wchar_t zoomstr[256]; + swprintf_s(zoomstr, L"%.1f%%", m_pWebDiffWindow->GetZoom() * 100.0); + SetDlgItemText(m_hWnd, IDC_ZOOM, zoomstr); + EnableWindow(GetDlgItem(m_hWnd, IDC_WIDTH), !m_pWebDiffWindow->GetFitToWindow()); + EnableWindow(GetDlgItem(m_hWnd, IDC_HEIGHT), !m_pWebDiffWindow->GetFitToWindow()); - RECT rc; - GetClientRect(m_hWnd, &rc); - int cx = rc.right - rc.left; - int cy = rc.bottom - rc.top; - - RECT rcTmp; - HWND hwndDiffMap = GetDlgItem(m_hWnd, IDC_DIFFMAP); - GetWindowRect(hwndDiffMap, &rcTmp); - POINT pt = { rcTmp.left, rcTmp.top }; - ScreenToClient(m_hWnd, &pt); - int mw = 0; - int mh = 0; - if (w > 0 && h > 0) - { - mh = h * (cx - 8) / w; - if (mh + pt.y > cy - 8) - mh = cy - 8 - pt.y; - mw = mh * w / h; - } - RECT rcDiffMap = { (cx - mw) / 2, pt.y, (cx + mw) / 2, pt.y + mh }; - SetWindowPos(hwndDiffMap, NULL, rcDiffMap.left, rcDiffMap.top, - rcDiffMap.right - rcDiffMap.left, rcDiffMap.bottom - rcDiffMap.top, SWP_NOZORDER); + m_bInSync = false; + } + void RedrawDiffMap() + { InvalidateRect(GetDlgItem(m_hWnd, IDC_DIFFMAP), NULL, TRUE); - - m_bInSync = false; } void SetWebDiffWindow(IWebDiffWindow *pWebDiffWindow) { m_pWebDiffWindow = pWebDiffWindow; - m_pWebDiffWindow->AddEventListener(OnEvent, this); + m_pWebDiffWindow->AddEventListener(this); } void Translate(TranslateCallback translateCallback) override @@ -128,54 +106,104 @@ class CWebToolWindow : public IWebToolWindow int controlId; }; static const StringIdControlId stringIdControlId[] = { - {IDS_DIFF_GROUP, IDC_DIFF_GROUP}, - {IDS_DIFF_HIGHLIGHT, IDC_DIFF_HIGHLIGHT}, - {IDS_DIFF_BLINK, IDC_DIFF_BLINK}, - {IDS_DIFF_BLOCKSIZE, IDC_DIFF_BLOCKSIZE}, - {IDS_DIFF_BLOCKALPHA, IDC_DIFF_BLOCKALPHA}, - {IDS_DIFF_CDTHRESHOLD, IDC_DIFF_CDTHRESHOLD}, - {IDS_DIFF_INSERTION_DELETION_DETECTION, IDC_DIFF_INSERTION_DELETION_DETECTION}, - {IDS_OVERLAY_GROUP, IDC_OVERLAY_GROUP}, - {IDS_OVERLAY_ALPHA, IDC_OVERLAY_ALPHA}, - {IDS_ZOOM, IDC_ZOOM}, - {IDS_PAGE, IDC_PAGE}, + {IDS_COMPARE, IDC_COMPARE}, + {IDS_ZOOM, IDC_ZOOM_LABEL}, + {IDS_SYNC_EVENTS, IDC_SYNC_EVENTS}, + {IDS_SHOWDIFFERENCES, IDC_SHOWDIFFERENCES}, }; for (auto& e: stringIdControlId) ::SetDlgItemText(m_hWnd, e.controlId, translateString(e.stringId).c_str()); - // translate ComboBox list + static const StringIdControlId stringIdControlId1[] = { + {IDS_WEB_COMPARE_SCREENSHOTS, ID_WEB_COMPARE_SCREENSHOTS}, + {IDS_WEB_COMPARE_FULLSIZE_SCREENSHOTS, ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS}, + {IDS_WEB_COMPARE_HTMLS, ID_WEB_COMPARE_HTMLS}, + {IDS_WEB_COMPARE_TEXTS, ID_WEB_COMPARE_TEXTS}, + {IDS_WEB_COMPARE_RESOURCETREES, ID_WEB_COMPARE_RESOURCETREES}, + }; + for (auto& e : stringIdControlId1) + { + std::wstring str = translateString(e.stringId); + MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; + mii.fMask = MIIM_STRING; + mii.dwTypeData = const_cast(str.c_str()); + ::SetMenuItemInfo(m_hComparePopup, e.controlId, FALSE, &mii); + } static const StringIdControlId stringIdControlId2[] = { - {IDS_DIFF_INSERTION_DELETION_DETECTION_NONE, IDC_DIFF_INSERTION_DELETION_DETECTION_MODE}, - {IDS_OVERLAY_MODE_NONE, IDC_OVERLAY_MODE}, + {IDS_WEB_SYNC_ENABLED, ID_WEB_SYNC_ENABLED}, + {IDS_WEB_SYNC_SCROLL, ID_WEB_SYNC_SCROLL}, + {IDS_WEB_SYNC_CLICK, ID_WEB_SYNC_CLICK}, + {IDS_WEB_SYNC_INPUT, ID_WEB_SYNC_INPUT}, + {IDS_WEB_SYNC_GOBACKFORWARD, ID_WEB_SYNC_GOBACKFORWARD}, }; - for (auto& e: stringIdControlId2) + for (auto& e : stringIdControlId2) { - int cursel = static_cast(::SendDlgItemMessage(m_hWnd, e.controlId, CB_GETCURSEL, 0, 0)); - int count = static_cast(::SendDlgItemMessage(m_hWnd, e.controlId, CB_GETCOUNT, 0, 0)); - ::SendDlgItemMessage(m_hWnd, e.controlId, CB_RESETCONTENT, 0, 0); - for (int i = 0; i < count; ++i) - ::SendDlgItemMessage(m_hWnd, e.controlId, CB_ADDSTRING, 0, reinterpret_cast(translateString(e.stringId + i).c_str())); - ::SendDlgItemMessage(m_hWnd, e.controlId, CB_SETCURSEL, static_cast(cursel), 0); + std::wstring str = translateString(e.stringId); + MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; + mii.fMask = MIIM_STRING; + mii.dwTypeData = const_cast(str.c_str()); + ::SetMenuItemInfo(m_hEventSyncPopup, e.controlId, FALSE, &mii); } } private: BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { - SendDlgItemMessage(hwnd, IDC_DIFF_BLOCKSIZE_SLIDER, TBM_SETRANGE, TRUE, MAKELPARAM(1, 64)); - SendDlgItemMessage(hwnd, IDC_DIFF_BLOCKALPHA_SLIDER, TBM_SETRANGE, TRUE, MAKELPARAM(0, 100)); - SendDlgItemMessage(hwnd, IDC_OVERLAY_ALPHA_SLIDER, TBM_SETRANGE, TRUE, MAKELPARAM(0, 100)); - SendDlgItemMessage(hwnd, IDC_ZOOM_SLIDER, TBM_SETRANGE, TRUE, MAKELPARAM(-7, 56)); - SendDlgItemMessage(hwnd, IDC_DIFF_INSERTION_DELETION_DETECTION_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("None"))); - SendDlgItemMessage(hwnd, IDC_DIFF_INSERTION_DELETION_DETECTION_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("Vertical"))); - SendDlgItemMessage(hwnd, IDC_DIFF_INSERTION_DELETION_DETECTION_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("Horizontal"))); - SendDlgItemMessage(hwnd, IDC_OVERLAY_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("None"))); - SendDlgItemMessage(hwnd, IDC_OVERLAY_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("XOR"))); - SendDlgItemMessage(hwnd, IDC_OVERLAY_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("Alpha Blend"))); - SendDlgItemMessage(hwnd, IDC_OVERLAY_MODE, CB_ADDSTRING, 0, (LPARAM)(_T("Alpha Animation"))); + HWND hwndZoom = GetDlgItem(hwnd, IDC_ZOOM); + for (const auto v : + {L"25%", L"33.3%", L"50%", L"66.7%", L"75%", L"80%", L"90%", L"100%", + L"110%", L"125%", L"150%", L"175%", L"200%", L"250%", L"300%"}) + ComboBox_AddString(hwndZoom, v); return TRUE; } + void ShowComparePopupMenu() + { + RECT rc; + GetWindowRect(GetDlgItem(m_hWnd, IDC_COMPARE), &rc); + POINT point{ rc.left, rc.bottom }; + HMENU hSubMenu = GetSubMenu(m_hComparePopup, 0); + TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, 0, m_hWnd, nullptr); + } + + void ShowSyncEventsPopupMenu() + { + RECT rc; + GetWindowRect(GetDlgItem(m_hWnd, IDC_SYNC_EVENTS), &rc); + POINT point{ rc.left, rc.bottom }; + HMENU hSubMenu = GetSubMenu(m_hEventSyncPopup, 0); + CheckMenuItem(hSubMenu, ID_WEB_SYNC_ENABLED, m_pWebDiffWindow->GetSyncEvents() ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hSubMenu, ID_WEB_SYNC_SCROLL, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_SCROLL) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hSubMenu, ID_WEB_SYNC_CLICK, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_CLICK) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hSubMenu, ID_WEB_SYNC_INPUT, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_INPUT) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hSubMenu, ID_WEB_SYNC_GOBACKFORWARD, m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_GOBACKFORWARD) ? MF_CHECKED : MF_UNCHECKED); + TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, 0, m_hWnd, nullptr); + } + + void OnClickDiffMap(HWND hwndDiffMap) + { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hwndDiffMap, &pt); + RECT rc; + GetClientRect(hwndDiffMap, &rc); + const int paneCount = m_pWebDiffWindow->GetPaneCount(); + + auto [scaleX, scaleY] = CalcScalingFactor(rc); + for (int pane = 0; pane < paneCount; ++pane) + { + RECT rcContainer = GetContainerRect(pane, 0, rc, scaleX, scaleY); + if (pt.x < rcContainer.right) + { + auto visibleArea = static_cast(m_pWebDiffWindow)->GetVisibleAreaRect(pane); + const float scrollX = std::clamp(static_cast((pt.x - rcContainer.left) / scaleX - visibleArea.width / 2), 0.0f, FLT_MAX); + const float scrollY = std::clamp(static_cast((pt.y - rcContainer.top) / scaleY - visibleArea.height / 2), 0.0f, FLT_MAX); + static_cast(m_pWebDiffWindow)->ScrollTo(pane, scrollX, scrollY); + break; + } + } + } + void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { if (m_bInSync) @@ -183,93 +211,126 @@ class CWebToolWindow : public IWebToolWindow switch (id) { - case IDC_DIFF_HIGHLIGHT: + case IDC_SHOWDIFFERENCES: if (codeNotify == BN_CLICKED) m_pWebDiffWindow->SetShowDifferences(Button_GetCheck(hwndCtl) == BST_CHECKED); break; - case IDC_DIFF_BLINK: + case IDC_FITTOWINDOW: if (codeNotify == BN_CLICKED) - m_pWebDiffWindow->SetBlinkDifferences(Button_GetCheck(hwndCtl) == BST_CHECKED); + { + m_pWebDiffWindow->SetFitToWindow(Button_GetCheck(hwndCtl) == BST_CHECKED); + Sync(); + } break; - case IDC_DIFF_INSERTION_DELETION_DETECTION_MODE: - if (codeNotify == CBN_SELCHANGE) - m_pWebDiffWindow->SetInsertionDeletionDetectionMode(static_cast(ComboBox_GetCurSel(hwndCtl))); + case IDC_WIDTH: + if (codeNotify == EN_CHANGE) + m_pWebDiffWindow->SetSize( + { static_cast(GetDlgItemInt(m_hWnd, IDC_WIDTH, nullptr, false)), + m_pWebDiffWindow->GetSize().cy }); break; - case IDC_OVERLAY_MODE: - if (codeNotify == CBN_SELCHANGE) - m_pWebDiffWindow->SetOverlayMode(static_cast(ComboBox_GetCurSel(hwndCtl))); + case IDC_HEIGHT: + if (codeNotify == EN_CHANGE) + m_pWebDiffWindow->SetSize( + { m_pWebDiffWindow->GetSize().cx, + static_cast(GetDlgItemInt(m_hWnd, IDC_HEIGHT, nullptr, false)) }); break; - case IDC_PAGE_EDIT: + case IDC_ZOOM: + if (codeNotify == CBN_EDITCHANGE) + { + wchar_t zoom[256]{}; + GetDlgItemText(m_hWnd, IDC_ZOOM, zoom, sizeof(zoom)/sizeof(zoom[0])); + float zoomf = wcstof(zoom, nullptr); + m_pWebDiffWindow->SetZoom(zoomf / 100.0f); + } + else if (codeNotify == CBN_SELCHANGE) + { + HWND hZoom = GetDlgItem(m_hWnd, IDC_ZOOM); + int cursel = ComboBox_GetCurSel(hZoom); + wchar_t zoom[256]{}; + ComboBox_GetLBText(hZoom, cursel, zoom); + float zoomf = wcstof(zoom, nullptr); + m_pWebDiffWindow->SetZoom(zoomf / 100.0f); + } + break; + case IDC_USERAGENT: if (codeNotify == EN_CHANGE) { - int page = static_cast(SendDlgItemMessage(hwnd, IDC_PAGE_SPIN, UDM_GETPOS, 0, 0)); - m_pWebDiffWindow->SetCurrentPageAll(page - 1); + wchar_t ua[256]; + GetDlgItemText(m_hWnd, IDC_USERAGENT, ua, sizeof(ua)/sizeof(ua[0])); + m_pWebDiffWindow->SetUserAgent(ua); } break; + case IDC_COMPARE: + if (codeNotify == BN_CLICKED) + m_pWebDiffWindow->Recompare(nullptr); + break; + case IDC_SYNC_EVENTS: + if (codeNotify == BN_CLICKED) + ShowSyncEventsPopupMenu(); + break; + case ID_WEB_SYNC_ENABLED: + m_pWebDiffWindow->SetSyncEvents(!m_pWebDiffWindow->GetSyncEvents()); + break; + case ID_WEB_SYNC_SCROLL: + m_pWebDiffWindow->SetSyncEventFlag(IWebDiffWindow::EVENT_SCROLL, !m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_SCROLL)); + break; + case ID_WEB_SYNC_CLICK: + m_pWebDiffWindow->SetSyncEventFlag(IWebDiffWindow::EVENT_CLICK, !m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_CLICK)); + break; + case ID_WEB_SYNC_INPUT: + m_pWebDiffWindow->SetSyncEventFlag(IWebDiffWindow::EVENT_INPUT, !m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_INPUT)); + break; + case ID_WEB_SYNC_GOBACKFORWARD: + m_pWebDiffWindow->SetSyncEventFlag(IWebDiffWindow::EVENT_GOBACKFORWARD, !m_pWebDiffWindow->GetSyncEventFlag(IWebDiffWindow::EVENT_GOBACKFORWARD)); + break; + case ID_WEB_COMPARE_SCREENSHOTS: + m_pWebDiffWindow->RaiseEvent({ WebDiffEvent::CompareScreenshotsSelected, -1 }); + break; + case ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS: + m_pWebDiffWindow->RaiseEvent({ WebDiffEvent::CompareFullsizeScreenshotsSelected, -1 }); + break; + case ID_WEB_COMPARE_HTMLS: + m_pWebDiffWindow->RaiseEvent({ WebDiffEvent::CompareHTMLsSelected, -1 }); + break; + case ID_WEB_COMPARE_TEXTS: + m_pWebDiffWindow->RaiseEvent({ WebDiffEvent::CompareTextsSelected, -1 }); + break; + case ID_WEB_COMPARE_RESOURCETREES: + m_pWebDiffWindow->RaiseEvent({ WebDiffEvent::CompareResourceTreesSelected, -1 }); + break; case IDC_DIFFMAP: if (codeNotify == STN_CLICKED) { - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hwndCtl, &pt); - RECT rc; - GetClientRect(hwndCtl, &rc); - CWebDiffWindow *pWebDiffWindow = static_cast(m_pWebDiffWindow); - pWebDiffWindow->ScrollTo( - pt.x * pWebDiffWindow->GetDiffImageWidth() / rc.right, - pt.y * pWebDiffWindow->GetDiffImageHeight() / rc.bottom, - true); + OnClickDiffMap(hwndCtl); } break; } } - void OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos) + void OnNotify(HWND hwnd, WPARAM idControl, NMHDR *pNMHDR) { - int val = static_cast(SendMessage(hwndCtl, TBM_GETPOS, 0, 0)); - switch (GetDlgCtrlID(hwndCtl)) + if (pNMHDR->code == BCN_DROPDOWN) { - case IDC_DIFF_BLOCKALPHA_SLIDER: - m_pWebDiffWindow->SetDiffColorAlpha(val / 100.0); - break; - case IDC_DIFF_BLOCKSIZE_SLIDER: - m_pWebDiffWindow->SetDiffBlockSize(val); - break; - case IDC_DIFF_CDTHRESHOLD_SLIDER: - m_pWebDiffWindow->SetColorDistanceThreshold(val); - break; - case IDC_OVERLAY_ALPHA_SLIDER: - m_pWebDiffWindow->SetOverlayAlpha(val / 100.0); - break; - case IDC_ZOOM_SLIDER: - m_pWebDiffWindow->SetZoom(1.0 + val * 0.125); - break; + switch (pNMHDR->idFrom) + { + case IDC_COMPARE: + ShowComparePopupMenu(); + break; + case IDC_SYNC_EVENTS: + ShowSyncEventsPopupMenu(); + break; + } } - Sync(); } void OnSize(HWND hwnd, UINT nType, int cx, int cy) { static const int nIDs[] = { - IDC_DIFF_GROUP, - IDC_DIFF_HIGHLIGHT, - IDC_DIFF_BLINK, - IDC_OVERLAY_GROUP, - IDC_VIEW_GROUP, - IDC_DIFF_BLOCKSIZE, - IDC_DIFF_BLOCKALPHA, - IDC_DIFF_BLOCKALPHA_SLIDER, - IDC_DIFF_BLOCKSIZE_SLIDER, - IDC_DIFF_CDTHRESHOLD, - IDC_DIFF_CDTHRESHOLD_SLIDER, - IDC_DIFF_INSERTION_DELETION_DETECTION, - IDC_DIFF_INSERTION_DELETION_DETECTION_MODE, - IDC_OVERLAY_ALPHA, - IDC_OVERLAY_ALPHA_SLIDER, - IDC_OVERLAY_MODE, + IDC_COMPARE, IDC_ZOOM, - IDC_ZOOM_SLIDER, - IDC_PAGE, + IDC_USERAGENT, + IDC_SYNC_EVENTS, + IDC_SHOWDIFFERENCES, }; HDWP hdwp = BeginDeferWindowPos(static_cast(std::size(nIDs))); RECT rc; @@ -281,77 +342,191 @@ class CWebToolWindow : public IWebToolWindow GetWindowRect(hwndCtrl, &rcCtrl); POINT pt = { rcCtrl.left, rcCtrl.top }; ScreenToClient(m_hWnd, &pt); - DeferWindowPos(hdwp, hwndCtrl, nullptr, pt.x, pt.y, rc.right - pt.x * 2, rcCtrl.bottom - rcCtrl.top, SWP_NOMOVE | SWP_NOZORDER); + DeferWindowPos(hdwp, hwndCtrl, nullptr, pt.x, pt.y, rc.right - pt.x - 2, rcCtrl.bottom - rcCtrl.top, SWP_NOMOVE | SWP_NOZORDER); } + RECT rcWidth, rcHeight, rcBy; + HWND hwndWidth = GetDlgItem(m_hWnd, IDC_WIDTH); + HWND hwndBy = GetDlgItem(m_hWnd, IDC_BY); + HWND hwndHeight = GetDlgItem(m_hWnd, IDC_HEIGHT); + GetWindowRect(hwndWidth, &rcWidth); + GetWindowRect(hwndBy, &rcBy); + GetWindowRect(hwndHeight, &rcHeight); + POINT ptWidth = { rcWidth.left, rcWidth.top }; + ScreenToClient(m_hWnd, &ptWidth); + int wby = rcBy.right - rcBy.left; + int w = (rc.right - 2 - ptWidth.x - wby - 4) / 2; + int h = rcWidth.bottom - rcWidth.top; + DeferWindowPos(hdwp, hwndWidth, nullptr, ptWidth.x, ptWidth.y, w, h, SWP_NOMOVE | SWP_NOZORDER); + DeferWindowPos(hdwp, hwndBy, nullptr, ptWidth.x + w + 2, ptWidth.y, rcBy.right - rcBy.left, h, SWP_NOZORDER); + DeferWindowPos(hdwp, hwndHeight, nullptr, ptWidth.x + w + 2 + wby + 2 , ptWidth.y, w, h, SWP_NOZORDER); + + RECT rcTmp; + HWND hwndDiffMap = GetDlgItem(m_hWnd, IDC_DIFFMAP); + GetWindowRect(hwndDiffMap, &rcTmp); + POINT pt = { rcTmp.left, rcTmp.top }; + ScreenToClient(m_hWnd, &pt); + const int margin = pt.x; + DeferWindowPos(hdwp, hwndDiffMap, nullptr, + pt.x, pt.y, rc.right - rc.left - margin * 2, rc.bottom - rc.top - pt.y - margin, SWP_NOZORDER); + InvalidateRect(hwndDiffMap, nullptr, TRUE); EndDeferWindowPos(hdwp); - static const int nID2s[] = { - IDC_DIFF_BLOCKSIZE_STATIC, - IDC_DIFF_BLOCKALPHA_STATIC, - IDC_DIFF_CDTHRESHOLD_STATIC, - IDC_OVERLAY_ALPHA_STATIC, - IDC_ZOOM_STATIC, - IDC_PAGE_SPIN, - }; - hdwp = BeginDeferWindowPos(static_cast(std::size(nID2s))); - RECT rcSlider; - GetWindowRect(GetDlgItem(m_hWnd, IDC_DIFF_BLOCKALPHA_SLIDER), &rcSlider); - for (int id: nID2s) + Sync(); + } + + void FillSolidRect(HDC hdc, const RECT& rc, COLORREF clr) + { + SetBkColor(hdc, clr); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, nullptr, 0, nullptr); + } + + void DrawTransparentRectangle(HDC hdc, int left, int top, int right, int bottom, COLORREF clr, BYTE alpha) { + int width = right - left; + int height = bottom - top; + HDC hdcMem = CreateCompatibleDC(hdc); + HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height); + SelectObject(hdcMem, hBitmap); + + RECT rect = { 0, 0, width, height }; + FillSolidRect(hdcMem, rect, clr); + + BLENDFUNCTION blendFunc{}; + blendFunc.BlendOp = AC_SRC_OVER; + blendFunc.SourceConstantAlpha = alpha; + + AlphaBlend(hdc, left, top, width, height, hdcMem, 0, 0, width, height, blendFunc); + + DeleteDC(hdcMem); + DeleteObject(hBitmap); + } + + std::pair CalcScalingFactor(const RECT& rc) const + { + float maxPaneWidth = 0, maxPaneHeight = 0; + const int width = rc.right - rc.left; + const int height = rc.bottom - rc.top; + const int paneCount = m_pWebDiffWindow->GetPaneCount(); + for (int pane = 0; pane < paneCount; ++pane) { - RECT rcCtrl; - HWND hwndCtrl = GetDlgItem(m_hWnd, id); - GetWindowRect(hwndCtrl, &rcCtrl); - POINT pt = { rcSlider.right - (rcCtrl.right - rcCtrl.left), rcCtrl.top }; - ScreenToClient(m_hWnd, &pt); - DeferWindowPos(hdwp, hwndCtrl, nullptr, pt.x, pt.y, rcCtrl.right - rcCtrl.left, rcCtrl.bottom - rcCtrl.top, SWP_NOSIZE | SWP_NOZORDER); + if (!m_containerRects[pane].empty()) + { + if (maxPaneWidth < m_containerRects[pane][0].scrollWidth) + maxPaneWidth = m_containerRects[pane][0].scrollWidth; + if (maxPaneHeight < m_containerRects[pane][0].scrollHeight) + maxPaneHeight = m_containerRects[pane][0].scrollHeight; + } } - EndDeferWindowPos(hdwp); + double scaleX = static_cast(width - MARGIN * (paneCount * 2)) / (maxPaneWidth * paneCount); + double scaleY = static_cast(height - MARGIN * 2) / maxPaneHeight; + return { scaleX, scaleY }; + } - static const int nID3s[] = { - IDC_PAGE_EDIT, - }; - hdwp = BeginDeferWindowPos(static_cast(std::size(nID3s))); - RECT rcSpin; - GetWindowRect(GetDlgItem(m_hWnd, IDC_PAGE_SPIN), &rcSpin); - for (int id: nID3s) + RECT GetContainerRect(int pane, int i, const RECT& rcDiffMap, double scaleX, double scaleY) const + { + RECT rc{}; + if (m_containerRects[pane].empty()) + return rc; + const int left = rcDiffMap.left; + const int top = rcDiffMap.top; + const int width = rcDiffMap.right - rcDiffMap.left; + const int height = rcDiffMap.bottom - rcDiffMap.top; + const int paneCount = m_pWebDiffWindow->GetPaneCount(); + if (i == 0) { - RECT rcCtrl; - HWND hwndCtrl = GetDlgItem(m_hWnd, id); - GetWindowRect(hwndCtrl, &rcCtrl); - POINT pt = { rcSpin.left - (rcCtrl.right - rcCtrl.left), rcCtrl.top }; - ScreenToClient(m_hWnd, &pt); - DeferWindowPos(hdwp, hwndCtrl, nullptr, pt.x, pt.y, rcCtrl.right - rcCtrl.left, rcCtrl.bottom - rcCtrl.top, SWP_NOSIZE | SWP_NOZORDER); + rc.left = left + MARGIN + width * pane / paneCount; + rc.top = top + MARGIN; + rc.right = rc.left + static_cast(m_containerRects[pane][i].scrollWidth * scaleX); + rc.bottom = rc.top + static_cast(m_containerRects[pane][i].scrollHeight * scaleY); } - EndDeferWindowPos(hdwp); + else + { + rc.left = left + MARGIN + width * pane / paneCount + static_cast(m_containerRects[pane][i].left * scaleX); + rc.top = top + MARGIN + static_cast(m_containerRects[pane][i].top * scaleY); + rc.right = rc.left + static_cast(m_containerRects[pane][i].width * scaleX); + rc.bottom = rc.top + static_cast(m_containerRects[pane][i].height * scaleY); + } + return rc; + }; - Sync(); + void DrawDiffMap(HDC hdcMem, const RECT& rc) + { + FillSolidRect(hdcMem, { 0, 0, rc.right, rc.bottom }, RGB(255, 255, 255)); + + const int paneCount = m_pWebDiffWindow->GetPaneCount(); + if (!m_pWebDiffWindow || paneCount == 0) + return; + for (int pane = 0; pane < paneCount; ++pane) + { + m_containerRects[pane] = static_cast(m_pWebDiffWindow)->GetContainerRectArray(pane); + m_rects[pane] = static_cast(m_pWebDiffWindow)->GetDiffRectArray(pane); + } + for (int pane = 0; pane < paneCount; ++pane) + { + if (m_containerRects[pane].size() == 0) + return; + } + + IWebDiffWindow::ColorSettings colors; + m_pWebDiffWindow->GetDiffColorSettings(colors); + auto [scaleX, scaleY] = CalcScalingFactor(rc); + + const int curDiff = m_pWebDiffWindow->GetCurrentDiffIndex(); + for (int pane = 0; pane < paneCount; ++pane) + { + if (m_rects[pane].size() > 0) + { + const RECT rcContainer = GetContainerRect(pane, 0, rc, scaleX, scaleY); + for (int i = 0; i < m_rects[pane].size(); ++i) + { + if (m_rects[pane][i].left != -99999.9f || m_rects[pane][i].top != -99999.9f) + { + const int diffLeft = rcContainer.left + static_cast(m_rects[pane][i].left * scaleX); + const int diffTop = rcContainer.top + static_cast(m_rects[pane][i].top * scaleY); + const int diffRight = rcContainer.left + static_cast((m_rects[pane][i].left + m_rects[pane][i].width) * scaleX); + const int diffBottom = rcContainer.top + static_cast((m_rects[pane][i].top + m_rects[pane][i].height) * scaleY); + const RECT rc = { diffLeft, diffTop, diffRight, diffBottom }; + FillSolidRect(hdcMem, rc, (curDiff == m_rects[pane][i].id) ? colors.clrSelDiff : colors.clrDiff); + } + } + } + HBRUSH hOldBrush = SelectBrush(hdcMem, GetStockBrush(NULL_BRUSH)); + for (int i = 0; i < m_containerRects[pane].size(); ++i) + { + const RECT rcContainer = GetContainerRect(pane, i, rc, scaleX, scaleY); + Rectangle(hdcMem, rcContainer.left, rcContainer.top, rcContainer.right, rcContainer.bottom); + } + + RECT rcContainer = GetContainerRect(pane, 0, rc, scaleX, scaleY); + auto visibleArea = static_cast(m_pWebDiffWindow)->GetVisibleAreaRect(pane); + + rcContainer.left += static_cast(visibleArea.left * scaleX); + rcContainer.right = static_cast(rcContainer.left + visibleArea.width * scaleX); + rcContainer.top += static_cast(visibleArea.top * scaleY); + rcContainer.bottom = static_cast(rcContainer.top + visibleArea.height * scaleY); + DrawTransparentRectangle(hdcMem, + rcContainer.left, rcContainer.top, rcContainer.right, rcContainer.bottom, + RGB(96, 96, 255), 64); + + SelectBrush(hdcMem, hOldBrush); + } } void OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT *pDrawItem) { - if (!m_pWebDiffWindow || m_pWebDiffWindow->GetPaneCount() == 0) - return; RECT rc; GetClientRect(pDrawItem->hwndItem, &rc); - Image *pImage = static_cast(m_pWebDiffWindow)->GetDiffMapImage(rc.right - rc.left, rc.bottom - rc.top); - RGBQUAD bkColor = { 0xff, 0xff, 0xff, 0xff }; - pImage->getFipImage()->drawEx(pDrawItem->hDC, rc, false, &bkColor); - HWND hwndLeftPane = m_pWebDiffWindow->GetPaneHWND(0); - SCROLLINFO sih{ sizeof(sih), SIF_POS | SIF_PAGE | SIF_RANGE }; - GetScrollInfo(hwndLeftPane, SB_HORZ, &sih); - SCROLLINFO siv{ sizeof(siv), SIF_POS | SIF_PAGE | SIF_RANGE }; - GetScrollInfo(hwndLeftPane, SB_VERT, &siv); + HDC hdcMem = CreateCompatibleDC(pDrawItem->hDC); + HBITMAP hBitmap = CreateCompatibleBitmap(pDrawItem->hDC, rc.right, rc.bottom); + HBITMAP hOldBitmap = SelectBitmap(hdcMem, hBitmap); - if (static_cast(sih.nPage) < sih.nMax || static_cast(siv.nPage) < siv.nMax) - { - RECT rcFrame; - rcFrame.left = rc.left + (rc.right - rc.left) * sih.nPos / sih.nMax; - rcFrame.right = rcFrame.left + (rc.right - rc.left) * sih.nPage / sih.nMax; - rcFrame.top = rc.top + (rc.bottom - rc.top) * siv.nPos / siv.nMax; - rcFrame.bottom = rcFrame.top + (rc.bottom - rc.top) * siv.nPage / siv.nMax; - FrameRect(pDrawItem->hDC, &rcFrame, reinterpret_cast(GetStockObject(BLACK_BRUSH))); - } + DrawDiffMap(hdcMem, rc); + + BitBlt(pDrawItem->hDC, 0, 0, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY); + + SelectBitmap(hdcMem, hOldBitmap); + DeleteBitmap(hBitmap); + DeleteDC(hdcMem); } INT_PTR OnWndMsg(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) @@ -363,8 +538,8 @@ class CWebToolWindow : public IWebToolWindow case WM_COMMAND: HANDLE_WM_COMMAND(hwnd, wParam, lParam, OnCommand); break; - case WM_HSCROLL: - HANDLE_WM_HSCROLL(hwnd, wParam, lParam, OnHScroll); + case WM_NOTIFY: + HANDLE_WM_NOTIFY(hwnd, wParam, lParam, OnNotify); break; case WM_SIZE: HANDLE_WM_SIZE(hwnd, wParam, lParam, OnSize); @@ -387,24 +562,35 @@ class CWebToolWindow : public IWebToolWindow return FALSE; } - static void OnEvent(const IWebDiffWindow::Event& evt) + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; } + ULONG STDMETHODCALLTYPE AddRef(void) override { return ++m_nRef; } + ULONG STDMETHODCALLTYPE Release(void) override { if (--m_nRef == 0) { return 0; } return m_nRef; } + + HRESULT Invoke(const WebDiffEvent& event) { - switch (evt.eventType) + switch (event.type) { - case IWebDiffWindow::HSCROLL: - case IWebDiffWindow::VSCROLL: - case IWebDiffWindow::SIZE: - case IWebDiffWindow::MOUSEWHEEL: - case IWebDiffWindow::REFRESH: - case IWebDiffWindow::SCROLLTODIFF: - case IWebDiffWindow::OPEN: - reinterpret_cast(evt.userdata)->Sync(); + case WebDiffEvent::ZoomFactorChanged: + Sync(); + RedrawDiffMap(); + break; + case WebDiffEvent::WebMessageReceived: + case WebDiffEvent::FrameWebMessageReceived: + case WebDiffEvent::DiffSelected: + RedrawDiffMap(); break; } + return S_OK; } HWND m_hWnd; HINSTANCE m_hInstance; + HMENU m_hComparePopup; + HMENU m_hEventSyncPopup; IWebDiffWindow *m_pWebDiffWindow; + std::vector m_containerRects[3]; + std::vector m_rects[3]; bool m_bInSync; + int m_nRef; + }; diff --git a/src/WinWebDiffLib/dllmain.cpp b/src/WinWebDiffLib/WinWebDiffLib.cpp similarity index 55% rename from src/WinWebDiffLib/dllmain.cpp rename to src/WinWebDiffLib/WinWebDiffLib.cpp index effdc4d..28bb8f2 100644 --- a/src/WinWebDiffLib/dllmain.cpp +++ b/src/WinWebDiffLib/WinWebDiffLib.cpp @@ -1,6 +1,7 @@ -// dllmain.cpp : Defines the entry point for the DLL application. +// WinWebDiffLib.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "WebDiffWindow.hpp" +#include "WebToolWindow.hpp" extern "C" IWebDiffWindow * WinWebDiff_CreateWindow(HINSTANCE hInstance, HWND hWndParent, int nID) @@ -20,6 +21,24 @@ WinWebDiff_DestroyWindow(IWebDiffWindow * pWebDiffWindow) return true; } +extern "C" IWebToolWindow * +WinWebDiff_CreateToolWindow(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow * pWebDiffWindow) +{ + CWebToolWindow* pWebToolWindow = new CWebToolWindow(); + pWebToolWindow->Create(hInstance, hWndParent); + pWebToolWindow->SetWebDiffWindow(pWebDiffWindow); + return static_cast(pWebToolWindow); +} + +extern "C" bool +WinWebDiff_DestroyToolWindow(IWebToolWindow * pWebToolWindow) +{ + CWebToolWindow* pWebToolWindow2 = static_cast(pWebToolWindow); + pWebToolWindow2->Destroy(); + delete pWebToolWindow2; + return true; +} + BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { diff --git a/src/WinWebDiffLib/WinWebDiffLib.def b/src/WinWebDiffLib/WinWebDiffLib.def index dca66b5..1e25de0 100644 --- a/src/WinWebDiffLib/WinWebDiffLib.def +++ b/src/WinWebDiffLib/WinWebDiffLib.def @@ -1,3 +1,5 @@ EXPORTS WinWebDiff_CreateWindow WinWebDiff_DestroyWindow + WinWebDiff_CreateToolWindow + WinWebDiff_DestroyToolWindow diff --git a/src/WinWebDiffLib/WinWebDiffLib.h b/src/WinWebDiffLib/WinWebDiffLib.h index e4e29a2..2e450b2 100644 --- a/src/WinWebDiffLib/WinWebDiffLib.h +++ b/src/WinWebDiffLib/WinWebDiffLib.h @@ -6,7 +6,7 @@ struct WebDiffEvent { - enum EVENT_TYPE { ZoomFactorChanged, NewWindowRequested, WindowCloseRequested, NavigationStarting, FrameNavigationStarting, HistoryChanged, SourceChanged, DocumentTitleChanged, NavigationCompleted, FrameNavigationCompleted, WebMessageReceived, FrameWebMessageReceived, GoBacked, GoForwarded, TabChanged, HSCROLL, VSCROLL, CompareStateChanged }; + enum EVENT_TYPE { ZoomFactorChanged, NewWindowRequested, WindowCloseRequested, NavigationStarting, FrameNavigationStarting, HistoryChanged, SourceChanged, DocumentTitleChanged, NavigationCompleted, FrameNavigationCompleted, WebMessageReceived, FrameWebMessageReceived, GoBacked, GoForwarded, TabChanged, HSCROLL, VSCROLL, CompareStateChanged, CompareScreenshotsSelected, CompareFullsizeScreenshotsSelected, CompareHTMLsSelected, CompareTextsSelected, CompareResourceTreesSelected, DiffSelected }; EVENT_TYPE type; int pane; }; @@ -191,11 +191,21 @@ struct IWebDiffWindow virtual bool GetSyncEventFlag(EventType event) const = 0; virtual void SetSyncEventFlag(EventType event, bool flag) = 0; virtual CompareState GetCompareState() const = 0; + virtual void RaiseEvent(const WebDiffEvent& e) = 0; +}; + +struct IWebToolWindow +{ + using TranslateCallback = void(*)(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst); + virtual HWND GetHWND() const = 0; + virtual void Sync() = 0; + virtual void Translate(TranslateCallback translateCallback) = 0; }; extern "C" { IWebDiffWindow* WinWebDiff_CreateWindow(HINSTANCE hInstance, HWND hWndParent, int nID = 0); bool WinWebDiff_DestroyWindow(IWebDiffWindow* pWebDiffWindow); + IWebToolWindow * WinWebDiff_CreateToolWindow(HINSTANCE hInstance, HWND hWndParent, IWebDiffWindow *pWebDiffWindow); + bool WinWebDiff_DestroyToolWindow(IWebToolWindow *pWebToolWindow); }; - diff --git a/src/WinWebDiffLib/WinWebDiffLib.js b/src/WinWebDiffLib/WinWebDiffLib.js index 58f125b..2318f53 100644 Binary files a/src/WinWebDiffLib/WinWebDiffLib.js and b/src/WinWebDiffLib/WinWebDiffLib.js differ diff --git a/src/WinWebDiffLib/WinWebDiffLib.rc b/src/WinWebDiffLib/WinWebDiffLib.rc index 618629f..526819f 100644 --- a/src/WinWebDiffLib/WinWebDiffLib.rc +++ b/src/WinWebDiffLib/WinWebDiffLib.rc @@ -105,7 +105,7 @@ BEGIN VALUE "FileDescription", "WinWebDiffLib" VALUE "FileVersion", STRFILEVER VALUE "InternalName", "WinWebDiffLib.dll" - VALUE "LegalCopyright", "Copyright (C) 2022-2023 sdottaka@users.sourceforge.net" + VALUE "LegalCopyright", "Copyright (C) 2022-2024 sdottaka@users.sourceforge.net" VALUE "OriginalFilename", "WinWebDiffLib.dll" VALUE "ProductName", "WinWebDiffLib" VALUE "ProductVersion", STRPRODUCTVER @@ -127,22 +127,83 @@ IDD_DIALOGBAR DIALOGEX 0, 0, 74, 273 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg", 400, 0, 0x0 BEGIN - CONTROL "",IDC_DIFFMAP,"Static",SS_OWNERDRAW | SS_NOTIFY,2,54,70,214 - EDITTEXT IDC_WIDTH,6,18,24,15,ES_AUTOHSCROLL - LTEXT "x",IDC_STATIC,33,22,8,8 - EDITTEXT IDC_HEIGHT,42,18,24,15,ES_AUTOHSCROLL - COMBOBOX IDC_ZOOM,6,36,36,18,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Menu",IDC_MENU,6,0,61,12 + CONTROL "Compare",IDC_COMPARE,"Button",BS_SPLITBUTTON | WS_TABSTOP,2,2,68,12 + CONTROL "",IDC_FITTOWINDOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,2,14,10,14 + EDITTEXT IDC_WIDTH,16,16,24,12,ES_AUTOHSCROLL | WS_TABSTOP + LTEXT "x",IDC_BY,40,20,6,8 + EDITTEXT IDC_HEIGHT,46,16,24,12,ES_AUTOHSCROLL | WS_TABSTOP + LTEXT "Zoom:",IDC_ZOOM_LABEL,2,30,34,10 + COMBOBOX IDC_ZOOM,37,30,34,87,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + LTEXT "UA:",IDC_STATIC,2,44,16,10 + EDITTEXT IDC_USERAGENT,18,44,52,14,ES_AUTOHSCROLL | WS_TABSTOP + CONTROL "&Event Sync",IDC_SYNC_EVENTS,"Button",BS_SPLITBUTTON | WS_TABSTOP,2,60,68,12 + CONTROL "View &Differences",IDC_SHOWDIFFERENCES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,2,74,70,8 + CONTROL "",IDC_DIFFMAP,"Static",SS_OWNERDRAW | SS_NOTIFY,2,84,70,184 END ///////////////////////////////////////////////////////////////////////////// // -// SCRIPT +// RCDATA // IDR_SCRIPT RCDATA "WinWebDiffLib.js" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_POPUP_WEBPAGE_COMPARE MENU +BEGIN + POPUP "_POPUP_" + BEGIN + MENUITEM "&Screenshots", ID_WEB_COMPARE_SCREENSHOTS + MENUITEM "&Full Size Screenshots", ID_WEB_COMPARE_FULLSIZE_SCREENSHOTS + MENUITEM "&HTMLs", ID_WEB_COMPARE_HTMLS + MENUITEM "&Texts", ID_WEB_COMPARE_TEXTS + MENUITEM "&Resource Trees", ID_WEB_COMPARE_RESOURCETREES + END +END + +IDR_POPUP_WEBPAGE_SYNC_EVENTS MENU +BEGIN + POPUP "_POPUP_" + BEGIN + MENUITEM "&Enabled", ID_WEB_SYNC_ENABLED + MENUITEM SEPARATOR + MENUITEM "&Scroll", ID_WEB_SYNC_SCROLL + MENUITEM "&Click", ID_WEB_SYNC_CLICK + MENUITEM "&Input", ID_WEB_SYNC_INPUT + MENUITEM "&GoBack/Forward", ID_WEB_SYNC_GOBACKFORWARD + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_COMPARE "Compare" + IDS_ZOOM "Zoom:" + IDS_SYNC_EVENTS "&Event Sync" + IDS_SHOWDIFFERENCES "View &Differences" + IDS_WEB_COMPARE_SCREENSHOTS "&Screenshots" + IDS_WEB_COMPARE_FULLSIZE_SCREENSHOTS "&Full Size Screenshots" + IDS_WEB_COMPARE_HTMLS "&HTMLs" + IDS_WEB_COMPARE_TEXTS "&Texts" + IDS_WEB_COMPARE_RESOURCETREES "&Resource Trees" + IDS_WEB_SYNC_ENABLED "&Enabled" + IDS_WEB_SYNC_SCROLL "&Scroll" + IDS_WEB_SYNC_CLICK "&Click" + IDS_WEB_SYNC_INPUT "&Input" + IDS_WEB_SYNC_GOBACKFORWARD "&GoBack/Forward" +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/src/WinWebDiffLib/WinWebDiffLib.vcxproj b/src/WinWebDiffLib/WinWebDiffLib.vcxproj index 9be8470..712d2de 100644 --- a/src/WinWebDiffLib/WinWebDiffLib.vcxproj +++ b/src/WinWebDiffLib/WinWebDiffLib.vcxproj @@ -266,17 +266,18 @@ + + - Create Create @@ -285,6 +286,7 @@ Create Create + diff --git a/src/WinWebDiffLib/WinWebDiffLib.vcxproj.filters b/src/WinWebDiffLib/WinWebDiffLib.vcxproj.filters index 662af0e..101ac64 100644 --- a/src/WinWebDiffLib/WinWebDiffLib.vcxproj.filters +++ b/src/WinWebDiffLib/WinWebDiffLib.vcxproj.filters @@ -45,12 +45,18 @@ Header Files + + Header Files + + + Header Files + - + Source Files - + Source Files