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 "FileLocation.h"
31 #include "Constants.h"
32 #include "DropHandler.h"
39 /** @brief Location for image compare specific help to open. */
40 static const TCHAR ImgMergeFrameHelpLocation[] = _T("::/htmlhelp/Compare_images.html");
42 /////////////////////////////////////////////////////////////////////////////
45 IMPLEMENT_DYNCREATE(CImgMergeFrame, CMergeFrameCommon)
47 BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
48 //{{AFX_MSG_MAP(CImgMergeFrame)
54 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
55 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
56 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
57 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
58 ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
59 ON_COMMAND(ID_FILE_SAVE_MIDDLE, OnFileSaveMiddle)
60 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
61 ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
62 ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
63 ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS_MIDDLE, OnUpdateFileSaveAsMiddle)
64 ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
65 ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
66 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
67 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
68 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
69 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
70 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
71 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
72 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
73 ON_COMMAND(ID_RESCAN, OnFileReload)
74 ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnFileRecompareAs)
75 ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnUpdateFileRecompareAs)
76 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnWindowChangePane)
77 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
78 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
79 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
80 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
81 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
82 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
83 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
84 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
85 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
86 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
87 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
88 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
89 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
90 ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
91 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
92 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
93 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
94 ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
95 ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
96 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
97 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
98 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
99 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
100 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
101 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
102 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
103 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
104 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
105 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
106 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
107 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
108 ON_COMMAND(ID_L2R, OnL2r)
109 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
110 ON_COMMAND(ID_R2L, OnR2l)
111 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
112 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
113 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
114 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
115 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
116 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
117 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
118 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
119 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
120 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
121 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
122 ON_COMMAND(ID_IMG_VIEWDIFFERENCES, OnImgViewDifferences)
123 ON_UPDATE_COMMAND_UI(ID_IMG_VIEWDIFFERENCES, OnUpdateImgViewDifferences)
124 ON_COMMAND_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnImgZoom)
125 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnUpdateImgZoom)
126 ON_COMMAND_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnImgOverlayMode)
127 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnUpdateImgOverlayMode)
128 ON_COMMAND_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnImgDraggingMode)
129 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnUpdateImgDraggingMode)
130 ON_COMMAND_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnImgDiffBlockSize)
131 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnUpdateImgDiffBlockSize)
132 ON_COMMAND_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnImgThreshold)
133 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnUpdateImgThreshold)
134 ON_COMMAND_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnImgInsertionDeletionDetectionMode)
135 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnUpdateImgInsertionDeletionDetectionMode)
136 ON_COMMAND(ID_IMG_PREVPAGE, OnImgPrevPage)
137 ON_UPDATE_COMMAND_UI(ID_IMG_PREVPAGE, OnUpdateImgPrevPage)
138 ON_COMMAND(ID_IMG_NEXTPAGE, OnImgNextPage)
139 ON_UPDATE_COMMAND_UI(ID_IMG_NEXTPAGE, OnUpdateImgNextPage)
140 ON_COMMAND(ID_IMG_CURPANE_PREVPAGE, OnImgCurPanePrevPage)
141 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_PREVPAGE, OnUpdateImgCurPanePrevPage)
142 ON_COMMAND(ID_IMG_CURPANE_NEXTPAGE, OnImgCurPaneNextPage)
143 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_NEXTPAGE, OnUpdateImgCurPaneNextPage)
144 ON_COMMAND(ID_IMG_USEBACKCOLOR, OnImgUseBackColor)
145 ON_UPDATE_COMMAND_UI(ID_IMG_USEBACKCOLOR, OnUpdateImgUseBackColor)
146 ON_COMMAND_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnImgVectorImageScaling)
147 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnUpdateImgVectorImageScaling)
148 ON_COMMAND(ID_IMG_COMPARE_EXTRACTED_TEXT, OnImgCompareExtractedText)
149 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
150 ON_COMMAND(ID_REFRESH, OnRefresh)
152 ON_COMMAND(ID_HELP, OnHelp)
156 CMenu CImgMergeFrame::menu;
158 /////////////////////////////////////////////////////////////////////////////
159 // CImgMergeFrame construction/destruction
161 CImgMergeFrame::CImgMergeFrame()
162 : CMergeFrameCommon(IDI_EQUALIMAGE, IDI_NOTEQUALIMAGE)
164 , m_bAutoMerged(false)
165 , m_pImgMergeWindow(nullptr)
166 , m_pImgToolWindow(nullptr)
167 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
173 CImgMergeFrame::~CImgMergeFrame()
175 if (m_pDirDoc != nullptr)
177 m_pDirDoc->MergeDocClosing(this);
181 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
182 if (hModule != nullptr)
184 bool (*pfnWinIMerge_DestroyWindow)(IImgMergeWindow *) =
185 (bool (*)(IImgMergeWindow *))GetProcAddress(hModule, "WinIMerge_DestroyWindow");
186 bool (*pfnWinIMerge_DestroyToolWindow)(IImgToolWindow *) =
187 (bool (*)(IImgToolWindow *))GetProcAddress(hModule, "WinIMerge_DestroyToolWindow");
188 if (pfnWinIMerge_DestroyWindow != nullptr && pfnWinIMerge_DestroyToolWindow != nullptr)
190 if (m_pImgMergeWindow != nullptr)
191 pfnWinIMerge_DestroyWindow(m_pImgMergeWindow);
192 if (m_pImgToolWindow != nullptr)
193 pfnWinIMerge_DestroyToolWindow(m_pImgToolWindow);
194 m_pImgMergeWindow = nullptr;
195 m_pImgToolWindow = nullptr;
200 bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent)
202 int nNormalBuffer = 0;
203 for (int pane = 0; pane < nFiles; ++pane)
205 m_filePaths.SetPath(pane, fileloc[pane].filepath);
206 m_bRO[pane] = bRO[pane];
207 m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
208 if (fileloc[pane].filepath.empty())
209 m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
212 m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
218 LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
219 ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
221 if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
224 int nCmdShow = SW_SHOW;
225 if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
226 nCmdShow = SW_SHOWMAXIMIZED;
227 ShowWindow(nCmdShow);
228 BringToTop(nCmdShow);
230 GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
232 if (nNormalBuffer > 0)
235 UpdateDiffItem(m_pDirDoc);
237 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
238 m_pImgMergeWindow->FirstDiff();
243 void CImgMergeFrame::MoveOnLoad(int nPane, int)
247 nPane = GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE);
248 if (nPane < 0 || nPane >= m_pImgMergeWindow->GetPaneCount())
252 m_pImgMergeWindow->SetActivePane(nPane);
255 void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
257 if (!PromptAndSaveIfNeeded(true))
260 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
261 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
263 m_filePaths[nBuffer] = path;
264 m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
265 m_strDesc[nBuffer] = _T("");
267 if (m_filePaths.GetSize() == 2)
268 m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
270 m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
272 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
274 m_fileInfo[pane].Update(m_filePaths[pane]);
276 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
277 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
280 UpdateHeaderPath(nBuffer);
281 UpdateLastCompareResult();
284 bool CImgMergeFrame::IsModified() const
286 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
287 if (m_pImgMergeWindow->IsModified(pane))
292 void CImgMergeFrame::DoAutoMerge(int dstPane)
294 int autoMergedCount = m_pImgMergeWindow->CopyDiff3Way(dstPane);
295 if (autoMergedCount > 0)
296 m_bAutoMerged = true;
298 // move to first conflict
299 m_pImgMergeWindow->FirstConflict();
302 strutils::format_string2(
303 _T("The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"),
304 strutils::format(_T("%d"), autoMergedCount),
305 strutils::format(_T("%d"), m_pImgMergeWindow->GetConflictCount())).c_str(),
310 * @brief DirDoc gives us its identity just after it creates us
312 void CImgMergeFrame::SetDirDoc(CDirDoc * pDirDoc)
314 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
318 IMergeDoc::FileChange CImgMergeFrame::IsFileChangedOnDisk(int pane) const
321 if (!dfi.Update(m_filePaths[pane]))
322 return FileChange::Removed;
324 if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
325 tolerance = SmallTimeDiff; // From MainFrm.h
326 int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
327 if (timeDiff < 0) timeDiff = -timeDiff;
328 if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
329 return FileChange::Changed;
330 return FileChange::NoChange;
333 void CImgMergeFrame::CheckFileChanged(void)
335 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
337 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
339 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]);
340 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING) == IDYES)
350 * @brief Create a status bar to be associated with a heksedit control
352 void CImgMergeFrame::CreateImgWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
354 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
355 wndStatusBar.SetIndicators(0, 1);
356 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
357 wndStatusBar.SetParent(this);
358 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
361 void CImgMergeFrame::OnChildPaneEvent(const IImgMergeWindow::Event& evt)
363 if (evt.eventType == IImgMergeWindow::KEYDOWN)
365 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
370 ::SendMessage(pFrame->m_pImgMergeWindow->GetPaneHWND(evt.pane), WM_VSCROLL, evt.keycode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0);
376 if (::GetAsyncKeyState(VK_SHIFT) & 0x8000)
378 int nActivePane = pFrame->m_pImgMergeWindow->GetActivePane();
379 int m = (::GetAsyncKeyState(VK_CONTROL) & 0x8000) ? 8 : 1;
380 int dx = (-(evt.keycode == VK_LEFT) + (evt.keycode == VK_RIGHT)) * m;
381 int dy = (-(evt.keycode == VK_UP) + (evt.keycode == VK_DOWN)) * m;
382 pFrame->m_pImgMergeWindow->AddImageOffset(nActivePane, dx, dy);
388 /* if (evt.eventType == IImgMergeWindow::CONTEXTMENU)
390 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
392 menu.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
393 BCMenu* pPopup = (BCMenu *)menu.GetSubMenu(0);
394 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
395 evt.x, evt.y, AfxGetMainWnd());
401 * @brief returns true if WinIMergeLib.dll is loadable
403 bool CImgMergeFrame::IsLoadable()
405 static HMODULE hModule;
406 if (hModule == nullptr)
408 hModule = LoadLibraryW(L"WinIMerge\\WinIMergeLib.dll");
409 if (hModule == nullptr)
416 * @brief Create the splitter, the filename bar, the status bar, and the two views
418 BOOL CImgMergeFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
419 CCreateContext* pContext)
424 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
425 if (hModule == nullptr)
428 IImgMergeWindow * (*pfnWinIMerge_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
429 (IImgMergeWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinIMerge_CreateWindow");
430 if (pfnWinIMerge_CreateWindow == nullptr ||
431 (m_pImgMergeWindow = pfnWinIMerge_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
433 FreeLibrary(hModule);
437 COLORSETTINGS colors;
438 Options::DiffColors::Load(GetOptionsMgr(), colors);
439 m_pImgMergeWindow->SetDiffColor(colors.clrDiff);
440 m_pImgMergeWindow->SetDiffDeletedColor(colors.clrDiffDeleted);
441 m_pImgMergeWindow->SetSelDiffColor(colors.clrSelDiff);
442 m_pImgMergeWindow->SetSelDiffDeletedColor(colors.clrSelDiffDeleted);
443 m_pImgMergeWindow->AddEventListener(OnChildPaneEvent, this);
447 if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
449 bResult = m_pImgMergeWindow->NewImages(m_filePaths.GetSize(), 1, 256, 256);
453 if (m_filePaths.GetSize() == 2)
454 bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
456 bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
459 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
461 m_fileInfo[pane].Update(m_filePaths[pane]);
463 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
464 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
467 // Merge frame has also a dockable bar at the very left
468 // This is not the client area, but we create it now because we want
469 // to use the CCreateContext
470 String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
471 if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
473 TRACE0("Failed to create LocationBar\n");
477 IImgToolWindow * (*pfnWinIMerge_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *) =
478 (IImgToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *pImgMergeWindow))GetProcAddress(hModule, "WinIMerge_CreateToolWindow");
479 if (pfnWinIMerge_CreateToolWindow == nullptr ||
480 (m_pImgToolWindow = pfnWinIMerge_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pImgMergeWindow)) == nullptr)
485 m_pImgToolWindow->Translate(TranslateLocationPane);
487 m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
492 void CImgMergeFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
494 swprintf_s(dst, dstbufsize, L"%s", tr("ImgMergeFrame|LocationPane", ucr::toUTF8(org)).c_str());
497 /////////////////////////////////////////////////////////////////////////////
498 // CImgMergeFrame message handlers
500 int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
502 if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
505 EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
507 CMergeFrameCommon::RemoveBarBorder();
509 // Merge frame has a header bar at top
510 if (!m_wndFilePathBar.Create(this))
512 TRACE0("Failed to create dialog bar\n");
513 return -1; // fail to create
516 m_wndFilePathBar.SetPaneCount(m_pImgMergeWindow->GetPaneCount());
517 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) { m_pImgMergeWindow->SetActivePane(pane); });
519 // Merge frame also has a dockable bar at the very left
520 // created in OnCreateClient
521 m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
522 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
523 m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
524 DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
526 for (int nPane = 0; nPane < m_pImgMergeWindow->GetPaneCount(); nPane++)
528 m_pImgMergeWindow->SetReadOnly(nPane, m_bRO[nPane]);
530 m_wndFilePathBar.SetActive(nPane, FALSE);
531 CreateImgWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pImgMergeWindow->GetPaneHWND(nPane)));
532 UpdateHeaderPath(nPane);
535 CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
536 m_rectBorder.bottom = size.cy;
538 CDockState pDockState;
539 pDockState.LoadState(_T("Settings-ImgMergeFrame"));
540 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
541 SetDockState(pDockState);
542 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
543 m_wndLocationBar.LoadState(_T("Settings-ImgMergeFrame"));
549 * @brief We must use this function before a call to SetDockState
551 * @note Without this, SetDockState will assert or crash if a bar from the
552 * CDockState is missing in the current CMergeEditFrame.
553 * The bars are identified with their ID. This means the missing bar bug is triggered
554 * when we run WinMerge after changing the ID of a bar.
556 bool CImgMergeFrame::EnsureValidDockState(CDockState& state)
558 for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
560 bool barIsCorrect = true;
561 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
562 if (pInfo == nullptr)
563 barIsCorrect = false;
566 if (!pInfo->m_bFloating)
568 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
569 if (pInfo->m_pBar == nullptr)
570 barIsCorrect = false; //toolbar id's probably changed
575 state.m_arrBarInfo.RemoveAt(i);
581 * @brief Save the window's position, free related resources, and destroy the window
583 BOOL CImgMergeFrame::DestroyWindow()
589 return CMergeFrameCommon::DestroyWindow();
592 void CImgMergeFrame::LoadOptions()
594 m_pImgMergeWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_IMG_SHOWDIFFERENCES));
595 m_pImgMergeWindow->SetOverlayMode(static_cast<IImgMergeWindow::OVERLAY_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYMOVE)));
596 m_pImgMergeWindow->SetOverlayAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYALPHA) / 100.0);
597 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DRAGGING_MODE)));
598 m_pImgMergeWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_IMG_ZOOM) / 1000.0);
599 m_pImgMergeWindow->SetUseBackColor(GetOptionsMgr()->GetBool(OPT_CMP_IMG_USEBACKCOLOR));
600 COLORREF clrBackColor = GetOptionsMgr()->GetInt(OPT_CMP_IMG_BACKCOLOR);
601 RGBQUAD backColor = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
602 m_pImgMergeWindow->SetBackColor(backColor);
603 m_pImgMergeWindow->SetDiffBlockSize(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFBLOCKSIZE));
604 m_pImgMergeWindow->SetDiffColorAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFCOLORALPHA) / 100.0);
605 m_pImgMergeWindow->SetColorDistanceThreshold(GetOptionsMgr()->GetInt(OPT_CMP_IMG_THRESHOLD) / 1000.0);
606 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE)));
607 m_pImgMergeWindow->SetVectorImageZoomRatio(GetOptionsMgr()->GetInt(OPT_CMP_IMG_VECTOR_IMAGE_ZOOM_RATIO) / 1000.0f);
610 void CImgMergeFrame::SaveOptions()
612 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_SHOWDIFFERENCES, m_pImgMergeWindow->GetShowDifferences());
613 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYMOVE, m_pImgMergeWindow->GetOverlayMode());
614 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYALPHA, static_cast<int>(m_pImgMergeWindow->GetOverlayAlpha() * 100));
615 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DRAGGING_MODE, static_cast<int>(m_pImgMergeWindow->GetDraggingMode()));
616 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_ZOOM, static_cast<int>(m_pImgMergeWindow->GetZoom() * 1000));
617 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_USEBACKCOLOR, m_pImgMergeWindow->GetUseBackColor());
618 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
619 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_BACKCOLOR, static_cast<int>(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue)));
620 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFBLOCKSIZE, m_pImgMergeWindow->GetDiffBlockSize());
621 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFCOLORALPHA, static_cast<int>(m_pImgMergeWindow->GetDiffColorAlpha() * 100.0));
622 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_THRESHOLD, static_cast<int>(m_pImgMergeWindow->GetColorDistanceThreshold() * 1000));
623 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE, static_cast<int>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode()));
624 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_VECTOR_IMAGE_ZOOM_RATIO, static_cast<int>(m_pImgMergeWindow->GetVectorImageZoomRatio() * 1000));
627 * @brief Save coordinates of the frame, splitters, and bars
629 * @note Do not save the maximized/restored state here. We are interested
630 * in the state of the active frame, and maybe this frame is not active
632 void CImgMergeFrame::SavePosition()
637 // save the bars layout
638 // save docking positions and sizes
639 CDockState m_pDockState;
640 GetDockState(m_pDockState);
641 m_pDockState.SaveState(_T("Settings-ImgMergeFrame"));
642 // for the dimensions of the diff pane, use the CSizingControlBar save
643 m_wndLocationBar.SaveState(_T("Settings-ImgMergeFrame"));
646 void CImgMergeFrame::SaveActivePane()
648 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pImgMergeWindow->GetActivePane());
651 void CImgMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
653 CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
657 GetMainFrame()->PostMessage(WM_USER + 1);
661 void CImgMergeFrame::OnClose()
663 // Allow user to cancel closing
664 if (!PromptAndSaveIfNeeded(true))
667 // clean up pointers.
668 CMergeFrameCommon::OnClose();
670 GetMainFrame()->ClearStatusbarItemCount();
673 void CImgMergeFrame::OnDestroy()
675 if (!m_pImgMergeWindow)
677 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
678 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
681 bool CImgMergeFrame::DoFileSave(int pane)
683 if (m_pImgMergeWindow->IsModified(pane))
685 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED)
689 String filename = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
690 bool bApplyToAll = false;
691 if (theApp.HandleReadonlySave(filename, false, bApplyToAll) == IDCANCEL)
693 theApp.CreateBackup(false, filename);
694 if (!m_pImgMergeWindow->SaveImage(pane))
696 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());
697 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
699 return DoFileSaveAs(pane);
703 UpdateDiffItem(m_pDirDoc);
704 m_fileInfo[pane].Update(m_filePaths[pane]);
709 bool CImgMergeFrame::DoFileSaveAs(int pane)
711 const String &path = m_filePaths.GetPath(pane);
715 title = _("Save Left File As");
716 else if (pane == m_pImgMergeWindow->GetPaneCount() - 1)
717 title = _("Save Right File As");
719 title = _("Save Middle File As");
721 if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
723 std::wstring filename = ucr::toUTF16(strPath);
724 if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
726 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());
727 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
734 // We are saving scratchpad (unnamed file)
735 m_nBufferType[pane] = BUFFERTYPE::UNNAMED_SAVED;
736 m_strDesc[pane].erase();
739 m_filePaths.SetPath(pane, strPath);
740 UpdateDiffItem(m_pDirDoc);
741 m_fileInfo[pane].Update(m_filePaths[pane]);
742 UpdateHeaderPath(pane);
748 * @brief Saves both files
750 void CImgMergeFrame::OnFileSave()
752 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
757 * @brief Called when "Save" item is updated
759 void CImgMergeFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
761 pCmdUI->Enable(IsModified());
765 * @brief Saves left-side file
767 void CImgMergeFrame::OnFileSaveLeft()
773 * @brief Called when "Save middle (...)" item is updated
775 void CImgMergeFrame::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
777 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
781 * @brief Saves middle-side file
783 void CImgMergeFrame::OnFileSaveMiddle()
789 * @brief Saves right-side file
791 void CImgMergeFrame::OnFileSaveRight()
793 DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
797 * @brief Called when "Save middle (as...)" item is updated
799 void CImgMergeFrame::OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI)
801 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
805 * @brief Saves left-side file with name asked
807 void CImgMergeFrame::OnFileSaveAsLeft()
813 * @brief Saves middle-side file with name asked
815 void CImgMergeFrame::OnFileSaveAsMiddle()
821 * @brief Saves right-side file with name asked
823 void CImgMergeFrame::OnFileSaveAsRight()
825 DoFileSaveAs(m_pImgMergeWindow->GetPaneCount() - 1);
829 * @brief Reloads the opened files
831 void CImgMergeFrame::OnFileReload()
833 if (!PromptAndSaveIfNeeded(true))
835 m_pImgMergeWindow->ReloadImages();
836 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
837 m_fileInfo[pane].Update(m_filePaths[pane]);
840 void CImgMergeFrame::OnFileClose()
846 * @brief Enable/disable left buffer read-only
848 void CImgMergeFrame::OnLeftReadOnly()
850 m_bRO[0] = !m_bRO[0];
851 m_pImgMergeWindow->SetReadOnly(0, m_bRO[0]);
855 * @brief Called when "Left read-only" item is updated
857 void CImgMergeFrame::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
859 pCmdUI->Enable(true);
860 pCmdUI->SetCheck(m_bRO[0]);
864 * @brief Enable/disable middle buffer read-only
866 void CImgMergeFrame::OnMiddleReadOnly()
868 if (m_pImgMergeWindow->GetPaneCount() == 3)
870 m_bRO[1] = !m_bRO[1];
871 m_pImgMergeWindow->SetReadOnly(1, m_bRO[1]);
876 * @brief Called when "Middle read-only" item is updated
878 void CImgMergeFrame::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
880 if (m_pImgMergeWindow->GetPaneCount() < 3)
882 pCmdUI->Enable(false);
886 pCmdUI->Enable(true);
887 pCmdUI->SetCheck(m_bRO[1]);
892 * @brief Enable/disable right buffer read-only
894 void CImgMergeFrame::OnRightReadOnly()
896 int pane = m_pImgMergeWindow->GetPaneCount() - 1;
897 m_bRO[pane] = !m_bRO[pane];
898 m_pImgMergeWindow->SetReadOnly(pane, m_bRO[pane]);
902 * @brief Called when "Right read-only" item is updated
904 void CImgMergeFrame::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
906 pCmdUI->Enable(true);
907 pCmdUI->SetCheck(m_pImgMergeWindow->GetReadOnly(m_pImgMergeWindow->GetPaneCount() - 1));
910 void CImgMergeFrame::OnFileRecompareAs(UINT nId)
912 FileLocation fileloc[3];
915 int nBuffers = m_filePaths.GetSize();
916 CDirDoc *pDirDoc = m_pDirDoc->GetMainView() ? m_pDirDoc :
917 static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
918 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
920 fileloc[nBuffer].setPath(m_filePaths[nBuffer]);
921 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
922 strDesc[nBuffer] = m_strDesc[nBuffer];
925 GetMainFrame()->ShowMergeDoc(nId, pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
928 void CImgMergeFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
930 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_XML);
933 void CImgMergeFrame::OnWindowChangePane()
935 m_pImgMergeWindow->SetActivePane((m_pImgMergeWindow->GetActivePane() + 1) % m_pImgMergeWindow->GetPaneCount());
939 * @brief Write path and filename to headerbar
940 * @note SetText() does not repaint unchanged text
942 void CImgMergeFrame::UpdateHeaderPath(int pane)
946 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
947 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
949 sText = m_strDesc[pane];
953 sText = m_filePaths.GetPath(pane);
954 if (m_pDirDoc != nullptr)
955 m_pDirDoc->ApplyDisplayRoot(pane, sText);
957 if (m_pImgMergeWindow->IsModified(pane))
958 sText.insert(0, _T("* "));
960 m_wndFilePathBar.SetText(pane, sText.c_str());
965 /// update splitting position for panels 1/2 and headerbar and statusbar
966 void CImgMergeFrame::UpdateHeaderSizes()
968 if (m_pImgMergeWindow != nullptr)
971 CRect rc, rcMergeWindow;
972 int nPaneCount = m_pImgMergeWindow->GetPaneCount();
974 ::GetWindowRect(m_pImgMergeWindow->GetHWND(), &rcMergeWindow);
975 ScreenToClient(rcMergeWindow);
976 if (!m_pImgMergeWindow->GetHorizontalSplit())
978 for (int pane = 0; pane < nPaneCount; pane++)
980 RECT rc1 = m_pImgMergeWindow->GetPaneWindowRect(pane);
981 w[pane] = rc1.right - rc1.left - 4;
982 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
987 for (int pane = 0; pane < nPaneCount; pane++)
988 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
991 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
993 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
995 // resize controls in header dialog bar
996 m_wndFilePathBar.Resize(w);
998 rc.left = rcMergeWindow.left;
999 rc.top = rc.bottom - m_rectBorder.bottom;
1001 for (int pane = 0; pane < nPaneCount; pane++)
1003 rc.right += w[pane] + 4 + 2;
1004 m_wndStatusBar[pane].MoveWindow(&rc);
1012 * @brief Update document filenames to title
1014 void CImgMergeFrame::SetTitle(LPCTSTR lpszTitle)
1016 String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc);
1017 CMergeFrameCommon::SetTitle(sTitle.c_str());
1018 if (m_hWnd != nullptr)
1019 SetWindowText(sTitle.c_str());
1022 void CImgMergeFrame::UpdateLastCompareResult()
1024 SetLastCompareResult(m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0);
1027 void CImgMergeFrame::UpdateAutoPaneResize()
1031 void CImgMergeFrame::UpdateSplitter()
1036 * @brief Update associated diff item
1038 int CImgMergeFrame::UpdateDiffItem(CDirDoc *pDirDoc)
1040 // If directory compare has results
1041 if (pDirDoc && pDirDoc->HasDiffs())
1043 const String &pathLeft = m_filePaths.GetLeft();
1044 const String &pathRight = m_filePaths.GetRight();
1045 CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
1047 // if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
1049 // DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
1050 // ::UpdateDiffItem(m_nBuffers, di, &ctxt);
1053 int result = m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0;
1054 SetLastCompareResult(result != 0);
1059 * @brief Asks and then saves modified files.
1061 * This function saves modified files. Dialog is shown for user to select
1062 * modified file(s) one wants to save or discard changed. Cancelling of
1063 * save operation is allowed unless denied by parameter. After successfully
1064 * save operation file statuses are updated to directory compare.
1065 * @param [in] bAllowCancel If false "Cancel" button is disabled.
1066 * @return true if user selected "OK" so next operation can be
1067 * executed. If false user choosed "Cancel".
1068 * @note If filename is empty, we assume scratchpads are saved,
1069 * so instead of filename, description is shown.
1070 * @todo If we have filename and description for file, what should
1071 * we do after saving to different filename? Empty description?
1072 * @todo Parameter @p bAllowCancel is always true in callers - can be removed.
1074 bool CImgMergeFrame::PromptAndSaveIfNeeded(bool bAllowCancel)
1076 bool bLModified = false, bMModified = false, bRModified = false;
1078 bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
1080 if (m_pImgMergeWindow->GetPaneCount() == 3)
1082 bLModified = m_pImgMergeWindow->IsModified(0);
1083 bMModified = m_pImgMergeWindow->IsModified(1);
1084 bRModified = m_pImgMergeWindow->IsModified(2);
1088 bLModified = m_pImgMergeWindow->IsModified(0);
1089 bRModified = m_pImgMergeWindow->IsModified(1);
1091 if (!bLModified && !bMModified && !bRModified)
1095 dlg.DoAskFor(bLModified, bMModified, bRModified);
1097 dlg.m_bDisableCancel = true;
1098 if (!m_filePaths.GetLeft().empty())
1100 if (theApp.m_strSaveAsPath.empty())
1101 dlg.m_sLeftFile = m_filePaths.GetLeft();
1103 dlg.m_sLeftFile = theApp.m_strSaveAsPath;
1106 dlg.m_sLeftFile = m_strDesc[0];
1107 if (m_pImgMergeWindow->GetPaneCount() == 3)
1109 if (!m_filePaths.GetMiddle().empty())
1111 if (theApp.m_strSaveAsPath.empty())
1112 dlg.m_sMiddleFile = m_filePaths.GetMiddle();
1114 dlg.m_sMiddleFile = theApp.m_strSaveAsPath;
1117 dlg.m_sMiddleFile = m_strDesc[1];
1119 if (!m_filePaths.GetRight().empty())
1121 if (theApp.m_strSaveAsPath.empty())
1122 dlg.m_sRightFile = m_filePaths.GetRight();
1124 dlg.m_sRightFile = theApp.m_strSaveAsPath;
1127 dlg.m_sRightFile = m_strDesc[m_pImgMergeWindow->GetPaneCount() - 1];
1129 if (dlg.DoModal() == IDOK)
1131 if (bLModified && dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
1133 bLSaveSuccess = DoFileSave(0);
1138 if (bMModified && dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
1140 bMSaveSuccess = DoFileSave(1);
1145 if (bRModified && dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
1147 bRSaveSuccess = DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
1157 // If file were modified and saving was successfull,
1158 // update status on dir view
1159 if ((bLModified && bLSaveSuccess) ||
1160 (bMModified && bMSaveSuccess) ||
1161 (bRModified && bRSaveSuccess))
1163 // If directory compare has results
1164 if (m_pDirDoc && m_pDirDoc->HasDiffs())
1173 /// Document commanding us to close
1174 bool CImgMergeFrame::CloseNow()
1176 // Allow user to cancel closing
1177 if (!PromptAndSaveIfNeeded(true))
1180 SavePosition(); // Save settings before closing!
1189 * @brief Update any resources necessary after a GUI language change
1191 void CImgMergeFrame::UpdateResources()
1193 m_pImgToolWindow->Translate(TranslateLocationPane);
1197 * @brief Handle some keys when in merging mode
1199 bool CImgMergeFrame::MergeModeKeyDown(MSG* pMsg)
1201 bool bHandled = false;
1203 // Allow default text selection when SHIFT pressed
1204 if (::GetAsyncKeyState(VK_SHIFT))
1207 // Allow default editor functions when CTRL pressed
1208 if (::GetAsyncKeyState(VK_CONTROL))
1211 // If we are in merging mode (merge with cursor keys)
1212 // handle some keys here
1213 switch (pMsg->wParam)
1238 * @brief Check for keyboard commands
1240 BOOL CImgMergeFrame::PreTranslateMessage(MSG* pMsg)
1242 if (pMsg->message == WM_KEYDOWN)
1244 // If we are in merging mode (merge with cursor keys)
1245 // handle some keys here
1246 if (theApp.GetMergingMode())
1248 bool bHandled = MergeModeKeyDown(pMsg);
1253 // Close window in response to VK_ESCAPE if user has allowed it from options
1254 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) != 0)
1256 PostMessage(WM_CLOSE, 0, 0);
1260 return CMergeFrameCommon::PreTranslateMessage(pMsg);
1263 void CImgMergeFrame::OnSize(UINT nType, int cx, int cy)
1265 CMergeFrameCommon::OnSize(nType, cx, cy);
1266 UpdateHeaderSizes();
1270 * @brief Synchronize control and status bar placements with splitter position,
1271 * update mod indicators, synchronize scrollbars
1273 void CImgMergeFrame::OnIdleUpdateCmdUI()
1275 if (IsWindowVisible())
1277 POINT pt = {-1, -1}, ptCursor;
1278 GetCursorPos(&ptCursor);
1279 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1282 ::GetWindowRect(m_pImgMergeWindow->GetPaneHWND(pane), &rc);
1283 if (PtInRect(&rc, ptCursor))
1284 pt = m_pImgMergeWindow->GetCursorPos(pane);
1288 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1289 color[pane] = m_pImgMergeWindow->GetPixelColor(pane, pt.x, pt.y);
1290 double colorDistance01 = m_pImgMergeWindow->GetColorDistance(0, 1, pt.x, pt.y);
1291 double colorDistance12 = 0;
1292 if (m_pImgMergeWindow->GetPaneCount() == 3)
1293 colorDistance12 = m_pImgMergeWindow->GetColorDistance(1, 2, pt.x, pt.y);
1295 int nActivePane = m_pImgMergeWindow->GetActivePane();
1296 if (nActivePane != -1)
1297 m_nActivePane = nActivePane;
1299 UpdateHeaderSizes();
1300 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1302 // Update mod indicators
1303 String ind = m_wndFilePathBar.GetText(pane);
1304 if (m_pImgMergeWindow->IsModified(pane) ? ind[0] != _T('*') : ind[0] == _T('*'))
1305 UpdateHeaderPath(pane);
1307 m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1310 if (m_pImgMergeWindow->ConvertToRealPos(pane, pt, ptReal))
1312 text += strutils::format(_("Pt: (%d, %d) RGBA: (%d, %d, %d, %d) "), ptReal.x, ptReal.y,
1313 color[pane].rgbRed, color[pane].rgbGreen, color[pane].rgbBlue, color[pane].rgbReserved);
1314 if (pane == 1 && m_pImgMergeWindow->GetPaneCount() == 3)
1315 text += strutils::format(_("Dist: %g, %g "), colorDistance01, colorDistance12);
1317 text += strutils::format(_("Dist: %g "), colorDistance01);
1319 if (m_pImgMergeWindow->IsRectangleSelectionVisible(pane))
1321 RECT rc = m_pImgMergeWindow->GetRectangleSelection(pane);
1322 text += strutils::format(_("Rc: (%d, %d) "), rc.right - rc.left, rc.bottom - rc.top);
1324 text += strutils::format(_("Page: %d/%d Zoom: %d%% %dx%dpx %dbpp"),
1325 m_pImgMergeWindow->GetCurrentPage(pane) + 1,
1326 m_pImgMergeWindow->GetPageCount(pane),
1327 static_cast<int>(m_pImgMergeWindow->GetZoom() * 100),
1328 m_pImgMergeWindow->GetImageWidth(pane),
1329 m_pImgMergeWindow->GetImageHeight(pane),
1330 m_pImgMergeWindow->GetImageBitsPerPixel(pane)
1332 m_wndStatusBar[pane].SetPaneText(0, text.c_str());
1334 UpdateLastCompareResult();
1336 CMergeFrameCommon::OnIdleUpdateCmdUI();
1340 * @brief Save pane sizes and positions when one of panes requests it.
1342 LRESULT CImgMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1348 void CImgMergeFrame::OnUpdateStatusNum(CCmdUI* pCmdUI)
1350 TCHAR sIdx[32] = { 0 };
1351 TCHAR sCnt[32] = { 0 };
1353 const int nDiffs = m_pImgMergeWindow->GetDiffCount();
1355 // Files are identical - show text "Identical"
1357 s = theApp.LoadString(IDS_IDENTICAL);
1359 // There are differences, but no selected diff
1360 // - show amount of diffs
1361 else if (m_pImgMergeWindow->GetCurrentDiffIndex() < 0)
1363 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1364 _itot_s(nDiffs, sCnt, 10);
1365 strutils::replace(s, _T("%1"), sCnt);
1368 // There are differences and diff selected
1369 // - show diff number and amount of diffs
1372 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1373 const int signInd = m_pImgMergeWindow->GetCurrentDiffIndex();
1374 _itot_s(signInd + 1, sIdx, 10);
1375 strutils::replace(s, _T("%1"), sIdx);
1376 _itot_s(nDiffs, sCnt, 10);
1377 strutils::replace(s, _T("%2"), sCnt);
1379 pCmdUI->SetText(s.c_str());
1383 * @brief Undo last action
1385 void CImgMergeFrame::OnEditUndo()
1387 m_pImgMergeWindow->Undo();
1388 if (!m_pImgMergeWindow->IsUndoable())
1389 m_bAutoMerged = false;
1390 UpdateLastCompareResult();
1394 * @brief Called when "Undo" item is updated
1396 void CImgMergeFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1398 pCmdUI->Enable(m_pImgMergeWindow->IsUndoable());
1402 * @brief Redo last action
1404 void CImgMergeFrame::OnEditRedo()
1406 m_pImgMergeWindow->Redo();
1407 UpdateLastCompareResult();
1411 * @brief Called when "Redo" item is updated
1413 void CImgMergeFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1415 pCmdUI->Enable(m_pImgMergeWindow->IsRedoable());
1419 * @brief Copy current selection to clipboard
1421 void CImgMergeFrame::OnEditCopy()
1423 m_pImgMergeWindow->Copy();
1427 * @brief Called when "Copy" item is updated
1429 void CImgMergeFrame::OnUpdateEditCopy(CCmdUI* pCmdUI)
1431 pCmdUI->Enable(m_pImgMergeWindow->IsCopyable());
1435 * @brief Cut current selection to clipboard
1437 void CImgMergeFrame::OnEditCut()
1439 m_pImgMergeWindow->Cut();
1443 * @brief Called when "Cut" item is updated
1445 void CImgMergeFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
1447 pCmdUI->Enable(m_pImgMergeWindow->IsCuttable());
1451 * @brief Paste image from clipboard
1453 void CImgMergeFrame::OnEditPaste()
1455 m_pImgMergeWindow->Paste();
1459 * @brief Called when "Paste" item is updated
1461 void CImgMergeFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)
1463 pCmdUI->Enable(m_pImgMergeWindow->IsPastable());
1467 * @brief Select entire image
1469 void CImgMergeFrame::OnEditSelectAll()
1471 m_pImgMergeWindow->SelectAll();
1475 * @brief Called when user selects View/Zoom In from menu.
1477 void CImgMergeFrame::OnViewZoomIn()
1479 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() + 0.1);
1483 * @brief Called when user selects View/Zoom Out from menu.
1485 void CImgMergeFrame::OnViewZoomOut()
1487 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() - 0.1);
1491 * @brief Called when user selects View/Zoom Normal from menu.
1493 void CImgMergeFrame::OnViewZoomNormal()
1495 m_pImgMergeWindow->SetZoom(1.0);
1499 * @brief Split panes vertically
1501 void CImgMergeFrame::OnViewSplitVertically()
1503 bool bSplitVertically = !m_pImgMergeWindow->GetHorizontalSplit();
1504 bSplitVertically = !bSplitVertically; // toggle
1505 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1506 m_pImgMergeWindow->SetHorizontalSplit(!bSplitVertically);
1510 * @brief Update "Split Vertically" UI items
1512 void CImgMergeFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
1514 pCmdUI->Enable(TRUE);
1515 pCmdUI->SetCheck(!m_pImgMergeWindow->GetHorizontalSplit());
1519 * @brief Go to first diff
1521 * Called when user selects "First Difference"
1522 * @sa CImgMergeFrame::SelectDiff()
1524 void CImgMergeFrame::OnFirstdiff()
1526 m_pImgMergeWindow->FirstDiff();
1530 * @brief Update "First diff" UI items
1532 void CImgMergeFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1534 OnUpdatePrevdiff(pCmdUI);
1538 * @brief Go to last diff
1540 void CImgMergeFrame::OnLastdiff()
1542 m_pImgMergeWindow->LastDiff();
1546 * @brief Update "Last diff" UI items
1548 void CImgMergeFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1550 OnUpdateNextdiff(pCmdUI);
1554 * @brief Go to next diff and select it.
1556 void CImgMergeFrame::OnNextdiff()
1558 if (m_pImgMergeWindow->GetCurrentDiffIndex() != m_pImgMergeWindow->GetDiffCount() - 1)
1559 m_pImgMergeWindow->NextDiff();
1560 else if (m_pImgMergeWindow->GetCurrentMaxPage() != m_pImgMergeWindow->GetMaxPageCount() - 1)
1562 if (AfxMessageBox(_("Do you want to move to the next page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1564 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1565 UpdateLastCompareResult();
1568 else if (m_pDirDoc != nullptr)
1569 m_pDirDoc->MoveToNextDiff(this);
1573 * @brief Update "Next diff" UI items
1575 void CImgMergeFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1578 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1 ||
1579 m_pImgMergeWindow->GetNextDiffIndex() >= 0 ||
1580 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1582 if (!enabled && m_pDirDoc != nullptr)
1583 enabled = m_pDirDoc->MoveableToNextDiff();
1585 pCmdUI->Enable(enabled);
1589 * @brief Go to previous diff and select it.
1591 void CImgMergeFrame::OnPrevdiff()
1593 if (m_pImgMergeWindow->GetCurrentDiffIndex() > 0)
1595 m_pImgMergeWindow->PrevDiff();
1597 else if (m_pImgMergeWindow->GetCurrentMaxPage() != 0)
1599 if (AfxMessageBox(_("Do you want to move to the previous page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1601 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1602 UpdateLastCompareResult();
1605 else if (m_pDirDoc != nullptr)
1606 m_pDirDoc->MoveToPrevDiff(this);
1610 * @brief Update "Previous diff" UI items
1612 void CImgMergeFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1615 m_pImgMergeWindow->GetCurrentMaxPage() > 0 ||
1616 m_pImgMergeWindow->GetPrevDiffIndex() >= 0 ||
1617 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1619 if (!enabled && m_pDirDoc != nullptr)
1620 enabled = m_pDirDoc->MoveableToPrevDiff();
1622 pCmdUI->Enable(enabled);
1626 * @brief Go to next conflict and select it.
1628 void CImgMergeFrame::OnNextConflict()
1630 m_pImgMergeWindow->NextConflict();
1634 * @brief Update "Next Conflict" UI items
1636 void CImgMergeFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1639 m_pImgMergeWindow->GetPaneCount() > 2 && (
1640 m_pImgMergeWindow->GetNextConflictIndex() >= 0 ||
1641 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1647 * @brief Go to previous diff and select it.
1649 void CImgMergeFrame::OnPrevConflict()
1651 m_pImgMergeWindow->PrevConflict();
1655 * @brief Update "Previous diff" UI items
1657 void CImgMergeFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1660 m_pImgMergeWindow->GetPaneCount() > 2 && (
1661 m_pImgMergeWindow->GetPrevConflictIndex() >= 0 ||
1662 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1667 void CImgMergeFrame::OnUpdateX2Y(CCmdUI* pCmdUI, int srcPane, int dstPane)
1669 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentDiffIndex() >= 0 &&
1670 srcPane >= 0 && srcPane <= m_pImgMergeWindow->GetPaneCount() &&
1671 dstPane >= 0 && dstPane <= m_pImgMergeWindow->GetPaneCount() &&
1676 void CImgMergeFrame::OnX2Y(int srcPane, int dstPane)
1678 m_pImgMergeWindow->CopyDiff(m_pImgMergeWindow->GetCurrentDiffIndex(), srcPane, dstPane);
1679 UpdateLastCompareResult();
1683 * @brief Copy diff from left pane to right pane
1685 void CImgMergeFrame::OnL2r()
1687 int srcPane = m_pImgMergeWindow->GetActivePane();
1688 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1689 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1692 int dstPane = srcPane + 1;
1693 OnX2Y(srcPane, dstPane);
1697 * @brief Called when "Copy to left" item is updated
1699 void CImgMergeFrame::OnUpdateL2r(CCmdUI* pCmdUI)
1701 int srcPane = m_pImgMergeWindow->GetActivePane();
1702 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1703 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1706 int dstPane = srcPane + 1;
1707 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1711 * @brief Copy diff from right pane to left pane
1713 void CImgMergeFrame::OnR2l()
1715 int srcPane = m_pImgMergeWindow->GetActivePane();
1718 int dstPane = srcPane - 1;
1719 OnX2Y(srcPane, dstPane);
1723 * @brief Called when "Copy to right" item is updated
1725 void CImgMergeFrame::OnUpdateR2l(CCmdUI* pCmdUI)
1727 int srcPane = m_pImgMergeWindow->GetActivePane();
1730 int dstPane = srcPane - 1;
1731 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1734 void CImgMergeFrame::OnCopyFromLeft()
1736 int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1739 int dstPane = srcPane + 1;
1740 OnX2Y(srcPane, dstPane);
1744 * @brief Called when "Copy from left" item is updated
1746 void CImgMergeFrame::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1748 int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1751 int dstPane = srcPane + 1;
1752 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1755 void CImgMergeFrame::OnCopyFromRight()
1757 int srcPane = m_pImgMergeWindow->GetActivePane() + 1;
1758 if (srcPane > m_pImgMergeWindow->GetPaneCount() - 1)
1759 srcPane = m_pImgMergeWindow->GetPaneCount() - 1;
1760 int dstPane = srcPane - 1;
1761 OnX2Y(srcPane, dstPane);
1765 * @brief Called when "Copy from right" item is updated
1767 void CImgMergeFrame::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1769 int srcPane = m_pImgMergeWindow->GetActivePane() + 1;
1770 if (srcPane > m_pImgMergeWindow->GetPaneCount() - 1)
1771 srcPane = m_pImgMergeWindow->GetPaneCount() - 1;
1772 int dstPane = srcPane - 1;
1773 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1777 * @brief Copy all diffs from right pane to left pane
1779 void CImgMergeFrame::OnAllLeft()
1781 int srcPane = m_pImgMergeWindow->GetActivePane();
1784 int dstPane = srcPane - 1;
1786 CWaitCursor waitstatus;
1788 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1789 UpdateLastCompareResult();
1793 * @brief Called when "Copy all to left" item is updated
1795 void CImgMergeFrame::OnUpdateAllLeft(CCmdUI* pCmdUI)
1797 int srcPane = m_pImgMergeWindow->GetActivePane();
1800 int dstPane = srcPane - 1;
1802 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1806 * @brief Copy all diffs from left pane to right pane
1808 void CImgMergeFrame::OnAllRight()
1810 int srcPane = m_pImgMergeWindow->GetActivePane();
1811 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1812 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1815 int dstPane = srcPane + 1;
1817 CWaitCursor waitstatus;
1819 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1820 UpdateLastCompareResult();
1824 * @brief Called when "Copy all to right" item is updated
1826 void CImgMergeFrame::OnUpdateAllRight(CCmdUI* pCmdUI)
1828 int srcPane = m_pImgMergeWindow->GetActivePane();
1829 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1830 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1833 int dstPane = srcPane + 1;
1835 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1839 * @brief Do Auto merge
1841 void CImgMergeFrame::OnAutoMerge()
1843 int dstPane = m_pImgMergeWindow->GetActivePane();
1845 // Check current pane is not readonly
1846 if (dstPane < 0 || IsModified() || m_bAutoMerged || m_bRO[dstPane])
1849 CWaitCursor waitstatus;
1851 DoAutoMerge(dstPane);
1855 * @brief Called when "Auto Merge" item is updated
1857 void CImgMergeFrame::OnUpdateAutoMerge(CCmdUI* pCmdUI)
1859 int dstPane = m_pImgMergeWindow->GetActivePane();
1861 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 &&
1862 dstPane >= 0 && !IsModified() && !m_bAutoMerged && !m_bRO[dstPane]);
1865 void CImgMergeFrame::OnImgViewDifferences()
1867 m_pImgMergeWindow->SetShowDifferences(!m_pImgMergeWindow->GetShowDifferences());
1871 void CImgMergeFrame::OnUpdateImgViewDifferences(CCmdUI* pCmdUI)
1873 pCmdUI->SetCheck(m_pImgMergeWindow->GetShowDifferences() ? 1 : 0);
1876 void CImgMergeFrame::OnImgZoom(UINT nId)
1878 m_pImgMergeWindow->SetZoom(pow(2.0, int(nId - ID_IMG_ZOOM_100)));
1882 void CImgMergeFrame::OnUpdateImgZoom(CCmdUI* pCmdUI)
1884 pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_ZOOM_100)) == m_pImgMergeWindow->GetZoom());
1887 void CImgMergeFrame::OnImgOverlayMode(UINT nId)
1889 if (nId == ID_IMG_OVERLAY_NONE)
1890 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_NONE);
1891 else if (nId == ID_IMG_OVERLAY_XOR)
1892 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_XOR);
1893 else if (nId == ID_IMG_OVERLAY_ALPHABLEND)
1894 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND);
1895 else if (nId == ID_IMG_OVERLAY_ALPHABLEND_ANIM)
1896 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND_ANIM);
1900 void CImgMergeFrame::OnUpdateImgOverlayMode(CCmdUI* pCmdUI)
1902 pCmdUI->SetRadio(static_cast<IImgMergeWindow::OVERLAY_MODE>(pCmdUI->m_nID - ID_IMG_OVERLAY_NONE) == m_pImgMergeWindow->GetOverlayMode());
1905 void CImgMergeFrame::OnImgDraggingMode(UINT nId)
1907 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(nId - ID_IMG_DRAGGINGMODE_NONE));
1911 void CImgMergeFrame::OnUpdateImgDraggingMode(CCmdUI* pCmdUI)
1913 pCmdUI->SetRadio(static_cast<IImgMergeWindow::DRAGGING_MODE>(pCmdUI->m_nID - ID_IMG_DRAGGINGMODE_NONE) == m_pImgMergeWindow->GetDraggingMode());
1916 void CImgMergeFrame::OnImgDiffBlockSize(UINT nId)
1918 m_pImgMergeWindow->SetDiffBlockSize(1 << (nId - ID_IMG_DIFFBLOCKSIZE_1));
1922 void CImgMergeFrame::OnUpdateImgDiffBlockSize(CCmdUI* pCmdUI)
1924 pCmdUI->SetRadio(1 << (pCmdUI->m_nID - ID_IMG_DIFFBLOCKSIZE_1) == m_pImgMergeWindow->GetDiffBlockSize() );
1927 void CImgMergeFrame::OnImgThreshold(UINT nId)
1929 if (nId == ID_IMG_THRESHOLD_0)
1930 m_pImgMergeWindow->SetColorDistanceThreshold(0.0);
1932 m_pImgMergeWindow->SetColorDistanceThreshold((1 << (nId - ID_IMG_THRESHOLD_2)) * 2);
1936 void CImgMergeFrame::OnUpdateImgThreshold(CCmdUI* pCmdUI)
1938 if (pCmdUI->m_nID == ID_IMG_THRESHOLD_0)
1939 pCmdUI->SetRadio(m_pImgMergeWindow->GetColorDistanceThreshold() == 0.0);
1941 pCmdUI->SetRadio((1 << (pCmdUI->m_nID - ID_IMG_THRESHOLD_2)) * 2 == m_pImgMergeWindow->GetColorDistanceThreshold() );
1944 void CImgMergeFrame::OnImgInsertionDeletionDetectionMode(UINT nId)
1946 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(nId - ID_IMG_INSERTIONDELETIONDETECTION_NONE));
1950 void CImgMergeFrame::OnUpdateImgInsertionDeletionDetectionMode(CCmdUI* pCmdUI)
1952 pCmdUI->SetRadio(static_cast<unsigned>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode() + ID_IMG_INSERTIONDELETIONDETECTION_NONE) == pCmdUI->m_nID);
1955 void CImgMergeFrame::OnImgPrevPage()
1957 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1958 UpdateLastCompareResult();
1961 void CImgMergeFrame::OnUpdateImgPrevPage(CCmdUI* pCmdUI)
1963 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentMaxPage() > 0);
1966 void CImgMergeFrame::OnImgNextPage()
1968 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1969 UpdateLastCompareResult();
1972 void CImgMergeFrame::OnUpdateImgNextPage(CCmdUI* pCmdUI)
1975 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1);
1978 void CImgMergeFrame::OnImgCurPanePrevPage()
1980 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) - 1);
1981 UpdateLastCompareResult();
1984 void CImgMergeFrame::OnUpdateImgCurPanePrevPage(CCmdUI* pCmdUI)
1986 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) > 0);
1989 void CImgMergeFrame::OnImgCurPaneNextPage()
1991 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) + 1);
1992 UpdateLastCompareResult();
1995 void CImgMergeFrame::OnUpdateImgCurPaneNextPage(CCmdUI* pCmdUI)
1998 m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) <
1999 m_pImgMergeWindow->GetPageCount(m_pImgMergeWindow->GetActivePane()) - 1);
2002 void CImgMergeFrame::OnImgUseBackColor()
2004 bool bUseBackColor = !m_pImgMergeWindow->GetUseBackColor();
2007 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
2008 CColorDialog dialog(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue));
2009 static DWORD dwCustColors[16];
2010 Options::CustomColors::Load(GetOptionsMgr(), dwCustColors);
2011 dialog.m_cc.lpCustColors = dwCustColors;
2012 if (dialog.DoModal() == IDOK)
2014 COLORREF clrBackColor = dialog.GetColor();
2015 RGBQUAD backColor1 = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
2016 m_pImgMergeWindow->SetBackColor(backColor1);
2017 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2022 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2027 void CImgMergeFrame::OnUpdateImgUseBackColor(CCmdUI* pCmdUI)
2029 pCmdUI->SetCheck(m_pImgMergeWindow->GetUseBackColor() ? 1 : 0);
2032 void CImgMergeFrame::OnImgVectorImageScaling(UINT nId)
2034 m_pImgMergeWindow->SetVectorImageZoomRatio(
2035 static_cast<float>(pow(2.0f, int(nId - ID_IMG_VECTORIMAGESCALING_100))));
2039 void CImgMergeFrame::OnUpdateImgVectorImageScaling(CCmdUI* pCmdUI)
2041 pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_VECTORIMAGESCALING_100)) == m_pImgMergeWindow->GetVectorImageZoomRatio());
2044 void CImgMergeFrame::OnImgCompareExtractedText()
2048 for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); ++nBuffer)
2050 BSTR bstr = m_pImgMergeWindow->ExtractTextFromImage(nBuffer,
2051 m_pImgMergeWindow->GetCurrentPage(nBuffer),
2052 static_cast<IImgMergeWindow::OCR_RESULT_TYPE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OCR_RESULT_TYPE)));
2055 text[nBuffer].assign(bstr, SysStringLen(bstr));
2056 desc[nBuffer] = m_strDesc[nBuffer].empty() ?
2057 paths::FindFileName(m_filePaths[nBuffer]) : m_strDesc[nBuffer];
2058 SysFreeString(bstr);
2061 GetMainFrame()->ShowTextMergeDoc(m_pDirDoc, m_filePaths.GetSize(), text, desc, _T(".yaml"));
2065 * @brief Generate report from file compare results.
2067 bool CImgMergeFrame::GenerateReport(const String& sFileName) const
2069 String imgdir_full, imgdir, imgfilepath[3], diffimg_filename[3], path, name, ext;
2070 paths::SplitFilename(sFileName, &path, &name, &ext);
2071 imgdir_full = paths::ConcatPath(path, name) + _T(".files");
2072 imgdir = paths::FindFileName(imgdir_full);
2073 paths::CreateIfNeeded(imgdir_full);
2074 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2076 imgfilepath[i] = ucr::toTString(m_pImgMergeWindow->GetFileName(i));
2077 diffimg_filename[i] = strutils::format(_T("%s/%d.png"), imgdir, i + 1);
2078 m_pImgMergeWindow->SaveDiffImageAs(i, ucr::toUTF16(strutils::format(_T("%s\\%d.png"), imgdir_full, i + 1)).c_str());
2082 if (!file.Open(sFileName, _T("wt")))
2084 String errMsg = GetSysError(GetLastError());
2085 String msg = strutils::format_string1(
2086 _("Error creating the report:\n%1"), errMsg);
2087 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
2091 file.SetCodepage(ucr::CP_UTF_8);
2094 _T("<!DOCTYPE html>\n")
2097 _T("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n")
2098 _T("<title>WinMerge Image Compare Report</title>\n")
2099 _T("<style type=\"text/css\">\n")
2100 _T("table { table-layout: fixed; width: 100%; height: 100%; border-collapse: collapse; }\n")
2101 _T("td,th { border: solid 1px black; }\n")
2102 _T(".title { color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
2103 _T(".img { overflow: scroll; text-align: center; }\n")
2109 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2110 file.WriteString(strutils::format(_T("<th class=\"title\">%s</th>\n"), imgfilepath[i]));
2114 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2116 strutils::format(_T("<td><div class=\"img\"><img src=\"%s\" alt=\"%s\"></div></td>\n"),
2117 diffimg_filename[i], diffimg_filename[i]));
2127 * @brief Generate report from file compare results.
2129 void CImgMergeFrame::OnToolsGenerateReport()
2134 if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
2137 if (GenerateReport(s))
2138 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
2141 void CImgMergeFrame::OnRefresh()
2143 if (UpdateDiffItem(m_pDirDoc) == 0)
2145 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
2146 [](LPCTSTR msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
2150 void CImgMergeFrame::OnDropFiles(int pane, const std::vector<String>& files)
2152 if (files.size() > 1 || paths::IsDirectory(files[0]))
2154 GetMainFrame()->GetDropHandler()->GetCallback()(files);
2158 ChangeFile(pane, files[0]);
2161 void CImgMergeFrame::OnSetFocus(CWnd* pNewWnd)
2163 if (m_nActivePane != -1)
2164 m_pImgMergeWindow->SetActivePane(m_nActivePane);
2169 * @brief Open help from mainframe when user presses F1
2171 void CImgMergeFrame::OnHelp()
2173 theApp.ShowHelp(ImgMergeFrameHelpLocation);