1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file ImgMergeFrm.cpp
10 * @brief Implementation file for CImgMergeFrame
15 #include "ImgMergeFrm.h"
20 #include "OptionsDef.h"
21 #include "OptionsMgr.h"
22 #include "OptionsDiffColors.h"
23 #include "OptionsCustomColors.h"
25 #include "PathContext.h"
27 #include "FileOrFolderSelect.h"
29 #include "SaveClosingDlg.h"
30 #include "SelectPluginDlg.h"
31 #include "FileLocation.h"
32 #include "Constants.h"
33 #include "DropHandler.h"
34 #include "Environment.h"
41 /////////////////////////////////////////////////////////////////////////////
44 IMPLEMENT_DYNCREATE(CImgMergeFrame, CMergeFrameCommon)
46 BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
47 //{{AFX_MSG_MAP(CImgMergeFrame)
54 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
55 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
57 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
58 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
59 ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
60 ON_COMMAND(ID_FILE_SAVE_MIDDLE, OnFileSaveMiddle)
61 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
62 ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
63 ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
64 ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS_MIDDLE, OnUpdateFileSaveAsMiddle)
65 ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
66 ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
67 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
68 ON_COMMAND(ID_FILE_LEFT_READONLY, OnFileReadOnlyLeft)
69 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateFileReadOnlyLeft)
70 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnFileReadOnlyMiddle)
71 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateFileReadOnlyMiddle)
72 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnFileReadOnlyRight)
73 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateFileReadOnlyRight)
74 ON_COMMAND(ID_RESCAN, OnFileReload)
75 ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnFileRecompareAs)
76 ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateFileRecompareAs)
78 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
79 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
80 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
81 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
82 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
83 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
84 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
85 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
86 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
87 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
88 ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
90 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
91 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
92 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
93 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
94 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
95 ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
96 ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
97 ON_COMMAND(ID_REFRESH, OnRefresh)
99 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
100 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
101 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
102 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
103 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
104 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
105 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
106 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
107 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
108 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
109 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
110 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
111 ON_COMMAND(ID_L2R, OnL2r)
112 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
113 ON_COMMAND(ID_R2L, OnR2l)
114 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
115 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
116 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
117 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
118 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
119 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
120 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
121 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
122 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
123 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
124 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
125 ON_COMMAND_RANGE(ID_COPY_TO_MIDDLE_L, ID_COPY_FROM_LEFT_R, OnCopyX2Y)
126 ON_UPDATE_COMMAND_UI_RANGE(ID_COPY_TO_MIDDLE_L, ID_COPY_FROM_LEFT_R, OnUpdateCopyFromXToY)
127 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
128 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
130 ON_COMMAND(ID_IMG_VIEWDIFFERENCES, OnImgViewDifferences)
131 ON_UPDATE_COMMAND_UI(ID_IMG_VIEWDIFFERENCES, OnUpdateImgViewDifferences)
132 ON_COMMAND_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnImgZoom)
133 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnUpdateImgZoom)
134 ON_COMMAND_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnImgOverlayMode)
135 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnUpdateImgOverlayMode)
136 ON_COMMAND_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnImgDraggingMode)
137 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnUpdateImgDraggingMode)
138 ON_COMMAND_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnImgDiffBlockSize)
139 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnUpdateImgDiffBlockSize)
140 ON_COMMAND_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnImgThreshold)
141 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnUpdateImgThreshold)
142 ON_COMMAND_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnImgInsertionDeletionDetectionMode)
143 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnUpdateImgInsertionDeletionDetectionMode)
144 ON_COMMAND(ID_IMG_PREVPAGE, OnImgPrevPage)
145 ON_UPDATE_COMMAND_UI(ID_IMG_PREVPAGE, OnUpdateImgPrevPage)
146 ON_COMMAND(ID_IMG_NEXTPAGE, OnImgNextPage)
147 ON_UPDATE_COMMAND_UI(ID_IMG_NEXTPAGE, OnUpdateImgNextPage)
148 ON_COMMAND(ID_IMG_CURPANE_ROTATE_RIGHT_90, OnImgCurPaneRotateRight90)
149 ON_COMMAND(ID_IMG_CURPANE_ROTATE_LEFT_90, OnImgCurPaneRotateLeft90)
150 ON_COMMAND(ID_IMG_CURPANE_FLIP_VERTICALLY, OnImgCurPaneFlipVertically)
151 ON_COMMAND(ID_IMG_CURPANE_FLIP_HORIZONTALLY, OnImgCurPaneFlipHorizontally)
152 ON_COMMAND(ID_IMG_CURPANE_PREVPAGE, OnImgCurPanePrevPage)
153 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_PREVPAGE, OnUpdateImgCurPanePrevPage)
154 ON_COMMAND(ID_IMG_CURPANE_NEXTPAGE, OnImgCurPaneNextPage)
155 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_NEXTPAGE, OnUpdateImgCurPaneNextPage)
156 ON_COMMAND(ID_IMG_USEBACKCOLOR, OnImgUseBackColor)
157 ON_UPDATE_COMMAND_UI(ID_IMG_USEBACKCOLOR, OnUpdateImgUseBackColor)
158 ON_COMMAND_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnImgVectorImageScaling)
159 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnUpdateImgVectorImageScaling)
160 ON_COMMAND(ID_IMG_COMPARE_EXTRACTED_TEXT, OnImgCompareExtractedText)
162 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
164 ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
165 ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
167 ON_COMMAND_RANGE(ID_NEXT_PANE, ID_PREV_PANE, OnWindowChangePane)
169 ON_COMMAND(ID_HELP, OnHelp)
171 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
175 CMenu CImgMergeFrame::menu;
177 /////////////////////////////////////////////////////////////////////////////
178 // CImgMergeFrame construction/destruction
180 CImgMergeFrame::CImgMergeFrame()
181 : CMergeFrameCommon(IDI_EQUALIMAGE, IDI_NOTEQUALIMAGE)
183 , m_bAutoMerged(false)
184 , m_pImgMergeWindow(nullptr)
185 , m_pImgToolWindow(nullptr)
186 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
192 CImgMergeFrame::~CImgMergeFrame()
194 GetMainFrame()->UnwatchDocuments(this);
196 if (m_pDirDoc != nullptr)
198 m_pDirDoc->MergeDocClosing(this);
202 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
203 if (hModule != nullptr)
205 bool (*pfnWinIMerge_DestroyWindow)(IImgMergeWindow *) =
206 (bool (*)(IImgMergeWindow *))GetProcAddress(hModule, "WinIMerge_DestroyWindow");
207 bool (*pfnWinIMerge_DestroyToolWindow)(IImgToolWindow *) =
208 (bool (*)(IImgToolWindow *))GetProcAddress(hModule, "WinIMerge_DestroyToolWindow");
209 if (pfnWinIMerge_DestroyWindow != nullptr && pfnWinIMerge_DestroyToolWindow != nullptr)
211 if (m_pImgMergeWindow != nullptr)
212 pfnWinIMerge_DestroyWindow(m_pImgMergeWindow);
213 if (m_pImgToolWindow != nullptr)
214 pfnWinIMerge_DestroyToolWindow(m_pImgToolWindow);
215 m_pImgMergeWindow = nullptr;
216 m_pImgToolWindow = nullptr;
221 bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent)
223 CWaitCursor waitstatus;
224 int nNormalBuffer = 0;
225 for (int pane = 0; pane < nFiles; ++pane)
227 m_filePaths.SetPath(pane, fileloc[pane].filepath, false);
228 m_bRO[pane] = bRO[pane];
229 m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
230 if (fileloc[pane].filepath.empty())
231 m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
234 m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
240 const tchar_t* lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
241 ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
243 if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
246 int nCmdShow = SW_SHOW;
247 if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
248 nCmdShow = SW_SHOWMAXIMIZED;
249 ShowWindow(nCmdShow);
250 BringToTop(nCmdShow);
252 GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
254 if (nNormalBuffer > 0)
257 UpdateDiffItem(m_pDirDoc);
259 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
260 m_pImgMergeWindow->FirstDiff();
262 GetMainFrame()->WatchDocuments(this);
267 void CImgMergeFrame::MoveOnLoad(int nPane, int)
271 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
272 if (nPane < 0 || nPane >= m_pImgMergeWindow->GetPaneCount())
276 m_pImgMergeWindow->SetActivePane(nPane);
279 void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
281 if (!PromptAndSaveIfNeeded(true))
284 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
285 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
287 m_filePaths[nBuffer] = path;
288 m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
289 m_strDesc[nBuffer].clear();
290 int nActivePane = m_pImgMergeWindow->GetActivePane();
293 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
295 m_fileInfo[pane].Update(m_filePaths[pane]);
297 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
298 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
301 MoveOnLoad(nActivePane);
303 UpdateHeaderPath(nBuffer);
304 UpdateLastCompareResult();
307 bool CImgMergeFrame::IsModified() const
309 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
310 if (m_pImgMergeWindow->IsModified(pane))
315 void CImgMergeFrame::DoAutoMerge(int dstPane)
317 int autoMergedCount = m_pImgMergeWindow->CopyDiff3Way(dstPane);
318 if (autoMergedCount > 0)
319 m_bAutoMerged = true;
321 // move to first conflict
322 m_pImgMergeWindow->FirstConflict();
325 strutils::format_string2(
326 _("The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"),
327 strutils::format(_T("%d"), autoMergedCount),
328 strutils::format(_T("%d"), m_pImgMergeWindow->GetConflictCount())).c_str(),
333 * @brief DirDoc gives us its identity just after it creates us
335 void CImgMergeFrame::SetDirDoc(CDirDoc * pDirDoc)
337 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
341 IMergeDoc::FileChange CImgMergeFrame::IsFileChangedOnDisk(int pane) const
344 if (!dfi.Update(m_filePaths[pane]))
345 return FileChange::Removed;
347 if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
348 tolerance = SmallTimeDiff; // From MainFrm.h
349 int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
350 if (timeDiff < 0) timeDiff = -timeDiff;
351 if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
352 return FileChange::Changed;
353 return FileChange::NoChange;
356 void CImgMergeFrame::CheckFileChanged(void)
358 if (!m_pImgMergeWindow)
360 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
362 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
364 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]);
365 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
375 * @brief Create a status bar to be associated with a heksedit control
377 void CImgMergeFrame::CreateImgWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
379 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
380 wndStatusBar.SetIndicators(0, 1);
381 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
382 wndStatusBar.SetParent(this);
383 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
386 void CImgMergeFrame::OnChildPaneEvent(const IImgMergeWindow::Event& evt)
388 if (evt.eventType == IImgMergeWindow::KEYDOWN)
390 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
395 ::SendMessage(pFrame->m_pImgMergeWindow->GetPaneHWND(evt.pane), WM_VSCROLL, evt.keycode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0);
401 if (::GetAsyncKeyState(VK_SHIFT) & 0x8000)
403 int nActivePane = pFrame->m_pImgMergeWindow->GetActivePane();
404 int m = (::GetAsyncKeyState(VK_CONTROL) & 0x8000) ? 8 : 1;
405 int dx = (-(evt.keycode == VK_LEFT) + (evt.keycode == VK_RIGHT)) * m;
406 int dy = (-(evt.keycode == VK_UP) + (evt.keycode == VK_DOWN)) * m;
407 pFrame->m_pImgMergeWindow->AddImageOffset(nActivePane, dx, dy);
412 else if (evt.eventType == IImgMergeWindow::CONTEXTMENU)
415 menuPopup.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMG_CTXT));
416 theApp.TranslateMenu(menuPopup.m_hMenu);
417 BCMenu* pPopup = (BCMenu *)menuPopup.GetSubMenu(0);
418 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
419 evt.x, evt.y, AfxGetMainWnd());
424 * @brief returns true if WinIMergeLib.dll is loadable
426 bool CImgMergeFrame::IsLoadable()
428 static HMODULE hModule;
429 if (hModule == nullptr)
431 hModule = LoadLibraryW(L"WinIMerge\\WinIMergeLib.dll");
432 if (hModule == nullptr)
439 * @brief Create the splitter, the filename bar, the status bar, and the two views
441 BOOL CImgMergeFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
442 CCreateContext* pContext)
447 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
448 if (hModule == nullptr)
451 IImgMergeWindow * (*pfnWinIMerge_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
452 (IImgMergeWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinIMerge_CreateWindow");
453 if (pfnWinIMerge_CreateWindow == nullptr ||
454 (m_pImgMergeWindow = pfnWinIMerge_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
456 FreeLibrary(hModule);
460 m_pImgMergeWindow->AddEventListener(OnChildPaneEvent, this);
464 if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
466 m_infoUnpacker.Initialize(false);
467 bResult = m_pImgMergeWindow->NewImages(m_filePaths.GetSize(), 1, 256, 256);
471 bResult = OpenImages();
474 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
476 m_fileInfo[pane].Update(m_filePaths[pane]);
478 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
479 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
482 // Merge frame has also a dockable bar at the very left
483 // This is not the client area, but we create it now because we want
484 // to use the CCreateContext
485 String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
486 if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
488 TRACE0("Failed to create LocationBar\n");
492 IImgToolWindow * (*pfnWinIMerge_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *) =
493 (IImgToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *pImgMergeWindow))GetProcAddress(hModule, "WinIMerge_CreateToolWindow");
494 if (pfnWinIMerge_CreateToolWindow == nullptr ||
495 (m_pImgToolWindow = pfnWinIMerge_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pImgMergeWindow)) == nullptr)
500 m_pImgToolWindow->Translate(TranslateLocationPane);
502 m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
507 void CImgMergeFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
509 swprintf_s(dst, dstbufsize, L"%s", tr("ImgMergeFrame|LocationPane", ucr::toUTF8(org)).c_str());
512 /////////////////////////////////////////////////////////////////////////////
513 // CImgMergeFrame message handlers
515 int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
517 if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
520 EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
522 CMergeFrameCommon::RemoveBarBorder();
524 // Merge frame has a header bar at top
525 if (!m_wndFilePathBar.Create(this))
527 TRACE0("Failed to create dialog bar\n");
528 return -1; // fail to create
531 m_wndFilePathBar.SetPaneCount(m_pImgMergeWindow->GetPaneCount());
532 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) {
533 if (m_nActivePane != pane)
534 m_pImgMergeWindow->SetActivePane(pane);
536 m_wndFilePathBar.SetOnCaptionChangedCallback([&](int pane, const String& sText) {
537 m_strDesc[pane] = sText;
538 UpdateHeaderPath(pane);
540 m_wndFilePathBar.SetOnFileSelectedCallback([&](int pane, const String& sFilepath) {
541 ChangeFile(pane, sFilepath);
544 // Merge frame also has a dockable bar at the very left
545 // created in OnCreateClient
546 m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
547 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
548 m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
549 DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
551 for (int nPane = 0; nPane < m_pImgMergeWindow->GetPaneCount(); nPane++)
553 m_pImgMergeWindow->SetReadOnly(nPane, m_bRO[nPane]);
555 m_wndFilePathBar.SetActive(nPane, FALSE);
556 CreateImgWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pImgMergeWindow->GetPaneHWND(nPane)));
557 UpdateHeaderPath(nPane);
560 CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
561 m_rectBorder.bottom = size.cy;
563 CDockState pDockState;
564 pDockState.LoadState(_T("Settings-ImgMergeFrame"));
565 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
566 SetDockState(pDockState);
567 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
568 m_wndLocationBar.LoadState(_T("Settings-ImgMergeFrame"));
574 * @brief We must use this function before a call to SetDockState
576 * @note Without this, SetDockState will assert or crash if a bar from the
577 * CDockState is missing in the current CMergeEditFrame.
578 * The bars are identified with their ID. This means the missing bar bug is triggered
579 * when we run WinMerge after changing the ID of a bar.
581 bool CImgMergeFrame::EnsureValidDockState(CDockState& state)
583 for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
585 bool barIsCorrect = true;
586 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
587 if (pInfo == nullptr)
588 barIsCorrect = false;
591 if (!pInfo->m_bFloating)
593 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
594 if (pInfo->m_pBar == nullptr)
595 barIsCorrect = false; //toolbar id's probably changed
600 state.m_arrBarInfo.RemoveAt(i);
606 * @brief Save the window's position, free related resources, and destroy the window
608 BOOL CImgMergeFrame::DestroyWindow()
614 CFrameWnd* pParentFrame = GetParentFrame();
615 BOOL result = CMergeFrameCommon::DestroyWindow();
617 pParentFrame->OnUpdateFrameTitle(FALSE);
621 void CImgMergeFrame::LoadOptions()
625 m_pImgMergeWindow->SetHorizontalSplit(GetOptionsMgr()->GetBool(OPT_SPLIT_HORIZONTALLY));
626 m_pImgMergeWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_IMG_SHOWDIFFERENCES));
627 m_pImgMergeWindow->SetOverlayMode(static_cast<IImgMergeWindow::OVERLAY_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYMODE)));
628 m_pImgMergeWindow->SetOverlayAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYALPHA) / 100.0);
629 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DRAGGING_MODE)));
630 m_pImgMergeWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_IMG_ZOOM) / 1000.0);
631 m_pImgMergeWindow->SetUseBackColor(GetOptionsMgr()->GetBool(OPT_CMP_IMG_USEBACKCOLOR));
632 COLORREF clrBackColor = GetOptionsMgr()->GetInt(OPT_CMP_IMG_BACKCOLOR);
633 RGBQUAD backColor = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
634 m_pImgMergeWindow->SetBackColor(backColor);
635 m_pImgMergeWindow->SetDiffBlockSize(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFBLOCKSIZE));
636 m_pImgMergeWindow->SetDiffColorAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFCOLORALPHA) / 100.0);
637 m_pImgMergeWindow->SetColorDistanceThreshold(GetOptionsMgr()->GetInt(OPT_CMP_IMG_THRESHOLD) / 1000.0);
638 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE)));
639 m_pImgMergeWindow->SetVectorImageZoomRatio(GetOptionsMgr()->GetInt(OPT_CMP_IMG_VECTOR_IMAGE_ZOOM_RATIO) / 1000.0f);
642 void CImgMergeFrame::SaveOptions()
644 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_SHOWDIFFERENCES, m_pImgMergeWindow->GetShowDifferences());
645 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYMODE, m_pImgMergeWindow->GetOverlayMode());
646 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYALPHA, static_cast<int>(m_pImgMergeWindow->GetOverlayAlpha() * 100));
647 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DRAGGING_MODE, static_cast<int>(m_pImgMergeWindow->GetDraggingMode()));
648 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_ZOOM, static_cast<int>(m_pImgMergeWindow->GetZoom() * 1000));
649 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_USEBACKCOLOR, m_pImgMergeWindow->GetUseBackColor());
650 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
651 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_BACKCOLOR, static_cast<int>(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue)));
652 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFBLOCKSIZE, m_pImgMergeWindow->GetDiffBlockSize());
653 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFCOLORALPHA, static_cast<int>(m_pImgMergeWindow->GetDiffColorAlpha() * 100.0));
654 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_THRESHOLD, static_cast<int>(m_pImgMergeWindow->GetColorDistanceThreshold() * 1000));
655 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE, static_cast<int>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode()));
656 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_VECTOR_IMAGE_ZOOM_RATIO, static_cast<int>(m_pImgMergeWindow->GetVectorImageZoomRatio() * 1000));
659 * @brief Save coordinates of the frame, splitters, and bars
661 * @note Do not save the maximized/restored state here. We are interested
662 * in the state of the active frame, and maybe this frame is not active
664 void CImgMergeFrame::SavePosition()
669 // save the bars layout
670 // save docking positions and sizes
671 CDockState m_pDockState;
672 GetDockState(m_pDockState);
673 m_pDockState.SaveState(_T("Settings-ImgMergeFrame"));
674 // for the dimensions of the diff pane, use the CSizingControlBar save
675 m_wndLocationBar.SaveState(_T("Settings-ImgMergeFrame"));
678 void CImgMergeFrame::SaveActivePane()
680 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pImgMergeWindow->GetActivePane());
683 void CImgMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
685 CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
689 GetMainFrame()->PostMessage(WM_USER + 1);
693 void CImgMergeFrame::OnClose()
695 // Allow user to cancel closing
696 if (!PromptAndSaveIfNeeded(true))
699 // clean up pointers.
700 CMergeFrameCommon::OnClose();
703 void CImgMergeFrame::OnDestroy()
705 if (!m_pImgMergeWindow)
707 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
708 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
711 bool CImgMergeFrame::DoFileSave(int pane)
713 if (m_pImgMergeWindow->IsModified(pane))
715 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED)
719 String filename = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
720 bool bApplyToAll = false;
721 if (CMergeApp::HandleReadonlySave(m_filePaths[pane], false, bApplyToAll) == IDCANCEL)
723 CMergeApp::CreateBackup(false, m_filePaths[pane]);
724 int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
725 if (!m_pImgMergeWindow->SaveImage(pane))
727 String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), filename, GetSysError());
728 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
730 return DoFileSaveAs(pane);
733 if (filename != m_filePaths[pane])
735 if (!m_infoUnpacker.Packing(filename, m_filePaths[pane], m_unpackerSubcodes[pane], { m_filePaths[pane] }))
737 // Restore save point
738 m_pImgMergeWindow->SetSavePoint(pane, savepoint);
740 String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), m_filePaths[pane], m_infoUnpacker);
741 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
743 return DoFileSaveAs(pane, false);
748 UpdateDiffItem(m_pDirDoc);
749 m_fileInfo[pane].Update(m_filePaths[pane]);
754 bool CImgMergeFrame::DoFileSaveAs(int pane, bool packing)
756 const String &path = m_filePaths.GetPath(pane);
760 title = _("Save Left File As");
761 else if (pane == m_pImgMergeWindow->GetPaneCount() - 1)
762 title = _("Save Right File As");
764 title = _("Save Middle File As");
766 if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
768 std::wstring filename = ucr::toUTF16(strPath);
769 if (packing && !m_infoUnpacker.GetPluginPipeline().empty())
771 String tempPath = env::GetTemporaryPath();
772 filename = ucr::toUTF16(env::GetTemporaryFileName(tempPath, _T("MRG_"), 0)
773 + paths::FindExtension(m_pImgMergeWindow->GetFileName(pane)));
775 int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
776 if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
778 String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), strPath, GetSysError());
779 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
784 if (filename != strPath)
786 if (!m_infoUnpacker.Packing(filename, strPath, m_unpackerSubcodes[pane], { strPath }))
788 // Restore save point
789 m_pImgMergeWindow->SetSavePoint(pane, savepoint);
791 String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), strPath, m_infoUnpacker);
792 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
794 return DoFileSaveAs(pane, false);
800 // We are saving scratchpad (unnamed file)
801 m_nBufferType[pane] = BUFFERTYPE::UNNAMED_SAVED;
802 m_strDesc[pane].erase();
805 m_filePaths.SetPath(pane, strPath);
806 UpdateDiffItem(m_pDirDoc);
807 m_fileInfo[pane].Update(m_filePaths[pane]);
808 UpdateHeaderPath(pane);
814 * @brief Saves both files
816 void CImgMergeFrame::OnFileSave()
818 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
823 * @brief Called when "Save" item is updated
825 void CImgMergeFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
827 pCmdUI->Enable(IsModified());
831 * @brief Saves left-side file
833 void CImgMergeFrame::OnFileSaveLeft()
839 * @brief Called when "Save middle (...)" item is updated
841 void CImgMergeFrame::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
843 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
847 * @brief Saves middle-side file
849 void CImgMergeFrame::OnFileSaveMiddle()
855 * @brief Saves right-side file
857 void CImgMergeFrame::OnFileSaveRight()
859 DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
863 * @brief Called when "Save middle (as...)" item is updated
865 void CImgMergeFrame::OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI)
867 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
871 * @brief Saves left-side file with name asked
873 void CImgMergeFrame::OnFileSaveAsLeft()
879 * @brief Saves middle-side file with name asked
881 void CImgMergeFrame::OnFileSaveAsMiddle()
887 * @brief Saves right-side file with name asked
889 void CImgMergeFrame::OnFileSaveAsRight()
891 DoFileSaveAs(m_pImgMergeWindow->GetPaneCount() - 1);
895 * @brief Reloads the opened files
897 void CImgMergeFrame::OnFileReload()
899 if (!PromptAndSaveIfNeeded(true))
901 int nActivePane = m_pImgMergeWindow->GetActivePane();
903 MoveOnLoad(nActivePane);
904 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
905 m_fileInfo[pane].Update(m_filePaths[pane]);
908 void CImgMergeFrame::OnFileClose()
914 * @brief Enable/disable left buffer read-only
916 void CImgMergeFrame::OnFileReadOnlyLeft()
918 m_bRO[0] = !m_bRO[0];
919 m_pImgMergeWindow->SetReadOnly(0, m_bRO[0]);
923 * @brief Called when "Left read-only" item is updated
925 void CImgMergeFrame::OnUpdateFileReadOnlyLeft(CCmdUI* pCmdUI)
927 pCmdUI->Enable(true);
928 pCmdUI->SetCheck(m_bRO[0]);
932 * @brief Enable/disable middle buffer read-only
934 void CImgMergeFrame::OnFileReadOnlyMiddle()
936 if (m_pImgMergeWindow->GetPaneCount() == 3)
938 m_bRO[1] = !m_bRO[1];
939 m_pImgMergeWindow->SetReadOnly(1, m_bRO[1]);
944 * @brief Called when "Middle read-only" item is updated
946 void CImgMergeFrame::OnUpdateFileReadOnlyMiddle(CCmdUI* pCmdUI)
948 if (m_pImgMergeWindow->GetPaneCount() < 3)
950 pCmdUI->Enable(false);
954 pCmdUI->Enable(true);
955 pCmdUI->SetCheck(m_bRO[1]);
960 * @brief Enable/disable right buffer read-only
962 void CImgMergeFrame::OnFileReadOnlyRight()
964 int pane = m_pImgMergeWindow->GetPaneCount() - 1;
965 m_bRO[pane] = !m_bRO[pane];
966 m_pImgMergeWindow->SetReadOnly(pane, m_bRO[pane]);
970 * @brief Called when "Right read-only" item is updated
972 void CImgMergeFrame::OnUpdateFileReadOnlyRight(CCmdUI* pCmdUI)
974 pCmdUI->Enable(true);
975 pCmdUI->SetCheck(m_pImgMergeWindow->GetReadOnly(m_pImgMergeWindow->GetPaneCount() - 1));
978 void CImgMergeFrame::OnFileRecompareAs(UINT nID)
980 PathContext paths = m_filePaths;
981 fileopenflags_t dwFlags[3];
983 int nBuffers = m_filePaths.GetSize();
984 PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
986 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
988 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
989 strDesc[nBuffer] = m_strDesc[nBuffer];
991 if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
993 infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
994 nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_IMAGE : -ID_MERGE_COMPARE_IMAGE;
998 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
999 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
1002 void CImgMergeFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
1004 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_IMAGE);
1007 void CImgMergeFrame::OnOpenWithUnpacker()
1009 CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
1010 strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
1011 CSelectPluginDlg::PluginType::Unpacker, false);
1012 if (dlg.DoModal() == IDOK)
1014 PackingInfo infoUnpacker(dlg.GetPluginPipeline());
1015 PathContext paths = m_filePaths;
1016 fileopenflags_t dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
1017 String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
1019 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
1020 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr,
1021 GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_IMAGE : -ID_MERGE_COMPARE_IMAGE);
1025 void CImgMergeFrame::OnWindowChangePane(UINT nID)
1027 int npanes = m_pImgMergeWindow->GetPaneCount();
1028 int pane = m_pImgMergeWindow->GetActivePane();
1029 pane = (nID == ID_NEXT_PANE) ? ((pane + 1) % npanes) : ((pane + npanes - 1) % npanes);
1030 m_pImgMergeWindow->SetActivePane(pane);
1034 * @brief Write path and filename to headerbar
1035 * @note SetText() does not repaint unchanged text
1037 void CImgMergeFrame::UpdateHeaderPath(int pane)
1041 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
1042 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
1044 sText = m_strDesc[pane];
1048 sText = m_filePaths.GetPath(pane);
1049 if (m_pDirDoc != nullptr)
1050 m_pDirDoc->ApplyDisplayRoot(pane, sText);
1052 if (m_pImgMergeWindow->IsModified(pane))
1053 sText.insert(0, _T("* "));
1055 m_wndFilePathBar.SetText(pane, sText.c_str());
1060 /// update splitting position for panels 1/2 and headerbar and statusbar
1061 void CImgMergeFrame::UpdateHeaderSizes()
1063 if (m_pImgMergeWindow != nullptr)
1065 const int scrollbarWidth = GetSystemMetrics(SM_CXVSCROLL);
1067 CRect rc, rcMergeWindow;
1068 int nPaneCount = m_pImgMergeWindow->GetPaneCount();
1070 ::GetWindowRect(m_pImgMergeWindow->GetHWND(), &rcMergeWindow);
1071 ScreenToClient(rcMergeWindow);
1072 if (!m_pImgMergeWindow->GetHorizontalSplit())
1074 for (int pane = 0; pane < nPaneCount; pane++)
1076 RECT rc1 = m_pImgMergeWindow->GetPaneWindowRect(pane);
1077 w[pane] = rc1.right - rc1.left - 4;
1078 if (pane == nPaneCount - 1)
1079 w[pane] -= scrollbarWidth;
1080 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
1085 for (int pane = 0; pane < nPaneCount; pane++)
1086 w[pane] = (rcMergeWindow.Width() - scrollbarWidth) / nPaneCount - 6;
1089 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
1091 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
1093 // resize controls in header dialog bar
1094 m_wndFilePathBar.Resize(w);
1096 rc.left = rcMergeWindow.left;
1097 rc.top = rc.bottom - m_rectBorder.bottom;
1099 for (int pane = 0; pane < nPaneCount; pane++)
1101 rc.right += w[pane] + 4 + 2;
1102 m_wndStatusBar[pane].MoveWindow(&rc);
1110 * @brief Update document filenames to title
1112 void CImgMergeFrame::SetTitle(LPCTSTR lpszTitle)
1114 String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr);
1115 CMergeFrameCommon::SetTitle(sTitle.c_str());
1116 if (m_hWnd != nullptr)
1117 SetWindowText(sTitle.c_str());
1120 void CImgMergeFrame::UpdateLastCompareResult()
1122 SetLastCompareResult(m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0);
1125 void CImgMergeFrame::UpdateAutoPaneResize()
1129 void CImgMergeFrame::UpdateSplitter()
1133 bool CImgMergeFrame::OpenImages()
1136 String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
1137 String strTempFileName[3];
1138 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1140 strTempFileName[pane] = m_filePaths[pane];
1141 if (!m_infoUnpacker.Unpacking(&m_unpackerSubcodes[pane], strTempFileName[pane], filteredFilenames, { strTempFileName[pane] }))
1146 if (m_filePaths.GetSize() == 2)
1147 bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str());
1149 bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str());
1154 * @brief Update associated diff item
1156 int CImgMergeFrame::UpdateDiffItem(CDirDoc *pDirDoc)
1158 // If directory compare has results
1159 if (pDirDoc && pDirDoc->HasDiffs())
1162 // const String &pathLeft = m_filePaths.GetLeft();
1163 // const String &pathRight = m_filePaths.GetRight();
1164 // CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
1165 // if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
1167 // DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
1168 // ::UpdateDiffItem(m_nBuffers, di, &ctxt);
1171 int result = m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0;
1172 SetLastCompareResult(result != 0);
1177 * @brief Asks and then saves modified files.
1179 * This function saves modified files. Dialog is shown for user to select
1180 * modified file(s) one wants to save or discard changed. Cancelling of
1181 * save operation is allowed unless denied by parameter. After successfully
1182 * save operation file statuses are updated to directory compare.
1183 * @param [in] bAllowCancel If false "Cancel" button is disabled.
1184 * @return true if user selected "OK" so next operation can be
1185 * executed. If false user choosed "Cancel".
1186 * @note If filename is empty, we assume scratchpads are saved,
1187 * so instead of filename, description is shown.
1188 * @todo If we have filename and description for file, what should
1189 * we do after saving to different filename? Empty description?
1190 * @todo Parameter @p bAllowCancel is always true in callers - can be removed.
1192 bool CImgMergeFrame::PromptAndSaveIfNeeded(bool bAllowCancel)
1194 bool bLModified = false, bMModified = false, bRModified = false;
1196 bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
1198 if (m_pImgMergeWindow->GetPaneCount() == 3)
1200 bLModified = m_pImgMergeWindow->IsModified(0);
1201 bMModified = m_pImgMergeWindow->IsModified(1);
1202 bRModified = m_pImgMergeWindow->IsModified(2);
1206 bLModified = m_pImgMergeWindow->IsModified(0);
1207 bRModified = m_pImgMergeWindow->IsModified(1);
1209 if (!bLModified && !bMModified && !bRModified)
1213 dlg.DoAskFor(bLModified, bMModified, bRModified);
1215 dlg.m_bDisableCancel = true;
1216 if (!m_filePaths.GetLeft().empty())
1218 if (theApp.m_strSaveAsPath.empty())
1219 dlg.m_sLeftFile = m_filePaths.GetLeft();
1221 dlg.m_sLeftFile = theApp.m_strSaveAsPath;
1224 dlg.m_sLeftFile = m_strDesc[0];
1225 if (m_pImgMergeWindow->GetPaneCount() == 3)
1227 if (!m_filePaths.GetMiddle().empty())
1229 if (theApp.m_strSaveAsPath.empty())
1230 dlg.m_sMiddleFile = m_filePaths.GetMiddle();
1232 dlg.m_sMiddleFile = theApp.m_strSaveAsPath;
1235 dlg.m_sMiddleFile = m_strDesc[1];
1237 if (!m_filePaths.GetRight().empty())
1239 if (theApp.m_strSaveAsPath.empty())
1240 dlg.m_sRightFile = m_filePaths.GetRight();
1242 dlg.m_sRightFile = theApp.m_strSaveAsPath;
1245 dlg.m_sRightFile = m_strDesc[m_pImgMergeWindow->GetPaneCount() - 1];
1247 if (dlg.DoModal() == IDOK)
1249 if (bLModified && dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
1251 bLSaveSuccess = DoFileSave(0);
1256 if (bMModified && dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
1258 bMSaveSuccess = DoFileSave(1);
1263 if (bRModified && dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
1265 bRSaveSuccess = DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
1275 // If file were modified and saving was successfull,
1276 // update status on dir view
1277 if ((bLModified && bLSaveSuccess) ||
1278 (bMModified && bMSaveSuccess) ||
1279 (bRModified && bRSaveSuccess))
1281 // If directory compare has results
1282 if (m_pDirDoc && m_pDirDoc->HasDiffs())
1291 /// Document commanding us to close
1292 bool CImgMergeFrame::CloseNow()
1294 // Allow user to cancel closing
1295 if (!PromptAndSaveIfNeeded(true))
1303 * @brief A string to display as a tooltip for MDITabbar
1305 CString CImgMergeFrame::GetTooltipString() const
1307 return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
1311 * @brief Update any resources necessary after a GUI language change
1313 void CImgMergeFrame::UpdateResources()
1315 m_pImgToolWindow->Translate(TranslateLocationPane);
1318 void CImgMergeFrame::RefreshOptions()
1320 COLORSETTINGS colors;
1321 Options::DiffColors::Load(GetOptionsMgr(), colors);
1322 m_pImgMergeWindow->SetDiffColor(colors.clrDiff);
1323 m_pImgMergeWindow->SetDiffDeletedColor(colors.clrDiffDeleted);
1324 m_pImgMergeWindow->SetSelDiffColor(colors.clrSelDiff);
1325 m_pImgMergeWindow->SetSelDiffDeletedColor(colors.clrSelDiffDeleted);
1326 m_pImgMergeWindow->SetDiffAlgorithm(static_cast<IImgMergeWindow::DIFF_ALGORITHM>(GetOptionsMgr()->GetInt(OPT_CMP_DIFF_ALGORITHM)));
1330 * @brief Handle some keys when in merging mode
1332 bool CImgMergeFrame::MergeModeKeyDown(MSG* pMsg)
1334 bool bHandled = false;
1336 // Allow default text selection when SHIFT pressed
1337 if (::GetAsyncKeyState(VK_SHIFT))
1340 // Allow default editor functions when CTRL pressed
1341 if (::GetAsyncKeyState(VK_CONTROL))
1344 // If we are in merging mode (merge with cursor keys)
1345 // handle some keys here
1346 switch (pMsg->wParam)
1371 * @brief Check for keyboard commands
1373 BOOL CImgMergeFrame::PreTranslateMessage(MSG* pMsg)
1375 if (pMsg->message == WM_KEYDOWN)
1377 // If we are in merging mode (merge with cursor keys)
1378 // handle some keys here
1379 if (theApp.GetMergingMode())
1381 bool bHandled = MergeModeKeyDown(pMsg);
1386 // Close window in response to VK_ESCAPE if user has allowed it from options
1387 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) != 0)
1389 PostMessage(WM_CLOSE, 0, 0);
1393 return CMergeFrameCommon::PreTranslateMessage(pMsg);
1396 void CImgMergeFrame::OnSize(UINT nType, int cx, int cy)
1398 CMergeFrameCommon::OnSize(nType, cx, cy);
1399 UpdateHeaderSizes();
1403 * @brief Synchronize control and status bar placements with splitter position,
1404 * update mod indicators, synchronize scrollbars
1406 void CImgMergeFrame::OnIdleUpdateCmdUI()
1408 if (IsWindowVisible())
1410 POINT pt = {-1, -1}, ptCursor;
1411 GetCursorPos(&ptCursor);
1412 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1415 ::GetWindowRect(m_pImgMergeWindow->GetPaneHWND(pane), &rc);
1416 if (PtInRect(&rc, ptCursor))
1417 pt = m_pImgMergeWindow->GetCursorPos(pane);
1421 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1422 color[pane] = m_pImgMergeWindow->GetPixelColor(pane, pt.x, pt.y);
1423 double colorDistance01 = m_pImgMergeWindow->GetColorDistance(0, 1, pt.x, pt.y);
1424 double colorDistance12 = 0;
1425 if (m_pImgMergeWindow->GetPaneCount() == 3)
1426 colorDistance12 = m_pImgMergeWindow->GetColorDistance(1, 2, pt.x, pt.y);
1428 int nActivePane = m_pImgMergeWindow->GetActivePane();
1429 if (nActivePane != -1)
1430 m_nActivePane = nActivePane;
1432 UpdateHeaderSizes();
1433 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1435 // Update mod indicators
1436 String ind = m_wndFilePathBar.GetText(pane);
1437 if (m_pImgMergeWindow->IsModified(pane) ? ind[0] != _T('*') : ind[0] == _T('*'))
1438 UpdateHeaderPath(pane);
1440 m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1443 if (m_pImgMergeWindow->ConvertToRealPos(pane, pt, ptReal))
1445 text += strutils::format(_("Pt: (%d, %d) RGBA: (%d, %d, %d, %d) "), ptReal.x, ptReal.y,
1446 color[pane].rgbRed, color[pane].rgbGreen, color[pane].rgbBlue, color[pane].rgbReserved);
1447 if (pane == 1 && m_pImgMergeWindow->GetPaneCount() == 3)
1448 text += strutils::format(_("Dist: %g, %g "), colorDistance01, colorDistance12);
1450 text += strutils::format(_("Dist: %g "), colorDistance01);
1452 if (m_pImgMergeWindow->IsRectangleSelectionVisible(pane))
1454 RECT rc = m_pImgMergeWindow->GetRectangleSelection(pane);
1455 text += strutils::format(_("Rc: (%d, %d) "), rc.right - rc.left, rc.bottom - rc.top);
1457 text += strutils::format(_("Page: %d/%d Zoom: %d%% %dx%dpx %dbpp "),
1458 m_pImgMergeWindow->GetCurrentPage(pane) + 1,
1459 m_pImgMergeWindow->GetPageCount(pane),
1460 static_cast<int>(m_pImgMergeWindow->GetZoom() * 100),
1461 m_pImgMergeWindow->GetImageWidth(pane),
1462 m_pImgMergeWindow->GetImageHeight(pane),
1463 m_pImgMergeWindow->GetImageBitsPerPixel(pane)
1465 bool verticalFlip = m_pImgMergeWindow->GetVerticalFlip(pane);
1466 bool horizontalFlip = m_pImgMergeWindow->GetHorizontalFlip(pane);
1467 float angle = m_pImgMergeWindow->GetRotation(pane);
1468 if (verticalFlip || horizontalFlip)
1475 text += strutils::format(_("Flipped: %s "), flip);
1478 text += strutils::format(_("Rotated: %d "), static_cast<int>(angle));
1479 m_wndStatusBar[pane].SetPaneText(0, text.c_str());
1481 UpdateLastCompareResult();
1483 CMergeFrameCommon::OnIdleUpdateCmdUI();
1487 * @brief Save pane sizes and positions when one of panes requests it.
1489 LRESULT CImgMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1495 void CImgMergeFrame::OnUpdateStatusNum(CCmdUI* pCmdUI)
1497 tchar_t sCnt[32] = { 0 };
1499 const int nDiffs = m_pImgMergeWindow->GetDiffCount();
1501 // Files are identical - show text "Identical"
1503 s = theApp.LoadString(IDS_IDENTICAL);
1505 // There are differences, but no selected diff
1506 // - show amount of diffs
1507 else if (m_pImgMergeWindow->GetCurrentDiffIndex() < 0)
1509 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1510 _itot_s(nDiffs, sCnt, 10);
1511 strutils::replace(s, _T("%1"), sCnt);
1514 // There are differences and diff selected
1515 // - show diff number and amount of diffs
1518 tchar_t sIdx[32] = { 0 };
1519 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1520 const int signInd = m_pImgMergeWindow->GetCurrentDiffIndex();
1521 _itot_s(signInd + 1, sIdx, 10);
1522 strutils::replace(s, _T("%1"), sIdx);
1523 _itot_s(nDiffs, sCnt, 10);
1524 strutils::replace(s, _T("%2"), sCnt);
1526 pCmdUI->SetText(s.c_str());
1530 * @brief Undo last action
1532 void CImgMergeFrame::OnEditUndo()
1534 m_pImgMergeWindow->Undo();
1535 if (!m_pImgMergeWindow->IsUndoable())
1536 m_bAutoMerged = false;
1537 UpdateLastCompareResult();
1541 * @brief Called when "Undo" item is updated
1543 void CImgMergeFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1545 pCmdUI->Enable(m_pImgMergeWindow->IsUndoable());
1549 * @brief Redo last action
1551 void CImgMergeFrame::OnEditRedo()
1553 m_pImgMergeWindow->Redo();
1554 UpdateLastCompareResult();
1558 * @brief Called when "Redo" item is updated
1560 void CImgMergeFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1562 pCmdUI->Enable(m_pImgMergeWindow->IsRedoable());
1566 * @brief Copy current selection to clipboard
1568 void CImgMergeFrame::OnEditCopy()
1570 m_pImgMergeWindow->Copy();
1574 * @brief Called when "Copy" item is updated
1576 void CImgMergeFrame::OnUpdateEditCopy(CCmdUI* pCmdUI)
1578 pCmdUI->Enable(m_pImgMergeWindow->IsCopyable());
1582 * @brief Cut current selection to clipboard
1584 void CImgMergeFrame::OnEditCut()
1586 m_pImgMergeWindow->Cut();
1590 * @brief Called when "Cut" item is updated
1592 void CImgMergeFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
1594 pCmdUI->Enable(m_pImgMergeWindow->IsCuttable());
1598 * @brief Paste image from clipboard
1600 void CImgMergeFrame::OnEditPaste()
1602 m_pImgMergeWindow->Paste();
1606 * @brief Called when "Paste" item is updated
1608 void CImgMergeFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)
1610 pCmdUI->Enable(m_pImgMergeWindow->IsPastable());
1614 * @brief Select entire image
1616 void CImgMergeFrame::OnEditSelectAll()
1618 m_pImgMergeWindow->SelectAll();
1622 * @brief Called when user selects View/Zoom In from menu.
1624 void CImgMergeFrame::OnViewZoomIn()
1626 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() + 0.1);
1630 * @brief Called when user selects View/Zoom Out from menu.
1632 void CImgMergeFrame::OnViewZoomOut()
1634 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() - 0.1);
1638 * @brief Called when user selects View/Zoom Normal from menu.
1640 void CImgMergeFrame::OnViewZoomNormal()
1642 m_pImgMergeWindow->SetZoom(1.0);
1646 * @brief Split panes vertically
1648 void CImgMergeFrame::OnViewSplitVertically()
1650 bool bSplitVertically = !m_pImgMergeWindow->GetHorizontalSplit();
1651 bSplitVertically = !bSplitVertically; // toggle
1652 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1653 m_pImgMergeWindow->SetHorizontalSplit(!bSplitVertically);
1657 * @brief Update "Split Vertically" UI items
1659 void CImgMergeFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
1661 pCmdUI->Enable(TRUE);
1662 pCmdUI->SetCheck(!m_pImgMergeWindow->GetHorizontalSplit());
1666 * @brief Go to first diff
1668 * Called when user selects "First Difference"
1669 * @sa CImgMergeFrame::SelectDiff()
1671 void CImgMergeFrame::OnFirstdiff()
1673 m_pImgMergeWindow->FirstDiff();
1677 * @brief Update "First diff" UI items
1679 void CImgMergeFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1681 OnUpdatePrevdiff(pCmdUI);
1685 * @brief Go to last diff
1687 void CImgMergeFrame::OnLastdiff()
1689 m_pImgMergeWindow->LastDiff();
1693 * @brief Update "Last diff" UI items
1695 void CImgMergeFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1697 OnUpdateNextdiff(pCmdUI);
1701 * @brief Go to next diff and select it.
1703 void CImgMergeFrame::OnNextdiff()
1705 if (m_pImgMergeWindow->GetCurrentDiffIndex() != m_pImgMergeWindow->GetDiffCount() - 1)
1706 m_pImgMergeWindow->NextDiff();
1707 else if (m_pImgMergeWindow->GetCurrentMaxPage() != m_pImgMergeWindow->GetMaxPageCount() - 1)
1709 if (AfxMessageBox(_("Do you want to move to the next page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN, IDS_MOVE_TO_NEXTPAGE) == IDYES)
1711 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1712 UpdateLastCompareResult();
1715 else if (m_pDirDoc != nullptr)
1716 m_pDirDoc->MoveToNextDiff(this);
1720 * @brief Update "Next diff" UI items
1722 void CImgMergeFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1725 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1 ||
1726 m_pImgMergeWindow->GetNextDiffIndex() >= 0 ||
1727 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1729 if (!enabled && m_pDirDoc != nullptr)
1730 enabled = m_pDirDoc->MoveableToNextDiff();
1732 pCmdUI->Enable(enabled);
1736 * @brief Go to previous diff and select it.
1738 void CImgMergeFrame::OnPrevdiff()
1740 if (m_pImgMergeWindow->GetCurrentDiffIndex() > 0)
1742 m_pImgMergeWindow->PrevDiff();
1744 else if (m_pImgMergeWindow->GetCurrentMaxPage() != 0)
1746 if (AfxMessageBox(_("Do you want to move to the previous page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN, IDS_MOVE_TO_PREVPAGE) == IDYES)
1748 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1749 UpdateLastCompareResult();
1752 else if (m_pDirDoc != nullptr)
1753 m_pDirDoc->MoveToPrevDiff(this);
1757 * @brief Update "Previous diff" UI items
1759 void CImgMergeFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1762 m_pImgMergeWindow->GetCurrentMaxPage() > 0 ||
1763 m_pImgMergeWindow->GetPrevDiffIndex() >= 0 ||
1764 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1766 if (!enabled && m_pDirDoc != nullptr)
1767 enabled = m_pDirDoc->MoveableToPrevDiff();
1769 pCmdUI->Enable(enabled);
1773 * @brief Go to next conflict and select it.
1775 void CImgMergeFrame::OnNextConflict()
1777 m_pImgMergeWindow->NextConflict();
1781 * @brief Update "Next Conflict" UI items
1783 void CImgMergeFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1786 m_pImgMergeWindow->GetPaneCount() > 2 && (
1787 m_pImgMergeWindow->GetNextConflictIndex() >= 0 ||
1788 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1794 * @brief Go to previous diff and select it.
1796 void CImgMergeFrame::OnPrevConflict()
1798 m_pImgMergeWindow->PrevConflict();
1802 * @brief Update "Previous diff" UI items
1804 void CImgMergeFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1807 m_pImgMergeWindow->GetPaneCount() > 2 && (
1808 m_pImgMergeWindow->GetPrevConflictIndex() >= 0 ||
1809 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1814 void CImgMergeFrame::OnCopyX2Y(UINT nID)
1816 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1817 if (srcPane >= 0 && dstPane >= 0)
1818 OnX2Y(srcPane, dstPane);
1821 void CImgMergeFrame::OnUpdateCopyFromXToY(CCmdUI* pCmdUI)
1823 OnUpdateX2Y(pCmdUI);
1826 void CImgMergeFrame::OnUpdateX2Y(CCmdUI* pCmdUI)
1828 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1829 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentDiffIndex() >= 0 &&
1830 srcPane >= 0 && srcPane <= m_pImgMergeWindow->GetPaneCount() &&
1831 dstPane >= 0 && dstPane <= m_pImgMergeWindow->GetPaneCount() &&
1834 if (m_pImgMergeWindow->GetPaneCount() > 2)
1835 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
1838 void CImgMergeFrame::OnX2Y(int srcPane, int dstPane)
1840 m_pImgMergeWindow->CopyDiff(m_pImgMergeWindow->GetCurrentDiffIndex(), srcPane, dstPane);
1841 UpdateLastCompareResult();
1845 * @brief Copy diff from left pane to right pane
1847 void CImgMergeFrame::OnL2r()
1853 * @brief Called when "Copy to Right" item is updated
1855 void CImgMergeFrame::OnUpdateL2r(CCmdUI* pCmdUI)
1857 OnUpdateX2Y(pCmdUI);
1861 * @brief Copy diff from right pane to left pane
1863 void CImgMergeFrame::OnR2l()
1869 * @brief Called when "Copy to Left" item is updated
1871 void CImgMergeFrame::OnUpdateR2l(CCmdUI* pCmdUI)
1873 OnUpdateX2Y(pCmdUI);
1877 * @brief Update "Copy right and advance" UI item
1879 void CImgMergeFrame::OnUpdateL2RNext(CCmdUI* pCmdUI)
1881 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1882 pCmdUI->Enable(false);
1883 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
1887 * @brief Update "Copy left and advance" UI item
1889 void CImgMergeFrame::OnUpdateR2LNext(CCmdUI* pCmdUI)
1891 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1892 pCmdUI->Enable(false);
1893 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
1896 void CImgMergeFrame::OnCopyFromLeft()
1898 OnCopyX2Y(ID_COPY_FROM_LEFT);
1902 * @brief Called when "Copy from left" item is updated
1904 void CImgMergeFrame::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1906 OnUpdateX2Y(pCmdUI);
1909 void CImgMergeFrame::OnCopyFromRight()
1911 OnCopyX2Y(ID_COPY_FROM_RIGHT);
1915 * @brief Called when "Copy from right" item is updated
1917 void CImgMergeFrame::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1919 OnUpdateX2Y(pCmdUI);
1923 * @brief Copy all diffs from right pane to left pane
1925 void CImgMergeFrame::OnAllLeft()
1927 UINT userChoice = 0;
1928 String msg = _("Are you sure you want to copy all differences to the other file?");
1929 userChoice = AfxMessageBox(msg.c_str(), MB_YESNO |
1930 MB_ICONWARNING | MB_DEFBUTTON2 | MB_DONT_ASK_AGAIN, IDS_CONFIRM_COPY_ALL_DIFFS);
1931 if (userChoice == IDNO)
1934 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_ALL_LEFT, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1936 CWaitCursor waitstatus;
1938 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1939 UpdateLastCompareResult();
1943 * @brief Called when "Copy all to left" item is updated
1945 void CImgMergeFrame::OnUpdateAllLeft(CCmdUI* pCmdUI)
1947 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1949 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1950 if (m_pImgMergeWindow->GetPaneCount() > 2)
1951 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
1955 * @brief Copy all diffs from left pane to right pane
1957 void CImgMergeFrame::OnAllRight()
1959 UINT userChoice = 0;
1960 String msg = _("Are you sure you want to copy all differences to the other file?");
1961 userChoice = AfxMessageBox(msg.c_str(), MB_YESNO |
1962 MB_ICONWARNING | MB_DEFBUTTON2 | MB_DONT_ASK_AGAIN, IDS_CONFIRM_COPY_ALL_DIFFS);
1963 if (userChoice == IDNO)
1966 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_ALL_RIGHT, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1968 CWaitCursor waitstatus;
1970 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1971 UpdateLastCompareResult();
1975 * @brief Called when "Copy all to right" item is updated
1977 void CImgMergeFrame::OnUpdateAllRight(CCmdUI* pCmdUI)
1979 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetPaneCount());
1981 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1982 if (m_pImgMergeWindow->GetPaneCount() > 2)
1983 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
1987 * @brief Do Auto merge
1989 void CImgMergeFrame::OnAutoMerge()
1991 int dstPane = m_pImgMergeWindow->GetActivePane();
1993 // Check current pane is not readonly
1994 if (dstPane < 0 || IsModified() || m_bAutoMerged || m_bRO[dstPane])
1997 CWaitCursor waitstatus;
1999 DoAutoMerge(dstPane);
2003 * @brief Called when "Auto Merge" item is updated
2005 void CImgMergeFrame::OnUpdateAutoMerge(CCmdUI* pCmdUI)
2007 int dstPane = m_pImgMergeWindow->GetActivePane();
2009 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 &&
2010 dstPane >= 0 && !IsModified() && !m_bAutoMerged && !m_bRO[dstPane]);
2013 void CImgMergeFrame::OnImgViewDifferences()
2015 m_pImgMergeWindow->SetShowDifferences(!m_pImgMergeWindow->GetShowDifferences());
2019 void CImgMergeFrame::OnUpdateImgViewDifferences(CCmdUI* pCmdUI)
2021 pCmdUI->SetCheck(m_pImgMergeWindow->GetShowDifferences() ? 1 : 0);
2024 void CImgMergeFrame::OnImgZoom(UINT nId)
2026 m_pImgMergeWindow->SetZoom(pow(2.0, int(nId - ID_IMG_ZOOM_100)));
2030 void CImgMergeFrame::OnUpdateImgZoom(CCmdUI* pCmdUI)
2032 pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_ZOOM_100)) == m_pImgMergeWindow->GetZoom());
2035 void CImgMergeFrame::OnImgOverlayMode(UINT nId)
2037 if (nId == ID_IMG_OVERLAY_NONE)
2038 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_NONE);
2039 else if (nId == ID_IMG_OVERLAY_XOR)
2040 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_XOR);
2041 else if (nId == ID_IMG_OVERLAY_ALPHABLEND)
2042 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND);
2043 else if (nId == ID_IMG_OVERLAY_ALPHABLEND_ANIM)
2044 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND_ANIM);
2048 void CImgMergeFrame::OnUpdateImgOverlayMode(CCmdUI* pCmdUI)
2050 pCmdUI->SetRadio(static_cast<IImgMergeWindow::OVERLAY_MODE>(pCmdUI->m_nID - ID_IMG_OVERLAY_NONE) == m_pImgMergeWindow->GetOverlayMode());
2053 void CImgMergeFrame::OnImgDraggingMode(UINT nId)
2055 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(nId - ID_IMG_DRAGGINGMODE_NONE));
2059 void CImgMergeFrame::OnUpdateImgDraggingMode(CCmdUI* pCmdUI)
2061 pCmdUI->SetRadio(static_cast<IImgMergeWindow::DRAGGING_MODE>(pCmdUI->m_nID - ID_IMG_DRAGGINGMODE_NONE) == m_pImgMergeWindow->GetDraggingMode());
2064 void CImgMergeFrame::OnImgDiffBlockSize(UINT nId)
2066 m_pImgMergeWindow->SetDiffBlockSize(1 << (nId - ID_IMG_DIFFBLOCKSIZE_1));
2070 void CImgMergeFrame::OnUpdateImgDiffBlockSize(CCmdUI* pCmdUI)
2072 pCmdUI->SetRadio(1 << (pCmdUI->m_nID - ID_IMG_DIFFBLOCKSIZE_1) == m_pImgMergeWindow->GetDiffBlockSize() );
2075 void CImgMergeFrame::OnImgThreshold(UINT nId)
2077 if (nId == ID_IMG_THRESHOLD_0)
2078 m_pImgMergeWindow->SetColorDistanceThreshold(0.0);
2080 m_pImgMergeWindow->SetColorDistanceThreshold((1 << (nId - ID_IMG_THRESHOLD_2)) * 2);
2084 void CImgMergeFrame::OnUpdateImgThreshold(CCmdUI* pCmdUI)
2086 if (pCmdUI->m_nID == ID_IMG_THRESHOLD_0)
2087 pCmdUI->SetRadio(m_pImgMergeWindow->GetColorDistanceThreshold() == 0.0);
2089 pCmdUI->SetRadio((1 << (pCmdUI->m_nID - ID_IMG_THRESHOLD_2)) * 2 == m_pImgMergeWindow->GetColorDistanceThreshold() );
2092 void CImgMergeFrame::OnImgInsertionDeletionDetectionMode(UINT nId)
2094 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(nId - ID_IMG_INSERTIONDELETIONDETECTION_NONE));
2098 void CImgMergeFrame::OnUpdateImgInsertionDeletionDetectionMode(CCmdUI* pCmdUI)
2100 pCmdUI->SetRadio(static_cast<unsigned>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode() + ID_IMG_INSERTIONDELETIONDETECTION_NONE) == pCmdUI->m_nID);
2103 void CImgMergeFrame::OnImgPrevPage()
2105 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
2106 UpdateLastCompareResult();
2109 void CImgMergeFrame::OnUpdateImgPrevPage(CCmdUI* pCmdUI)
2111 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentMaxPage() > 0);
2114 void CImgMergeFrame::OnImgNextPage()
2116 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
2117 UpdateLastCompareResult();
2120 void CImgMergeFrame::OnUpdateImgNextPage(CCmdUI* pCmdUI)
2123 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1);
2126 void CImgMergeFrame::OnImgCurPaneRotateRight90()
2128 int curPane = m_pImgMergeWindow->GetActivePane();
2129 m_pImgMergeWindow->SetRotation(curPane, m_pImgMergeWindow->GetRotation(curPane) - 90.f);
2130 UpdateLastCompareResult();
2133 void CImgMergeFrame::OnImgCurPaneRotateLeft90()
2135 int curPane = m_pImgMergeWindow->GetActivePane();
2136 m_pImgMergeWindow->SetRotation(curPane, m_pImgMergeWindow->GetRotation(curPane) + 90.f);
2137 UpdateLastCompareResult();
2140 void CImgMergeFrame::OnImgCurPaneFlipVertically()
2142 int curPane = m_pImgMergeWindow->GetActivePane();
2143 m_pImgMergeWindow->SetVerticalFlip(curPane, !m_pImgMergeWindow->GetVerticalFlip(curPane));
2144 UpdateLastCompareResult();
2147 void CImgMergeFrame::OnImgCurPaneFlipHorizontally()
2149 int curPane = m_pImgMergeWindow->GetActivePane();
2150 m_pImgMergeWindow->SetHorizontalFlip(curPane, !m_pImgMergeWindow->GetHorizontalFlip(curPane));
2151 UpdateLastCompareResult();
2154 void CImgMergeFrame::OnImgCurPanePrevPage()
2156 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) - 1);
2157 UpdateLastCompareResult();
2160 void CImgMergeFrame::OnUpdateImgCurPanePrevPage(CCmdUI* pCmdUI)
2162 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) > 0);
2165 void CImgMergeFrame::OnImgCurPaneNextPage()
2167 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) + 1);
2168 UpdateLastCompareResult();
2171 void CImgMergeFrame::OnUpdateImgCurPaneNextPage(CCmdUI* pCmdUI)
2174 m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) <
2175 m_pImgMergeWindow->GetPageCount(m_pImgMergeWindow->GetActivePane()) - 1);
2178 void CImgMergeFrame::OnImgUseBackColor()
2180 bool bUseBackColor = !m_pImgMergeWindow->GetUseBackColor();
2183 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
2184 CColorDialog dialog(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue));
2185 static DWORD dwCustColors[16];
2186 Options::CustomColors::Load(GetOptionsMgr(), dwCustColors);
2187 dialog.m_cc.lpCustColors = dwCustColors;
2188 if (dialog.DoModal() == IDOK)
2190 COLORREF clrBackColor = dialog.GetColor();
2191 RGBQUAD backColor1 = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
2192 m_pImgMergeWindow->SetBackColor(backColor1);
2193 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2198 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2203 void CImgMergeFrame::OnUpdateImgUseBackColor(CCmdUI* pCmdUI)
2205 pCmdUI->SetCheck(m_pImgMergeWindow->GetUseBackColor() ? 1 : 0);
2208 void CImgMergeFrame::OnImgVectorImageScaling(UINT nId)
2210 m_pImgMergeWindow->SetVectorImageZoomRatio(
2211 static_cast<float>(pow(2.0f, int(nId - ID_IMG_VECTORIMAGESCALING_100))));
2215 void CImgMergeFrame::OnUpdateImgVectorImageScaling(CCmdUI* pCmdUI)
2217 pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_VECTORIMAGESCALING_100)) == m_pImgMergeWindow->GetVectorImageZoomRatio());
2220 void CImgMergeFrame::OnImgCompareExtractedText()
2224 for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); ++nBuffer)
2226 BSTR bstr = m_pImgMergeWindow->ExtractTextFromImage(nBuffer,
2227 m_pImgMergeWindow->GetCurrentPage(nBuffer),
2228 static_cast<IImgMergeWindow::OCR_RESULT_TYPE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OCR_RESULT_TYPE)));
2231 text[nBuffer].assign(bstr, SysStringLen(bstr));
2232 desc[nBuffer] = m_strDesc[nBuffer].empty() ?
2233 paths::FindFileName(m_filePaths[nBuffer]) : m_strDesc[nBuffer];
2234 SysFreeString(bstr);
2237 GetMainFrame()->ShowTextMergeDoc(m_pDirDoc, m_filePaths.GetSize(), text, desc, _T(".yaml"));
2240 bool CImgMergeFrame::GenerateReport(const String& sFileName) const
2242 return GenerateReport(sFileName, true);
2246 * @brief Generate report from file compare results.
2248 bool CImgMergeFrame::GenerateReport(const String& sFileName, bool allPages) const
2250 String imgdir_full, imgdir, path, name, ext;
2251 String imgfilepath[3];
2252 std::vector<std::array<String, 3>> diffimg_filename;
2253 paths::SplitFilename(sFileName, &path, &name, &ext);
2254 imgdir_full = paths::ConcatPath(path, name) + _T(".files");
2255 imgdir = paths::FindFileName(imgdir_full);
2256 paths::CreateIfNeeded(imgdir_full);
2259 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2260 curPages[pane] = m_pImgMergeWindow->GetCurrentPage(pane);
2263 diffimg_filename.resize(m_pImgMergeWindow->GetMaxPageCount());
2264 for (int page = 0; page < m_pImgMergeWindow->GetMaxPageCount(); ++page)
2266 m_pImgMergeWindow->SetCurrentPageAll(page);
2267 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2269 imgfilepath[pane] = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
2270 const int curPage = m_pImgMergeWindow->GetCurrentPage(pane) + 1;
2271 diffimg_filename[page][pane] = strutils::format(_T("%s/%d_%d.png"),
2272 imgdir, pane + 1, curPage);
2273 m_pImgMergeWindow->SaveDiffImageAs(pane,
2274 ucr::toUTF16(strutils::format(_T("%s\\%d_%d.png"),
2275 imgdir_full, pane + 1, curPage)).c_str());
2281 diffimg_filename.resize(1);
2282 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2284 imgfilepath[pane] = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
2285 const int curPage = m_pImgMergeWindow->GetCurrentPage(pane) + 1;
2286 diffimg_filename[0][pane] = strutils::format(_T("%s/%d_%d.png"),
2287 imgdir, pane + 1, curPage);
2288 m_pImgMergeWindow->SaveDiffImageAs(pane,
2289 ucr::toUTF16(strutils::format(_T("%s\\%d_%d.png"),
2290 imgdir_full, pane + 1, curPage)).c_str());
2293 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2294 m_pImgMergeWindow->SetCurrentPage(pane, curPages[pane]);
2297 if (!file.Open(sFileName, _T("wt")))
2299 String errMsg = GetSysError(GetLastError());
2300 String msg = strutils::format_string1(
2301 _("Error creating the report:\n%1"), errMsg);
2302 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
2306 file.SetCodepage(ucr::CP_UTF_8);
2309 _T("<!DOCTYPE html>\n")
2312 _T("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n")
2313 _T("<title>WinMerge Image Compare Report</title>\n")
2314 _T("<style type=\"text/css\">\n")
2315 _T("table { table-layout: fixed; width: 100%; height: 100%; border-collapse: collapse; }\n")
2316 _T("th {position: sticky; top: 0;}\n")
2317 _T("td,th { border: solid 1px black; }\n")
2318 _T(".title { color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
2319 _T(".img { overflow: scroll; text-align: center; }\n")
2325 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2326 file.WriteString(strutils::format(_T("<th class=\"title\">%s</th>\n"), imgfilepath[pane]));
2327 file.WriteString(_T("</tr>\n"));
2328 for (const auto filenames: diffimg_filename)
2332 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
2334 strutils::format(_T("<td><div class=\"img\"><img src=\"%s\" alt=\"%s\"></div></td>\n"),
2335 filenames[pane], filenames[pane]));
2347 * @brief Generate report from file compare results.
2349 void CImgMergeFrame::OnToolsGenerateReport()
2352 BOOL allPages = true;
2354 #if NTDDI_VERSION >= NTDDI_VISTA
2355 CFileDialog dlg(FALSE, _T("htm"), nullptr,
2356 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
2357 _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||").c_str());
2358 dlg.AddCheckButton(1001, _("All pages").c_str(), true);
2359 if (dlg.DoModal() != IDOK)
2361 dlg.GetCheckButtonState(1001, allPages);
2362 s = dlg.GetPathName();
2365 if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
2369 CWaitCursor waitstatus;
2370 if (GenerateReport(s, allPages))
2371 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
2374 void CImgMergeFrame::OnRefresh()
2376 if (UpdateDiffItem(m_pDirDoc) == 0)
2378 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
2379 [](const tchar_t* msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
2383 void CImgMergeFrame::OnDropFiles(int pane, const std::vector<String>& files)
2385 if (files.size() > 1 || paths::IsDirectory(files[0]))
2387 GetMainFrame()->GetDropHandler()->GetCallback()(files);
2391 ChangeFile(pane, files[0]);
2394 void CImgMergeFrame::OnSetFocus(CWnd* pNewWnd)
2396 if (m_nActivePane != -1)
2397 m_pImgMergeWindow->SetActivePane(m_nActivePane);
2402 * @brief Open help from mainframe when user presses F1
2404 void CImgMergeFrame::OnHelp()
2406 theApp.ShowHelp(ImgMergeFrameHelpLocation);