1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
22 * @file ImgMergeFrm.cpp
24 * @brief Implementation file for CImgMergeFrame
29 #include "ImgMergeFrm.h"
34 #include "OptionsDef.h"
35 #include "OptionsMgr.h"
36 #include "OptionsDiffColors.h"
37 #include "OptionsCustomColors.h"
39 #include "PathContext.h"
41 #include "FileOrFolderSelect.h"
43 #include "SaveClosingDlg.h"
44 #include "FileLocation.h"
45 #include "Constants.h"
46 #include "DropHandler.h"
53 /////////////////////////////////////////////////////////////////////////////
56 IMPLEMENT_DYNCREATE(CImgMergeFrame, CMergeFrameCommon)
58 BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
59 //{{AFX_MSG_MAP(CImgMergeFrame)
64 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
65 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
66 ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
67 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
68 ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
69 ON_COMMAND(ID_FILE_SAVE_MIDDLE, OnFileSaveMiddle)
70 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
71 ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
72 ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
73 ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS_MIDDLE, OnUpdateFileSaveAsMiddle)
74 ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
75 ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
76 ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
77 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
78 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
79 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
80 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
81 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
82 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
83 ON_COMMAND(ID_RESCAN, OnFileReload)
84 ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnFileRecompareAs)
85 ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_IMAGE, OnUpdateFileRecompareAs)
86 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnWindowChangePane)
87 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
88 ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
89 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
90 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
91 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
92 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
93 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
94 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
95 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
96 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
97 ON_COMMAND(ID_VIEW_SPLITVERTICALLY, OnViewSplitVertically)
98 ON_UPDATE_COMMAND_UI(ID_VIEW_SPLITVERTICALLY, OnUpdateViewSplitVertically)
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_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
116 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
117 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
118 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
119 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
120 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
121 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
122 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
123 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
124 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
125 ON_COMMAND(ID_IMG_VIEWDIFFERENCES, OnImgViewDifferences)
126 ON_UPDATE_COMMAND_UI(ID_IMG_VIEWDIFFERENCES, OnUpdateImgViewDifferences)
127 ON_COMMAND_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnImgZoom)
128 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnUpdateImgZoom)
129 ON_COMMAND_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnImgOverlayMode)
130 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnUpdateImgOverlayMode)
131 ON_COMMAND_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_ADJUST_OFFSET, OnImgDraggingMode)
132 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_ADJUST_OFFSET, OnUpdateImgDraggingMode)
133 ON_COMMAND_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnImgDiffBlockSize)
134 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnUpdateImgDiffBlockSize)
135 ON_COMMAND_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnImgThreshold)
136 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnUpdateImgThreshold)
137 ON_COMMAND_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnImgInsertionDeletionDetectionMode)
138 ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_INSERTIONDELETIONDETECTION_NONE, ID_IMG_INSERTIONDELETIONDETECTION_HORIZONTAL, OnUpdateImgInsertionDeletionDetectionMode)
139 ON_COMMAND(ID_IMG_PREVPAGE, OnImgPrevPage)
140 ON_UPDATE_COMMAND_UI(ID_IMG_PREVPAGE, OnUpdateImgPrevPage)
141 ON_COMMAND(ID_IMG_NEXTPAGE, OnImgNextPage)
142 ON_UPDATE_COMMAND_UI(ID_IMG_NEXTPAGE, OnUpdateImgNextPage)
143 ON_COMMAND(ID_IMG_CURPANE_PREVPAGE, OnImgCurPanePrevPage)
144 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_PREVPAGE, OnUpdateImgCurPanePrevPage)
145 ON_COMMAND(ID_IMG_CURPANE_NEXTPAGE, OnImgCurPaneNextPage)
146 ON_UPDATE_COMMAND_UI(ID_IMG_CURPANE_NEXTPAGE, OnUpdateImgCurPaneNextPage)
147 ON_COMMAND(ID_IMG_USEBACKCOLOR, OnImgUseBackColor)
148 ON_UPDATE_COMMAND_UI(ID_IMG_USEBACKCOLOR, OnUpdateImgUseBackColor)
149 ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
150 ON_COMMAND(ID_REFRESH, OnRefresh)
154 CMenu CImgMergeFrame::menu;
156 /////////////////////////////////////////////////////////////////////////////
157 // CImgMergeFrame construction/destruction
159 CImgMergeFrame::CImgMergeFrame()
160 : CMergeFrameCommon(IDI_EQUALIMAGE, IDI_NOTEQUALIMAGE)
162 , m_bAutoMerged(false)
163 , m_pImgMergeWindow(nullptr)
164 , m_pImgToolWindow(nullptr)
165 , m_nBufferType{BUFFER_NORMAL, BUFFER_NORMAL, BUFFER_NORMAL}
170 CImgMergeFrame::~CImgMergeFrame()
172 if (m_pDirDoc != nullptr)
174 m_pDirDoc->MergeDocClosing(this);
178 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
179 if (hModule != nullptr)
181 bool (*pfnWinIMerge_DestroyWindow)(IImgMergeWindow *) =
182 (bool (*)(IImgMergeWindow *))GetProcAddress(hModule, "WinIMerge_DestroyWindow");
183 bool (*pfnWinIMerge_DestroyToolWindow)(IImgToolWindow *) =
184 (bool (*)(IImgToolWindow *))GetProcAddress(hModule, "WinIMerge_DestroyToolWindow");
185 if (pfnWinIMerge_DestroyWindow != nullptr && pfnWinIMerge_DestroyToolWindow != nullptr)
187 if (m_pImgMergeWindow != nullptr)
189 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
190 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
191 pfnWinIMerge_DestroyWindow(m_pImgMergeWindow);
193 if (m_pImgToolWindow != nullptr)
194 pfnWinIMerge_DestroyToolWindow(m_pImgToolWindow);
195 m_pImgMergeWindow = nullptr;
196 m_pImgToolWindow = nullptr;
201 bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent)
204 for (int pane = 0; pane < nFiles; ++pane)
206 m_filePaths.SetPath(pane, fileloc[pane].filepath);
207 m_bRO[pane] = bRO[pane];
208 m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
209 m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFER_NORMAL : BUFFER_NORMAL_NAMED;
213 LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
214 ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
216 if (!CMDIChildWnd::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
219 int nCmdShow = SW_SHOW;
220 if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
221 nCmdShow = SW_SHOWMAXIMIZED;
222 ShowWindow(nCmdShow);
223 BringToTop(nCmdShow);
227 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
228 m_pImgMergeWindow->FirstDiff();
233 void CImgMergeFrame::MoveOnLoad(int nPane, int)
237 nPane = GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE);
238 if (nPane < 0 || nPane >= m_pImgMergeWindow->GetPaneCount())
242 m_pImgMergeWindow->SetActivePane(nPane);
245 void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
247 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
248 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
250 m_filePaths[nBuffer] = path;
251 m_nBufferType[nBuffer] = BUFFER_NORMAL;
252 m_strDesc[nBuffer] = _T("");
254 if (m_filePaths.GetSize() == 2)
255 m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
257 m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
259 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
261 m_fileInfo[pane].Update(m_filePaths[pane]);
263 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
264 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
267 UpdateHeaderPath(nBuffer);
268 UpdateLastCompareResult();
271 bool CImgMergeFrame::IsModified() const
273 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
274 if (m_pImgMergeWindow->IsModified(pane))
279 void CImgMergeFrame::DoAutoMerge(int dstPane)
281 int autoMergedCount = m_pImgMergeWindow->CopyDiff3Way(dstPane);
282 if (autoMergedCount > 0)
283 m_bAutoMerged = true;
285 // move to first conflict
286 m_pImgMergeWindow->FirstConflict();
289 strutils::format_string2(
290 _T("The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"),
291 strutils::format(_T("%d"), autoMergedCount),
292 strutils::format(_T("%d"), m_pImgMergeWindow->GetConflictCount())).c_str(),
297 * @brief DirDoc gives us its identity just after it creates us
299 void CImgMergeFrame::SetDirDoc(CDirDoc * pDirDoc)
301 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
305 bool CImgMergeFrame::IsFileChangedOnDisk(int pane) const
308 dfi.Update(m_filePaths[pane]);
310 if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
311 tolerance = SmallTimeDiff; // From MainFrm.h
312 int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
313 if (timeDiff < 0) timeDiff = -timeDiff;
314 if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
319 void CImgMergeFrame::CheckFileChanged(void)
321 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
323 if (IsFileChangedOnDisk(pane))
325 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]);
326 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING) == IDYES)
335 BOOL CImgMergeFrame::PreCreateWindow(CREATESTRUCT& cs)
337 CMDIChildWnd::PreCreateWindow(cs);
338 cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
343 * @brief Create a status bar to be associated with a heksedit control
345 void CImgMergeFrame::CreateImgWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
347 wndStatusBar.Create(pwndPane, WS_CHILD|WS_VISIBLE);
348 wndStatusBar.SetIndicators(0, 1);
349 wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
350 wndStatusBar.SetParent(this);
351 wndStatusBar.SetWindowPos(&wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
354 void CImgMergeFrame::OnChildPaneEvent(const IImgMergeWindow::Event& evt)
356 if (evt.eventType == IImgMergeWindow::KEYDOWN)
358 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
363 ::SendMessage(pFrame->m_pImgMergeWindow->GetPaneHWND(evt.pane), WM_VSCROLL, evt.keycode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0);
369 if (GetAsyncKeyState(VK_SHIFT))
371 int nActivePane = pFrame->m_pImgMergeWindow->GetActivePane();
372 int m = GetAsyncKeyState(VK_CONTROL) ? 8 : 1;
373 int dx = (-(evt.keycode == VK_LEFT) + (evt.keycode == VK_RIGHT)) * m;
374 int dy = (-(evt.keycode == VK_UP) + (evt.keycode == VK_DOWN)) * m;
375 pFrame->m_pImgMergeWindow->AddImageOffset(nActivePane, dx, dy);
381 /* if (evt.eventType == IImgMergeWindow::CONTEXTMENU)
383 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
385 menu.LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
386 BCMenu* pPopup = (BCMenu *)menu.GetSubMenu(0);
387 pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
388 evt.x, evt.y, AfxGetMainWnd());
394 * @brief returns true if WinIMergeLib.dll is loadable
396 bool CImgMergeFrame::IsLoadable()
398 static HMODULE hModule;
399 if (hModule == nullptr)
401 hModule = LoadLibraryW(L"WinIMerge\\WinIMergeLib.dll");
402 if (hModule == nullptr)
409 * @brief Create the splitter, the filename bar, the status bar, and the two views
411 BOOL CImgMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
412 CCreateContext* pContext)
417 HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
418 if (hModule == nullptr)
421 IImgMergeWindow * (*pfnWinIMerge_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
422 (IImgMergeWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinIMerge_CreateWindow");
423 if (pfnWinIMerge_CreateWindow == nullptr ||
424 (m_pImgMergeWindow = pfnWinIMerge_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
426 FreeLibrary(hModule);
430 COLORSETTINGS colors;
431 Options::DiffColors::Load(GetOptionsMgr(), colors);
432 m_pImgMergeWindow->SetDiffColor(colors.clrDiff);
433 m_pImgMergeWindow->SetDiffDeletedColor(colors.clrDiffDeleted);
434 m_pImgMergeWindow->SetSelDiffColor(colors.clrSelDiff);
435 m_pImgMergeWindow->SetSelDiffDeletedColor(colors.clrSelDiffDeleted);
436 m_pImgMergeWindow->AddEventListener(OnChildPaneEvent, this);
440 if (m_filePaths.GetSize() == 2)
441 bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
443 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());
445 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
447 m_fileInfo[pane].Update(m_filePaths[pane]);
449 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
450 new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
453 // Merge frame has also a dockable bar at the very left
454 // This is not the client area, but we create it now because we want
455 // to use the CCreateContext
456 String sCaption = theApp.LoadString(IDS_LOCBAR_CAPTION);
457 if (!m_wndLocationBar.Create(this, sCaption.c_str(), WS_CHILD | WS_VISIBLE, ID_VIEW_LOCATION_BAR))
459 TRACE0("Failed to create LocationBar\n");
463 IImgToolWindow * (*pfnWinIMerge_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *) =
464 (IImgToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *pImgMergeWindow))GetProcAddress(hModule, "WinIMerge_CreateToolWindow");
465 if (pfnWinIMerge_CreateToolWindow == nullptr ||
466 (m_pImgToolWindow = pfnWinIMerge_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pImgMergeWindow)) == nullptr)
471 m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
476 /////////////////////////////////////////////////////////////////////////////
477 // CImgMergeFrame message handlers
479 int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
482 if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
485 EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
487 // Merge frame has a header bar at top
488 if (!m_wndFilePathBar.Create(this))
490 TRACE0("Failed to create dialog bar\n");
491 return -1; // fail to create
494 m_wndFilePathBar.SetPaneCount(m_pImgMergeWindow->GetPaneCount());
495 m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) { m_pImgMergeWindow->SetActivePane(pane); });
497 // Merge frame also has a dockable bar at the very left
498 // created in OnCreateClient
499 m_wndLocationBar.SetBarStyle(m_wndLocationBar.GetBarStyle() |
500 CBRS_SIZE_DYNAMIC | CBRS_ALIGN_LEFT);
501 m_wndLocationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
502 DockControlBar(&m_wndLocationBar, AFX_IDW_DOCKBAR_LEFT);
504 for (int nPane = 0; nPane < m_pImgMergeWindow->GetPaneCount(); nPane++)
506 m_pImgMergeWindow->SetReadOnly(nPane, m_bRO[nPane]);
508 m_wndFilePathBar.SetActive(nPane, FALSE);
509 CreateImgWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pImgMergeWindow->GetPaneHWND(nPane)));
510 UpdateHeaderPath(nPane);
513 CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
514 m_rectBorder.bottom = size.cy;
520 * @brief We must use this function before a call to SetDockState
522 * @note Without this, SetDockState will assert or crash if a bar from the
523 * CDockState is missing in the current CMergeEditFrame.
524 * The bars are identified with their ID. This means the missing bar bug is triggered
525 * when we run WinMerge after changing the ID of a bar.
527 bool CImgMergeFrame::EnsureValidDockState(CDockState& state)
529 for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
531 bool barIsCorrect = true;
532 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
533 if (pInfo == nullptr)
534 barIsCorrect = false;
537 if (!pInfo->m_bFloating)
539 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
540 if (pInfo->m_pBar == nullptr)
541 barIsCorrect = false; //toolbar id's probably changed
546 state.m_arrBarInfo.RemoveAt(i);
552 * @brief Save the window's position, free related resources, and destroy the window
554 BOOL CImgMergeFrame::DestroyWindow()
559 return CMDIChildWnd::DestroyWindow();
562 void CImgMergeFrame::LoadOptions()
564 m_pImgMergeWindow->SetShowDifferences(GetOptionsMgr()->GetBool(OPT_CMP_IMG_SHOWDIFFERENCES));
565 m_pImgMergeWindow->SetOverlayMode(static_cast<IImgMergeWindow::OVERLAY_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYMOVE)));
566 m_pImgMergeWindow->SetOverlayAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OVERLAYALPHA) / 100.0);
567 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DRAGGING_MODE)));
568 m_pImgMergeWindow->SetZoom(GetOptionsMgr()->GetInt(OPT_CMP_IMG_ZOOM) / 1000.0);
569 m_pImgMergeWindow->SetUseBackColor(GetOptionsMgr()->GetBool(OPT_CMP_IMG_USEBACKCOLOR));
570 COLORREF clrBackColor = GetOptionsMgr()->GetInt(OPT_CMP_IMG_BACKCOLOR);
571 RGBQUAD backColor = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
572 m_pImgMergeWindow->SetBackColor(backColor);
573 m_pImgMergeWindow->SetDiffBlockSize(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFBLOCKSIZE));
574 m_pImgMergeWindow->SetDiffColorAlpha(GetOptionsMgr()->GetInt(OPT_CMP_IMG_DIFFCOLORALPHA) / 100.0);
575 m_pImgMergeWindow->SetColorDistanceThreshold(GetOptionsMgr()->GetInt(OPT_CMP_IMG_THRESHOLD) / 1000.0);
576 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE)));
579 void CImgMergeFrame::SaveOptions()
581 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_SHOWDIFFERENCES, m_pImgMergeWindow->GetShowDifferences());
582 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYMOVE, m_pImgMergeWindow->GetOverlayMode());
583 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_OVERLAYALPHA, static_cast<int>(m_pImgMergeWindow->GetOverlayAlpha() * 100));
584 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DRAGGING_MODE, static_cast<int>(m_pImgMergeWindow->GetDraggingMode()));
585 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_ZOOM, static_cast<int>(m_pImgMergeWindow->GetZoom() * 1000));
586 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_USEBACKCOLOR, m_pImgMergeWindow->GetUseBackColor());
587 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
588 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_BACKCOLOR, static_cast<int>(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue)));
589 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFBLOCKSIZE, m_pImgMergeWindow->GetDiffBlockSize());
590 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_DIFFCOLORALPHA, static_cast<int>(m_pImgMergeWindow->GetDiffColorAlpha() * 100.0));
591 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_THRESHOLD, static_cast<int>(m_pImgMergeWindow->GetColorDistanceThreshold() * 1000));
592 GetOptionsMgr()->SaveOption(OPT_CMP_IMG_INSERTIONDELETIONDETECTION_MODE, static_cast<int>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode()));
595 * @brief Save coordinates of the frame, splitters, and bars
597 * @note Do not save the maximized/restored state here. We are interested
598 * in the state of the active frame, and maybe this frame is not active
600 void CImgMergeFrame::SavePosition()
604 GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pImgMergeWindow->GetActivePane());
606 // save the bars layout
607 // save docking positions and sizes
608 CDockState m_pDockState;
609 GetDockState(m_pDockState);
610 m_pDockState.SaveState(_T("Settings-ImgMergeFrame"));
611 // for the dimensions of the diff pane, use the CSizingControlBar save
612 m_wndLocationBar.SaveState(_T("Settings-ImgMergeFrame"));
615 void CImgMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
619 CDockState pDockState;
620 pDockState.LoadState(_T("Settings-ImgMergeFrame"));
621 if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
622 SetDockState(pDockState);
623 // for the dimensions of the diff and location pane, use the CSizingControlBar loader
624 m_wndLocationBar.LoadState(_T("Settings-ImgMergeFrame"));
626 CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
629 GetMainFrame()->PostMessage(WM_USER + 1);
633 void CImgMergeFrame::OnClose()
635 // Allow user to cancel closing
636 if (!PromptAndSaveIfNeeded(true))
639 // clean up pointers.
640 CMDIChildWnd::OnClose();
642 GetMainFrame()->ClearStatusbarItemCount();
645 bool CImgMergeFrame::DoFileSave(int pane)
647 if (m_pImgMergeWindow->IsModified(pane))
649 if (m_nBufferType[pane] == BUFFER_UNNAMED)
653 String filename = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
654 bool bApplyToAll = false;
655 if (theApp.HandleReadonlySave(filename, false, bApplyToAll) == IDCANCEL)
657 theApp.CreateBackup(false, filename);
658 if (!m_pImgMergeWindow->SaveImage(pane))
660 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());
661 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
663 return DoFileSaveAs(pane);
667 UpdateDiffItem(m_pDirDoc);
668 m_fileInfo[pane].Update(m_filePaths[pane]);
673 bool CImgMergeFrame::DoFileSaveAs(int pane)
675 const String &path = m_filePaths.GetPath(pane);
679 title = _("Save Left File As");
680 else if (pane == m_pImgMergeWindow->GetPaneCount() - 1)
681 title = _("Save Right File As");
683 title = _("Save Middle File As");
685 if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
687 std::wstring filename = ucr::toUTF16(strPath);
688 if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
690 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());
691 int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
698 // We are saving scratchpad (unnamed file)
699 m_nBufferType[pane] = BUFFER_UNNAMED_SAVED;
700 m_strDesc[pane].erase();
703 m_filePaths.SetPath(pane, strPath);
704 UpdateDiffItem(m_pDirDoc);
705 m_fileInfo[pane].Update(m_filePaths[pane]);
706 UpdateHeaderPath(pane);
712 * @brief Saves both files
714 void CImgMergeFrame::OnFileSave()
716 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
721 * @brief Called when "Save" item is updated
723 void CImgMergeFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
725 pCmdUI->Enable(IsModified());
729 * @brief Saves left-side file
731 void CImgMergeFrame::OnFileSaveLeft()
737 * @brief Called when "Save middle (...)" item is updated
739 void CImgMergeFrame::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
741 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
745 * @brief Saves middle-side file
747 void CImgMergeFrame::OnFileSaveMiddle()
753 * @brief Saves right-side file
755 void CImgMergeFrame::OnFileSaveRight()
757 DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
761 * @brief Called when "Save middle (as...)" item is updated
763 void CImgMergeFrame::OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI)
765 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
769 * @brief Saves left-side file with name asked
771 void CImgMergeFrame::OnFileSaveAsLeft()
777 * @brief Saves middle-side file with name asked
779 void CImgMergeFrame::OnFileSaveAsMiddle()
785 * @brief Saves right-side file with name asked
787 void CImgMergeFrame::OnFileSaveAsRight()
789 DoFileSaveAs(m_pImgMergeWindow->GetPaneCount() - 1);
793 * @brief Reloads the opened files
795 void CImgMergeFrame::OnFileReload()
797 if (!PromptAndSaveIfNeeded(true))
799 m_pImgMergeWindow->ReloadImages();
800 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
801 m_fileInfo[pane].Update(m_filePaths[pane]);
804 void CImgMergeFrame::OnFileClose()
810 * @brief Enable/disable left buffer read-only
812 void CImgMergeFrame::OnLeftReadOnly()
814 m_bRO[0] = !m_bRO[0];
815 m_pImgMergeWindow->SetReadOnly(0, m_bRO[0]);
819 * @brief Called when "Left read-only" item is updated
821 void CImgMergeFrame::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
823 pCmdUI->Enable(true);
824 pCmdUI->SetCheck(m_bRO[0]);
828 * @brief Enable/disable middle buffer read-only
830 void CImgMergeFrame::OnMiddleReadOnly()
832 if (m_pImgMergeWindow->GetPaneCount() == 3)
834 m_bRO[1] = !m_bRO[1];
835 m_pImgMergeWindow->SetReadOnly(1, m_bRO[1]);
840 * @brief Called when "Middle read-only" item is updated
842 void CImgMergeFrame::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
844 if (m_pImgMergeWindow->GetPaneCount() < 3)
846 pCmdUI->Enable(false);
850 pCmdUI->Enable(true);
851 pCmdUI->SetCheck(m_bRO[1]);
856 * @brief Enable/disable right buffer read-only
858 void CImgMergeFrame::OnRightReadOnly()
860 int pane = m_pImgMergeWindow->GetPaneCount() - 1;
861 m_bRO[pane] = !m_bRO[pane];
862 m_pImgMergeWindow->SetReadOnly(pane, m_bRO[pane]);
866 * @brief Called when "Right read-only" item is updated
868 void CImgMergeFrame::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
870 pCmdUI->Enable(true);
871 pCmdUI->SetCheck(m_pImgMergeWindow->GetReadOnly(m_pImgMergeWindow->GetPaneCount() - 1));
874 void CImgMergeFrame::OnFileRecompareAs(UINT nId)
876 FileLocation fileloc[3];
879 int nBuffers = m_filePaths.GetSize();
880 CDirDoc *pDirDoc = m_pDirDoc->GetMainView() ? m_pDirDoc :
881 static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
882 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
884 fileloc[nBuffer].setPath(m_filePaths[nBuffer]);
885 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
886 strDesc[nBuffer] = m_strDesc[nBuffer];
889 if (nId == ID_MERGE_COMPARE_TEXT)
890 GetMainFrame()->ShowMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
891 else if (nId == ID_MERGE_COMPARE_HEX)
892 GetMainFrame()->ShowHexMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
894 GetMainFrame()->ShowImgMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
897 void CImgMergeFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
899 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_XML);
902 void CImgMergeFrame::OnWindowChangePane()
904 m_pImgMergeWindow->SetActivePane((m_pImgMergeWindow->GetActivePane() + 1) % m_pImgMergeWindow->GetPaneCount());
908 * @brief Write path and filename to headerbar
909 * @note SetText() does not repaint unchanged text
911 void CImgMergeFrame::UpdateHeaderPath(int pane)
915 if (m_nBufferType[pane] == BUFFER_UNNAMED ||
916 m_nBufferType[pane] == BUFFER_NORMAL_NAMED)
918 sText = m_strDesc[pane];
922 sText = m_filePaths.GetPath(pane);
923 if (m_pDirDoc != nullptr)
924 m_pDirDoc->ApplyDisplayRoot(pane, sText);
926 if (m_pImgMergeWindow->IsModified(pane))
927 sText.insert(0, _T("* "));
929 m_wndFilePathBar.SetText(pane, sText.c_str());
934 /// update splitting position for panels 1/2 and headerbar and statusbar
935 void CImgMergeFrame::UpdateHeaderSizes()
937 if (IsWindowVisible() && m_pImgMergeWindow != nullptr)
940 CRect rc, rcMergeWindow;
941 int nPaneCount = m_pImgMergeWindow->GetPaneCount();
943 ::GetWindowRect(m_pImgMergeWindow->GetHWND(), &rcMergeWindow);
944 ScreenToClient(rcMergeWindow);
945 if (!m_pImgMergeWindow->GetHorizontalSplit())
947 for (int pane = 0; pane < nPaneCount; pane++)
949 RECT rc1 = m_pImgMergeWindow->GetPaneWindowRect(pane);
950 w[pane] = rc1.right - rc1.left - 4;
951 if (w[pane]<1) w[pane]=1; // Perry 2003-01-22 (I don't know why this happens)
956 for (int pane = 0; pane < nPaneCount; pane++)
957 w[pane] = rc.Width() / nPaneCount - 4;
960 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
962 std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
964 // resize controls in header dialog bar
965 m_wndFilePathBar.Resize(w);
967 rc.left = rcMergeWindow.left;
968 rc.top = rc.bottom - m_rectBorder.bottom;
970 for (int pane = 0; pane < nPaneCount; pane++)
972 rc.right += w[pane] + 4 + 2;
973 m_wndStatusBar[pane].MoveWindow(&rc);
981 * @brief Update document filenames to title
983 void CImgMergeFrame::SetTitle(LPCTSTR lpszTitle)
988 if (lpszTitle != nullptr)
992 for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); nBuffer++)
994 if (!m_strDesc[nBuffer].empty())
995 sFileName[nBuffer] = m_strDesc[nBuffer];
1000 paths::SplitFilename(m_filePaths[nBuffer], nullptr, &file, &ext);
1001 sFileName[nBuffer] += file;
1004 sFileName[nBuffer] += _T(".");
1005 sFileName[nBuffer] += ext;
1009 const int nBuffers = m_filePaths.GetSize();
1010 if (std::count(&sFileName[0], &sFileName[0] + nBuffers, sFileName[0]) == nBuffers)
1011 sTitle = sFileName[0] + strutils::format(_T(" x %d"), nBuffers);
1013 sTitle = strutils::join(&sFileName[0], &sFileName[0] + nBuffers, _T(" - "));
1015 CMDIChildWnd::SetTitle(sTitle.c_str());
1016 if (m_hWnd != nullptr)
1017 SetWindowText(sTitle.c_str());
1020 void CImgMergeFrame::UpdateLastCompareResult()
1022 SetLastCompareResult(m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0);
1025 void CImgMergeFrame::UpdateAutoPaneResize()
1029 void CImgMergeFrame::UpdateSplitter()
1034 * @brief Update associated diff item
1036 int CImgMergeFrame::UpdateDiffItem(CDirDoc *pDirDoc)
1038 // If directory compare has results
1039 if (pDirDoc && pDirDoc->HasDiffs())
1041 const String &pathLeft = m_filePaths.GetLeft();
1042 const String &pathRight = m_filePaths.GetRight();
1043 CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
1045 // if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
1047 // DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
1048 // ::UpdateDiffItem(m_nBuffers, di, &ctxt);
1051 int result = m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0;
1052 SetLastCompareResult(result != 0);
1057 * @brief Asks and then saves modified files.
1059 * This function saves modified files. Dialog is shown for user to select
1060 * modified file(s) one wants to save or discard changed. Cancelling of
1061 * save operation is allowed unless denied by parameter. After successfully
1062 * save operation file statuses are updated to directory compare.
1063 * @param [in] bAllowCancel If false "Cancel" button is disabled.
1064 * @return true if user selected "OK" so next operation can be
1065 * executed. If false user choosed "Cancel".
1066 * @note If filename is empty, we assume scratchpads are saved,
1067 * so instead of filename, description is shown.
1068 * @todo If we have filename and description for file, what should
1069 * we do after saving to different filename? Empty description?
1070 * @todo Parameter @p bAllowCancel is always true in callers - can be removed.
1072 bool CImgMergeFrame::PromptAndSaveIfNeeded(bool bAllowCancel)
1074 bool bLModified = false, bMModified = false, bRModified = false;
1076 bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
1078 if (m_pImgMergeWindow->GetPaneCount() == 3)
1080 bLModified = m_pImgMergeWindow->IsModified(0);
1081 bMModified = m_pImgMergeWindow->IsModified(1);
1082 bRModified = m_pImgMergeWindow->IsModified(2);
1086 bLModified = m_pImgMergeWindow->IsModified(0);
1087 bRModified = m_pImgMergeWindow->IsModified(1);
1089 if (!bLModified && !bMModified && !bRModified)
1093 dlg.DoAskFor(bLModified, bMModified, bRModified);
1095 dlg.m_bDisableCancel = true;
1096 if (!m_filePaths.GetLeft().empty())
1098 if (theApp.m_strSaveAsPath.empty())
1099 dlg.m_sLeftFile = m_filePaths.GetLeft();
1101 dlg.m_sLeftFile = theApp.m_strSaveAsPath;
1104 dlg.m_sLeftFile = m_strDesc[0];
1105 if (m_pImgMergeWindow->GetPaneCount() == 3)
1107 if (!m_filePaths.GetMiddle().empty())
1109 if (theApp.m_strSaveAsPath.empty())
1110 dlg.m_sMiddleFile = m_filePaths.GetMiddle();
1112 dlg.m_sMiddleFile = theApp.m_strSaveAsPath;
1115 dlg.m_sMiddleFile = m_strDesc[1];
1117 if (!m_filePaths.GetRight().empty())
1119 if (theApp.m_strSaveAsPath.empty())
1120 dlg.m_sRightFile = m_filePaths.GetRight();
1122 dlg.m_sRightFile = theApp.m_strSaveAsPath;
1125 dlg.m_sRightFile = m_strDesc[m_pImgMergeWindow->GetPaneCount() - 1];
1127 if (dlg.DoModal() == IDOK)
1129 if (bLModified && dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
1131 bLSaveSuccess = DoFileSave(0);
1136 if (bMModified && dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
1138 bMSaveSuccess = DoFileSave(1);
1143 if (bRModified && dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
1145 bRSaveSuccess = DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
1155 // If file were modified and saving was successfull,
1156 // update status on dir view
1157 if ((bLModified && bLSaveSuccess) ||
1158 (bMModified && bMSaveSuccess) ||
1159 (bRModified && bRSaveSuccess))
1161 // If directory compare has results
1162 if (m_pDirDoc && m_pDirDoc->HasDiffs())
1171 /// Document commanding us to close
1172 bool CImgMergeFrame::CloseNow()
1174 // Allow user to cancel closing
1175 if (!PromptAndSaveIfNeeded(true))
1178 SavePosition(); // Save settings before closing!
1186 * @brief Update any resources necessary after a GUI language change
1188 void CImgMergeFrame::UpdateResources()
1193 * @brief Handle some keys when in merging mode
1195 bool CImgMergeFrame::MergeModeKeyDown(MSG* pMsg)
1197 bool bHandled = false;
1199 // Allow default text selection when SHIFT pressed
1200 if (::GetAsyncKeyState(VK_SHIFT))
1203 // Allow default editor functions when CTRL pressed
1204 if (::GetAsyncKeyState(VK_CONTROL))
1207 // If we are in merging mode (merge with cursor keys)
1208 // handle some keys here
1209 switch (pMsg->wParam)
1234 * @brief Check for keyboard commands
1236 BOOL CImgMergeFrame::PreTranslateMessage(MSG* pMsg)
1238 if (pMsg->message == WM_KEYDOWN)
1240 // If we are in merging mode (merge with cursor keys)
1241 // handle some keys here
1242 if (theApp.GetMergingMode())
1244 bool bHandled = MergeModeKeyDown(pMsg);
1249 // Close window in response to VK_ESCAPE if user has allowed it from options
1250 if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC))
1252 PostMessage(WM_CLOSE, 0, 0);
1256 return CMDIChildWnd::PreTranslateMessage(pMsg);
1259 void CImgMergeFrame::OnSize(UINT nType, int cx, int cy)
1261 CMDIChildWnd::OnSize(nType, cx, cy);
1262 UpdateHeaderSizes();
1266 * @brief Synchronize control and status bar placements with splitter position,
1267 * update mod indicators, synchronize scrollbars
1269 void CImgMergeFrame::OnIdleUpdateCmdUI()
1271 if (IsWindowVisible())
1273 POINT pt = {-1, -1}, ptCursor;
1274 GetCursorPos(&ptCursor);
1275 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1278 ::GetWindowRect(m_pImgMergeWindow->GetPaneHWND(pane), &rc);
1279 if (PtInRect(&rc, ptCursor))
1280 pt = m_pImgMergeWindow->GetCursorPos(pane);
1284 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1285 color[pane] = m_pImgMergeWindow->GetPixelColor(pane, pt.x, pt.y);
1286 double colorDistance01 = m_pImgMergeWindow->GetColorDistance(0, 1, pt.x, pt.y);
1287 double colorDistance12 = 0;
1288 if (m_pImgMergeWindow->GetPaneCount() == 3)
1289 colorDistance12 = m_pImgMergeWindow->GetColorDistance(1, 2, pt.x, pt.y);
1291 UpdateHeaderSizes();
1292 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1294 // Update mod indicators
1295 String ind = m_wndFilePathBar.GetText(pane);
1296 if (m_pImgMergeWindow->IsModified(pane) ? ind[0] != _T('*') : ind[0] == _T('*'))
1297 UpdateHeaderPath(pane);
1299 m_wndFilePathBar.SetActive(pane, pane == m_pImgMergeWindow->GetActivePane());
1302 if (m_pImgMergeWindow->ConvertToRealPos(pane, pt, ptReal))
1304 text += strutils::format(_T("Pt:(%d,%d) RGBA:(%d,%d,%d,%d) "), ptReal.x, ptReal.y,
1305 color[pane].rgbRed, color[pane].rgbGreen, color[pane].rgbBlue, color[pane].rgbReserved);
1306 if (pane == 1 && m_pImgMergeWindow->GetPaneCount() == 3)
1307 text += strutils::format(_T("Dist:%g,%g "), colorDistance01, colorDistance12);
1309 text += strutils::format(_T("Dist:%g "), colorDistance01);
1312 text += strutils::format(_T("Page:%d/%d Zoom:%d%% %dx%dpx %dbpp"),
1313 m_pImgMergeWindow->GetCurrentPage(pane) + 1,
1314 m_pImgMergeWindow->GetPageCount(pane),
1315 static_cast<int>(m_pImgMergeWindow->GetZoom() * 100),
1316 m_pImgMergeWindow->GetImageWidth(pane),
1317 m_pImgMergeWindow->GetImageHeight(pane),
1318 m_pImgMergeWindow->GetImageBitsPerPixel(pane)
1320 m_wndStatusBar[pane].SetPaneText(0, text.c_str());
1323 CMDIChildWnd::OnIdleUpdateCmdUI();
1327 * @brief Save pane sizes and positions when one of panes requests it.
1329 LRESULT CImgMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1335 void CImgMergeFrame::OnUpdateStatusNum(CCmdUI* pCmdUI)
1337 TCHAR sIdx[32] = { 0 };
1338 TCHAR sCnt[32] = { 0 };
1340 const int nDiffs = m_pImgMergeWindow->GetDiffCount();
1342 // Files are identical - show text "Identical"
1344 s = theApp.LoadString(IDS_IDENTICAL);
1346 // There are differences, but no selected diff
1347 // - show amount of diffs
1348 else if (m_pImgMergeWindow->GetCurrentDiffIndex() < 0)
1350 s = theApp.LoadString(nDiffs == 1 ? IDS_1_DIFF_FOUND : IDS_NO_DIFF_SEL_FMT);
1351 _itot_s(nDiffs, sCnt, 10);
1352 strutils::replace(s, _T("%1"), sCnt);
1355 // There are differences and diff selected
1356 // - show diff number and amount of diffs
1359 s = theApp.LoadString(IDS_DIFF_NUMBER_STATUS_FMT);
1360 const int signInd = m_pImgMergeWindow->GetCurrentDiffIndex();
1361 _itot_s(signInd + 1, sIdx, 10);
1362 strutils::replace(s, _T("%1"), sIdx);
1363 _itot_s(nDiffs, sCnt, 10);
1364 strutils::replace(s, _T("%2"), sCnt);
1366 pCmdUI->SetText(s.c_str());
1370 * @brief Undo last action
1372 void CImgMergeFrame::OnEditUndo()
1374 m_pImgMergeWindow->Undo();
1375 if (!m_pImgMergeWindow->IsUndoable())
1376 m_bAutoMerged = false;
1377 UpdateLastCompareResult();
1381 * @brief Called when "Undo" item is updated
1383 void CImgMergeFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1385 pCmdUI->Enable(m_pImgMergeWindow->IsUndoable());
1389 * @brief Redo last action
1391 void CImgMergeFrame::OnEditRedo()
1393 m_pImgMergeWindow->Redo();
1394 UpdateLastCompareResult();
1398 * @brief Called when "Redo" item is updated
1400 void CImgMergeFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1402 pCmdUI->Enable(m_pImgMergeWindow->IsRedoable());
1406 * @brief Called when user selects View/Zoom In from menu.
1408 void CImgMergeFrame::OnViewZoomIn()
1410 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() + 0.1);
1414 * @brief Called when user selects View/Zoom Out from menu.
1416 void CImgMergeFrame::OnViewZoomOut()
1418 m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() - 0.1);
1422 * @brief Called when user selects View/Zoom Normal from menu.
1424 void CImgMergeFrame::OnViewZoomNormal()
1426 m_pImgMergeWindow->SetZoom(1.0);
1430 * @brief Split panes vertically
1432 void CImgMergeFrame::OnViewSplitVertically()
1434 bool bSplitVertically = !m_pImgMergeWindow->GetHorizontalSplit();
1435 bSplitVertically = !bSplitVertically; // toggle
1436 GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1437 m_pImgMergeWindow->SetHorizontalSplit(!bSplitVertically);
1441 * @brief Update "Split Vertically" UI items
1443 void CImgMergeFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI)
1445 pCmdUI->Enable(TRUE);
1446 pCmdUI->SetCheck(!m_pImgMergeWindow->GetHorizontalSplit());
1450 * @brief Go to first diff
1452 * Called when user selects "First Difference"
1453 * @sa CImgMergeFrame::SelectDiff()
1455 void CImgMergeFrame::OnFirstdiff()
1457 m_pImgMergeWindow->FirstDiff();
1461 * @brief Update "First diff" UI items
1463 void CImgMergeFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1465 OnUpdatePrevdiff(pCmdUI);
1469 * @brief Go to last diff
1471 void CImgMergeFrame::OnLastdiff()
1473 m_pImgMergeWindow->LastDiff();
1477 * @brief Update "Last diff" UI items
1479 void CImgMergeFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1481 OnUpdateNextdiff(pCmdUI);
1485 * @brief Go to next diff and select it.
1487 void CImgMergeFrame::OnNextdiff()
1489 if (m_pImgMergeWindow->GetCurrentDiffIndex() != m_pImgMergeWindow->GetDiffCount() - 1)
1490 m_pImgMergeWindow->NextDiff();
1491 else if (m_pImgMergeWindow->GetCurrentMaxPage() != m_pImgMergeWindow->GetMaxPageCount() - 1)
1493 if (AfxMessageBox(_("Do you want to move to the next page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1495 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1496 UpdateLastCompareResult();
1499 else if (m_pDirDoc != nullptr)
1500 m_pDirDoc->MoveToNextDiff(this);
1504 * @brief Update "Next diff" UI items
1506 void CImgMergeFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1509 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1 ||
1510 m_pImgMergeWindow->GetNextDiffIndex() >= 0 ||
1511 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1513 if (!enabled && m_pDirDoc != nullptr)
1514 enabled = m_pDirDoc->MoveableToNextDiff();
1516 pCmdUI->Enable(enabled);
1520 * @brief Go to previous diff and select it.
1522 void CImgMergeFrame::OnPrevdiff()
1524 if (m_pImgMergeWindow->GetCurrentDiffIndex() > 0)
1526 m_pImgMergeWindow->PrevDiff();
1528 else if (m_pImgMergeWindow->GetCurrentMaxPage() != 0)
1530 if (AfxMessageBox(_("Do you want to move to the previous page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1532 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1533 UpdateLastCompareResult();
1536 else if (m_pDirDoc != nullptr)
1537 m_pDirDoc->MoveToPrevDiff(this);
1541 * @brief Update "Previous diff" UI items
1543 void CImgMergeFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1546 m_pImgMergeWindow->GetCurrentMaxPage() > 0 ||
1547 m_pImgMergeWindow->GetPrevDiffIndex() >= 0 ||
1548 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1550 if (!enabled && m_pDirDoc != nullptr)
1551 enabled = m_pDirDoc->MoveableToPrevDiff();
1553 pCmdUI->Enable(enabled);
1557 * @brief Go to next conflict and select it.
1559 void CImgMergeFrame::OnNextConflict()
1561 m_pImgMergeWindow->NextConflict();
1565 * @brief Update "Next Conflict" UI items
1567 void CImgMergeFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1570 m_pImgMergeWindow->GetPaneCount() > 2 && (
1571 m_pImgMergeWindow->GetNextConflictIndex() >= 0 ||
1572 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1578 * @brief Go to previous diff and select it.
1580 void CImgMergeFrame::OnPrevConflict()
1582 m_pImgMergeWindow->PrevConflict();
1586 * @brief Update "Previous diff" UI items
1588 void CImgMergeFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1591 m_pImgMergeWindow->GetPaneCount() > 2 && (
1592 m_pImgMergeWindow->GetPrevConflictIndex() >= 0 ||
1593 (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1598 void CImgMergeFrame::OnUpdateX2Y(CCmdUI* pCmdUI, int srcPane, int dstPane)
1600 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentDiffIndex() >= 0 &&
1601 srcPane >= 0 && srcPane <= m_pImgMergeWindow->GetPaneCount() &&
1602 dstPane >= 0 && dstPane <= m_pImgMergeWindow->GetPaneCount() &&
1607 void CImgMergeFrame::OnX2Y(int srcPane, int dstPane)
1609 m_pImgMergeWindow->CopyDiff(m_pImgMergeWindow->GetCurrentDiffIndex(), srcPane, dstPane);
1610 UpdateLastCompareResult();
1614 * @brief Copy diff from left pane to right pane
1616 void CImgMergeFrame::OnL2r()
1618 int srcPane = m_pImgMergeWindow->GetActivePane();
1619 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1620 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1623 int dstPane = srcPane + 1;
1624 OnX2Y(srcPane, dstPane);
1628 * @brief Called when "Copy to left" item is updated
1630 void CImgMergeFrame::OnUpdateL2r(CCmdUI* pCmdUI)
1632 int srcPane = m_pImgMergeWindow->GetActivePane();
1633 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1634 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1637 int dstPane = srcPane + 1;
1638 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1642 * @brief Copy diff from right pane to left pane
1644 void CImgMergeFrame::OnR2l()
1646 int srcPane = m_pImgMergeWindow->GetActivePane();
1649 int dstPane = srcPane - 1;
1650 OnX2Y(srcPane, dstPane);
1654 * @brief Called when "Copy to right" item is updated
1656 void CImgMergeFrame::OnUpdateR2l(CCmdUI* pCmdUI)
1658 int srcPane = m_pImgMergeWindow->GetActivePane();
1661 int dstPane = srcPane - 1;
1662 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1665 void CImgMergeFrame::OnCopyFromLeft()
1667 int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1670 int dstPane = srcPane + 1;
1671 OnX2Y(srcPane, dstPane);
1675 * @brief Called when "Copy from left" item is updated
1677 void CImgMergeFrame::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1679 int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1682 int dstPane = srcPane + 1;
1683 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1686 void CImgMergeFrame::OnCopyFromRight()
1688 int srcPane = m_pImgMergeWindow->GetActivePane() + 1;
1689 if (srcPane > m_pImgMergeWindow->GetPaneCount() - 1)
1690 srcPane = m_pImgMergeWindow->GetPaneCount() - 1;
1691 int dstPane = srcPane - 1;
1692 OnX2Y(srcPane, dstPane);
1696 * @brief Called when "Copy from right" item is updated
1698 void CImgMergeFrame::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1700 int srcPane = m_pImgMergeWindow->GetActivePane() + 1;
1701 if (srcPane > m_pImgMergeWindow->GetPaneCount() - 1)
1702 srcPane = m_pImgMergeWindow->GetPaneCount() - 1;
1703 int dstPane = srcPane - 1;
1704 OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1708 * @brief Copy all diffs from right pane to left pane
1710 void CImgMergeFrame::OnAllLeft()
1712 int srcPane = m_pImgMergeWindow->GetActivePane();
1715 int dstPane = srcPane - 1;
1717 CWaitCursor waitstatus;
1719 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1720 UpdateLastCompareResult();
1724 * @brief Called when "Copy all to left" item is updated
1726 void CImgMergeFrame::OnUpdateAllLeft(CCmdUI* pCmdUI)
1728 int srcPane = m_pImgMergeWindow->GetActivePane();
1731 int dstPane = srcPane - 1;
1733 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1737 * @brief Copy all diffs from left pane to right pane
1739 void CImgMergeFrame::OnAllRight()
1741 int srcPane = m_pImgMergeWindow->GetActivePane();
1742 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1743 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1746 int dstPane = srcPane + 1;
1748 CWaitCursor waitstatus;
1750 m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1751 UpdateLastCompareResult();
1755 * @brief Called when "Copy all to right" item is updated
1757 void CImgMergeFrame::OnUpdateAllRight(CCmdUI* pCmdUI)
1759 int srcPane = m_pImgMergeWindow->GetActivePane();
1760 if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1761 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1764 int dstPane = srcPane + 1;
1766 pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1770 * @brief Do Auto merge
1772 void CImgMergeFrame::OnAutoMerge()
1774 int dstPane = m_pImgMergeWindow->GetActivePane();
1776 // Check current pane is not readonly
1777 if (dstPane < 0 || IsModified() || m_bAutoMerged || m_bRO[dstPane])
1780 CWaitCursor waitstatus;
1782 DoAutoMerge(dstPane);
1786 * @brief Called when "Auto Merge" item is updated
1788 void CImgMergeFrame::OnUpdateAutoMerge(CCmdUI* pCmdUI)
1790 int dstPane = m_pImgMergeWindow->GetActivePane();
1792 pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 &&
1793 dstPane >= 0 && !IsModified() && !m_bAutoMerged && !m_bRO[dstPane]);
1796 void CImgMergeFrame::OnImgViewDifferences()
1798 m_pImgMergeWindow->SetShowDifferences(!m_pImgMergeWindow->GetShowDifferences());
1802 void CImgMergeFrame::OnUpdateImgViewDifferences(CCmdUI* pCmdUI)
1804 pCmdUI->SetCheck(m_pImgMergeWindow->GetShowDifferences() ? 1 : 0);
1807 void CImgMergeFrame::OnImgZoom(UINT nId)
1809 m_pImgMergeWindow->SetZoom(pow(2.0, int(nId - ID_IMG_ZOOM_100)));
1813 void CImgMergeFrame::OnUpdateImgZoom(CCmdUI* pCmdUI)
1815 pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_ZOOM_100)) == m_pImgMergeWindow->GetZoom());
1818 void CImgMergeFrame::OnImgOverlayMode(UINT nId)
1820 if (nId == ID_IMG_OVERLAY_NONE)
1821 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_NONE);
1822 else if (nId == ID_IMG_OVERLAY_XOR)
1823 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_XOR);
1824 else if (nId == ID_IMG_OVERLAY_ALPHABLEND)
1825 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND);
1826 else if (nId == ID_IMG_OVERLAY_ALPHABLEND_ANIM)
1827 m_pImgMergeWindow->SetOverlayMode(IImgMergeWindow::OVERLAY_ALPHABLEND_ANIM);
1831 void CImgMergeFrame::OnUpdateImgOverlayMode(CCmdUI* pCmdUI)
1833 pCmdUI->SetRadio(static_cast<IImgMergeWindow::OVERLAY_MODE>(pCmdUI->m_nID - ID_IMG_OVERLAY_NONE) == m_pImgMergeWindow->GetOverlayMode());
1836 void CImgMergeFrame::OnImgDraggingMode(UINT nId)
1838 m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(nId - ID_IMG_DRAGGINGMODE_NONE));
1842 void CImgMergeFrame::OnUpdateImgDraggingMode(CCmdUI* pCmdUI)
1844 pCmdUI->SetRadio(static_cast<IImgMergeWindow::DRAGGING_MODE>(pCmdUI->m_nID - ID_IMG_DRAGGINGMODE_NONE) == m_pImgMergeWindow->GetDraggingMode());
1847 void CImgMergeFrame::OnImgDiffBlockSize(UINT nId)
1849 m_pImgMergeWindow->SetDiffBlockSize(1 << (nId - ID_IMG_DIFFBLOCKSIZE_1));
1853 void CImgMergeFrame::OnUpdateImgDiffBlockSize(CCmdUI* pCmdUI)
1855 pCmdUI->SetRadio(1 << (pCmdUI->m_nID - ID_IMG_DIFFBLOCKSIZE_1) == m_pImgMergeWindow->GetDiffBlockSize() );
1858 void CImgMergeFrame::OnImgThreshold(UINT nId)
1860 if (nId == ID_IMG_THRESHOLD_0)
1861 m_pImgMergeWindow->SetColorDistanceThreshold(0.0);
1863 m_pImgMergeWindow->SetColorDistanceThreshold((1 << (nId - ID_IMG_THRESHOLD_2)) * 2);
1867 void CImgMergeFrame::OnUpdateImgThreshold(CCmdUI* pCmdUI)
1869 if (pCmdUI->m_nID == ID_IMG_THRESHOLD_0)
1870 pCmdUI->SetRadio(m_pImgMergeWindow->GetColorDistanceThreshold() == 0.0);
1872 pCmdUI->SetRadio((1 << (pCmdUI->m_nID - ID_IMG_THRESHOLD_2)) * 2 == m_pImgMergeWindow->GetColorDistanceThreshold() );
1875 void CImgMergeFrame::OnImgInsertionDeletionDetectionMode(UINT nId)
1877 m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(nId - ID_IMG_INSERTIONDELETIONDETECTION_NONE));
1881 void CImgMergeFrame::OnUpdateImgInsertionDeletionDetectionMode(CCmdUI* pCmdUI)
1883 pCmdUI->SetRadio(static_cast<unsigned>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode() + ID_IMG_INSERTIONDELETIONDETECTION_NONE) == pCmdUI->m_nID);
1886 void CImgMergeFrame::OnImgPrevPage()
1888 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1889 UpdateLastCompareResult();
1892 void CImgMergeFrame::OnUpdateImgPrevPage(CCmdUI* pCmdUI)
1894 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentMaxPage() > 0);
1897 void CImgMergeFrame::OnImgNextPage()
1899 m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1900 UpdateLastCompareResult();
1903 void CImgMergeFrame::OnUpdateImgNextPage(CCmdUI* pCmdUI)
1906 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1);
1909 void CImgMergeFrame::OnImgCurPanePrevPage()
1911 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) - 1);
1912 UpdateLastCompareResult();
1915 void CImgMergeFrame::OnUpdateImgCurPanePrevPage(CCmdUI* pCmdUI)
1917 pCmdUI->Enable(m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) > 0);
1920 void CImgMergeFrame::OnImgCurPaneNextPage()
1922 m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) + 1);
1923 UpdateLastCompareResult();
1926 void CImgMergeFrame::OnUpdateImgCurPaneNextPage(CCmdUI* pCmdUI)
1929 m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) <
1930 m_pImgMergeWindow->GetPageCount(m_pImgMergeWindow->GetActivePane()) - 1);
1933 void CImgMergeFrame::OnImgUseBackColor()
1935 bool bUseBackColor = !m_pImgMergeWindow->GetUseBackColor();
1938 RGBQUAD backColor = m_pImgMergeWindow->GetBackColor();
1939 CColorDialog dialog(RGB(backColor.rgbRed, backColor.rgbGreen, backColor.rgbBlue));
1940 static DWORD dwCustColors[16];
1941 Options::CustomColors::Load(GetOptionsMgr(), dwCustColors);
1942 dialog.m_cc.lpCustColors = dwCustColors;
1943 if (dialog.DoModal() == IDOK)
1945 COLORREF clrBackColor = dialog.GetColor();
1946 RGBQUAD backColor1 = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
1947 m_pImgMergeWindow->SetBackColor(backColor1);
1948 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
1953 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
1958 void CImgMergeFrame::OnUpdateImgUseBackColor(CCmdUI* pCmdUI)
1960 pCmdUI->SetCheck(m_pImgMergeWindow->GetUseBackColor() ? 1 : 0);
1964 * @brief Generate report from file compare results.
1966 bool CImgMergeFrame::GenerateReport(const String& sFileName) const
1968 String imgdir_full, imgdir, imgfilepath[3], diffimg_filename[3], path, name, ext;
1969 paths::SplitFilename(sFileName, &path, &name, &ext);
1970 imgdir_full = paths::ConcatPath(path, name) + _T(".files");
1971 imgdir = paths::FindFileName(imgdir_full);
1972 paths::CreateIfNeeded(imgdir_full);
1973 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
1975 imgfilepath[i] = ucr::toTString(m_pImgMergeWindow->GetFileName(i));
1976 diffimg_filename[i] = strutils::format(_T("%s/%d.png"), imgdir, i + 1);
1977 m_pImgMergeWindow->SaveDiffImageAs(i, ucr::toUTF16(strutils::format(_T("%s\\%d.png"), imgdir_full, i + 1)).c_str());
1981 if (!file.Open(sFileName, _T("wt")))
1983 String errMsg = GetSysError(GetLastError());
1984 String msg = strutils::format_string1(
1985 _("Error creating the report:\n%1"), errMsg);
1986 AfxMessageBox(msg.c_str(), MB_OK | MB_ICONSTOP);
1990 file.SetCodepage(ucr::CP_UTF_8);
1993 _T("<!DOCTYPE html>\n")
1996 _T("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n")
1997 _T("<title>WinMerge Image Compare Report</title>\n")
1998 _T("<style type=\"text/css\">\n")
1999 _T("table { table-layout: fixed; width: 100%; height: 100%; border-collapse: collapse; }\n")
2000 _T("td,th { border: solid 1px black; }\n")
2001 _T(".title { color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
2002 _T(".img { overflow: scroll; text-align: center; }\n")
2008 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2009 file.WriteString(strutils::format(_T("<th class=\"title\">%s</th>\n"), imgfilepath[i]));
2013 for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2015 strutils::format(_T("<td><div class=\"img\"><img src=\"%s\" alt=\"%s\"></div></td>\n"),
2016 diffimg_filename[i], diffimg_filename[i]));
2026 * @brief Generate report from file compare results.
2028 void CImgMergeFrame::OnToolsGenerateReport()
2033 if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
2038 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
2041 void CImgMergeFrame::OnRefresh()
2043 if (UpdateDiffItem(m_pDirDoc) == 0)
2044 LangMessageBox(IDS_FILESSAME, MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN);
2047 void CImgMergeFrame::OnDropFiles(int pane, const std::vector<String>& files)
2049 if (files.size() > 1 || paths::IsDirectory(files[0]))
2051 GetMainFrame()->GetDropHandler()->GetCallback()(files);
2055 ChangeFile(pane, files[0]);