OSDN Git Service

18e35071fbe654495d9e056c3602ad5f06d00392
[winmerge-jp/winmerge-jp.git] / Src / ImgMergeFrm.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //    SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
7 /** 
8  * @file  ImgMergeFrm.cpp
9  *
10  * @brief Implementation file for CImgMergeFrame
11  *
12  */
13
14 #include "stdafx.h"
15 #include "ImgMergeFrm.h"
16 #include "Merge.h"
17 #include "MainFrm.h"
18 #include "BCMenu.h"
19 #include "DirDoc.h"
20 #include "OptionsDef.h"
21 #include "OptionsMgr.h"
22 #include "OptionsDiffColors.h"
23 #include "OptionsCustomColors.h"
24 #include "paths.h"
25 #include "PathContext.h"
26 #include "unicoder.h"
27 #include "FileOrFolderSelect.h"
28 #include "UniFile.h"
29 #include "SaveClosingDlg.h"
30 #include "FileLocation.h"
31 #include "Constants.h"
32 #include "DropHandler.h"
33 #include <cmath>
34
35 #ifdef _DEBUG
36 #define new DEBUG_NEW
37 #endif
38
39 /** @brief Location for image compare specific help to open. */
40 static const TCHAR ImgMergeFrameHelpLocation[] = _T("::/htmlhelp/Compare_images.html");
41
42 /////////////////////////////////////////////////////////////////////////////
43 // CImgMergeFrame
44
45 IMPLEMENT_DYNCREATE(CImgMergeFrame, CMergeFrameCommon)
46
47 BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
48         //{{AFX_MSG_MAP(CImgMergeFrame)
49         ON_WM_CREATE()
50         ON_WM_CLOSE()
51         ON_WM_DESTROY()
52         ON_WM_MDIACTIVATE()
53         ON_WM_SIZE()
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)
151         ON_WM_SETFOCUS ()
152         ON_COMMAND(ID_HELP, OnHelp)
153         //}}AFX_MSG_MAP
154 END_MESSAGE_MAP()
155
156 CMenu CImgMergeFrame::menu;
157
158 /////////////////////////////////////////////////////////////////////////////
159 // CImgMergeFrame construction/destruction
160
161 CImgMergeFrame::CImgMergeFrame()
162 : CMergeFrameCommon(IDI_EQUALIMAGE, IDI_NOTEQUALIMAGE)
163 , m_pDirDoc(nullptr)
164 , m_bAutoMerged(false)
165 , m_pImgMergeWindow(nullptr)
166 , m_pImgToolWindow(nullptr)
167 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
168 , m_bRO{}
169 , m_nActivePane(-1)
170 {
171 }
172
173 CImgMergeFrame::~CImgMergeFrame()
174 {
175         if (m_pDirDoc != nullptr)
176         {
177                 m_pDirDoc->MergeDocClosing(this);
178                 m_pDirDoc = nullptr;
179         }
180
181         HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
182         if (hModule != nullptr)
183         {
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)
189                 {
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;
196                 }
197         }
198 }
199
200 bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent)
201 {
202         int nNormalBuffer = 0;
203         for (int pane = 0; pane < nFiles; ++pane)
204         {
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;
210                 else
211                 {
212                         m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
213                         ++nNormalBuffer;
214                 }
215         }
216         SetTitle(nullptr);
217
218         LPCTSTR lpszWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
219                         ::LoadCursor(nullptr, IDC_ARROW), (HBRUSH)(COLOR_WINDOW+1), nullptr);
220
221         if (!CMergeFrameCommon::Create(lpszWndClass, GetTitle(), WS_OVERLAPPEDWINDOW | WS_CHILD, rectDefault, pParent))
222                 return false;
223
224         int nCmdShow = SW_SHOW;
225         if (GetOptionsMgr()->GetBool(OPT_ACTIVE_FRAME_MAX))
226                 nCmdShow = SW_SHOWMAXIMIZED;
227         ShowWindow(nCmdShow);
228         BringToTop(nCmdShow);
229
230         GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
231
232         if (nNormalBuffer > 0)
233                 OnRefresh();
234         else
235                 UpdateDiffItem(m_pDirDoc);
236
237         if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
238                 m_pImgMergeWindow->FirstDiff();
239
240         return true;
241 }
242
243 void CImgMergeFrame::MoveOnLoad(int nPane, int)
244 {
245         if (nPane < 0)
246         {
247                 nPane = GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE);
248                 if (nPane < 0 || nPane >= m_pImgMergeWindow->GetPaneCount())
249                         nPane = 0;
250         }
251
252         m_pImgMergeWindow->SetActivePane(nPane);
253 }
254
255 void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
256 {
257         if (!PromptAndSaveIfNeeded(true))
258                 return;
259
260         for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
261                 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
262
263         m_filePaths[nBuffer] = path;
264         m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
265         m_strDesc[nBuffer] = _T("");
266
267         if (m_filePaths.GetSize() == 2)
268                 m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
269         else
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());
271
272         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
273         {
274                 m_fileInfo[pane].Update(m_filePaths[pane]);
275
276                 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
277                         new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
278         }
279
280         UpdateHeaderPath(nBuffer);
281         UpdateLastCompareResult();
282 }
283
284 bool CImgMergeFrame::IsModified() const
285 {
286         for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
287                 if (m_pImgMergeWindow->IsModified(pane))
288                         return true;
289         return false;
290 }
291
292 void CImgMergeFrame::DoAutoMerge(int dstPane)
293 {
294         int autoMergedCount = m_pImgMergeWindow->CopyDiff3Way(dstPane);
295         if (autoMergedCount > 0)
296                 m_bAutoMerged = true;
297
298         // move to first conflict 
299         m_pImgMergeWindow->FirstConflict();
300
301         AfxMessageBox(
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(),
306                 MB_ICONINFORMATION);
307 }
308
309 /**
310  * @brief DirDoc gives us its identity just after it creates us
311  */
312 void CImgMergeFrame::SetDirDoc(CDirDoc * pDirDoc)
313 {
314         ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
315         m_pDirDoc = pDirDoc;
316 }
317
318 IMergeDoc::FileChange CImgMergeFrame::IsFileChangedOnDisk(int pane) const
319 {
320         DiffFileInfo dfi;
321         if (!dfi.Update(m_filePaths[pane]))
322                 return FileChange::Removed;
323         int tolerance = 0;
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;
331 }
332
333 void CImgMergeFrame::CheckFileChanged(void)
334 {
335         for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
336         {
337                 if (IsFileChangedOnDisk(pane) == FileChange::Changed)
338                 {
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)
341                         {
342                                 OnFileReload();
343                         }
344                         break;
345                 }
346         }
347 }
348
349 /**
350  * @brief Create a status bar to be associated with a heksedit control
351  */
352 void CImgMergeFrame::CreateImgWndStatusBar(CStatusBar &wndStatusBar, CWnd *pwndPane)
353 {
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);
359 }
360
361 void CImgMergeFrame::OnChildPaneEvent(const IImgMergeWindow::Event& evt)
362 {
363         if (evt.eventType == IImgMergeWindow::KEYDOWN)
364         {
365                 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
366                 switch (evt.keycode)
367                 {
368                 case VK_PRIOR:
369                 case VK_NEXT:
370                         ::SendMessage(pFrame->m_pImgMergeWindow->GetPaneHWND(evt.pane), WM_VSCROLL, evt.keycode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0);
371                         break;
372                 case VK_LEFT:
373                 case VK_RIGHT:
374                 case VK_UP:
375                 case VK_DOWN:
376                         if (::GetAsyncKeyState(VK_SHIFT) & 0x8000)
377                         {
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);
383                         }
384                         break;
385                 }
386         }
387
388 /*      if (evt.eventType == IImgMergeWindow::CONTEXTMENU)
389         {
390                 CImgMergeFrame *pFrame = reinterpret_cast<CImgMergeFrame *>(evt.userdata);
391                 BCMenu menu;
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());
396         }
397         */
398 }
399
400 /**
401  * @brief returns true if WinIMergeLib.dll is loadable
402  */
403 bool CImgMergeFrame::IsLoadable()
404 {
405         static HMODULE hModule;
406         if (hModule == nullptr)
407         {
408                 hModule = LoadLibraryW(L"WinIMerge\\WinIMergeLib.dll");
409                 if (hModule == nullptr)
410                         return false;
411         }
412         return true;
413 }
414
415 /**
416  * @brief Create the splitter, the filename bar, the status bar, and the two views
417  */
418 BOOL CImgMergeFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
419         CCreateContext* pContext)
420 {
421         if (!IsLoadable())
422                 return FALSE;
423
424         HMODULE hModule = GetModuleHandleW(L"WinIMergeLib.dll");
425         if (hModule == nullptr)
426                 return FALSE;
427
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)
432         {
433                 FreeLibrary(hModule);
434                 return FALSE;
435         }
436
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);
444         LoadOptions();
445
446         bool bResult;
447         if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
448         {
449                 bResult = m_pImgMergeWindow->NewImages(m_filePaths.GetSize(), 1, 256, 256);
450         }
451         else
452         {
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());
455                 else
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());
457         }
458
459         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
460         {
461                 m_fileInfo[pane].Update(m_filePaths[pane]);
462
463                 RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
464                         new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
465         }
466
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))
472         {
473                 TRACE0("Failed to create LocationBar\n");
474                 return FALSE;
475         }
476
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)
481         {
482                 return FALSE;
483         }
484
485         m_pImgToolWindow->Translate(TranslateLocationPane);
486
487         m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
488
489         return TRUE;
490 }
491
492 void CImgMergeFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
493 {
494         swprintf_s(dst, dstbufsize, L"%s", tr("ImgMergeFrame|LocationPane", ucr::toUTF8(org)).c_str());
495 }
496
497 /////////////////////////////////////////////////////////////////////////////
498 // CImgMergeFrame message handlers
499
500 int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
501 {
502         if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
503                 return -1;
504
505         EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
506
507         CMergeFrameCommon::RemoveBarBorder();
508
509         // Merge frame has a header bar at top
510         if (!m_wndFilePathBar.Create(this))
511         {
512                 TRACE0("Failed to create dialog bar\n");
513                 return -1;      // fail to create
514         }
515
516         m_wndFilePathBar.SetPaneCount(m_pImgMergeWindow->GetPaneCount());
517         m_wndFilePathBar.SetOnSetFocusCallback([&](int pane) { m_pImgMergeWindow->SetActivePane(pane); });
518
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);
525
526         for (int nPane = 0; nPane < m_pImgMergeWindow->GetPaneCount(); nPane++)
527         {
528                 m_pImgMergeWindow->SetReadOnly(nPane, m_bRO[nPane]);
529
530                 m_wndFilePathBar.SetActive(nPane, FALSE);
531                 CreateImgWndStatusBar(m_wndStatusBar[nPane], CWnd::FromHandle(m_pImgMergeWindow->GetPaneHWND(nPane)));
532                 UpdateHeaderPath(nPane);
533         }
534
535         CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
536         m_rectBorder.bottom = size.cy;
537
538         CDockState pDockState;
539         pDockState.LoadState(_T("Settings-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"));
544
545         return 0;
546 }
547
548 /**
549 * @brief We must use this function before a call to SetDockState
550 *
551 * @note Without this, SetDockState will assert or crash if a bar from the
552 * CDockState is missing in the current CMergeEditFrame.
553 * The bars are identified with their ID. This means the missing bar bug is triggered
554 * when we run WinMerge after changing the ID of a bar.
555 */
556 bool CImgMergeFrame::EnsureValidDockState(CDockState& state)
557 {
558         for (int i = (int)state.m_arrBarInfo.GetSize() - 1; i >= 0; i--)
559         {
560                 bool barIsCorrect = true;
561                 CControlBarInfo* pInfo = (CControlBarInfo*)state.m_arrBarInfo[i];
562                 if (pInfo == nullptr)
563                         barIsCorrect = false;
564                 else
565                 {
566                         if (!pInfo->m_bFloating)
567                         {
568                                 pInfo->m_pBar = GetControlBar(pInfo->m_nBarID);
569                                 if (pInfo->m_pBar == nullptr)
570                                         barIsCorrect = false; //toolbar id's probably changed   
571                         }
572                 }
573
574                 if (!barIsCorrect)
575                         state.m_arrBarInfo.RemoveAt(i);
576         }
577         return true;
578 }
579
580 /**
581  * @brief Save the window's position, free related resources, and destroy the window
582  */
583 BOOL CImgMergeFrame::DestroyWindow() 
584 {
585         SavePosition();
586         SaveActivePane();
587         SaveOptions();
588         SaveWindowState();
589         return CMergeFrameCommon::DestroyWindow();
590 }
591
592 void CImgMergeFrame::LoadOptions()
593 {
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);
608 }
609
610 void CImgMergeFrame::SaveOptions()
611 {
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));
625 }
626 /**
627  * @brief Save coordinates of the frame, splitters, and bars
628  *
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
631  */
632 void CImgMergeFrame::SavePosition()
633 {
634         CRect rc;
635         GetWindowRect(&rc);
636
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"));
644 }
645
646 void CImgMergeFrame::SaveActivePane()
647 {
648         GetOptionsMgr()->SaveOption(OPT_ACTIVE_PANE, m_pImgMergeWindow->GetActivePane());
649 }
650
651 void CImgMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
652 {
653         CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
654
655         if (bActivate)
656         {
657                 GetMainFrame()->PostMessage(WM_USER + 1);
658         }
659 }
660
661 void CImgMergeFrame::OnClose() 
662 {
663         // Allow user to cancel closing
664         if (!PromptAndSaveIfNeeded(true))
665                 return;
666
667         // clean up pointers.
668         CMergeFrameCommon::OnClose();
669
670         GetMainFrame()->ClearStatusbarItemCount();
671 }
672
673 void CImgMergeFrame::OnDestroy()
674 {
675         if (!m_pImgMergeWindow)
676                 return;
677         for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
678                 RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
679 }
680
681 bool CImgMergeFrame::DoFileSave(int pane)
682 {
683         if (m_pImgMergeWindow->IsModified(pane))
684         {
685                 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED)
686                         DoFileSaveAs(pane);
687                 else
688                 {
689                         String filename = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
690                         bool bApplyToAll = false;
691                         if (theApp.HandleReadonlySave(filename, false, bApplyToAll) == IDCANCEL)
692                                 return false;
693                         theApp.CreateBackup(false, filename);
694                         if (!m_pImgMergeWindow->SaveImage(pane))
695                         {
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);
698                                 if (answer == IDOK)
699                                         return DoFileSaveAs(pane);
700                                 return false;
701                         }
702                 }
703                 UpdateDiffItem(m_pDirDoc);
704                 m_fileInfo[pane].Update(m_filePaths[pane]);
705         }
706         return true;
707 }
708
709 bool CImgMergeFrame::DoFileSaveAs(int pane)
710 {
711         const String &path = m_filePaths.GetPath(pane);
712         String strPath;
713         String title;
714         if (pane == 0)
715                 title = _("Save Left File As");
716         else if (pane == m_pImgMergeWindow->GetPaneCount() - 1)
717                 title = _("Save Right File As");
718         else
719                 title = _("Save Middle File As");
720 RETRY:
721         if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
722         {
723                 std::wstring filename = ucr::toUTF16(strPath);
724                 if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
725                 {
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);
728                         if (answer == IDOK)
729                                 goto RETRY;
730                         return false;
731                 }
732                 if (path.empty())
733                 {
734                         // We are saving scratchpad (unnamed file)
735                         m_nBufferType[pane] = BUFFERTYPE::UNNAMED_SAVED;
736                         m_strDesc[pane].erase();
737                 }
738
739                 m_filePaths.SetPath(pane, strPath);
740                 UpdateDiffItem(m_pDirDoc);
741                 m_fileInfo[pane].Update(m_filePaths[pane]);
742                 UpdateHeaderPath(pane);
743         }
744         return true;
745 }
746
747 /**
748  * @brief Saves both files
749  */
750 void CImgMergeFrame::OnFileSave() 
751 {
752         for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
753                 DoFileSave(pane);
754 }
755
756 /**
757  * @brief Called when "Save" item is updated
758  */
759 void CImgMergeFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
760 {
761         pCmdUI->Enable(IsModified());
762 }
763
764 /**
765  * @brief Saves left-side file
766  */
767 void CImgMergeFrame::OnFileSaveLeft() 
768 {
769         DoFileSave(0);
770 }
771
772 /**
773  * @brief Called when "Save middle (...)" item is updated
774  */
775 void CImgMergeFrame::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
776 {
777         pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
778 }
779
780 /**
781  * @brief Saves middle-side file
782  */
783 void CImgMergeFrame::OnFileSaveMiddle()
784 {
785         DoFileSave(1);
786 }
787
788 /**
789  * @brief Saves right-side file
790  */
791 void CImgMergeFrame::OnFileSaveRight()
792 {
793         DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
794 }
795
796 /**
797  * @brief Called when "Save middle (as...)" item is updated
798  */
799 void CImgMergeFrame::OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI)
800 {
801         pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 ? true : false);
802 }
803
804 /**
805  * @brief Saves left-side file with name asked
806  */
807 void CImgMergeFrame::OnFileSaveAsLeft()
808 {
809         DoFileSaveAs(0);
810 }
811
812 /**
813  * @brief Saves middle-side file with name asked
814  */
815 void CImgMergeFrame::OnFileSaveAsMiddle()
816 {
817         DoFileSaveAs(1);
818 }
819
820 /**
821  * @brief Saves right-side file with name asked
822  */
823 void CImgMergeFrame::OnFileSaveAsRight()
824 {
825         DoFileSaveAs(m_pImgMergeWindow->GetPaneCount() - 1);
826 }
827
828 /**
829  * @brief Reloads the opened files
830  */
831 void CImgMergeFrame::OnFileReload()
832 {
833         if (!PromptAndSaveIfNeeded(true))
834                 return;
835         m_pImgMergeWindow->ReloadImages();
836         for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
837                 m_fileInfo[pane].Update(m_filePaths[pane]);
838 }
839
840 void CImgMergeFrame::OnFileClose() 
841 {
842         OnClose();
843 }
844
845 /**
846  * @brief Enable/disable left buffer read-only
847  */
848 void CImgMergeFrame::OnLeftReadOnly()
849 {
850         m_bRO[0] = !m_bRO[0];
851         m_pImgMergeWindow->SetReadOnly(0, m_bRO[0]);
852 }
853
854 /**
855  * @brief Called when "Left read-only" item is updated
856  */
857 void CImgMergeFrame::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
858 {
859         pCmdUI->Enable(true);
860         pCmdUI->SetCheck(m_bRO[0]);
861 }
862
863 /**
864  * @brief Enable/disable middle buffer read-only
865  */
866 void CImgMergeFrame::OnMiddleReadOnly()
867 {
868         if (m_pImgMergeWindow->GetPaneCount() == 3)
869         {
870                 m_bRO[1] = !m_bRO[1];
871                 m_pImgMergeWindow->SetReadOnly(1, m_bRO[1]);
872         }
873 }
874
875 /**
876  * @brief Called when "Middle read-only" item is updated
877  */
878 void CImgMergeFrame::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
879 {
880         if (m_pImgMergeWindow->GetPaneCount() < 3)
881         {
882                 pCmdUI->Enable(false);
883         }
884         else
885         {
886                 pCmdUI->Enable(true);
887                 pCmdUI->SetCheck(m_bRO[1]);
888         }
889 }
890
891 /**
892  * @brief Enable/disable right buffer read-only
893  */
894 void CImgMergeFrame::OnRightReadOnly()
895 {
896         int pane = m_pImgMergeWindow->GetPaneCount() - 1;
897         m_bRO[pane] = !m_bRO[pane];
898         m_pImgMergeWindow->SetReadOnly(pane, m_bRO[pane]);
899 }
900
901 /**
902  * @brief Called when "Right read-only" item is updated
903  */
904 void CImgMergeFrame::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
905 {
906         pCmdUI->Enable(true);
907         pCmdUI->SetCheck(m_pImgMergeWindow->GetReadOnly(m_pImgMergeWindow->GetPaneCount() - 1));
908 }
909
910 void CImgMergeFrame::OnFileRecompareAs(UINT nId)
911 {
912         FileLocation fileloc[3];
913         DWORD dwFlags[3];
914         String strDesc[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)
919         {
920                 fileloc[nBuffer].setPath(m_filePaths[nBuffer]);
921                 dwFlags[nBuffer] = m_bRO[nBuffer] ? FFILEOPEN_READONLY : 0;
922                 strDesc[nBuffer] = m_strDesc[nBuffer];
923         }
924         CloseNow();
925         GetMainFrame()->ShowMergeDoc(nId, pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
926 }
927
928 void CImgMergeFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
929 {
930         pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_XML);
931 }
932
933 void  CImgMergeFrame::OnWindowChangePane() 
934 {
935         m_pImgMergeWindow->SetActivePane((m_pImgMergeWindow->GetActivePane() + 1) % m_pImgMergeWindow->GetPaneCount());
936 }
937
938 /**
939  * @brief Write path and filename to headerbar
940  * @note SetText() does not repaint unchanged text
941  */
942 void CImgMergeFrame::UpdateHeaderPath(int pane)
943 {
944         String sText;
945
946         if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
947                 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
948         {
949                 sText = m_strDesc[pane];
950         }
951         else
952         {
953                 sText = m_filePaths.GetPath(pane);
954                 if (m_pDirDoc != nullptr)
955                         m_pDirDoc->ApplyDisplayRoot(pane, sText);
956         }
957         if (m_pImgMergeWindow->IsModified(pane))
958                 sText.insert(0, _T("* "));
959
960         m_wndFilePathBar.SetText(pane, sText.c_str());
961
962         SetTitle(nullptr);
963 }
964
965 /// update splitting position for panels 1/2 and headerbar and statusbar 
966 void CImgMergeFrame::UpdateHeaderSizes()
967 {
968         if (m_pImgMergeWindow != nullptr)
969         {
970                 int w[3];
971                 CRect rc, rcMergeWindow;
972                 int nPaneCount = m_pImgMergeWindow->GetPaneCount();
973                 GetClientRect(&rc);
974                 ::GetWindowRect(m_pImgMergeWindow->GetHWND(), &rcMergeWindow);
975                 ScreenToClient(rcMergeWindow);
976                 if (!m_pImgMergeWindow->GetHorizontalSplit())
977                 {
978                         for (int pane = 0; pane < nPaneCount; pane++)
979                         {
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)
983                         }
984                 }
985                 else
986                 {
987                         for (int pane = 0; pane < nPaneCount; pane++)
988                                 w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
989                 }
990
991                 if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
992                 {
993                         std::copy_n(w, nPaneCount - 1, m_nLastSplitPos);
994
995                         // resize controls in header dialog bar
996                         m_wndFilePathBar.Resize(w);
997
998                         rc.left = rcMergeWindow.left;
999                         rc.top = rc.bottom - m_rectBorder.bottom;
1000                         rc.right = rc.left;
1001                         for (int pane = 0; pane < nPaneCount; pane++)
1002                         {
1003                                 rc.right += w[pane] + 4 + 2;
1004                                 m_wndStatusBar[pane].MoveWindow(&rc);
1005                                 rc.left = rc.right;
1006                         }
1007                 }
1008         }
1009 }
1010
1011 /**
1012  * @brief Update document filenames to title
1013  */
1014 void CImgMergeFrame::SetTitle(LPCTSTR lpszTitle)
1015 {
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());
1020 }
1021
1022 void CImgMergeFrame::UpdateLastCompareResult()
1023 {
1024         SetLastCompareResult(m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0);
1025 }
1026
1027 void CImgMergeFrame::UpdateAutoPaneResize()
1028 {
1029 }
1030
1031 void CImgMergeFrame::UpdateSplitter()
1032 {
1033 }
1034
1035 /**
1036  * @brief Update associated diff item
1037  */
1038 int CImgMergeFrame::UpdateDiffItem(CDirDoc *pDirDoc)
1039 {
1040         // If directory compare has results
1041         if (pDirDoc && pDirDoc->HasDiffs())
1042         {
1043                 const String &pathLeft = m_filePaths.GetLeft();
1044                 const String &pathRight = m_filePaths.GetRight();
1045                 CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
1046 // FIXME:
1047 //              if (UINT_PTR pos = pDirDoc->FindItemFromPaths(pathLeft, pathRight))
1048 //              {
1049 //                      DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
1050 //                      ::UpdateDiffItem(m_nBuffers, di, &ctxt);
1051 //              }
1052         }
1053         int result = m_pImgMergeWindow->GetDiffCount() > 0 ? 1 : 0;
1054         SetLastCompareResult(result != 0);
1055         return result;
1056 }
1057
1058 /**
1059  * @brief Asks and then saves modified files.
1060  *
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.
1073  */
1074 bool CImgMergeFrame::PromptAndSaveIfNeeded(bool bAllowCancel)
1075 {
1076         bool bLModified = false, bMModified = false, bRModified = false;
1077         bool result = true;
1078         bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
1079
1080         if (m_pImgMergeWindow->GetPaneCount() == 3)
1081         {
1082                 bLModified = m_pImgMergeWindow->IsModified(0);
1083                 bMModified = m_pImgMergeWindow->IsModified(1);
1084                 bRModified = m_pImgMergeWindow->IsModified(2);
1085         }
1086         else
1087         {
1088                 bLModified = m_pImgMergeWindow->IsModified(0);
1089                 bRModified = m_pImgMergeWindow->IsModified(1);
1090         }
1091         if (!bLModified && !bMModified && !bRModified)
1092                  return true;
1093
1094         SaveClosingDlg dlg;
1095         dlg.DoAskFor(bLModified, bMModified, bRModified);
1096         if (!bAllowCancel)
1097                 dlg.m_bDisableCancel = true;
1098         if (!m_filePaths.GetLeft().empty())
1099         {
1100                 if (theApp.m_strSaveAsPath.empty())
1101                         dlg.m_sLeftFile = m_filePaths.GetLeft();
1102                 else
1103                         dlg.m_sLeftFile = theApp.m_strSaveAsPath;
1104         }
1105         else
1106                 dlg.m_sLeftFile = m_strDesc[0];
1107         if (m_pImgMergeWindow->GetPaneCount() == 3)
1108         {
1109                 if (!m_filePaths.GetMiddle().empty())
1110                 {
1111                         if (theApp.m_strSaveAsPath.empty())
1112                                 dlg.m_sMiddleFile = m_filePaths.GetMiddle();
1113                         else
1114                                 dlg.m_sMiddleFile = theApp.m_strSaveAsPath;
1115                 }
1116                 else
1117                         dlg.m_sMiddleFile = m_strDesc[1];
1118         }
1119         if (!m_filePaths.GetRight().empty())
1120         {
1121                 if (theApp.m_strSaveAsPath.empty())
1122                         dlg.m_sRightFile = m_filePaths.GetRight();
1123                 else
1124                         dlg.m_sRightFile = theApp.m_strSaveAsPath;
1125         }
1126         else
1127                 dlg.m_sRightFile = m_strDesc[m_pImgMergeWindow->GetPaneCount() - 1];
1128
1129         if (dlg.DoModal() == IDOK)
1130         {
1131                 if (bLModified && dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
1132                 {
1133                         bLSaveSuccess = DoFileSave(0);
1134                         if (!bLSaveSuccess)
1135                                 result = false;
1136                 }
1137
1138                 if (bMModified && dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
1139                 {
1140                         bMSaveSuccess = DoFileSave(1);
1141                         if (!bMSaveSuccess)
1142                                 result = false;
1143                 }
1144
1145                 if (bRModified && dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
1146                 {
1147                         bRSaveSuccess = DoFileSave(m_pImgMergeWindow->GetPaneCount() - 1);
1148                         if (!bRSaveSuccess)
1149                                 result = false;
1150                 }
1151         }
1152         else
1153         {       
1154                 result = false;
1155         }
1156
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))
1162         {
1163                 // If directory compare has results
1164                 if (m_pDirDoc && m_pDirDoc->HasDiffs())
1165                 {
1166                         // FIXME:
1167                 }
1168         }
1169
1170         return result;
1171 }
1172
1173 /// Document commanding us to close
1174 bool CImgMergeFrame::CloseNow()
1175 {
1176         // Allow user to cancel closing
1177         if (!PromptAndSaveIfNeeded(true))
1178                 return false;
1179
1180         SavePosition(); // Save settings before closing!
1181         SaveActivePane();
1182         SaveOptions();
1183         MDIActivate();
1184         MDIDestroy();
1185         return true;
1186 }
1187
1188 /**
1189  * @brief Update any resources necessary after a GUI language change
1190  */
1191 void CImgMergeFrame::UpdateResources()
1192 {
1193         m_pImgToolWindow->Translate(TranslateLocationPane);
1194 }
1195
1196 /**
1197  * @brief Handle some keys when in merging mode
1198  */
1199 bool CImgMergeFrame::MergeModeKeyDown(MSG* pMsg)
1200 {
1201         bool bHandled = false;
1202
1203         // Allow default text selection when SHIFT pressed
1204         if (::GetAsyncKeyState(VK_SHIFT))
1205                 return false;
1206
1207         // Allow default editor functions when CTRL pressed
1208         if (::GetAsyncKeyState(VK_CONTROL))
1209                 return false;
1210
1211         // If we are in merging mode (merge with cursor keys)
1212         // handle some keys here
1213         switch (pMsg->wParam)
1214         {
1215         case VK_LEFT:
1216                 OnR2l();
1217                 bHandled = true;
1218                 break;
1219
1220         case VK_RIGHT:
1221                 OnL2r();
1222                 bHandled = true;
1223                 break;
1224
1225         case VK_UP:
1226                 OnPrevdiff();
1227                 bHandled = true;
1228                 break;
1229         case VK_DOWN:
1230                 OnNextdiff();
1231                 bHandled = true;
1232                 break;
1233         }
1234
1235         return bHandled;
1236 }
1237 /**
1238  * @brief Check for keyboard commands
1239  */
1240 BOOL CImgMergeFrame::PreTranslateMessage(MSG* pMsg)
1241 {
1242         if (pMsg->message == WM_KEYDOWN)
1243         {
1244                 // If we are in merging mode (merge with cursor keys)
1245                 // handle some keys here
1246                 if (theApp.GetMergingMode())
1247                 {
1248                         bool bHandled = MergeModeKeyDown(pMsg);
1249                         if (bHandled)
1250                                 return true;
1251                 }
1252
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)
1255                 {
1256                         PostMessage(WM_CLOSE, 0, 0);
1257                         return true;
1258                 }
1259         }
1260         return CMergeFrameCommon::PreTranslateMessage(pMsg);
1261 }
1262
1263 void CImgMergeFrame::OnSize(UINT nType, int cx, int cy) 
1264 {
1265         CMergeFrameCommon::OnSize(nType, cx, cy);
1266         UpdateHeaderSizes();
1267 }
1268
1269 /**
1270  * @brief Synchronize control and status bar placements with splitter position,
1271  * update mod indicators, synchronize scrollbars
1272  */
1273 void CImgMergeFrame::OnIdleUpdateCmdUI()
1274 {
1275         if (IsWindowVisible())
1276         {
1277                 POINT pt = {-1, -1}, ptCursor;
1278                 GetCursorPos(&ptCursor);
1279                 for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
1280                 {
1281                         RECT rc;
1282                         ::GetWindowRect(m_pImgMergeWindow->GetPaneHWND(pane), &rc);
1283                         if (PtInRect(&rc, ptCursor))
1284                                 pt = m_pImgMergeWindow->GetCursorPos(pane);
1285                 }
1286                 
1287                 RGBQUAD color[3];
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);
1294
1295                 int nActivePane = m_pImgMergeWindow->GetActivePane();
1296                 if (nActivePane != -1)
1297                         m_nActivePane = nActivePane;
1298
1299                 UpdateHeaderSizes();
1300                 for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
1301                 {
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);
1306
1307                         m_wndFilePathBar.SetActive(pane, pane == nActivePane);
1308                         POINT ptReal;
1309                         String text;
1310                         if (m_pImgMergeWindow->ConvertToRealPos(pane, pt, ptReal))
1311                         {
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);
1316                                 else
1317                                         text += strutils::format(_("Dist: %g  "), colorDistance01);
1318                         }
1319                         if (m_pImgMergeWindow->IsRectangleSelectionVisible(pane))
1320                         {
1321                                 RECT rc = m_pImgMergeWindow->GetRectangleSelection(pane);
1322                                 text += strutils::format(_("Rc: (%d, %d)  "), rc.right - rc.left, rc.bottom - rc.top);
1323                         }
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)
1331                                         );
1332                         m_wndStatusBar[pane].SetPaneText(0, text.c_str());
1333                 }
1334                 UpdateLastCompareResult();
1335         }
1336         CMergeFrameCommon::OnIdleUpdateCmdUI();
1337 }
1338
1339 /**
1340  * @brief Save pane sizes and positions when one of panes requests it.
1341  */
1342 LRESULT CImgMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
1343 {
1344         SavePosition();
1345         return 0;
1346 }
1347
1348 void CImgMergeFrame::OnUpdateStatusNum(CCmdUI* pCmdUI) 
1349 {
1350         TCHAR sIdx[32] = { 0 };
1351         TCHAR sCnt[32] = { 0 };
1352         String s;
1353         const int nDiffs = m_pImgMergeWindow->GetDiffCount();
1354         
1355         // Files are identical - show text "Identical"
1356         if (nDiffs <= 0)
1357                 s = theApp.LoadString(IDS_IDENTICAL);
1358         
1359         // There are differences, but no selected diff
1360         // - show amount of diffs
1361         else if (m_pImgMergeWindow->GetCurrentDiffIndex() < 0)
1362         {
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);
1366         }
1367         
1368         // There are differences and diff selected
1369         // - show diff number and amount of diffs
1370         else
1371         {
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);
1378         }
1379         pCmdUI->SetText(s.c_str());
1380 }
1381         
1382 /**
1383  * @brief Undo last action
1384  */
1385 void CImgMergeFrame::OnEditUndo()
1386 {
1387         m_pImgMergeWindow->Undo();
1388         if (!m_pImgMergeWindow->IsUndoable())
1389                 m_bAutoMerged = false;
1390         UpdateLastCompareResult();
1391 }
1392
1393 /**
1394  * @brief Called when "Undo" item is updated
1395  */
1396 void CImgMergeFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
1397 {
1398         pCmdUI->Enable(m_pImgMergeWindow->IsUndoable());
1399 }
1400
1401 /**
1402  * @brief Redo last action
1403  */
1404 void CImgMergeFrame::OnEditRedo()
1405 {
1406         m_pImgMergeWindow->Redo();
1407         UpdateLastCompareResult();
1408 }
1409
1410 /**
1411  * @brief Called when "Redo" item is updated
1412  */
1413 void CImgMergeFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
1414 {
1415         pCmdUI->Enable(m_pImgMergeWindow->IsRedoable());
1416 }
1417
1418 /**
1419  * @brief Copy current selection to clipboard
1420  */
1421 void CImgMergeFrame::OnEditCopy()
1422 {
1423         m_pImgMergeWindow->Copy();
1424 }
1425
1426 /**
1427  * @brief Called when "Copy" item is updated
1428  */
1429 void CImgMergeFrame::OnUpdateEditCopy(CCmdUI* pCmdUI)
1430 {
1431         pCmdUI->Enable(m_pImgMergeWindow->IsCopyable());
1432 }
1433
1434 /**
1435  * @brief Cut current selection to clipboard
1436  */
1437 void CImgMergeFrame::OnEditCut()
1438 {
1439         m_pImgMergeWindow->Cut();
1440 }
1441
1442 /**
1443  * @brief Called when "Cut" item is updated
1444  */
1445 void CImgMergeFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
1446 {
1447         pCmdUI->Enable(m_pImgMergeWindow->IsCuttable());
1448 }
1449
1450 /**
1451  * @brief Paste image from clipboard
1452  */
1453 void CImgMergeFrame::OnEditPaste()
1454 {
1455         m_pImgMergeWindow->Paste();
1456 }
1457
1458 /**
1459  * @brief Called when "Paste" item is updated
1460  */
1461 void CImgMergeFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)
1462 {
1463         pCmdUI->Enable(m_pImgMergeWindow->IsPastable());
1464 }
1465
1466 /**
1467  * @brief Select entire image
1468  */
1469 void CImgMergeFrame::OnEditSelectAll()
1470 {
1471         m_pImgMergeWindow->SelectAll();
1472 }
1473
1474 /**
1475  * @brief Called when user selects View/Zoom In from menu.
1476  */
1477 void CImgMergeFrame::OnViewZoomIn()
1478 {
1479         m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() + 0.1);
1480 }
1481
1482 /**
1483  * @brief Called when user selects View/Zoom Out from menu.
1484  */
1485 void CImgMergeFrame::OnViewZoomOut()
1486 {
1487         m_pImgMergeWindow->SetZoom(m_pImgMergeWindow->GetZoom() - 0.1);
1488 }
1489
1490 /**
1491  * @brief Called when user selects View/Zoom Normal from menu.
1492  */
1493 void CImgMergeFrame::OnViewZoomNormal()
1494 {
1495         m_pImgMergeWindow->SetZoom(1.0);
1496 }
1497
1498 /**
1499  * @brief Split panes vertically
1500  */
1501 void CImgMergeFrame::OnViewSplitVertically() 
1502 {
1503         bool bSplitVertically = !m_pImgMergeWindow->GetHorizontalSplit();
1504         bSplitVertically = !bSplitVertically; // toggle
1505         GetOptionsMgr()->SaveOption(OPT_SPLIT_HORIZONTALLY, !bSplitVertically);
1506         m_pImgMergeWindow->SetHorizontalSplit(!bSplitVertically);
1507 }
1508
1509 /**
1510  * @brief Update "Split Vertically" UI items
1511  */
1512 void CImgMergeFrame::OnUpdateViewSplitVertically(CCmdUI* pCmdUI) 
1513 {
1514         pCmdUI->Enable(TRUE);
1515         pCmdUI->SetCheck(!m_pImgMergeWindow->GetHorizontalSplit());
1516 }
1517
1518 /**
1519  * @brief Go to first diff
1520  *
1521  * Called when user selects "First Difference"
1522  * @sa CImgMergeFrame::SelectDiff()
1523  */
1524 void CImgMergeFrame::OnFirstdiff()
1525 {
1526         m_pImgMergeWindow->FirstDiff();
1527 }
1528
1529 /**
1530  * @brief Update "First diff" UI items
1531  */
1532 void CImgMergeFrame::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1533 {
1534         OnUpdatePrevdiff(pCmdUI);
1535 }
1536
1537 /**
1538  * @brief Go to last diff
1539  */
1540 void CImgMergeFrame::OnLastdiff()
1541 {
1542         m_pImgMergeWindow->LastDiff();
1543 }
1544
1545 /**
1546  * @brief Update "Last diff" UI items
1547  */
1548 void CImgMergeFrame::OnUpdateLastdiff(CCmdUI* pCmdUI)
1549 {
1550         OnUpdateNextdiff(pCmdUI);
1551 }
1552
1553 /**
1554  * @brief Go to next diff and select it.
1555  */
1556 void CImgMergeFrame::OnNextdiff()
1557 {
1558         if (m_pImgMergeWindow->GetCurrentDiffIndex() != m_pImgMergeWindow->GetDiffCount() - 1)
1559                 m_pImgMergeWindow->NextDiff();
1560         else if (m_pImgMergeWindow->GetCurrentMaxPage() != m_pImgMergeWindow->GetMaxPageCount() - 1)
1561         {
1562                 if (AfxMessageBox(_("Do you want to move to the next page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1563                 {
1564                         m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1565                         UpdateLastCompareResult();
1566                 }
1567         }
1568         else if (m_pDirDoc != nullptr)
1569                 m_pDirDoc->MoveToNextDiff(this);
1570 }
1571
1572 /**
1573  * @brief Update "Next diff" UI items
1574  */
1575 void CImgMergeFrame::OnUpdateNextdiff(CCmdUI* pCmdUI)
1576 {
1577         bool enabled =
1578                 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1 ||
1579                 m_pImgMergeWindow->GetNextDiffIndex() >= 0 ||
1580                 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1581
1582         if (!enabled && m_pDirDoc != nullptr)
1583                 enabled = m_pDirDoc->MoveableToNextDiff();
1584
1585         pCmdUI->Enable(enabled);
1586 }
1587
1588 /**
1589  * @brief Go to previous diff and select it.
1590  */
1591 void CImgMergeFrame::OnPrevdiff()
1592 {
1593         if (m_pImgMergeWindow->GetCurrentDiffIndex() > 0)
1594         {
1595                 m_pImgMergeWindow->PrevDiff();
1596         }
1597         else if (m_pImgMergeWindow->GetCurrentMaxPage() != 0)
1598         {
1599                 if (AfxMessageBox(_("Do you want to move to the previous page?").c_str(), MB_YESNO | MB_DONT_ASK_AGAIN) == IDYES)
1600                 {
1601                         m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1602                         UpdateLastCompareResult();
1603                 }
1604         }
1605         else if (m_pDirDoc != nullptr)
1606                 m_pDirDoc->MoveToPrevDiff(this);
1607 }
1608
1609 /**
1610  * @brief Update "Previous diff" UI items
1611  */
1612 void CImgMergeFrame::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1613 {
1614         bool enabled =
1615                 m_pImgMergeWindow->GetCurrentMaxPage() > 0 ||
1616                 m_pImgMergeWindow->GetPrevDiffIndex() >= 0 ||
1617                 (m_pImgMergeWindow->GetDiffCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1);
1618
1619         if (!enabled && m_pDirDoc != nullptr)
1620                 enabled = m_pDirDoc->MoveableToPrevDiff();
1621
1622         pCmdUI->Enable(enabled);
1623 }
1624
1625 /**
1626  * @brief Go to next conflict and select it.
1627  */
1628 void CImgMergeFrame::OnNextConflict()
1629 {
1630         m_pImgMergeWindow->NextConflict();
1631 }
1632
1633 /**
1634  * @brief Update "Next Conflict" UI items
1635  */
1636 void CImgMergeFrame::OnUpdateNextConflict(CCmdUI* pCmdUI)
1637 {
1638         pCmdUI->Enable(
1639                 m_pImgMergeWindow->GetPaneCount() > 2 && (
1640                         m_pImgMergeWindow->GetNextConflictIndex() >= 0 ||
1641                         (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1642                 )
1643         );
1644 }
1645
1646 /**
1647  * @brief Go to previous diff and select it.
1648  */
1649 void CImgMergeFrame::OnPrevConflict()
1650 {
1651         m_pImgMergeWindow->PrevConflict();
1652 }
1653
1654 /**
1655  * @brief Update "Previous diff" UI items
1656  */
1657 void CImgMergeFrame::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1658 {
1659         pCmdUI->Enable(
1660                 m_pImgMergeWindow->GetPaneCount() > 2 && (
1661                         m_pImgMergeWindow->GetPrevConflictIndex() >= 0 ||
1662                         (m_pImgMergeWindow->GetConflictCount() > 0 && m_pImgMergeWindow->GetCurrentDiffIndex() == -1)
1663                 )
1664         );
1665 }
1666
1667 void CImgMergeFrame::OnUpdateX2Y(CCmdUI* pCmdUI, int srcPane, int dstPane)
1668 {
1669         pCmdUI->Enable(m_pImgMergeWindow->GetCurrentDiffIndex() >= 0 && 
1670                 srcPane >= 0 && srcPane <= m_pImgMergeWindow->GetPaneCount() &&
1671                 dstPane >= 0 && dstPane <= m_pImgMergeWindow->GetPaneCount() &&
1672                 !m_bRO[dstPane]
1673                 );
1674 }
1675
1676 void CImgMergeFrame::OnX2Y(int srcPane, int dstPane)
1677 {
1678         m_pImgMergeWindow->CopyDiff(m_pImgMergeWindow->GetCurrentDiffIndex(), srcPane, dstPane);
1679         UpdateLastCompareResult();
1680 }
1681
1682 /**
1683  * @brief Copy diff from left pane to right pane
1684  */
1685 void CImgMergeFrame::OnL2r()
1686 {
1687         int srcPane = m_pImgMergeWindow->GetActivePane();
1688         if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1689                 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1690         if (srcPane < 0)
1691                 srcPane = 0;
1692         int dstPane = srcPane + 1;
1693         OnX2Y(srcPane, dstPane);
1694 }
1695
1696 /**
1697  * @brief Called when "Copy to left" item is updated
1698  */
1699 void CImgMergeFrame::OnUpdateL2r(CCmdUI* pCmdUI)
1700 {
1701         int srcPane = m_pImgMergeWindow->GetActivePane();
1702         if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1703                 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1704         if (srcPane < 0)
1705                 srcPane = 0;
1706         int dstPane = srcPane + 1;
1707         OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1708 }
1709
1710 /**
1711  * @brief Copy diff from right pane to left pane
1712  */
1713 void CImgMergeFrame::OnR2l()
1714 {
1715         int srcPane = m_pImgMergeWindow->GetActivePane();
1716         if (srcPane < 1)
1717                 srcPane = 1;
1718         int dstPane = srcPane - 1;
1719         OnX2Y(srcPane, dstPane);
1720 }
1721
1722 /**
1723  * @brief Called when "Copy to right" item is updated
1724  */
1725 void CImgMergeFrame::OnUpdateR2l(CCmdUI* pCmdUI)
1726 {
1727         int srcPane = m_pImgMergeWindow->GetActivePane();
1728         if (srcPane < 1)
1729                 srcPane = 1;
1730         int dstPane = srcPane - 1;
1731         OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1732 }
1733
1734 void CImgMergeFrame::OnCopyFromLeft()
1735 {
1736         int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1737         if (srcPane < 0)
1738                 srcPane = 0;
1739         int dstPane = srcPane + 1;
1740         OnX2Y(srcPane, dstPane);
1741 }
1742
1743 /**
1744  * @brief Called when "Copy from left" item is updated
1745  */
1746 void CImgMergeFrame::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1747 {
1748         int srcPane = m_pImgMergeWindow->GetActivePane() - 1;
1749         if (srcPane < 0)
1750                 srcPane = 0;
1751         int dstPane = srcPane + 1;
1752         OnUpdateX2Y(pCmdUI, srcPane, dstPane);
1753 }
1754
1755 void CImgMergeFrame::OnCopyFromRight()
1756 {
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);
1762 }
1763
1764 /**
1765  * @brief Called when "Copy from right" item is updated
1766  */
1767 void CImgMergeFrame::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1768 {
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);
1774 }
1775
1776 /**
1777  * @brief Copy all diffs from right pane to left pane
1778  */
1779 void CImgMergeFrame::OnAllLeft()
1780 {
1781         int srcPane = m_pImgMergeWindow->GetActivePane();
1782         if (srcPane < 1)
1783                 srcPane = 1;
1784         int dstPane = srcPane - 1;
1785
1786         CWaitCursor waitstatus;
1787
1788         m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1789         UpdateLastCompareResult();
1790 }
1791
1792 /**
1793  * @brief Called when "Copy all to left" item is updated
1794  */
1795 void CImgMergeFrame::OnUpdateAllLeft(CCmdUI* pCmdUI)
1796 {
1797         int srcPane = m_pImgMergeWindow->GetActivePane();
1798         if (srcPane < 1)
1799                 srcPane = 1;
1800         int dstPane = srcPane - 1;
1801
1802         pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1803 }
1804
1805 /**
1806  * @brief Copy all diffs from left pane to right pane
1807  */
1808 void CImgMergeFrame::OnAllRight()
1809 {
1810         int srcPane = m_pImgMergeWindow->GetActivePane();
1811         if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1812                 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1813         if (srcPane < 0)
1814                 srcPane = 0;
1815         int dstPane = srcPane + 1;
1816
1817         CWaitCursor waitstatus;
1818
1819         m_pImgMergeWindow->CopyDiffAll(srcPane, dstPane);
1820         UpdateLastCompareResult();
1821 }
1822
1823 /**
1824  * @brief Called when "Copy all to right" item is updated
1825  */
1826 void CImgMergeFrame::OnUpdateAllRight(CCmdUI* pCmdUI)
1827 {
1828         int srcPane = m_pImgMergeWindow->GetActivePane();
1829         if (srcPane >= m_pImgMergeWindow->GetPaneCount() - 1)
1830                 srcPane = m_pImgMergeWindow->GetPaneCount() - 2;
1831         if (srcPane < 0)
1832                 srcPane = 0;
1833         int dstPane = srcPane + 1;
1834
1835         pCmdUI->Enable(m_pImgMergeWindow->GetDiffCount() > 0 && !m_bRO[dstPane]);
1836 }
1837
1838 /**
1839  * @brief Do Auto merge
1840  */
1841 void CImgMergeFrame::OnAutoMerge()
1842 {
1843         int dstPane = m_pImgMergeWindow->GetActivePane();
1844         
1845         // Check current pane is not readonly
1846         if (dstPane < 0 || IsModified() || m_bAutoMerged || m_bRO[dstPane])
1847                 return;
1848
1849         CWaitCursor waitstatus;
1850
1851         DoAutoMerge(dstPane);
1852 }
1853
1854 /**
1855  * @brief Called when "Auto Merge" item is updated
1856  */
1857 void CImgMergeFrame::OnUpdateAutoMerge(CCmdUI* pCmdUI)
1858 {
1859         int dstPane = m_pImgMergeWindow->GetActivePane();
1860         
1861         pCmdUI->Enable(m_pImgMergeWindow->GetPaneCount() == 3 && 
1862                 dstPane >= 0 && !IsModified() && !m_bAutoMerged && !m_bRO[dstPane]);
1863 }
1864
1865 void CImgMergeFrame::OnImgViewDifferences()
1866 {
1867         m_pImgMergeWindow->SetShowDifferences(!m_pImgMergeWindow->GetShowDifferences());
1868         SaveOptions();
1869 }
1870
1871 void CImgMergeFrame::OnUpdateImgViewDifferences(CCmdUI* pCmdUI)
1872 {
1873         pCmdUI->SetCheck(m_pImgMergeWindow->GetShowDifferences() ? 1 : 0);
1874 }
1875
1876 void CImgMergeFrame::OnImgZoom(UINT nId)
1877 {
1878         m_pImgMergeWindow->SetZoom(pow(2.0, int(nId - ID_IMG_ZOOM_100)));
1879         SaveOptions();
1880 }
1881
1882 void CImgMergeFrame::OnUpdateImgZoom(CCmdUI* pCmdUI)
1883 {
1884         pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_ZOOM_100)) == m_pImgMergeWindow->GetZoom());
1885 }
1886
1887 void CImgMergeFrame::OnImgOverlayMode(UINT nId)
1888 {
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);
1897         SaveOptions();
1898 }
1899
1900 void CImgMergeFrame::OnUpdateImgOverlayMode(CCmdUI* pCmdUI)
1901 {
1902         pCmdUI->SetRadio(static_cast<IImgMergeWindow::OVERLAY_MODE>(pCmdUI->m_nID - ID_IMG_OVERLAY_NONE) == m_pImgMergeWindow->GetOverlayMode());
1903 }
1904
1905 void CImgMergeFrame::OnImgDraggingMode(UINT nId)
1906 {
1907         m_pImgMergeWindow->SetDraggingMode(static_cast<IImgMergeWindow::DRAGGING_MODE>(nId - ID_IMG_DRAGGINGMODE_NONE));
1908         SaveOptions();
1909 }
1910
1911 void CImgMergeFrame::OnUpdateImgDraggingMode(CCmdUI* pCmdUI)
1912 {
1913         pCmdUI->SetRadio(static_cast<IImgMergeWindow::DRAGGING_MODE>(pCmdUI->m_nID - ID_IMG_DRAGGINGMODE_NONE) == m_pImgMergeWindow->GetDraggingMode());
1914 }
1915
1916 void CImgMergeFrame::OnImgDiffBlockSize(UINT nId)
1917 {
1918         m_pImgMergeWindow->SetDiffBlockSize(1 << (nId - ID_IMG_DIFFBLOCKSIZE_1));
1919         SaveOptions();
1920 }
1921
1922 void CImgMergeFrame::OnUpdateImgDiffBlockSize(CCmdUI* pCmdUI)
1923 {
1924         pCmdUI->SetRadio(1 << (pCmdUI->m_nID - ID_IMG_DIFFBLOCKSIZE_1) == m_pImgMergeWindow->GetDiffBlockSize() );
1925 }
1926
1927 void CImgMergeFrame::OnImgThreshold(UINT nId)
1928 {
1929         if (nId == ID_IMG_THRESHOLD_0)
1930                 m_pImgMergeWindow->SetColorDistanceThreshold(0.0);
1931         else
1932                 m_pImgMergeWindow->SetColorDistanceThreshold((1 << (nId - ID_IMG_THRESHOLD_2)) * 2);
1933         SaveOptions();
1934 }
1935
1936 void CImgMergeFrame::OnUpdateImgThreshold(CCmdUI* pCmdUI)
1937 {
1938         if (pCmdUI->m_nID == ID_IMG_THRESHOLD_0)
1939                 pCmdUI->SetRadio(m_pImgMergeWindow->GetColorDistanceThreshold() == 0.0);
1940         else
1941                 pCmdUI->SetRadio((1 << (pCmdUI->m_nID - ID_IMG_THRESHOLD_2)) * 2 == m_pImgMergeWindow->GetColorDistanceThreshold() );
1942 }
1943
1944 void CImgMergeFrame::OnImgInsertionDeletionDetectionMode(UINT nId)
1945 {
1946         m_pImgMergeWindow->SetInsertionDeletionDetectionMode(static_cast<IImgMergeWindow::INSERTION_DELETION_DETECTION_MODE>(nId - ID_IMG_INSERTIONDELETIONDETECTION_NONE));
1947         SaveOptions();
1948 }
1949
1950 void CImgMergeFrame::OnUpdateImgInsertionDeletionDetectionMode(CCmdUI* pCmdUI)
1951 {
1952         pCmdUI->SetRadio(static_cast<unsigned>(m_pImgMergeWindow->GetInsertionDeletionDetectionMode() + ID_IMG_INSERTIONDELETIONDETECTION_NONE) == pCmdUI->m_nID);
1953 }
1954
1955 void CImgMergeFrame::OnImgPrevPage()
1956 {
1957         m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() - 1);
1958         UpdateLastCompareResult();
1959 }
1960
1961 void CImgMergeFrame::OnUpdateImgPrevPage(CCmdUI* pCmdUI)
1962 {
1963         pCmdUI->Enable(m_pImgMergeWindow->GetCurrentMaxPage() > 0);
1964 }
1965
1966 void CImgMergeFrame::OnImgNextPage()
1967 {
1968         m_pImgMergeWindow->SetCurrentPageAll(m_pImgMergeWindow->GetCurrentMaxPage() + 1);
1969         UpdateLastCompareResult();
1970 }
1971
1972 void CImgMergeFrame::OnUpdateImgNextPage(CCmdUI* pCmdUI)
1973 {
1974         pCmdUI->Enable(
1975                 m_pImgMergeWindow->GetCurrentMaxPage() < m_pImgMergeWindow->GetMaxPageCount() - 1);
1976 }
1977
1978 void CImgMergeFrame::OnImgCurPanePrevPage()
1979 {
1980         m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) - 1);
1981         UpdateLastCompareResult();
1982 }
1983
1984 void CImgMergeFrame::OnUpdateImgCurPanePrevPage(CCmdUI* pCmdUI)
1985 {
1986         pCmdUI->Enable(m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) > 0);
1987 }
1988
1989 void CImgMergeFrame::OnImgCurPaneNextPage()
1990 {
1991         m_pImgMergeWindow->SetCurrentPage(m_pImgMergeWindow->GetActivePane(), m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) + 1);
1992         UpdateLastCompareResult();
1993 }
1994
1995 void CImgMergeFrame::OnUpdateImgCurPaneNextPage(CCmdUI* pCmdUI)
1996 {
1997         pCmdUI->Enable(
1998                 m_pImgMergeWindow->GetCurrentPage(m_pImgMergeWindow->GetActivePane()) < 
1999                 m_pImgMergeWindow->GetPageCount(m_pImgMergeWindow->GetActivePane()) - 1);
2000 }
2001
2002 void CImgMergeFrame::OnImgUseBackColor()
2003 {
2004         bool bUseBackColor = !m_pImgMergeWindow->GetUseBackColor();
2005         if (bUseBackColor)
2006         {
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)
2013                 {
2014                         COLORREF clrBackColor = dialog.GetColor();
2015                         RGBQUAD backColor1 = {GetBValue(clrBackColor), GetGValue(clrBackColor), GetRValue(clrBackColor)};
2016                         m_pImgMergeWindow->SetBackColor(backColor1);
2017                         m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2018                 }
2019         }
2020         else
2021         {
2022                 m_pImgMergeWindow->SetUseBackColor(bUseBackColor);
2023         }
2024         SaveOptions();
2025 }
2026
2027 void CImgMergeFrame::OnUpdateImgUseBackColor(CCmdUI* pCmdUI)
2028 {
2029         pCmdUI->SetCheck(m_pImgMergeWindow->GetUseBackColor() ? 1 : 0);
2030 }
2031
2032 void CImgMergeFrame::OnImgVectorImageScaling(UINT nId)
2033 {
2034         m_pImgMergeWindow->SetVectorImageZoomRatio(
2035                 static_cast<float>(pow(2.0f, int(nId - ID_IMG_VECTORIMAGESCALING_100))));
2036         SaveOptions();
2037 }
2038
2039 void CImgMergeFrame::OnUpdateImgVectorImageScaling(CCmdUI* pCmdUI)
2040 {
2041         pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_VECTORIMAGESCALING_100)) == m_pImgMergeWindow->GetVectorImageZoomRatio());
2042 }
2043
2044 void CImgMergeFrame::OnImgCompareExtractedText()
2045 {
2046         String text[3];
2047         String desc[3];
2048         for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); ++nBuffer)
2049         {
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)));
2053                 if (bstr)
2054                 {
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);
2059                 }
2060         }
2061         GetMainFrame()->ShowTextMergeDoc(m_pDirDoc, m_filePaths.GetSize(), text, desc, _T(".yaml"));
2062 }
2063
2064 /**
2065  * @brief Generate report from file compare results.
2066  */
2067 bool CImgMergeFrame::GenerateReport(const String& sFileName) const
2068 {
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)
2075         {
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());
2079         }
2080
2081         UniStdioFile file;
2082         if (!file.Open(sFileName, _T("wt")))
2083         {
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);
2088                 return false;
2089         }
2090
2091         file.SetCodepage(ucr::CP_UTF_8);
2092
2093         file.WriteString(
2094                 _T("<!DOCTYPE html>\n")
2095                 _T("<html>\n")
2096                 _T("<head>\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")
2104                 _T("</style>\n")
2105                 _T("</head>\n")
2106                 _T("<body>\n")
2107                 _T("<table>\n")
2108                 _T("<tr>\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]));
2111         file.WriteString(
2112                 _T("</tr>\n")
2113                 _T("<tr>\n"));
2114         for (int i = 0; i < m_pImgMergeWindow->GetPaneCount(); ++i)
2115                 file.WriteString(
2116                         strutils::format(_T("<td><div class=\"img\"><img src=\"%s\" alt=\"%s\"></div></td>\n"),
2117                         diffimg_filename[i], diffimg_filename[i]));
2118         file.WriteString(
2119                 _T("</tr>\n")
2120                 _T("</table>\n")
2121                 _T("</body>\n")
2122                 _T("</html>\n"));
2123         return true;
2124 }
2125
2126 /**
2127  * @brief Generate report from file compare results.
2128  */
2129 void CImgMergeFrame::OnToolsGenerateReport()
2130 {
2131         String s;
2132         CString folder;
2133
2134         if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
2135                 return;
2136
2137         if (GenerateReport(s))
2138                 LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
2139 }
2140
2141 void CImgMergeFrame::OnRefresh()
2142 {
2143         if (UpdateDiffItem(m_pDirDoc) == 0)
2144         {
2145                 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
2146                         [](LPCTSTR msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
2147         }
2148 }
2149
2150 void CImgMergeFrame::OnDropFiles(int pane, const std::vector<String>& files)
2151 {
2152         if (files.size() > 1 || paths::IsDirectory(files[0]))
2153         {
2154                 GetMainFrame()->GetDropHandler()->GetCallback()(files);
2155                 return;
2156         }
2157
2158         ChangeFile(pane, files[0]);
2159 }
2160
2161 void CImgMergeFrame::OnSetFocus(CWnd* pNewWnd)
2162 {
2163         if (m_nActivePane != -1)
2164                 m_pImgMergeWindow->SetActivePane(m_nActivePane);
2165 }
2166
2167
2168 /**
2169  * @brief Open help from mainframe when user presses F1
2170  */
2171 void CImgMergeFrame::OnHelp()
2172 {
2173         theApp.ShowHelp(ImgMergeFrameHelpLocation);
2174 }